masonfe-hyunsu
๐ฑ 1. ๊ฐ์ : ์๋ก์ด ๋ผ์ฐํฐ๋ณด๋ค ์๋ก์ด ๋ ๋๋ง ๋ชจ๋ธ.

App Router๋ Next.js 13์์ ๋์ ๋๊ณ 13.4์์ ์์ ํ๋์๋ค.
์ฒ์์๋
pages/๋์app/์ ์ฐ๋ ์๋ก์ด ํ์ผ ๋ผ์ฐํ ์ ๋๋ก ์๊ฐํ๋ค.์ง์ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ผ๊ฐ ๋ณด๋ ํต์ฌ์ ํด๋ ์ด๋ฆ๋ณด๋ค React Server Components(RSC), ์ค์ฒฉ ๋ ์ด์์, ์คํธ๋ฆฌ๋ฐ, ๋ฐ์ดํฐ ์บ์๊ฐ ํจ๊ป ์์ง์ด๋ ๋ ๋๋ง ๋ชจ๋ธ์ ์์๋ค.
์ด ํฌ์คํ ์ ํ๋ก์ ํธ์์ ์ฌ์ฉ ์ค์ธ Next.js 15 ๊ธฐ์ค์ผ๋ก App Router์ ๊ตฌ์กฐ์ ์์ฃผ ํ๋ฆฌ๋ ์ง์ ์ ์ ๋ฆฌํด ๋ณธ๋ค.
App Router๋ URL์ ๋ง๋๋ ๊ท์น๋ง ๋ฐ๊พผ ๊ฒ์ด ์๋๋ผ, ์๋ฒ์ ํด๋ผ์ด์ธํธ๊ฐ UI๋ฅผ ๋๋๋ ๊ธฐ์ค๊น์ง ๋ฐ๊ฟจ๋ค.
๐ 2. Pages Router์ ๋ฌด์์ด ๋ค๋ฅธ๊ฐ.
๋๋ ํฐ๋ฆฌ์ ๋ผ์ฐํธ.
Pages Router
pages/about.tsx์ฒ๋ผ ํ์ผ ํ๋๊ฐ/about๋ผ์ฐํธ๊ฐ ๋๋ค._app.tsx,_document.tsx์์ ์ฑ ์ ์ฒด ๊ณตํต ๊ตฌ์กฐ๋ฅผ ๊ด๋ฆฌํ๋ค.getStaticProps,getServerSideProps๊ฐ์ ํ์ด์ง ๋จ์ ๋ฐ์ดํฐ ํจ์๋ฅผ ์ฌ์ฉํ๋ค.
App Router
app/about/page.tsx์ฒ๋ผ ํด๋๊ฐ URL segment๋ฅผ ๋ง๋ค๊ณpage.tsx๊ฐ ๊ณต๊ฐ ํ์ด์ง๋ฅผ ๋ง๋ ๋ค.ํด๋๋ง๋ค
layout.tsx,loading.tsx,error.tsx๊ฐ์ ํ์ผ์ ํจ๊ป ๋ ์ ์๋ค.ํ์ด์ง์ ๋ ์ด์์์ ๊ธฐ๋ณธ์ ์ผ๋ก Server Component๋ค.

๋ ๋ผ์ฐํฐ ๋ชจ๋ ๊ฒฝ๋ก ๋จ์ ์ฝ๋ ๋ถํ ์ ์ง์ํ๋ค. App Router์์ ์๋กญ๊ฒ ์ฒด๊ฐ๋๋ ๋ถ๋ถ์ segment๋ณ ๋ ์ด์์๊ณผ ๋ก๋ฉ, ์๋ฌ ๊ฒฝ๊ณ, ๊ทธ๋ฆฌ๊ณ ์๋ฒ์ ํด๋ผ์ด์ธํธ ๋ชจ๋ ๊ทธ๋ํ์ ๋ถ๋ฆฌ์๋ค.
์ค์ฒฉ ๋ ์ด์์.
app/
โโโ layout.tsx
โโโ page.tsx
โโโ dashboard/
โโโ layout.tsx
โโโ loading.tsx
โโโ page.tsxapp/layout.tsx๋ ํ์ Root Layout์ด๋ค.<html>๊ณผ<body>๋ฅผ ์ง์ ํฌํจํด์ผ ํ๋ค.app/dashboard/layout.tsx๋/dashboard์๋์์๋ง ์ ์ง๋๋ ๊ณตํต UI๋ค.๊ฐ์ layout ์์์ ์ด๋ํ๋ฉด layout์ ์ํ์ DOM์ ์ ์ง๋๊ณ page ์์ญ์ด ๋ฐ๋๋ค.
layout์ ๋ค์ ๋ง์ดํธํด์ผ ํ๋ ํ๋ฉด์๋
template.tsx๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๐งญ 3. ํน์ ํ์ผ ๊ท์น.
App Router๋ ํ์ผ ์ด๋ฆ์ผ๋ก segment์ ์ญํ ๊ณผ ๊ฒฝ๊ณ๋ฅผ ์ ์ธํ๋ค.
page.tsxํด๋น ๊ฒฝ๋ก์์ ๊ณต๊ฐ๋๋ UI๋ค.
page๊ฐ ์๋ ํด๋๋ URL segment๊ฐ ๋ ์ ์์ด๋ ์ง์ ์ ๊ทผํ ํ์ด์ง๋ ๋ง๋ค์ง ์๋๋ค.
layout.tsx์ฌ๋ฌ ํ์ route๊ฐ ๊ณต์ ํ๋ UI๋ค.
navigation ์ ์ ์ง๋๋ฏ๋ก ๋งค๋ฒ ์๋ก ์คํ๋ผ์ผ ํ๋ ๋ก์ง์ ๋ฌด์ฌ์ฝ ๋์ง ์๋๋ค.
loading.tsxsegment์ ์ฆ์ ๋ก๋ฉ UI๋ค.
๋ด๋ถ์ ์ผ๋ก Suspense ๊ฒฝ๊ณ๋ฅผ ๋ง๋ค์ด ์ค๋น๋์ง ์์ ๋ด์ฉ์ ์คํธ๋ฆฌ๋ฐํ๋ค.
error.tsxsegment์ Error Boundary ์ญํ ์ ํ๋ค.
reset()๊ณผ ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ฏ๋ก Client Component์ฌ์ผ ํ๋ค.
not-found.tsxnotFound()ํธ์ถ์ด๋ ์ฐพ์ ์ ์๋ ๊ฒฝ๋ก์ UI๋ฅผ ์ฒ๋ฆฌํ๋ค.
route.tsGET,POST๊ฐ์ Web Request/Response ๊ธฐ๋ฐ Route Handler๋ฅผ ๋ง๋ ๋ค.
template.tsxlayout๊ณผ ๋น์ทํ์ง๋ง navigation ๋ ์์ ์ธ์คํด์ค๋ฅผ ์๋ก ๋ง๋ค๊ณ effect๋ ๋ค์ ์คํํ๋ค.
// app/dashboard/error.tsx
'use client';
export default function Error({
reset,
}: {
reset: () => void;
}) {
return <button onClick={reset}>๋ค์ ์๋</button>;
}Route Group.
app/(marketing)/about/page.tsx์ ๊ดํธ ํด๋๋ URL์ ํฌํจ๋์ง ์๋๋ค.URL์ ๋ฐ๊พธ์ง ์๊ณ route๋ฅผ ์์ญ๋ณ๋ก ๋ฌถ๊ฑฐ๋ ์๋ก ๋ค๋ฅธ layout์ ์ ์ฉํ ๋ ์ฌ์ฉํ๋ค.
์ฌ๋ฌ Root Layout ์ฌ์ด๋ฅผ ์ด๋ํ๋ฉด client-side navigation์ด ์๋๋ผ full page load๊ฐ ์ผ์ด๋ ์ ์๋ค.
๐งฉ 4. Server Component์ Client Component์ ๊ฒฝ๊ณ.
App Router์ page์ layout์ ๊ธฐ๋ณธ์ ์ผ๋ก Server Component๋ค.
์๋ฒ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ API์ ์ ๊ทผํ๊ณ , secret์ ๋ ธ์ถํ์ง ์์ผ๋ฉด์ ๊ฒฐ๊ณผ๋ฅผ UI์ ๊ฐ๊น์ด ๋ ์ ์๋ค.
์ํ, ์ด๋ฒคํธ ํธ๋ค๋ฌ, effect, ๋ธ๋ผ์ฐ์ API๊ฐ ํ์ํ ๋ถ๋ถ๋ง Client Component๋ก ๋ง๋ ๋ค.
// app/posts/[slug]/page.tsx โ Server Component
import LikeButton from './LikeButton';
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return <LikeButton initialLikes={post.likes} />;
}// app/posts/[slug]/LikeButton.tsx โ Client Component
'use client';
import { useState } from 'react';
export default function LikeButton({
initialLikes,
}: {
initialLikes: number;
}) {
const [likes, setLikes] = useState(initialLikes);
return <button onClick={() => setLikes(likes + 1)}>{likes}</button>;
}
์ฌ๊ธฐ์ "use client"๋ ํ์ฌ ํ์ผ๋ง ํ์ํ๋ ์ฃผ์์ด ์๋๋ผ ์๋ฒ์ ํด๋ผ์ด์ธํธ ๋ชจ๋ ๊ทธ๋ํ์ ๊ฒฝ๊ณ๋ค.
"use client"ํ์ผ์ด importํ ๋ชจ๋์ client bundle์ ํฌํจ๋ ์ ์๋ค.Server Component์์ Client Component๋ก ๋๊ธฐ๋ props๋ React๊ฐ ์ง๋ ฌํํ ์ ์์ด์ผ ํ๋ค.
ํฐ layout ์ ์ฒด์
"use client"๋ฅผ ๋ถ์ด๊ธฐ๋ณด๋ค ๋ฒํผ์ด๋ ์ ๋ ฅ์ฒ๋ผ ์ํธ์์ฉ์ด ํ์ํ ์์ ์ฌ์ผ๋ก ๋ด๋ฆฐ๋ค.์๋ฒ ์ ์ฉ ๋ชจ๋์๋
import 'server-only'๋ฅผ ๋ฃ์ด Client Component์์ ์๋ชป ๊ฐ์ ธ์ค๋ ์ผ์ ๋น๋ ๋จ๊ณ์์ ๋ง์ ์ ์๋ค.
์ด๊ธฐ ์์ฒญ์์๋ HTML๋ก ๋น ๋ฅธ ํ๋ฉด์ ๋ณด์ฌ ์ฃผ๊ณ , RSC Payload๋ก ์๋ฒ์ ํด๋ผ์ด์ธํธ ํธ๋ฆฌ๋ฅผ ๋ง์ถ ๋ค Client Component์๋ง JavaScript๋ฅผ ์ฐ๊ฒฐํด hydrationํ๋ค.
๐๏ธ 5. ๋์ ๋ผ์ฐํธ์ ๋น๋๊ธฐ params.
app/posts/[slug]/page.tsx๋ /posts/hello, /posts/nextjs ๊ฐ์ ์ฃผ์๋ฅผ ํ๋์ page๋ก ์ฒ๋ฆฌํ๋ค.
Next.js 15๋ถํฐ params๋ Promise๋ค. ์ด์ ๋ฒ์ ์ params.slug ์์ ๋ฅผ ๊ทธ๋๋ก ์ฐ๊ธฐ๋ณด๋ค await params๋ก ๊ฐ์ ๊บผ๋ด๋ ํธ์ด ์์ ํ๋ค.
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return <article>{slug}</article>;
}[slug]: ํ segment๋ฅผ ๋ฐ๋๋ค.[...slug]: ๋ค์ ์ด์ด์ง๋ segment๋ฅผ ๋ฐฐ์ด๋ก ๋ชจ๋ ๋ฐ๋๋ค.[[...slug]]: ๊ฐ์ด ์๋ ๊ธฐ๋ณธ ๊ฒฝ๋ก๊น์ง ํฌํจํ๋ค.๋ฏธ๋ฆฌ ์ ์ ์๋ ๊ฒฝ๋ก๋
generateStaticParams()๋ก ๋น๋ ์ ์์ฑํ ์ ์๋ค.Client Component์์๋
use(params)๋๋useParams()๋ก ํ์ฌ ๊ฐ์ ์ฝ์ ์ ์๋ค.
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({ slug: post.slug }));
}route param์ ์ฌ์ฉ์๊ฐ ์ฃผ์์ฐฝ์ ์ง์ ์
๋ ฅํ ์ ์๋ ๋ฐํ์ ๊ฐ์ด๋ค. union type๋ง ๋ฏฟ์ง ์๊ณ ์ ํจํ์ง ์์ ๊ฐ์ ๊ฒ์ฌํ ๋ค notFound()๋ก ์ฒ๋ฆฌํด์ผ ํ๋ค.
๐ฆ 6. ๋ฐ์ดํฐ ๋ก๋ฉ, ์บ์, ์คํธ๋ฆฌ๋ฐ.
Server Component๋ ์ปดํฌ๋ํธ ์์์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๊ธฐ๋ค๋ฆด ์ ์๋ค.
export default async function Page() {
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
return <PostList posts={posts} />;
}Next.js์ ์บ์ ๊ธฐ๋ณธ๊ฐ์ ๋ฒ์ ์ ๋ฐ๋ผ ๋ฌ๋ผ์ก๋ค. Next.js 15์ fetch๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์๊ตฌ ์บ์๋์ง ์๋๋ค.
๋งค ์์ฒญ๋ง๋ค ์ต์ ๋ฐ์ดํฐ:
cache: 'no-store'๋ช ์์ ์ธ ์๊ตฌ ์บ์:
cache: 'force-cache'์๊ฐ ๊ธฐ๋ฐ ๊ฐฑ์ :
next: { revalidate: 60 }ํ๊ทธ ๊ธฐ๋ฐ ๋ฌดํจํ:
next: { tags: ['posts'] }์revalidateTag()
const response = await fetch('https://api.example.com/posts', {
next: { revalidate: 60, tags: ['posts'] },
});๋์ผํ GET ์์ฒญ์ด ํ ๋ฒ์ React ๋ ๋ ํธ๋ฆฌ์์ ์ค๋ณต๋๋ฉด memoization๋ ์ ์์ง๋ง, ์ด๊ฒ์ ์์ฒญ์ ๋์ด ์ ์ง๋๋ Data Cache์ ๊ฐ์ ๊ฐ๋
์ด ์๋๋ค.
๋ก๋ฉ UI์ ์คํธ๋ฆฌ๋ฐ.
route ์ ์ฒด์ ๊ธฐ๋ณธ fallback์
loading.tsx์ ๋๋ค.์ผ๋ถ ๋๋ฆฐ ์์ญ๋ง ๋๋๊ณ ์ถ๋ค๋ฉด
<Suspense>๋ฅผ ์ง์ ๋๋ค.์ด ๋ฐฉ์์ ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ๋๋ ๋๊น์ง ๋น ํ๋ฉด์ผ๋ก ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ , ์ค๋น๋ UI๋ถํฐ ๋ณด๋ธ๋ค.
import { Suspense } from 'react';
export default function Page() {
return (
<Suspense fallback={<PostListSkeleton />}>
<PostList />
</Suspense>
);
}cookies(), headers()์ฒ๋ผ ์์ฒญ ์์ ์ ๊ฒฐ์ ๋๋ API๋ uncached data๋ฅผ ์ฌ์ฉํ๋ฉด route๊ฐ ๋์ ์ผ๋ก ๋ ๋๋ง๋ ์ ์๋ค. ์ ์ ๋๋ ๋์ ์ด๋ผ๋ ์ด๋ฆ๋ณด๋ค ์ด๋ค ๋ฐ์ดํฐ๊ฐ ์ธ์ ํ์ ๋๋์ง๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ณด๋ ํธ์ด ์ดํดํ๊ธฐ ์ฌ์ ๋ค.
๐ท๏ธ 7. Metadata์ Route Handler.
Metadata API.
๊ณ ์ ๊ฐ์ metadata ๊ฐ์ฒด๋ก ์ ์ธํ๋ค.
๋์ ์์ธ ํ์ด์ง๋ generateMetadata()์์
params์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ค.title,description, Open Graph ๊ฐ์ head ์ ๋ณด๋ฅผ React ํธ๋ฆฌ์ ํจ๊ป ๊ด๋ฆฌํ ์ ์๋ค.
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPost(slug);
return {
title: post.title,
description: post.excerpt,
};
}Route Handler.
route.ts๋ ๋ธ๋ผ์ฐ์ ํ์ด์ง๊ฐ ์๋๋ผ HTTP endpoint๋ฅผ ๋ง๋ ๋ค.
// app/api/posts/route.ts
export async function GET() {
const posts = await getPosts();
return Response.json(posts);
}webhook, ์ธ๋ถ ํด๋ผ์ด์ธํธ์ฉ API, ํ์ผ ์๋ต์ฒ๋ผ HTTP ๊ฒฝ๊ณ๊ฐ ํ์ํ ๊ณณ์ ์ฌ์ฉํ๋ค.
Server Component๊ฐ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ ์ํด ๋ฌด์กฐ๊ฑด ๋ด๋ถ Route Handler๋ฅผ ๊ฑฐ์น ํ์๋ ์๋ค.
์๋ฒ์์๋ง ์ฐ๋ ๋ฐ์ดํฐ ํจ์๋ DB ํธ์ถ์ ์ง์ importํ๋ ํธ์ด ๋ถํ์ํ HTTP ์๋ณต์ ์ค์ธ๋ค.
๐คจ 8. Pages Router์์ ๋จ๊ณ์ ์ผ๋ก ์ด์ ํ๊ธฐ.
app/๊ณผ pages/ ๋ณํ์ ๊ฐ๋ฅํ๋ค. ๋ค๋ง ๋์ผํ URL์ ์์ชฝ์์ ๋์์ ๋ง๋ค ์๋ ์๋ค.
์ ๊ธฐ๋ฅ์ด๋ ๋ ๋ฆฝ๋ ๊ฒฝ๋ก๋ถํฐ
app/์ ๋ง๋ ๋ค._app.tsx์ Provider๋ ํ์ํ ๋ฒ์์ Client Component Provider๋ก ์ฎ๊ธด๋ค.getServerSideProps๋ async Server Component์ ๋์ ๋ฐ์ดํฐ ์ ๊ทผ์ผ๋ก ๋ฐ๊พผ๋ค.getStaticProps์getStaticPaths๋ ์บ์ ์ค์ ๊ณผgenerateStaticParams๋ก ์ญํ ์ ๋๋ ์ฎ๊ธด๋ค.next/router๋next/navigation์useRouter,usePathname,useSearchParams๋ก ๊ต์ฒดํ๋ค.API Routes๋ ์ ์งํ๊ฑฐ๋ ํ์ํ ๊ฒฝ๋ก๋ง Route Handler๋ก ์ด์ ํ๋ค.
๋ง์ด๊ทธ๋ ์ด์ ์ค์๋ ๋ค์์ ํนํ ํ์ธํด์ผ ํ๋ค.
"use client"๋ฒ์๋ฅผ ๋๋ฌด ์๋ก ๋์ด์ฌ๋ฆฌ์ง ์์๋๊ฐServer Component๊ฐ ๋ธ๋ผ์ฐ์ API๋ฅผ ํธ์ถํ์ง ์๋๊ฐ
Client Component props๊ฐ ์ง๋ ฌํ ๊ฐ๋ฅํ๊ฐ
params,cookies(),headers()๊ฐ์ ๋น๋๊ธฐ API๋ฅผawaitํ๋๊ฐ๊ธฐ์กด ์บ์ ๋์์ Next.js 15 ๊ธฐ๋ณธ๊ฐ์ ๋ง๊ฒ ๋ช ์ํ๋๊ฐ
๐ 9. ์ ๋ฆฌ.
App Router์ ํต์ฌ์ segment ๊ธฐ๋ฐ UI ๊ฒฝ๊ณ์ Server Component ๊ธฐ๋ณธ ๋ชจ๋ธ์ด๋ค.
page,layout,loading,error,route๊ฐ์ ํน์ ํ์ผ์ด ๋ผ์ฐํ ๊ณผ ๋ ๋๋ง ์ญํ ์ ๋๋๋ค."use client"๋ ์ํธ์์ฉ์ด ํ์ํ ์์ ๊ฒฝ๊ณ์ ๋๊ณ client bundle์ ๋ถํ์ํ๊ฒ ํค์ฐ์ง ์๋๋ค.Next.js 15์์๋
params๊ฐ Promise์ด๋ฉฐfetch๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์๊ตฌ ์บ์๋์ง ์๋๋ค.์บ์, ์คํธ๋ฆฌ๋ฐ, ๋์ ๋ ๋๋ง์ ๋ฐ์ดํฐ๊ฐ ์ธ์ ํ์ ๋์ด์ผ ํ๋์ง์ ๋ง์ถฐ ์ ํํ๋ค.
์ฒ์์๋ App Router๋ฅผ ํด๋ ๊ท์น์ ๋ณํ๋ก ๋ดค๋ค. ์ ๋ฆฌํ๊ณ ๋๋ ๋ ์ค์ํ ๊ฒ์ ์๋ฒ์์ ๋๋ผ ์ผ๊ณผ ๋ธ๋ผ์ฐ์ ๋ก ๋๊ธธ ์ผ์ ๊ฒฝ๊ณ ์ง๋ ๋ฐฉ์์ด์๋ค. ํ์ผ ๊ท์น์ ์ธ์ฐ๋ ๊ฒ๋ณด๋ค ์ด ๊ฒฝ๊ณ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ณด๋ฉด ๋ฐ์ดํฐ ๋ก๋ฉ๊ณผ UI ๊ตฌ์กฐ๋ ํจ๊ป ์ดํดํ๊ธฐ ์ฌ์ ๋ค.
๋๊ธ