[Next.js] ๊ฐ์ธ ํ๋ก์ ํธ 11์์ 14๋ก ๋ง์ด๊ทธ๋ ์ด์ ๊ณผ์
๐๋ค์ด๊ฐ๋ฉฐ
์ฝ 2๋ ์ Next.js(SSG) + TypeScript๋ก ์ ์ํ ๋กค MBTI ํ๋ก์ ํธ๋ฅผ 14 ๋ฒ์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ ๊ณผ์ ๋ฐ ํ๊ธฐ๋ฅผ ์์ฑํด๋ณด๋ ค ํ๋ค.
ํด๋น ๊ธ์ ๋ง์ด๊ทธ๋ ์ด์ ์ ์ด๋ป๊ฒ ํ๋ ๊ฐ์ ๋ํ ๊ธ์ด ์๋๋๋ค.
์ ๋ชฉ ๊ทธ๋๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ ํ๋ ๊ณผ์ ์ ๊ธฐ๋กํ๋ ๊ธ์ ๋๋ค.
๋ง์ด๊ทธ๋ ์ด์ ์ ์ ํด์ผ ํ๋๊ฐ?
์ฌ์ค ์๋น์ค ์์ฒด๋ก๋ ๋ฌธ์ ์์ด ์ ๋์๊ฐ๊ณ ์์๋ค.
๋์ ์ Next.js๋ก ์ง์ A - Z๊น์ง ํด๋ณธ ํ๋ก์ ํธ๋ ๋กค MBTI ํ๋ก์ ํธ๋ฐ์ ์์๋ค.
Next.js๊ฐ 11๋ฒ์ ์ ๋นํด 14 ๋ฒ์ ๊น์ง(ํนํ 13 ๋ฒ์ ์์) ๋ง์ ๋ถ๋ถ์ด ๋ณ๊ฒฝ์ด ๋๊ณ , ์ด๋ป๊ฒ ํ์ฉํ ์ ์๋์ง ์ง์ ๋ถ๋ชํ๋ฉฐ ๊นจ๋ฌ์ผ๋ฉฐ ํ์ตํ๋ ๋ฐฉ์์ผ๋ก ์ง์์ ํก์ํ๋ ๋์ ์ฑํฅ์ธ ์ด์ ์ ์ต์ ๋ฒ์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๋ฉด์ ๊ธฐ์กด ์ฝ๋์๋ ์ด๋ป๊ฒ ๋ฌ๋ผ์ง๊ณ ์ด๋ค ๊ฐ์ ์ ์ด ์๊ธธ๊น ํ๋ ๋ถ๋ถ์์ ๊ถ๊ธํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ถํ์ ๊ธฐ๋ฅ ์ถ๊ฐ ์์ ๋ ์๊ธฐ๋ ํ๊ณ 2๋ ์ ์์ฑ๋ ์ฝ๋๋ผ ๋ฒ์จ ๋ ๊ฑฐ์ ์ฝ๋๊ฐ ๋์ด๋ฒ๋ ธ๋ค..
๊ทธ๋์ ๊ธฐ์กด์ ์๋ Next.js 11 ๋ฒ์ ์ ํ๋ก์ ํธ๋ฅผ ์ต์ ๋ฒ์ (14 ๋ฒ์ )์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํด๋ณด๊ธฐ๋ก ๋ง์๋จน๊ฒ ๋์๋ค.
๊ธฐ์กด ํ๋ก์ ํธ์์ ์ด๋ค ๋ถ๋ถ๋ค์ด ๋ณ๊ฒฝ์ด ๋์๋๊ฐ?
์๋๋ ๋ง์ด๊ทธ๋ ์ด์ ์ ํ๋ฉด์ ํ๋ก์ ํธ ์ค ๋ณ๊ฒฝ๋ ๋ด์ฉ์ด๋ค.
1. ๊ด๋ จ ์ข ์์ฑ ์ ๋ฐ์ดํธ
- Next.js 11.0.1 -> 14.0.4
- React 17.0.2 -> 18.2.0
- styled-components 5.3.0 -> 6.1.1
- Typescript 4.3.5 -> 4.5.2
2. page router -> app router
์๋๋ app router๋ก ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ๋ฉด์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ค์ด๋ค.
- ๊ตฌ๊ธ ์ ๋๋ฆฌํฑ์ค ์ฝ๋ ๋ณ๊ฒฝ
- styled-components ๊ด๋ จ ์ฝ๋ ๋ณ๊ฒฝ
- ์นด์นด์คํก ๊ณต์ ํ๊ธฐ ๋ก์ง ๋ณ๊ฒฝ
- ์ ์ฒด์ ์ธ ํด๋๊ตฌ์กฐ ๋ณ๊ฒฝ
- ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ ๋ถ๋ฆฌ
3. getStaticPaths -> generateStaticParams
4. ํฐํธ ์ ์ฉ ๋ณ๊ฒฝ ๋ฐ ์ต์ ํ
- @fontsource -> next/font
5. metadata ์ ์ฉ ๋ฐฉ๋ฒ ๋ณ๊ฒฝ
๋ง์ด๊ทธ๋ ์ด์ ๊ณผ์
๊ด๋ จ ์ข ์์ฑ ์ ๋ฐ์ดํธ
๋จผ์ next.js ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด์ ์ต์ ๋ฒ์ ์ผ๋ก ์ ๋ฐ์ดํธ ์์ผฐ๋ค.
yarn add next@latest react@latest react-dom@latest eslint-config-next@latest
์ดํ์ yarn dev๋ฅผ ์ด์ฉํด์ ์ ์คํ์ด ๋๋์ง ํ์ธ์ ํด๋ณด์์ง๋ง,
ํ์ ์คํฌ๋ฆฝํธ๋ 4.5.2 ๋ฒ์ ์ด์์ด ๋์ด์ผ ๋๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ํด์ ์ต์ ๊ตฌ๋ ๋ฒ์ ์ธ 4.5.2 ๋ฒ์ ์ผ๋ก ๊ธ๋ก๋ฒ๋ก ์ ๋ฐ์ดํธํ๋ค.
npm install -g typescript@4.5.2
๊ทธ๋ฆฌ๊ณ , ๊ธฐ์กด styled-components๋ ๋ฒ์ (5.3.0)์ด ๋งค์ฐ ์ค๋๋์๊ธฐ์, ์ด ์ฐธ์ ์ต์ ๋ฒ์ (6.1.1)์ผ๋ก ์ ๋ฐ์ดํธํ๋ค.
yarn add styled-components@latest
๊ทธ๋ฆฌ๊ณ ๋น์์๋ "@types/styled-components": "^5.1.11"๋ฅผ ๋ฐ๋ก ์ค์นํด ์คฌ์๋๋ฐ ์ด๋ฒ์ ์ ๋ฐ์ดํธํ๋ฉด์ @types๋ ๋ฐ๋ก ํ์ํ์ง ์๊ฒ ๋ ๊ฒ ๊ฐ์ ์ ๊ฑฐํด ์คฌ๋ค.
์ถ๊ฐ๋ก node ๋ฒ์ ๋ ์ต์ 18.17 ์ด์์์๋ง ๊ตฌ๋์ด ๋๊ธฐ ๋๋ฌธ์ node ๋ฒ์ ๋ lts ๋ฒ์ ์ผ๋ก ๋ง์ท๋ค.(20.10.0 ๋ฒ์ )
app router ์ ์ฉ
๋๋ง์ app router ์ ์ฉ์ด๋ค..
13 ๋ฒ์ ์ด ๋์จ ํ page router ์ธ์ app router๊ฐ ์๊ฒผ๋ค.
app directory ๋ด๋ถ์์๋ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฒ์ปดํฌ๋ํธ๋ก ๋์ํ๋ค.
React์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ ์๋ฒ์ปดํฌ๋ํธ
ํ์ฌ page router์ app router๋ฅผ ์ ํํ ์ ์๋ค.
page router๋ ๊ธฐ์กด์ ๋ฐฉ์๊ณผ ๋์ผํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ธ๊ฐ์ง๋ง app router๋ก ๋ฐ๋๋ฉด์ ๊ตฌ์กฐ๊ฐ ์๋นํ ๋ฐ๋์๋ค.
ํ์คํ app router๊ฐ ๋ถํธํ๋ค๋ ๊ธ ๋ค์ด ๋ง์ด ๋ณด์๋๋ฐ ์ด๋ฒ์ app rotuer๋ฅผ ์ ์ฉํ๋ฉด์ ์ ๋ถํธํ๋ค๋ ๊ฑด์ง ๋ชธ์ ๋๋ผ๊ฒ ๋์๋ค.
๊ทธ๋์ app router๋ฅผ ์ ์ฉํ๊ธฐ ์ ์ ์ app router๋ฅผ ์ฌ์ฉํด์ผ ํ๋์ง app router๋ก์จ ์ด๋ค ์ด์ ์ ๊ฐ์ ธ๊ฐ ์ ์๋์ง ์ ์๊ฐํด ๋ณด๊ธธ ๋ฐ๋๋ค.
์๋ฌด ์ด์ ์์ด app router๋ฅผ ์ ์ฉํ๋ค๊ฐ ์คํ๋ ค ์๋ฒ ์ปดํฌ๋ํธ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ ๋๋ฌธ์ ์์ฐ์ฑ์ด ์ ํ๋ ์ ์๋ค๊ณ ๋๊ผ๋ค.
๋ด๊ฐ app router๋ฅผ ์ ์ฉํ ์ด์ ๋ ํ๋ก์ ํธ ์์ฒด๊ฐ ํฌ์ง ์๊ณ ํ ์ด ํ๋ก์ ํธ ์ ๋์ ๊ท๋ชจ์ด๊ธฐ ๋๋ฌธ์ app router๋ฅผ ์ ์ฉํ๋ค๊ณ ํด์ ๋๊ท๋ชจ ์ ๋ฐ์ดํธ(?)๊ฐ ์ผ์ด๋์ง ์์ ๊ฒ์ด๋ผ ์๊ฐํ๊ณ ๊ธฐ์กด page router์ ๋ฌด์จ ์ฐจ์ด๊ฐ ์๋์ง, ์ ์ฌ๋๋ค์ด ๋ถํธํ๋ค๊ณ ํ๋ ๋ชฉ์๋ฆฌ๊ฐ ๋ค๋ฆฌ๋์ง ๋๊ปด๋ณด๊ณ ์ถ์๋ค.
์๋๋ฉด ๋ถํธํ ๊ฒ์ ์ฃผ๊ด์ ์ธ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ด๊ฐ ์ฌ์ฉํ์ ๋๋ ์คํ๋ ค app router๊ฐ ๋ ๋ง์ ์ ์๋ค๋ ์๊ฐ๋ ๋ค์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ์ฌ์ง์ app router์ ๊ตฌ์กฐ๋ค.
page ํ์ผ ์ด๋ฆ์ด ๊ณง ํด๋น ๋ผ์ฐํธ๋ฅผ ๋ณด์ฌ์ค ์ปดํฌ๋ํธ๊ฐ ๋๋ ๊ฒ์ด๋ค.
app ํด๋ ๋ด์ ํด๋ ์ด๋ฆ์ด route๊ฐ ๋๋ค.
ํด๋น ๊ท์น์ ๊ผญ ์ง์ผ์ผ ํ๋ค.
page ํ์ผ ์ด๋ฆ ์ธ์ ๋ค๋ฅธ ํ์ผ์ด๋ฆ์ด ์์ด๋ ๋ค๋ฅธ ํ์ผ์ ๋ฐ๋ก route๋ก ์ธ์๋์ง ์๋๋ค.
ํ์ผ ๊ตฌ์กฐ ๋ณ๊ฒฝ
๋จผ์ ์๋๋ ๋ด ํ๋ก์ ํธ์์ app router๋ฅผ ์ ์ฉํ app ํด๋์ ๊ตฌ์กฐ๋ค
app ํด๋ ๋ด์์ ์ฌ๋งํ ์ปดํฌ๋ํธ๋ฅผ ๊ด๋ฆฌํ๋ ์ฝ๋๋ค๋ ๋ดค๊ณ ๊ธฐ์กด์ฒ๋ผ components ํด๋๋ฅผ ์์ฑํ๊ณ ๊ทธ๊ณณ์์ ๊ด๋ฆฌํ๋ ์ฝ๋๋ ๋ดค๊ณ ์ฌ๋ฌ ํด๋ ๊ด๋ฆฌ ์ปจ๋ฒค์ ์ด ์๋ ๊ฒ ๊ฐ๋ค.
๋๋ ๊ธฐ์กด๊ณผ ๋ง์ ๋ณํ๊ฐ ์๋๋ก ํ๊ณ ํ์๊ฐ ๋ ๊ฐ๋ ์ฑ์ด ์ข๊ฒ ๋๊ปด์ ํ์๋ฅผ ํํ๋ค.
๊ธฐ์กด์ page rotuer์ ๋ค๋ฅด๊ฒ _document.tsx์ _app.tsx๊ฐ ์ฌ๋ผ์ง๊ณ layout.tsx์์ ๋ชจ๋ ๊ด๋ฆฌํ๊ฒ ๋ณ๊ฒฝ๋์๋ค.
๊ธฐ์กด Layout์ผ๋ก ๊ณตํต ์ปดํฌ๋ํธ ์คํ์ผ๋ง์ ํด์คฌ์๋๋ฐ layout.tsx์์ ๊ด๋ฆฌํ๋ฉด ๋๋๋ก ๋ณ๊ฒฝํ๋ค.
// app router ์ ์ฉ ์ Layout.tsx
import GlobalStyle from 'styles/GlobalStyle';
import styled from 'styled-components';
import { size } from 'styles/theme';
import Footer from './Footer';
import DarkModeBtn from '../Buttons/DarkModeBtn';
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import * as gtag from 'utils/gtag';
...
const Layout = ({ children }: ILayoutProps) => {
...
return (
<Wrapper>
<GlobalStyle />
<DarkModeBtn />
{children}
<Footer />
</Wrapper>
);
};
export default Layout;
์ ์ฝ๋๋ ์ด์ ๋ ์ด์์์ ๋ด๋นํ๋ ์ฝ๋๋ค.
๊ธฐ์กด Layout.tsx์ ์๋ ์ฝ๋ app/layout.tsx ํ์ผ์ ์์ฑ ํ ๊ทธ๋๋ก ๊ฐ๋ค๊ฐ ์ฎ๊ฒผ๋๋ฐ
- 'use clinet'๋ฅผ ์ฌ์ฉํ ์ ์๋ ๊ด๋ จ ์๋ฌ๊ฐ ๋ฐ์
- Uncaught Error: invariant expected app router to be mounted ์๋ฌ ๋ฐ์
๋จผ์ 1๋ฒ์ ๊ดํ ์๋ฌ์ ๋ํ ํด๊ฒฐ์ฑ ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ ๊ด๋ จ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ ํ RootLayout์์ ๊ฐ์ธ๋ ํ์์ผ๋ก ์ฌ์ฉ์ ๊ฐ๋ฅํ๋ค๋ ํด๊ฒฐ์ฑ ์ ๋ฐ๊ฒฌํ๋ค.
2๋ฒ์ ๊ดํด์๋ layout.tsx๋ body๋ฅผ ์๋์ผ๋ก ์์ฑํ์ง ์๊ธฐ ๋๋ฌธ์ ๊ผญ body๋ฅผ ๊ฐ์ด ์์ฑํด์ค์ผ ํ๋ค. (์ฐธ๊ณ )
์์ ๊ฐ์ด ๊ธฐ์กด์ Layout.tsx์์ ์ฌ์ฉํ๋ ์ฝ๋๋ค์ 'use clinet' ๋ฌธ๋ฒ๊ณผ ํจ๊ป ์์ฑ ํ layout.tsx์ ์ ์ฉ์์ผ ์ฃผ์๋ค.
(ClientComponentContainer.tsx๋ฅผ ๋ณด๋ฉด StyledComponentsResistry ์ปดํฌ๋ํธ๊ฐ ์๋๋ฐ ์ด์ ๊ดํ ๋ด์ฉ์ ๋ค์์ ๋ค๋ฃฌ๋ค.)
๊ทธ๋ฆฌ๊ณ ๊ธฐ์กด์๋ next/head๋ฅผ ์ด์ฉํด์ meta tag๋ฅผ ์์ฑํด ์คฌ์ง๋ง Next 14 ์ดํ์์๋ nextjs์ metadata๋ฅผ ํตํด ์์ฑํ ์ ์๋ค.
Next.js์๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ฉํ๋ฐ์ดํฐ(์: HTML ๋ด์ meta ๋ฐ link ํ๊ทธ )๋ฅผ ์ ์ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ๋ฉํ๋ฐ์ดํฐ API๊ฐ ์์ต๋๋ค. head ์์) ๊ฐ์ ๋ SEO ๋ฐ ์น ๊ณต์ ๊ฐ๋ฅ์ฑ.
ํด๋น ๊ณต์๋ฌธ์
์ฃผ์ ์ฌํญ์ผ๋ก metadata๊ฐ ์์ฑ๋ ๊ณณ์ ๊ผญ ์๋ฒ ์ปดํฌ๋ํธ์์ ์์ฑ๋์ด์ผ ํ๋ค!
ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์์ฑํ๊ฒ ๋๋ฉด ์ค๋ฅ๋ฅผ ๋ง์ดํ ์ ์์ ๊ฒ์ด๋ค.
๊ทธ๋์ Mbti ํ์ด์ง๋ Mbti ์ปดํฌ๋ํธ๋ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ์์ฑ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ import ํ๋ ๋ฐฉ์์ผ๋ก ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ค.
app/Mbti/page.tsx
SSG
์ด์ ๊ธฐ์กด mbti ํ์ ๋ค์ ๋ํ ํ์ด์ง์ ๋ํด์ SSG๋ฅผ ์ ์ฉํ๋ getStaticPaths๋ฅผ ์ ์ฉํ๋ ๊ฒ๋ค์ ๋ณ๊ฒฝํ ์ฐจ๋ก๋ค.
๋จผ์ Next 13 ์ดํ app router์ ๋ํด์๋ generateStaticParams๋ฅผ ํตํด SSG๋ฅผ ๊ตฌํํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ด์ ์๋ ๋์ ์ผ๋ก metadata๋ฅผ ์ค์ ํด ์คฌ๊ธฐ ๋๋ฌธ์ ์ด๋ฒ์๋ generateMetadata๋ฅผ ํตํด ๋์ ์ผ๋ก metadata๋ฅผ ์ ์ฉํ ์ ์๋ค.
app/mbti/[type]/page.tsx
์ฌ๊ธฐ์ ๋จผ์ ์ฃผ์ํ ์ ์ ํด๋ ๋ช ์ [type]์ผ๋ก ์ง์ ํ๊ธฐ ๋๋ฌธ์ generateStaticParams์ return ๊ฐ์ฒด ์ด๋ฆ๋ type์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ์ง์ ํด์ผ ํ๋ค.
๊ธฐ์กด์ View๋ฅผ ๋ด๋นํ๋ ์ฝ๋๋ค์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ด๊ธฐ ๋๋ฌธ์ Type ์ปดํฌ๋ํธ๋ก ๋ฐ๋ก ๋ถ๋ฆฌํด์ ๋ถ๋ฌ์์ ์ฌ์ฉํ๋ค.
styled-components ์ถ๊ฐ ๋ก์ง
๊ธฐ์กด์ Next์์ styled-components๋ฅผ ์ ์ฉํ๊ธฐ ์ํด์ _document.tsx์ ์ถ๊ฐ ์ฝ๋๋ฅผ ์์ฑํด์ค์ผ ํ๋ค.
ํ์ง๋ง app router์์ _document.tsx ํ์ผ์ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ์ ์ธ ๋ก์ง์ด ํ์ํ๋ค.
ํด๋น ๊ณต์๋ฌธ์ ๊ทธ๋๋ก ๋ฐ๋ผ ํ๋ฉด ์ ์ ์ฉ์ด ๋์๊ธฐ ๋๋ฌธ์ ๋ฑํ ์ด๋ ค์ด ์ ์ ์์๋ค.
๋จผ์ 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>
);
}
์ ์ฝ๋๋ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ด๊ธฐ ๋๋ฌธ์ ์ต์์(layout.tsx)์ ๋ฐฐ์นํ๊ธฐ ์ ์ ์๋์ ๊ฐ์ด ClientComponentContainer.tsx์ ๋ฃ์ด์คฌ๋ค.
'use client';
import styled from 'styled-components';
import ThemeProvider from 'context/ThemeProvider';
import GlobalStyle from 'styles/GlobalStyle';
import Footer from 'components/common/Footer';
import DarkModeBtn from 'components/Buttons/DarkModeBtn';
import StyledComponentsRegistry from 'lib/registry';
import { size } from 'styles/theme';
const Wrapper = styled.div`
width: 100%;
max-width: ${size.mobile};
min-height: 100vh;
margin: auto;
padding: ${({ theme }) => theme.padding.base};
display: flex;
flex-direction: column;
justify-content: center;
`;
interface IClientComponentContainerProps {
children: React.ReactNode;
}
function ClientComponentContainer({
children,
}: IClientComponentContainerProps) {
return (
<StyledComponentsRegistry>
<ThemeProvider>
<Wrapper>
<GlobalStyle />
<DarkModeBtn />
{children}
<Footer />
</Wrapper>
</ThemeProvider>
</StyledComponentsRegistry>
);
}
export default ClientComponentContainer;
๊ทธ๋ฆฌ๊ณ ์๋์ ๊ฐ์ด next.config.ts์ ๊ธฐ์กด์ ์๋ ์ฝ๋๋ฅผ ์ญ์ ํ๋ค๊ฐ classname did not match ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
compiler: {
// ssr and displayName are configured by default
styledComponents: true,
},
์ ์ฝ๋๋ ๊ธฐ์กด๊ณผ ๋์ผํ๊ฒ ํ์ํ๋ฏ๋ก ๊ผญ ์์ฑํด ์ฃผ๋๋ก ํ๋ค.
๊ตฌ๊ธ ์ ๋๋ฆฌํฑ์ค ์ฝ๋ ๋ณ๊ฒฝ
app router๋ก ๋ณ๊ฒฝ๋๋ฉด์ ๊ตฌ๊ธ ์ ๋๋ฆฌํฑ์ค์ ์ฝ๋์ ์์ ๋ ํ์ํ๋ค. (์ฐธ๊ณ ํ ๋ธ๋ก๊ทธ)
๊ธฐ์กด์๋ _document.tsx์ ๊ด๋ จ ์ฝ๋๋ฅผ ์์ฑํ์ง๋ง ์ง๊ธ ์ํฉ์์๋ Script ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ก ์์ฑ ํ ๋ถ๋ฌ์ค๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๋ค.
ํด๋น ์ปดํฌ๋ํธ๋ฅผ ์์ฑ ํ layout.tsx์ body ์ฌ์ด ์๋จ์ ๋ฃ์ด์ค์ผ๋ก์จ ์ฝ๊ฒ ์ ์ฉํ ์ ์์๋ค.
ํ์ง๋ง ๋ฌธ์ ์ ์ด ์์๋ค.
์ด์ ์๋ ์ด๋ฒคํธ ์ถ์ ์ ํ๋ ๊ด๋ จ ์ฝ๋๊ฐ ์กด์ฌํ๋ค.
useEffect(() => {
if (process.env.NODE_ENV === 'production') {
const handleRouteChange = (url: URL) => {
gtag.pageview(url);
};
router.events.on('routeChangeComplete', handleRouteChange);
router.events.on('hashChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
router.events.off('hashChangeComplete', handleRouteChange);
};
}
}, [router.events]);
์ด์ ์๋ ์์ ๊ฐ์ด userRouter๋ฅผ ์ด์ฉํด์ router.evnets๋ฅผ ํตํด ์ด๋ฒคํธ๋ฅผ ์ถ์ ํ ์ ์์๋ค.
ํ์ง๋ง Next 13์์๋ useRouter์ events๋ ์ฌ๋ผ์ก๋ค...?
https://github.com/vercel/next.js/discussions/41934
https://github.com/vercel/next.js/discussions/42016
์์ ๋งํฌ(issue)๋ค์์ ์์ฃผ ๋ง์ ์ฌ๋๋ค์ ๋๋ ค๋์ผ๋ผ๋ ํ์ฅ์ ๊ณก์๋ฆฌ(?)๋ฅผ ๋ค์ ์ ์๋ค... (๋ค๋ค ์์ฒญ ํ๊ฐ ๋์๋ ๋ฏํ ๋๋..)
์ ๋ง ๋ง์ ์ฌ๋๋ค์ด ์ํ๊ณ ์๋ ๊ธฐ๋ฅ์ธ ๊ฒ ๊ฐ๋ค.
์ด์ฌํ ์ฐพ์๋ณธ ๊ฒฐ๊ณผ router.events๋ฅผ ๋์ฒดํ๋ ๊ธฐ๋ฅ์ ์๋ค.
๋์ ์ ๋์์ ์์๋ค.
- page router ์ฌ์ฉ
- router.evnets์ ์ ์ฌํ๊ฒ ๋์ํ๋ ์ฝ๋ ์ง์ ๊ตฌํ ๋๋ ์ฌ๋ฌ ์ฌ๋์ด ์ ์ฌํ๊ฒ ๋ง๋ค์ด ๋์ ์ฝ๋ ๊ฐ์ ธ๋ค ์ฐ๊ธฐ
๊ณ ๋ฏผ์ ํ๋ค.
1๋ฒ์ ๋ํด์๋ app router๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ชฉ์ ์ด์๊ธฐ ๋๋ฌธ์ PASS..
2๋ฒ์ ๋ํด์๋ issue๋ฅผ ํ์ธํด ๋ณด๋ฉด ์ ์ฌํ๊ฒ ๋์ํ๋๋ก ๊ตฌํํด์ ๊ณต์ ํ ์ฝ๋๊ฐ ์๊ธด ํ๋ค.
ํ์ง๋ง ๋๋ฌด ๋ง์ ๋ถ๋ฅ(?)์ ์ฝ๋๊ฐ ์๊ณ ์ฝ๋ ์๋ ์ ์ง ์๋ค๊ณ ํ๋จ๋์๋ค.
๊ฒฐ๋ก ์ ๋ด ํ๋ก์ ํธ์ ๊ตฌ๊ธ ์ ๋๋ฆฌํฑ์ค๋ ์ด ํ์ด์ง ๋ทฐ์๋ง ํ์ธํด๋ ๊ด์ฐฎ๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ ๊ด๋ จ ์ฝ๋๋ ์ญ์ ํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๊ธฐ์กด useRouter๋ฅผ ์ฌ์ฉํ๋ ๊ณณ์์ NextRouter was not mounted ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ด ์๋ฌ๊ฐ ๋ฐ์ํ ์ด์ ๋ 'use client'๋ฅผ ๋ช ์ํ ๊ณณ. ์ฆ, ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ useRouter๋ฅผ ๋ถ๋ฌ์ฌ ๋๋ ์๋์ ๊ฐ์ด ๋ถ๋ฌ์์ผ ํ๋ค.
'use client';
// ๋ณ๊ฒฝ ์
import { useRouter } from 'next/router';
// ๋ณ๊ฒฝ ํ
import { useRouter } from 'next/navigation';
Link ๋ณ๊ฒฝ
Link ๊ด๋ จํด์๋ Link์ ์์์ผ๋ก ๊ผญ ๊ฐ์ธ์ค์ผ ํ๋ aํ๊ทธ๋ ์์ฑํ์ง ์๋๋ก ๋ณ๊ฒฝ๋์๋ค.
// ๋ณ๊ฒฝ ์
return (
<SButton fontColor="laime" borderColor="laime">
<Link href="/mbti">
<a>๋ชจ๋ ์ ํ ๋ณด๋ฌ๊ฐ๊ธฐ</a>
๋ชจ๋ ์ ํ ๋ณด๋ฌ๊ฐ๊ธฐ
</Link>
</SButton>
);
// ๋ณ๊ฒฝ ํ
return (
<SButton fontColor="laime" borderColor="laime">
<Link href="/mbti">
๋ชจ๋ ์ ํ ๋ณด๋ฌ๊ฐ๊ธฐ
</Link>
</SButton>
);
defaultProps will be removed ๊ฒฝ๊ณ ๋ฐ์
์ด ๋ฌธ์ ๋ React๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด์ ๋ฐ์ํ ๊ฒฝ๊ณ ์ด๋ค.
"Warning: App: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead."
์์ง defaultProps์ Deprecated๋ RFC ๋จ๊ณ์ด๊ธด ํ๋, ๋ฉ์์ง์ ๋ด์ฉ์ผ๋ก ๋ณด์ ๋ค์ ๋ฉ์ด์ ๋ฒ์ (React 19)๋ถํฐ ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
defaultProps๋ class ์ปดํฌ๋ํธ์์ ์ ์ฉํ์ง๋ง, ํจ์ํ ์ปดํฌ๋ํธ์์๋ ES6์ default parameters๋ก ๋์ฒด๊ฐ ๊ฐ๋ฅํ๊ฒ ๋์๋ค.
์ด ๊ฒฝ๊ณ ์ฐฝ์ ํด๊ฒฐํ๊ธฐ ์ํด Button ์ปดํฌ๋ํธ์ ๊ธฐ์กด์ defaultProps๋ก ์์ฑ๋์ด ์๋ ๋ถ๋ถ์ default parameters๋ก ๋ณ๊ฒฝํด ์ฃผ์๋ค.
next/font ์ ์ฉ
๊ธฐ์กด์ ํฐํธ๋ @fontsource/noto-sans-kr๋ก ํฐํธ๋ฅผ ์ ์ฉํด ์คฌ๋ค.
์ ๋ด์ฉ์ ํด๋น ๊ณต์๋ฌธ์์ ์ผ๋ถ์ด๋ค.
๊ฐ๋จํ๊ฒ ๋งํ๋ฉด next/font๋ฅผ ์ฌ์ฉํ๋ฉด ์ต์ ํ๋ ๊ฐ๋ฅํ๊ณ ๋ณ๋ค๋ฅธ ๋คํธ์ํฌ ์์ฒญ๋ ํ์ํ์ง ์์์ ๋น ๋ฅด๊ฒ ๋ก๋๋ฅผ ํ ์ ์๋ค๋ ๋ด์ฉ์ด๋ค.
๊ทธ๋์ ๊ธฐ์กด์ ์ค์นํ๋ "@fontsource/noto-sans-kr": "^4.5.12"๋ ์ด์ ํ์ํ์ง ์๊ธฐ ๋๋ฌธ ๋ฐ๋ก ์ ๊ฑฐํด ์ฃผ๊ณ ์งํํ๋ค.
๋ด ํ๋ก์ ํธ์์๋ GlobalStyles.ts์์ ํฐํธ๋ฅผ ์ ์ฉํ๊ธฐ ๋๋ฌธ์ ํด๋น ํ์ผ์์ ์งํํ๋ค.
์นด์นด์คํก ๊ณต์ ํ๊ธฐ ๊ด๋ จ ์ฝ๋ ๋ณ๊ฒฝ
์๊ทผํ ์ ๋ฅผ ๋จน์๋ ๋ถ๋ถ์ด๋ค.
๋จผ์ ๊ธฐ์กด์๋ _document.tsx์ kakao ๊ด๋ จ ์คํฌ๋ฆฝํธ๋ฅผ ์ถ๊ฐํด ์ฃผ๊ณ Layout.tsx์์ useEffect๋ฅผ ํตํด Kakao.init ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ์ ์ฉํด ์ฃผ์๋ค. (๋น์ ๊ด๋ จ ๋ธ๋ก๊ทธ ํฌ์คํ )
๊ทธ๋์ app router์ ๋ง๊ฒ ํด๋น ๋ถ๋ถ์ layout.tsx์ ๊ด๋ จ ์คํฌ๋ฆฝํธ๋ฅผ ์ถ๊ฐํ๊ณ ClientComponentContainer.tsx์ init ์ ์ฉํ๋ฉด ์ฝ๊ฒ ๋๋ ์ค ์์๋ค.
ํ์ง๋ง kakao.init์ด ์กด์ฌํ์ง ์๋๋ค๋ ์ค๋ฅ๋ง ๊ณ์ ๋ฐ์๋์๋ค.
kakao cannot read properties of undefined (reading 'init')
๋์ ๋งค์ฐ ๊ฐ์ ์ํฉ์ ๋ง์ฃผํ ๋ถ์ด ๊ณ์ จ๋ค. ์๋ฌ ๋ด์ฉ ๋ฐ ๊ณผ์ 100% ๋์ผ! (๋ธ๋ก๊ทธ ๋งํฌ)
์ํ๊น๊ฒ๋ ํด๋น ๋ด์ฉ์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐ๋์ง๋ ์๊ณ ๊ฐ์ ์๋ฌ๋ง ๋ง์ดํ ๋ฟ์ด์๋ค.
์ฐจ๊ทผ์ฐจ๊ทผ ์นด์นด์ค ๊ด๋ จ ์คํฌ๋ฆฝํธ๊ฐ ์ ๋ถ๋ฌ์์ก๋์ง ํ์ธํ๋ค.
๊ทธ๋์ useEffect ํ ๋(init ์ ์ฉํ ๋) window ๊ฐ์ฒด๋ฅผ ์ถ๋ ฅํด์ window์ Kakao ๊ฐ์ฒด๊ฐ ๋ด๊ฒผ๋์ง ํ์ธํด ๋ดค๋ค.
ํ์ง๋ง ๊ฒฐ๊ณผ๋ undefinend..
๊ด๋ จ ์ฝ๋๋ ์คํฌ๋ฆฝํธ ์ ์ฉ ์ฝ๋๋ ์๋์ ๊ฐ๋ค.
<script src="https://developers.kakao.com/sdk/js/kakao.min.js" ></script>
์คํฌ๋ฆฝํธ๊ฐ ๋ก๋๊ฐ ๋๊ธฐ ์ ์ init์ ์งํํ๋ ค ํด์ ๊ทธ๋ฐ ๊ฑด ๊ฐ ์ถ์๋ค. (์ฌ์ค ์ด๊ฒ ๋ง๋ค.)
๊ทธ๋์ defer ์ต์ ๋ ์ฃผ๊ณ async ์ต์ ๋ ์ฃผ๊ณ ๋ค ํด๋ดค๋๋ฐ ๊ฒฐ๊ณผ๋ ๋์ผํ๋ค.
๋์ค์ ์๊ณ ๋ณด๋๊น ์นด์นด์ค ๊ด๋ จ ์คํฌ๋ฆฝํธ๋ async๋ก ์์์ ์ ์ฉ๋๋ค๊ณ ํ๋ค.
๋๋์ฒด ์ ๋ ์ด์ ๊ฐ ์๋๋ฐ ์ ์๊พธ ๋ชป ์ฐพ๋ ๊ฒ์ธ์ง ์ง์ง ๋๋ฌด ๋ต๋ตํ๋ค.
์ด๋๊น์ง ์ด์ ์ด๋ ํฌ๊ฒ ๋ฌ๋ผ์ง ์ฝ๋ ์์ฒด๊ฐ ์์๊ธฐ ๋๋ฌธ์ด๋ค.
์งํธ๋ผ๊ธฐ๋ผ๋ ์ก๋ ์ฌ์ ์ผ๋ก ์ ๋ง ํน์๋ ํด์ ๋ธ๋ผ์ฐ์ ์ฝ์์ฐฝ์ window.Kakao๋ฅผ ์ฐ์ด๋ณด๋๊น ๊ฐ์ฒด๊ฐ ์ถ๋ ฅ์ด ๋๋ค.
์ด ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ๋ญ๊ฐ ๋ ์ฌ๋ํด์ก๋ค.
๋ด๊ฐ ์์ํ๋ ๋ฌธ์ ๊ฐ ๋ง๊ธด ํ๋๋ฐ ๋๋์ฒด ์ด๋ป๊ฒ ํด๊ฒฐํด์ผ ํ๋ ์ถ์๋ค.
useEffect์ ๋ ๋ฒ์งธ ๋ฐฐ์ด ์ธ์๋ก window๋ ๋ฃ์ด๋ณด๊ณ window.Kakao๋ ๋ฃ์ด๋ณด๊ณ ๋ชจ๋ ์๋ํด ๋ณด์์ง๋ง init์ด ์ ์ฉ์ด ์๋์๋ค.
๊ฒฐ๊ตญ GPT์๊ฒ ๋ฌผ์ด๋ดค๋ค.
์ด์ ๊น์ง GPT์๊ฒ ๋ฌผ์ด๋ณด์ง ์์ ์ด์ ๊ฐ Next 13 ์ดํ ๋ด์ฉ์ GPT๊ฐ ์์ง ๋ชปํด์ ์ฌ์ฉํ๊ณ ์ถ์ด๋ ์ฌ์ฉํ์ง ๋ชปํ์ง๋ง
์ด ๋ถ๋ถ์ Next 13๊ณผ ๋ณ๊ฐ์ธ ๋ถ๋ถ์ด์ด์ ๋ฌผ์ด๋ดค๋ค.
์์ธ์ ๋ด๊ฐ ์๊ฐํ๋ ์คํฌ๋ฆฝํธ๊ฐ ์ ์ฉ์ด ๋๊ธฐ ์ ์ init์ ํธ์ถํด์ ์ผ์ด๋ ๋ฌธ์ ๊ฐ ๋ง์๊ณ ,
setInterval์ ์ ์ฉํด ๋ณด๋ผ๊ณ ํ๋ค.
useEffect(() => {
const interval = setInterval(() => {
if (window.Kakao) {
window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_API_KEY);
clearInterval(interval);
}
}, 100);
}, []);
์ฒ์ GPT๊ฐ ์ ์ํ ์ฝ๋์๋ค.
์ด๋ ๊ฒ ํ๋๊น ์ง์ง ์ ๋๋ก ๋์ํ๊ธด ํ๋ค.
ํ์ง๋ง ์๋ฌด๋ฆฌ ์ฐพ์๋ด๋ ์ด๋ฐ ์์ผ๋ก ์ฝ๋๋ฅผ ์ ์ฉํ ์ํฉ์ ๋ณด์ง ๋ชปํ๊ณ ์ด๊ฒ setinterval์ ์ฌ์ฉํ ์ฝ๋๋ณด๋ค ๋ ๋์ ํด๊ฒฐ์ฑ ์ด ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค.
๋ค์ ๋ฌผ์ด๋ดค๋ค. ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์๋ ๊ฑฐ๋๊ณ . ์๋์ ๊ฐ์ ์ฝ๋๋ฅผ ์ ์ํ๋ค.
const loadKakaoSDK = () => {
return new Promise((resolve) => {
const script = document.createElement('script');
script.src = 'https://developers.kakao.com/sdk/js/kakao.js';
script.onload = () => resolve();
document.body.appendChild(script);
});
};
useEffect(() => {
const initKakao = async () => {
await loadKakaoSDK();
if (window.Kakao.isInitialized() === false) {
window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_API_KEY);
}
};
initKakao();
}, []);
์ ์ฝ๋๋ฅผ ํ์ฉํ์ง๋ ์์์ง๋ง ์ฌ๊ธฐ์ ๋๋ฌด ํฐ ํํธ๋ฅผ ์ป์ด๋ฒ๋ ธ๋ค.
๋ฐ๋ก onLoad ์์ฑ์ด ์์๋ ๊ฒ์ด์๋ค.
๊ผญ useEffect๋ฅผ ์ฌ์ฉํ์ง ์์๋ ์คํฌ๋ฆฝํธ๊ฐ ๋ถ๋ฌ์์ง๋ฉด ๋ฐ๋ก init์ ์ ์ฉ์์ผ ๋ฒ๋ฆฌ๋ฉด ๋๋ ๊ฒ์ด์๋ค....
'use client';
import Script from 'next/script';
function KakaoScript() {
const onLoad = () => {
window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_API_KEY);
};
return (
<Script
src="https://developers.kakao.com/sdk/js/kakao.js"
async
onLoad={onLoad}
/>
);
}
export default KakaoScript;
์ด๋ ๊ฒ ํ๊ณ ํด๋น ์ปดํฌ๋ํธ๋ฅผ layout.tsx์ body ํ๋จ์ ๋ฐฐ์นํ๋ค.
์์ฃผ ๊น๋ํ๊ฒ ์ ๋์ํ๋ค.
์ด๋ ๊ฒ ์ ์ฉํ ์ฌ๋ก๋ ๋ณด์ง ๋ชปํ๊ณ Next 13 ์ดํ ์๋ฃ๊ฐ ๋ง์ด ์๊ธฐ ๋๋ฌธ์ ๋๋ฌด ํ๋ค๊ฒ ํด๊ฒฐํ๋ค.
onLoad๋ก ๊ฐ๋ฅํ ๊ฑฐ๋ผ๊ณ ์๊ฐ์ง๋ ๋ชปํ๊ณ ์์๋ค.
๊ทธ๋์ ์ด์ ์ ๋ด๊ฐ Next.js ์นด์นด์คํก ๊ณต์ ํ๊ธฐ ๊ด๋ จ ํฌ์คํ ์ ๋ฐ๋ก ๋ณด์ํ ์์ ์ด๋ค.
next deploy
๋ง์ง๋ง์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ ์๋ฃํ๊ณ vercel์ ํตํด ๋ฐฐํฌ๋ฅผ ์งํํ๋ ๋์ค
The "next export" command has been removed in favor of "output: … …export" in next.config.js
์์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
๊ธฐ์กด package.json์ "deploy": "next build && next export"๊ฐ ์ถ๊ฐ๋์ด ์์๋๋ฐ Next 14 ๋ฒ์ ๋ถํฐ๋ next export๊ฐ ์ญ์ ๋์๋ค๊ณ ํ๋ค.
๋์ ์ next.config.js์ output: 'export'๋ฅผ ์ถ๊ฐํด ์ฃผ๋ฉด ๋๋ค๊ณ ํ๋ค.
์์ ๊ฐ์ด deploy ๋ช ๋ น์ด๋ ์ด์ ํ์๊ฐ ์์ด์ก๊ธฐ ๋๋ฌธ์ ๊ณผ๊ฐํ ์ญ์ ํด์ฃผ๊ณ , next.config.js์ output:'export๋ฅผ ์ถ๊ฐํด์คฌ๋ค.
vercel์์ ๋ฐฐํฌ ๋ช ๋ น์ด๋ฅผ yarn build๋ก ์์ ํด ์ฃผ์๋ค.
๐๋ง๋ฌด๋ฆฌ
์ฌ์ค ๋ง์ด๊ทธ๋ ์ด์ ์ ์งํํ๊ธฐ ์ ์๋ ํ๋ก์ ํธ๊ฐ ๋งค์ฐ ์์ ํธ์ด๋ผ๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ ๋ง์ ๋ฌธ์ ๋ฅผ ๋ง์ดํ ๊ฑฐ๋ผ๊ณ ์๊ฐ์ ํ์ง ๋ชปํ๋ค.
๋ง์ด๊ทธ๋ ์ด์ ์ ํ๋ฉด์ ๋ํ์ ์ผ๋ก ๋๊ผ๋ ๋ถํธํ ์ ์ ์๋์ ๊ฐ๋ค.
- GPT ์ฌ์ฉ ๋ชป ํจ(GPT๋ 21๋ 9์๊น์ง์ ์ ๋ณด๋ง ๊ฐ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ต๊ทผ ์ถ์ ๋ Next 13 ๋ฒ์ ์ ์์ง ๋ชปํ๋ค. ๋ฌผ์ด๋ณด๋ฉด ์ด์ ๋ฒ์ ์ ๊ด๋ จํด์ ๋ต๋ณ์ด ๋์์จ๋ค.)
- ์ฐธ๊ณ ์๋ฃ๊ฐ ๋๋ฌด ์๋ค.
- ๊ธฐ์กด page router์์๋ ์ง์๋๊ณ app router์์๋ ์ง์์ด ์ ๋๋ ๊ฒ๋ค์ด ๋ง๋ค.
๊ฐ์ ํด์ผ ํ ์ ์ ์๋์ ๊ฐ๋ค.
- styled-components ๊ฑท์ด๋ด๊ธฐ
- ์๋ฒ์ปดํฌ๋ํธ๋ฅผ ์ง์ํ์ง ์์์ ์ฌ์ฉํ๋ ๋ชจ๋ ๋ถ๋ถ์ 'use client'๋ฅผ ๋ช ์ํด์ผํจ์ผ๋ก์จ, ์๋ฒ์ปดํฌ๋ํธ์ ์ด์ ์ ํ์ฉํ์ง ๋ชปํ๋ค.
- ๊ณต์๋ฌธ์์์ ์์ฒด์ ์ผ๋ก ์ถ์ฒํ๋ tailwind-css๋ฅผ ๊ณ ๋ คํ๋ ์ค์์๋ค.
์ฌ์ค app router์ ๊ตฌ์กฐ๋ ์ฒ์์ด๊ณ ์์ง ์ด๊ธฐ ๋จ๊ณ์ฌ์ ์ ์๋์ง ์์ ๋ถํธํ ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค.
์๊ฐ์ด ์ง๋ ์๋ก ๋ณด์ด ๋๋ฉด์ ์ฐธ๊ณ ์๋ฃ๋ ๋ง์์ง๋ฉด์ ํ์ฌ page router๋ฅผ ์ฌ๋๋ค์ด ํธํ๊ฒ ์ฐ๋ ๊ฒ์ฒ๋ผ app router๋ ๊ทธ๋ ๊ฒ ๋์ง ์์๊น ์๊ฐํ๋ค.
๊ตฌ๊ธ๋ง์ ํ๋ฉด์๋ ๋๋ถ๋ถ "Next.js 13 ~~" ์ด๋ฐ ์์ผ๋ก ๊ฒ์์ ํ๋ค.
์๋ํ๋ฉด Next์์ ํฐ ๋ณํ๋ฅผ ์ผ์ผํจ ๋ถ๋ถ์ Next 13 ๋ฒ์ ์ด ์ถ์๋์์ ๋ ๋ณํ๊ฐ ๊ฐ์ฅ ์ปธ๊ธฐ ๋๋ฌธ์ ๋น์ค์ Next 13์ด ๊ฐ์ฅ ํฌ๊ณ , 14 ๋ฒ์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ์ ์งํํ์ง๋ง 13 ๋ฒ์ ๊ด๋ จํด์ ๊ตฌ๊ธ๋ง์ ์งํํ๋ ๊ฒ์ด ๋ง๋ค๊ณ ์๊ฐํ๋ค.
๊ทธ๋ฆฌ๊ณ app router๋ก ๋ณ๊ฒฝํ๋ฉด์ ์ฑ๋ฅ ์์ ์ด์ ์ ๋ณด์ด์ง ์๋ ๊ฒ ๊ฐ๋ค.
์ ์ผ ํฐ ์ด์ ๋ ํ๋ก์ ํธ ๊ท๋ชจ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋์ ๋๋ ๋ณํ๋ ์๋ ๊ฒ ๊ฐ๋ค.
ํ์ง๋ง ์ด๋ ๊ฒ ๋ง์ด๊ทธ๋ ์ด์ ์ ํ๋ฉด์ ๊ฒช์ ๊ฒ๋ค์ ๊ฐ์ง๋ค๊ณ ์๊ฐํ๋ค.
์ด๋ป๊ฒ ๋ณด๋ฉด ๋์ Nextjs ๊ด๋ จํ ์ง์์ 11 ๋ฒ์ ์์ ๋ฉ์ถฐ์๋ค๊ณ ํด๋ ๋ฌด๋ฐฉํ์๊ณ , 11 ๋ฒ์ ์ดํ์ ๋ง์ ๋ณํ๊ฐ ์์๊ธฐ ๋๋ฌธ์ ์ด๋ค ๋ถ๋ถ๋ค์ด ์ด๋ป๊ฒ ๋ณ๊ฒฝ๋์๋์ง ์ง์ ๋๋ผ๊ณ ์ด๋ฐ ๊ณผ์ ์์ ๊ฒช์๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐํ ๋ฌธ์ ๋ค์ด ๋ด ์์คํ ์์ฐ์ด ๋์๋ค๊ณ ์๊ฐํ๋ค.