mason
mason-log.

GitHub ๋กœ๊ทธ์ธ ์ค‘

Next.js App Router.
mason

masonfe-hyunsu

๐Ÿฑ 1. ๊ฐœ์š” : ์ƒˆ๋กœ์šด ๋ผ์šฐํ„ฐ๋ณด๋‹ค ์ƒˆ๋กœ์šด ๋ Œ๋”๋ง ๋ชจ๋ธ.

๊ธฐ์กด ๊ตฌ์กฐ๋ฅผ ์ •๋ฆฌํ•˜๊ณ  App Router๋ฅผ ์‚ดํŽด๋ณด๋Š” ๊ณผ์ •
  • 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๋‹ค.

Pages Router์™€ App Router์˜ ๋ผ์šฐํŒ… ๊ตฌ์กฐ ๋น„๊ต

๋‘ ๋ผ์šฐํ„ฐ ๋ชจ๋‘ ๊ฒฝ๋กœ ๋‹จ์œ„ ์ฝ”๋“œ ๋ถ„ํ• ์„ ์ง€์›ํ•œ๋‹ค. App Router์—์„œ ์ƒˆ๋กญ๊ฒŒ ์ฒด๊ฐ๋˜๋Š” ๋ถ€๋ถ„์€ segment๋ณ„ ๋ ˆ์ด์•„์›ƒ๊ณผ ๋กœ๋”ฉ, ์—๋Ÿฌ ๊ฒฝ๊ณ„, ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๋ชจ๋“ˆ ๊ทธ๋ž˜ํ”„์˜ ๋ถ„๋ฆฌ์˜€๋‹ค.

์ค‘์ฒฉ ๋ ˆ์ด์•„์›ƒ.

app/
โ”œโ”€โ”€ layout.tsx
โ”œโ”€โ”€ page.tsx
โ””โ”€โ”€ dashboard/
    โ”œโ”€โ”€ layout.tsx
    โ”œโ”€โ”€ loading.tsx
    โ””โ”€โ”€ page.tsx
  • app/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.tsx

    • segment์˜ ์ฆ‰์‹œ ๋กœ๋”ฉ UI๋‹ค.

    • ๋‚ด๋ถ€์ ์œผ๋กœ Suspense ๊ฒฝ๊ณ„๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋น„๋˜์ง€ ์•Š์€ ๋‚ด์šฉ์„ ์ŠคํŠธ๋ฆฌ๋ฐํ•œ๋‹ค.

  • error.tsx

    • segment์˜ Error Boundary ์—ญํ• ์„ ํ•œ๋‹ค.

    • reset()๊ณผ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ Client Component์—ฌ์•ผ ํ•œ๋‹ค.

  • not-found.tsx

    • notFound() ํ˜ธ์ถœ์ด๋‚˜ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ๊ฒฝ๋กœ์˜ UI๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

  • route.ts

    • GET, POST ๊ฐ™์€ Web Request/Response ๊ธฐ๋ฐ˜ Route Handler๋ฅผ ๋งŒ๋“ ๋‹ค.

  • template.tsx

    • layout๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ 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>;
}
App Router์˜ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๋ Œ๋”๋ง ๊ฒฝ๊ณ„

์—ฌ๊ธฐ์„œ "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 ๊ตฌ์กฐ๋„ ํ•จ๊ป˜ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์› ๋‹ค.


๐Ÿ“Ž 10. ์ฐธ๊ณ  URL.

์ด์ „ ๊ธ€
E2E ํ…Œ์ŠคํŠธ (Playwright).

๋Œ“๊ธ€

๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘...
๋ชฉ๋ก์œผ๋กœ