framework/NextJS

๐Ÿ”„ Next.js App Router ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€์ด๋“œ

octo54 2025. 4. 28. 10:28
๋ฐ˜์‘ํ˜•

๐Ÿ”„ 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
{post.title}
;
}

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