본문 바로가기

front/next.js

[Error] Next.js & styled-components, 새로고침 시 css 초기화 에러 발생 이유와 해결방법

728x90

1. Next.js Error

Next.js에서 새로고침 시 css가 초기화되는 오류가 발생했다.  

다시 저장하면 제대로 css가 적용되지만, 새로고침만 하면 css가 먹지 않고 초기의 모습 그대로 출력됐다.

 

2. 에러 생성 이유

구글에 해당 에러에 대한 해결방안을 검색해보자, styled-components와 렌더링 문제라는 것을 알게 되었다.

 

1) Next.js 렌더링 과정

해당 에러의 원인을 알기 위해선, Next.js의 렌더링 과정을 먼저 알아야 한다.

 

Next.js는 서버에서 모든 페이지를 미리 렌더링(pre-render) 한다.

그 후, 서버는 클라이언트에 미리 렌더링 한 HTML 문서를 전달한다.

 

브라우저는 서버에서 받은 HTML을 문서를 화면에 렌더링 하면서 해당 문서에 존재하는 자바스크립트 파일을 로드, 실행한다.

클라이언트에서는 ReactDOM.render 함수를 통해 React Element를 렌더링 하고,

ReactDOM.hydrate 함수를 이용해 DOM 요소에 이벤트 리스너를 적용하고 렌더링을 진행한다.

 

그러나 pre-rendering 과정은 항상 이루어지지 않는다.

서버에서 미리 페이지가 렌더링 되는 경우는 다음과 같다.

 

- location.href를 사용해 페이지를 이동했을 경우

- a 태그의 href로 페이지를 이동했을 경우

- 브라우저 주소창에 url을 입력해 해당 페이지에 접근했을 경우

- 새로고침 했을 경우

 

 

2) 에러 생성 이유

위에서 설명했듯이 Next.js는 HTML이 렌더링 된 후, JS가 로드된다.

styled-compoents는 Javascript에 의해 동적으로 CSS가 생성된다.

 

즉, CSS가 렌더링 되기 전에 HTML이 렌더링 되기 때문에 새로고침시 에러가 발생하는 것이다. 

 

 

3. 해결 방법

1) babel-plugin-styled-components

yarn add -D babel-plugin-styled-components

위 명령어를 이용해 babel-plugin-styled-components를 설치해 준다.

 

 

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "styled-components",
      {
        "ssr": true,
        "displayName": true,
        "preprocess": false
      }
    ]
  ]
}

root에 .babelrc를 만들어 babel을 설정해 준다.

 

ssr은 style이 server-side에서 적용되도록 한다.

displayName은 디버그 하시 쉬운 className을 설정할 수 있도록 한다.

 

 

 

import Document, {
  Head,
  Main,
  NextScript,
  DocumentContext,
} from 'next/document';
import { ServerStyleSheet } from 'styled-components';

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

  render() {
    return (
      <html lang="ko">
        <Head>
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <meta charSet="utf-8" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

page 폴더 하위에 _documents.ts 파일을 만들어준다.

 

 

사실 구글에 검색했을 때 거의 다 위 방법처럼 나와있길래 그대로 따라 했더니 에러가 발생해다.

_documents.ts 파일에서 에러가 발생했고, 당시 시간이 없어서 파일을 빈 상태로 둔 채 실행했더니 새로고침이 해결됐다.

 

_documents.ts 파일이 없어도 babel-plugin-styled-components로 인해 해결된 것 같았다. 

 

 

 

 

 

728x90