전통적인 CSS의 어려움 | CSS-in-JS가 해결해줄 수 있는 것 |
---|---|
글로벌 공간에 선언된 이름의 명명 규칙 필요 | class명이 빌드 타임에 유니크하게 변경되기 때문에 별도의 명명 규칙이 필요치 않음 |
CSS 파일 간의 의존성으로 관리가 필요 | CSS가 컴포넌트 단위로 추상화되기 때문에 CSS(모듈)간 의존성 고려가 필요없음 |
사용되지 않는 스타일을 쉽게 구분할 수 없기 때문에 미사용 코드 검출 | (주로) 컴포넌트와 CSS가 동일한 파일 내에 존재하기 때문에 수정 및 삭제가 용이함 |
클래스 이름의 최소화 (Minification) | 빌드 타임에 짧은 길이의 고유한 클래스를 자동으로 생성하여 문서의 크기를 줄여줌 |
상수 공유 (Sharing Constants) | CSS 코드가 JS에 작성되므로 컴포넌트 상태 공유 가능 |
CSS roading 우선 순위 이슈 | CSS가 컴포넌트 스코프로 적용되므로 우선순위 문제가 없음 |
CSS와 JS의 상속에 따른 격리 필요 이슈 | CSS가 컴포넌트에 격리되어 있어 상속 문제가 없음 |
CSS-in-JS 점유율 (2021년 기준)
결국 CSS-in-JS는 비슷한듯 장점은. 뭘 선택하냐에 따라.. 사용하는 문법 및 API, CSS 추출 방식, 성능 최적화 등에서 일부 차이만 있는듯
SSR이 필요한 리액트(React) 앱을 구축한다면, 대부분 Next.js를 사용한다. Next.js는 초기부터 Styled JSX라는 자체 CSS 라이브러리를 기본으로 포함했다. 9.2 버전부터 CSS Module을 기본으로 지원하기 시작했다. 왜일까?
우리는 일부 CSS를 기반으로 하는 기존 스타일과 디자인 시스템을 재사용해야 한다는 필요성이 있음을 발견했습니다. 일반적으로, 이것은 next-css 플러그인을 설치해야 한다는 것을 의미합니다. 우리는 Next.js 사용자의 약 50%가 이 플러그인을 추가한다는 것을 발견했습니다.
- Next.js blog, Next.js 9.1 에서 발췌 (https://nextjs.org/blog/next-9-1) -
찾아보니 이처럼 CSS-in-JS를 썼다가 다시 기존 방식으로 돌아가는 경우가 많았다. 이유를 알아보자.
1. 런타임 오버헤드를 더한다.
2. 번들 크기를 늘린다.
3. React DevTools를 어지럽힌다.
css 프로퍼티를 사용하는 각 요소에 대해 Emotion은 <EmotionCssPropInternal>
및 <Insertion>
컴포넌트를 렌더링한다.
많은 요소에서 css 프로퍼티를 사용하는 경우 아래 그림처럼 Emotion의 내부 컴포넌트가 React DevTools를 어지럽힐 수 있다.
CSS 규칙을 자주 삽입하면 브라우저에서 더 많은 추가 작업을 수행해야한다.
동시(concurrent) 렌더링에서, 리액트는 렌더링 사이에 브라우저에 양보할 수 있습니다. 컴포넌트에 새 규칙을 삽입하면 리액트가 생성되고, 브라우저는 해당 규칙이 기존 트리에 적용되어 있는지를 다시 확인합니다. 이후 스타일 규칙을 다시 계산하게 됩니다. 그런 다음 리액트는 다음 컴포넌트를 렌더링 하고, 해당 컴포넌트는 새로운 규칙을 발견하고 다시 실행하기를 반복합니다. - 리액트 코어 팀 멤버이자 리액트 훅의 디자이너였던 Sebastian Markbåge
CSS-in-JS 라이브러리는 컴포넌트가 렌더링 될 때 새로운 스타일 규칙을 삽입해 작동하며 이는 근본적으로 성능에 좋지 않다.
CSS-in-JS를 사용하면 특히 SSR 및 혹은 또는 컴포넌트 라이브러리를 사용할 때 잘못될 수 있는 부분이 훨씬 더 많다.
CSS 모듈은 CSS를 모듈화 하여 사용하는 방식.
styled-components | CSS Module | |
---|---|---|
렌더링(Rendering) | 29ms | 28ms |
스크립팅(Scripting) | 483ms | 265ms |
그리기(Painting) | 7ms | 9ms |
성능 측정을 위해 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를 개선하고 코드 중복을 줄이는 기능이 아직은 부족하다. 또한 일반적인 스타일을 적용하는 클래스를 직접 만들어야 한다.
그래서들 또 많이 쓰는 것이..
유틸리티 클래스를 제공하는 가장 인기 있는 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>
하지만 단점도 있다.
rem
단위가 기본이기 때문에, px
단위 서비스라면 기본값을 바꿔줘야 함(ex. p-12
=> padding: 12px
).p-[paddingTopProp]
)animate-spin
과 같이 미리 만들어진 애니메이션만 제공하기 때문에 필요한 애니메이션을 추가해야 함.