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

μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ ν΄λΌμ΄μ–ΈνŠΈ μ»΄ν¬λ„ŒνŠΈ κ΅¬λΆ„ν•˜κΈ°

μ‚¬μš©μžμ—κ²Œ 전달 λ˜μ–΄μ•Ό ν•˜λŠ” μ½”λ“œκ°€ ν¬ν•¨λœ 경우만 ν΄λΌμ΄μ–ΈνŠΈ μ»΄ν¬λ„ŒνŠΈλ‘œ λ§Œλ“€μ–΄ μ„±λŠ₯을 λ†’μ΄μž.

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