Stop using CSS with React

stop using css with react

TL;TR

React(혹은 자체적인 Component를 가지고 있는 프레임워크)를 사용할 때, CSS selector가 제 역할을 하지 못하게 되어
React의 Component에 styling과 직접적으로 관련된 추가적인 로직이 포함되게 됩니다.

이는 CSS(SCSS)와 Component 사이의 어중간한 분리를 불러일으킵니다(Control coupling).

이 문제에 대해, styled-component와 emotion같은 CSS-IN-JS 라이브러리들은 Component와 그 Component의 Style을 1대 1로,
아주 강하게 묶어내서 하나의 모듈로 병합시키는 방식
을 선택했습니다.
이 방식은 지금까지 등장한 방식 중 단연 효과적이였습니다. 1대 1로 강하게 묶어버리면서 두 개의 분리된 모듈하나의 모듈로 병합시켰고,
그로 인해 코드가 낮은 결합도를 취할 수 있게 하였습니다.

이런 이유로 저는 여러분들께 CSS(SCSS)를 떠나 CSS-IN-JS를 쓰시는 것을 추천드립니다.

CSS’S Styling

Cascading Style Sheets (CSS) is a simple mechanism for adding style (e.g., fonts, colors, spacing) to Web documents. - W3C

Cascading Style Sheets (CSS) 는 웹 문서에 스타일을 제공하기 위한 간단한 메커니즘 입니다. - W3C

아시다시피, CSS는 웹 문서에 스타일을 적용하기 위한 언어입니다.
웹 문서에 스타일을 적용하기 때문에, 대상은 자연스럽게 HTML Element가 됩니다.

하지만, HTML에는 정말 많은 종류의 HTML Element가 정말 많은 위치에 자리하고 있습니다(i.e: <head> 안에, <body><div> 안에, 등등…).
CSS는 어떻게 ‘어떤 종류의 Element’, ‘어디에 있는 Element’를 찾아 스타일링할까요?

CSS는 CSS selector를 통해 스타일링의 대상을 지정하는 방식을 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
body {
background-color: yellow;
}

input[type='file'] {
background-color: red;
}

.draggableImage {
cursor: grab;
}

CSS selector를 사용하면 스타일링할 HTML Element의 거의 모든 정보를 서술하여 구체적인 대상을 설정할 수 있습니다.
(심지어 그 위치까지 서술할 수 있습니다!!!)

이렇게 강력한 무기를 가졌기 때문에 CSS는 지금까지도 대체품을 만드려는 거대한 그룹이 없습니다. 이를 확장한 언어를 만드는 그룹은 있어도요(SCSS).

하지만 React와 같이 Virtual DOM과 자체적인 Element를 가진 프레임워크(혹은 라이브러리, 뭐시 중허나요.)가 등장하자 CSS의 강력한 무기인 selector의 위기가 찾아왔습니다.

CSS Selector is not a sliver bullet!

앞서 이야기했듯이, CSS가 스타일링 할 대상은 HTML Element 입니다. React Element가 아니라요.
그렇기 때문에 CSS selector는 HTML Element를 지정하기 위한 정보를 서술할 수는 있어도 React Element를 지정하기 위한 정보는 서술할 수 없습니다.

이 점을 알고 있는 React 개발자들은 React Element(와 기타 데이터들)를 DOM Node로 변환시키기 위한 라이브러리인 react-dom 에서
HTML Element에 대응하는 React Element를 생성할 때, className 이라는 property를 받아, 차후 HTML Element 로 변환할 때 class attribute로 넣어주는 기능을 포함해서,
대응하는 React Element의 property를 통해 HTML Element의 attribute를 설정하는 기능을 만들었습니다.

이 기능을 통해 React를 사용하는 개발자들은 CSS selector를 이용할 수 있게 되었습니다.

harmony
1
2
3
4
5
6
7
import React from 'react';

const MyButton = () => (
<button className='customButton blue'>
click me!
</button>
);

1
2
3
4
5
6
7
.customButton {
border-radius: 50%;
}

.blue {
background-color: blue;
}

음… Component의 Style을 바로 볼 수 없는 게 안타깝긴 하지만 이건 개인 취향이니 넘어가도록 하지요.
이렇게 간단한 컴포넌트를 만드는 경우에는 className(class attribute)를 통해 styling을 받는 것도 만족스러워 보입니다.

하지만, Component의 property 혹은 상태에 따라 style을 변경해야 하는 경우에는 이야기가 달라집니다.

harmony
1
2
3
4
5
6
7
8
9
10
11
import React from 'react';

const MyButton = ({ clicked, onClick }) => {
const color = clicked ? 'red' : 'blue';

return (
<button className={`customButton ${color}`} onClick={onClick}>
click me!
</button>
);
};

1
2
3
4
5
6
7
8
9
10
11
.customButton {
border-radius: 50%;
}

.red {
background-color: red;
}

.blue {
background-color: blue;
}

문제점이 보이시나요? 다시 처음으로 돌아가 생각해보죠.

  1. CSS란? -> 웹 문서에 스타일을 적용하기 위한 언어
  2. CSS의 역할은? -> HTML Element 스타일링
  3. 스타일링이란? -> 특정 디자인이 필요한 대상에게 디자인을 입혀주는 것
  4. 대상에게 입혀주는 것이란? -> 대상을 찾아 능동적으로 디자인을 입혀주는 것 (만약 대상이 직접 찾아오는 거였다면 CSS selector는 필요하지 않은 요소이다.)
  5. 위 예제에서 CSS는 CSS selector를 통해 능동적으로 HTML Element를 스타일링하는가? -> 9번째 줄에서 class name을 연산하고, 12번째 줄에서 연산한 class name을 React Component가 적용하기 때문에 아니다.
  6. 위 예제에서 CSS는 자신이 마땅히 해야 하는 일을 다 하였는가? -> 아니다, HTML Element의 색을 스스로 결정하지 못했기 때문이다.

이제 문제점이 보이시나요?

위 코드의 문제는 바로, CSS가 해야 하는 스타일링 이라는 작업의 일부를 React Component가 하고 있는 것입니다!
CSS와 React Component가 서로 완벽하게 분리된 것도, 완벽하게 결합된 것도 아닙니다. 어중간하게 결합된 것 입니다!

즉, 만약 우리가 웹 페이지의 형태를 바꾸기 위해 CSS 코드를 수정하면 React Component를 수정해야 할 지도 모릅니다. React Component와 CSS가 스타일링 이라는 작업을 같이 담당하고 있기 때문이죠.
반대로 React Component를 수정하면 CSS 코드를 수정해야 할 지 모릅니다.

이렇게, 우리의 든든한 아군이자, CSS의 강력한 무기였던 CSS selector가 만병통치약이 아님이 밝혀졌습니다.

Use CSS-IN-JS instead of CSS selector

자, React Element 스타일링이라는 카이주를 잡기 위한, 새로운 예거를 여러분들께 소개해드리겠습니다.

바로.. CSS-IN-JS!

CSS-IN-JS is great!

“CSS-in-JS” refers to a pattern where CSS is composed using JavaScript instead of defined in external files. - React Blog

“CSS-in-JS”는 CSS를 추가적인 파일에 정의하지 않고 JavaScript 코드와 결합시켜서 정의하는 패턴을 의미합니다. - React Blog

CSS selector가 React Element를 만나면 무능력해지는 문제를 React 진영의 몇몇 사람들은 CSS 코드를 React Component와 묶어버리는 방법을 통해 해결했습니다.
아래와 같이요.

harmony
1
2
3
4
5
6
7
import React from 'react';
import styled from 'styled-components'

const MyButton = styled.button`
border-radius: 50%;
color: ${({ clicked }) => clicked ? 'red' : 'blue'}
`;

이렇게 styled-components를 통해 CSS-IN-JS를 사용함으로서 React Component와 CSS가 하나로 합쳐지면서 두 개의 어중간한 결합성을 가진 개체에서 하나의 독립적인 개체가 되었습니다.

이제 우리는 CSS 와 React Component간의 결합도에 대해 걱정하지 않아도 됩니다. 한 몸이 되었기 때문이죠.

자, 어떠신가요? 이제라도 CSS-IN-JS를 쓰고 싶지 않으신가요? 마지막으로 저는 제가 좋아하는 CSS-IN-JS 라이브러리들을 여러분들께 소개해드리며 이만 글을 마치도록 하겠습니다.

  • styled-components
    가장 유명한 CSS-IN-JS 라이브러리입니다. tagged template 을 이용해 깔끔하고 세련된 방식으로 component를 styling 하는 방법을 제공합니다.

  • glamorous
    paypal의 CSS-IN-JS 라이브러리입니다. 지금은 관리가 중단되어 프로젝트가 정지된 상태입니다. 객체 리터럴을 이용한 styling을 제공합니다.
    또한 styling 하며 생길 수 있는 몇몇 문제에 대한 해법이 마련되어 있습니다. (제 손에 익은 라이브러리입니다.)

  • emotion
    glamorous와 styled-components를 계승하는 라이브러입니다. glamorous보다 더 빠르고 더 가벼우며 tagged template과 객체 리터럴을 이용한 styling을 지원합니다.

공유하기