03 — Tailwind CSS
한 줄 답: Tailwind는 “클래스를 합성해 컴포넌트를 만든다” 는 Atomic CSS 사상의 가장 성공한 구현체다. v4(2025)는 CSS-first config·
@utility·Oxide 엔진으로 JS 설정 파일을 없애고 빌드를 10배 빠르게 했다. CSS 캐스케이드를 회피하는 길에서 이용하는 길로 전환한 분기점.
Why — 왜 Atomic CSS인가
전통 CSS의 문제:
/* 컴포넌트별 클래스 → 컴포넌트 수만큼 CSS 증가 */
.card { padding: 16px; background: white; border-radius: 8px; }
.notice { padding: 16px; background: white; border-radius: 8px; }
.dialog { padding: 16px; background: white; border-radius: 8px; }
/* 같은 4 속성이 3번 반복 */Atomic CSS의 발상:
.p-4 { padding: 16px; }
.bg-white { background: white; }
.rounded { border-radius: 8px; }<div class="p-4 bg-white rounded">card</div>
<div class="p-4 bg-white rounded">notice</div>
<div class="p-4 bg-white rounded">dialog</div>효과:
- CSS는 유틸리티 N개에서 멈춘다 (선형 증가 → 상한).
- HTML이 길어지지만 CSS는 짧아진다.
- 컴포넌트마다 이름을 짓지 않아도 된다 (BEM의 인지 부하 해소).
- 디자인 토큰을 강제한다 (
p-4는 4*4px=16px로 고정 → 임의 값 못 씀).
How — Tailwind의 동작 원리
JIT(Just-In-Time) 엔진 — v3.0(2021) 이후 기본
- 소스 파일을 실시간 스캔해서 사용된 클래스만 추출.
- 임의 값 지원:
class="top-[117px]"→top: 117px;즉시 생성. - 빌드 시 수십 GB였던 가상 CSS가 수십 KB로 줄어듦.
Tailwind v4 (2025) — Oxide 엔진
- Rust 기반 Oxide 엔진 — v3 대비 전체 빌드 ~10x, 증분 빌드 ~100x 빠름.
- Zero-config —
tailwind.config.js불필요. - CSS-first config — 설정을 CSS 파일에서 직접.
- 자동 콘텐츠 감지 —
.gitignore기반.
What — v4의 CSS-first config
tailwind.config.js(JS) → app.css(CSS) 한 곳으로 통합.
v3 (JS config)
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{html,js}'],
theme: {
extend: {
colors: { brand: '#2563eb' },
spacing: { 18: '4.5rem' },
},
},
};v4 (CSS config)
/* app.css */
@import "tailwindcss";
@theme {
--color-brand: #2563eb;
--spacing-18: 4.5rem;
--font-display: "Inter Display", sans-serif;
}효과:
- 토큰이 순수 CSS 변수로 출력 → 런타임에서도
var(--color-brand)로 사용 가능. - DevTools에서 바로 확인.
- JS 빌드 도구 없이도 동작 (Vite/Webpack/CDN 모두).
What — v4의 @utility (커스텀 유틸리티)
이전엔 @layer utilities로 추가했는데, v4는 전용 디렉티브.
@utility tab-4 {
tab-size: 4;
}
@utility scrollbar-hide {
&::-webkit-scrollbar { display: none; }
-ms-overflow-style: none;
scrollbar-width: none;
}<pre class="tab-4">...</pre>
<div class="scrollbar-hide">...</div>장점:
- Variant (
hover:,md:) 자동 적용. - 빌드 타임에 정상 tree-shake.
What — Variants 시스템
Tailwind의 핵심 표현력은 클래스에 조건을 붙이는 variant.
| Variant | 예시 | 의미 |
|---|---|---|
| 상태 | hover:bg-blue-600 | :hover |
| 포커스 | focus-visible:ring-2 | :focus-visible |
| 반응형 | md:flex | @media (width >= 768px) |
| 다크모드 | dark:bg-gray-900 | @media (prefers-color-scheme: dark) 또는 .dark & |
| 자식 | [&>p]:mt-2 | & > p |
| 그룹 | group-hover:opacity-100 | 부모에 .group |
| 컨테이너 쿼리 | @md:flex | container query (v4) |
:has() | has-[input:invalid]:border-red-500 | :has() |
@starting-style | starting:opacity-0 | (v4) |
조합:
<button class="bg-blue-500 hover:bg-blue-600 md:px-6 dark:bg-blue-700">— 하나의 HTML 클래스 속성에 6가지 분기를 표현. 같은 일을 BEM으로 하면 btn btn--primary btn--md btn--dark btn--hovering ... 식의 modifier 폭주.
What — @apply (컴포넌트화)
긴 유틸 합성을 컴포넌트 클래스로 묶고 싶을 때.
.btn-primary {
@apply px-4 py-2 rounded bg-blue-500 text-white hover:bg-blue-600;
}<button class="btn-primary">Save</button>언제 쓰나:
- 같은 합성을 서버 렌더 HTML(템플릿)에서 반복 →
@apply로 묶기. - 디자인 시스템 공용 컴포넌트 정의.
언제 쓰지 마라:
- React/Vue 컴포넌트가 이미 컴포넌트 추상화를 한다 →
<Button />안에서 클래스 합성하면 됨. @apply남발은 유틸리티-퍼스트의 장점을 무효화.
What — Tailwind v4의 @layer 통합
v4는 모든 Tailwind CSS를 *명시적 @layer*로 감싼다.
@layer theme, base, components, utilities;
@layer base { /* preflight (reset) */ }
@layer components { /* @apply로 만든 클래스 */ }
@layer utilities { /* px-4, bg-blue-500 등 */ }→ 사용자가 비-Tailwind CSS를 자유롭게 끼워도 utilities가 항상 이긴다 (layer 순서 덕분). !important 거의 불필요.
What-if — Tailwind의 함정
1) 클래스 길이 폭주
<button class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2">
Save
</button>(이건 shadcn/ui의 실제 Button 클래스다.) HTML이 읽기 어려워진다.
해결:
- React/Vue 컴포넌트로 클래스 합성을 캡슐화 →
<Button variant="default" size="lg" />. clsx/tailwind-variants로 조건부 결합 깔끔히.- 진짜 자주 반복되는 패턴만
@apply.
2) “Tailwind는 디자인 시스템을 대체하지 않는다”
<button class="bg-blue-500 text-white">A</button>
<button class="bg-blue-600 text-white">B</button>
<button class="bg-sky-500 text-white">C</button>— 셋 다 비슷한 파란색 버튼인데 클래스가 다르다. Tailwind는 팔레트만 주지, 의미는 안 준다. semantic-token + @theme 으로 한 번 더 추상화해야 함:
@theme {
--color-action-primary: theme(--color-blue-500);
}<button class="bg-action-primary text-white">A</button>3) 임의 값(arbitrary values) 남발
<div class="w-[473px] mt-[19px] text-[#2a3b4c]">— 토큰 시스템 무시. 디자인 시스템의 의미가 사라짐. 임의 값은 토큰화의 마지막 수단이어야.
4) tailwind.config.js(v3) 비대화
v3 시절 거대한 theme.extend 객체에 수천 줄. v4는 CSS-first로 분리·import 가능해 해소.
5) Tailwind만으로 복잡한 애니메이션 시도
<div class="transition-all duration-300 ease-out hover:scale-110 hover:rotate-12 ...">— 3D 트랜스폼·스프링·시퀀스 애니메이션은 Framer Motion/View Transitions API가 적합. Tailwind는 간단한 트랜지션만.
What-if — Tailwind를 어떤 프로젝트에서 쓰면 안 되나
| 상황 | 권장 |
|---|---|
| 정적 마케팅 사이트, 1인 개발 | Tailwind 적합 |
| 대규모 디자인 시스템 (Figma + Storybook + 50명) | Tailwind + 의미 토큰 레이어 |
| Email HTML | 인라인 스타일 필요 — Tailwind 부적합 |
| 라이브러리 (npm 패키지) | CSS Modules 또는 CSS-in-JS — 소비자의 Tailwind와 충돌 위험 |
| Web Components / Shadow DOM | Shadow root에 Tailwind가 안 들어감 — 인라인 또는 CSS Modules |
매우 동적인 스타일 (color: ${userColor}) | CSS Variables 또는 CSS-in-JS |
Insight — Atomic CSS의 재림
Tailwind의 성공은 CSS의 패배가 아니라 모던 CSS의 승리 다.
2014년 Adam Wathan이 Tailwind를 만들기 전, 비슷한 시도들이 있었다:
- 2011 Atomic CSS (Yahoo, Steve Souders)
- 2014 BASSCSS, Tachyons
- 2016 Atomizer
이들은 모두 클래스를 합성해 컴포넌트를 만든다는 사상이었지만 두 가지 한계가 있었다:
- 클래스 수가 폭발한다 → 빌드된 CSS가 수 MB.
- 임의 값 불가 →
top: 117px이 안 됨.
2021년 Tailwind v3의 JIT 엔진이 두 한계를 동시에 해결했다 — 소스 코드를 실시간 스캔해 쓰인 것만 생성, 임의 값도 바로 컴파일. 완벽한 tree-shake.
흥미로운 점은 Tailwind v4가 모던 CSS의 결과물이라는 것이다:
- v4의
@layer통합은 CSS Cascade 5(2022) 가 가능케 했다. @theme디렉티브는 CSS Custom Properties +@property위에 서 있다.@utility는 CSS Nesting(2023) 위에 서 있다.
CSS 자체가 언어로 충분히 발전했기 때문에, Tailwind는 얇은 어댑터가 될 수 있었다. v4의 출력 CSS를 보면 거의 순수 모던 CSS다.
Adam Wathan은 v4 발표에서 “Tailwind v4는 가능한 한 적게 우리가 만들고, 가능한 한 많이 CSS 자체가 하도록 했다” 고 했다. 도구가 얇아지는 것 — 그게 언어가 두꺼워졌다는 신호다.
Atomic CSS의 재림은 단순한 트렌드 회귀가 아니다. 모던 CSS가 따라잡은 결과 — 캐스케이드 회피가 아니라 이용하는 길이 열렸기 때문이다.
요약 + Mermaid
- Tailwind는 OOCSS의 극단 — 클래스 1개 = 속성 1개.
- JIT(v3)로 빌드 폭발 문제 해결, 임의 값 지원.
- v4(2025): Oxide(Rust) 엔진, CSS-first config,
@utility,@layer통합. - **
@apply**는 컴포넌트화 도구 — 적게 쓰는 게 정답. - 함정: 클래스 폭주, 의미 부재, 임의 값 남발.
- 해결: 컴포넌트로 캡슐화 + 의미 토큰 레이어 + 디자인 시스템.
다음: 04-css-modules — 빌드 타임 스코핑의 또 다른 길.