스타일 · Tailwind CSS · 구성 · v4 변화

Tailwind CSS · 구성과 v4 변화

Tailwind?미리 만들어 둔 작은 클래스 조각class 에 끼워 모양을 입히는 방식입니다. 이 장에서는 어떻게 설치하고 설정하는지, 그리고 버전 4에서 무엇이 바뀌었는지를 봅니다. 이 프로젝트는 정확히 Tailwind CSS v4 (4.1.x) 를 씁니다.

1. 유틸리티-퍼스트란

보통 CSS?.card { padding:16px; ... } 처럼 이름을 짓고 규칙을 모아 만듭니다. Tailwind는 반대로, padding:16px 같은 속성 하나하나를 이미 p-4 라는 작은 클래스로 잘라 둡니다. 그래서 별도 CSS 파일 없이 HTML?class 에 조각을 붙여 쓰면 끝납니다.

요리에 비유하면, 매번 양파를 사 와서 직접 써는(=CSS 직접 작성) 대신 이미 깍둑썰기 해 둔 재료 묶음(p-4, flex, rounded-lg)에서 필요한 것만 골라 담는 셈입니다. 재료의 크기·모양이 정해져 있어 결과물도 일관됩니다.

Tailwind 클래스는 Tailwind 엔진이 있어야 진짜 CSS로 바뀝니다. 아래는 똑같은 HTML(클래스 그대로)인데, 왼쪽은 엔진이 없어 클래스가 무시된 맨몸이고, 오른쪽은 엔진이 켜져 디자인이 적용된 모습이에요.

/* 기존 CSS 방식 — 이름 짓고 규칙을 모음 */
.card {
  display: flex;
  gap: 8px;
  padding: 16px;
  border-radius: 8px;
}
<div class="card">...</div>

/* Tailwind 방식 — 작은 조각을 class 에 직접 조합 */
<div class="flex gap-2 p-4 rounded-lg">...</div>

p-4 = padding 1rem(16px), gap-2 = gap 0.5rem(8px). 숫자는 정해진 척도(scale)라 1단위가 4px씩 늘어납니다.

2. 설치와 구성 (v4)

v4의 핵심 한 줄: 설정이 자바스크립트 파일이 아니라 CSS 안으로 들어왔다는 점입니다. 이 프로젝트는 Vite를 쓰므로 @tailwindcss/vite 플러그인을 붙이고, CSS 맨 위에 @import "tailwindcss"; 한 줄만 적으면 동작합니다.

1) 설치 — 패키지 두 개를 받습니다.

# Tailwind 본체 + Vite 플러그인
npm install tailwindcss @tailwindcss/vite

2) Vite 플러그인 등록vite.config.ts 의 plugins 에 추가합니다.

import { defineConfig } from "vite"
import tailwindcss from "@tailwindcss/vite"

export default defineConfig({
  plugins: [
    tailwindcss(),   // 이 한 줄이 Tailwind를 빌드에 끼워 넣음
  ],
})

3) CSS에서 불러오기 — 메인 CSS 파일 맨 위에 한 줄.

@import "tailwindcss";
실제 study-frontend의 global.css

이 프로젝트는 정말로 이렇게 시작합니다. study-frontend/src/app/global.css 첫 부분입니다.

@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";

/* Tailwind가 클래스를 찾아 스캔할 경로 */
@source "../**/*.{ts,tsx}";

@import "tailwindcss"; 한 줄이 v3의 지시문 세 줄(@tailwind base/components/utilities)을 대신합니다. 아래 @import 들은 애니메이션 유틸과 Shadcn? 기본 스타일을 얹는 것입니다.

3. @theme 으로 디자인 토큰 정하기

색·폰트·여백 같은 디자인 토큰(반복해서 쓰는 기본값)을 v4에서는 CSS 안 @theme {} 블록에 적습니다. 여기에 적은 변수는 단순한 CSS 변수가 아니라, Tailwind에게 "이 이름으로 유틸리티 클래스도 만들어 줘"라고 시키는 특별한 선언입니다.

@theme회사의 공식 색·폰트 팔레트표입니다. 표에 "mint-500은 이 색"이라고 한 번 등록하면, 이후 어디서나 bg-mint-500 으로 그 색을 꺼내 쓸 수 있습니다.
@import "tailwindcss";

@theme {
  /* 색 — 만들면 bg-mint-500, text-mint-500 클래스가 생김 */
  --color-mint-500: oklch(0.72 0.11 178);
  /* 폰트 — font-script 클래스로 사용 */
  --font-script: "Great Vibes", cursive;
  /* 여백 척도 1단위 — p-*, gap-* 등의 기준 (기본값 0.25rem = 16px 기준 4px) */
  --spacing: 0.25rem;
}
네임스페이스(접두사)만들어지는 클래스
--color-*색: bg-*, text-*, border-*
--font-*글꼴: font-sans
--text-*글자 크기: text-xl
--spacing-*여백/크기: px-4, max-h-16
--radius-*모서리: rounded-sm
--breakpoint-*반응형 기준점: sm:, md:

다른 변수를 참조할 때는 @theme inline 을 씁니다. 이 프로젝트가 실제로 그렇게 합니다 — :root 에 oklch 색 변수를 두고, @theme inline 에서 그것을 Tailwind 토큰으로 연결합니다.

/* study-frontend/src/app/global.css 발췌 */
:root {
  --background: oklch(1 0 0);
  --primary: oklch(0.59 0.14 242);
  --radius: 0.5rem;
}

@theme inline {
  /* var()로 위 변수를 참조 → bg-background, bg-primary 클래스 생성 */
  --color-background: var(--background);
  --color-primary: var(--primary);
  --radius-lg: var(--radius);
}

그래서 화면에서는 bg-background, text-foreground, bg-primary 같은 클래스로 테마 색을 꺼내 씁니다.

@source — 클래스 찾을 경로 알려주기

Tailwind는 코드에 실제로 쓰인 클래스만 골라 만듭니다(안 쓰는 건 버려 가볍게). v4는 보통 자동으로 파일을 감지하지만, 범위를 넓히거나 명시하고 싶으면 @source "../**/*.{ts,tsx}"; 처럼 적습니다. 이 프로젝트도 이 한 줄로 .ts/.tsx 전체를 스캔합니다.

4. v3 → v4 무엇이 바뀌었나

큰 흐름은 "설정이 JS에서 CSS로 옮겨졌다"입니다. v3 방식은 참고로만, 기본은 v4입니다.

항목v3 방식 (옛날)v4 방식 (지금)
설정 파일 tailwind.config.js (자바스크립트) CSS 안 @theme {}
불러오기 @tailwind base; @tailwind components; @tailwind utilities; @import "tailwindcss"; 한 줄
Vite 연결 PostCSS 플러그인 설정 @tailwindcss/vite 플러그인
스캔 경로 config의 content: [...] 배열 자동 감지 + 필요 시 @source
빌드 방식(JIT) 옵션으로 켜야 함 기본 내장(항상 필요한 것만 생성)
기본 색 팔레트 rgb/hex 위주 oklch(더 또렷하고 일관된 색)
v4는 최신 브라우저가 필요

v4는 @property·color-mix() 같은 최신 CSS 기능을 써서 Safari 16.4+, Chrome 111+, Firefox 128+ 이상이 필요합니다. 아주 오래된 브라우저를 지원해야 하면 v3를 써야 합니다.

5. 임의값 — 정해진 척도 밖의 값

p-4, w-64 처럼 정해진 척도로 안 되는, 딱 그 한 번만 필요한 값은 대괄호 [...] 로 적습니다. 이를 임의값(arbitrary value)이라 합니다.

<!-- 척도에 없는 정확한 너비/색을 직접 지정 -->
<div class="w-[317px] bg-[#1da1f2] top-[13px]">...</div>

<!-- @theme 변수도 임의값으로 꺼내 쓸 수 있음 -->
<div class="bg-[var(--primary)]">...</div>
남용은 금물

임의값은 "예외"용입니다. 자주 쓰는 값이라면 @theme 에 토큰으로 등록해 이름으로 부르는 편이 일관성에 좋습니다.

6. @apply 로 클래스 묶기

같은 유틸리티 조합이 여러 곳에 반복되면, @apply 로 묶어 하나의 클래스 이름으로 만들 수 있습니다. 이 프로젝트도 global.css 에서 실제로 씁니다(예: .help { @apply text-muted-foreground }).

/* 반복되는 조합을 한 이름으로 */
.btn-primary {
  @apply px-4 py-2 rounded-lg bg-primary text-white;
}
<button class="btn-primary">저장</button>
주의 — 처음부터 @apply로 가지 말 것

@apply 를 많이 쓰면 결국 다시 "이름 짓고 규칙 모으는" 옛 CSS 방식으로 돌아가, Tailwind의 장점이 줄어듭니다. 보통은 HTML에서 직접 조합하고, 정말 반복이 심한 곳에서만 @apply 또는 React? 컴포넌트로 묶는 게 권장됩니다.

7. cn() — 조건부·충돌 클래스 합치기

React에서는 상황에 따라 클래스를 켜고 끄거나 합쳐야 합니다. 이때 이 프로젝트는 cn() 유틸을 씁니다. cnclsx(조건부 클래스 정리)와 tailwind-merge(충돌하는 Tailwind 클래스 중 뒤엣것만 남김)를 합친 함수입니다.

// study-frontend/src/lib/utils.ts 실제 코드
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

왜 필요할까요? Tailwind 클래스는 나중 것이 이기는 게 아니라 CSS 우선순위로 정해져, 같은 종류가 겹치면 의도와 다르게 보일 수 있습니다. twMerge 가 이를 정리합니다.

// p-2 와 p-4 가 충돌 → twMerge 가 뒤(p-4)만 남김
cn("p-2 text-sm", "p-4")          // → "text-sm p-4"

// 조건부 — active 일 때만 색 추가 (clsx 가 false 를 걸러냄)
cn("rounded-lg border", active && "bg-blue-50 border-blue-400")
function Card({ active }) {
  return (
    <div className={cn(
      "flex gap-2 p-4 rounded-lg border",
      active && "bg-blue-50 border-blue-400"
    )}>
      내용
    </div>
  )
}

8. 직접 해보기 (라이브)

아래는 진짜 Tailwind v4가 브라우저에서 실시간으로 컴파일하는 데모입니다. 클래스를 바꾸고 실행을 눌러 보세요.

임의값 [...] 과 반응형 접두사 md: 도 직접 확인해 봅니다.

한눈에 — 구성 핵심

무엇v4에서
불러오기@import "tailwindcss"; (CSS 맨 위)
Vite 연결@tailwindcss/vite 플러그인
토큰 정의@theme {} / 참조 시 @theme inline
스캔 경로자동 감지 + @source "..."
예외 값임의값 class="w-[317px]"
조합 묶기@apply (남용 주의)
조건부/충돌 병합cn() = clsx + tailwind-merge
색 표기oklch(...) 기본
이 프로젝트와의 관계

이 프로젝트 프런트엔드는 Tailwind 4.1.18 + @tailwindcss/vite 조합으로 스타일링하고, global.css 에서 @import "tailwindcss"; 로 시작합니다. 프런트엔드 환경 전반은 프런트엔드 환경 에서, 클래스 충돌 병합용 cnsrc/lib/utils.ts 에 정의돼 있습니다.

다음 단계