CSS-in-JS
CSS-in-JS는 많은 인기를 얻었습니다. 현재 시점까지도.. 그 이유는 당연 React 때문일 겁니다.
그래서 우리는 Emotion, styled-componets 를 사용해본 경험이 있을 겁니다.
그럼 우린 왜 사용하고 있었을까요?
CSS모듈도 지역 스코프 스타일을 제공하지만, CSS-in-JS는 기본적으로 스타일을 지역 스코프로 지정합니다.
이로써, 클래스 이름이 충돌나는 일은 없을 것이고, 어떤 CSS가 적용되었는지 우리는 한 눈아 알기 쉬울겁니다.
또한, CSS에서 JS 문법을 사용할 수 있어서 props, state 등을 사용하여 중복된 코드를 줄일 수 있고, 컴포넌트 파일에 관련 코드들을 함께 작성이 가능하게 됩니다.
모든 기술에는 장점만 존재하지 않습니다. 단점은 무엇일까요?
CSS-in-JS는 런타임이 발생합니다. CSS-in-JS는 보통 두가지 방법으로 런타임에 스타일을 추가하는데요.
1. <head> 태그에 <style> 태그를 만들어서 삽입하는 방법
2. CSSStyleSheet.insertRule 을 사용하여 CSSOM에 직접 삽입하는 방법
위 방법들로 스타일을 런타임에 생성하게 되면, js파일을 실행하면서 style을 생성하고 style 생성 규모가 크고 빈번하게 될 수록 성능이 저하 될 수 있습니다.
또한 Next.js 같은 SSR 환경에서 동적스타일 사용으로 인한 Hydration error를 자주 확인 할 수 있습니다.
그래서 위 같은 현상을 해결하고자, Zero-runtime CSS 가 등장했습니다.
Zero-runtime CSS ( Vanilla-extract )
말 그대로 런타임이 없는 CSS입니다. 장점은 아래와 같습니다.
1. 빌드타임에 CSS를 생성하므로, JS에서 CSS로 파싱하는 과정이 없기 때문에 빠른 렌더링 성능을 볼 수 있습니다.
2. 빌드타임에 생성되므로, Hydration 과정에서 스타일 불일치가 생기지 않으므로 위에서 언급한 Hydration Error 가 발생하지 않습니다.
3. Tailwind 처럼 Atomic CSS를 구성할 수 있습니다.
기술에는 장점만 존재하지 않죠. 제 생각에 단점은 이렇습니다.
1. 빌드타임에 생성하므로, props 기반 동적 스타일 적용이 어렵습니다.
기존 styled-componets 에서는 아래 코드처럼 props를 활용했다면,
const Button = styled.button`
background: ${(props) => (props.primary ? "blue" : "gray")};
`;
Zero-runtime CSS에서는 이를 미리 분기 처리해놔야 합니다.
import { style } from '@vanilla-extract/css';
export const primaryButton = style({ backgroundColor: 'blue' });
export const secondaryButton = style({ backgroundColor: 'gray' });
2. build 타임에 css파일로 변환되고 head 태그에 삽입되기 때문에 bundle 설정이 필수입니다.
( 하지만, 거의 모든 번들러를 지원 )
3. develop 환경에서 핫리로드가 원활하지 않을 수 있습니다.
결론
상황에 따라 어떤 기술을 쓰던지, 장단점에 맞춰서 어느곳에 포커싱을 할 지 생각하고 사용하자.