Next.js styled-components SSR 최적화 시키기

0

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 프로젝트에서 효율적인 스타일링을 구현해 보세요.

Leave a Reply