Frontend/React

React - 2. 리액트 컴포넌트 스타일링하기

둉이 2022. 1. 16. 03:11

리액트에서 컴포넌트에 style을 적용하는 방법에는 일반적인 CSS를 사용하는 방법도 있지만, 보통 다음과 같은 4가지 방법을 주로 사용한다.

 

1. Sass(Scss)

2. CSS Module

3. Styled-components

4. Inline style

 

Sass(Syntactically Awesome Style Sheets)

SASS는 CSS 전처리기이며, 개발과 유지보수에 용이한 Sass, Scss라는 확장된 CSS 문법을 제공한다.

 

CSS 전처리기(CSS pre-processor)란?

CSS를 확장할 수 있는 스크립팅 언어로서, 컴파일러를 통해 브라우저에서 사용할 수 있는 일반 CSS 문법 형태로 변환하여 사용할 수 있음

 

Node.js 환경에서 SASS 문법은 sass 패키지를 설치하여 사용이 가능하다.

# npm i sass

 

SASS와 일반 CSS 문법과의 차이가 무엇인지 예시 코드를 통해 살펴보자.

 

- 일반 CSS

:root {
  --font-stack: Helvetica, sans-serif;
  --primary-color: #333;
}

body {
  font: 100% var(--font-stack);
  color: var(--primary-color);
}

일반 CSS에서의 변수 선언은 :root 선택자 내에서 할 수 있다.

주의할 점으로는, 변수명 앞에 반드시 --를 붙여야 한다.

위에서 선언한 변수를 사용할 때는 var(변수명) 형태로 사용하면 된다.

 

- SASS

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

body
  font: 100% $font-stack
  color: $primary-color

SASS에서는 중괄호({})를 사용하지 않고, 탭(\t)을 사용하여 계층 관계나 중첩을 나타낸다.

변수 선언은 위 예제 코드처럼 변수명 앞에 반드시 $를 붙여야 하며, 사용할 때도 $변수명 형태로 사용한다.

 

- SCSS

$font-stack:    Helvetica, sans-serif;
$primary-color: #333;

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

기본적인 문법은 SASS와 유사하나, 탭 대신 중괄호를 사용하며, 코드의 끝마다 세미콜론을 붙인다는 차이점이 있다.

 

 

그 외의 특징은 다음과 같다.

 

- 한 줄 주석(//) 사용 가능

 

- @use 키워드를 사용하여 다른 css, sass 파일을 import할 수 있음

 

- @mixin 키워드를 사용하여 공통으로 사용되는 CSS 선언 그룹을 만들어 자유롭게 import 가능

  (@include 키워드를 사용하여 선언한 mixin을 import할 수 있음)

@mixin theme($theme: #1187cf) {
  background: $theme;
  box-shadow: 0 0 1px rgba($theme, .25);
  color: #fff;
}

.info {
  @include theme;
}

.alert {
  @include theme($theme: #ff0000);
}

위 코드에서는 인자로 $theme이라는 color 변수를 전달받아 사용할 수 있는 theme mixin을 선언했다.

 

.info에서처럼 인자를 넘겨주지 않는 경우에는 default value인 #1187cf가 적용되며, .alert에서처럼 $theme 값을 넘겨주는 경우에는 해당 값이 적용된다.

 

- 스타일의 확장 및 상속 가능

%message-shared {
  border: 1px solid #ccc;
  padding: 10px;
  color: #333;
}

.success {
  @extend %message-shared;
  border-color: green;
}

.error {
  @extend %message-shared;
  border-color: red;
}

% 키워드를 사용하여 확장이 가능한 스타일을 선언하고, @extend 키워드를 사용하여 해당 스타일을 다른 스타일 내에 포함시키는 방법으로 확장이 가능하다.

 

- 스타일 중첩 가능

$blue: #1187cf;
$white: #fff;

button {
  background-color: $blue;
  color: $white;
  border-width: 0;
  
  span {
  	background-color: $white;
    color: #111;
  }

  &:hover {
    background-color: darken($blue, 10%);
  }
  
  & + & {
    margin-left: 1rem;
  }
}

스타일 안에 스타일을 정의하는 중첩 문법을 지원한다.

 

위 코드로 설명을 해보자면, button 내의 span은 button span { ... } 으로 선언한 것처럼 동작한다.

 

&:hover는 button:hover { ... }을 의미한다.

 

& + &는 button + button { ... }을 의미한다.

요소 오른쪽에 나열된 요소가 존재한다면, 첫 번째 요소를 제외한 나머지 요소들에 style이 할당된다.

 

- calc() 없이 수학 연산이 가능

calc() 함수로 감싸지 않더라도 사칙연산을 포함한 여러 연산의 사용이 가능하다.

+, - *, % 기호를 사용하면 되고, 나눗셈의 경우에는 sass:math 라이브러리를 import하여 math.div()를 사용하면 된다.

@use 'sass:math';

article[role="main"] {
  width: 600px / 960px * 100%;
}

button {
  background-color: $blue;
  color: $white;
  border-width: 0;
  width: math.div(200px, 2px) + px;
  height: 20px * 5;

  &:hover {
    background-color: darken($blue, 10%);
  }
}

 

- lighten(), darken() 등의 유용한 함수 제공

$blue: #1187cf;

.Button {
  &:hover {
    background: lighten($blue, 10%); // 색상 10% 밝게
  }

  &:active {
    background: darken($blue, 10%); // 색상 10% 어둡게
  }
}

lighten(color, percent) 함수는 인자로 전달받은 색상을 percent만큼 밝게 연산하여 반환한다.

darken(color, percent) 함수는 인자로 전달받은 색상을 percent만큼 어둡게 연산하여 반환한다.

 

CSS Module

CSS를 작성하다 보면 CSS 클래스 이름이 중복되는 일이 발생할 수 있다.

이럴 때 CSS Module 기술을 사용하게 되면 해당 CSS 파일 내의 모든 CSS 클래스 이름들의 고유성을 보장할 수 있다.

 

이게 무슨 소리냐? 한 번 알아보자.

 

CSS Module로 선언된 CSS 파일을 불러와 style을 적용하는 경우에는, 아래 사진처럼 작성한 클래스 이름이 해시값으로 변환되어 적용된다.

 

그러므로 다른 CSS Module 내에 동일한 클래스명으로 선언된 style이 있더라도 다른 해시값으로 변환하여 클래스 이름의 중복을 방지할 수 있다. 

test_button이라는 클래스명이 저렇게 바뀜

 

CSS Module은 파일명.module.css 형태로 파일명을 선언하여 사용할 수 있다.

작성한 CSS Module은 아래 코드처럼 import하여 변수처럼 className 속성에 할당하여 사용하면 된다.

import React from 'react';
import styles from '@css/test.module.css';

const Button = ({ val, children }) => {
  return (
    <button type="button" className={styles.test_button} value={val}>
      {children}
    </button>
  );
};
export default Button;

 

이러한 CSS Module 방식은 기존 레거시 프로젝트에 리액트를 도입하게 되어 클래스 이름이 중복될 가능성이 있거나 클래스 명명 규칙을 정하기 번거로운 상황에 사용하면 좋다.

 

Styled-components

CSS in JS란 말 그대로 JS 파일 내에 CSS를 함께 작성하는 것을 의미한다.

 

styled-components는 CSS in JS를 사용할 수 있도록 도와주는 라이브러리이다.

 

styled-components 외에도 emotion, styled-jsx 등의 라이브러리도 있으니 각각의 장단점을 비교하고 선택하여 사용하자!

 

styled-components의 기본적인 문법 형태와 사용 예시는 다음과 같다.

import React from 'react';
import styled from 'styled-components';

const Circle = styled.div`
  width: 5rem;
  height: 5rem;
  background: black;
  border-radius: 50%;
`;

function App() {
  return <Circle />;
}

export default App;

 

위 코드처럼 변수명을 컴포넌트명으로 명명한 후, template literal 방식으로 적용할 style을 작성하여 컴포넌트처럼 선언하여 사용하면 된다.

 

 

이러한 styled-components의 장점은 다음과 같다.

 

- SASS에서 제공하는 중첩 문법 등을 지원

 

- props를 전달받아 사용할 수 있음

CSS in JS 방식은 컴포넌트의 props를 전달받아 style을 지정할 때 사용할 수 있다.

import styled from '@emotion/styled';

const ButtonContainer = styled.button`
  width: ${({ width }) => width || '100%'};
  height: ${({ height }) => height || 'auto'};
  background: ${({ background }) => background || 'inherit'};
  font-size: ${({ fontSize }) => fontSize || 'inherit'};
  padding: ${({ paddingH }) => paddingH || '5%'} 0;
  font-family: ${({ theme }) => theme.fonts.gmarket};
  border-radius: 60px;
  border: 2px solid ${({ theme }) => theme.colors.black};
  box-shadow: 4px 1px 0 ${({ theme }) => theme.colors.black};
  cursor: pointer;
  white-space: nowrap;
  
  @media (max-width: ${({ theme }) => theme.breakpoints.md}) {
    border: 1px solid ${({ theme }) => theme.colors.black};
    box-shadow: 3px 1px 0 ${({ theme }) => theme.colors.black};
  }
`;

const Button = ({
  width,
  height,
  background,
  fontSize,
  paddingH,
  children,
  onClick,
  disabled,
}) => {
  return (
    <ButtonContainer
      width={width}
      height={height}
      background={background}
      fontSize={fontSize}
      paddingH={paddingH}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </ButtonContainer>
  );
};

export default Button;

 

- 스타일 확장 가능

CSS in JS 방식으로 선언한 스타일은 손쉽게 확장이 가능하다.

아래 방식처럼 새로운 스타일을 만들 때, 기존 스타일을 상속받아 확장할 수 있다.

import React from "react";
import styled from "styled-components";

const App = () => (
  <>
    <Button>버튼</Button>
    <BlueButton>파란버튼</BlueButton>
  </>
);

const Button = styled.button`
  outline: none;
  border: none;
  border-radius: 50%;
`;

const BlueButton = styled(Button)`
  background-color: blue;
`;

export default App;

 

- CSS style을 분리하지 않아도 되므로 개발에 편리(개발 생산성 증가)

CSS in JS 방식은 JS 파일 내에 CSS 코드를 작성할 수 있으므로 따로 CSS 파일을 관리하지 않아도 되고, 개발하는 데 편리하다는 장점이 있다.

 

- 현재 페이지에서 필요한 컴포넌트의 CSS만 로딩하므로 효율적

CSS in CSS 방식은 현재 페이지에서 사용하지 않는 CSS 스타일 요소도 로딩하므로 CSS 파일의 크기가 클 수록 비효율적이지만, CSS in JS 방식은 현재 페이지에서 사용되는 컴포넌트의 CSS만 로딩하므로 효율적이다.

 

 

하지만 이런 장점이 많은 styled-components에도 단점은 존재한다.

 

- 해당 컴포넌트가 리렌더링될 때마다 CSS 코드를 파싱해야 하므로 성능에 좋지 않음

리액트에서는 state가 변경될 때마다 해당 state를 사용하는 컴포넌트에 리렌더링이 발생한다.

 

따라서, 리렌더링이 발생할 때 해당 컴포넌트 파일 내의 CSS in JS 코드도 함께 리렌더링되고, 그에 따라 CSS 코드를 파싱하는 작업이 다시 이루어지므로 추가적인 시간이 소요된다.

 

- 라이브러리를 설치하여 사용해야 하므로 번들 크기가 증가하여 초기 페이지 로딩 시간이 길어질 수 있음

CSR의 최대 단점인 번들 크기로 인한 초기 로딩 속도 문제이다.

 

CSR은 사용자가 처음 페이지에 접속했을 때 번들링된 JS 파일을 다운로드한 후, 해당 JS 파일을 실행하여 DOM을 렌더링한다.

 

CSS in JS 방식을 사용할 경우에는 해당 기능을 지원하는 라이브러리를 필수적으로 설치해야 하기 때문에 번들 크기가 커지고, 그만큼 번들 다운로드 시간이 길어지므로 초기 페이지 로딩 시간이 길어진다는 단점이 있다.

 

inline-style

사실 리액트에서는 HTML 요소에 inline 스타일을 적용하는 것처럼 스타일 객체를 style 속성의 인자로 전달하여 inline 스타일을 적용하는 것이 가능하다.

 

하지만 이 방법으로는 ::before, ::after 등의 pseudo element와 :hover, :active 등의 pseudo class를 사용할 수 없다는 단점이 존재한다. (SASS의 Nesting 문법도 사용 불가)

 

또한, 어플리케이션 규모가 커질수록 코드 복잡성이 증가하게 되어 유지보수가 어렵다.

 

따라서 리액트 공식 문서에서도 inline 방식의 스타일 적용을 추천하지 않는다.

const App = () => {
  return (
    <button val="123" style={{ color: 'blue', backgroundColor: 'transparent' }}>
      하이
    </button>
  );
};

 

 

참고 자료

 

Sass 강좌 – 한 눈에 보기 | VELOPERT.LOG

이전부터 Sass 의 존재를 알고있었고,배우고싶었는데, 미뤄오셨나요? 그렇다면 잘 오셨습니다. 이 포스트에서는 Sass 의 특징에 대하여 알아보고, Sass 로 할 수 있는 멋진 것들을 알아볼테니까요.

velopert.com

 

 

Sass: Sass Basics

Before you can use Sass, you need to set it up on your project. If you want to just browse here, go ahead, but we recommend you go install Sass first. Go here if you want to learn how to get everything set up. Preprocessing CSS on its own can be fun, but s

sass-lang.com