본문 바로가기
📺 Front End/-- react & redux & nextjs

[Next.js] renderPage 함수로 styled-components 오류 방지하기.

by Wonit 2021. 1. 3.

React 에서 스타일링을 위한 라이브러리에서는 개인적으로 단연 styled-components가 압도적으로 좋다고 생각한다.

 

하지만 Next에서는 styled-components가 제대로 동작하지 않을 수 있다.

 

오늘 그 오류들을 분석해보고 어떻게 하면 해결할 수 있는지 알아보자.

그냥 styled-components를 사용한다면 어떤 문제가 생가나요?

Next.js 앱에서 styled-components를 사용하면 스타일이 적용되지 않고 일정 시간 이후에 스타일이 적용된다.

마치 CSS 파일이 로드되지 않다가 갑자기 로드되는 것 처럼 보이는데, 이는 UX에서 나쁜 평가로 각인될 수 있고 사용자에게 아주 불쾌한 경험을 선사할 수 있다.

왜 그런 문제가 생기나요?

Next는 우선 모든 페이지가 Pre-Render 된다고 지난 시간에 배웠었다.

 

이런 Pre-Render에는 2가지 Stage가 존재하는데,

  1. Initial Load Stage
  2. Hydration Stage

각각의 Stage에 대해서 간략히 설명하자면, Initial Stage에서 Static하게 생성된 HTML이 렌더된 후, Hydration Stage에서 나머지 JS 파일들이 로드되어 Sync된다.

 

결국 우리가 getStaticPropsgetStaticPaths로 SSG를 이용해 static한 HTML을 만들어 SSR(SSG) 환경을 구축했다고 치더라도 JS에 의해 동적으로 CSS가 생성되는 CSS-In-Js 방식인 styld-components는 SSG 과정에서 생성되는 HTML에 우리의 코드가 함께 Build 되지 않게 된다.

 

이를 한 문장으로 이야기 한다면,

 

Next.js는 SSR혹은 SSG를 기본으로 사용해서 Pre-Rendering 을 하면서 Initial Load 과정에서 미리 HTML을 로드하고 Hydration 과정에서 다른 파일들을 로드하기 떄문에 발생한다.

 

로 말할 수 있다.

어떻게 해결할 수 있나요?

 

해결 방법은 Next.js 공식 홈페이지에서 이야기하는 renderPage 함수로 해결할 수 있다.

공식 홈페이지에서는 renderPage 함수를 커스터마이징하는 것은 오로지 CSS-in-js 라이브러리를 사용할 떄만 사용하라고 나와있다.

 

그리고 우리는 다음과 같은 방식으로 이 문제를 해결할 것이다.

 

  • pages 디렉토리에 Next 앱의 HTML Custom 설정을 할 수 있는_document.js 파일을 생성
  • ServerStyleSheet 함수를 styled-components에서 import 하여 Global하게 설정
  • renderPage 함수로 렌더링 조건 Customizing.

 

1. _document.js 파일 생성

pages 디렉토리 아래에 _document.js 파일을 생성한다.
next는 _document.js 파일을 Build 시점에 env 파일로 간주하고 생략한다.

import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

 

2. ServerStyleSheet 함수 import

_document.js에서 SC의 ServerStyleSheet를 import 한다.

// ... 생략
import { ServerStyleSheet } from 'styled-components';
// ... 생략

 

3. renderPage 함수 조건 추가.

_document.js 에서 renderPage 함수 추가.

import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    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();
    }
  }
}

 

그리고 다시 실행시키게 된다면 다음과 같이 정상적으로 styled-components가 잘 동작하는 것을 볼 수 있다.

 

댓글0