반응형
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"를 추가해주면 에러가 발생하지 않고 스타일이 잘 적용된다.
반응형