Tailwind CSS 완벽 가이드 | 설치·사용법·커스터마이징·반응형·다크모드
이 글의 핵심
Tailwind CSS를 실무에 활용하는 완벽 가이드입니다. 설치부터 유틸리티 클래스, 커스터마이징, 반응형 디자인, 다크모드, 성능 최적화까지 실전 예제로 정리했습니다.
실무 경험 공유: 대규모 스트리밍 플랫폼의 관리자 대시보드를 Tailwind CSS로 리뉴얼하면서, 기존 10,000줄의 CSS를 3,000줄로 줄이고 빌드 시간을 70% 단축한 경험을 바탕으로 작성했습니다.
들어가며: “CSS 작성이 너무 번거로워요”
실무 문제 시나리오
시나리오 1: 클래스 이름 짓기가 힘들어요
.button-primary-large-rounded-blue처럼 긴 클래스 이름을 매번 고민합니다. Tailwind는 유틸리티 클래스로 즉시 스타일링할 수 있습니다.
시나리오 2: CSS 파일이 너무 커져요
프로젝트가 커질수록 CSS 파일이 수천 줄이 됩니다. Tailwind는 사용하지 않는 스타일을 자동으로 제거합니다.
시나리오 3: 일관성 없는 디자인
개발자마다 margin: 15px, margin: 20px처럼 제각각입니다. Tailwind는 디자인 시스템을 강제합니다.
flowchart LR
subgraph Before["기존 CSS"]
A1[클래스 이름 고민]
A2[CSS 파일 비대화]
A3[일관성 부족]
end
subgraph After["Tailwind CSS"]
B1[유틸리티 클래스]
B2[자동 최적화]
B3[디자인 시스템]
end
Before --> After
1. Tailwind CSS 시작하기
설치 (Next.js)
npx create-next-app@latest my-app
cd my-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
}
/* app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
설치 (Vite + React)
npm create vite@latest my-app -- --template react
cd my-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
첫 번째 컴포넌트
export default function Button() {
return (
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
클릭하세요
</button>
);
}
2. 핵심 유틸리티 클래스
레이아웃
// Flexbox
<div className="flex items-center justify-between">
<div>왼쪽</div>
<div>오른쪽</div>
</div>
// Grid
<div className="grid grid-cols-3 gap-4">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
// 중앙 정렬
<div className="flex items-center justify-center h-screen">
<div>중앙</div>
</div>
간격 (Spacing)
// Margin
<div className="m-4">margin: 1rem</div>
<div className="mx-4">margin-left/right: 1rem</div>
<div className="mt-8">margin-top: 2rem</div>
// Padding
<div className="p-4">padding: 1rem</div>
<div className="px-6 py-3">padding-x: 1.5rem, padding-y: 0.75rem</div>
// 간격 스케일
// 0, 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64
타이포그래피
// 폰트 크기
<h1 className="text-4xl font-bold">제목</h1>
<p className="text-base">본문</p>
<small className="text-sm text-gray-500">작은 텍스트</small>
// 폰트 굵기
<p className="font-light">Light (300)</p>
<p className="font-normal">Normal (400)</p>
<p className="font-bold">Bold (700)</p>
// 텍스트 정렬
<p className="text-left">왼쪽</p>
<p className="text-center">중앙</p>
<p className="text-right">오른쪽</p>
// 줄 높이
<p className="leading-tight">좁은 줄 간격</p>
<p className="leading-relaxed">넓은 줄 간격</p>
색상
// 배경색
<div className="bg-blue-500">파란색</div>
<div className="bg-red-600">빨간색</div>
<div className="bg-gray-100">회색</div>
// 텍스트 색상
<p className="text-blue-500">파란 텍스트</p>
<p className="text-red-600">빨간 텍스트</p>
// 색상 스케일: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900
테두리 및 그림자
// 테두리
<div className="border border-gray-300 rounded">기본 테두리</div>
<div className="border-2 border-blue-500 rounded-lg">두꺼운 테두리</div>
<div className="rounded-full">원형</div>
// 그림자
<div className="shadow-sm">작은 그림자</div>
<div className="shadow-md">중간 그림자</div>
<div className="shadow-lg">큰 그림자</div>
<div className="shadow-xl">매우 큰 그림자</div>
3. 반응형 디자인
브레이크포인트
| 접두사 | 최소 너비 | CSS |
|---|---|---|
sm: | 640px | @media (min-width: 640px) |
md: | 768px | @media (min-width: 768px) |
lg: | 1024px | @media (min-width: 1024px) |
xl: | 1280px | @media (min-width: 1280px) |
2xl: | 1536px | @media (min-width: 1536px) |
모바일 퍼스트
// 모바일: 세로, 태블릿 이상: 가로
<div className="flex flex-col md:flex-row">
<div className="w-full md:w-1/2">왼쪽</div>
<div className="w-full md:w-1/2">오른쪽</div>
</div>
// 모바일: 1열, 태블릿: 2열, 데스크톱: 3열
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
// 모바일: 숨김, 데스크톱: 표시
<div className="hidden lg:block">
데스크톱에서만 보임
</div>
실전 예제: 반응형 네비게이션
export default function Navbar() {
const [isOpen, setIsOpen] = useState(false);
return (
<nav className="bg-white shadow-lg">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
{/* 로고 */}
<div className="flex items-center">
<h1 className="text-xl font-bold">로고</h1>
</div>
{/* 데스크톱 메뉴 */}
<div className="hidden md:flex items-center space-x-8">
<a href="#" className="text-gray-700 hover:text-blue-500">홈</a>
<a href="#" className="text-gray-700 hover:text-blue-500">서비스</a>
<a href="#" className="text-gray-700 hover:text-blue-500">문의</a>
</div>
{/* 모바일 메뉴 버튼 */}
<div className="md:hidden flex items-center">
<button onClick={() => setIsOpen(!isOpen)}>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</div>
{/* 모바일 메뉴 */}
{isOpen && (
<div className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1">
<a href="#" className="block px-3 py-2 text-gray-700 hover:bg-gray-100 rounded">홈</a>
<a href="#" className="block px-3 py-2 text-gray-700 hover:bg-gray-100 rounded">서비스</a>
<a href="#" className="block px-3 py-2 text-gray-700 hover:bg-gray-100 rounded">문의</a>
</div>
</div>
)}
</nav>
);
}
4. 다크 모드
설정
// tailwind.config.js
module.exports = {
darkMode: 'class', // 또는 'media'
// ...
}
사용법
// 클래스 기반 다크 모드
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
<h1 className="text-2xl">제목</h1>
<p className="text-gray-600 dark:text-gray-300">본문</p>
</div>
다크 모드 토글 구현
'use client';
import { useEffect, useState } from 'react';
export default function DarkModeToggle() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
// 로컬 스토리지에서 테마 불러오기
const theme = localStorage.getItem('theme');
if (theme === 'dark') {
setIsDark(true);
document.documentElement.classList.add('dark');
}
}, []);
const toggleDarkMode = () => {
if (isDark) {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
setIsDark(false);
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
setIsDark(true);
}
};
return (
<button
onClick={toggleDarkMode}
className="p-2 rounded-lg bg-gray-200 dark:bg-gray-700"
>
{isDark ? '🌞' : '🌙'}
</button>
);
}
5. 커스터마이징
색상 추가
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
'brand': {
50: '#f0f9ff',
100: '#e0f2fe',
500: '#0ea5e9',
900: '#0c4a6e',
},
'primary': '#3b82f6',
'secondary': '#8b5cf6',
},
},
},
}
<div className="bg-brand-500 text-white">브랜드 색상</div>
<button className="bg-primary hover:bg-blue-600">버튼</button>
폰트 추가
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontFamily: {
'sans': ['Pretendard', 'system-ui', 'sans-serif'],
'display': ['Montserrat', 'sans-serif'],
},
},
},
}
/* globals.css */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@700&display=swap');
<h1 className="font-display text-4xl">Display Font</h1>
<p className="font-sans">본문 폰트</p>
간격 추가
// tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: {
'128': '32rem',
'144': '36rem',
},
},
},
}
<div className="mt-128">큰 여백</div>
애니메이션 추가
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.5s ease-in',
'slide-up': 'slideUp 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(20px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
}
<div className="animate-fade-in">페이드 인</div>
<div className="animate-slide-up">슬라이드 업</div>
6. 컴포넌트 패턴
@apply 지시어
/* components.css */
@layer components {
.btn-primary {
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
}
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
.input-field {
@apply border border-gray-300 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500;
}
}
<button className="btn-primary">버튼</button>
<div className="card">카드</div>
<input className="input-field" />
재사용 가능한 컴포넌트
// components/Button.jsx
export default function Button({
children,
variant = 'primary',
size = 'md',
...props
}) {
const baseClasses = 'font-bold rounded transition-colors';
const variants = {
primary: 'bg-blue-500 hover:bg-blue-700 text-white',
secondary: 'bg-gray-500 hover:bg-gray-700 text-white',
outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50',
};
const sizes = {
sm: 'py-1 px-2 text-sm',
md: 'py-2 px-4',
lg: 'py-3 px-6 text-lg',
};
return (
<button
className={`${baseClasses} ${variants[variant]} ${sizes[size]}`}
{...props}
>
{children}
</button>
);
}
<Button variant="primary" size="md">기본 버튼</Button>
<Button variant="outline" size="lg">아웃라인 버튼</Button>
7. 실전 예제: 랜딩 페이지
export default function LandingPage() {
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
{/* Hero Section */}
<section className="container mx-auto px-4 py-20">
<div className="max-w-4xl mx-auto text-center">
<h1 className="text-5xl md:text-6xl font-bold text-gray-900 mb-6">
당신의 비즈니스를
<span className="text-blue-600"> 성장</span>시키세요
</h1>
<p className="text-xl text-gray-600 mb-8">
최고의 솔루션으로 생산성을 2배 향상시킬 수 있습니다
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<button className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-8 rounded-lg transition-colors">
무료 시작하기
</button>
<button className="border-2 border-blue-600 text-blue-600 hover:bg-blue-50 font-bold py-3 px-8 rounded-lg transition-colors">
데모 보기
</button>
</div>
</div>
</section>
{/* Features Section */}
<section className="container mx-auto px-4 py-20">
<h2 className="text-3xl font-bold text-center mb-12">주요 기능</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{[
{ icon: '⚡', title: '빠른 속도', desc: '초고속 처리 성능' },
{ icon: '🔒', title: '보안', desc: '엔터프라이즈급 보안' },
{ icon: '📊', title: '분석', desc: '실시간 데이터 분석' },
].map((feature, i) => (
<div key={i} className="bg-white rounded-lg shadow-lg p-8 hover:shadow-xl transition-shadow">
<div className="text-4xl mb-4">{feature.icon}</div>
<h3 className="text-xl font-bold mb-2">{feature.title}</h3>
<p className="text-gray-600">{feature.desc}</p>
</div>
))}
</div>
</section>
{/* CTA Section */}
<section className="bg-blue-600 py-20">
<div className="container mx-auto px-4 text-center">
<h2 className="text-3xl font-bold text-white mb-6">
지금 바로 시작하세요
</h2>
<p className="text-xl text-blue-100 mb-8">
14일 무료 체험, 신용카드 불필요
</p>
<button className="bg-white text-blue-600 hover:bg-gray-100 font-bold py-3 px-8 rounded-lg transition-colors">
무료로 시작하기
</button>
</div>
</section>
</div>
);
}
8. 성능 최적화
PurgeCSS (자동)
Tailwind는 프로덕션 빌드 시 사용하지 않는 CSS를 자동으로 제거합니다.
// tailwind.config.js
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
// 사용된 클래스만 최종 CSS에 포함
}
JIT (Just-In-Time) 모드
Tailwind 3.0부터 JIT가 기본값입니다.
// tailwind.config.js
module.exports = {
mode: 'jit', // 기본값
// ...
}
장점:
- 개발 시 빌드 속도 향상
- 임의 값 사용 가능:
w-[137px],top-[-113px] - 모든 변형 조합 가능
임의 값 사용
// JIT 모드에서 가능
<div className="w-[137px] h-[42px]">커스텀 크기</div>
<div className="top-[-113px]">커스텀 위치</div>
<div className="bg-[#1da1f2]">커스텀 색상</div>
9. 플러그인
공식 플러그인
npm install -D @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio
// tailwind.config.js
module.exports = {
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
}
@tailwindcss/forms
// 기본 폼 스타일 자동 적용
<input type="text" className="rounded-md" />
<select className="rounded-md">
<option>옵션 1</option>
</select>
<textarea className="rounded-md"></textarea>
@tailwindcss/typography
// 마크다운 콘텐츠 스타일링
<article className="prose lg:prose-xl">
<h1>제목</h1>
<p>본문 텍스트...</p>
<ul>
<li>항목 1</li>
<li>항목 2</li>
</ul>
</article>
10. 자주 하는 실수와 해결법
문제 1: 클래스가 적용 안 됨
// ❌ 잘못된 코드 - 동적 클래스명
const color = 'blue';
<div className={`bg-${color}-500`}>텍스트</div>
// ✅ 올바른 코드 - 전체 클래스명
const colorClasses = {
blue: 'bg-blue-500',
red: 'bg-red-500',
};
<div className={colorClasses[color]}>텍스트</div>
문제 2: 스타일 우선순위
// ❌ 잘못된 코드 - 나중 클래스가 우선하지 않음
<div className="text-red-500 text-blue-500">
파란색이 아니라 빨간색!
</div>
// ✅ 올바른 코드 - 조건부 렌더링
<div className={isActive ? 'text-blue-500' : 'text-red-500'}>
텍스트
</div>
문제 3: 반응형 클래스 순서
// ❌ 잘못된 코드
<div className="lg:text-xl md:text-lg text-base">
순서 상관없음 (모바일 퍼스트)
</div>
// ✅ 올바른 코드 (가독성)
<div className="text-base md:text-lg lg:text-xl">
작은 화면부터 큰 화면 순서
</div>
정리 및 체크리스트
핵심 요약
- 유틸리티 퍼스트: 클래스 이름 고민 없이 즉시 스타일링
- 반응형 디자인: 모바일 퍼스트, 브레이크포인트 접두사
- 다크 모드:
dark:접두사로 간단 구현 - 커스터마이징:
tailwind.config.js로 디자인 시스템 구축 - 성능 최적화: 사용하지 않는 CSS 자동 제거
실무 체크리스트
- Tailwind CSS 설치 및 설정
-
content경로 올바르게 설정 - 디자인 시스템 커스터마이징 (색상, 폰트 등)
- 다크 모드 구현
- 반응형 디자인 테스트
- 재사용 컴포넌트 작성
- 프로덕션 빌드 최적화 확인
같이 보면 좋은 글
- Next.js 15 완벽 가이드 | App Router·Server Actions·Turbopack
- React 18 완벽 가이드 | Concurrent Rendering·Suspense
- ChatGPT API 완벽 가이드 | 사용법·요금·프롬프트
이 글에서 다루는 키워드
Tailwind CSS, CSS, 유틸리티 클래스, 반응형, 다크모드, Frontend, Web, UI
자주 묻는 질문 (FAQ)
Q. Tailwind CSS vs 일반 CSS, 어떤 게 나은가요?
A. 빠른 개발과 일관성이 중요하면 Tailwind를 권장합니다. 복잡한 애니메이션이나 특수한 스타일은 일반 CSS가 나을 수 있습니다.
Q. 클래스명이 너무 길어지는데요?
A. @apply 지시어로 재사용 가능한 클래스를 만들거나, 컴포넌트로 추상화하세요.
Q. 번들 크기가 커지지 않나요?
A. 프로덕션 빌드 시 사용하지 않는 CSS가 자동으로 제거되어 최종 파일은 매우 작습니다 (보통 10KB 이하).
Q. 기존 CSS와 함께 사용할 수 있나요?
A. 네, Tailwind는 기존 CSS와 함께 사용할 수 있습니다. 점진적으로 마이그레이션할 수 있습니다.