[Next.js] Styled-components와 SSR

sumin·2023년 8월 9일
0

Next.js

목록 보기
1/5

Next.js에서 Styled-components를 사용할 때 SSR 설정이 필요한 이유?

styled-components는 JS 코드를 통해 컴포넌트에 스타일을 적용하며, 이 스타일은 브라우저에서 페이지가 로드된 후 실행되는 CSR 방식으로 작동한다.
즉, 사용자가 페이지를 방문했을 때 브라우저에서 스타일이 생성되고 적용된다.

SSR 설정을 하면

styled-components로 구현하였을 때, 스타일이 적용되지 않은 상태로 화면이 먼저 표기되고, 이후에 스타일이 적용되는 깜빡임 현상이 나타날 수 있다.
SSR 방식으로 설정하면 서버에서 렌더링 된 HTML 문서에 미리 스타일이 적용되어 전달되기 때문에 이 현상을 최소화할 수 있고, 초기 페이지 로드 속도를 개선 시킬 수 있으며 SEO 최적화에도 도움이 된다.

왜 _document.js 파일에서 스타일을 전달하는 걸까?

_documents.js는 Next.js에서 서버 측 렌더링을 담당하는 파일 중 하나이며, HTML, HEAD, BODY 등의 기본 레이아웃을 설정해 준다.

styled-components에서 제공하는 ServerStyleSheet API를 사용하여 서버에서 스타일 코드를 수집하여 SSR에 필요한 스타일 태그를 생성해 준다. 그리고 스타일 태그를 HTML 문서의 <head>에 삽입하여 최초 페이지 로드 시 스타일이 적용된 상태로 보여준다.

기본 구조

// pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document'
 
export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

Styled-components SSR을 지원하는 구조

// pages/_document.js
import Document from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    /* ServerStyleSheet를 사용하여 React 앱 전체의 스타일 수집 
      -> 서버 렌더링 시에도 모든 스타일이 포함됨 */ 
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      /* getInitialProps 메서드에서 enhanceApp를 이용하여 
         ServerStyleSheet를 적용하고 스타일을 수집 */ 
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        });
	  
      /* getStyleElement()로 수집한 스타일이 
         initialProps.styles와 함께 반환 */
      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }
}

html, body 태그를 설정하고 싶으면 render(){}에 작성하여 추가하면 된다.

import Document, { Html, Head, Main, NextScript } from "next/document";
...(생략)
render() {
    return (
      <Html lang="ko">
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }

참고
vercel github

0개의 댓글