스타일링 전략: Tailwind CSS 및 CSS Modules 통합
Next.js 14의 스타일링 전략
Next.js 14(App Router) 환경에서는 서버 컴포넌트(RSC)와의 호환성을 고려하여 스타일링 방식을 선택해야 합니다.
가장 권장되고 많이 쓰이는 두 가지 방법인 Tailwind CSS와 CSS Modules에 대해 알아봅시다.
가장 권장되고 많이 쓰이는 두 가지 방법인 Tailwind CSS와 CSS Modules에 대해 알아봅시다.
1. Tailwind CSS (권장)
- 서버 컴포넌트와 클라이언트 컴포넌트 모두에서 제약 없이 완벽하게 동작합니다.
- 별도의 CSS 파일을 넘나들 필요 없이 컴포넌트 내부에서 마크업과 스타일을 동시에 작성할 수 있어 생산성이 높습니다.
- Next.js 공식 문서에서도 App Router 환경의 기본 스타일링 도구로 강력히 추천하고 있습니다.
2. CSS Modules
.module.css확장자를 사용하여 고유한 클래스명을 자동으로 생성해 줍니다. 클래스명 충돌 걱정이 없습니다.- 전통적인 CSS 작성 방식을 선호하거나, 기존 CSS 코드를 Next.js로 마이그레이션할 때 유리합니다.
- 서버 컴포넌트에서도 안전하게 렌더링되며, 빌드 시 자동으로 최소화(minified)됩니다.
⚠️ CSS-in-JS (Styled-components, Emotion) 사용 시 주의점
런타임 기반의 CSS-in-JS 라이브러리들은 현재 서버 컴포넌트(RSC)를 지원하지 않습니다.
만약 반드시 사용해야 한다면, 컴포넌트 최상단에 "use client" 지시어를 선언하여 클라이언트 컴포넌트로 강제 전환해야 합니다. 이로 인해 서버 컴포넌트의 이점(초기 로딩 속도, 번들 사이즈 감소 등)을 잃을 수 있으므로 신중하게 결정해야 합니다.
App.jsx
import React from 'react';
import Button from './Button';
export default function App() {
return (
<div className="p-8 text-center bg-slate-50 rounded-xl border border-slate-200">
<h2 className="text-2xl font-bold text-slate-800 mb-6">스타일링 전략 믹스매치</h2>
<div className="flex justify-center gap-4">
{/* Tailwind + CSS Modules가 결합된 버튼 컴포넌트 */}
<Button variant="primary">Primary 버튼</Button>
<Button variant="secondary">Secondary 버튼</Button>
</div>
<p className="mt-8 text-slate-500 text-sm">
☝️ 위 버튼들은 Tailwind의 유틸리티 클래스와 <br />
CSS Modules의 스코프 스타일이 결합되어 렌더링되었습니다.
</p>
</div>
);
}Button.jsx
import React, { useEffect } from 'react';
// 실제 환경에서는 import styles from './Button.module.css' 로 불러옵니다.
// 브라우저 렌더링용 임시 목업(Mock) 객체
const styles = {
primaryBtn: 'custom-primary-btn',
secondaryBtn: 'custom-secondary-btn'
};
export default function Button({ children, variant = 'primary' }) {
// 미리보기 화면에 CSS를 동적으로 주입하는 코드 (학습용)
useEffect(() => {
if (!document.getElementById('custom-btn-styles')) {
const style = document.createElement('style');
style.id = 'custom-btn-styles';
style.innerHTML = `
.custom-primary-btn {
background-color: #3b82f6;
color: white;
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3);
}
.custom-primary-btn:hover { background-color: #2563eb; transform: translateY(-2px); }
.custom-secondary-btn {
background-color: #f1f5f9;
color: #334155;
border: 1px solid #cbd5e1;
}
.custom-secondary-btn:hover { background-color: #e2e8f0; }
`;
document.head.appendChild(style);
}
}, []);
// 1. Tailwind 클래스 (공통 스타일: 패딩, 둥근 모서리, 글꼴 등)
const baseClasses = "px-6 py-2 rounded-lg font-semibold transition-all duration-200";
// 2. CSS Module 클래스 (테마 색상, 복잡한 커스텀 스타일)
const variantClass = variant === 'primary' ? styles.primaryBtn : styles.secondaryBtn;
return (
<button className={`${baseClasses} ${variantClass}`}>
{children}
</button>
);
}Button.module.css
/*
* 💡 참고: 실제 Next.js 프로젝트에서 사용하는 CSS 파일의 모습입니다.
* 고유한 해시값이 클래스명에 붙어 스타일 충돌을 방지합니다.
*/
.primaryBtn {
background-color: #3b82f6;
color: white;
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.3);
}
.primaryBtn:hover {
background-color: #2563eb;
transform: translateY(-2px);
}
.secondaryBtn {
background-color: #f1f5f9;
color: #334155;
border: 1px solid #cbd5e1;
}
.secondaryBtn:hover {
background-color: #e2e8f0;
}