[F-Lab 모각코 챌린지 42일차] CSS-in-JS

Nami·2023년 7월 12일
0

66일 포스팅 챌린지

목록 보기
42/66
post-thumbnail

CSS-in-JS

  • 자바스크립트 또는 타입스크립트 코드에서 직접 CSS를 작성하여 리액트 컴포넌트 스타일을 지정하는 것.
  • 2014년 페이스북 개발자인 Christopher Chedeau aka Vjeux가 처음 소개했다. 그가 설명한 것을 토대로 표를 정리해보았다.
전통적인 CSS의 어려움CSS-in-JS가 해결해줄 수 있는 것
글로벌 공간에 선언된 이름의 명명 규칙 필요class명이 빌드 타임에 유니크하게 변경되기 때문에 별도의 명명 규칙이 필요치 않음
CSS 파일 간의 의존성으로 관리가 필요CSS가 컴포넌트 단위로 추상화되기 때문에 CSS(모듈)간 의존성 고려가 필요없음
사용되지 않는 스타일을 쉽게 구분할 수 없기 때문에 미사용 코드 검출(주로) 컴포넌트와 CSS가 동일한 파일 내에 존재하기 때문에 수정 및 삭제가 용이함
클래스 이름의 최소화 (Minification)빌드 타임에 짧은 길이의 고유한 클래스를 자동으로 생성하여 문서의 크기를 줄여줌
상수 공유 (Sharing Constants)CSS 코드가 JS에 작성되므로 컴포넌트 상태 공유 가능
CSS roading 우선 순위 이슈CSS가 컴포넌트 스코프로 적용되므로 우선순위 문제가 없음
CSS와 JS의 상속에 따른 격리 필요 이슈CSS가 컴포넌트에 격리되어 있어 상속 문제가 없음

CSS-in-JS 점유율 (2021년 기준)

Styled Components

  • 리액트(React) 기반의 CSS-in-JS 라이브러리
  • 컴포넌트와 스타일을 함께 정의하고 결합하는 기능을 제공
  • 스타일 컴포넌트로 알려진 패턴을 사용하여 컴포넌트에 스타일을 적용할 수 있도록 도와준다.

Emotion

  • JavaScript 기반의 CSS-in-JS 라이브러리
  • 리액트(React) 및 다른 프레임워크와 함께 사용할 수 있습니다.
  • Emotion은 컴포넌트와 스타일을 결합하여 동적이고 재사용 가능한 스타일링을 가능하게 해주는 강력한 기능을 제공함.

결국 CSS-in-JS는 비슷한듯 장점은. 뭘 선택하냐에 따라.. 사용하는 문법 및 API, CSS 추출 방식, 성능 최적화 등에서 일부 차이만 있는듯

Why We're Breaking Up with CSS-in-JS

SSR이 필요한 리액트(React) 앱을 구축한다면, 대부분 Next.js를 사용한다. Next.js는 초기부터 Styled JSX라는 자체 CSS 라이브러리를 기본으로 포함했다. 9.2 버전부터 CSS Module을 기본으로 지원하기 시작했다. 왜일까?

우리는 일부 CSS를 기반으로 하는 기존 스타일과 디자인 시스템을 재사용해야 한다는 필요성이 있음을 발견했습니다. 일반적으로, 이것은 next-css 플러그인을 설치해야 한다는 것을 의미합니다. 우리는 Next.js 사용자의 약 50%가 이 플러그인을 추가한다는 것을 발견했습니다.

찾아보니 이처럼 CSS-in-JS를 썼다가 다시 기존 방식으로 돌아가는 경우가 많았다. 이유를 알아보자.

1. 런타임 오버헤드를 더한다.

  • 컴포넌트가 렌더링될 때 CSS-in-JS 라이브러리는 document에 삽입할 수 있는 일반 CSS로 스타일을 직렬화해야한다.
  • 추가적인 CPU를 차지한다.
  • JavaScript를 사용하여 스타일을 처리하므로 런타임에서 스타일을 해석하고 적용

2. 번들 크기를 늘린다.

  • 사이트를 방문하는 각 사용자는 CSS-in-JS 라이브러리용 자바스크립트를 다운로드해야한다.
  • Emotion은 압축되었을 때 7.9kB이고 styled-components는 12.7kB
  • 두 라이브러리 모두 거대하지는 않지만 모두 추가된다.
    (비교를 위해 설명하자면 react + react-dom은 44.5KB)

3. React DevTools를 어지럽힌다.

  • css 프로퍼티를 사용하는 각 요소에 대해 Emotion은 <EmotionCssPropInternal><Insertion> 컴포넌트를 렌더링한다.

  • 많은 요소에서 css 프로퍼티를 사용하는 경우 아래 그림처럼 Emotion의 내부 컴포넌트가 React DevTools를 어지럽힐 수 있다.

  1. CSS 규칙을 자주 삽입하면 브라우저에서 더 많은 추가 작업을 수행해야한다.

    동시(concurrent) 렌더링에서, 리액트는 렌더링 사이에 브라우저에 양보할 수 있습니다. 컴포넌트에 새 규칙을 삽입하면 리액트가 생성되고, 브라우저는 해당 규칙이 기존 트리에 적용되어 있는지를 다시 확인합니다. 이후 스타일 규칙을 다시 계산하게 됩니다. 그런 다음 리액트는 다음 컴포넌트를 렌더링 하고, 해당 컴포넌트는 새로운 규칙을 발견하고 다시 실행하기를 반복합니다. - 리액트 코어 팀 멤버이자 리액트 훅의 디자이너였던 Sebastian Markbåge

    CSS-in-JS 라이브러리는 컴포넌트가 렌더링 될 때 새로운 스타일 규칙을 삽입해 작동하며 이는 근본적으로 성능에 좋지 않다.

  2. CSS-in-JS를 사용하면 특히 SSR 및 혹은 또는 컴포넌트 라이브러리를 사용할 때 잘못될 수 있는 부분이 훨씬 더 많다.

그래서 다시 CSS Modules

CSS 모듈은 CSS를 모듈화 하여 사용하는 방식.

  • CSS 클래스를 만들면 자동으로 고유한 클래스명을 만들어서 Scope를 지역적으로 제한한다.
  • 모듈화된 CSS를 번들러(Webpack 등)로 불러오면 사용자가 정의했던 클래스명과 고유한 클래스명으로 이뤄진 객체가 반환된다.
  • 많은 개발자들이 기존의 CSS 방식, 또는 CSS Module과 같은 새로운 기술을 병행해서 사용한다. 테마 등의 공통 영역, 일부 유틸리티 등은 CSS-in-JS로 쉽게 해결이 어렵기 때문.
  • CSS Module 역시 CSS-in-JS의 장점처럼 컴포넌트 단위로 추상화가 가능
  • 프로젝트 소스 안에 CSS 클래스 이름이 중복되어도 새로운 이름이 입혀져 중복 및 관리의 위험성이 적고 CSS 네이밍 규칙이 간소화 된다.
  • 한 곳에서 모든 것을 작성하지 않아 별도로 많은 CSS 파일을 만들어 관리해야되는 단점이 있음.
  • 주로 React와 함께 사용됨.

styled-componentsCSS Module
렌더링(Rendering)29ms28ms
스크립팅(Scripting)483ms265ms
그리기(Painting)7ms9ms

성능 측정을 위해 styled-components 문서를 참고하여 다음과 같은 페이지를 만들어 보고, 같은 페이지를 CSS Module, styled-components로 각각 구현, 비교 결과.

자세한 것은 이 곳 참조

동일 화면이기 때문에, Rendering과 Painting 소요시간은 크게 다르지 않은 반면, Scriping은 2배 가까운 성능 차이를 보인다. CSS-in-JS 방식은 CSS 코드를 변환하는 과정이 필요하기 때문.
CSS Module 역시 CSS 코드가 JS 번들에 포함되어 있지만, 온전한 CSS 형태를 유지하고 있기에 styled-components에 비해 짧은 시간에 화면을 그릴 수 있다.

하지만, CSS 모듈의 주요 단점은 여전히 일반 CSS이며, 일반 CSS에는 DX를 개선하고 코드 중복을 줄이는 기능이 아직은 부족하다. 또한 일반적인 스타일을 적용하는 클래스를 직접 만들어야 한다.

그래서들 또 많이 쓰는 것이..

Tailwind

유틸리티 클래스를 제공하는 가장 인기 있는 CSS 프레임워크.

디자인 요소가 중요한 서비스는, 디자이너와의 협업이 중시되는데 특히 테마같은 디자인 시스템이 구축되어 있지 않다면 효율이 떨어지고 공통으로 사용하는 유틸리티 코드를 따로 만들어야한다. 필요에 따라 유틸리티 코드를 넣지만 매번 추가해야하는 번거로움이 있음.

과거에 많이 사용한 Bootstrap과 비슷하지만 확장성에 큰 차이가 있고 사용하지 않는 요소까지 포함되어있어 번들이 커짐. 테일윈드는 그런 단점이 전부 해결됨.

디자인 시스템 구축 가능, 빌드 시 사용하지 않는 클래스는 최소화된다.

<style>
  .button {
    padding: 1rem 2rem;
    margin: 0;
    height: 3rem;
    border: 1px solid black;
    border-radius: 0.5rem;
    background: white;
  }
</style>

<button className={styles.button}>버튼</button>

테일윈드 사용시

<button className="px-8 py-4 m-0 h-12 border border-solid rounded-lg bg-white">버튼</button>

하지만 단점도 있다.

  • tailwind는 rem 단위가 기본이기 때문에, px 단위 서비스라면 기본값을 바꿔줘야 함(ex. p-12 => padding: 12px).
  • 빌드 타임에 모두 생성되므로 동적 변수를 사용할 수 없는 문제가 있다(ex. p-[paddingTopProp])
  • 애니메이션(animation)과 트랜지션(transition) 사용에 제약이 있다. animate-spin과 같이 미리 만들어진 애니메이션만 제공하기 때문에 필요한 애니메이션을 추가해야 함.

결론

  • 모든 기술은 장단 점이 있다. 궁극적으로 이런 장단점을 평가한 다음 해당 기술이 사용 사례에 적합한지 여부에 관해 결정을 내리는 것은 개발자의 몫이다.
  • 다들 사용한다고 무작정 도입하는 우를 범하지 말아야 한다. 아무리 많은 사람이 사용하고 검증된 기술이라 할지라도 서비스의 목적과 맞지 않는다면, 소용이 없다.
  • 마크업과 JS를 모두 작성하는 소규모 팀에서는 CSS-in-JS 방식이 유용하다.
  • 규모 있고 안정적인 서비스를 구축한다면 CSS Module이나 tailwind 같은 유틸리티 CSS를 추천.

참조 ✅

0개의 댓글