본문 바로가기
react

CSS-in-JS vs CSS-module vs Post CSS 차이 구분하기

by HomieKim 2022. 9. 2.

리액트 기반의 프론트엔드를 공부하다 보면 다양한 스타일링 방식을 알게됩니다.

프로젝트를 진행하며 다양한 방식의 스타일링을 사용해보려 했고 CSS-in-JS 방식과 CSS-Module 방식을 주로 사용하게 되었습니다.

최근 프로젝트를 진행하며 자주 사용되는 속성을 변수화 globalstyle에 변수화 하여 사용했습니다.

 해당 프로젝트에서는 styled-components를 사용하였는데 저렇게 사용자 지정 속성을 사용해도 되지만 라이브러리에서 제공하는 ThemeProvider를 사용하는 것이 더 적합하다는 걸 뒤늦게 알게되었습니다.

https://styled-components.com/docs/advanced

 

styled-components: Advanced Usage

Theming, refs, Security, Existing CSS, Tagged Template Literals, Server-Side Rendering and Style Objects

styled-components.com

 

라이브러리 사용 방법의 미숙함도 있지만 CSS-in-JS 방식과 CSS-Module 방식의 차이를 명확하게 알고있지 않다는 걸 알게되어 공부하려 합니다.

 


우선 css가 발전하며 어떠한 문제점을 해결하고 있는지 알아보았습니다.

CSS의 문제점?

 기존의 문서 단위의 웹 페이지 개발방식에서 최근에는 컴포넌트 단위의 웹 어플리케이션 개발 방식으로 바뀌면서 Global Scope를 가지는 기존의 css를 import 하는 방식으로는 개발하는데 어려움이 있습니다. 

 global scope 를 가지기 때문에 중복되지 않는 class name을 가져야 하고 JS와의 의존 관계, 컴포넌트의 상태 값에 따른 스타일적용, CSS로드에 따른 우선 순위 등 CSS 의 구조적인 문제점들을 해결하기 위해 BEM 이라는 코드 컨벤션을 사용하기도 하고, sass 같은 css 전처리기를 사용하기도 합니다.

css 전처리기란?
전처리기의 자신만의 특별한 Syntax를 가지고 CSS를 생성하도록 하는 프로그램입니다.
- 출처: https://developer.mozilla.org/ko/docs/Glossary/CSS_preprocessor

즉, css가 가진 문법적인 한계를 극복하기 위해 변수, 함수 등 전처리기가 가진 특정 문법을 사용해 css를 작성하면 일반적인 css 포맷으로 컴파일하여 변환해주는 것을 말합니다.

* 대표적인 전처리기 모듈

- Sass/SCSS
- less
- Stylus
- PostCSS

 css 내부적으로 문제를 해결하려는 시도는 있었지만 컴포넌트 단위의 개발 방법에서 css 파일을 따로 관리해야하고 추상화 할 수 없다는 문제는 해결할 수 없었습니다.

이러한 CSS의 문제점을 JS를 통해 해결하려고 나온 도구 들의 CSS-Module, CSS-in-JS, Post-CSS 같은 도구들입니다.

Post-CSS 이란?

위의 mdn문서에서 예를 들었듯이 post-css는 css 전처리기로 분류됩니다. 리액트를 사용하여 개발해 보았다면 .module.css 이런식으로 모듈 파일을 만들어 컴포넌트에 import styles form '~~~.module.css'; 이런식으로 사용해 보았을 겁니다. 여기서 정확이 css-module 과 post-css 는 같은건가? 라는 의문이 들었습니다.

A tool for transforming CSS with JavaScript
출처 : https://postcss.org/

 post css의 공식 홈페이지에서는 자바스크립트를 이용하여 CSS를 transform 하는 도구라고 소개 해 주고 있습니다. 

즉  js 플러그인을 사용하여 css를 변환해주는 역할을 하는 도구 입니다.

여기서 말하는 js 플러그인은 css의 모듈화, autoprefix (webkit 과 같이 브라우저간 호환이 되게 css를 자동으로 작성해줌) 등 css 사용의 편의성의 올려주는 기능을 js로 만들어놓은 도구 이다.

https://github.com/postcss/postcss/blob/main/docs/plugins.md

 

GitHub - postcss/postcss: Transforming styles with JS plugins

Transforming styles with JS plugins. Contribute to postcss/postcss development by creating an account on GitHub.

github.com

플러그인 종류는 굉장히 다양하고 해당 git-hub에서 확인해 볼 수 있습니다.

 

CRA에서 post-css?

 post-css에 대해 공부하다 보니 CRA를 이용하여 프로젝트를 생성할 경우 기본적으로 post-css를 내장하고 있어서 별도의 설정없이 .module.css 이렇게 생성하여 사용하기만 했었지, 어떻게 설정 되어있는지 추가적으로 플러그인을 사용하려면 어떻게 해야하는지 궁금해졌습니다.

CRA 프로젝트를 하나 만들어 서 eject 한뒤 생성된 webpack.config.js

tailwind가 저런식으로 내장되어있는건 또 처음알았네요

postcss-loader를 통해 설정하고있는 Options를 하나씩 알아보았습니다.

  • postcss-flexbugs-fixes : flexbus's에 대한 이슈들을 해결하기위한 플러그인 입니다.
    flexbus's에 대한 내용은 https://github.com/philipwalton/flexbugs 해당 문서에서 확인 할 수 있었습니다. 
  • postcss-preset-env : 최신 CSS를 대부분의 브라우저가 이해할 수 있는 것으로 변환하여 대상 브라우저 또는 런타임 환경에 따라 필요한 폴리필을 결정하는 플러그인
  • postcss-normarlize : 브라우저 리스트에 따라서 noramlize.css 또는 sanitize.css를 사용하게 함
    이건 무슨 말인지 잘 모르겠는데 CRA를 통해 프로젝트 생성시 package.json에 browserlist가 생성되는데 여기에 맞는 최신 css를 제공해주는 역할을 하는 것 같다.

 추가적으로 알아본 결과 브라우저 마다 설정되어있는 기본 스타일의 차이를 해결하기위해 Reset CSS 나 Normalize css 를 사용한다고 한다. Reset CSS 말그대로 모든 스타일을 완전히 초기화, Normalize는 일부 스타일만 리셋하고 유용한 기본스타일은 유지한다고 한다.

Normalize.css는 HTML 요소의 기본 스타일을 브라우저 간 일관성을 유지하도록 돕는 CSS 파일이다. 이것은 Boilerplate(보일러플레이트) 및 Bootstrap(부트스트랩) 등과 같은 크고 작은 프로젝트에서도 두루두루 사용되고 있다.
출처: https://webdir.tistory.com/455

 

  post-css === css-module 이렇게 잘못 알고 있었던 것 같습니다. 사실 모듈 방식으로 css를 사용하는 기능은 webpack에서 css-loader에서 지원하고 있습니다. 역시 CRA 생성 시 기본적으로 적용되어 있습니다. css-module에 대해서 조금 더 자세히 알아 보겠습니다.

 CSS-Module 이란?

 css-module 이란 위에서 설명한 대로 .module.css 와 같은 파일을 만들어서 각 컴포넌트마다 import 해서 사용하는 방법이다. 내 프로젝트 코드로 예시를 들어 보면

 이런식으로 styles라는 폴더를 만들어서 컴포넌트 별 혹은 domain 별로 묶어서 사용하고 있다. 이렇게 했을 때의 장점은 global scope를 가지지 않기 때문에 class name의 중복을 고려하지 않아도 되고 그렇기 때문에 재사용이 일반 css 를 사용하는 것 보다 훨신 용이합니다. 이렇게 만들 css 모듈을 다음과 같이 각 컴포넌트에서 import해서 적용할 수 있습니다.

어떻게 동작하는가?

 css-module 의 github을 보면 다음과 같이 정의하고 있습니다.

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.
출처 : https://github.com/css-modules/css-modules

 class name과 animation name이 기본적으로 local scope를 가지게하는 것을 말합니다. 이러한 동작이 가능한 이유는 css-loader를 이용한 번들링 과정에서 별도의 css 컴파일러를 사용해 고유한 class name을 만들어 내게 됩니다.

 위 그림에서 볼 수 있듯 CSS 모듈 컴파일 과정을 통해 .[파일명]_[클래스 명]_[고유 해쉬 값] 형태의 class name을 만들어 내게 됩니다. 

 CSS-in-JS란?

 css-module을 사용해 global scope로 인한 class name 중복에 대한 문제를 해결했지만, 프로젝트 규모가 커질 수록 많은 양의 style 파일을 따로 관리해야 했고 이런 불편함을 위해 등장하였습니다.

 용어 그대로 css를 js를 통해 생성해내는 기법으로 css를 jsx 파일 한에 넣어서 사용할 수 있습니다. React 같은 SPA 라이브러리 경우 컴포넌트 단위로 개발하기 이에 최적화된 기법이라고 할 수 있습니다.

가장 대표적인 라이브러리인 styled-compoents를 기준으로 살펴 보겠습니다.

https://styled-components.com/docs/basics#motivation

공식 문서의 motivation을 요약하면 다음 과 같습니다.

styled-components is the result of wondering how we could enhance CSS for styling React component systems.

 

 즉, 컴포넌트 컴포넌트 시스템에서 CSS 스타일링을 위해 개발자의 사용 경험과 성능을 최적화한 라이브러리 라고 소개하고 있습니다. 또한 다음과 같은 장점을 제공하고 있습니다.

  • Automaitc critical CSS : 현재 페이지에 렌더링된 css 만 자동적으로 삽입해주는 것을 말합니다.
  • No Class name bugs : 유니크한 class name을 생성하여 class name 중복으로 인한 버그를 막아 줍니다.
  • Easier deletion of CSS : 손쉽게 삭제할 수 있다는 말은, class name이 어디서 쓰였는지 찾을 필요없이 styled-components로 구성된 컴포넌트를 삭제하면 손쉽게 사용되지 않은 부분을 찾을 수 있습니다.
  • Simple dynamic styling : class name의 조작없이 손쉽게 동적 스타일링을 구현할 수 있습니다.
  • Painless maintenance : 컴포넌트 내에서 js로 생성이 가능하기 때문에 파일간에 의존성 없이 손쉽게 유지보수 가능합니다.
  • Automatic vender prefixing : post-css의 autoprefix 같은 기능

어떻게 동작 하는가?

출처: https://velog.io/@dev-mish-mash/CSS-in-JS-%EA%B0%80-%EC%A2%8B%EC%9D%80-%EC%9D%B4%EC%9C%A0

 

 css-module과 비슷하게 hashing 과정을 거쳐 유니크한 class name을 생성합니다. 컨셉 자체는 css-module과 유사하지만 결국엔 js로 css를 생성한다는 것이 가장 큰 차이입니다. 그 차이를 이해하려면 결국 브라우저의 동작 원리를 알아야 합니다. 브라우저에 대한 내용을 간단히 정리한 다음 글에서 확인 할 수 있습니다. 

2022.03.27 - [javascript/📖 study] - [8주차 스터디]38장-브라우저 렌더링

출처: https://poiemaweb.com/js-browser

 다음 그림 처럼 브라우저는 렌더링 과정에서 html, css 파일을 파싱하여 DOM과 CSSOM을 생성하고 javascript runtime에 dom api를 이용하여 dom을 제어 합니다. 즉, css-module 방식은 build-time에 생성되고 styled-components의 경우 javascript의 runtime에 style 태그를 생성한다. 

js를 이용해 컴포넌트를 생성하는 방식인 SPA의 경우 초기 자바스크립트의 번들링 사이즈가 클수록 초기 로딩 속도가 느려진다는 문제점이 있다 즉 CSS-in-JS 방식의 경우 runtime-overhead의 문제점이 있다.

 

+) 추가적인 지식

 - runtime-overhead 문제를 해결하기위해 최근에는 Zero-Time CSS, Near Zero CSS와 같은 컨셉을 들고 나오는 중입니다. (linaria, stitches.js 같은 라이브러리가 있다고 함)

 - 또는 CSS-in-JS 방식에서 아예 벗어나 필요한 수치를 입력을 해두면 필요한 CSS를 자동으로 생성을 해두는 주문형(on-demand) 패러다임 또한 등장 (Atomic CSS 라고 하며, tailwindCSS가 대표적)

  정리 및 의견

 이런 동작원리나 이론들을 공부할 때 접했었지만 이내 까먹고 사용방법에만 집중했던 것 같다. 확실히 정리하고 알고 써야계속해서 바뀌고 있는 CSS 트렌드를 따라 갈 수 있을 것 같다. 간단히 정리해 보면 

  • CSS-Module : 별도의 css 컴파일러를 사용해 고유한 class name을 만들어 냄 
  • CSS-in-JS : js를 이용하여 run-time에  style 태그를 만들어 적용됨, 유니크한 class name값 생성

 개인적으로 두 방식을 모두 사용하여 프로젝트를 진행 해보았을 때 확실히 CSS-in-JS 방식이 개발하기는 편한 것 같다. 특히 state나 props에 따라 color 값을 따로 주거나 다른 style을 적용해야할 때 taged template 문법을 쓰는 styled-components 같은 경우  ${props = > ~~~} 이런식으로 props를 공유 가능하고 타입 까지 지정할 수 있기 때문에 논리적인 에러의 위험도 적다 반면 CSS-Module을 쓰면 styles={state ? "active" : ''} 이런식으로 inline 방식의 분기 처리를 해줬었다. 

 

 개발자 입장에서 편한 방식을 채택하는 것도 중요하지만 프로젝트의 특성에 맞는 방식을 선택하는 것이 더 중요할 것  같다. 물론 규모가 작은 프로젝트의 경우 큰 차이 없을 거라고 보지만 사용자의 인터렉션이 많거나 css의 비중이 크다면 빌드 시에 모든 css가 미리 로딩되어 있는 css-module 방식이 더욱 효과 적일 수도 있다.

 


참조

https://velog.io/@teo/css-history-1

https://d0gf00t.tistory.com/22

https://glenmaddern.com/articles/css-modules

https://s-core.co.kr/insight/view/%EC%9B%B9-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EA%B4%80%EB%A6%AC-css-in-js-vs-css-in-css/

https://velog.io/@dev-mish-mash/CSS-in-JS-%EA%B0%80-%EC%A2%8B%EC%9D%80-%EC%9D%B4%EC%9C%A0

https://so-so.dev/web/css-in-js-whats-the-defference/

댓글