[React][리액트를 다루는 기술] 컴포넌트 스타일링

uddi·2024년 3월 15일
0

React

목록 보기
13/16

📌 스타일링 방식의 종류

  • 일반 CSS : 컴포넌트를 스타일링하는 가장 기본적인 방식
  • Sass : 확장된 CSS 문법을 사용하여 CSS 코드를 더욱 쉽게 작성할 수 있도록 해주는 CSS 전처리기
  • CSS Module : CSS 클래스가 다른 CSS 클래스의 이름과 절대 충돌하지 않도록 파일마다 고유한 이름을 자동으로 생성해 주는 옵션
  • styled-components : 스타일을 자바스크립트 파일에 내장시키는 방식으로 스타일을 작성함과 동시에 해당 스타일이 적용된 컴포넌트를 만듦

📌 CSS 작성시 주의점

CSS를 작성할 때 가장 중요한 점은 CSS 클래스를 중복되지 않게 만드는 것이다

CSS 클래스 중복 방지를 위한 방법 중 하나는 이름을 지을 때 특별한 규칙을 사용하는 것이고, 또 다른 하나는 CSS Selector를 활용하는 것이다

🍞 이름 짓는 규칙

.App-logo {...}
.App-header {...}
.App-link {...}

리액트 프로젝트를 시작해봤다면 위와 같이 자동 생성된 App.css 파일을 본 적 있을 것이다
여기서는 컴포넌트 이름-클래스 형태로 클래스 이름을 지은 것을 확인할 수 있다

이와 비슷한 CSS 방법론 중 하나로 BEM 네이밍 방식이 있다

BEM 네이밍 방식이란 이름을 지을 때 일종의 규칙을 준수하여 해당 클래스가 어디에서 어떤 용도로 사용되는지 명확하게 작성하는 방식이다
ex) .card__title-primary

🍞 CSS Selector

CSS Selector를 사용하면 CSS 클래스가 특정 클래스 내부에 있는 경우에만 스타일을 적용할 수 있다

.App .logo {...}

컴포넌트의 최상위 html 요소에는 컴포넌트의 이름으로 클래스 이름을 짓고, 그 내부에서는 소문자를 입력하거나 header 같은 태그를 사용하여 클래스 이름이 불필요한 경우 아예 생략할 수 있다

📌 Sass

Sass는 CSS 전처리기로 복잡한 작업을 쉽게 할 수 있도록 해주고, 스타일 코드의 재활용성을 높이며 코드의 가독성을 높여 유지 보수를 더욱 쉽게 해준다

Sass에서는 .scss와 .sass 두 가지 확장자를 지원한다

  • .sass
$font-stack: Helvetica, sans-serif
$primary-color: #333

body
  font: 100% $font-stack
  color: $primary-color
  • .scss
$font-stack: Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;
}

위 코드를 보면 두 개의 문법은 차이가 있는 것을 알 수 있다

.sass 확장자는 중괄호와 세미콜론을 사용하지 않으며 .scss 확장자는 기존 CSS를 작성하는 방식과 비교해서 문법이 크게 다르지 않다

보통 .scss 확장자를 더 많이 사용한다

🍞 .scss 확장자 사용하기

// 변수 사용
$red: #fa5252;
$orange: #fd7e14;

// 믹스인 만들기(재사용되는 스타일 블록을 함수처럼 사용 가능)
@mixin square($size) {
  $calculated: 32px * $size;
  width: $calculated;
  height: $calculated;
}

.SassComponent {
  .box {	// .SassComponent .box와 동일
    background: red;
    cursor: pointer;
    &.red {
      // .red 클래스가 .box와 함께 사용되었을 때
      background: $red;
      @include square(1);
    }
    &.orange {
      background: $orange;
      @include square(2);
    }
  }
}

utils 함수 분리하기

여러 파일에서 사용될 수 있는 변수 및 믹스인은 다른 파일로 따로 분리해 작성한 뒤 필요한 곳에서 쉽게 불러와 사용할 수 있다

보통 파일명은 utils.scss 로 한다

다른 scss 파일을 불러올 때는 최상단에 @import 구문을 사용한다

📌 CSS Module

CSS Module은 CSS를 불러와서 사용할 때 클래스 이름을 고유한 값인 [파일 이름]_[클래스 이름]__[해시값] 형태로 자동으로 만들어 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해 주는 기술이다

자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 사용 가능

:global .something {...}

특정 클래스가 웹 페이지에서 전역적으로 사용되는 경우라면 :global을 앞에 입력해줘야 한다

반대로 CSS Module이 아닌 일반 .css/.scss 파일에서 :local을 사용해 CSS Module을 사용할 수 있다

import styles from './css파일';

(...)

<span className = {styles.wrapper}></span>
<span className = "something"></span>

클래스를 적용하고 싶은 JSX 엘리먼트에 className = {styles.[클래스 이름]} 형태로 전달해주면 된다

:global을 사용해 전역적으로 선언한 클래스의 경우 문자열로 넣어 준다

🍞 클래스 이름을 두 개 이상 적용할 때

<div className = {`${styles.wrapper} ${styles.inverted}`}></div>

또는
className = {[styles.wrapper, styles.inverted].join(' ')}

🍞 classnames

classnames는 CSS 클래스를 조건부로 설정할 때 매우 유용한 라이브러리로, CSS Module을 사용할 때 여러 클래스를 적용함에 있어 매우 편리하다

classnames의 기본적인 사용법

import classNames from 'classnames';

classNames('one', 'two');	// 'one two'
classNames('one', { two: true });	// 'one two'
classNames('one', { two: false });	// 'one'
classNames('one', ['two', 'three']);	// 'one two three'

const myClass = 'hello';
classNames('one', myClass, { myCondition: true });	// 'one hello myCondition'

이와 같이 여러 종류의 파라미터를 조합해 CSS 클래스를 설정할 수 있기 때문에 컴포넌트에서 조건부로 클래스를 설정할 때 매우 편하다는 장점이 있다

classnames의 bind 함수

classnames의 bind 함수를 사용하면 클래스를 넣을 때마다 styles.[클래스 이름] 형태를 사용할 필요가 없다

import classNames from 'classnames/bind';
import styles from './파일 이름';

const cx = classNames.bind(styles);	// 미리 styles에서 클래스를 받아오도록 설정

(...)

<div className = {cx('wrapper', 'inverted')}></div>

🍞 Sass에서 CSS Module 사용하기

Sass를 사용할 때도 확장자명을 .module.scss 라고 하면 CSS Module로 사용할 수 있다

📌 styled-components

이 방식은 자바스크립트 파일 안에 스타일을 선언하는 방식으로 CSS-in-JS라고 부른다
관련 라이브러리가 많아서 참고해도 좋다

styled-components는 CSS-in-JS 라이브러리 중에서 개발자들이 가장 선호하며, 이와 비슷하고 대체할 수 있는 라이브러리로 emotion이 대표적이다

🍞 styled-components의 장점

  1. 자바스크립트 파일 하나에 스타일까지 작성할 수 있어서 .css, .scss 확장자를 가진 스타일 파일을 따로 만들지 않아도 된다
import styled, { css } from "styled-components";

const Box = styled.div`
  background: ${(props) =>
    props.color || "blue"}; // props로 넣어준 값 직접 전달 가능
  padding: 1rem;
  display: flex;
`;

const Button = styled.button`
  background: white;

  // & 문자를 사용해서 Sass처럼 자신 선택 가능
  &:hover {	
    background: blue;
  }

  // inverted 값이 true일 때 특정 스타일 부여
  ${(props) =>
    props.inverted &&
    css`
      background: none;

      $:hover {
        background: white;
      }
    `};
`;

const StyledComponent = () => (
  <Box color="black">
    <Button inverted={true}>테두리만</Button>
  </Box>
);

export default StyledComponent;
  1. 위 예시처럼 props 값으로 전달해 주는 값을 쉽게 스타일에 적용할 수 있다

VS Code에서 styled-components를 사용할 때 코드 신택스 하이라이팅이 제대로 적용되지 않는 문제가 발생한다 👉 VS Code 마켓플레이스에서 vscode-styled-components 설치

🍞 Tagged 템플릿 리터럴

styled-components에서 스타일을 작성할 때 `을 사용해 만든 문자열에 스타일 정보를 넣어 줬는데, 이 문법을 Tagged 템플릿 리터럴이라고 한다

일반 템플릿 리터럴과 다른 점

Tagged 템플릿 리터럴은 템플릿 안에 자바스크립트 객체나 함수를 전달할 때 온전히 추출할 수 있다

`hello ${{foo: 'bar'}} ${() => 'world'}!`	// "hello [object Object] () => 'world'!"

일반 템플릿의 경우 템플릿에 객체나 함수를 넣으면 형태를 잃게 된다

function tagged(...args) {
  console.log(args);
}
tagged`hello ${{foo: 'bar'}} ${() => 'world'}!`

하지만 위와 같이 tagged라는 함수 뒤에 템플릿 리터럴을 넣어 주면 템플릿 안에 넣은 값을 온전히 추출할 수 있다
결과

styled-components는 이러한 속성을 사용해 styled-components로 만든 컴포넌트의 props를 스타일 쪽에서 쉽게 조회할 수 있도록 해 준다

🍞 스타일링된 엘리먼트 만들기

styled-componets를 사용해 스타일링된 엘리먼트를 만들 때는 컴포넌트 파일의 상단에서 styled를 불러오고 styled.태그명을 사용해 구현한다

import styled from 'styled-components';

const MyComponent = styled.div`(...)`;

만약 사용해야 할 태그명이 유동적이거나 특정 컴포넌트 자체에 스타일링해 주고 싶다면 다음과 같은 형태로 구현할 수 있다

const MyInput = styled('태그타입')`(...)`	// 태그의 타입을 styled 함수의 인자로 전달

const StyledLink = styled(Link)`(...)`	// 컴포넌트 형식의 값을 넣어줌

컴포넌트를 styled의 파라미터에 넣는 경우 해당 컴포넌트에 className props를 최상위 DOM의 className 값으로 설정하는 작업이 내부적으로 되어 있어야 함

const Sample = ({ className }) => {
  return <div className={className}>Sample</div>;
};
const StyledSample = styled(Sample)`(...)`;

🍞 props에 따른 조건부 스타일링

className을 사용해 조건부 스타일링을 했던 일반 CSS 클래스와 달리 styled-components에서는 props로 처리할 수 있다

import styled, { css } from "styled-components";

const Button = styled.button`
  // inverted 값이 true일 때 특정 스타일 부여
  ${(props) =>
    props.inverted &&
    css`
      background: none;

      $:hover {
        background: white;
      }
    `};
`;

const StyledComponent = () => (
  <Button inverted={true}>테두리만</Button>
);

이처럼 스타일 코드 여러 줄을 props에 따라 넣어 줘야 할 때는 css를 import 해야 한다

css를 import 해야 하는 이유

  1. css를 사용하지 않고 바로 문자열을 넣어도 작동은 하지만 문자열로만 취급되기 때문에 신택스 하이라이팅이 제대로 이루어지지 않는다는 단점이 있다
  2. Tagged 템플릿 리터럴이 아니기 때문에 함수를 받아 사용하지 못해 해당 부분에서는 props 값을 사용하지 못한다

🍞 styled-componets에서 반응형 디자인하기

브라우저의 가로 크기에 따라 다른 스타일을 적용하기 위해서는 일반 CSS를 사용할 때와 똑같이 media query를 사용하면 된다

유틸 함수를 사용한 반응형 디자인

const sizes = {
  desktop: 1024,
  tablet: 768
};

// size 객체에 따라 자동으로 media 쿼리 함수 만들어 줌
const media = Object.keys(sizes).reduce((acc, label) => {
  acc[label] = (...args) => css`
    @media (max-width: ${sizes[label] / 16}em) {
	  ${css(...args)};
    }
  `;
  
  return acc;
}, {});

const Box = styled.div`
  (...)
  ${media.desktop`width: 768px;`}
  ${media.tablet`width: 100%;`};
`;

이처럼 media를 한번 선언하면 media를 사용할 때 스타일 쪽의 코드가 간단해진다
자세한 건 여기를 참고하면 된다

📌 마무리

리액트에서 컴포넌트를 스타일링 하기 위한 다양한 방식을 알게 되었다
나는 그동안 CSS Module을 사용해왔었는데, Sass도 한 번 사용해봐야겠다고 생각했다

profile
거북이는 느리지만 결국 결승선을 통과한다

0개의 댓글