🚀   새로운 블로그로 이전했습니다.

CSS-in-JS 라이브러리들에 대한 고찰

2022.02.11
11분

들어가면서

이 글의 목적

  • 다양한 CSS-in-JS 라이브러리가 있는데 이들은 어떤 차이점이 있을까?
  • 더 나아가 어떤 상황에서 어떤 라이브러리를 사용하면 좋을까?

CSS-in-JS가 대세인 이유

  • 중복되지 않는 class 이름을 고려할 필요가 없다.
  • JS 코드와 CSS가 상태 값을 공유할 수 있다.
  • 컴포넌트와 스타일 코드를 쉽게 오갈 수 있다.
  • 자동으로 vendor-prefix을 붙여준다.

개발 친화적 🌻  DX ( Developer Experience )


2020, 2021에 들어서서 많은 CSS-in-JS 라이브러리가 등장했다.

css-in-js의 동작 방식은 크게 runtime, zero-runtime 으로 나눠진다.
runtime이 반드시 성능저하를 발생시키진 않고 프로젝트 규모와 상황에 따라 달라질 수 있음을 염두하고 살펴보도록 하자.


runtime

javascript runtime에서 필요한 CSS를 동적으로 만들어 적용한다.

대표적으로 잘 알려진 styled-component, emotion 이 있다.

아래 styled-components로 만든 예시를 살펴보자.
버튼의 상태가 바뀌면서 style 코드가 동적으로 생성되어 삽입되는 것을 볼 수 있다.

  • 개발모드에서는 <style> 태그에 style을 추가하는 방식을 사용한다.

    • 디버깅에 이점이 있다고 한다.
  • 배포 모드에서는 stylesSheet을 CSSStylesSheet.insertRule 통해 바로CSSOM에 주입한다.

    • 성능상의 이짐이 있다고 한다.

css-loader가 필요 없다.

  • css파일을 생성하지 않기에 webpack에서 css-loader가 필요 없다.

런타임 오버헤드가 발생할 수 있다.

  • 런타임에서 동적으로 스타일을 생성하기에 스타일이 수시로 변경된다면...
  • ex) 스크롤, 드래그 앤 드랍 관련 복잡한 에니메이션

styled-components 과 emotion의 차이를 알고 싶다면?


zero-runtime

런타임에 css를 생성하지않으면서 페이지를 더 빨리 로드할 수 있다.
JS 번들에서 styles코드를 모두 실행되어야 페이지가 로드된다.

runtime에서 스타일이 생성되지 않는다.

  • props 변화에 따른 동적인 스타일은 css 변수를 통해 적용한다.

빌드 타임에 css를 생성해야기에 webpack 설정을 해야 한다.

  • React CRA을 사용한다면 eject해서 webpack 설정해야 하는데 굉장히 번거롭다.
  • runtime에서 css polyfill를 사용할 수 없어 브라우저 버전 이슈가 있을 수 있다.

첫 load는 빠르지만, 첫 paint는 느릴 수 있다.

css styles까지 모두 로드가 되어야 첫 paint를 시작된다.
반면 runtime에서는 style를 로드하면서 첫 paint를 시작할 수 있다. ( 로딩화면 )


대표적인 라이브러리

  • linaria
    • styled-component 문법 그대로 사용해서 러닝커브가 없을 것 같다.
    • styled-components와 속도 비교
    • mini-css-extract-plugin에 의해 critical css를 판단할 수 없는 경우 linaria의 collect를 사용가능하다.
  • vanilla-extract
    • 사실상 typescript로 css를 작성하는 라이브러리. (.css.ts)
    • css-module와 거의 흡사하다.
    • tagged template literals를 지원하지 않는다.
    • 현재 굉장히 높은 만족도와 관심을 받고 있다.

critical CSS

  • 초기 화면에서 필요한 CSS이다.
  • critical CSS 추출과 runtime CSS 생성은 trade-off 관계를 갖고 있다.
  • SSR에서 중요한 쟁점이다. 사전에 CSS 추출을 할 것인가?

near-zero-runtime (stitches)

stitches

  • SSR 환경에서도 잘 동작이 되도록 세팅이 되었다.
  • runtime overhead와 zero-runtime의 제약을 해결 ⇒ 빠르다 > benchmarks

런타임에서 각각의 CSS 프로퍼티가 Atomic CSS처럼 적용된다.

  • 반복되는 style을 atomics class로 변환하여 class를 재사용한다.
  • 불필요한 런타임에서의 props interpolations를 줄인다.
const StitchesButton1 = styled('button', {
  color: 'red',
  fontSize: 24,
});

const StitchesButton2 = styled('button', {
  color: 'red',
  fontSize: 24,
});

하지만 style 순서가 바뀌면 재사용할 수 없다.

const StitchesButton2 = styled('button', {
  fontSize: 24,
  color: 'red',
});

참고


emotion과 같이 CSSStylesSheet.insertRule 을 사용하여 CSSOM에 직접 삽입한다.

사전에 정의한 variants에 의해서 runtime 스타일링이 진행된다.

  • 공식문서가 굉장히 친절하고 친절하게 되어 있어서

최고의 개발경험 제공

  • 사전에 정의한 테마 변수, variants 자동완성 제공

그리고 한계점...

  • css style extraction이 되지 않는다.
  • tagged template literals를 지원하지 않는다.
    Object syntax을 사용하는 이유는 keep the bundle size to a minimum이라고 한다.

Atomic CSS


맺으면서

CSS-in-JS 사용에 고려할 사항

  • runtime overhead가 발생할 될 서비스인가?
    • 없다면 기존 runtime CSS-in-JS를 써도 전혀 문제가 없을 것이다.
  • 직접 스타일을 작성하는가? 아니면 CSS 소스를 사용하는가?
    • tagged template literals를 지원하지 않는다면 CSS 소스를 옮기기 굉장히 불편할 것이다.
    • 따라서 stitches와 vanilla-extract의 도입은 CSS 변환의 수고스러움을 감수해야 할 것이다.
  • SSR인가 CSR인가?
    • SSR를 설정하기 불편한 것이 있고 Critical CSS 최적화된 것이 있다.
    • CSR는 runtime stylesheets, SSR는 static CSS에 이점을 갖는다. 참고

멋진 개발팀들에서는 어떻게 라이브러리를 선택하는지 살펴보면 큰 도움이 될 것이다.


개인적으로 생각했을 땐 결국 개발 친화적 🌻이 제일 중요한 이슈인 것 같다.

  • 개발팀에서 편하게 디자인 시스템을 구축할 수 있는가?
  • 개발 리소스 비용이 크기 때문...

프론트엔드의 흐름, build-time?

현대 프론트엔드의 작업흐름이 build-time에서 최적화가 이뤄지고 있다.

  • 최신 CSS-in-JS 라이브러리들이 build-time(zero-runtime)으로 개발되고 있다.
  • TypeScript를 통해서 build-time 및 run-time 이전에 type 체크, code IntelliSense를 제공해준다.
  • webpack, babel를 통해서 다양한 브라우저에서 동작하는 앱을 만든다.
  • Next.js가 각광을 받으면서 CSR에서 SSR로 웹이 개발되어 가고 있다.
  • React, Vue를 넘어서 현재 Svelte가 많은 사람들의 주목을 받고 있다.
    • run-time에서 Virtual DOM를 통해 비교하여 변경사항을 반영하는 것이 아닌,
      build-time에서 어느 부분이 변경될지 파악하고 DOM을 업데이트하는 효율적인 명령 코드로 변환하여 사용한다.

javascript → typescript → build-time → compiler → c++

https://c.tenor.com/pl54bxCr1x8AAAAM/wow-omg.gif

대학교 1학년 때부터 세뇌받듯이 들었던 말이 프로그래밍 언어의 근본은 C++ 이다.
javascript, python 같은 스크립트 언어는 감히 고개를 내밀지도 못했다.

뛰어난 개발자들이 프론트엔드 생태계에 들어오면서 컴파일 시점에서의 최적화가 고도화 된 것 같다.

카카오의 좋은 문화 중 하나가 복잡할 수록 본질로 돌아가라이다.
그렇게 복잡한 웹 프론트엔드 생태계가 근본(?)을 되찾아가고 있지 않나 생각이 들었다.


참고 자료