Tailwind CSS
보통 웹사이트를 꾸밀 때는 별도의 CSS 파일에 코드를 길게 작성해야 한다. 하지만 Tailwind는 "이미 만들어진 태그(Class)"를 HTML에 바로 붙여서 디자인하는 방식이다.
1. Basics — 설치
NPM 설치 (Vite 기준)
- 터미널에 아래 명령어 입력
npm install tailwindcss @tailwindcss/vite
- vite.config.js 파일에 아래 코드 추가
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite' // ← 추가
export default defineConfig({
plugins: [react(), tailwindcss()],
})
- src/index.css 에 아래 코드 추가
@import "tailwindcss";
CDN 설치
HTML의 <head> 태그 안에 아래 코드를 삽입한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
</head>
<body>
<h1 class="text-3xl font-bold text-blue-600 underline">
Hello, Tailwind!
</h1>
</body>
</html>
Tip: 유틸리티 우선(Utility-First)
- 기존 방식:
.btn { background: blue; }클래스를 정의하고 HTML에서 적용한다.- Tailwind 방식:
class="bg-blue-500 text-white"와 같이 이미 정의된 클래스를 조합한다.
2. Sizing
2.1. Width
| 클래스 | 값 |
|---|---|
w-{n} |
spacing 스케일 (w-4 = 16px) |
w-full |
100% |
w-screen |
100vw |
w-auto |
auto |
w-1/2, w-1/3, w-2/3 |
50%, 33.3%, 66.6% |
w-1/4, w-3/4 |
25%, 75% |
w-[200px] |
임의값 |
min-w-0, min-w-full |
최솟값 |
max-w-sm ~ max-w-7xl |
최댓값 (sm=24rem~) |
2.2. Height
| 클래스 | 값 |
|---|---|
h-{n} |
spacing 스케일 |
h-full |
100% |
h-screen |
100vh |
h-auto |
auto |
min-h-screen |
min-height: 100vh |
min-h-0 |
min-height: 0 |
h-dvh |
100dvh (동적 뷰포트) |
h-svh |
100svh (소형 뷰포트) |
2.3. size 단축 클래스
| 클래스 | 값 |
|---|---|
size-10 |
w-10 h-10 |
size-[200px] |
w-200px h-200px |
// 1단계: w-full, h-16 — 고정 크기
<div className="w-full">가로 100%</div>
<div className="h-16">높이 64px</div>
// 2단계: 비율 — 부모 기준 백분율
<div className="w-1/2">부모의 50%</div>
<div className="w-1/3">부모의 33%</div>
<div className="w-2/3">부모의 66%</div>
// 3단계: max-w — 최대 너비 제한 (반응형에 자주 사용)
<div className="w-full max-w-xl">최대 576px까지만</div>
<div className="w-full max-w-4xl mx-auto">최대 너비 + 가운데 정렬</div>
// 4단계: size — 가로·세로 동시 지정
<div className="size-10">40x40</div>
<div className="size-16">64x64</div>
<div className="size-[200px]">200x200</div>
3. Typography — 타이포그래피
3.1. Font Size (text-*)
| 클래스 | 크기 | line-height |
|---|---|---|
text-xs |
12px | 1rem |
text-sm |
14px | 1.25rem |
text-base |
16px | 1.5rem |
text-lg |
18px | 1.75rem |
text-xl |
20px | 1.75rem |
text-2xl |
24px | 2rem |
text-3xl |
30px | 2.25rem |
text-4xl |
36px | 2.5rem |
text-5xl |
48px | 1 |
text-6xl |
60px | 1 |
3.2. Font Weight
font-thin /* 100 */
font-extralight /* 200 */
font-light /* 300 */
font-normal /* 400 */
font-medium /* 500 */
font-semibold /* 600 */
font-bold /* 700 */
font-extrabold /* 800 */
font-black /* 900 */
3.3. Text 색상
text-white text-black
text-gray-500 text-gray-900
text-yellow-300 text-yellow-400
text-blue-600 text-red-500
text-white/80 /* 투명도 포함 */
3.4. Text 정렬 · 변환 · 장식
/* 정렬 */
text-left text-center text-right text-justify
/* 변환 */
uppercase lowercase capitalize normal-case
/* 장식 */
underline line-through no-underline overline
/* 오버플로 처리 */
truncate /* 한 줄 말줄임 */
line-clamp-2 /* 2줄 말줄임 */
line-clamp-3 /* 3줄 말줄임 */
whitespace-nowrap /* 줄바꿈 금지 */
3.5. Letter Spacing · Line Height
/* 자간 */
tracking-tight tracking-normal tracking-wide tracking-widest
/* 행간 */
leading-none /* 1 */
leading-tight /* 1.25 */
leading-normal /* 1.5 */
leading-loose /* 2 */
leading-[1.8] /* 임의값 */
// 1단계: text-size — 글자 크기
<p className="text-sm">작은 텍스트 (14px)</p>
<p className="text-base">기본 텍스트 (16px)</p>
<p className="text-xl">큰 텍스트 (20px)</p>
<p className="text-4xl">제목용 텍스트 (36px)</p>
// 2단계: font-weight — 굵기
<p className="font-normal">일반 굵기</p>
<p className="font-bold">굵게</p>
<p className="font-black">가장 굵게</p>
// 3단계: text-color — 색상
<p className="text-white">흰색</p>
<p className="text-gray-400">회색</p>
<p className="text-yellow-400">노란색</p>
// 4단계: text-align — 정렬
<p className="text-left">왼쪽 정렬</p>
<p className="text-center">가운데 정렬</p>
<p className="text-right">오른쪽 정렬</p>
// 5단계: truncate / line-clamp — 넘치는 텍스트 처리
<p className="truncate">한 줄을 넘으면 말줄임표로 잘림...</p>
<p className="line-clamp-2">두 줄을 넘으면 잘림. 긴 텍스트 긴 텍스트 긴 텍스트 긴 텍스트...</p>
4. Backgrounds — 배경
4.1. 기본 색상 팔레트
Tailwind의 색상은 색상명-숫자 형태이다. 숫자가 클수록 어두워진다 (50 가장 밝음, 950 가장 어두움).
| 색상명 | 대표 클래스 예시 |
|---|---|
| slate, gray, zinc, neutral, stone | 무채색 계열 |
| red, orange, amber, yellow | 따뜻한 계열 |
| lime, green, emerald, teal | 초록 계열 |
| cyan, sky, blue, indigo | 파란 계열 |
| violet, purple, fuchsia, pink, rose | 보라·분홍 계열 |
4.2. 배경색 적용
/* 배경색 */
bg-white bg-black
bg-gray-900 bg-gray-800
bg-yellow-400 bg-blue-600
/* 투명도 (슬래시 문법) */
bg-black/50 /* background: rgba(0,0,0,0.5) */
bg-white/10 /* background: rgba(255,255,255,0.1) */
bg-black/90 /* GOFLIX 헤더에 사용 */
4.3. 테두리 색상
border-gray-200 border-yellow-400 border-transparent
// 1단계: bg-color — 단색 배경
<div className="bg-black">검은 배경</div>
<div className="bg-gray-800">어두운 회색 배경</div>
<div className="bg-yellow-400">노란 배경</div>
// 2단계: bg-color + text-color — 배경과 텍스트 색상 함께
<div className="bg-gray-900 text-white">어두운 배경에 흰 텍스트</div>
<div className="bg-yellow-400 text-black">노란 배경에 검은 텍스트</div>
// 3단계: bg-color/투명도 — 슬래시로 불투명도 조절
<div className="bg-black/50">50% 투명한 검은 배경</div>
<div className="bg-black/90">90% 불투명한 검은 배경</div>
// 4단계: bg-gradient — 그라디언트 배경
<div className="bg-gradient-to-r from-black to-gray-800">좌 → 우</div>
<div className="bg-gradient-to-t from-black to-transparent">아래 → 위(투명)</div>
5. Borders — 테두리 & Ring
5.1. Border Width · Color · Style
<div class="border border-gray-200">1px 테두리</div>
<div class="border-2 border-blue-500">2px 파란 테두리</div>
<div class="border-4 border-dashed border-red-400">4px 점선</div>
<div class="border-b border-gray-200">하단만</div>
<div class="border-t-2 border-l-2 border-blue-300">상단+좌측</div>
<div class="rounded-sm">약간</div>
<div class="rounded-lg">중간</div>
<div class="rounded-2xl">큰 (카드)</div>
<div class="rounded-full">원형</div>
None: 1:
border— 기본 1px 회색 테두리
2:border-2— 2px,border-blue-500— 파란색
3:border-dashed— 점선 스타일
5:border-b— 아래쪽만,border-t-2 border-l-2— 위쪽과 왼쪽 2px
9~12:rounded-*— 모서리 반지름 (sm<lg<2xl, full=50%)
18. GOFLIX 프로젝트 적용 예시
Tailwind로 영화 정보 사이트를 만드는 실전 예시입니다.
18.1. Header 분석
<header className="fixed top-0 left-0 w-full py-4 px-2 bg-black/90 z-50">
{/* fixed: 스크롤해도 고정 */}
{/* top-0 left-0: 화면 최상단 좌측 */}
{/* w-full: 전체 너비 */}
{/* py-4 px-2: 상하 16px, 좌우 8px 패딩 */}
{/* bg-black/90: 검은색 배경 90% 불투명 */}
{/* z-50: 다른 요소 위에 표시 */}
<div className="container mx-auto">
<Link to="/">
<h1 className="text-3xl text-yellow-300 font-bold">
{/* text-3xl: 30px 텍스트 */}
{/* text-yellow-300: 연한 노란색 */}
{/* font-bold: 굵은 글씨 */}
GOFLIX
</h1>
</Link>
</div>
</header>
18.2. 영화 카드 컴포넌트 패턴
function MovieCard({ movie }) {
return (
<div className="group relative rounded-lg overflow-hidden cursor-pointer
transition-transform duration-300 hover:scale-105
shadow-lg hover:shadow-yellow-400/30">
{/* 포스터 이미지 */}
<img
src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
alt={movie.title}
className="w-full aspect-[2/3] object-cover"
/>
{/* 호버 오버레이 */}
<div className="absolute inset-0 bg-black/70 opacity-0 group-hover:opacity-100
transition-opacity duration-300 flex flex-col justify-end p-4">
<h3 className="text-white font-bold text-sm line-clamp-2">{movie.title}</h3>
<p className="text-yellow-300 text-xs mt-1">⭐ {movie.vote_average.toFixed(1)}</p>
</div>
</div>
);
}
18.3. 전체 페이지 레이아웃
// App.jsx
<main className="bg-black text-white min-h-screen pt-16">
{/* bg-black: 검은 배경 */}
{/* text-white: 기본 텍스트 흰색 */}
{/* min-h-screen: 최소 전체 높이 */}
{/* pt-16: 상단 64px (fixed 헤더 높이만큼) */}
<Outlet />
</main>
// 홈 페이지
<section className="container mx-auto px-4 py-8">
<h2 className="text-2xl font-bold mb-6 text-yellow-300">인기 영화</h2>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{movies.map(movie => <MovieCard key={movie.id} movie={movie} />)}
</div>
</section>
18.4. 버튼 스타일 패턴
{/* 주요 액션 버튼 */}
<button className="bg-yellow-400 text-black font-bold px-6 py-2 rounded-lg
hover:bg-yellow-300 active:scale-95
transition-all duration-200 cursor-pointer">
자세히 보기
</button>
{/* 보조 버튼 (아웃라인) */}
<button className="border-2 border-white text-white font-semibold px-6 py-2 rounded-lg
hover:bg-white hover:text-black
transition-all duration-200 cursor-pointer">
목록 추가
</button>
18.5. 영화 상세 페이지
function MovieDetail({ movie }) {
return (
<div className="relative min-h-screen bg-black text-white">
{/* 배경 이미지 */}
<div className="relative h-[60vh]">
<img
src={`https://image.tmdb.org/t/p/original${movie.backdrop_path}`}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/40 to-transparent" />
</div>
{/* 콘텐츠 */}
<div className="container mx-auto px-4 -mt-32 relative z-10">
<div className="flex flex-col md:flex-row gap-8">
<img
src={`https://image.tmdb.org/t/p/w300${movie.poster_path}`}
className="w-40 md:w-56 rounded-xl shadow-2xl flex-none mx-auto md:mx-0"
/>
<div className="flex-1">
<h1 className="text-3xl md:text-5xl font-black">{movie.title}</h1>
<div className="flex items-center gap-3 mt-3 text-sm text-gray-400">
<span className="text-yellow-400 font-bold">⭐ {movie.vote_average.toFixed(1)}</span>
<span>{movie.release_date.slice(0, 4)}</span>
</div>
<p className="mt-4 text-gray-300 leading-relaxed max-w-2xl line-clamp-4 md:line-clamp-none">
{movie.overview}
</p>
<div className="flex gap-3 mt-6">
<button className="bg-yellow-400 text-black font-bold px-6 py-3 rounded-lg
hover:bg-yellow-300 active:scale-95 transition-all duration-200">
▶ 재생
</button>
<button className="border border-white/40 text-white px-6 py-3 rounded-lg
hover:bg-white/10 transition-all duration-200">
+ 내 목록
</button>
</div>
</div>
</div>
</div>
</div>
)
}
Tip: 클래스 조합 팁
복잡한 컴포넌트는 관심사별로 줄을 나눠 읽기 좋게 작성한다.
레이아웃 → 크기 → 색상 → 상태 순으로 배치하면 유지보수가 편하다.
Warning: 자주 하는 실수
hover:scale-105만 쓰면 애니메이션이 없음 →transition-transform duration-300필수opacity-0 group-hover:opacity-100을 쓸 때 부모에group클래스 빠뜨리지 않기fixed헤더가 있으면 main에pt-{헤더높이}필수 (겹침 방지)
6. Layout — 레이아웃 (Display · Position)
6.1. Display
| 클래스 | CSS | 설명 |
|---|---|---|
block |
display: block |
블록 요소 |
inline |
display: inline |
인라인 요소 |
inline-block |
display: inline-block |
인라인 블록 |
flex |
display: flex |
플렉스 컨테이너 |
inline-flex |
display: inline-flex |
인라인 플렉스 |
grid |
display: grid |
그리드 컨테이너 |
hidden |
display: none |
숨김 |
<div className="block">block — 한 줄 전체를 차지</div>
<span className="inline">inline — 내용 크기만큼, 줄바꿈 없음</span>
<span className="inline-block">inline-block — 줄바꿈 없이 나란히</span>
<div className="hidden">hidden — 화면에서 완전히 사라짐</div>
6.2. Position
| 클래스 | CSS |
|---|---|
static |
position: static |
relative |
position: relative |
absolute |
position: absolute |
fixed |
position: fixed |
sticky |
position: sticky |
6.3. 위치 지정 (inset)
| 클래스 | 의미 |
|---|---|
top-0 |
위에서 0 |
left-0 |
왼쪽에서 0 |
inset-0 |
상하좌우 모두 0 |
inset-x-0 |
좌우만 0 |
inset-y-0 |
상하만 0 |
// 1단계: relative — 자식의 기준점 역할
<div className="relative">
기준이 되는 부모
</div>
// 2단계: absolute + top/left — 부모 기준으로 위치 지정
<div className="relative">
<div className="absolute top-0 left-0">왼쪽 위에 배치</div>
<div className="absolute top-0 right-0">오른쪽 위에 배치</div>
</div>
// 3단계: inset-0 — 부모 전체를 꽉 채움
<div className="relative">
<div className="absolute inset-0">부모 전체 크기로 채움</div>
</div>
// 4단계: fixed — 스크롤해도 화면에 고정
<header className="fixed top-0 left-0">스크롤해도 고정되는 헤더</header>
// 5단계: sticky — 스크롤 시 특정 위치에 달라붙음
<h2 className="sticky top-0">스크롤하면 상단에 고정되는 제목</h2>
6.4. z-index
// 숫자가 클수록 위에 표시됨
<div className="z-0">맨 아래</div>
<div className="z-10">그 위</div>
<div className="z-50">가장 위</div>
7. Flexbox
가장 많이 쓰는 레이아웃 방식. 방향, 줄바꿈, 자식 크기를 클래스 조합으로 제어한다.
7.1. 방향 (flex-direction)
| 클래스 | CSS |
|---|---|
flex-row |
flex-direction: row (기본값) |
flex-row-reverse |
flex-direction: row-reverse |
flex-col |
flex-direction: column |
flex-col-reverse |
flex-direction: column-reverse |
7.2. 줄바꿈 · 자식 크기
/* 줄바꿈 */
flex-wrap flex-nowrap flex-wrap-reverse
/* 자식 요소 크기 제어 */
flex-1 /* flex: 1 1 0% — 공간을 균등 분할 */
flex-auto /* flex: 1 1 auto */
flex-none /* flex: none — 크기 고정 */
/* 개별 크기 */
grow /* flex-grow: 1 */
grow-0 /* flex-grow: 0 */
shrink /* flex-shrink: 1 */
shrink-0 /* flex-shrink: 0 */
// 1단계: flex — 자식을 가로로 나열
<div className="flex">
<div>아이템 1</div>
<div>아이템 2</div>
<div>아이템 3</div>
</div>
// 2단계: flex-col — 세로로 나열
<div className="flex flex-col">
<div>위</div>
<div>중간</div>
<div>아래</div>
</div>
// 3단계: gap — 자식 사이 간격
<div className="flex gap-4">
<div>아이템 1</div>
<div>아이템 2</div>
<div>아이템 3</div>
</div>
// 4단계: flex-1 / flex-none — 자식 크기 제어
<div className="flex">
<div className="flex-none">고정 크기</div>
<div className="flex-1">남은 공간을 모두 차지</div>
</div>
// 5단계: flex-wrap — 넘치면 줄바꿈
<div className="flex flex-wrap gap-2">
<div>태그 1</div>
<div>태그 2</div>
<div>태그 3</div>
<div>태그 4</div>
<div>태그 5</div>
</div>
7.3. Container
container는 화면 크기에 따라 max-width를 자동으로 맞춰주는 클래스다. 단독으로는 왼쪽 정렬이므로, mx-auto를 함께 써서 가운데 정렬한다.
| 브레이크포인트 | 적용되는 max-width |
|---|---|
| 기본 (모바일) | 100% |
| sm (640px~) | 640px |
| md (768px~) | 768px |
| lg (1024px~) | 1024px |
| xl (1280px~) | 1280px |
| 2xl (1536px~) | 1536px |
// 1단계: container 기본 — 반응형 max-width 자동 적용
<div className="container">콘텐츠</div>
// 2단계: mx-auto 추가 — 가운데 정렬
<div className="container mx-auto">콘텐츠</div>
// 3단계: 실전 — 페이지 전체 레이아웃에 적용
<main>
<section className="container mx-auto">
<h2>인기 영화</h2>
...
</section>
<section className="container mx-auto">
<h2>최신 영화</h2>
...
</section>
</main>
8. Grid
2차원 레이아웃. 영화 카드 목록처럼 열을 고정하거나 반응형으로 배치할 때 사용한다.
8.1. 열(Column) 정의
| 클래스 | CSS |
|---|---|
grid-cols-1 |
grid-template-columns: repeat(1, 1fr) |
grid-cols-2 ~ 12 |
2~12 등분 |
grid-cols-none |
컬럼 정의 없음 |
grid-cols-[200px_1fr] |
임의값 지정 |
8.2. 간격
gap-4 /* row-gap + column-gap: 1rem */
gap-x-6 /* column-gap: 1.5rem */
gap-y-4 /* row-gap: 1rem */
8.3. 자식 요소 병합 (span)
col-span-2 /* 열 2칸 차지 */
col-span-full /* 전체 열 차지 */
row-span-2 /* 행 2칸 차지 */
// 1단계: grid + grid-cols — 열 개수 지정
<div className="grid grid-cols-3">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
// 2단계: gap — 셀 사이 간격 추가
<div className="grid grid-cols-3 gap-4">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
// 3단계: col-span — 특정 셀이 여러 칸을 차지
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2">넓은 셀 (2칸)</div>
<div>1칸</div>
<div>1칸</div>
<div className="col-span-full">전체 너비 셀</div>
</div>
// 4단계: 반응형 — 화면 크기에 따라 열 수 변경
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{movies.map(movie => (
<MovieCard key={movie.id} movie={movie} />
))}
</div>
9. Alignment — 정렬 (Justify · Align)
Flexbox와 Grid에서 아이템을 정렬하는 유틸리티이다.
9.1. 주축 정렬 (justify-content)
| 클래스 | CSS |
|---|---|
justify-start |
justify-content: flex-start |
justify-center |
justify-content: center |
justify-end |
justify-content: flex-end |
justify-between |
justify-content: space-between |
justify-around |
justify-content: space-around |
justify-evenly |
justify-content: space-evenly |
9.2. 교차축 정렬 (align-items)
| 클래스 | CSS |
|---|---|
items-start |
align-items: flex-start |
items-center |
align-items: center |
items-end |
align-items: flex-end |
items-stretch |
align-items: stretch |
items-baseline |
align-items: baseline |
9.3. Grid 정렬
place-items-center /* align-items + justify-items: center */
place-content-center
// 1단계: justify-center — 가로 중앙 정렬
<div className="flex justify-center">
<div>가운데 배치됨</div>
</div>
// 2단계: justify-between — 양 끝으로 배치
<div className="flex justify-between">
<div>왼쪽</div>
<div>오른쪽</div>
</div>
// 3단계: items-center — 세로 중앙 정렬
<div className="flex items-center">
<div>세로 중앙에 배치됨</div>
</div>
// 4단계: justify + items 조합 — 완전 중앙
<div className="flex justify-center items-center">
<div>정중앙에 배치됨</div>
</div>
// 5단계: place-items-center — grid에서 셀 안 정중앙
<div className="grid grid-cols-3 place-items-center">
<div>★</div>
<div>★</div>
<div>★</div>
</div>
10. Spacing — 간격 (Margin · Padding · Gap)
기본 단위: 1 = 4px (0.25rem). 4 = 16px, 8 = 32px처럼 4배수로 계산한다.
10.1. Margin
| 클래스 | 방향 | 예시 |
|---|---|---|
m-{n} |
전체 | m-4 → 16px 사방 |
mx-{n} |
좌우 | mx-auto → 가운데 정렬 |
my-{n} |
상하 | my-8 |
mt-{n} |
위 | mt-16 |
mr-{n} |
오른쪽 | |
mb-{n} |
아래 | |
ml-{n} |
왼쪽 | |
-m-{n} |
음수 마진 | -mt-4 |
10.2. Padding (같은 패턴)
p-4 px-6 py-3 pt-2 pr-4 pb-6 pl-2
10.3. 자주 쓰는 값 참고표
| 클래스 숫자 | rem | px |
|---|---|---|
| 1 | 0.25rem | 4px |
| 2 | 0.5rem | 8px |
| 3 | 0.75rem | 12px |
| 4 | 1rem | 16px |
| 5 | 1.25rem | 20px |
| 6 | 1.5rem | 24px |
| 8 | 2rem | 32px |
| 10 | 2.5rem | 40px |
| 12 | 3rem | 48px |
| 16 | 4rem | 64px |
| 20 | 5rem | 80px |
| 24 | 6rem | 96px |
// 1단계: p — 사방 패딩
<div className="p-4">사방 16px 여백</div>
<div className="p-8">사방 32px 여백</div>
// 2단계: px / py — 방향별 패딩
<div className="px-6">좌우만 24px</div>
<div className="py-3">상하만 12px</div>
<div className="px-6 py-3">좌우 24px + 상하 12px</div>
// 3단계: mt / mb — 위아래 마진
<div className="mt-8">위에 32px 여백</div>
<div className="mb-4">아래에 16px 여백</div>
// 4단계: mx-auto — 가운데 정렬
<div className="mx-auto">가운데 정렬</div>
// 5단계: space-y — 자식 요소 사이 세로 간격
<div className="space-y-4">
<div>첫 번째 항목</div>
<div>두 번째 항목</div>
<div>세 번째 항목</div>
</div>
11. Effects — 그림자 & 불투명도
11.1. Box Shadow
shadow-sm /* 작은 그림자 */
shadow /* 기본 그림자 */
shadow-md /* 중간 그림자 */
shadow-lg /* 큰 그림자 */
shadow-xl /* 더 큰 그림자 */
shadow-2xl /* 가장 큰 그림자 */
shadow-none /* 그림자 제거 */
/* 색상 그림자 */
shadow-lg shadow-black/50
11.2. Opacity (불투명도)
opacity-0 /* 완전 투명 */
opacity-50 /* 반투명 */
opacity-100 /* 불투명 */
// 1단계: shadow — 그림자 크기
<div className="shadow">기본 그림자</div>
<div className="shadow-md">중간 그림자</div>
<div className="shadow-xl">큰 그림자</div>
// 2단계: shadow-color — 색상 그림자
<div className="shadow-lg shadow-black/50">검은 그림자</div>
<div className="shadow-lg shadow-yellow-400/30">노란 그림자</div>
// 3단계: opacity — 요소 전체 투명도
<div className="opacity-100">완전 불투명</div>
<div className="opacity-50">반투명</div>
<div className="opacity-0">완전 투명 (공간은 차지)</div>
12. Filters — 필터 (Blur · Backdrop)
blur-none blur-sm blur blur-md blur-lg blur-xl
/* 배경 블러 (유리 효과) */
backdrop-blur-sm backdrop-blur-md backdrop-blur-lg
// 1단계: blur — 요소 자체를 흐리게
<img className="blur-none" src={img} alt="" />
<img className="blur-sm" src={img} alt="" />
<img className="blur-lg" src={img} alt="" />
// 2단계: backdrop-blur — 요소 뒤 배경을 흐리게
<div className="backdrop-blur-sm">약하게 흐린 배경</div>
<div className="backdrop-blur-md">중간 흐린 배경</div>
<div className="backdrop-blur-lg">많이 흐린 배경</div>
// 3단계: backdrop-blur + bg-*/투명도 — 유리 효과
<div className="backdrop-blur-md bg-white/10">유리처럼 반투명한 카드</div>
13. Transitions & Animation — 트랜지션
transition /* all 속성 */
transition-colors /* 색상만 */
transition-transform
transition-opacity
duration-150 /* 150ms */
duration-300 /* 300ms */
ease-in ease-out ease-in-out
// 1단계: transition — 변화를 부드럽게 (hover와 함께)
<div className="transition hover:opacity-50">호버 시 서서히 반투명</div>
// 2단계: duration — 애니메이션 시간
<div className="transition duration-150 hover:opacity-50">빠름 (150ms)</div>
<div className="transition duration-500 hover:opacity-50">느림 (500ms)</div>
// 3단계: transition-colors — 색상만 전환
<div className="transition-colors duration-200 hover:bg-yellow-400">
호버 시 배경색만 전환
</div>
// 4단계: transition-transform — 크기·위치 전환
<div className="transition-transform duration-300 hover:scale-105">
호버 시 크기만 전환
</div>
// 5단계: ease — 가속도 곡선
<div className="transition duration-300 ease-in hover:opacity-0">천천히 시작</div>
<div className="transition duration-300 ease-out hover:opacity-0">천천히 끝</div>
<div className="transition duration-300 ease-in-out hover:opacity-0">양쪽 천천히</div>
14. Transforms — 변형
scale-95 scale-100 scale-105 scale-110
-translate-y-1 translate-x-2
rotate-45 rotate-90 rotate-180
// 1단계: scale — 크기 변형
<div className="scale-75">75% 축소</div>
<div className="scale-100">기본 크기</div>
<div className="scale-110">110% 확대</div>
// 2단계: translate — 위치 이동 (공간은 유지)
<div className="translate-x-4">오른쪽으로 16px</div>
<div className="-translate-y-2">위로 8px</div>
// 3단계: rotate — 회전
<div className="rotate-45">45도 회전</div>
<div className="rotate-90">90도 회전</div>
<div className="rotate-180">180도 회전</div>
// 4단계: transition + transform — 부드러운 변형
<div className="transition-transform duration-300 hover:scale-110">
호버 시 서서히 확대
</div>
<div className="transition-transform duration-300 hover:-translate-y-1">
호버 시 서서히 위로 이동
</div>
15. Interactivity — 상호작용 (State Variants · Cursor)
15.1. Cursor
cursor-pointer cursor-default cursor-not-allowed cursor-wait
15.2. 주요 State Variants
클래스 앞에 상태:를 붙이면 해당 상태일 때만 적용된다.
| Variant | 설명 | 예시 |
|---|---|---|
hover: |
마우스 올림 | hover:bg-yellow-400 |
focus: |
포커스 | focus:ring-2 |
active: |
클릭 중 | active:scale-95 |
disabled: |
비활성화 | disabled:opacity-50 |
checked: |
체크됨 | checked:bg-blue-600 |
group-hover: |
부모 hover 시 | group-hover:opacity-100 |
peer-focus: |
형제 포커스 시 | peer-focus:text-blue-500 |
first: |
첫 번째 자식 | first:mt-0 |
last: |
마지막 자식 | last:mb-0 |
odd: / even: |
홀수/짝수 | odd:bg-gray-50 |
// 1단계: hover: — 마우스를 올렸을 때
<div className="hover:opacity-50">호버 시 반투명</div>
<div className="hover:bg-yellow-400">호버 시 노란 배경</div>
// 2단계: focus: — 포커스 받았을 때 (입력 필드 등)
<input className="focus:outline-none" />
<input className="focus:ring-2 focus:ring-yellow-400" />
// 3단계: active: — 클릭하는 순간
<button className="active:scale-95">클릭 시 줄어듦</button>
<button className="active:opacity-70">클릭 시 반투명</button>
// 4단계: disabled: — 비활성화 상태
<button disabled className="disabled:opacity-50">비활성 (흐리게)</button>
<button disabled className="disabled:cursor-not-allowed">비활성 (커서 금지)</button>
// 5단계: group-hover: — 부모에 hover 시 자식에 적용
<div className="group">
<p className="group-hover:text-yellow-400">부모에 호버하면 색상 변경</p>
</div>
// 부모 hover 시 숨겨진 오버레이 표시
<div className="group relative">
<img src={poster} />
<div className="absolute inset-0 opacity-0 group-hover:opacity-100">
오버레이
</div>
</div>
// 6단계: odd: / even: — 홀짝 번째 스타일
<ul>
<li className="odd:bg-gray-900 even:bg-gray-800">항목 1 (홀수 → 어두운)</li>
<li className="odd:bg-gray-900 even:bg-gray-800">항목 2 (짝수 → 밝은)</li>
<li className="odd:bg-gray-900 even:bg-gray-800">항목 3 (홀수 → 어두운)</li>
</ul>
16. 반응형 (Breakpoint)
Tailwind는 모바일 퍼스트이다. 기본값이 모바일이고, 접두사로 더 큰 화면을 지정한다.
16.1. 기본 브레이크포인트
| 접두사 | 최소 너비 | 기기 |
|---|---|---|
| (없음) | 0px~ | 모바일 (기본) |
sm: |
640px~ | 소형 태블릿 |
md: |
768px~ | 태블릿 |
lg: |
1024px~ | 노트북 |
xl: |
1280px~ | 데스크탑 |
2xl: |
1536px~ | 대형 모니터 |
// 1단계: 브레이크포인트 하나 — md 이상에서 적용
<p className="text-sm md:text-xl">md 이상에서 커짐</p>
// 2단계: 브레이크포인트 두 개 — 단계적으로 변경
<p className="text-sm md:text-lg lg:text-2xl">화면이 커질수록 텍스트도 커짐</p>
// 3단계: hidden / block 조합 — 특정 화면에서 보이기/숨기기
<div className="hidden md:block">md 이상에서만 보임</div>
<div className="block md:hidden">md 미만에서만 보임 (모바일 전용)</div>
// 4단계: flex-col → flex-row — 모바일: 세로, 데스크탑: 가로
<div className="flex flex-col md:flex-row">
<div>첫 번째</div>
<div>두 번째</div>
</div>
// 5단계: grid-cols 반응형 — 화면 크기에 따라 열 수 변경
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div>카드 1</div>
<div>카드 2</div>
<div>카드 3</div>
<div>카드 4</div>
</div>
17. Dark Mode
17.1. 설정 (media 모드 — OS 자동)
// OS 다크모드 설정에 따라 자동 전환
<div className="bg-white dark:bg-gray-900">
라이트: 흰 배경 / 다크: 어두운 배경
</div>
17.2. 수동 토글 방식 (class 모드)
/* src/index.css */
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
// HTML 루트에 dark 클래스 추가/제거로 제어
document.documentElement.classList.toggle('dark')
// 1단계: dark: 텍스트 색상
<p className="text-black dark:text-white">라이트: 검은색 / 다크: 흰색</p>
<p className="text-gray-700 dark:text-gray-300">라이트: 진한 회색 / 다크: 밝은 회색</p>
// 2단계: dark: 배경 색상
<div className="bg-white dark:bg-gray-900">라이트: 흰 배경 / 다크: 어두운 배경</div>
<div className="bg-gray-100 dark:bg-gray-800">라이트: 밝은 회색 / 다크: 짙은 회색</div>
// 3단계: 텍스트 + 배경 조합
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
다크모드 대응 카드
</div>
// 4단계: 토글 버튼으로 직접 제어
function DarkToggle() {
const [dark, setDark] = useState(false)
const toggle = () => {
setDark(prev => !prev)
document.documentElement.classList.toggle('dark')
}
return (
<button onClick={toggle}>
{dark ? '🌙 다크' : '☀️ 라이트'}
</button>
)
}
18.1. Header 분석
<header className="fixed top-0 left-0 w-full py-4 px-2 bg-black/90 z-50">
{/* fixed: 스크롤해도 고정 */}
{/* top-0 left-0: 화면 최상단 좌측 */}
{/* w-full: 전체 너비 */}
{/* py-4 px-2: 상하 16px, 좌우 8px 패딩 */}
{/* bg-black/90: 검은색 배경 90% 불투명 */}
{/* z-50: 다른 요소 위에 표시 */}
<div className="container mx-auto">
<Link to="/">
<h1 className="text-3xl text-yellow-300 font-bold">
{/* text-3xl: 30px 텍스트 */}
{/* text-yellow-300: 연한 노란색 */}
{/* font-bold: 굵은 글씨 */}
GOFLIX
</h1>
</Link>
</div>
</header>
18.2. 영화 카드 컴포넌트 패턴
function MovieCard({ movie }) {
return (
<div className="group relative rounded-lg overflow-hidden cursor-pointer
transition-transform duration-300 hover:scale-105
shadow-lg hover:shadow-yellow-400/30">
{/* 포스터 이미지 */}
<img
src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
alt={movie.title}
className="w-full aspect-[2/3] object-cover"
/>
{/* 호버 오버레이 */}
<div className="absolute inset-0 bg-black/70 opacity-0 group-hover:opacity-100
transition-opacity duration-300 flex flex-col justify-end p-4">
<h3 className="text-white font-bold text-sm line-clamp-2">{movie.title}</h3>
<p className="text-yellow-300 text-xs mt-1">⭐ {movie.vote_average.toFixed(1)}</p>
</div>
</div>
);
}
18.3. 전체 페이지 레이아웃
// App.jsx
<main className="bg-black text-white min-h-screen pt-16">
{/* bg-black: 검은 배경 */}
{/* text-white: 기본 텍스트 흰색 */}
{/* min-h-screen: 최소 전체 높이 */}
{/* pt-16: 상단 64px (fixed 헤더 높이만큼) */}
<Outlet />
</main>
// 홈 페이지
<section className="container mx-auto px-4 py-8">
<h2 className="text-2xl font-bold mb-6 text-yellow-300">인기 영화</h2>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{movies.map(movie => <MovieCard key={movie.id} movie={movie} />)}
</div>
</section>
18.4. 버튼 스타일 패턴
{/* 주요 액션 버튼 */}
<button className="bg-yellow-400 text-black font-bold px-6 py-2 rounded-lg
hover:bg-yellow-300 active:scale-95
transition-all duration-200 cursor-pointer">
자세히 보기
</button>
{/* 보조 버튼 (아웃라인) */}
<button className="border-2 border-white text-white font-semibold px-6 py-2 rounded-lg
hover:bg-white hover:text-black
transition-all duration-200 cursor-pointer">
목록 추가
</button>
18.5. 영화 상세 페이지
function MovieDetail({ movie }) {
return (
<div className="relative min-h-screen bg-black text-white">
{/* 배경 이미지 */}
<div className="relative h-[60vh]">
<img
src={`https://image.tmdb.org/t/p/original${movie.backdrop_path}`}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/40 to-transparent" />
</div>
{/* 콘텐츠 */}
<div className="container mx-auto px-4 -mt-32 relative z-10">
<div className="flex flex-col md:flex-row gap-8">
<img
src={`https://image.tmdb.org/t/p/w300${movie.poster_path}`}
className="w-40 md:w-56 rounded-xl shadow-2xl flex-none mx-auto md:mx-0"
/>
<div className="flex-1">
<h1 className="text-3xl md:text-5xl font-black">{movie.title}</h1>
<div className="flex items-center gap-3 mt-3 text-sm text-gray-400">
<span className="text-yellow-400 font-bold">⭐ {movie.vote_average.toFixed(1)}</span>
<span>{movie.release_date.slice(0, 4)}</span>
</div>
<p className="mt-4 text-gray-300 leading-relaxed max-w-2xl line-clamp-4 md:line-clamp-none">
{movie.overview}
</p>
<div className="flex gap-3 mt-6">
<button className="bg-yellow-400 text-black font-bold px-6 py-3 rounded-lg
hover:bg-yellow-300 active:scale-95 transition-all duration-200">
▶ 재생
</button>
<button className="border border-white/40 text-white px-6 py-3 rounded-lg
hover:bg-white/10 transition-all duration-200">
+ 내 목록
</button>
</div>
</div>
</div>
</div>
</div>
)
}
Tip: 클래스 조합 팁
복잡한 컴포넌트는 관심사별로 줄을 나눠 읽기 좋게 작성한다.
레이아웃 → 크기 → 색상 → 상태 순으로 배치하면 유지보수가 편하다.
Warning: 자주 하는 실수
hover:scale-105만 쓰면 애니메이션이 없음 →transition-transform duration-300필수opacity-0 group-hover:opacity-100을 쓸 때 부모에group클래스 빠뜨리지 않기fixed헤더가 있으면 main에pt-{헤더높이}필수 (겹침 방지)