๐ Next.js App Router ์ ๊ทธ๋ ์ด๋ ๊ฐ์ด๋
๐ Next.js App Router ์ ๊ทธ๋ ์ด๋ ๊ฐ์ด๋
Next.js App Router๋ Pages Router ๋๋น ํจ์ฌ ๋ ์ ์ฐํ๊ณ ํ์ฅ์ฑ ๋์ ๊ตฌ์กฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๊ธฐ์กด ํ๋ก์ ํธ๋ฅผ ์ต์ ๋ฒ์ (App Router ๊ธฐ๋ฐ)์ผ๋ก ์
๊ทธ๋ ์ด๋ํ๋ ๋ฐฉ๋ฒ์ ๋จ๊ณ๋ณ๋ก ์ ๋ฆฌํฉ๋๋ค.
โ 1. Next.js ๋ฐ Node.js ๋ฒ์ ์ ๋ฐ์ดํธ
- Next.js: ์ต์ ๋ฒ์ ์ค์น (13.4 ์ด์)
- Node.js: ์ต์ 18.17 ์ด์ ํ์
npm install next@latest react@latest react-dom@latest
์ถ๊ฐ๋ก ESLint ์ค์ ๋ ์ ๋ฐ์ดํธํ๋ฉด ์ข์ต๋๋ค.
npm install -D eslint-config-next@latest
โ 2. ์๋ก์ด ๊ธฐ๋ฅ ๋์
๐ท Image ์ปดํฌ๋ํธ ๊ฐ์
๋ ์ด์ next/future/image๊ฐ ํ์ ์์ต๋๋ค. ๊ธฐ๋ณธ next/image๋ฅผ ์ฌ์ฉํ์ธ์.
import Image from 'next/image';
export default function Example() {
return <Image src="/logo.png" width={500} height={300} alt="๋ก๊ณ " />;
}
๐ Link ์ปดํฌ๋ํธ ๊ฐ์ํ
<a> ํ๊ทธ ์์ด ๋ฐ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
import Link from 'next/link';
<Link href="/about">About</Link>
๐ง ํฐํธ ์ต์ ํ (next/font)
Google Fonts ๋๋ ๋ก์ปฌ ํฐํธ๋ฅผ ๊ฐํธํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function Page() {
return <div className={inter.className}>ํฐํธ ์ ์ฉ ์๋ฃ</div>;
}
โ 3. App Router๋ก ๋ง์ด๊ทธ๋ ์ด์
๐ app ๋๋ ํ ๋ฆฌ ์์ฑ
๋ฃจํธ์ app/ ํด๋๋ฅผ ๋ง๋ค๊ณ , ๋ชจ๋ ํ์ด์ง์ ๋ ์ด์์์ ์ด๊ณณ์ ๊ตฌ์ฑํฉ๋๋ค.
app/
layout.tsx
page.tsx
about/
page.tsx
๐๏ธ ๋ ์ด์์ ์ค์
layout.tsx๋ ๋ชจ๋ ํ์ ํ์ด์ง์ ๊ณตํต ์ ์ฉ๋ฉ๋๋ค.
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>{children}</body>
</html>
);
}
โ 4. ๋ฐ์ดํฐ ํจ์นญ ๊ตฌ์กฐ ๋ณ๊ฒฝ
๊ธฐ์กด ๋ฐฉ์ (getServerSideProps, getStaticProps) ์ ๊ฑฐ
App Router์์๋ ์๋ฒ ์ปดํฌ๋ํธ ์์์ ๋ฐ๋ก fetch๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
export default async function PostPage({ params }: { params: { id: string } }) {
const res = await fetch(`https://api.example.com/posts/${params.id}`);
const post = await res.json();
return
;
}
generateStaticParams ์ฌ์ฉ (SSG)
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(res => res.json());
return posts.map((post: { id: string }) => ({ id: post.id }));
}
๊ฒฝ๋ก๋ณ ์ ์ ์์ฑ์ ์ํด ์ฌ์ฉํฉ๋๋ค.
โ 5. ๋ผ์ฐํ ํ ๋ณ๊ฒฝ
Pages Router์์ ์ฌ์ฉํ๋ useRouter ๋์ App Router ์ ์ฉ ํ ์ ์ฌ์ฉํฉ๋๋ค.
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
โ 6. ์๋ฌ ๋ฐ Not Found ์ฒ๋ฆฌ
์๋ฌ ํ์ด์ง
// app/error.tsx
'use client';
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
return (
<div>
<h1>๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.</h1>
<button onClick={() => reset()}>๋ค์ ์๋</button>
</div>
);
}
404 ํ์ด์ง
// app/not-found.tsx
export default function NotFound() {
return <h1>ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค</h1>;
}
โ 7. ์คํ์ผ๋ง
์ ์ญ ์คํ์ผ์ layout.tsx์ import ํฉ๋๋ค.
import './globals.css';
Tailwind CSS ๊ฐ์ ๊ฒ๋ ๊ทธ๋๋ก ์ ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
๐ง ํต์ฌ ์ฒดํฌ๋ฆฌ์คํธ
ํญ๋ชฉ ๋ด์ฉ
Node.js | 18.17 ์ด์ |
Next.js | 13.4 ์ด์ |
app/ ๋๋ ํ ๋ฆฌ ์ฌ์ฉ | page.tsx, layout.tsx ์ค์ฌ |
๋ฐ์ดํฐ ํจ์นญ | ์๋ฒ ์ปดํฌ๋ํธ + fetch ๊ธฐ๋ฐ |
์๋ฌ ์ฒ๋ฆฌ | error.tsx, not-found.tsx ์ถ๊ฐ |
SEO/OG ์ค์ | metadata ๊ฐ์ฒด ํ์ฉ ๊ฐ๋ฅ |
https://nextjs.org/docs/app/getting-started/upgrading
Next.js, App Router, ์ ๊ทธ๋ ์ด๋, ๋ง์ด๊ทธ๋ ์ด์ , ๋ฐ์ดํฐ ํจ์นญ, ์๋ฒ ์ปดํฌ๋ํธ, ์๋ฌ ์ฒ๋ฆฌ, ๋ผ์ฐํ ๋ณ๊ฒฝ, SEO ์ต์ ํ, App Directory