Next.js 프로젝트에서 Tailwind CSS를 사용할 때 효율적으로 스타일을 관리하는 방법을 알아보겠습니다.
자주 사용되는 Tailwind 클래스 조합을 상수로 관리하면 재사용성과 일관성을 높일 수 있습니다.
// styles/common.ts
export const tw = {
// 레이아웃
container: 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8',
section: 'py-12 sm:py-16',
// 플렉스 박스
flexCenter: 'flex items-center justify-center',
flexBetween: 'flex items-center justify-between',
// 카드
card: 'bg-white rounded-lg shadow-md p-6',
// 버튼
buttonPrimary: 'bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded',
buttonSecondary: 'bg-gray-100 hover:bg-gray-200 text-gray-800 font-semibold py-2 px-4 rounded'
}
사용 예시:
import { tw } from '@/styles/common'
export default function Header() {
return (
<header className={`${tw.flexBetween} ${tw.container} h-16`}>
<Logo />
<Nav />
</header>
)
}
clsx나 cn 유틸리티 함수를 사용하여 조건부 스타일링을 깔끔하게 처리할 수 있습니다.
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// components/Button.tsx
import { cn } from '@/lib/utils'
import { tw } from '@/styles/common'
interface ButtonProps {
variant?: 'primary' | 'secondary'
className?: string
children: React.ReactNode
}
export default function Button({
variant = 'primary',
className,
children
}: ButtonProps) {
return (
<button
className={cn(
variant === 'primary' ? tw.buttonPrimary : tw.buttonSecondary,
className
)}
>
{children}
</button>
)
}
복잡한 스타일 조합은 @apply 디렉티브를 사용하여 추상화할 수 있습니다.
/* app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary {
@apply bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded;
}
.card-hover {
@apply hover:shadow-lg transition-shadow duration-200;
}
.input-field {
@apply w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500;
}
}
각 컴포넌트 내부에서 스타일을 객체로 관리하여 가독성을 높일 수 있습니다.
// components/ProductCard.tsx
export default function ProductCard() {
const styles = {
wrapper: 'relative overflow-hidden rounded-lg',
image: 'w-full h-48 object-cover',
content: 'p-4',
title: 'text-lg font-semibold mb-2',
price: 'text-green-600 font-bold',
badge: 'absolute top-2 right-2 bg-red-500 text-white px-2 py-1 rounded-full text-sm'
}
return (
<div className={styles.wrapper}>
<img src="product.jpg" className={styles.image} alt="Product" />
<div className={styles.content}>
<h3 className={styles.title}>Product Name</h3>
<p className={styles.price}>$99.99</p>
</div>
<span className={styles.badge}>Sale</span>
</div>
)
}
반응형 디자인을 위한 브레이크포인트를 상수로 관리할 수 있습니다.
// styles/screens.ts
export const screens = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
} as const
export const mediaQueries = {
sm: `@media (min-width: ${screens.sm})`,
md: `@media (min-width: ${screens.md})`,
lg: `@media (min-width: ${screens.lg})`,
xl: `@media (min-width: ${screens.xl})`,
'2xl': `@media (min-width: ${screens['2xl']})`,
}
일관성 유지
모듈화
유지보수성
성능 최적화
Tailwind CSS를 사용하면서 스타일을 체계적으로 관리하기 위해서는 적절한 추상화와 구조화가 필요합니다. 위에서 소개한 방법들을 프로젝트의 특성에 맞게 조합하여 사용하면 효율적인 스타일 관리가 가능합니다.