← 목록으로 돌아가기

Tailwind CSS v4 마이그레이션 실전: CSS-first 설정과 새 엔진이 기존 프로젝트에 미치는 영향 분석

CSS

Tailwind CSS v4 migration CSS engine Lightning CSS

PostCSS 기반 세계관이 무너지는 날

우리 팀이 Tailwind CSS v4 마이그레이션을 처음 마주한 건 2026년 초였습니다. Next.js App Router로 전환한 지 얼마 지나지 않아, Tailwind 팀이 v4 정식 릴리즈를 선언했습니다. 릴리즈 노트를 열자마자 첫 번째 느낌은 "업그레이드"가 아니라 "다른 도구"를 배우는 기분이었습니다. tailwind.config.js가 사라지고, @tailwind base; @tailwind components; @tailwind utilities; 세 줄이 @import "tailwindcss" 한 줄로 대체됐으며, 핵심 빌드 엔진이 PostCSS 플러그인 체인에서 Lightning CSS로 교체됐습니다.

익숙한 API가 바뀌는 것은 항상 저항감을 부릅니다. 하지만 실제로 프로덕션 프로젝트에 적용해보니, 변화의 방향은 일관됐습니다. CSS 플랫폼이 성숙해지면서 JavaScript로 대신 해결하던 것들을 네이티브 CSS로 돌려보내는 철학이었습니다. 이 글은 v3에서 v4로 이전하는 과정에서 부딪히는 핵심 변화 10가지를 실무 코드 수준에서 분석합니다.


1. v3에서 v4의 근본적 변화: PostCSS 플러그인에서 Lightning CSS로

Tailwind CSS v3까지는 PostCSS를 핵심 처리 파이프라인으로 사용했습니다. CSS 파일이 입력되면 PostCSS 플러그인 체인이 @tailwind 지시어를 해석하고, autoprefixer가 벤더 프리픽스를 붙이고, postcss-import@import 구문을 처리했습니다. 이 구조는 유연했지만, 플러그인 수가 많을수록 파이프라인이 길어지고 빌드 속도는 느려졌습니다.

v4는 핵심 파서와 변환 엔진을 Lightning CSS로 교체했습니다. Lightning CSS는 Rust로 작성된 고성능 CSS 툴체인으로, 파싱·벤더 프리픽스·@import 번들링·문법 다운레벨링을 단일 패스에서 처리합니다. Tailwind 팀이 공식 블로그에서 공개한 벤치마크에 따르면, 풀 빌드 기준 3.78배, 새 CSS가 없는 증분 빌드에서는 최대 182배 속도 향상을 달성했습니다.

이 변화의 실무적 의미는 두 가지입니다. 첫째, autoprefixer를 별도로 설치하지 않아도 됩니다. Lightning CSS가 내부적으로 처리합니다. 둘째, postcss-import가 필요 없습니다. @import 지원이 기본 내장됩니다. 중요한 점은 Sass, Less, Stylus와 같은 CSS 전처리기가 v4에서 공식적으로 지원되지 않는다는 것입니다.


2. @import 기반 CSS-first 설정으로의 전환

v3 프로젝트의 진입점 CSS는 이렇게 생겼습니다.

/* v3 방식: globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

v4에서는 이 세 줄이 하나로 압축됩니다.

/* v4 방식: globals.css */
@import "tailwindcss";

/* 커스텀 테마 변수 선언 */
@theme {
  --font-sans: 'Pretendard', -apple-system, BlinkMacSystemFont, sans-serif;
  --color-brand-500: oklch(0.62 0.19 254);
  --color-brand-600: oklch(0.54 0.20 254);
  --breakpoint-3xl: 1920px;
  --spacing-18: 4.5rem;
}

@source "../node_modules/@company/ui-components/src";

@theme 블록은 v4에서 가장 중요한 새 개념입니다. 여기에 선언한 CSS 변수들은 자동으로 :root에 등록되고, 동시에 Tailwind 유틸리티 클래스로도 활성화됩니다. --color-brand-500: oklch(...) 한 줄이면 bg-brand-500, text-brand-500, border-brand-500 클래스가 즉시 생성됩니다.

content 설정도 사라졌습니다. v4는 .gitignore를 존중하면서 프로젝트 파일을 자동 스캔합니다.


3. 커스텀 유틸리티 작성법 변화: @utility 디렉티브

v3에서 커스텀 유틸리티를 만들 때는 @layer utilities 블록 안에 클래스를 작성했습니다. v4에서는 이 패턴이 @utility 디렉티브로 대체됩니다.

/* v4 방식 */
@utility tab-4 {
  tab-size: 4;
}

@utility content-auto {
  content-visibility: auto;
}

@utility scrollbar-hide {
  scrollbar-width: none;
  -ms-overflow-style: none;

  &::-webkit-scrollbar {
    display: none;
  }
}

@utility로 등록된 클래스는 Tailwind의 레이어 관리 시스템과 완전히 통합됩니다. 캐스케이드 레이어와 명시도 충돌 패턴은 CSS @layer 실전 가이드에서 자세히 다뤘습니다.

@utility는 반응형 프리픽스, 상태 변형(hover, focus 등), 임의값(arbitrary values)을 자동으로 지원합니다.


4. 다크 모드 전략 변경: @media와 @custom-variant

v3에서 다크 모드는 tailwind.config.jsdarkMode 옵션으로 제어했습니다. v4에서는 기본 동작이 @media (prefers-color-scheme: dark)로 바뀌었습니다. .dark 클래스 기반으로 전환하고 싶다면 @custom-variant를 사용합니다.

/* v4에서 .dark 클래스 기반 다크 모드 활성화 */
@import "tailwindcss";

@custom-variant dark (&:where(.dark, .dark *));

/* 사이드바 축소 상태에서의 변형 클래스 */
@custom-variant sidebar-collapsed (&:where([data-sidebar="collapsed"] *));

/* 모바일 터치 디바이스에서 hover를 항상 활성화 */
@custom-variant hover (&:hover);

v4에서 기본 hover 변형은 @media (hover: hover) 조건 하에서만 작동합니다. 터치 디바이스에서도 hover 스타일을 적용해야 한다면 위와 같이 @custom-variant로 재정의해야 합니다. 이는 Breaking Change 목록에 포함되는 동작 변경이므로 QA 시 반드시 모바일 환경에서 hover 상태를 확인해야 합니다.


5. tailwind.config.js → CSS 변수 마이그레이션 가이드

v3 프로젝트의 tailwind.config.js는 대부분 @theme 블록으로 이전할 수 있습니다.

v3 설정 항목v4 대응 방식
theme.extend.colors@theme { --color-*: ... }
theme.extend.fontFamily@theme { --font-*: ... }
theme.extend.spacing@theme { --spacing-*: ... }
theme.extend.screens@theme { --breakpoint-*: ... }
theme.container@utility container { ... }
plugins: [require('...')]@plugin "..."
safelist@source inline("클래스명")
@import "tailwindcss";

@theme {
  --color-primary-50:  oklch(0.97 0.01 254);
  --color-primary-500: oklch(0.62 0.19 254);
  --color-primary-600: oklch(0.54 0.20 254);

  --font-sans: 'Pretendard Variable', sans-serif;
  --font-mono: 'JetBrains Mono', monospace;

  --breakpoint-xs: 480px;
  --breakpoint-3xl: 1920px;

  --spacing-18: 4.5rem;
  --spacing-72: 18rem;
}

공식 함수/지시어 문서(tailwindcss.com/docs/functions-and-directives)에서 @themereset 옵션을 참조하세요.


6. 빌드 속도 차이와 측정 결과

Tailwind 공식 팀이 공개한 벤치마크는 세 가지 시나리오로 측정됩니다.

빌드 시나리오v3 소요 시간v4 소요 시간개선 배수
풀 빌드378ms100ms3.78x
새 CSS 있는 증분 빌드44ms5ms8.8x
새 CSS 없는 증분 빌드35ms0.192ms182x

새 CSS가 없는 증분 빌드(JavaScript 파일만 변경되어 클래스 스캔 결과가 동일한 경우)가 마이크로초 단위로 떨어지는 이유는 Lightning CSS가 이 경우 CSS 파일을 완전히 재처리하지 않아도 되기 때문입니다.

우리 팀이 실제 Next.js App Router 프로젝트에서 마이그레이션 전후를 비교했을 때, next dev 시작 후 첫 페이지 HMR 반영 시간이 체감상 절반 이하로 줄었습니다.


7. 알려진 Breaking Change 목록

업그레이드 공식 문서가 정리한 Breaking Change 중 실무에서 자주 마주치는 항목들입니다.

유틸리티 이름 변경: shadowshadow-sm으로, shadow-smshadow-xs로, blur-smblur-xs로 한 단계씩 밀렸습니다. rounded-smrounded-xs로 바뀌었고, outline-noneoutline-hidden으로 변경됐습니다.

!important 수식어 위치 변경: v3에서 !flex처럼 앞에 붙던 !가 v4에서는 flex!처럼 뒤에 붙습니다.

ring 기본값 변경: 기본 너비가 3px에서 1px로, 기본 색상이 blue-500에서 currentColor로 바뀌었습니다.

임의값 CSS 변수 문법 변경: bg-[--brand-color]bg-(--brand-color)로 바뀌었습니다.

hover가 @media (hover: hover) 조건부로 변경: 모바일 터치 환경에서 hover 스타일이 적용되지 않습니다.


8. 컴포넌트 라이브러리(shadcn/ui, Radix) 호환성

shadcn/ui는 2026년 기준 Tailwind CSS v4를 공식 지원합니다. shadcn/ui의 컴포넌트 스타일은 CSS 변수를 통해 테마를 주입합니다. shadcn/ui가 사용하는 --background, --foreground, --primary 같은 변수명은 v4의 @theme 네임스페이스와 겹치지 않으므로 공존 가능합니다.

cn() 함수(clsx + tailwind-merge 조합)는 v4에서도 동일하게 작동합니다. tailwind-merge는 v4의 새 클래스 이름 규칙에 맞게 업데이트된 버전(v3.3 이상)을 사용해야 합니다.

Tailwind CSS로 디자인 시스템을 구축하는 전반적인 설계 전략은 Tailwind CSS로 디자인 시스템 구축하기에서 다룬 내용을 참고하시면 도움이 됩니다.


9. v4 + Next.js App Router 세팅

# Tailwind CSS v4 + Vite 플러그인 (Next.js는 Vite 대신 PostCSS 사용)
npm install tailwindcss @tailwindcss/postcss

# 기존 프로젝트 자동 마이그레이션
npx @tailwindcss/upgrade
// postcss.config.mjs
const config = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

export default config;
/* app/globals.css */
@import "tailwindcss";

@theme {
  --font-sans: var(--font-pretendard), -apple-system, sans-serif;
  --color-primary-500: oklch(0.62 0.19 262);
  --breakpoint-xs: 480px;
}

@layer base {
  body {
    font-family: var(--font-sans);
    -webkit-font-smoothing: antialiased;
  }
}

@custom-variant dark (&:where(.dark, .dark *));

Server Components 환경에서 Tailwind 클래스는 정적 분석 대상입니다. 'bg-' + color + '-500' 패턴은 v3에서도 v4에서도 작동하지 않습니다. cn() 함수와 조건부 객체 패턴을 사용하는 것이 올바른 방식입니다.


10. 마이그레이션 체크리스트

사전 준비

  • 브라우저 지원 범위 확인: Safari 16.4+, Chrome 111+, Firefox 128+ 미만을 지원해야 한다면 v4는 사용할 수 없습니다.
  • Sass/Less 등 CSS 전처리기 사용 여부 확인: v4와 함께 사용할 수 없습니다.
  • tailwind-merge 버전 확인: v3.3 이상.

자동 마이그레이션 실행

git checkout -b chore/tailwind-v4-migration
npx @tailwindcss/upgrade

수동 확인 항목

  • !important 수식어 치환 누락 없는지 전수 확인
  • ring, shadow, blur, rounded 관련 클래스 시각 QA
  • 모바일 환경에서 hover 상태 동작 확인
  • 다크 모드 .dark 클래스 기반 사용 시 @custom-variant dark 재정의 여부

결론

Tailwind CSS v4 마이그레이션은 단순한 버전 번호 교체가 아닙니다. PostCSS 플러그인 체인에 의존하던 스타일 파이프라인이 Lightning CSS 기반의 단일 고성능 엔진으로 전환되고, JavaScript 설정 파일 중심의 워크플로우가 CSS-first 선언 방식으로 바뀝니다.

  • 브라우저 지원 확인 먼저: v4는 모던 브라우저 전용입니다.
  • 자동 마이그레이션 도구를 새 브랜치에서 실행: npx @tailwindcss/upgrade가 대부분의 기계적 변환을 처리합니다.
  • ring, shadow, hover 변경은 수동 QA 필수: 모바일 터치 환경에서의 hover 동작 변경은 자동화 테스트가 잡기 어렵습니다.
  • @theme 블록으로 디자인 토큰 일원화.
  • 동적 클래스 조합 코드 전수 점검.