애드블럭 종료 후 사이트를 이용해 주세요.

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • PandaCSS vs Tailwind CSS vs Vanilla Extract 비교 분석
    Tip 2025. 5. 11. 19:45
    728x90
    반응형

    PandaCSS vs Tailwind CSS vs Vanilla Extract 비교 분석

     

    RSC(React Server Component)의 출시와 Next.js의 App Router 전환 뒤 전통적인 CSS-in-JS 방식의 한계로 더욱 각광받고 있는 Tailwind CSS, Vanilla Extract, PandaCSS를 비교해 보겠습니다.

    1. CSS-in-JS의 한계와 새로운 접근법의 필요성

    과거에는 styled-components나 Emotion과 같은 CSS-in-JS 런타임 스타일 생성 방식은 런타임에 JS로 스타일을 생성·주입하는 과정은 성능 오버헤드로 이어지고, SSR(서버사이드 렌더링) 환경에서는 추가 설정이 필요하거나 FOUC 문제가 발생하는 등의 단점이 명확했지만, 편리함이라는 장점이 너무 컸기에 폭넓게 사용되었습니다.

     

    하지만 Next.js 13의 App Router 도입 이후, 기존 CSS-in-JS 라이브러리들은 React 서버 컴포넌트(RSC)를 지원하지 못하는 문제가 대두되었는데, 이는 런타임에 CSS를 삽입하는 방식의 한계 때문이었습니다.

    이러한 이유로 빌드 타임에 CSS를 생성하여 런타임 오버헤드가 없는 접근법, 이른바 Zero-Runtime CSS-in-JS 방식이 주목받고 있습니다.

     

    CSS를 JS/TS 안에 작성하되 실제 CSS 파일은 빌드 시 생성하므로, 런타임에 별도 JS 처리 없이 브라우저가 일반 CSS처럼 적용할 수 있습니다.

    이렇게 하면 성능상 이점(스타일을 동적으로 계산/주입하지 않아 초기 로드 속도 향상)과 예측 가능성, 번들 크기 감소, SSR 간소화 등의 효과를 얻을 수 있습니다.

    이번에 소개하는 스타일링 도구들은 Zero-Runtime 접근법이나 Utility-First 철학 등을 바탕으로 개발자 경험과 성능을 개선하는 것에 중점을 둔 라이브러리들입니다.

    2. 철학 및 핵심 특성

    2.1 Tailwind CSS – Utility-First의 대명사

    Tailwind CSS는 단언컨대 현시점에 가장 핫한 스타일링 라이브러리고 할 수 있습니다.

    HTML 마크업 내에 바로 사용할 수 있는 낮은 수준의 유틸리티 클래스들로 UI를 구성하는 방식으로 동작합니다.

    Tailwind의 모토는 "현대적인 웹사이트를 HTML을 떠나지 않고 빠르게 구축한다."는 것으로, 개발자가 flex, pt-4, text-center, rotate-90 같은 작은 스타일 단위를 조합해 어떤 디자인이든 구현할 수 있게 합니다.

    별도의 CSS 파일을 작성하는 대신 미리 정의된 클래스를 활용함으로써, 클래스 네이밍에 신경 쓰지 않고 필요한 디자인을 빠르게 구현하는 개발 경험을 제공합니다.

     

    Tailwind는 모던 CSS 기능을 적극 활용하며 개발자 경험을 개선하는 데 집중합니다.

    예를 들어 반응형 디자인을 위해 클래스 앞에 sm:, md:, lg: 같은 브레이크포인트 프리픽스를 붙이거나, 다크 모드 테마를 위해 dark: 접두사를 사용할 수 있고, 최신 CSS 변수와 cascade layer까지 지원하는 등 현대적 CSS 기능을 도구에 녹여냈습니다.

    Tailwind 자체는 JS 런타임이 필요 없으며, 빌드 시 JIT(Just-In-Time) 컴파일러가 사용된 클래스에 한해서만 CSS를 생성하여 불필요한 CSS를 제거합니다.

    그 결과 사용된 스타일만 생성하는 효율적인 동작을 하며, 이는 Tailwind v3부터 도입되어 v4에 이르러 더욱 고도화된 방식입니다.

    2.2 Vanilla Extract – Zero-Runtime 스타일시트-in-TS

    Vanilla Extract는 타입스크립트로 스타일을 작성하고 빌드 타임에 정적 CSS로 추출하는 방식을 취한 라이브러리입니다.

    한마디로 런타임이 전혀 없는 TypeScript 기반 스타일시트라고 소개되며, 타입스크립트를 스타일 전처리기로 활용하여 로컬 범위 클래스와 CSS 변수, 테마 등을 타입 안전하게 정의하고, 빌드 시 실제 CSS 파일로 생성합니다.

    Sass나 Less처럼 정적 빌드 출력물을 만들지만, 개발 단계에서는 TS의 강타입 지원을 받아 편리함을 얻는 것이 특징입니다.

     

    Vanilla Extract의 철학은 "CSS의 모든 기능을 포기하지 않고도 타입 안정성과 유지보수성을 높이자."는 것입니다.

    실제로 CSS의 변수, 선택자, 의사 클래스, 미디어쿼리, 키프레임 등 플랫폼 기능을 모두 지원하여 개발자가 익숙한 CSS 개념을 그대로 사용할 수 있습니다.

    동시에 TS로 작성되므로 잘못된 스타일 프로퍼티나 토큰 이름에는 컴파일 타임 오류를 내주고, 스타일은 자동으로 지역화된 클래스로 만들어져 전역 충돌을 방지합니다.

    또한 테마 시스템을 내장하고 있어 다크/라이트 모드 등 여러 테마를 한 번에 지원할 수 있으며, 심지어 서로 다른 테마를 동시에 적용하는 것도 가능합니다.

    createTheme()로 디자인 토큰 계약을 정의하고 여러 테마를 생성하면, 타입스크립트가 유효한 토큰만 사용하도록 보장해 주므로 테마 교체 시 누락이나 오타를 방지합니다.

     

    요약하면 Vanilla Extract는 스타일을 코드로 관리하면서도 산출물은 정적 CSS이기에, 런타임 비용 없이 CSS-in-JS의 생산성과 CSS의 성능을 조화시킨 도구로 기존 styled-components나 emotion에 익숙한 사용자라면 가장 빨리 익숙해질 수 있는 라이브러리라고 할 수 있습니다.

    2.3 PandaCSS – Tailwind와 CSS-in-JS의 장점을 결합

    PandaCSS는 비교적 최신에 등장한 라이브러리로, Tailwind의 유틸리티 접근법과 Stitches 같은 CSS-in-JS의 Variants/토큰 시스템을 결합한 진화형 스타일 엔진입니다.

    Panda는 Chakra UI의 제작자 등이 참여하여 개발되었으며, Chakra UI, Vanilla Extract, Stitches, Tailwind CSS 등의 장점을 효과적으로 흡수했다고 평가됩니다.

    핵심 슬로건은 타입 안전성, Zero-Runtime, 멀티 variant 지원, RSC 호환으로 요약되며, 빌드 타임에 스타일을 생성하기 때문에 Panda 자체를 CSS-in-JS 라이브러리라고 부르는 것이 어색할 정도로 기존 CSS-in-JS와 다른 느낌입니다.

     

    PandaCSS의 철학은 "유틸리티 클래스의 성능을 CSS-in-JS의 사용성으로."라고 할 수 있을 것 같습니다.

    실제로 Panda는 Tailwind처럼 JIT 컴파일 방식을 도입하여 사용한 스타일만 뽑아주고, 중복된 스타일 선언은 한 번만 생성하므로 스타일시트가 불필요하게 비대해지지 않습니다.

    출력되는 CSS는 Tailwind와 유사하게 원자적 클래스들(@layer로 구성)이며, 디자인 토큰 값을 CSS 변수로 노출해 토큰 기반 디자인이 가능합니다. 

    또한 Panda는 Variants(레시피) 개념을 내장하여 컴포넌트의 상태나 종류에 따른 스타일 변이를 쉽게 정의할 수 있습니다. 

    예를 들어 cva() 함수를 사용해 버튼 컴포넌트의 기본 스타일과 success/warning 등의 variant 스타일을 한 곳에서 선언하고 조합할 수 있습니다. 

    개발자는 마치 Tailwind utility를 객체로 사용하는 듯한 ({ bg: 'red.500', p:4 } 형태) API나 JSX Style Prop( <Box bg="red.500" p="4">)을 활용할 수 있어, Tailwind 사용 경험이 있는 경우 비교적 자연스럽게 적응할 수 있습니다. 

    PandaCSS 역시 결과적으로 정적 CSS 파일을 생성하고 런타임 JS를 싣지 않으므로, 성능과 호환성 면에서 이점이 있습니다.

    3. 비교 분석

    개발 경험(DX), 협업 효율, 성능, 유지보수성 측면에서 세 도구를 비교하겠습니다.

    아래 표는 각 항목별로 세 도구의 특징을 요약한 것입니다.

    비교 항목/이름 Tailwind CSS Vanilla Extract PandaCSS
    개발 경험
    (자동완성 & 타입 안정성, 설정 복잡도 등)
    1. VS Code 확장으로 풍부한 클래스 자동완성 지원.

    2. 별도 타입체크는 없으나 잘못된 클래스는 빌드 시 무시됨 (오타 발견은 에디터에 의존).

    3. 설정은 tailwind.config.js로 테마/플러그인 구성 – 기본 설정으로 바로 사용 가능하지만, 커스텀 디자인 시스템 반영 시 학습 필요.
    1. TS로 스타일 작성 → TS 컴파일러가 프로퍼티/토큰 타입 체크를 수행하여 오류 사전 발견.

    2. 에디터에서 TS IntelliSense로 CSS 프로퍼티 자동완성 가능.

    3. 초기 설정으로 웹팩/Vite 등의 빌드 설정 필요 (공식 플러그인 제공).

    4. 한번 구축하면 일반 TS 모듈처럼 동작.
    1. JSX에서 유틸리티 사용 → Tailwind 유틸과 유사한 단축명과 토큰을 타입 안전하게 사용 (bg="blue.500" 등).

    2. TS가 제공하는 자동완성과 타입 체크 혜택.

    3. panda.config에서 디자인 토큰/유틸 정의 – Tailwind보다는 자유도가 높으나 초기 설계에 시간 필요.

    4. npx panda init으로 기본 설정 생성 지원.
    협업 효율
    (디자인 시스템 연동, 토큰 활용, 코드 구조화)
    1. Tailwind 테마 변수로 디자인 토큰 관리. 

    2. 토큰(예: 색상 팔레트)에 이름을 붙여 bg-primary 등의 커스텀 유틸 클래스 사용 가능.

    3. 다만 디자인 변경 시 클래스명 자체를 리팩토링하거나 테마 변수 업데이트 필요.

    4. 스타일이 HTML에 직접 존재하므로 디자이너가 클래스 의미를 바로 이해하기는 어려울 수 있으나, 팀 내 컨벤션으로 보완.

    5. 컴포넌트 단위로 스타일을 캡슐화하려면 클래스 조합을 컴포넌트 내부로 숨기는 패턴 필요.
    1. 디자인 토큰을 코드로 정의하여 일관성 보장.

    2. createThemeContract로 색상/폰트/공간 등의 토큰 구조를 정의하고 사용하므로 디자인 시스템과 1:1 매핑 용이.

    3. 토큰 값 변경 시 TS 타입이 적용되는 모든 스타일에 일괄 반영됨.

    4. 컴포넌트별 *.css.ts 파일에 스타일을 관리해 구조가 명확하고, variant는 styleVariants나 Recipes 등으로 체계적으로 정의.

    5. 디자이너에게는 CSS 변수 출력이나 문서화를 통해 토큰 정보를 전달.
    1. 토큰 및 디자인 시스템 내장 지원.

    2. Panda는 기본적으로 Chakra UI 등을 참고한 기본 토큰 세트를 제공하며, 사용자가 테마 확장 가능. 토큰은 CSS 변수로 출력되어 테마 간 전환도 지원.

    3. 컴포넌트 스타일은 레시피(variants)로 정의 가능해 prop에 따른 스타일 분기가 한 곳에 모임. 

    4. Tailwind처럼 스타일 단위를 여기저기 중복 작성하지 않고, 중앙에서 정의 후 필요 시 조합만 하므로 디자인 시스템 변화에도 대응하기 쉬움.
    성능
    (CSS 출력 크기, 처리 시점)
    1. JIT 컴파일로 사용된 클래스에 대응하는 CSS만 생성.

    2. 미사용 유틸리티는 프로덕션 빌드에서 포함되지 않아 최종 CSS는 최소화됨.

    3. 예컨대 Tailwind 프로젝트는 사용한 스타일만 모아 작은 CSS로 배포 가능.

    4. 스타일 계산은 빌드 시점에 완료되며, 런타임에는 단순 클래스 적용만 이루어져 추가 JS 부하 없음. 

    5. Tailwind 자체는 순수 CSS이므로 SSR 호환성도 우수.
    1. 모든 스타일이 빌드 타임에 정적 CSS로 만들어져 번들에 포함.

    2. 런타임에 스타일 생성이나 삽입이 없고 브라우저는 완전한 CSS 파일을 받으므로 JS 실행 부담 없음.

    3. 클래스명은 해시로 짧게 처리되고 중복 선언을 자동 제거하므로 출력 CSS가 비교적 최적화됨.

    4. 다만 Tailwind처럼 자동 purge 개념은 없으므로, 정의했으나 쓰지 않은 스타일은 남을 수 있어 사용자가 구조 관리를 해야 함.
    1. 빌드 타임에 원자적 CSS를 생성하여 필요한 스타일만 출력.

    2. Tailwind와 같이 사용한 것만 생성하지만, Panda도 자체 JIT 엔진을 통해 매우 빠르게 컴파일.

    3. 런타임 JS 없이 동작하므로 SSR/RSC 환경에 최적화.

    3. React 서버 컴포넌트에서도 문제없고, 번들에 불필요한 스타일 처리 로직이 없어서 페이지 로드 성능이 우수.
    유지보수
    (클래스 네이밍, 리팩토링 용이성 등)
    1. 네이밍 고민 최소화: 미리 정의된 유틸 클래스 조합으로 스타일을 표현하므로 BEM처럼 새 클래스명을 짓는 일은 적음.

    2. 그러나 HTML에 다수의 짧은 클래스가 나열되기에 코드 가독성은 팀원 숙련도에 따라 좌우. (예: class="px-4 py-2 bg-gray-100 ..." 형태)

    3. 스타일 변경 시 해당 클래스명을 사용하는 모든 곳을 찾아 수정해야 하지만, 클래스명이 공통 토큰을 나타낸다면 테마 설정 변경으로 전체 일괄 조정도 가능.
    1. 스타일 정의와 사용 분리: 스타일은 .css.ts 파일에서 변수로 관리되고 컴포넌트에서는 그 변수를 참조하므로, 리팩토링이 타입 지원을 받아 안정적으로 수행.

    2. 예를 들어 클래스명을 바꾸는 대신 TS 변수명을 변경하고 사용처에서 자동으로 바뀌게 가능.

    3. 로컬 스코프 클래스이므로 전역 충돌 위험이 없고, 코드베이스가 커져도 스타일 검색/수정이 용이.

    4. 다만 초기에는 TS와 CSS 두 가지 지식을 모두 요구하여 러닝커브 존재.
    1. 최고 수준 DX와 가독성: Panda 사용자들은 "유틸리티 클래스의 성능과 CSS-in-JS의 편의성을 동시에 누며, 가독성과 DX가 크게 향상됐다."고 평가.

    2. 스타일이 JS/TS 코드와 함께 있기 때문에 컴포넌트 내에서 한눈에 관리되며, variant 레시피로 조건부 스타일도 직관적.

    3. Tailwind처럼 클래스명이 의미를 드러내지 않으면 어떨까 우려할 수 있으나, Panda는 클래스명을 신경 쓸 필요 없이 스타일 함수 호출로 처리하고 결과를 auto-generated 클래스에 맡기므로 네이밍 부담이 없음.

    4. 전체적으로 새로운 라이브러리인 만큼 작은 커뮤니티의 리소스 제한은 고려.

     

    Tailwind는 간편한 유틸 클래스로 빠르게 개발할 수 있으나 HTML에 스타일이 몰려 협업 시 추상화 계층을 만들어야 할 수 있고, 타입 안전성은 지원되지 않습니다.

    Vanilla Extract는 CSS를 TS화하여 컴파일 타임 안전성과 구조화를 얻는 반면, 초기 설정과 학습이 필요합니다.

    PandaCSS는 현대적 CSS-in-TS로서 Tailwind 유틸리티의 익숙함과 TS 타입 안전성을 결합하여 높은 DX를 추구하지만, 새로운 툴이라 생태계는 지금부터 형성되고 있습니다.

    4. 버튼 컴포넌트로 실전 예시 코드 비교

    세 도구로 동일한 스타일의 버튼 컴포넌트를 구현하는 예제를 살펴보겠습니다. 

    가정 요건

    • 기본 버튼은 파랑 배경에 흰 텍스트, 호버 시 약간 어두운 파랑, 둥근 모서리와 약간의 패딩을 가진다. 
    • variant prop에 따라 primary(위 기본 스타일)와 secondary(회색 배경에 검정 텍스트, 호버 시 밝은 회색)를 지원한다.

    4.1 Tailwind CSS 구현

    Tailwind에서는 HTML 클래스 조합으로 스타일을 적용합니다.

    변수에 클래스 문자열을 할당하거나 classnames/clsx 같은 헬퍼로 조건부 조합할 수 있습니다.

    // TailwindButton.tsx
    import React, { ButtonHTMLAttributes } from 'react';
    import clsx from 'clsx';
    
    const baseClass = "px-4 py-2 font-semibold rounded-lg";  
    const styles = {
      primary: "bg-blue-500 text-white hover:bg-blue-600",
      secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300"
    } as const;
    
    type Variant = keyof typeof styles;
    
    interface TailwindButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
      variant?: Variant;
    }
    
    export function TailwindButton({
      variant = "primary",
      ...props
    }: TailwindButtonProps) {
      return (
        <button 
          className={clsx(baseClass, styles[variant])} 
          {...props}
        />
      );
    }

     

    위 코드에서 baseClass는 공통 스타일(패딩, 폰트 굵기, 모서리 radius)을 담고 있고, styles 객체는 variant별 Tailwind 클래스 세트를 제공합니다.

     

    장점: 간결한 문자열로 바로 스타일 적용.

    단점: 클래스 이름 오타는 runtime에서 스타일 누락으로 드러나며, variant 종류가 늘어나면 관리가 어려워질 수 있습니다.

    4.2 Vanilla Extract 구현

    Vanilla Extract에서는 스타일을 TS로 작성하고 class명을 가져와 사용합니다.

    예를 들어 .css.ts 파일에 variant별 스타일을 정의하고, 컴포넌트에서 조건에 따라 적용합니다.

    // Button.css.ts
    import { style, styleVariants } from "@vanilla-extract/css";
    
    export const base = style({
      padding: '8px 16px',
      fontWeight: 600,
      borderRadius: '8px',
    });
    
    export const variants = styleVariants({
      primary: {
        backgroundColor: 'blue',
        color: 'white',
        selectors: { '&:hover': { backgroundColor: 'darkblue' } }
      },
      secondary: {
        backgroundColor: 'lightgray',
        color: 'black',
        selectors: { '&:hover': { backgroundColor: '#d0d0d0' } }
      }
    });
    
    // VEButton.tsx
    import React, { ButtonHTMLAttributes } from 'react';
    import * as styles from './Button.css';
    
    type Variant = keyof typeof styles.variants;
    
    interface VEButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
      variant?: Variant;
    }
    
    export function VEButton({
      variant = "primary",
      ...props
    }: VEButtonProps) {
      return (
        <button 
          className={`${styles.base} ${styles.variants[variant]}`} 
          {...props}
        />
      );
    }

     

    Vanilla Extract의 경우 .css.ts에서 styleVariants를 사용해 variant별 스타일 객체를 만들었습니다. 

    Hover 등의 상태도 selectors로 정의할 수 있습니다. 

    컴포넌트에서는 styles.base와 styles.variants[variant]를 조합하여 클래스 속성에 넣는데, 이때 styles.variants의 키들은 TS 타입으로 "primary" | "secondary"로 추론되어 잘못된 variant를 사용하면 컴파일 오류가 납니다. 

     

    장점: 스타일이 분리되어 읽기 좋고, 타입 지원으로 안전합니다. 

    단점: 스타일 변경 시 CSS/TS 파일을 수정해야 하므로 초기 속도는 느릴 수 있습니다.

    4.3 PandaCSS 구현

     Panda에서는 스타일을 TSX에서 바로 작성하거나 사전에 recipe로 정의할 수 있습니다.

    여기서는 Panda가 제공하는 cva (Class Variance Authority 유틸과 유사한 API)를 사용해 variant를 정의해 보겠습니다.

    // panda.ts (Panda configuration is assumed to be set up already)
    import { cva } from "@/styled-system/css";  // Panda's utility import
    
    export const buttonStyles = cva({
      base: {
        px: "4", py: "2",
        fontWeight: "semibold",
        rounded: "lg",
      },
      variants: {
        variant: {
          primary: { bg: "blue.500", color: "white", _hover: { bg: "blue.600" } },
          secondary: { bg: "gray.200", color: "gray.800", _hover: { bg: "gray.300" } }
        }
      },
      defaultVariants: {
        variant: "primary"
      }
    });
    
    // PandaButton.tsx
    import React, { ButtonHTMLAttributes } from 'react';
    import { buttonStyles } from './panda';
    
    type Variant = Parameters<typeof buttonStyles>[0]['variant'];
    
    interface PandaButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
      variant?: Variant;
    }
    
    export function PandaButton({
      variant = 'primary',
      ...props
    }: PandaButtonProps) {
      return (
        <button className={buttonStyles({ variant })} {...props} />
      );
    }

     

    PandaCSS의 cva 함수를 통해 베이스 스타일과 variants를 함께 정의했습니다. 

    buttonStyles는 함수처럼 동작하여 { variant: "secondary" } 등 인자를 넣으면 해당 variant에 맞는 클래스 이름들을 조합해 반환합니다. 

    Panda는 JS runtime 없이 이 과정을 컴파일 단계에서 처리하며, 결과적으로 buttonStyles({variant:"primary"})는 Tailwind의 클래스 조합처럼 여러 원자 클래스(.bg_blue\.500, .text_white, .hover\:bg_blue\.600 등)을 생성합니다. 

     

    장점: Tailwind의 패턴을 타입 안전하게 활용하고 variant 로직을 간결하게 관리합니다. 

    단점: Panda를 위한 빌드 세팅과 초기 학습이 필요하고, 생성된 클래스명이 추상화되어 직접 디버깅 시 CSS를 확인해야 할 수도 있습니다 (소스맵이나 디버그 모드로 보완 가능).

    5. 빌드/런타임 처리 흐름

    세 도구 모두 스타일을 빌드 시 준비하고 런타임에는 CSS 적용만 일어나는 흐름을 가지지만, 세부 과정에는 차이가 있습니다.

    각 도구의 스타일 처리 흐름을 요약한 다이어그램

    5.1 Tailwind CSS

    개발자가 JSX 등에 Tailwind의 유틸리티 클래스명을 직접 입력하면, Tailwind CLI(또는 PostCSS 플러그인)이 소스 파일을 스캔하여 사용된 모든 클래스의 CSS 규칙을 빌드합니다.

    Tailwind v3+에서는 JIT 모드로 필요한 순간 즉시 스타일을 생성하지만, 프로덕션 번들링 시에도 결과적으로 하나의 정적 CSS 파일 (혹은 <style> 태그)이 생성됩니다.

    따라서 브라우저는 그 CSS를 로드해 클래스에 대응되는 스타일을 적용할 뿐, 추가적인 JS 처리는 없습니다.

    5.2 Vanilla Extract

    .css.ts 파일로 작성된 스타일은 웹팩, Vite 등의 전용 플러그인을 통해 처리됩니다.

    빌드 시 플러그인은 .css.ts 내용을 파싱 하여 CSS 파일을 생성하고, 원래 JS/TS 모듈에는 해당 CSS의 자동 생성된 클래스명이 export 됩니다.

    컴포넌트 코드가 이 클래스를 import 해서 사용하면, 동시에 CSS도 번들에 포함되어 <style>로 주입됩니다.

    결과적으로 Tailwind처럼 최종 산출물은 HTML + CSS이고, 런타임에 JS가 스타일을 계산하는 과정은 없습니다.

    5.3 PandaCSS

    Panda는 개발 단계에서 @pandacss/dev 패키지가 제공하는 빌드 프로세스를 사용합니다.

    Vite/Next 등 환경에 Panda를 설정하면, JSX 내의 css({ ... })나 styled.div({...}) 호출을 컴파일러가 추적하여 그 내용으로 원자 CSS 규칙들을 생성합니다.

    그리고 해당 JSX에는 그 규칙들의 클래스를 넣도록 코드를 변환합니다.

    Tailwind와 유사하게 사용된 스타일만 모아 CSS 파일이 출력되고, 브라우저는 그 CSS를 적용합니다.

    Panda의 처리도 전적으로 빌드 시점에 일어나며, 런타임에는 Panda 관련 JS가 개입하지 않습니다.

     

    모두 초기 로딩 성능이 우수하며, CSR/SSR 어디서나 동일한 CSS가 적용되는 결과를 만듭니다.

    단, 개발 경험 측면에서 Tailwind는 순수 HTML 클래스 접근이라 빌드 툴 의존도가 낮고 바로 눈에 보이는 반면, Vanilla Extract와 Panda는 빌드 도구와의 연계 설정이 필요하지만 대신 타입이나 구조적 이점이 있습니다.

    6. 추천 가이드: 언제 어떤 도구를 선택해야 할까?

    6.1 빠른 프로토타이핑 & 소규모 프로젝트 : Tailwind CSS

    Tailwind는 초기 설정이 간단하고, 정해진 유틸리티 클래스만 알면 되므로 새로운 팀원도 비교적 빨리 참여할 수 있습니다.

    작은 프로젝트에서는 복잡한 theming보다 일관된 spacing, color 스케일만 써도 충분하므로 Tailwind의 기본 설정을 그대로 활용하면 됩니다.

    마이그레이션 팁(CSS/SASS > Tailwind CSS)
    먼저 공통되는 디자인 요소(색상, 여백 등)를 Tailwind 클래스 조합으로 변경합니다.
    Tailwind 사이트의 검색 기능으로 특정 CSS 속성에 대응하는 유틸리티 클래스를 찾을 수 있습니다.
    점진적으로 .css 파일의 내용을 지우고 HTML 클래스에 녹여내는 방식으로 진행하면 됩니다.
    클래스 이름이 너무 장황해지면 커스텀 유틸리티 (Tailwind config의 @apply나 plugin)을 만들어 반복을 줄일 수 있습니다.

    6.2 디자인 시스템 구축 & 대규모 유지보수: Vanilla Extract

    컴포넌트 라이브러리나 디자인 시스템을 만들 때는 스타일의 재사용성과 토큰 일관성이 중요합니다.

    Vanilla Extract는 이러한 환경에서 토큰을 체계적으로 관리하고, 컴포넌트별로 스타일 파일을 분리하여 코드 구조를 깔끔하게 유지할 수 있습니다. 

    또한 TS의 유형 검사로 유지보수 시 실수를 줄이고, 리팩토링 시 IDE의 도움을 받아 안전하게 수정할 수 있는 장점이 있습니다.

    마이그레이션 팁(Tailwind > Vanilla Extract)
    새로운 공통 컴포넌트는 VE(Vanilla Extract)로 작성하되, 기존 Tailwind 클래스도 함께 사용할 수 있습니다. Vanilla Extract의 
    Sprinkles 유틸을 활용하면 Tailwind의 spacing, color 스케일을 유사하게 정의해 이전 코드와 어색하지 않게 호환할 수도 있습니다.
    이렇게 혼용하는 과도기에서, Tailwind의 스타일은 그대로 두고 신규 스타일만 VE로 작성하다가, 충분한 기반 컴포넌트가 마련되면 HTML의 Tailwind 클래스들을 VE 스타일 참조로 교체해 가는 방식입니다.
    이때 VE에서 Tailwind와 동일한 디자인 토큰을 정의해 두면 시각적 변화 없이 교체할 수 있습니다.

    6.3 다크 모드/다중 테마 지원 프로젝트: Vanilla Extract 또는 PandaCSS

    Tailwind도 dark: 지원이나 @theme 디렉티브를 통해 다크 모드와 CSS 변수 테마를 구현할 수 있지만, 여러 개의 완전히 다른 테마를 동시에 제공하는 경우엔 Vanilla Extract의 createTheme로 각 테마별 CSS 변수 세트를 관리하거나 Panda의 Semantic tokens 기능을 활용하는 편이 논리적으로 관리하기 쉽습니다.

    마이그레이션 팁(Tailwind > Vanilla Extract/PandaCSS)
    기존 Tailwind 프로젝트에 다크 모드를 추가하려면 Tailwind config에 다크 색상 변수를 추가하고 dark: 프리픽스를 적용하는 방법으로 시작할 수 있습니다.
    그러나 조직적인 테마 관리를 원한다면, 핵심 디자인 토큰을 Vanilla Extract의 테마 계약으로 재정의하고, Tailwind 클래스 사용 부분을 점차 VE로 이관하는 방법을 고려하는 게 좋습니다.
    PandaCSS의 경우, Tailwind 클래스 사용 부분을 Panda의 token 참조로 변환하는 스크립트를 작성해 볼 수도 있습니다 (예: bg-blue-500 → bg: "blue.500" 객체로 변환).
    비교적 매핑이 명확하므로 부분 자동화도 가능합니다.

    6.4 React 서버 컴포넌트(RSC) 및 엄격한 성능 요구: PandaCSS

    PandaCSS는 앞서 언급했듯 RSC 호환과 Zero-runtime에 초점을 맞춰 개발되었기 때문에 Next.js 최신 기능들과도 충돌이 없습니다.

    또한 Atomic CSS 출력으로 중복 스타일을 최소화하므로, 페이지가 매우 많고 다양한 스타일 조합을 쓰는 대규모 애플리케이션에서 성능을 유지하는데 유리합니다.

    Tailwind 역시 JIT라서 사용하지 않는 CSS는 없지만, Panda는 더 세밀하게 필요한 부분만 생성하고 variant 시스템으로 조건부 스타일을 효율적으로 처리합니다.

    마이그레이션 팁(Emotion/Styled-Components > PandaCSS)
    먼저 Panda를 설정하고 기존 컴포넌트의 스타일을 Panda 문법으로 래핑 합니다.
    styled-component의 CSS 문자열을 Panda의 styled 함수 혹은 css 객체로 변환해야 하는데, 단순한 경우 find & replace로도 가능할 정도로 문법이 가깝습니다.
    예를 들어 styled.div`padding: 4px; color: red;` 는 Panda에서 styled.div({ p: "1", color: "red.500" }) (토큰값으로 대체) 형태로 바꿀 수 있습니다.
    하나씩 변환하며 결과 CSS가 동일한지 확인하고, 최종적으로 styled-component 패키지를 제거하면 됩니다.
    이때 Panda가 생성한 CSS가 기존과 다를 수 있으므로 UI 비교 테스트를 하는 것이 좋습니다.

     

    요약하면, Tailwind는 빠른 구축과 기본 제공 디자인 시스템에 강하고, Vanilla Extract는 대규모 TS 기반 프로젝트에서의 체계적인 스타일 관리에, PandaCSS는 최신 React 생태계 호환과 디자인 시스템 유연성에 강점이 있습니다.

    기존 코드베이스를 고려해 혼용하거나 점진적으로 도입하는 전략도 충분히 가능합니다.

    7. 결론 및 전망

    7.1 Tailwind CSS

    개발 속도를 극대화하는 Utility-First 프레임워크로, 러닝커브만 넘으면 작은 팀에서도 생산성을 높일 수 있습니다.

    클래스 기반으로 스타일을 관리하므로 초반에는 HTML이 어수선해 보일 수 있지만, 컴포넌트 추상화와 팀 합의된 패턴으로 극복 가능합니다.

    그리고 위에서는 간단한 프로젝트에서 사용을 추천했지만, 이미 다른 두 라이브러리보다도 방대한 생태계(ex. shadcn)와 문서가 뒷받침되고 있습니다.

    다만 타입 안정성 부재와 고도로 커스텀한 디자인 시스템 적용의 어려움은 고려해야 합니다.

    7.2 Vanilla Extract

    대규모 프로젝트의 안정적인 선택지로 부상하고 있습니다.

    Zero-runtime이라는 CSS-in-JS의 이상을 실현했고, 실제 많은 기업들이 디자인 시스템 구축에 VE를 활용한다고 들었습니다.

    유지보수성 측면에서는 세 도구 중 가장 우수하며, TS로 관리되는 CSS는 앞으로도 점점 호환성과 도구 지원이 늘어날 것입니다.

    반면 커뮤니티는 Tailwind만큼 크지 않아 활용 사례나 튜토리얼이 비교적 적을 수 있고, 빌드 설정 이해가 필요합니다.

    하지만 CSS-in-JS의 단점 없는 형태라는 평가대로, 트레이드오프가 적은 균형 잡힌 도구라고 볼 수 있습니다.

    7.3 PandaCSS

    최신 트렌드를 모두 아우르는 야심 찬 스타일링 엔진입니다.

    Chakra UI 팀의 노하우와 Tailwind의 인기 요소를 결합하여, "모든 것의 장점이 모여 있다."는 커뮤니티 피드백이 있을 정도로 기대를 모읍니다.

    Variants, Tokens, Utility API 등 개발자가 필요로 하는 기능을 망라하면서도 런타임 오버헤드 제로라는 점에서 앞으로 점차 도입이 늘어날 가능성이 있습니다.

    2024년 초 등장 이후 활발히 개선되고 있으며, 아직은 새로운 만큼 보수적인 조직에서는 바로 도입하기 망설일 수 있습니다.

    그러나 Next.js 앱 라우터 이슈 등으로 CSS-in-JS의 방향 전환이 일어나고 있는 지금, PandaCSS는 충분히 차세대 통합 스타일링 라이브러리로 주목할만합니다.

     

    결론적으로, 세 도구 모두 정적 빌드, 경량 런타임이라는 현대 프론트엔드 스타일링의 흐름에 위치해 있습니다.

     

    CSS도 Container Query, Scope 등 새로운 기능이 등장하면서 스타일링 방식도 발전하고 있습니다.

    Tailwind, Vanilla Extract, PandaCSS처럼 변화에 빠르게 대응하는 도구들이 늘어나고 있으며, 이러한 도구와 기술의 변화를 꾸준히 따라가는 것은 프론트엔드 개발자에게 중요한 역량입니다.

    참고 자료

    What is Zero-Runtime CSS in JS? Which Library Should You Pick? - DEV Community

     

    What is Zero-Runtime CSS in JS? Which Library Should You Pick?

    Introduction: The End of CSS in JS and the Transition to Zero-Runtime Front-end technology...

    dev.to

    Panda CSS: Revolutionizing CSS-In-JS Libraries

     

    Panda CSS: Revolutionizing CSS-In-JS Libraries

    Looking for a new styling system? Try Panda, a new type-safe CSS-in-JS library with zero runtime, multi-variant support, and RSC compatibility.

    www.cobeisfresh.com

    Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.

     

    Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.

    Tailwind CSS is a utility-first CSS framework for rapidly building modern websites without ever leaving your HTML.

    tailwindcss.com

    vanilla-extract — Zero-runtime Stylesheets-in-TypeScript.

     

    vanilla-extract — Zero-runtime Stylesheets-in-TypeScript.

    Zero-runtime Stylesheets-in-TypeScript.

    vanilla-extract.style

    Panda CSS - Build modern websites using build time and type-safe CSS-in-JS

     

    Panda CSS - Build modern websites using build time and type-safe CSS-in-JS

    Build modern websites using build time and type-safe CSS-in-JS

    panda-css.com

    From Tailwind CSS to PandaCSS - Dang Thanh Digital Garden

     

    From Tailwind CSS to PandaCSS - Dang Thanh Digital Garden

    I’m a long time user of Tailwind CSS and Tailwind CSS is awesome. Now, I’ve been using PandaCSS to build the website. After spending a few weeks building the website using Nuxt.js and Next.js with PandaCSS, here are some reasons I like it and want to m

    dangthanh.org

    From Tailwind to Vanilla Extract: the right CSS tool for the Design System job

     

    From Tailwind to Vanilla Extract: the right CSS tool for the Design System job

    A story about migrating a nascent component library from Tailwind to Vanilla Extract and the lessons learned during the process.

    gafemoyano.com

    반응형

    댓글

Designed by Tistory.