CSS-in-JS 와 Zero Runtime(feat:kakao style 기술블로그)

5_wintaek·2025년 6월 9일

들어가는 글

프론트엔드팀의 디자인 시스템 글을 읽다가 꼬리의 꼬리를 무는 질문이 머릿속에 마주잡이로 생겨 글을 쓰게 되었습니다.

CSS-in-JS는 어떤걸까 ? 스타일 캐싱이 무슨 뜻이며 SSR의 어려움이 정확히 무엇을 말하는걸까 ?
제로 런타임은 무슨 방식인지 어떤 차이점이 있을까 ? 라는저의 질문에 답을 하기 위해 글을 한번 풀어서 써보겠습니다.

카카오 프론트엔드 팀의 디자인 시스템 재구축기 글

카카오 프론트엔드팀 UI/UX를 통합적으로 관리할 수 있는 디자인 시스템의 재구축 글 중 목표 설정의 있는 글입니다.

프로젝트 구조 정의: 모듈화를 통해 유지보수와 확장성을 개선.
스타일링 방식 선택: 런타임 CSS-in-JS 방식의 한계를 고려하여 제로 런타임 스타일링 방식으로 전환.
CSS 변수 기반 디자인 토큰과 테마 관리: 테마와 디자인 토큰 관리의 효율성 개선.
상품 카드 컴포넌트 일원화: 중복 코드를 줄이고 일관성을 높이기 위한 시스템 통합.

자.. 여기서 제가 메인으로 궁금한 CSS-in-JSS 는 무엇이고 제로 런타임과 무엇이 차이가 있는지 간단하게 설명을 해보겠습니다.

CSS-in-JS 는 무엇일까 ?

CSS-in-JS는 JavaScript 코드 안에서 CSS를 작성하는 방식입니다.

우리가 흔히 아는 styled-components 입력 방식을 예시로 들겠습니다.

import styled from 'styled-components';

const Button = styled.button`
  background-color: blue;
  color: white;
`;

장점은 어떤게 있나 ?!

  • JavaScript 코드 안에 CSS를 정의 → 모듈화, props 기반 스타일링 가능
  • 컴포넌트 단위로 스타일이 분리됨
  • 런타임에 CSS가 생성됨

단점들은 어떤게 있나?

styled-components 같은 라이브러리는 컴포넌트가 렌더링될 때 동적으로 CSS를 생성합니다.

만약 같은 스타일의 버튼이 수십 개 있다면, 스타일도 매번 확인하거나 다시 생성합니다.

  • 성능 문제: 앱 실행 중, 스타일을 동적으로 생성 → 초기 로딩 속도 저하
  • 디버깅 불편: CSS가 동적으로 생성되므로 클래스명이 직관적이지 않음.
    • 여기서 말하는 클래스명이 직관적이지 않다! 라는거는 개발자 도구 시점에서 styeld-component는 class 명이 sc-kvZOFW 같은 무작위 해시 클래스명이 나온다는 뜻입니다.
  • 스타일 캐싱과 SSR 어려움: 서버사이드 렌더링 시 추가 처리 필요

여기서 말하는 스타일 캐싱이란 이미 생성한 스타일을 브라우저나 서버에서 재사용해 중복 생성을 피하는 기법입니다.

그리고 여기서 말하는 SSR의 어려움을 정확히 뜻하는 말은
서버 사이드 렌더링(SSR)은 서버에서 HTML을 먼저 만들어 클라이언트로 보내는 방식인데 CSS-in-JSS를 사용하려면, 예를들어 styled-component는 ServerStyleSheet 를 사용해야합니다.

root가 되는 app.tsx 같은 페이지에서 커스터마이징이 필수이고 설정을 하지 않게 된다면 스타일이 서버에서 적용되지 않거나, FOUC(스타일 깜빡임) 발생합니다. 또한 CSS 캐시나 최적화 처리를 수동으로 해야 합니다.

제로 런타임 CSS-in-JS란?

Zero Runtime CSS-in-JS는 브라우저에서 CSS를 생성하지 않는 방식입니다. 즉, 빌드 타임에 CSS를 생성합니다.

HTML에 <link rel="stylesheet" href="styles.css" />처럼 자동 포함이 됩니다. 또한 SSR 중 별도의 스타일 추출 없이 그냥 HTML+CSS만 렌더링하면 됨

대표로 Vanilla Extract, Linaria 가 있습니다.

제로 런타임의 특징은 어떤게 있을까?

  1. CSS 변수 기반 스타일링 지원: CSS 변수를 활용해 디자인 토큰을 유연하게 관리할 수 있는지.
  2. 유연한 테마 관리: 다크 테마, 라이트 테마 등 다양한 테마를 효율적으로 지원할 수 있는지.
  3. 타입 안전성 및 휴먼 에러 방지: 오타로 인한 오류를 방지하고, 존재하지 않는 토큰 사용 시 개발자가 즉각 피드백을 받을 수 있는지.
  4. 가벼운 번들 사이즈: 서비스 번들 크기를 최소화할 수 있는지.
  5. 제로 런타임 지원: 스타일링은 빌드 시 생성되어야 하며, 런타임이 필요한 경우에도 최소한의 크기로 RSC와 호환 가능한지.

Vanilla Extract:

  • 장점:
    • 타입 안전성과 디자인 시스템 구축에 필요한 유틸리티 제공 (e.g., styleVariants, recipes, sprinkles, createThemeContract)
    • 기본적으로 빌드 시 스타일을 추출하며, 필요시 런타임 스타일도 제공
    • 프레임워크에 구애받지 않음
    • 런타임 스타일 사용 시 가벼운 번들 크기 (GZIP 기준 718B)
  • 단점:
    • Tagged Template Literals 미지원
    • 복잡한 CSS 선택자 사용 제한
    • 초기 학습 러닝 커브 존재

Linaria:

  • 장점:
    • Emotion과 유사한 사용법으로 러닝 커브가 낮음
    • 스타일을 빌드 시 생성하므로 RSC와 호환 가능
  • 단점:
    • Vanilla Extract와 비교해 고도화된 유틸리티 미제공

Zero-runtime은 디버깅이 용이한가?

styeld-component와 다르게 빌드 시 클래스명을 정적이고 직관적으로 생성합니다.

build전

const button = style({
  backgroundColor: 'blue',
})

build후

.button_1a2b3c {
  background-color: blue;
}

마무리 글

꼬리의 꼬리를 무는 저의 머릿속에 남아있던 질문들을 한번 정리해보았습니다. 무심코 쓰던 styled-component가 CSS-in-JS 방식인것 또한 알았고 CSS-in-JS 이 무엇인지 머릿속에 있었습니다만! 정말 정확하게 설명하라하면 입으로 대답하기 힘들것같아 한번 글로 다시 써보며 리마인드를 시켜보았습니다. 간단하게 CSS-in-JS 와 Zero-runtime 의 차이점이라고 보면 될 것 같습니다.

Ref

카카오 프론트엔드팀의 기술블로그

profile
물음표를 느낌표로 바꾸는 개발자

0개의 댓글