[TypeScript Error] Next.js + TypeScript + Styled-components 에러

고병표·2022년 3월 14일
1

Error 모음

목록 보기
10/13
post-thumbnail

😥 Next.js에서 발생한 Styled-Components 에러

React나 TypeScript를 이용하여 프로젝트를 진행할 때 컴포넌트의 props를 이용하여 쉽게 스타일을 다르게 코딩할 수 있는 장점 때문에 styled-components를 사용한다.

하지만 이번에 Next.js를 이용하여 프로젝트를 진행했는데 style이 바로 적용되지 않고 일정 시간 후에 적용되는 에러를 발견했다.

그 전 프로젝트는 프로젝트 설계를 다른 팀원이 했기에 발견하지 못해서 당황했지만 역시 구글은 답을 알고 있었다...(갓 구글!)

🤔 에러 발생 이유

에러가 발생한 원인은 Next.js의 Pre-Render 때문이다.

Pre-Render에는 2가지 Stage가 존재한다.

  1. Initial Load Stage
  2. Hydration Stage

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

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

한 문장으로 요약!

Next.js의 특징인 Pre-Rendering, Initial Load 과정에서 미리 HTML을 로드하고 Hydration 과정에서 다른 파일들을 로드하기 떄문에 발생한다.

🔥 How to fix ?

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

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

  • 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, DocumentContext } from 'next/document';
import { ServerStyleSheet } from 'styled-components';

class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

...

2. ServerStyleSheet 함수 import

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

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

3. renderPage 함수 조건 추가.

_document.js 에서 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개의 댓글