ssr방식에서 style-components
기존 pages 라우터 방식에서는 server side rendering을 위해 아래 코드를 추가하여 사용했다.
// _document.tsx
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()
}
}
}
하지만 app 라우터에서는 _app과 _document는 사용하지 않기 때문에 위 방식으로는 불가능하다.
이에 따라 공식문서에서는 새로운 방법을 제안하고 있다.
// lib/registry.tsx
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
export default function StyledComponentsRegistry({
children,
}: {
children: React.ReactNode;
}) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return <>{styles}</>;
});
if (typeof window !== 'undefined') return <>{children}</>;
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
{children}
</StyleSheetManager>
);
}
// app/layout.tsx
import StyledComponentsRegistry from './lib/registry';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
);
}
위 코드를 그대로 옮겨 적으면 styledComponentsStyleSheet.instance.clearTag()에서 다음과 같은 에러가 나타난다.
해당 코드 위에 // @ts-ignore를 추가해 주면 된다. ( https://github.com/vercel/next.js/issues/42526)
// @ts-ignore
styledComponentsStyleSheet.instance.clearTag();
하지만 아래와 같은 에러가 발생했다.
https://tesseractjh.tistory.com/164
위 블로그를 보고 next.config.js에 아래 코드를 추가해 주었다.
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
compiler: {
styledComponents: true,
},
};
module.exports = nextConfig;
만약 swc를 사용하지 않고 babel을 사용하는 경우에는 위 블로그 참고하면 된다.
이번에는 터미널에서 에러가 나타났다.
<StyledComponentsRegistry> 안에 여러 children이 있는 것이 의심되었고, {children} 하나만 남겨보았더니 에러가 더 이상 나타나지 않았다.
<ReactQueryDevtools />을 밖으로 빼 <StyledComponentsRegistry>의 자식이 하나가 되도록 해주었다.
// app/layout.tsx
"use client";
import { theme } from "@chooz/ui";
import styled, { ThemeProvider } from "styled-components";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { GlobalStyles } from "styles/globalStyles";
import Header from "components/common/Header";
import StyledComponentsRegistry from "../lib/registry";
function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}: {
children: React.ReactNode;
}) {
const queryClient = new QueryClient();
return (
<html lang="kr">
<body>
<div id="portal" />
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
<StyledComponentsRegistry>
<ThemeProvider theme={theme}>
<GlobalStyles />
<div id="stars" />
<div id="stars2" />
<div id="stars3" />
<Applayout>
<Header />
{children}
</Applayout>
</ThemeProvider>
</StyledComponentsRegistry>
</QueryClientProvider>
</body>
</html>
);
}
const Applayout = styled.div`
display: flex;
flex-direction: column;
min-height: 100vh;
padding: 0 16px;
flex: 1;
`;
export default RootLayout;
app 디렉터리에서 스타일 컴포넌트의 서버 사이드 랜더링에 대해서 자세히 알고 싶다면 아래에서 확인하면 된다.
https://beta.nextjs.org/docs/styling/css-in-js#styled-components
'Web > Next.js' 카테고리의 다른 글
Next.js 13 pages 라우터에서 app router로 마이그레이션하기 - Routing Hooks (0) | 2023.02.02 |
---|---|
Next.js 13 pages 라우터에서 app router로 마이그레이션하기 - next/head (0) | 2023.02.01 |
Warning: Extra attributes from the server: style (2) | 2023.01.30 |
Next.js 13 pages 폴더에서 app router로 마이그레이션하기 (0) | 2023.01.29 |