Next.js는 서버 사이드 렌더링(SSR)을 지원하는 React 기반의 프레임워크로, 초기 로딩 속도를 개선하고 SEO를 최적화하는 데 큰 장점을 제공합니다. 하지만 `styled-components`와 같은 CSS-in-JS 라이브러리를 사용할 때는 서버 사이드에서 스타일을 올바르게 적용하는 것이 중요합니다. 이를 위해 Next.js의 커스텀 `Document` 설정이 필요합니다. 이번 글에서는 `Document.tsx`를 커스텀하여 `styled-components`의 스타일을 서버 사이드에서 렌더링하는 방법을 자세히 알아봅니다.
커스텀 Document 설정의 필요성
기본적으로 Next.js는 HTML 문서의 최상위 구조를 제어하는 `Document` 컴포넌트를 제공합니다. 이를 커스터마이즈하여 페이지 렌더링 중에 스타일을 수집하고 주입하는 과정이 필요합니다. 이렇게 하면 클라이언트와 서버 간의 스타일 불일치를 방지하고, 초기 페이지 로드 시 깔끔하게 스타일이 적용된 상태를 유지할 수 있습니다.
Step-by-Step 가이드: Document.tsx 커스터마이징
1. `Document.tsx` 파일 생성
Next.js 프로젝트의 `pages` 디렉토리 내에 `_document.tsx` 파일을 생성합니다. 이 파일은 `Document` 컴포넌트를 커스터마이즈하는 데 사용됩니다.
2. styled-components 설치
먼저 `styled-components`와 관련 패키지를 설치합니다.
npm install styled-components @types/styled-components babel-plugin-styled-components
3. Babel 설정 업데이트
프로젝트 루트에 `.babelrc` 파일을 생성하거나 기존 파일에 다음 설정을 추가합니다.
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
4. 커스텀 Document 구현
`_document.tsx` 파일을 다음과 같이 작성합니다:
import Document, { DocumentContext, DocumentInitialProps } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
}
5. 코드 설명
1. `ServerStyleSheet` 초기화하기
const sheet = new ServerStyleSheet();
`styled-components`에서 서버 사이드 렌더링을 지원하기 위해 `ServerStyleSheet` 인스턴스를 생성합니다.
2. `originalRenderPage` 저장하기
const originalRenderPage = ctx.renderPage;
Next.js의 기본 `renderPage` 함수를 저장합니다. 이 함수는 페이지를 렌더링하는 역할을 합니다.
3. 커스텀 `renderPage` 설정하기
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
});
`ctx.renderPage`를 커스터마이즈하여 `App` 컴포넌트를 감싸는 고차 컴포넌트를 반환합니다. 이 고차 컴포넌트는 `styled-components`의 `collectStyles` 메서드를 사용하여 스타일을 수집합니다.
4. `initialProps` 가져오기
const initialProps = await Document.getInitialProps(ctx);
`Document.getInitialProps`를 호출하여 초기 props를 가져옵니다. 이는 HTML을 렌더링하기 위한 데이터를 포함합니다.
5. 스타일 주입하기
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
초기 props를 반환할 때, 수집된 스타일 요소를 포함하여 반환합니다. `sheet.getStyleElement()`는 수집된 스타일 요소를 JSX 형식으로 반환합니다.
6. 스타일 시트 마감하기
sheet.seal();
`finally` 블록에서 `sheet.seal()`을 호출하여 스타일 시트를 마감합니다. 이는 메모리 누수를 방지하고 스타일 수집을 완료합니다.
결론
Next.js에서 `styled-components`와 같은 CSS-in-JS 라이브러리를 사용하여 서버 사이드 렌더링을 최적화하려면 커스텀 `Document` 설정이 필수적입니다. 위에서 설명한 방법을 통해 초기 페이지 로딩 시 일관된 스타일을 유지하고, 사용자 경험과 SEO를 모두 향상시킬 수 있습니다. 이 가이드를 따라 Next.js 프로젝트에서 효율적인 스타일링을 구현해 보세요.