Next.js는 애플리케이션을 스타일링하기 위해 다음과 같은 여러 가지 유용한 방법들을 제공하고 있어요.
Tailwind CSS는 커스텀 디자인을 구축하기 위해 저수준(low-level)의 유틸리티 클래스들을 제공하는 '유틸리티 퍼스트(utility-first)' CSS 프레임워크입니다.
💡 [강사의 보충 설명 & 실무 팁]
'유틸리티 퍼스트'라는 말이 조금 어렵게 느껴질 수 있는데요, 쉽게 말해margin,padding,color같은 CSS 속성 하나하나를 미리 클래스 이름(예:m-4,text-blue-500)으로 만들어두고, HTML 태그에 레고 블록 조립하듯 끼워 넣어서 스타일링하는 방식입니다.
Next.js 환경에서는 서버 컴포넌트(Server Components)와 충돌 없이 완벽하게 호환되고, 설정도 매우 간편해서 현재 실무에서 가장 압도적으로 많이 쓰이는 방식이랍니다. VS Code를 사용하신다면Tailwind CSS IntelliSense확장 프로그램을 꼭 설치하세요! 클래스명 자동완성 기능 덕분에 개발 속도가 눈에 띄게 빨라질 거예요.
먼저 Tailwind CSS를 설치해 볼까요? 사용하는 패키지 매니저에 맞게 아래 명령어를 입력해 주세요.
pnpm add -D tailwindcss @tailwindcss/postcss
npm install -D tailwindcss @tailwindcss/postcss
yarn add -D tailwindcss @tailwindcss/postcss
bun add -D tailwindcss @tailwindcss/postcss
설치가 끝났다면, postcss.config.mjs 파일에 PostCSS 플러그인을 추가해 줍니다.
// filename="postcss.config.mjs"
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
그 다음, 전역(global) CSS 파일에 Tailwind를 임포트(import) 해주세요.
/* filename="app/globals.css" */
@import 'tailwindcss';
이제 최상위 레이아웃(root layout) 파일에서 이 CSS 파일을 불러옵니다.
// filename="app/layout.tsx" switcher
import './globals.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
// filename="app/layout.js" switcher
import './globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
짜잔! 이제 애플리케이션 내 어디에서든 Tailwind의 유틸리티 클래스를 바로 사용할 수 있습니다.
// filename="app/page.tsx" switcher
export default function Page() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1 className="text-4xl font-bold">Welcome to Next.js!</h1>
</main>
)
}
// filename="app/page.js" switcher
export default function Page() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<h1 className="text-4xl font-bold">Welcome to Next.js!</h1>
</main>
)
}
알아두면 좋은 정보 (Good to know): 만약 구형 브라우저에 대한 폭넓은 지원이 필요한 상황이라면, Tailwind CSS v3 설정 안내(https://nextjs.org/docs/app/guides/tailwind-v3-css)를 참고해 주세요.
CSS Modules는 컴파일 시에 고유한 클래스 이름을 생성하여 CSS를 로컬 스코프(locally scope)로 제한해 줍니다. 이 덕분에 서로 다른 파일에서 같은 클래스 이름을 사용하더라도 이름이 충돌할까 봐 걱정하지 않아도 됩니다.
💡 [강사의 보충 설명 & 실무 팁]
일반적인 CSS를 사용할 때는 프로젝트가 커지면button,container같은 흔한 클래스 이름이 겹쳐서 스타일이 엉망이 되는 일이 흔합니다. 하지만 CSS Modules를 사용하면 Next.js가 빌드할 때.blog라는 클래스를blog_1a2b3c처럼 임의의 해시값을 붙여서 세상에 하나뿐인 이름으로 바꿔줘요.
BEM(Block Element Modifier) 같은 복잡한 네이밍 규칙을 억지로 외우거나 고민할 필요 없이, 컴포넌트 단위로 아주 깔끔하게 스타일을 관리할 수 있는 효자 기능입니다!
CSS Modules를 사용하려면 확장자가 .module.css인 새 파일을 만들고, app 디렉토리 내의 원하는 컴포넌트에서 임포트하면 됩니다.
// filename="app/blog/blog.module.css"
.blog {
padding: 24px;
}
// filename="app/blog/page.tsx" switcher
import styles from './blog.module.css'
export default function Page() {
return <main className={styles.blog}></main>
}
// filename="app/blog/page.js" switcher
import styles from './blog.module.css'
export default function Layout() {
return <main className={styles.blog}></main>
}
애플리케이션 전체에 걸쳐 동일하게 적용되어야 하는 스타일이 있다면 전역 CSS를 사용할 수 있습니다.
app/global.css 파일을 만들고, 이를 최상위 레이아웃(root layout)에서 임포트하면 애플리케이션의 모든 라우트(경로)에 해당 스타일이 적용됩니다.
// filename="app/global.css"
body {
padding: 20px 20px 60px;
max-width: 680px;
margin: 0 auto;
}
// filename="app/layout.tsx" switcher
// 이 스타일들은 애플리케이션의 모든 라우트에 적용됩니다.
import './global.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
// filename="app/layout.js" switcher
// 이 스타일들은 애플리케이션의 모든 라우트에 적용됩니다.
import './global.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
알아두면 좋은 정보 (Good to know): 전역 스타일은
app디렉토리 내의 어떤 레이아웃, 페이지, 또는 컴포넌트에서도 임포트할 수 있습니다. 하지만, Next.js는 Suspense와 통합하기 위해 React에 내장된 스타일시트 지원 기능을 사용하기 때문에, 현재 라우트 간을 이동할 때 스타일시트가 제거되지 않아 스타일 충돌이 발생할 수 있습니다.따라서 저희는 다음 방식을 권장합니다: > * (Tailwind의 기본 스타일처럼) 진정으로 전역적인 CSS에만 전역 스타일을 사용하세요.
- 컴포넌트 스타일링에는 Tailwind CSS를 사용하세요.
- 특정 컴포넌트에만 종속되는 커스텀 CSS가 필요할 때는 CSS Modules를 사용하세요.
💡 [강사의 보충 설명]
이 부분, 굉장히 중요합니다! 특정 페이지(예:/about)에서만 쓰려고 전역 CSS 파일을 만들어 그 페이지에 임포트했다고 쳐볼게요. 사용자가/about페이지를 방문하면 그 CSS가 로드되지만, 다시 메인 페이지(/)로 돌아와도 그 CSS가 메모리에 그대로 남아있어서 메인 페이지의 디자인까지 망가뜨릴 수 있다는 뜻이에요. 전역 CSS는 말 그대로 폰트 설정, CSS 초기화(Reset) 등 '진짜 전체 화면에 항상 있어야 하는 것'에만 쓰셔야 합니다!
npm 등 외부 패키지로 배포된 스타일시트도 컴포넌트가 위치한 곳(colocated components)을 포함해 app 디렉토리 내 어디에서든 임포트할 수 있습니다.
// filename="app/layout.tsx" switcher
import 'bootstrap/dist/css/bootstrap.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className="container">{children}</body>
</html>
)
}
// filename="app/layout.js" switcher
import 'bootstrap/dist/css/bootstrap.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className="container">{children}</body>
</html>
)
}
알아두면 좋은 정보 (Good to know): React 19에서는
<link rel="stylesheet" href="..." />방식도 사용할 수 있습니다. 더 자세한 정보는 Reactlink문서(https://react.dev/reference/react-dom/components/link)를 확인해 보세요.
Next.js는 프로덕션 빌드(실제 서비스용으로 코드를 최적화하는 과정) 시에 스타일시트들을 자동으로 청킹(chunking, 병합 및 쪼개기)하여 CSS를 최적화합니다. 이때 CSS가 적용되는 순서는 여러분이 코드 내에서 스타일을 임포트한 순서에 따라 결정됩니다.
예를 들어 볼까요? 아래 코드에서는 <BaseButton> 컴포넌트가 page.module.css 보다 먼저 임포트 되었기 때문에, base-button.module.css의 스타일이 page.module.css의 스타일보다 먼저 순서가 매겨집니다.
// filename="page.tsx" switcher
import { BaseButton } from './base-button'
import styles from './page.module.css'
export default function Page() {
return <BaseButton className={styles.primary} />
}
// filename="page.js" switcher
import { BaseButton } from './base-button'
import styles from './page.module.css'
export default function Page() {
return <BaseButton className={styles.primary} />
}
// filename="base-button.tsx" switcher
import styles from './base-button.module.css'
export function BaseButton() {
return <button className={styles.primary} />
}
// filename="base-button.js" switcher
import styles from './base-button.module.css'
export function BaseButton() {
return <button className={styles.primary} />
}
CSS가 적용되는 순서를 예측 가능하게 유지하려면 다음 사항들을 지켜주세요.
<name>.tsx 파일 대신 <name>.module.css 형태를 사용하는 식입니다.sort-imports 처럼 임포트 구문을 자동으로 정렬해 주는 린터(linter)나 포매터(formatter) 기능은 끄는 것이 좋습니다. (자동 정렬 때문에 의도한 CSS 순서가 뒤바뀔 수 있습니다.)next.config.js 파일의 cssChunking 옵션을 사용하면 CSS가 청킹되는 방식을 직접 제어할 수도 있습니다.💡 [강사의 실무 팁]
이 CSS 순서 문제는 실무에서 생각보다 정말 자주 겪는 버그 중 하나입니다! 개발자 모드(next dev)에서는 스타일이 멀쩡해 보였는데, 실제 배포(next build후 운영 환경)를 하고 났더니 버튼 색상이 이상하게 덮어씌워진 경험, 프론트엔드 개발자라면 다들 한 번씩 겪어봤을 거예요.
그럴 땐 당황하지 마시고 "내가 컴포넌트와 CSS 파일을 임포트한 순서가 꼬이지 않았나?"를 가장 먼저 의심해 보세요!
next dev)에서는 Fast Refresh (https://nextjs.org/docs/architecture/fast-refresh) 기능 덕분에 CSS를 수정하면 화면에 즉각적으로 업데이트가 반영됩니다.next build)에서는 모든 CSS 파일들이 자동으로 다수의 압축된(minified) 코드 분할(code-split) .css 파일들로 병합(concatenated) 됩니다. 이를 통해 특정 라우트에 접근할 때 딱 필요한 최소한의 CSS만 로드되도록 최적화합니다.next build)를 진행하여 최종 CSS 순서가 의도한 대로 맞는지 항상 확인해야 합니다.여러분의 애플리케이션에 CSS를 적용하는 또 다른 대안들에 대해 더 자세히 알아보세요.
모든 문서의 구조적인 개요를 보려면 https://nextjs.org/docs/sitemap.md 를 참고해 주세요.
사용 가능한 전체 문서의 목차(index)를 확인하려면 https://nextjs.org/docs/llms.txt 를 참고해 주세요.
수고하셨습니다! 혹시 이 번역본을 읽어보시고 Tailwind CSS 세팅법이나 CSS Modules 문법 등 추가로 궁금한 점이 생기셨나요? 언제든지 질문해 주시면 친절하게 답변해 드릴게요!