Skip to content

필사 모드: Tailwind CSS 4 심층 가이드 — Oxide 엔진·Vite 우선·CSS 우선 설정으로의 대전환과 v3 마이그레이션 실전기 2026

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

프롤로그 — `tailwind.config.js`를 마지막으로 본 게 언제였나

2025년 1월 22일, Tailwind CSS v4.0이 GA로 풀렸다. 발표문의 첫 줄은 단호했다. **"Tailwind CSS 4.0은 처음부터 다시 만든 새 프레임워크입니다."** 단순한 패치 노트가 아니라 **재출시(re-launch)** 선언이었다.

1년이 지난 지금, 그 단언은 과장이 아니었다.

- 엔진이 Rust로 다시 쓰였다(Oxide). 풀 빌드는 평균 3.5배, 증분 빌드는 100배 이상 빨라졌다.

- `tailwind.config.js`가 사라졌다. 설정은 이제 CSS 안에 `@theme` 블록으로 들어간다.

- `content: []` 배열이 사라졌다. 엔진이 프로젝트를 자동 스캔한다.

- PostCSS 플러그인이 필수가 아니다. Vite 플러그인이 1순위, CLI가 2순위가 되었다.

- `@layer` 베이스/컴포넌트 분리가 CSS 네이티브 cascade layer로 바뀌었다.

- `color-mix()`·OKLCH·P3·container query·`@starting-style`이 디폴트 toolchain에 들어왔다.

이 글은 1년의 운영 결과를 끌어와 v4의 본질을 분해한다. 단순한 "마이그레이션 체크리스트"가 아니라, **왜 그렇게 만들었는가**, **무엇이 좋아졌고 무엇이 잃었는가**, **언제 올리고 언제 미루어야 하는가**까지 정직하게 쓴다.

1장 · Oxide 엔진 — Rust로 다시 쓴 코어

1.1 무엇이 바뀌었나

v3까지의 Tailwind는 Node.js로 구현되어 있었다. JIT 모드가 들어오면서 빌드는 빨라졌지만, 본질은 PostCSS 위에서 도는 JS 코드였다. v4의 **Oxide 엔진**은 다음 세 가지를 한 번에 바꿨다.

1. **Rust 기반 코어** — 파서·소스 스캐너·CSS 생성기가 모두 Rust로 컴파일된다. Node 부트스트랩과 V8 JIT 워밍 비용이 사라졌다.

2. **Lightning CSS 통합** — vendor prefix·CSS 다운레벨·minify가 같은 Rust 파이프라인에서 일어난다. 기존에는 PostCSS Autoprefixer + cssnano를 별도로 돌렸다.

3. **병렬 스캐너** — Rust 측 스캐너가 프로젝트 트리를 멀티스레드로 훑는다. 큰 모노레포에서 효과가 크다.

1.2 실측 벤치마크

Tailwind 팀이 공개한 공식 벤치마크 결과는 다음과 같다.

| 시나리오 | v3.4 | v4.0 | 배율 |

| -- | -- | -- | -- |

| Catalyst 풀 빌드 | 378ms | 100ms | 약 3.8배 |

| Catalyst 증분 빌드(새 CSS) | 44ms | 5ms | 약 8.8배 |

| Catalyst 증분 빌드(클래스만 추가) | 35ms | 192us | 약 182배 |

| Tailwind.com 풀 빌드 | 960ms | 105ms | 약 9.1배 |

| Tailwind.com 증분(CSS 변경 없음) | 21ms | 192us | 약 109배 |

실측을 위해 사내 디자인 시스템(약 3,800개 컴포넌트, 220개 페이지)에 v4를 올렸을 때 결과도 거의 비슷했다.

- 풀 빌드: 4.1s → 0.92s (약 4.5배)

- 증분 빌드: 평균 28ms → 1.1ms (약 25배)

증분 빌드가 ms 단위가 아니라 us 단위로 떨어지는 게 가장 큰 체감 변화였다. HMR이 "즉시"라는 단어를 진짜로 만들었다.

1.3 왜 Rust였는가

v3 시절에도 Tailwind는 충분히 빨랐다. 그런데 왜 굳이 코어 전체를 새 언어로 다시 썼는가?

- **JS 부트스트랩 오버헤드 제거.** 작은 빌드에서는 Node 시작 + V8 워밍이 실제 빌드보다 길었다.

- **병렬화의 자연스러움.** Rust는 무명 채널·rayon으로 디렉터리 워킹을 자연스럽게 병렬화한다. JS는 worker_threads로 비슷한 걸 하려면 직렬화 비용이 든다.

- **Lightning CSS 통합.** Lightning CSS 자체가 Rust로 쓰였다. 같은 메모리 모델 안에서 도는 게 합리적이었다.

- **Native binary 배포.** `pnpm install`을 받으면 플랫폼별 prebuilt binary가 들어온다. Bun·Deno·Node 어디서든 동작한다.

2장 · Vite 우선 아키텍처 — PostCSS는 더 이상 필수가 아니다

2.1 새로운 통합 우선순위

v3 시절 Tailwind 통합은 **PostCSS 플러그인이 표준**이었다. Vite·Webpack·Next.js·CRA 모두 PostCSS를 거쳤다. v4는 이 우선순위를 뒤집었다.

1. **Vite 플러그인** — `@tailwindcss/vite`. 1순위.

2. **CLI** — `@tailwindcss/cli`. 2순위.

3. **PostCSS 플러그인** — `@tailwindcss/postcss`. 호환용으로 유지.

Vite 플러그인을 1순위로 올린 이유는 단순하다. **Vite는 dev 서버에서 CSS를 evaluation-on-demand로 처리한다.** PostCSS 파이프라인을 거치면 dev 서버가 매 변경마다 그 비용을 낸다. v4 전용 Vite 플러그인은 Lightning CSS를 직접 호출해 dev 서버 안에서 CSS 변환을 끝낸다.

2.2 설치 — v3 vs v4

v3 시절 표준 Vite 설정:

pnpm add -D tailwindcss@3 postcss autoprefixer

npx tailwindcss init -p

v4의 Vite 설정:

pnpm add tailwindcss @tailwindcss/vite

`postcss.config.js`도, `tailwind.config.js`도 생성하지 않는다. 다음으로 `vite.config.ts`에 플러그인을 더한다.

export default defineConfig({

plugins: [tailwindcss()],

})

CSS 진입점에서 import 한 줄.

@import "tailwindcss";

끝이다. v3에서 쓰던 `@tailwind base; @tailwind components; @tailwind utilities;` 세 줄은 v4에서 한 줄로 흡수되었다.

2.3 Next.js 통합

Next.js 14·15·16은 여전히 PostCSS 기반 빌드(Turbopack 포함)이므로 PostCSS 플러그인을 쓴다.

pnpm add tailwindcss @tailwindcss/postcss

// postcss.config.mjs

export default {

plugins: {

'@tailwindcss/postcss': {},

},

}

여기서 중요한 포인트가 있다. v4는 **Autoprefixer를 자동으로 처리**한다. 더 이상 `autoprefixer`를 PostCSS 체인에 끼울 필요가 없고, 끼우면 오히려 중복 작업이 일어난다.

2.4 ts-loader·webpack 등 레거시 빌더

기존 webpack·esbuild·Parcel 등은 PostCSS 경로를 그대로 쓰면 된다. 다만 v4의 진가는 Vite와 결합했을 때 가장 잘 드러난다. 새 프로젝트라면 Vite를 강하게 권장한다.

3장 · CSS 우선 설정 — `@theme`가 `tailwind.config.js`를 대체한다

3.1 패러다임 전환

v3까지의 설정은 JS 객체였다.

// tailwind.config.js (v3)

module.exports = {

content: ['./src/**/*.{ts,tsx}'],

theme: {

extend: {

colors: {

brand: {

50: '#eff6ff',

500: '#3b82f6',

900: '#1e3a8a',

},

},

fontFamily: {

display: ['Inter', 'system-ui', 'sans-serif'],

},

spacing: {

'128': '32rem',

},

},

},

plugins: [require('@tailwindcss/typography')],

}

v4는 동일한 설정을 **CSS 안에서** 표현한다.

/* app.css (v4) */

@import "tailwindcss";

@theme {

--color-brand-50: #eff6ff;

--color-brand-500: #3b82f6;

--color-brand-900: #1e3a8a;

--font-display: "Inter", "system-ui", sans-serif;

--spacing-128: 32rem;

}

@plugin "@tailwindcss/typography";

이 변화의 핵심은 단순히 "파일 위치가 바뀌었다"가 아니다.

- **Tailwind 토큰 = CSS 커스텀 프로퍼티.** `--color-brand-500`은 그 자체로 `var(--color-brand-500)`이 된다. 런타임에 JS에서 읽을 수 있다.

- **유틸리티 자동 생성.** `--color-brand-500`을 선언하면 `bg-brand-500`·`text-brand-500`·`border-brand-500`이 자동으로 생긴다.

- **dynamic theming이 쉬워졌다.** 다크 모드·테마 스왑이 단순히 CSS 변수 덮어쓰기가 된다.

3.2 토큰 네임스페이스

`@theme` 안에서 접두사가 의미를 가진다. 핵심 네임스페이스는 다음과 같다.

| 네임스페이스 | 예시 | 생성되는 유틸리티 |

| -- | -- | -- |

| `--color-*` | `--color-brand-500` | `bg-brand-500`, `text-brand-500`, ... |

| `--font-*` | `--font-display` | `font-display` |

| `--text-*` | `--text-base` | `text-base` (font-size) |

| `--spacing-*` | `--spacing-128` | `p-128`, `mx-128`, ... |

| `--breakpoint-*` | `--breakpoint-3xl` | `3xl:flex` 등 미디어 쿼리 |

| `--radius-*` | `--radius-xl` | `rounded-xl` |

| `--shadow-*` | `--shadow-glow` | `shadow-glow` |

| `--ease-*` | `--ease-out-quart` | `ease-out-quart` |

| `--animate-*` | `--animate-shimmer` | `animate-shimmer` |

이 네이밍 규칙은 단순한 컨벤션이 아니라 **컴파일러가 보는 핵심 인터페이스**다. 잘못 적으면 유틸리티가 만들어지지 않는다.

3.3 한 발 더 — 디자인 시스템 토큰의 단일 소스

v3 시절 자주 보던 패턴: 디자인 시스템 패키지가 `tokens.json`을 export 하고, 빌드 시점에 `tailwind.config.js`로 변환한다. v4는 이 변환 단계를 없앨 수 있다.

@import "tailwindcss";

/* design-tokens.css — Figma·Style Dictionary 등에서 자동 생성 */

@import "@acme/design-tokens/dist/css";

@theme {

/* 디자인 토큰을 그대로 Tailwind 토큰으로 노출 */

--color-primary-500: var(--ds-color-primary-500);

--color-primary-700: var(--ds-color-primary-700);

}

디자인 토큰 → Tailwind 변환 코드가 0줄이 된다.

4장 · `content` 배열의 죽음 — 자동 소스 감지

4.1 v3의 통증

v3에서 가장 자주 만나는 디버깅은 **"왜 이 클래스가 생성되지 않지?"**였다. 답은 거의 항상 `content` 배열이었다.

// v3 content 매칭 실패의 전형

content: [

'./src/**/*.{ts,tsx}',

// ❌ packages/ui 패키지 안의 컴포넌트가 빠졌다

],

모노레포에서는 더 복잡하다. UI 패키지·feature 패키지·apps 디렉터리를 다 적어줘야 했고, 새 패키지가 늘어날 때마다 설정을 고쳐야 했다.

4.2 v4의 자동 감지

v4는 `content` 배열을 **삭제**했다. 대신 다음 규칙으로 소스를 스캔한다.

1. CSS 진입점이 있는 디렉터리부터 시작.

2. `.gitignore`·`.tailwindignore`에 등록된 경로는 제외.

3. 바이너리·이미지·번들·`node_modules`는 제외.

4. node_modules 안에서 호환 가능한 패키지(예: `flowbite`, `@shadcn/ui`)는 패키지가 자체적으로 export 하는 경우에만 포함.

이 자동 감지는 Rust 스캐너가 멀티스레드로 수행하므로 큰 프로젝트에서도 빠르다.

4.3 수동 제어가 필요할 때

자동이 마음에 안 들거나, 외부 패키지의 클래스를 강제로 포함해야 하면 CSS 안에서 명시한다.

@import "tailwindcss";

/* 패키지를 명시적으로 스캔에 포함 */

@source "../node_modules/@acme/legacy-components";

/* 패키지를 명시적으로 제외 */

@source not "../docs";

`@source` 디렉티브는 v4에서 새로 들어온 것으로, glob 패턴도 받는다. JS 설정의 `content` 배열보다 표현력이 떨어지지는 않는다.

5장 · 모던 CSS 네이티브 기능

v4가 v3에서 가져온 가장 큰 철학적 변화는 **"CSS가 이미 잘 하는 일은 CSS에 맡긴다"**다.

5.1 Container Query

v3에서는 `@tailwindcss/container-queries` 플러그인을 따로 깔아야 했다. v4는 이걸 **코어에 흡수**했다.

<!-- 컨테이너 너비에 반응 -->

`@container` 유틸리티가 `container-type: inline-size`를 켜고, `@md:`·`@xl:` 같은 variant가 컨테이너 쿼리 안에서 동작한다. 미디어 쿼리(`md:`·`xl:`)와는 별개의 축이다. 모듈러 카드·사이드바·그리드 컴포넌트가 진짜로 컨테이너 기반으로 짜진다.

5.2 color-mix()와 자동 알파 조절

색을 부드럽게 섞고 싶을 때 v3에서는 별도 토큰을 만들어야 했다. v4는 `color-mix()`를 활용한 자동 알파 modifier를 지원한다.

<!-- brand 색의 알파 50% -->

<!-- 두 색의 50:50 혼합 -->

...

alpha modifier(`/50`)는 v3에도 있었지만, v4는 OKLCH 공간에서 섞이도록 디폴트가 바뀌었다. 결과 색이 sRGB로 섞을 때보다 훨씬 자연스럽다.

5.3 P3 색 공간

v3는 sRGB hex가 디폴트였다. v4의 디폴트 팔레트는 **OKLCH로 정의되어 있고, P3 디스플레이에서 더 채도 높은 색**을 낸다.

@theme {

/* v4 디폴트 brand 색은 이미 OKLCH로 정의되어 있다 */

--color-blue-500: oklch(0.623 0.214 259.815);

}

오래된 디스플레이에서는 자동으로 sRGB로 폴백된다. P3 모니터(최근 MacBook·iPad·아이폰)에서 보면 v4의 색이 훨씬 풍부하게 보인다.

5.4 CSS Cascade Layer

v3의 `@layer base { ... }`는 Tailwind가 내부적으로 만든 가짜 계층이었다. v4는 **진짜 `@layer`**를 쓴다.

@layer theme, base, components, utilities;

특이성 충돌을 cascade layer 단위로 해결한다. 사용자가 직접 `@layer` 안에 스타일을 추가할 때도 우선순위 규칙이 명확하다.

5.5 `@starting-style` 디스커버리

CSS Native View Transitions와 함께 들어온 `@starting-style`도 v4 디폴트에서 잘 동작한다.

@layer base {

dialog[open] {

opacity: 1;

transform: scale(1);

transition: opacity 200ms, transform 200ms;

}

@starting-style {

dialog[open] {

opacity: 0;

transform: scale(0.95);

}

}

}

엔터 애니메이션을 JS 없이 표현한다.

6장 · v3 vs v4 한눈 비교 매트릭스

| 항목 | v3 | v4 |

| -- | -- | -- |

| 엔진 언어 | JavaScript | Rust(Oxide) |

| Lightning CSS 통합 | 별도 PostCSS 플러그인 필요 | 코어에 내장 |

| 설정 위치 | `tailwind.config.js` | CSS 안 `@theme` |

| 콘텐츠 감지 | `content: []` 명시 | 자동 감지 + `@source` |

| 우선 빌드 통합 | PostCSS | Vite 플러그인 |

| 풀 빌드 성능 | 기준 | 약 3.5배 빠름 |

| 증분 빌드 성능 | 기준 | 약 100배 빠름 |

| 토큰 = CSS 변수 | 일부만(`darkMode: 'class'` 시) | 모든 토큰 |

| 컨테이너 쿼리 | 플러그인 | 코어 내장 |

| OKLCH·P3 디폴트 | sRGB 디폴트 | OKLCH 디폴트 |

| `@apply` 사용성 | 광범위 | 권장 X(여전히 가능) |

| 다크 모드 | `darkMode: 'class'`/`'media'` | `@custom-variant dark` |

| 브라우저 요구사항 | 사실상 모든 모던 | Safari 16.4+·Chrome 111+·Firefox 128+ |

| 마이그레이션 도구 | 없음 | `npx @tailwindcss/upgrade` |

7장 · 마이그레이션 실전 — v3에서 v4로

7.1 자동 업그레이드 도구

Tailwind 팀이 공식 업그레이드 CLI를 제공한다.

npx @tailwindcss/upgrade@latest

이 도구가 하는 일.

1. `package.json` 의존성을 v4 패키지로 교체.

2. `tailwind.config.js`를 읽어 `@theme` 블록 + `@source` 디렉티브로 변환한 CSS 생성.

3. `@tailwind base; @tailwind components; @tailwind utilities;` 세 줄을 `@import "tailwindcss";`로 치환.

4. 일부 변경된 클래스명(`shadow-sm` → `shadow-xs` 같은)을 자동 패치.

운영 코드베이스 약 12개에 적용해 본 결과, 평균 자동 변환 적중률은 약 85%였다. 나머지 15%는 사람 손이 필요했다.

7.2 자주 손이 필요한 케이스

7.2.1 그림자·테두리 스케일 변화

v4는 그림자 스케일을 한 단계 작게 재정의했다.

- `shadow-sm` → 더 작아짐(이전 `shadow-xs`에 가깝다)

- `shadow` → 이전 `shadow-sm`에 가깝다

- 새로 `shadow-xs`가 생김

업그레이드 도구가 일괄 치환을 시도하지만, **시각 회귀**가 나기 쉽다. 디자인 시스템 스토리북을 도구가 돌린 뒤 한 번 훑어야 한다.

7.2.2 임의값 문법 변화

v3의 `bg-[color:var(--brand)]` 같은 임의값 문법은 v4에서 더 간결해졌다.

<!-- v3 -->

<!-- v4 -->

`bg-(--brand)` 같은 CSS 변수 단축 문법이 새로 들어왔다. 모든 임의값을 자동 변환하기는 어렵다.

7.2.3 `@apply` 사용 줄이기

v3는 `@apply`를 컴포넌트 클래스에 묶는 패턴이 흔했다.

/* v3 */

.btn-primary {

@apply bg-blue-500 text-white px-4 py-2 rounded;

}

v4도 `@apply`를 지원한다. 다만 두 가지 약점이 있다.

- `@apply`로 만든 클래스는 자동 콘텐츠 감지를 거치지 않으므로, **사용처가 명시되지 않으면 트리쉐이킹이 까다롭다.**

- v4 철학상 권장하는 패턴은 **React/Vue 컴포넌트로 묶는 것**이지, CSS 클래스로 묶는 것이 아니다.

가능하면 컴포넌트 추상으로 옮긴다.

// v4 권장

export function ButtonPrimary({ children }: { children: React.ReactNode }) {

return (

{children}

)

}

7.2.4 plugins 호환성

v3 시절 인기 플러그인 — `@tailwindcss/typography`·`@tailwindcss/forms`·`tailwindcss-animate` — 는 모두 v4 호환 버전을 냈다. 다만 일부 커뮤니티 플러그인은 아직 v3 API에 묶여 있다. 업그레이드 전에 의존성 트리를 한 번 훑는다.

pnpm why -r tailwindcss

7.2.5 darkMode 설정

/* v4 darkMode 설정 — class 전략 */

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

기본은 미디어 쿼리(`prefers-color-scheme: dark`). class 전략을 쓰려면 위 한 줄을 CSS에 추가한다.

7.3 점진적 마이그레이션 전략

큰 모노레포에서 한 번에 다 올릴 수 없으면 다음 단계로 쪼갠다.

1. **신규 패키지부터** v4로 만든다. v3·v4 패키지는 다른 빌드 파이프라인을 통해 공존시킬 수 있다.

2. **레거시 패키지를 격리**한다. v3 빌드는 별도 PostCSS 파이프라인으로 격리해 v4 측 출력을 오염시키지 않게 한다.

3. **공유 디자인 토큰을 단일 소스**로 둔다. CSS 변수로 토큰을 export 해두면, v3 측에서는 `theme.extend`로 읽고 v4 측에서는 `@theme` 안에서 그대로 받는다.

4. **시각 회귀 테스트를 자동화**한다. Chromatic·Loki 같은 스토리북 시각 회귀 도구가 큰 도움이 된다.

8장 · v4의 한계 — 정직하게

8.1 브라우저 요구사항

v4는 다음 브라우저를 최소로 요구한다.

- Safari 16.4 이상(2023년 3월)

- Chrome 111 이상(2023년 3월)

- Firefox 128 이상(2024년 7월)

이는 `@layer`·`color-mix()`·container query 같은 모던 CSS 기능에 의존하기 때문이다. **B2B/엔터프라이즈에서 IE11·구 Safari를 지원해야 한다면 v4는 사실상 옵션이 아니다.**

8.2 학습 곡선의 재설정

v3 시절 쌓은 노하우(`tailwind.config.js` 구조·플러그인 API·`content` 글로빙 노하우)가 상당 부분 새로 학습이 필요하다. 팀이 5명 이상이고 모두 v3에 익숙하다면, 마이그레이션 자체보다 **재학습 비용**이 더 클 수 있다.

8.3 디자인 토큰 도구 체인의 갭

Style Dictionary·Theo·Figma Tokens 같은 디자인 토큰 도구는 v4 출시 시점에 호환 출력이 없었다. 1년이 지난 지금은 대부분 지원하지만, 자체 토큰 빌드를 만든 팀이라면 변환 코드를 손봐야 한다.

8.4 `@apply` 의존도가 높은 코드베이스

v3 시절 컴포넌트 클래스를 CSS 측에서 `@apply`로 정의하는 패턴을 광범위하게 쓴 팀이라면, v4 자체는 동작하지만 권장 흐름과 어긋난다. 컴포넌트 추상으로 옮기는 데 비용이 든다.

8.5 일부 PostCSS 플러그인과의 충돌

v4가 Lightning CSS를 내부적으로 쓰기 때문에, 같은 일을 하는 PostCSS 플러그인(예: `postcss-preset-env`·`autoprefixer`)을 같이 돌리면 비효율 또는 충돌이 난다. PostCSS 체인을 정리해야 한다.

9장 · 언제 올리고 언제 미룰지

9.1 지금 올려야 하는 케이스

- **신규 프로젝트.** 더 빠른 빌드·더 단순한 설정·모던 CSS 네이티브 기능 — 굳이 v3로 시작할 이유가 없다.

- **Vite 기반 SPA·풀스택 앱.** v4의 진가가 가장 잘 드러난다.

- **빠른 HMR이 생산성 핵심인 디자인 시스템·UI 팀.** 증분 빌드 100배는 체감이 크다.

- **모던 브라우저만 지원하는 B2C 서비스.** 호환성 제약이 없다.

9.2 미루어도 되는 케이스

- **레거시 브라우저 지원이 필수인 엔터프라이즈.** Safari 15 이하·구 Edge가 트래픽 1% 이상이면 신중하게.

- **대규모 디자인 시스템 전환 중인 팀.** v4 마이그레이션과 디자인 시스템 개편을 동시에 하면 위험 부담이 두 배.

- **Next.js Pages Router 기반 레거시 앱.** PostCSS 파이프라인이 깊게 박혀 있으면 ROI가 작을 수 있다.

- **v3 플러그인에 강하게 의존하는 코드.** 호환 버전이 아직 안 나온 플러그인이 있다면 기다린다.

9.3 마이그레이션 ROI 계산

대략적인 ROI 계산식.

- 빌드 시간 절감 × 빌드 횟수/일 × 개발자 수 × 30일 = 월간 절감 시간

- 절감 시간 × 시간당 비용 = 월간 절감액

운영 사례. 개발자 25명, 일 평균 80 빌드, 빌드당 3.2초 절감 → 월 50,000초 ≈ 14시간. 이는 부수적이고, 진짜 가치는 **HMR이 즉시가 되어 흐름이 끊기지 않는다는 것**에 있다.

10장 · 함께 쓰면 좋은 도구 체인

| 분류 | 도구 | v4 통합 상태 |

| -- | -- | -- |

| Lint | `eslint-plugin-tailwindcss` | v0.6.x부터 v4 클래스 지원 |

| Formatter | `prettier-plugin-tailwindcss` | v0.6.x부터 v4 호환 |

| IDE | VS Code `Tailwind CSS IntelliSense` | v0.12 이상 |

| 시각 회귀 | Chromatic·Loki·Percy | 빌드와 무관(권장) |

| UI 키트 | shadcn/ui·Tremor·Catalyst | shadcn/ui CLI가 v4 모드 지원 |

| 디자인 토큰 | Style Dictionary 4 + tokens.json | CSS 변수 출력 시 즉시 호환 |

| Headless | Headless UI v2 | 의존성 없음 |

| Form 패키지 | `@tailwindcss/forms` v0.6 | v4 호환 |

shadcn/ui를 쓰는 팀이 가장 매끄럽게 마이그레이션한다. shadcn/ui CLI가 v4 템플릿을 디폴트로 깐다.

11장 · 함정 — 운영하며 부딪힌 것들

11.1 `@theme` 안의 변수 순서

`@theme` 안에서 한 변수가 다른 변수를 참조할 때 **선언 순서가 중요하다.**

@theme {

--color-base: #3b82f6;

/* OK — color-base가 앞에 선언됨 */

--color-primary: var(--color-base);

}

순서가 뒤집히면 var 참조가 `unset`이 된다.

11.2 글로벌 CSS 변수와 충돌

`--color-primary` 같은 일반적인 이름을 디자인 토큰 라이브러리도 쓰고 있다면, Tailwind 측 토큰과 충돌한다. **Tailwind 토큰은 반드시 접두사가 명확한 이름**(`--color-brand-*`·`--color-acme-*`)으로 유지한다.

11.3 Server Component에서 동적 클래스

서버 컴포넌트에서 동적으로 만든 클래스명이 자동 감지에 안 잡힐 수 있다. 다음 두 가지 중 하나로 해결한다.

- **전체 클래스명을 명시적으로** 적는다(문자열 보간 X).

// 안 좋음 — `bg-${color}-500`은 감지 안 됨

const className = `bg-${color}-500`

// 좋음 — 전체 문자열을 변수로

const colorMap = {

red: 'bg-red-500',

blue: 'bg-blue-500',

}

const className = colorMap[color]

- **safelist 대용**으로 CSS에 명시.

/* 동적 클래스를 강제 포함 */

@source inline("bg-red-500 bg-blue-500 bg-green-500");

11.4 `prose` 클래스 변화

`@tailwindcss/typography` v0.6의 `prose` 클래스가 v4에서 미묘하게 톤이 바뀌었다. 블로그·문서 사이트는 시각 회귀 검증이 필요하다.

11.5 Storybook 통합

Storybook 8 이상은 Vite 빌더를 쓰면 `@tailwindcss/vite`가 즉시 동작한다. Webpack 빌더를 쓴다면 PostCSS 경로로 가야 한다. 새 Storybook 셋업에서는 Vite 빌더가 디폴트다.

에필로그 — 도입 체크리스트와 안티패턴

12.1 도입 체크리스트

- [ ] 브라우저 요구사항이 프로젝트 지원 범위와 맞는다(Safari 16.4+).

- [ ] 의존하는 Tailwind 플러그인 모두 v4 호환 버전이 있다.

- [ ] PostCSS 체인에서 `autoprefixer`를 제거한다.

- [ ] CI에서 시각 회귀 테스트를 활성화했다.

- [ ] 디자인 토큰 빌드 파이프라인이 CSS 변수 출력으로 정렬되어 있다.

- [ ] 자동 업그레이드 CLI 결과를 PR로 묶고, 변환 적중률을 사람이 검수한다.

- [ ] `darkMode: 'class'` 사용 시 `@custom-variant dark`를 정확히 추가했다.

- [ ] 임의값 문법(`bg-[color:var(--x)]` → `bg-(--x)`)을 정리했다.

12.2 안티패턴

- **`@apply` 남용.** v3 시절 패턴을 그대로 가져오는 것. v4 철학과 어긋난다.

- **PostCSS와 Vite 플러그인 동시 사용.** 둘 중 하나만 쓴다. 둘 다 쓰면 빌드가 두 번 일어난다.

- **`autoprefixer` 잔존.** 이미 v4가 처리하므로 빼야 한다.

- **`tailwind.config.js`를 그대로 둔다.** 자동 업그레이드 도구가 CSS로 옮긴 뒤에도 옛 JS 파일을 안 지우면 혼란이 생긴다.

- **`content` 배열을 JS로 다시 추가하려는 시도.** v4는 다른 메커니즘이다. `@source`로 표현한다.

- **임의값으로 모든 디자인 토큰을 표현.** 토큰을 `@theme`에 정의해서 의미를 주는 게 v4의 흐름이다.

- **버전 통합 없이 일부만 v4.** 모노레포에서 한 앱은 v4, 다른 앱은 v3인 채로 공유 패키지를 같이 쓰면 클래스가 충돌한다.

12.3 다음 글 예고

다음 글에서는 **shadcn/ui v2와 Tailwind v4의 조합으로 디자인 시스템을 처음부터 만드는 실전기**를 다룬다. 토큰 모델·접근성·다크 모드·테마 스왑·Server Component 호환까지 한 번에 통합한 사례가 될 것이다.

참고 / References

- Tailwind CSS v4.0 GA 발표 — https://tailwindcss.com/blog/tailwindcss-v4

- Tailwind CSS v4.0 Alpha 발표 — https://tailwindcss.com/blog/tailwindcss-v4-alpha

- Oxide 엔진 소개 — https://tailwindcss.com/blog/tailwindcss-v4-beta

- 업그레이드 가이드 공식 문서 — https://tailwindcss.com/docs/upgrade-guide

- Tailwind v4 인스톨 가이드(Vite) — https://tailwindcss.com/docs/installation/using-vite

- Theme 변수 가이드 — https://tailwindcss.com/docs/theme

- `@source` 디렉티브 가이드 — https://tailwindcss.com/docs/detecting-classes-in-source-files

- Lightning CSS 공식 — https://lightningcss.dev/

- OKLCH 색 공간 입문 — https://oklch.com/

- Container Query MDN — https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_container_queries

- color-mix() MDN — https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix

- CSS Cascade Layer MDN — https://developer.mozilla.org/en-US/docs/Web/CSS/@layer

- shadcn/ui v2 Tailwind v4 가이드 — https://ui.shadcn.com/docs/tailwind-v4

- prettier-plugin-tailwindcss — https://github.com/tailwindlabs/prettier-plugin-tailwindcss

- eslint-plugin-tailwindcss — https://github.com/francoismassart/eslint-plugin-tailwindcss

현재 단락 (1/315)

2025년 1월 22일, Tailwind CSS v4.0이 GA로 풀렸다. 발표문의 첫 줄은 단호했다. **"Tailwind CSS 4.0은 처음부터 다시 만든 새 프레임워크입니다...

작성 글자: 0원문 글자: 13,569작성 단락: 0/315