Next.js 15 App Router (React Server Component 기반)에서는 CSS-in-JS
방식이 기존 CSR (클라이언트 사이드 렌더링)과 다르게 제한적으로 동작하거나 안정성이 떨어진다.
따라서 일반적인 CSS-in-JS보다는 CSS-in-JS대체인 vanilla-extract
나 TailwindCSS
를 사용하게 된다. 이 글에서는 TailwindCSS에 대해 설명한다.
App Router에서 CSS-in-JS 방식이 불안정한 이유
핵심 원인: React Server Components (RSC)의 동작 방식
- Next.js 15의 App Router는 RSC를 사용.
- RSC는 서버에서 React 트리 렌더 → RSC Payload → 클라이언트 Hydration.
- 서버에서 실행되는 코드에는 브라우저 API(
document
,window
, DOM 접근 등)가 없음. - **CSS-in-JS (예: styled-components, emotion)**는 일반적으로 런타임에 브라우저 환경에서 동적으로 CSS 생성 및 주입.
문제 발생
방식 | 문제 원인 |
---|---|
styled-components | 서버에서 스타일 생성 불가능 (스타일 태그 주입할 DOM 없음) |
emotion | 동일. RSC 환경에서 작동 불안정 (CSS-in-JS 라이브러리 대부분이 클라이언트 기반으로 만들어짐) |
vanilla-extract | 서버 컴파일 시 별도 CSS 파일을 만들어 빌드 시점에 생성 가능 → 상대적으로 안정적 (Runtime X) |
요약:
App Router는 서버 전용 코드에서 CSS 동적 생성 자체가 무의미하거나 Hydration 중 충돌 가능.
특히 RSC는 클라이언트와 서버가 동일한 실행 컨텍스트를 공유하지 않으므로 CSS-in-JS 런타임 접근 불가.
Tailwind CSS가 App Router에서 거의 표준처럼 쓰이는 이유
Atomic CSS → Build-time 생성 → Pure HTML처럼 처리 가능
- Tailwind는 미리 정의된 클래스 → HTML에 클래스 문자열만 붙임 → 서버에서도 안전하게 렌더 가능.
- 브라우저 동작 필요 X → 서버에서도 바로 HTML과 동일하게 처리.
- 스타일 충돌 없음 → 스타일 시트가 빌드 타임에 미리 완성 → 서버가 별도 관리 X.
App Router 친화성
Tailwind 특징 | App Router 최적화 이유 |
---|---|
클래스 기반 Static CSS | RSC에서 문제 없음 (CSS는 HTML에 포함되므로 서버/클라이언트 모두 안정적) |
미리 빌드된 글로벌 CSS | 서버에서 즉시 사용 가능, 별도 런타임 필요 없음 |
Dynamic class name 지원 | Next.js 15에서는 서버와 클라이언트 모두에서 유연하게 사용 가능 |
요약:
Tailwind는 브라우저 DOM 접근 없이 서버에서 HTML과 CSS를 즉시 렌더링 가능.
RSC 환경에서도 충돌 없이 SSR + CSR 양쪽 모두에서 호환.
Tailwind 기초 문법
Atomic CSS 방식
- Utility-First 지향
- CSS 속성이 아닌 클래스 이름 자체가 스타일 속성
- 예:
<div class="text-lg font-bold text-red-500 bg-gray-100 p-4 rounded"></div>
Tailwind 클래스 | 의미 |
---|---|
text-lg | 폰트 크기 Large |
font-bold | 폰트 두껍게 |
text-red-500 | 텍스트 색상 빨간색 (500 단계) |
bg-gray-100 | 배경색 회색 (100 단계) |
p-4 | 패딩 1rem |
rounded | 모서리 둥글게 |
2. Theme (custom theme)
📍 tailwind.config.js 확장
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
colors: {
primary: '#1e40af',
secondary: '#64748b',
},
spacing: {
'128': '32rem',
},
},
},
};
extend
사용 → 기본 테마 유지 + 추가 가능colors.primary
,spacing.128
식으로 사용 가능
<div class="bg-primary p-128 text-white"></div>
3. 다크모드 설정 방법
Tailwind의 2가지 다크모드 지원
방식 | 설명 | 장점/단점 |
---|---|---|
class | <html class="dark"> 존재 여부로 다크/라이트 | 사용자가 토글 가능, JS로 상태 관리 쉬움 |
media | (prefers-color-scheme: dark) 사용 | 자동 감지 가능, JS 필요 없음, 제어 어려움 |
tailwind.config.js
module.exports = {
darkMode: 'class', // 또는 'media'
};
사용 예시
<!-- class 방식 -->
<html class="dark">
<div class="bg-white dark:bg-black text-black dark:text-white">Hello</div>
</html>
<!-- media 방식 (자동) -->
<div class="bg-white dark:bg-black text-black dark:text-white">Hello</div>
4. 기타 심화 기능
4.1. 상태 기반 클래스
<button class="hover:bg-blue-500 focus:ring-2 active:scale-95">Click me</button>
4.2. Group hover
<div class="group">
<p class="group-hover:text-red-500">Hover parent changes me</p>
</div>
4.3. Responsive
<div class="text-base md:text-lg lg:text-xl">Responsive Text</div>
4.4. Theme-aware (with dark)
<div class="bg-white text-black dark:bg-black dark:text-white">Theme-aware</div>
4.5. Arbitrary value (임의 값)
<div class="bg-[#123456] p-[10px]">Arbitrary</div>
4.6. CSS 변수와 함께 사용
:root {
--custom-color: #123456;
}
<div class="text-[var(--custom-color)]">With CSS Variable</div>