Next 13

Next 13

๋ฒ„์ „์—…์ด ๋˜๋ฉด์„œ ๊ธฐ์กด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ๊ฒฝ์šฐ Breaking Changes๋กœ ์ธํ•ด ์ „๋ฐ˜์ ์œผ๋กœ ๋ณ€๊ฒฝ์‚ฌํ•ญ์— ๋งž์ถฐ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

Next๋Š” ์ด๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ ์ž ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ๊ณผ ๊ทœ์น™๋“ค์„ app/ ์— ์ ์šฉํ•˜์—ฌ ๊ธฐ์กด page/ ๊ตฌ์กฐ์˜ ์•ฑ์ด ๋ง๊ฐ€์ง€์ง€ ์•Š๊ณ  ์ ์ง„์ ์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ณตํ•œ๋‹ค.

Braking Changes

์†Œํ”„ํŠธ์›จ์–ด ์‹œ์Šคํ…œ์˜ ํ•œ ๋ถ€๋ถ„์ด ๋ณ€๊ฒฝ๋˜์–ด ๋‹ค๋ฅธ ๊ตฌ์„ฑ ์š”์†Œ์— ์ž ์žฌ์ ์œผ๋กœ ์žฅ์• ๋ฅผ ์ผ์œผํ‚ค๋Š” ๊ฒƒ

Routing

app
 โ”œโ”€โ”€ api
 โ”‚   โ””โ”€โ”€ hello
 โ”‚       โ””โ”€โ”€ route.ts
 โ”œโ”€โ”€ layout.tsx
 โ”œโ”€โ”€ page.tsx
 โ””โ”€โ”€ product
     โ”œโ”€โ”€ [slug]
     โ”‚   โ””โ”€โ”€ page.tsx
     โ”œโ”€โ”€ layout.tsx
     โ””โ”€โ”€ page.tsx

๋งค์นญํ•  ๊ฒฝ๋กœ๋ฅผ ํด๋” ์ด๋ฆ„์œผ๋กœ ๋‘๊ณ  ํ•˜์œ„์— page ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ์„ ๋‘๋ฉด ์ž๋™์œผ๋กœ ๋ผ์šฐํŒ… ๋œ๋‹ค. ํด๋”(๊ฒฝ๋กœ)๋ณ„๋กœ ๋ฒ”์œ„๋ฅผ ์ขํ˜€ ์žฌ์‚ฌ์šฉ๋˜๋Š” ๋ ˆ์ด์•„์›ƒ, ์—๋Ÿฌ ์ƒํƒœ, ๋กœ๋”ฉ UI ๋“ฑ์„ ๊ณต์œ ํ•˜์—ฌ ํ•ด๋‹น ๋ผ์šฐํŠธ ์•ˆ์—์„œ ์ฒ˜๋ฆฌํ•ด์ฃผ๊ธฐ ์œ„ํ•จ

  • page/ โ†’ app/

  • Folder & File based routing

Dynamic Routing

// app/[slug]/page.tsx
interface DynamicPageProps {
  params: {
    slug: string;
  }
}

export default function DynamicPage({ params }: DynamicPageProps) {
  
  return `slug: ${params.slug}`;
}

generateStaticParams()

์ด์ „ ๋ฒ„์ „์˜ getStaticPaths() ๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ํ•จ์ˆ˜

๋ฏธ๋ฆฌ ์ƒ์„ฑํ•  ํŽ˜์ด์ง€์˜ ๋™์  ๊ฒฝ๋กœ(slug)๋ฅผ ์ง€์ •ํ•˜๊ณ  { slug: value } ๊ตฌ์กฐ์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

export function generateStaticParams() {
  const slugs = ['a', 'b'];
  return slugs.map((slug) => {
    slug,
  }) 
}

Layout

  • ๋ ˆ์ด์•„์›ƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(๋ณ€๊ฒฝ ๊ฐ€๋Šฅ)

  • ๊ฐ™์€ ๊ฒฝ๋กœ ๋ฐ ํ•˜์œ„ ๊ฒฝ๋กœ์˜ ํŽ˜์ด์ง€์— ๊ณต์œ ๋˜๋ฉฐ ๋ž˜ํ•‘๋œ๋‹ค. โ†’ ๋ ˆ์ด์•„์›ƒ(์ปดํฌ๋„ŒํŠธ)

  • ๊ฒฝ๋กœ ๋ณ„๋กœ ๋ ˆ์ด์•„์›ƒ์„ ์ง€์ • ๊ฐ€๋Šฅํ•˜๋ฉฐ ์ƒ์œ„์— ์žˆ๋Š” ๋ ˆ์ด์•„์›ƒ์— ์ค‘์ฒฉ๋˜์–ด ์ ์šฉ๋œ๋‹ค.

  • ๋ฐ์ดํ„ฐ ํŒจ์นญ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ ๋ ˆ์ด์•„์›ƒ๊ฐ„ ์ „๋‹ฌ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ฒฝ๋กœ๊ฐ€ ๊ฐ™์€ ๊ฒฝ์šฐ ๊ฐ ๋ ˆ์ด์•„์›ƒ์ด ์ค‘๋ณต์œผ๋กœ ๋ฐ์ดํ„ฐ ํŒจ์นญ์‹œ React๊ฐ€ ์•Œ์•„์„œ ์ค‘๋ณต๋œ ์š”์ฒญ์„ ์ œ๊ฑฐํ•œ๋‹ค.

Root Layout (ํ•„์ˆ˜)

app/ ๋‚ด์—์„œ ๊ฐ€์žฅ ์ƒ์œ„์— ์žˆ๋Š” layout.tsx ๋ฅผ ๋ฃจํŠธ ๋ ˆ์ด์•„์›ƒ์ด๋ผ ์นญํ•˜๋ฉฐ ๋ชจ๋“  ํŽ˜์ด์ง€์— ๋ž˜ํ•‘๋œ๋‹ค.

  • ์ด์ „ ๋ฒ„์ „์˜_app.tsx, _document.ts ๋ฅผ ๋Œ€์ฒดํ•œ๋‹ค.

  • ์„œ๋ฒ„์—์„œ ๋ฐ˜ํ™˜๋œ ์ดˆ๊ธฐ HTML์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค

  • Next.js๊ฐ€ ์ž๋™์œผ๋กœ <html>, <body>๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฃจํŠธ ๋ ˆ์ด์•„์›ƒ์— ํ•ด๋‹น ํƒœ๊ทธ๋ฅผ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

Not Found Page

์ปค์Šคํ…€ 404 page๋ฅผ ๋งŒ๋“œ๋ ค๋ฉด ํŒŒ์ผ์ด๋ฆ„์„ not-found๋กœ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

ํด๋”(๊ฒฝ๋กœ)๋‚ด์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด not-found ํŒŒ์ผ์ด ์‚ฌ์šฉ๋˜์–ด ํŽ˜์ด์ง€ ๋ณ„๋กœ ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ์„ค์ • ๊ฐ€๋Šฅ

ํ˜„์žฌ ์‹œ์ ์—๋Š” notFound() ํ˜ธ์ถœ์— ์˜ํ•ด ํŠธ๋ฆฌ๊ฑฐ๋  ๋•Œ๋งŒ ๋ Œ๋”๋ง๋œ๋‹ค.

Global Not Found Page

ํ˜„ ์‹œ์  13.2.4 ๋ฒ„์ „์—์„œ๋Š” Global Not Found Page ๊ธฐ๋Šฅ์ด app/ ๋ผ์šฐํŒ…์—์„œ๋Š” ์•„์ง ์ œ๊ณต๋˜์ง€ ์•Š๊ณ  ์žˆ๋‹ค.

Global Not Found Page๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด?

  • ๊ธฐ์กด ๋ฐฉ์‹๋Œ€๋กœ page/ ํ•˜์œ„์˜ 404.tsx ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ด ์‚ฌ์šฉ

  • app/ ๋‚ด์—์„œ ๋™์  ๋ผ์šฐํŒ…์„ ํ™œ์šฉํ•˜์—ฌ ๊ตฌํ˜„

์šฐํšŒํ•ด์„œ app/ ๋ผ์šฐํŒ…์œผ๋กœ Global Not Found Page ๊ตฌํ˜„ํ•˜๊ธฐ

โ”œโ”€โ”€ [...not-found]
โ”‚   โ””โ”€โ”€ page.tsx
โ”œโ”€โ”€ not-found.tsx
// app/[...not-found]/page.tsx
import {notFound} from 'next/navigation';

export default function NotFoundCatchAll() {
  notFound();
}

Metadata

์–ด๋–ค ํŽ˜์ด์ง€(or ๋ ˆ์ด์•„์›ƒ)์—์„œ๋“  ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๋œ metadata ๋ณ€์ˆ˜๋ฅผ export ํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'ํƒ€์ดํ‹€',
}

Dynamic Metadata

generateMetadata() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ํŒจ์นญ ํ›„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์‚ฌ์šฉ์ž์—๊ฒŒ ํŽ˜์ด์ง€๋ฅผ ์ฃผ๊ธฐ ์ „ generateMetadata() ํ˜ธ์ถœ๋˜๊ณ  ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ •๋ณด๊ฐ€ ์™„์„ฑ ๋˜์–ด์•ผ์ง€๋งŒ ํŽ˜์ด์ง€๋ฅผ ์ „์†กํ•œ๋‹ค.

export async function generateMetadata({ params, searchParams }) {
  const data = await getData(params); // ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ํŒจ์นญ
  return { title: data.title };
}

Default Metadata

๋”ฐ๋กœ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ค์ •๋˜๋Š” ๋ฉ”ํƒ€ํƒœ๊ทธ

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

Rendering

๋ Œ๋”๋ง ๋ฐฉ์‹์ด (page/) ํŽ˜์ด์ง€ ๋‹จ์œ„ -> (app/) ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค.

๊ธฐ์กด page ๋‹จ์œ„ ๋ Œ๋”๋ง ๋ฐฉ์‹์˜ ๋ฌธ์ œ์ 

ํŽ˜์ด์ง€ ๋‹จ์œ„์˜ ๋ Œ๋”๋ง ๋ฐฉ์‹์€ ์•ฑ์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ›๋Š” ๋ฒˆ๋“ค ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๊ฐ€ ์ปค์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด React 18 ๋ฒ„์ „์— ๋งž์ถฐ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ, ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ๋ Œ๋”๋ง ํ•˜๋Š” ๋ฐฉ์‹์ด ๋„์ž…๋˜์—ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ๋ Œ๋”๋ง ๋ฐฉ์‹์˜ ์žฅ์ 

ํ•˜๋‚˜์˜ ํŽ˜์ด์ง€ ์•ˆ์—์„œ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ๋ Œ๋”๋ง ๋ฐฉ์‹์„ ๊ทœ์ •ํ•ด ์กฐํ•ฉํ•˜์—ฌ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„์—์„œ๋งŒ ์ž‘์—…์ด ์ด๋ฃจ์–ด์ง€๊ณ  ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋˜์–ด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ณด๋‚ผ ์ฝ”๋“œ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ›์„ ๋ฒˆ๋“ค ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๊ฐ€ ํ˜„์ €ํžˆ ์ค„์–ด๋“ค๊ฒŒ ๋œ๋‹ค.

์ฆ‰, ์‚ฌ์šฉ์ž์˜ ์ธํ„ฐ๋ ‰์…˜์ด ํ•„์š”ํ•œ ์ฝ”๋“œ๋งŒ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ๋˜์–ด ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋”์šฑ ๋นจ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.

React v18์—์„œ ์ƒˆ๋กญ๊ฒŒ ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ

์„œ๋ฒ„์ƒ์—์„œ ๋ฆฌ์•กํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

  • ์„œ๋ฒ„์ƒ์—์„œ๋งŒ ๋™์ž‘ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ

  • SSR !== RSC, ์„œ๋กœ ๋Œ€์ฒด์ œ๊ฐ€ ์•„๋‹ˆ๋ฉฐ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์„œ๋กœ ๋ณด์™„ํ•˜์—ฌ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ

    • ํ”„๋ฆฌ ๋ Œ๋”๋ง(SSR) + ๋ฒˆ๋“ค์‚ฌ์ด์ฆˆ ์ตœ์†Œํ™”(RSC)

  • app/ ๋‚ด์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

  • ์„œ๋ฒ„์—์„œ ๋นŒ๋“œ ๋  ๋•Œ ์‹คํ–‰๋˜๋Š” ์ปดํฌ๋„ŒํŠธ, ๊ธฐ๋ณธ์ ์œผ๋กœ SSG

  • ์ •์ ์ธ HTML ํ˜•ํƒœ๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ๋˜๋ฉฐ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌ๋˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋Š” Zero

  • ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ œ๊ณตํ•˜๋Š” API, Hooks๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์ง€๋งŒ node.js(์„œ๋ฒ„)์—์„œ ์ œ๊ณตํ•˜๋Š” API๋Š” ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•  ๋–„ ์ „์—ญ ์‹ฑ๊ธ€ํ†ค๊ณผ ๊ฐ™์€ ๋„ค์ดํ‹ฐ๋ธŒ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํŒจํ„ด์„ ํ™œ์šฉ

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์— ์„œ๋ฒ„(DB)์—์„œ ํŒจ์นญํ•œ ๋ฐ์ดํ„ฐ ์ž์›์„ ๊ณต์œ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ „๋‹ฌํ•  ํ•„์š” ์—†์ด ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” ๊ณณ์—์„œ ํŒจ์นญํ•˜๋ฉด ๋œ๋‹ค. ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋Š” ์บ์‹ฑ ๋˜์–ด ๋„ฅ์ŠคํŠธ์—์„œ ์ค‘๋ณต ์š”์ฒญ์„ ์ œ๊ฑฐํ•˜๋Š” ์‹์œผ๋กœ ์ตœ์ ํ™”๊ฐ€ ์ž๋™์œผ๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค.

Client Component

์ด์ „ ๋ฒ„์ „์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ ํ”„๋ฆฌ๋ Œ๋”๋ง + ํ•˜์ด๋“œ๋ ˆ์ด์…˜ ๋ฐฉ์‹

  • ๋””ํดํŠธ๊ฐ€ ์„œ๋ฒ„์ปดํฌ๋„ŒํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ƒ๋‹จ์— use client ๋ฅผ ๋ช…์‹œํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ผ๊ณ  ํ•ด์„œ ๋ฌด์กฐ๊ฑด csr ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.

  • ์„œ๋ฒ„์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฝ๊ณ  ์ •์ ์ธ ๋ถ€๋ถ„์„ ๋ถ„๋ฆฌํ•˜์—ฌ ํ”„๋ฆฌ๋ Œ๋”๋ง์— ํฌํ•จ์‹œํ‚ค๊ณ  ํ•˜์ด๋“œ๋ ˆ์ด์…˜ ๊ณผ์ •์ด ์ผ์–ด๋‚œ๋‹ค. (๊ธฐ์กด ๋ฐฉ์‹๊ณผ ๋™์ผ)

  • ์‚ฌ์šฉ์ž์˜ ์ด๋ฒคํŠธ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋“ค์ด ์žˆ๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

  • ์ฆ‰, ์‚ฌ์šฉ์ž(๋ธŒ๋ผ์šฐ์ €)์—๊ฒŒ ๋ณด๋‚ด์ ธ์•ผ ํ•˜๋Š” ์ฝ”๋“œ๋“ค์ด ๋‹ด๊ธด ์ปดํฌ๋„ŒํŠธ

  • ์ƒํƒœ๋ฅผ ์“ฐ๊ฑฐ๋‚˜ hooks๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๋Š” props๋Š” serialization(์ง๋ ฌํ™”)๊ฐ€ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.

  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ชจ๋“ˆ, ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ๋ชจ๋‘ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋œ๋‹ค.

    • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž„ํฌํŠธํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

    • ๋”ฐ๋ผ์„œ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ์ปดํฌ๋„ŒํŠธ Tree์—์„œ ๊ฐ€์žฅ ๋๋‹จ์— ์œ„์น˜ํ•˜๋Š” leaf๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

    • ๋ถˆ๊ฐ€ํ”ผํ•  ๊ฒฝ์šฐ ๋„ฅ์ŠคํŠธ์—์„œ ์ปดํฌ์ง€์…˜ ํŒจํ„ด(์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ props๋กœ ์ „๋‹ฌํ•˜์—ฌ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐํ•ฉํ•˜๋Š” ํŒจํ„ด)์„ ํ™œ์šฉํ• ๊ฒƒ์„ ๊ถŒ๊ณ ํ•œ๋‹ค.

serialization(์ง๋ ฌํ™”)๋ž€ ๊ฐ์ฒด๋ฅผ ๋„คํŠธ์›Œํฌ ์ƒ์—์„œ ์ „์†กํ•˜๊ฑฐ๋‚˜ ํŒŒ์ผ์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ์ฒด๋ฅผ ์ผ๋ จ์˜ ๋ฐ”์ดํŠธ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” JSON.stingify(), JSON.prase()ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ JSON ๋ฌธ์ž์—ด๋กœ ์ง๋ ฌํ™” & ์—ญ์ง๋ ฌํ™” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

Serialization(์ง๋ ฌํ™”) ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ๊ตฌ๋ถ„ - ๊ฐ€๋Šฅ: ๊ฐ์ฒด, ๋ฐฐ์—ด, ์ˆซ์ž, ๋ฌธ์ž์—ด, ๋ถˆ๋ฆฌ์–ธ, null, undefined - ๋ถˆ๊ฐ€๋Šฅ: ํ•จ์ˆ˜, Date

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๊ตฌ๋ถ„ํ•˜๊ธฐ

์‚ฌ์šฉ์ž์—๊ฒŒ ์ „๋‹ฌ ๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ๋งŒ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“ค์–ด ์„ฑ๋Šฅ์„ ๋†’์ด์ž.

case
์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ
ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ

๋ฐ์ดํ„ฐ ํŒจ์นญ

โœ…

๋ฐ์ดํ„ฐ ํŒจ์นญ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์—†๋‹ค๋ฉด ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ ํŒจ์นญ์„ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. (์„ฑ๋Šฅ, ์‚ฌ์šฉ์„ฑ)

๋ฐฑ์—”๋“œ ๋ฆฌ์†Œ์Šค ์ง์ ‘ ์ ‘๊ทผ

โœ…

โŒ

๋…ธ์ถœ๋˜๋ฉด ์•ˆ๋˜๋Š” ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ(์—‘์„ธ์Šค ํ† ํฐ, API Keys)

โœ…

โŒ

์‚ฌ์šฉ์ž์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์ฝ”๋“œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์‚ฌ์šฉ

โŒ

โœ…

React Hooks ์‚ฌ์šฉ (useState, useEffect ๋“ฑ)

โŒ

โœ…

๋ธŒ๋ผ์šฐ์ € API ์‚ฌ์šฉ

โŒ

โœ…

Data Fetching

getStaticProps() , gstServerSideProps() ๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ์‹์ด ์ถ”๊ฐ€ ๋˜์—ˆ๋‹ค.

์–ด๋Š ์‹œ์ ์— ํŽ˜์ด์ง€(or ๋ ˆ์ด์•„์›ƒ)๋ฅผ ์žฌ์ƒ์„ฑ ํ• ์ง€ ์ง€์ •

revalidate

๋ธŒ๋ผ์šฐ์ €์—์„œ ํŽ˜์ด์ง€(or ๋ ˆ์ด์•„์›ƒ)์˜ ์žฌ๊ฒ€์ฆํ•˜๋Š” ๊ธฐ๋ณธ ์‹œ๊ฐ„์„ ์ง€์ •

export const revalidate = false; // default
// false(SSG) | 'force-cache'(SSG) | 0(SSR) | number(ISR)

fetch() ์˜ ์„ค์ •๋œ ์บ์‹œ ์˜ต์…˜์„ override ํ•˜์ง€ ์•Š๋Š”๋‹ค. -> revalidate < fetch()

fetch(url, options)

async/await ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํŒจ์นญ

Next.js๋Š” Web fetch API๋ฅผ ํ™•์žฅํ•˜์—ฌ ์‚ฌ์šฉ

์บ์‹œ ์œ ๋ฌด์— ๋”ฐ๋ผ SSG, ISR, SSR ๋กœ ๋‚˜๋‰˜๊ฒŒ ๋œ๋‹ค.

export default async function Page() {
  const data = await fetch(url, {
    cache: 'force-cache'; // default (SSG)
    // no-store(SSR), number(ISR)
  });
}

Dynamic Rendering

๋™์  ํ•จ์ˆ˜ ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ fetch() ์š”์ฒญ์˜ ์บ์‹œ ์˜ต์…˜์ด no-store ์ผ ๋•Œ ๋™์  ๋ Œ๋”๋ง(SSR)์œผ๋กœ ์ „ํ™˜๋œ๋‹ค.

๋™์  ํ•จ์ˆ˜(Dynamic fuction)

์‚ฌ์šฉ์ž์˜ ์š”์ฒญ ์‹œ์ ์— ์•Œ ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•œ ํ•จ์ˆ˜๋ฅผ ๋™์  ํ•จ์ˆ˜๋ผ ํ•œ๋‹ค.

ex) ์š”์ฒญ ์‹œ์ ์— ์•Œ ์ˆ˜ ์žˆ๋Š” ์ •๋ณด -> ์ฟ ํ‚ค, HTTP ์š”์ฒญ ํ—ค๋”, URL params

API Route

Next๊ฐ€ Full Stack์œผ๋กœ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ์ด์œ 

์š”์ฒญ ๋ณ„(Get, Post)๋กœ ๋‚˜๋ˆ„์–ด ๊ตฌ์กฐ์ ์œผ๋กœ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•˜๋„๋ก ๋ฐ”๋€Œ์—ˆ๋‹ค.

// app/api/[endpoint]/route.ts
export async function GET(req: Request) {
  const data = await ...
  return NextResponse.json(data);
}

๋ผ์šฐํŠธ๋‹น ํด๋” ํ•˜๋‚˜

๋ผ์šฐํŠธ๊ฐ€ ๋˜๋Š” ํด๋” ์ฆ‰, ํŽ˜์ด์ง€ ๋ณ„๋กœ ๋‹ค์–‘ํ•˜๊ณ  ์œ ์šฉํ•œ ์•„์ดํ…œ๋“ค์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํด๋” ๋ณ„๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ๋“ค

  • page.tsx: ํ•ด๋‹น ํด๋”๋ฅผ ๊ฒฝ๋กœ๋กœ ํ•˜๋Š” ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ

  • layout.tsx: ํŽ˜์ด์ง€๋ฅผ ๊ฐ์‹ธ๋Š” ๊ณจ๊ฒฉ์„ ์„ค์ • (์ƒ์œ„ ๋ ˆ์ด์•„์›ƒ์— ์ค‘์ฒฉ๋จ)

  • template.tsx: ๋ ˆ์ด์•„์›ƒ๊ณผ ๋น„์Šทํ•˜๋‚˜. ํŽ˜์ด์ง€ ์ „ํ™˜์‹œ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒˆ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋งˆ์šดํŠธ๋˜์–ด ์‹คํ–‰๋˜๋Š” ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

  • loading.tsx: ํ•ด๋‹น ๋ผ์šฐํŠธ์—์„œ ํŽ˜์ด์ง€ UI๊ฐ€ ์ค€๋น„๋˜๊ธฐ ์ „ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š” UI ์ปดํฌ๋„ŒํŠธ (React.Suspense)

  • error.tsx: ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ์‹œ ๋Œ€์ฒด UI ์ปดํฌ๋„ŒํŠธ (React.ErrorBoundary)

  • not-found.tsx: ํ•ด๋‹น ๋ผ์šฐํŠธ์˜ 404 UI ์ปดํฌ๋„ŒํŠธ

loading.tsx

ํ•ด๋‹น ๋ผ์šฐํŠธ์˜ ํŽ˜์ด์ง€๊ฐ€ ์ค€๋น„๋˜๋Š” ๋™์•ˆ loading.tsx์— ์„ ์–ธํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณด์—ฌ์ง€๊ฒŒ ๋œ๋‹ค. * layout์„ ์ œ์™ธํ•œ page ๋ถ€๋ถ„๋งŒ ์ ์šฉ๋จ

์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ React.Suspense ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

<Layout>
  <Suspense fallback={<Loading />}>
    <Page />
  </Suspense>
</Layout>

๋กœ๋”ฉ์€ ๋™์ ์ธ SSR์„ ํ•  ๋•Œ ๋น ๋ฅด๊ฒŒ ๋กœ๋”ฉ UI๋ฅผ ๋จผ์ € ๋ณด์—ฌ์ฃผ๊ณ  ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณ‘๋ ฌ์ ์œผ๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰ํ›„ ์™„๋ฃŒ๋˜๋ฉด ์ „์†กํ•˜๋Š” ์‹์œผ๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ํŽ˜์ด์ง€๊ฐ€ ์„œ๋น™์ค‘์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ๋ฆฐ๋‹ค.

SSG๋Š” ์ด๋ฏธ ์ •์ ์ธ HTML์„ ์„œ๋ฒ„์—์„œ ์ „์†ก๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๋”ฉ์˜ ์˜๋ฏธ๊ฐ€ ํ•„์š”์—†๋‹ค.

ํ•œ๊ณ„์ 

ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” ํŽ˜์ด์ง€ ์ „์ฒด๋ฅผ ๋ฌถ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ์œ„ํ•ด์„œ๋Š” ๋‚ด๋ถ€ ์š”์†Œ๋“ค์— ์„œ์ŠคํŽœ์Šค๋ฅผ ๋”ฐ๋กœ ์„ค์ • ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

Suspense ์‚ฌ์šฉ ์ด์ 

UI๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋“œ๋  ๋–„๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆด ํ•„์š” ์—†์ด ํŽ˜์ด์ง€์˜ ์ผ๋ถ€๋ฅผ ๋” ๋นจ๋ฆฌ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Streaming Server Rendering: ์ŠคํŠธ๋ฆฌ๋ฐ์„ ์‚ฌ์šฉํ•˜๋ฉด ํŽ˜์ด์ง€์˜ HTML์„ ๋” ์ž‘์€ ์ฒญํฌ๋กœ ๋‚˜๋ˆ„๊ณ  ํ•ด๋‹น ์ฒญํฌ๋ฅผ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ์ ์ง„์ ์œผ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

  • Selective Hydration: ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ ‰์…˜์„ ํ†ตํ•ด ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋จผ์ € ์ˆ˜ํ™”ํ• ์ง€ ์„ ํƒ์ ์œผ๋กœ ๊ฒฐ์ •

Next.js์—์„œ loading.tsx๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Suspense๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ŠคํŠธ๋ฆฌ๋ฐ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ŠคํŠธ๋ฆฌ๋ฐ์€ ๊ฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‚˜์˜ ์ฒญํฌ๋กœ ๊ฐ„์ฃผํ•  ์ˆ˜ ์žˆ๋‹ค

error.tsx

์—๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋Š” ์–ด๋–ค ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€์™€ ํŽ˜์ด์ง€๋ฅผ ๋ฆฌ์…‹ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ props๋กœ ์ œ๊ณตํ•œ๋‹ค.

๋‚ด๋ถ€์ ์œผ๋กœ ErrorBoundary ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

<Layout>
  <ErrorBoundary fallback={<Error />}>
    <Page />
  </ErrorBoundary>
</Layout>

์—๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋Š” ๋Ÿฐํƒ€์ž„์— ํ•„์š”ํ•œ ์‚ฌ์šฉ์ž ์ฝ”๋“œ์™€ useEffect ๊ฐ™์€ ํ›…๋“ค์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋‹ˆ ๋ฌด์กฐ๊ฑด Client ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋œ๋‹ค.

loading.tsx ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํŽ˜์ด์ง€ ๋‚ด๋ถ€ ์ž‘์€ ๋‹จ์œ„๋กœ ์—๋Ÿฌ UI๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ  ์‹ถ๋‹ค๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ErrorBoundary๋กœ ๊ฐ์‹ธ๋ฉด ๋œ๋‹ค.

๊ฐ€์žฅ ๊ทผ์ ‘ํ•œ ๋ถ€๋ชจ์˜ error.tsx ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค. (๋ฒ„๋ธ”๋ง ๋จ)

Last updated