Next.js는 애플리케이션의 스타일링을 위해 다양한 방법을 지원한다.
Global CSS
: 전통적인 CSS에 익숙한 사용자에게는 간단하고 익숙한 방법이지만, 애플리케이션이 커짐에 따라 CSS 번들이 커지고 스타일 관리가 어려워질 수 있다.
CSS Module
: 이름 충돌을 피하고 유지 관리성을 향상시키기 위해 로컬 범위로 제한된 CSS 클래스를 생성한다.
Tailwind CSS
: 유틸리티 기반의 CSS 프레임워크로, 유틸리티 클래스를 조합하여 빠르게 사용자 정의 디자인을 만들 수 있다.
Sass
: 변수, 중첩된 규칙, 믹스인과 같은 기능을 추가하여 CSS를 확장하는 인기 있는 CSS 전처리기다.
CSS-in-JS
: JavaScript 컴포넌트에 CSS를 직접 포함시켜 동적이고 로컬 범위의 스타일링을 가능하게 한다.
Next.js는 .module.css
확장자를 사용하여 CSS Modules를 내장 지원한다.
CSS Modules는 자동으로 고유한 클래스 이름을 생성하여 CSS를 로컬 범위로 제한한다. 이를 통해 충돌 걱정 없이 다른 파일에서 동일한 클래스 이름을 사용할 수 있다. 이러한 동작으로 CSS Modules는 컴포넌트 수준의 CSS를 포함하기에 이상적인 방법이다.
// app/dashboard/layout.tsx
import styles from './styles.module.css';
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return <section className={styles.dashboard}>{children}</section>;
}
// app/dashboard/styles.module.css
.dashboard {
padding: 24px;
}
CSS Modules은 선택적인 기능이며, .module.css
확장자를 가진 파일에만 활성화된다. 일반 <link>
스타일시트와 글로벌 CSS 파일은 여전히 지원된다.
프로덕션 환경에서는 모든 CSS Module 파일이 자동으로 많은 축소 및 코드 분할된 .css
파일로 연결된다. 이러한 .css
파일은 애플리케이션에서 핫 실행 경로를 나타내며, 애플리케이션을 그리기(paint) 위해 최소한의 CSS만 로드되도록 보장한다.
global.css
를 통해 전역 스타일을 적용시킬 수 있다.
// app/global.css
body {
padding: 20px 20px 60px;
max-width: 680px;
margin: 0 auto;
}
루트 레이아웃 (app/layout.js
) 내에서 global.css
스타일시트를 가져와 애플리케이션의 모든 라우트에 스타일을 적용한다.
// app/layout.tsx
// These styles apply to every route in the application
import './global.css';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
외부 패키지에서 발행한 스타일시트는 app
디렉토리 내의 어디에서든, 컴포넌트와 함께 사용할 수 있다.
외부 스타일시트는 npm 패키지로부터 직접 가져와야 하거나 코드베이스와 함께 다운로드하여 동일한 위치에 놓아야 한다. <link rel="stylesheet" />
는 사용할 수 없다.
// app/layout.tsx
import 'bootstrap/dist/css/bootstrap.css';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className="container">{children}</body>
</html>
);
}
Next.js에서는 스타일 추가 작성 환경을 개선하는 추가 기능이 포함되어 있다.
next dev
로 로컬에서 실행할 때, 로컬 스타일시트(전역 스타일시트 또는 CSS 모듈)는 편집을 저장하는 동안 변경 사항이 즉시 반영되도록 Fast Refresh를 활용한다.
next build
로 프로덕션 빌드를 생성할 때, CSS 파일은 더 적은 수의 축소된 .css
파일로 번들링되어 스타일을 검색하는 데 필요한 네트워크 요청 수를 줄인다.
JavaScript를 비활성화하면 스타일은 프로덕션 빌드 (next start
)에서도 로드된다. 그러나 Fast Refresh를 사용하려면 여전히 next dev
에서 JavaScript가 필요하다.
Tailwind CSS는 Next.js와 아주 적합한 유틸리티 우선 CSS 프레임워크이다.
Tailwind CSS 패키지를 설치하고 init
명령을 실행하여 tailwind.config.js
및 postcss.config.js
파일을 생성한다.
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
tailwind.config.js
파일 내에서 Tailwind CSS 클래스 이름을 사용할 파일에 경로를 추가한다.
postcss.config.js
를 수정할 필요는 없다.
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // Note the addition of the `app` directory.
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
// Or if using `src` directory:
'./src/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
};
Tailwind가 생성한 스타일을 애플리케이션의 전역 스타일시트에 주입하기 위해 Tailwind가 사용할 Tailwind CSS 지시문을 추가한다.
// app/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
루트 레이아웃 (app/layout.tsx
) 내에서 globals.css
스타일시트를 가져와 애플리케이션의 모든 라우트에 스타일을 적용한다.
// app/layout.tsx
import type { Metadata } from 'next';
// These styles apply to every route in the application
import './globals.css';
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
export default function Page() {
return <h1 className="text-3xl font-bold underline">Hello, Next.js!</h1>;
}
Next.js 13.1부터는 Turbopack을 사용하여 Tailwind CSS와 PostCSS를 지원한다.
런타임 JavaScript가 필요한 CSS-in-JS 라이브러리는 현재 서버 컴포넌트에서 지원되지 않는다. CSS-in-JS를 사용하는 경우, 서버 컴포넌트와 스트리밍과 같은 최신 React 기능을 사용하려면 라이브러리 작성자가 최신 버전의 React를 지원해야 한다.
다음 라이브러리는 앱 디렉토리의 클라이언트 컴포넌트에서 지원된다.
styled-jsx
styled-components
tamagui
style9
vanilla-extract
다음은 현재 지원 작업이 진행 중인 라이브러리다.
emotion
Material UI
Chakra UI
서버 컴포넌트를 스타일링하려면 CSS Module
이나 PostCSS
또는 Tailwind CSS
와 같이 CSS 파일을 출력하는 기타 솔루션을 사용하는 것을 권장한다.
CSS-in-JS를 구성하는 것은 세 단계의 선택적인 프로세스로 진행된다.
렌더링 중에 모든 CSS 규칙을 수집하는 스타일 레지스트리
규칙이 사용될 가능성이 있는 콘텐츠 이전에 규칙을 삽입하는 새로운 useServerInsertedHTML
훅
초기 서버 사이드 렌더링 중에 앱을 스타일 레지스트리로 래핑하는 클라이언트 컴포넌트
styled-jsx
Client Components에서 styled-jsx
를 사용하려면 v5.1.0을 사용해야 한다. 먼저 새로운 레지스트리를 만든다.
// app/registry.tsx
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { StyleRegistry, createStyleRegistry } from 'styled-jsx';
export default function StyledJsxRegistry({
children,
}: {
children: React.ReactNode;
}) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [jsxStyleRegistry] = useState(() => createStyleRegistry());
useServerInsertedHTML(() => {
const styles = jsxStyleRegistry.styles();
jsxStyleRegistry.flush();
return <>{styles}</>;
});
return <StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>;
}
그런 다음 루트 레이아웃을 레지스트리로 래핑한다.
// app/layout.tsx
import StyledJsxRegistry from './registry';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
<StyledJsxRegistry>{children}</StyledJsxRegistry>
</body>
</html>
);
}
다음은 styled-components@v6.0.0-rc.1
이상을 구성하는 방법의 예시다.
먼저, styled-components
API를 사용하여 렌더링 중에 생성된 모든 CSS 스타일 규칙을 수집하는 전역 레지스트리 컴포넌트와 해당 규칙을 반환하는 함수를 만든다. 그런 다음 useServerInsertedHTML
훅을 사용하여 레지스트리에 수집된 스타일을 루트 레이아웃의 <head>
HTML 태그에 삽입한다.
// lib/registry.tsx
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
export default function StyledComponentsRegistry({
children,
}: {
children: React.ReactNode;
}) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return <>{styles}</>;
});
if (typeof window !== 'undefined') return <>{children}</>;
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
{children}
</StyleSheetManager>
);
}
루트 레이아웃의 children
prop을 스타일 레지스트리 컴포넌트로 래핑한다.
// app/layout.tsx
import StyledComponentsRegistry from './lib/registry';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
);
}
💡 알아두면 좋은 점
서버 렌더링 중에 스타일은 전역 레지스트리로 추출되고 HTML의 <head>
에 플러시된다. 이를 통해 스타일 규칙이 사용하는 콘텐츠 이전에 배치되도록 보장된다. 앞으로는 스타일을 삽입할 위치를 결정하기 위해 예정된 React 기능을 사용할 수도 있다.
스트리밍 중에는 각 청크의 스타일이 수집되어 기존 스타일에 추가된다. 클라이언트 측 수화(hydration
)가 완료된 후, styled-components
가 기존대로 작동하고 추가적인 동적 스타일을 삽입한다.
스타일 레지스트리에 대해 트리의 최상위에 클라이언트 컴포넌트를 사용하는 이유는 CSS 규칙을 이렇게 추출하는 것이 더 효율적이기 때문이다. 이렇게 하면 후속 서버 렌더링에서 스타일을 다시 생성하는 것을 피하고, 서버 컴포넌트 페이로드로 전송되는 것을 방지할 수 있다.
[ 출처 ]
https://nextjs.org/docs/app/building-your-application/styling