ETC -

NextJS 에서 styled-components와 함께 사용하기

  • -

👉🏻Next.js + Typescript + Styled-components 직접 세팅

최근에 NextJS 공부를 시작했습니다.
Next.js + Typescript + Styled-components를 한번 직접 세팅을 해보면서 궁금했던것들을 정리한 글입니다.
브이로그 잘 찾아보면 그냥 세팅하는 방법들은 잘 나와있습니다.
이 글은 NextJS 내부적으로 어떻게 돌아가는지 살펴본 글입니다.

yarn create next-app --typescript

프로젝트 터미널에서 다음과 같이 명령을 작성합니다.

yarn add styled-components @types/styled-components
//rock_paper_scissors/src/pages/_app.tsx

import { ThemeProvider } from "styled-components";
import type { AppProps } from 'next/app';
import theme from "../styles/theme";
import { GlobalStyle } from "../styles/globalStyle";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <GlobalStyle />
      <ThemeProvider theme={theme}>
        <Component {...pageProps} />
      </ThemeProvider>
    </>
  )
}

전체 루트 _app.tsx파일에서 ThemeProvider태그를 추가해서 theme를 props로 추가합니다.

// rock_paper_scissors/src/pages/_document.tsx

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

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>
        <Head>
          <link rel="icon" href="/favicon.ico" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

_document.tsx 파일을 만들어서 위와 같이 작성합니다.[링크참고]

👉🏻styled-components 동작원리 설명

_document.tsx 파일에서
sheet.collectStyles이 부분을 보시면 서버사이드에서 랜더링을 할때 스타일 태그들을 다 뽑아서 head태그 안쪽에
직렬화해서 넣어줍니다.

객체(Object) => 문자열(string) 로 변환하는 것 = 직렬화(Serialization)

문자열(string) => 객체(Object)로 반환하는 것 = 역직렬화(Deserialization) or 파싱(Parsing)

👉🏻직접눈으로 체감해봅시다.

sheet.collectStyles을 App컴포넌트에 넣어주고 빌드를 해보겠습니다.

yarn build

그럼 .next/server/pages/index.html 으로 이동해서 파일을 열어서 보면
다음과 같이 style 초기화했던 css 들이 head태그 안쪽으로 들어가 있는것을 확인 할수 있습니다.

이번에는 반대로 sheet.collectStyles을 App컴포넌트에서 빼고 빌드를 해보겠습니다.
그럼 위랑 똑같은 파일에가서 .next/server/pages/index.html 열어서 보면 다음과 같이 
style 초기화했던 css 들이 없어진것을 확인 할수가 있습니다. 🔥🔥

🙋🏻 그럼 어디로 간걸까요?

바로 위에 이미지에서 강조한 빨간색 박스 부분 head 태그 안쪽에 style 태그로 감싸져서 들어가 있습니다. 
그 안쪽에 css 속성들은 우리 눈에는 보이진 않지만 저기에 다 들어가 있는것입니다.

의심되어서 검사요소에서 저 style태그 부분만 삭제를 해보면
style 초기화했던 css 들이 적용이 안되는것을 확인할수가 있습니다.!!🔥👍✨

👉🏻.babelrc

최초 SSR 이후 CSR로 라우팅을 하게 되는데, 
이 때 서버에서 생성하는 해시값과 브라우저에서 생성하는 해시값이 서로 달라서 에러가 발생하게 됩니다.
(Prop className did not match)

이를 해결하기 위해 바벨 플러그인을 설치해 주었습니다. [링크참고]

// .babelrc
{
    "presets": ["next/babel"],
    "plugins": [
        [
            "babel-plugin-styled-components",
            {
                "fileName": true, //코드가 포함된 파일명을 알려줌
                "displayName": true, //클래스명에 해당 스타일 정보 추가
                "pure": true, //사용하지 않은 속성 제거
                "ssr": false //클라이언트 측에서 구성 요소를 동적으로 로드하려할때 서버 렌더링을 비활성화할 수 있습니다. 
            }
        ]
    ]
}

👉🏻Next.js + Typescript + Styled-components

boilerplate을 만들었습니다. 실행 방법 및 설명은 README파일 참고 하세요.

 

GitHub - younhoso/younhoso

Contribute to younhoso/younhoso development by creating an account on GitHub.

github.com

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.