next.js 13 migration from pages router to app router
app router
next.js 13 버전이 도입되면서 기존 pages 폴더와는 다르게 동작하는 app router가 생겼다.
app 디렉토리는 아직 베타버전이지만, 새로 등장한 기능들을 사용하기 위해선 앱 디렉터리를 사용해야 한다.
pages 라우터와 app라우터의 가장 큰 차이점은 app에선 기본적으로 server component가 적용된다는 것이다.
pages 폴더에서 app 폴더로 마이그레이션
1. next.config.js 코드 추가
app directory는 아직 베타버전으로, 사용하기 위해선 next.config.js에 아래 코드를 추가해줘야 한다.
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
};
module.exports = nextConfig;
2. app directory 생성
루트 폴더 혹은 src폴더에 app 폴더 추가
3. next/head 마이그레이션
https://jha-memo.tistory.com/97
4. 루트 폴더에 layout.tsx 파일 생성 후 _document.tsx, _app.tsx 제거
제거하기 전 _document.tsx, _app.tsx에서 필요한 코드들은 layout.tsx로 옮겨준다.
export default function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
기본적으로 layout.tsx는 위 코드만으로도 작동하지만
// pages/_app.tsx
import { theme } from "@chooz/ui";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import type { AppProps } from "next/app";
import styled, { ThemeProvider } from "styled-components";
import { GlobalStyles } from "styles/globalStyles";
import Header from "../components/header/Header";
function App({ Component, pageProps }: AppProps) {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
<ThemeProvider theme={theme}>
<GlobalStyles />
<div id="stars" />
<div id="stars2" />
<div id="stars3" />
<Applayout>
<Header />
<Component {...pageProps} />
</Applayout>
</ThemeProvider>
</QueryClientProvider>
);
}
const Applayout = styled.div`
display: flex;
flex-direction: column;
min-height: 100vh;
padding: 0 16px;
flex: 1;
`;
export default App;
// pages/_document.tsx
import type { DocumentContext } from "next/document";
import Document, { Head, Html, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
// Styled-components
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="kr">
<Head>
<link
as="style"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/variable/pretendardvariable.css"
rel="stylesheet"
/>
<link
as="style"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendardstatic.css"
rel="stylesheet"
/>
</Head>
<body>
<div id="portal" />
<Main />
<NextScript />
</body>
</Html>
);
}
}
위와 같이 document와 app에서 사용하던 코드들이 있어 이것들을 layout.tsx에 하나로 합쳐야 했다.
// 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";
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 />
<ThemeProvider theme={theme}>
<GlobalStyles />
<div id="stars" />
<div id="stars2" />
<div id="stars3" />
<Applayout>
{/* <Header /> */}
{children}
</Applayout>
</ThemeProvider>
</QueryClientProvider>
</body>
</html>
);
}
const Applayout = styled.div`
display: flex;
flex-direction: column;
min-height: 100vh;
padding: 0 16px;
flex: 1;
`;
export default RootLayout;
위와 같이 합쳐주었다.
5. pages 폴더 이동
기존의 index.tsx는 page.tsx로 변경하였다.
자세한 사항은 아래에서 확인할 수 있다.
https://beta.nextjs.org/docs/upgrade-guide#step-4-migrating-pages
공식 문서에서는 먼저 page.tsx를 만들고, 기존 컴포넌트들을 임포트해서 사용하는 것을 추천한다.
6. useRouter import 변경
https://jha-memo.tistory.com/98
7. ssr방식에서 style-components 사용
https://jha-memo.tistory.com/99
마치며
아직 베타버전인 app 디렉터리로 마이그레이션 하면서 수많은 에러를 마주했다.
구글을 뒤져봐도 자료가 별로 없어 공식문서를 계속 읽으면서 진행했다.
다른 사람들은 나처럼 많은 시행착오를 겪지 않기를 바라며 도움이 됐으면 한다.
Reference
https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration
'Web > Next.js' 카테고리의 다른 글
Next.js 13 pages 라우터에서 app router로 마이그레이션하기 - style-components (0) | 2023.02.06 |
---|---|
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 |