Tailwind CSS v4 마이그레이션 완벽 가이드: config 파일 없이 CSS만으로 설정하기
General

Tailwind CSS v4 마이그레이션 완벽 가이드: config 파일 없이 CSS만으로 설정하기


3줄 요약
  • Tailwind CSS v4는 2025년 1월 22일 GA 출시. Oxide(Rust) + Lightning CSS 엔진으로 전체 빌드는 최대 5배, 증분 빌드는 최대 100배 빠릅니다.
  • tailwind.config.js는 완전 삭제가 아닌 선택 사항으로 전환. CSS 파일의 @theme 지시어로 테마를 직접 정의하는 방식이 기본입니다.
  • 공식 CLI인 npx @tailwindcss/upgrade로 대부분의 설정을 자동 변환할 수 있지만, 클래스명 스케일 변경(shadow, blur, rounded)과 dark 모드 재설정은 수동 확인이 필요합니다.

v4가 출시되며 빌드 속도는 비약적으로 빨라졌지만, 익숙했던 tailwind.config.js가 사라지고 설정 방식이 통째로 바뀌었습니다. 이 글은 실제 마이그레이션 과정에서 수집한 에러 로그와 공식 문서를 바탕으로, v3 프로젝트를 v4로 전환하는 전 과정을 정리합니다.

자동 마이그레이션 툴로 커버되는 부분과 수동으로 해결해야 하는 부분을 명확히 구분해 두었으니, 상황에 맞는 섹션을 골라 읽으시면 됩니다.


Tailwind CSS v4, 무엇이 달라졌나?

v4의 핵심 변경점은 엔진 교체설정 방식 전환 두 가지입니다. 나머지 변경 사항은 이 두 방향에서 파생됩니다.

Oxide 엔진: Rust + Lightning CSS

v4 내부 엔진의 이름은 Oxide입니다. 가장 비용이 큰 병렬 처리 작업을 Rust로 이식하고, CSS 파싱·트랜스파일을 Lightning CSS에 위임했습니다. 프레임워크 코어 로직은 확장성을 위해 TypeScript로 유지했습니다.

공식 발표 기준 벤치마크는 다음과 같습니다.

시나리오v3.4v4.0개선율
전체 빌드 (tailwindcss.com 기준)960ms105ms약 9배
CSS 추가 시 증분 빌드44ms5ms약 8.8배
CSS 변화 없는 증분 빌드35ms192µs약 182배

일반적인 중간 규모 프로젝트에서도 전체 빌드가 378ms → 100ms 수준으로 줄어드는 것을 확인할 수 있습니다.

CSS-only 설정 방식

v3까지는 tailwind.config.js에서 테마, 플러그인, 콘텐츠 경로를 모두 설정했습니다. v4부터는 이 역할을 CSS 파일이 담당합니다.

  • @tailwind base, @tailwind components, @tailwind utilities 세 줄 → @import "tailwindcss" 한 줄로 통합
  • 테마 커스텀 → @theme 지시어로 CSS 파일 내 선언
  • 플러그인 → @plugin 지시어로 CSS 파일 내 등록
  • tailwind.config.js는 삭제가 아닌 선택 사항. 기존 파일을 유지하려면 CSS에서 @config "./tailwind.config.js"로 명시적 로드

주요 클래스명 변경 (스케일 시프트)

v4에서 일부 유틸리티의 크기 스케일이 한 단계 아래로 이동했습니다. 자동 마이그레이션 툴이 변환해 주지만, 수동 코드나 서드파티 라이브러리 사용 시 직접 확인해야 합니다.

v3v4
shadow-smshadow-xs
shadowshadow-sm
blur-smblur-xs
blurblur-sm
rounded-smrounded-xs
roundedrounded-sm
outline-noneoutline-hidden
ringring-3

bg-opacity-*, text-opacity-*, border-opacity-* 유틸리티는 완전히 제거됐습니다. bg-black/50 형태의 슬래시 표기법으로 대체합니다.

Tailwind CSS v3 vs v4 빌드 속도 비교 다이어그램

@tailwindcss/upgrade로 자동 마이그레이션하기

대부분의 경우 공식 업그레이드 CLI 하나로 해결됩니다.

사전 조건

  • Node.js 20 이상 필요
  • v3 프로젝트에서 실행 (v4가 이미 설치된 경우 동작하지 않음)

실행

npx @tailwindcss/upgrade

특정 CSS 파일만 대상으로 할 경우:

npx @tailwindcss/upgrade src/styles/tailwind.css

툴이 자동으로 처리하는 항목들입니다.

  • tailwindcss 패키지를 v4로 업그레이드
  • postcss.config.js의 플러그인을 @tailwindcss/postcss로 교체
  • CSS의 @tailwind base/components/utilities@import "tailwindcss"로 통합
  • tailwind.config.js의 테마 설정을 CSS @theme 블록으로 변환
  • 제거된 유틸리티(bg-opacity-* 등)를 슬래시 표기법으로 교체
  • 위에서 언급한 클래스명 스케일 시프트 자동 변환
@tailwindcss/upgrade CLI 실행 화면 일러스트

tailwind.config.js → CSS 수동 전환 가이드

자동 툴을 사용할 수 없거나, 변환 결과를 직접 제어하고 싶을 때의 수동 전환 방법입니다.

1. CSS 임포트 방식 변경

Before (v3)

@tailwind base;
@tailwind components;
@tailwind utilities;

After (v4)

@import "tailwindcss";

2. 테마 설정: @theme 지시어

tailwind.config.jstheme.extend 블록에 있던 내용을 @theme 지시어로 옮깁니다.

Before — tailwind.config.js

module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          500: '#6366f1',
          600: '#4f46e5',
        },
      },
      fontFamily: {
        sans: ['Pretendard', 'sans-serif'],
      },
      borderRadius: {
        xl: '1.25rem',
      },
    },
  },
}

After — globals.css

@import "tailwindcss";

@theme {
  --color-brand-500: #6366f1;
  --color-brand-600: #4f46e5;
  --font-sans: 'Pretendard', sans-serif;
  --radius-xl: 1.25rem;
}

이후 bg-brand-500, font-sans, rounded-xl 유틸리티를 동일하게 사용할 수 있습니다.

3. PostCSS 설정 변경

Before

// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

After

// postcss.config.mjs
export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
}

autoprefixer는 Lightning CSS가 내장 처리하므로 제거합니다.

4. Vite 프로젝트 설정

PostCSS 대신 Vite 플러그인을 사용하면 더 빠른 HMR 성능을 얻을 수 있습니다.

npm install @tailwindcss/vite
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [tailwindcss()],
})

CSS에서는 PostCSS 방식과 동일하게 @import "tailwindcss"를 사용합니다.

5. dark 모드 설정 변경

v3에서 darkMode: 'class'로 설정하던 방식이 CSS @custom-variant로 바뀌었습니다.

Before — tailwind.config.js

module.exports = {
  darkMode: 'class',
}

After — CSS

@import "tailwindcss";

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

데이터 어트리뷰트 기반으로 전환하려면:

@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));

v4의 기본값은 prefers-color-scheme 미디어 쿼리입니다. 클래스 토글 방식을 사용하는 프로젝트는 이 설정이 빠지면 dark: 유틸리티가 전혀 작동하지 않으니 반드시 추가해야 합니다.


마이그레이션 중 자주 발생하는 에러와 해결책

Next.js App Router 환경

Next.js는 Vite가 아닌 Webpack/Turbopack 기반이므로 @tailwindcss/vite 대신 @tailwindcss/postcss를 사용합니다.

npm install tailwindcss @tailwindcss/postcss postcss
// postcss.config.mjs
const config = {
  plugins: { '@tailwindcss/postcss': {} },
}
export default config
/* app/globals.css */
@import "tailwindcss";

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

@theme {
  /* 커스텀 테마 변수 */
}

create-next-app@latest로 생성한 프로젝트가 v3를 설치하는 경우가 있습니다. 이때는 직접 npm install tailwindcss@latest로 업그레이드 후 위 설정을 적용하세요. .next 캐시 디렉터리를 삭제하고 재시작하면 대부분 해결됩니다.

⚠️ Next.js 브라우저 호환성 주의
Tailwind CSS v4는 내부적으로 CSS cascade layers와 OKLCH 색상 함수를 사용합니다. Safari 16.4+, Chrome 111+, Firefox 128+ 미만 환경에서는 일부 유틸리티가 렌더링되지 않을 수 있습니다. 레거시 브라우저를 지원해야 하는 프로젝트는 마이그레이션 전 타겟 브라우저를 먼저 확인하세요.

CSS Modules / Vue·Svelte <style> 블록에서 @apply 미작동

별도로 번들링되는 스타일시트에서는 @theme로 정의한 변수에 직접 접근할 수 없습니다. @reference 지시어로 메인 CSS 파일을 참조해야 합니다.

/* Button.module.css */
@reference "../../app/globals.css";

.btn {
  @apply px-4 py-2 rounded-lg bg-brand-500 text-white;
}

또는 CSS 변수를 직접 참조하는 방식이 더 권장됩니다.

.btn {
  background-color: var(--color-brand-500);
  color: var(--color-white);
}

daisyUI v5 마이그레이션

daisyUI v5는 Tailwind CSS v4를 기반으로 완전 재작성되었습니다. v3의 require("daisyui") 방식은 동작하지 않습니다.

Before (daisyUI v4 + Tailwind v3)

// tailwind.config.js
module.exports = {
  plugins: [require('daisyui')],
}

After (daisyUI v5 + Tailwind v4)

/* globals.css */
@import "tailwindcss";
@plugin "daisyui";

테마 설정도 동일하게 CSS에서 처리합니다.

@plugin "daisyui" {
  themes: light --default, dark --prefersdark;
}

CSS arbitrary value 문법 변경

v4에서 CSS 변수를 arbitrary value로 사용하는 문법이 바뀌었습니다.

항목v3v4
CSS 변수 arbitrary valuebg-[--brand-color]bg-(--brand-color)
!important 마커!bg-red-500bg-red-500!
hover (터치 디바이스 제외)hover:bg-blue-500hover:bg-blue-500 (자동으로 @media (hover: hover) 적용)
Tailwind CSS v4 마이그레이션 트러블슈팅 일러스트

에러 없는 v4 기본 초기화 코드 (Boilerplate)

Vite 기반 프로젝트와 Next.js 프로젝트 각각의 최소 설정입니다. 복사해서 바로 사용하세요.

Vite 프로젝트 (React, SolidJS, Astro 등)

// vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react(), tailwindcss()],
})
/* src/index.css */
@import "tailwindcss";

/* 클래스 기반 다크 모드 사용 시 */
@custom-variant dark (&:where(.dark, .dark *));

@theme {
  --font-sans: 'Pretendard', ui-sans-serif, system-ui, sans-serif;
  /* 커스텀 색상 변수 */
  /* --color-primary-500: #6366f1; */
}

Next.js 프로젝트

// postcss.config.mjs
export default {
  plugins: { '@tailwindcss/postcss': {} },
}
/* app/globals.css */
@import "tailwindcss";

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

@theme {
  --font-sans: 'Pretendard', ui-sans-serif, system-ui, sans-serif;
}
💡 마이그레이션 체크리스트
  • [ ] npx @tailwindcss/upgrade 실행 및 변환 결과 확인
  • [ ] @tailwindcss/postcss 또는 @tailwindcss/vite 패키지 설치
  • [ ] CSS 파일에 @import “tailwindcss” 단일 임포트로 교체
  • [ ] dark 모드 사용 시 @custom-variant dark 추가
  • [ ] shadow, blur, rounded 클래스 스케일 시프트 수동 확인
  • [ ] daisyUI 등 서드파티 플러그인 v4 호환 버전으로 업그레이드
  • [ ] CSS Modules에서 @apply 사용 시 @reference 추가
  • [ ] 타겟 브라우저 호환성 확인 (Safari 16.4+, Chrome 111+, Firefox 128+)

Vite + React를 처음 설정하는 경우라면 [1편] 리액트 코드 기초 : 한 줄씩 완벽 이해하기에서 기본 프로젝트 구조부터 확인하실 수 있습니다. 또한 CSS 성능 관점에서 프레임워크를 선택할 때는 Solid.js + Astro가 2026년에 React를 위협하는 이유도 함께 읽어보시길 권장합니다.


자주 묻는 질문 (FAQ)

tailwind.config.js를 반드시 삭제해야 하나요?

삭제하지 않아도 됩니다. CSS 파일에 @config "./tailwind.config.js"를 추가하면 기존 파일을 계속 사용할 수 있습니다. 단, v4에서 제거된 옵션(corePlugins, separator 등)이 포함된 경우 해당 항목만 제거해야 합니다. 장기적으로는 CSS 방식으로 전환을 권장합니다.

PostCSS 방식과 Vite 플러그인 방식의 차이는 무엇인가요?

@tailwindcss/vite는 Vite의 빌드 파이프라인에 직접 통합되어 HMR 성능이 더 빠릅니다. Next.js, Create React App 등 PostCSS 기반 빌드 시스템을 사용하는 환경에서는 @tailwindcss/postcss를 사용해야 합니다.

v3 프로젝트에 v4를 점진적으로 도입할 수 있나요?

하나의 CSS 파일 내에서 v3와 v4를 혼용할 수는 없습니다. 프로젝트 단위로 전환하는 것이 일반적입니다. 모노레포 구조라면 패키지별로 분리해 순차 마이그레이션이 가능합니다.

@apply가 더 이상 작동하지 않습니다.

CSS Modules, Vue/Svelte <style> 블록 등 별도 번들링 환경에서는 @reference "경로/globals.css"를 파일 상단에 추가해야 합니다. 메인 CSS와 같은 번들 내에 있다면 별도 처리 없이 동작합니다.

업그레이드 후 dark 모드가 전혀 작동하지 않습니다.

v4의 기본값은 prefers-color-scheme 미디어 쿼리입니다. v3에서 darkMode: 'class'를 사용했다면, CSS에 @custom-variant dark (&:where(.dark, .dark *));를 반드시 추가해야 합니다.