본문 바로가기
프론트엔드/Next.js

[Next.js] Next.js13 App router에 styled-components 적용하기 / global style / registry.tsx

by PARADISE247 2023. 7. 11.
반응형

Next.js v13에서는 기존의 Pages Router와 달리 App Router 방식이 등장했다. 

그리하여, 이에 따라 styled-components 설정 방식도 변경되었다. 

 

 

기존 Page Router에서는 _document.jsx와 _app.jsx가 존재했으나 App router에선 사용하지 않는다. 

App router에선 lib/registry.jsx 와 app/layout.jsx를 사용한다. 

 

 

💀 Page Router에서 styled-components를 사용하기 위한 설정 방식

//_document.jsx

import { Fragment } from "react";
import Script from "next/script";
import { ServerStyleSheet } from "styled-components";
import Document, { Html, Head, Main, NextScript } from "next/document";

function MyDocument() {
  return (
    <Html lang="en" dir="ltr">
      <Head>
      ...생략
      </Head>

      <body>
       
        <noscript>
         ...생략
        </noscript>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

MyDocument.getInitialProps = async (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: (
        <Fragment>
          {initialProps.styles}
          {sheet.getStyleElement()}
        </Fragment>
      ),
    };
  } finally {
    sheet.seal();
  }
};

export default MyDocument;

_document.jsx에서 ServerStyleSheet 을 불러와 설정해주었다.

// _app.jsx

import Head from "next/head";
import { RecoilRoot } from "recoil";
import { Router } from "next/router";
import { useEffect, useState } from "react";
import GlobalStyle from "@/styles/global_style";

function App({ Component, pageProps, token, refresh }) {
  const getLayout = Component.getLayout || ((page) => page);

  return (
    <>
      <Head>
        <meta name="viewport" content="width=device-width, maximum-scale=1.0" />
      </Head>
      <GlobalStyle />
      <RecoilRoot>
        {getLayout(<Component {...pageProps} loading={loading} />)}
      </RecoilRoot>
    </>
  );
}
export default App;

_app.jsx에서 GlobalStyle을 적용해주었다. 

 

 

🤩 APP Router에서 styled-components를 사용하기 위한 설정 방식

// lib/registry.tsx

"use client";

import React, { useState } from "react";
import { useServerInsertedHTML } from "next/navigation";
import { ServerStyleSheet, StyleSheetManager } from "styled-components";
import GlobalStyle from "../../styles/global_style"; // global styling 적용 부분

export default function StyledComponentsRegistry({
  children,
}: {
  children: React.ReactNode;
}) {

  const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());

  useServerInsertedHTML(() => {
    const styles = styledComponentsStyleSheet.getStyleElement();
    styledComponentsStyleSheet.instance.clearTag();
    return <>{styles}</>;
  });

  if (typeof window !== "undefined")
    return (
      <>
        <GlobalStyle /> // global styling 적용 부분
        {children}
      </>
    );

  return (
    <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
      {children as React.ReactElement}
    </StyleSheetManager>
  );
}

13버전부터는 위와 같이 ServerStyleSheet 과 Global styling을 적용한다. 

 

이렇게 하면 이제 Next.js13의 App router에서 styled-components를 사용할 수 있다. 

 

 

예시

// styles/auth.js

import { styled } from "styled-components";

export const SigninLayout = styled.div`
  display: flex;
  width: 100%;
  height: 100%;
  min-width: 100vw;
  min-height: 100vh;
  background-color: #fff;
`;
// app/signin/page.tsx

import React from "react";
import { SigninLayout } from "../../styles/auth";

const SignIn: React.FC = () => {
  return <SigninLayout>Sign in page</SigninLayout>;
};

export default SignIn;

위와 같이 SigninLayout이라는 스타일링 컴포넌트를 사용하고자 할 경우, 아래와 같은 에러가 발생한다. 

Server Error
TypeError: createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component
This error happened while generating the page. Any console logs will be displayed in the terminal window.

styled-components는 클라이언트 사이드에서 동작하기 때문에 

 

"use client";

import React from "react";
import { SigninLayout } from "../../styles/auth";

const SignIn: React.FC = () => {
  return <SigninLayout>Sign in page</SigninLayout>;
};

export default SignIn;

이와 같이 상단에 "use client"를 추가해주면 에러가 발생하지 않고 스타일이 잘 적용된다. 

반응형