framework/NextJS

πŸ›‘οΈ Next.js App Router μ—λŸ¬ 처리 μ™„μ „ κ°€μ΄λ“œ

octo54 2025. 4. 23. 10:50
λ°˜μ‘ν˜•

πŸ›‘οΈ Next.js App Router μ—λŸ¬ 처리 μ™„μ „ κ°€μ΄λ“œ

 

Next.js App RouterλŠ” νŽ˜μ΄μ§€ λ‹¨μœ„, μ „μ—­ λ‹¨μœ„μ˜ λ‹€μ–‘ν•œ μ—λŸ¬ 처리λ₯Ό κ³΅μ‹μ μœΌλ‘œ μ§€μ›ν•©λ‹ˆλ‹€.
μ˜ˆμƒλœ 였λ₯˜(404 λ“±)뿐만 μ•„λ‹ˆλΌ 예기치 λͺ»ν•œ λŸ°νƒ€μž„ 였λ₯˜κΉŒμ§€ λ‹€λ£° 수 μžˆλ„λ‘ κ΅¬μ‘°ν™”λœ λ°©μ‹μœΌλ‘œ μ œκ³΅λ©λ‹ˆλ‹€. 이 κΈ€μ—μ„œλŠ” κ·Έ 핡심인 not-found.tsx, error.tsx, global-error.tsx 파일의 μ—­ν• κ³Ό μ‚¬μš©λ²•μ„ μ‹€μ „ μ˜ˆμ œμ™€ ν•¨κ»˜ μ •λ¦¬ν•©λ‹ˆλ‹€.


βœ… 1. not-found.tsx – 404 μ—λŸ¬ 처리

λ¦¬μ†ŒμŠ€λ₯Ό 찾을 수 μ—†λŠ” 상황(예: κ²Œμ‹œκΈ€μ΄ μ‘΄μž¬ν•˜μ§€ μ•Šμ„ λ•Œ)μ—λŠ” notFound() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ 404 νŽ˜μ΄μ§€λ‘œ μœ λ„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ‚¬μš© μ˜ˆμ‹œ

// app/posts/[id]/page.tsx
import { notFound } from 'next/navigation';

export default async function Page({ params }) {
  const post = await getPostById(params.id);
  if (!post) notFound();

  return <div>{post.title}</div>;
}

μ»€μŠ€ν…€ 404 νŽ˜μ΄μ§€ μž‘μ„±

// app/posts/[id]/not-found.tsx
export default function NotFound() {
  return <h1>이 κ²Œμ‹œκΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.</h1>;
}

전체 앱에 κ³΅ν†΅μ μœΌλ‘œ μ μš©ν•˜κ³  μ‹Άλ‹€λ©΄ app/not-found.tsxλ₯Ό μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.


πŸ’₯ 2. error.tsx – νŽ˜μ΄μ§€ λ‹¨μœ„ μ˜ˆμ™Έ 처리

예기치 λͺ»ν•œ μ—λŸ¬(예: fetch μ‹€νŒ¨, λ Œλ”λ§ 쀑 였λ₯˜ λ“±)λŠ” ν•΄λ‹Ή 라우트 폴더에 error.tsxλ₯Ό μΆ”κ°€ν•˜μ—¬ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

기본 ꡬ쑰

// app/dashboard/error.tsx
'use client';

export default function Error({ error, reset }: { error: Error; reset: () => void }) {
  return (
    <div>
      <h2>μ—λŸ¬κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</h2>
      <p>{error.message}</p>
      <button onClick={() => reset()}>λ‹€μ‹œ μ‹œλ„</button>
    </div>
  );
}
  • error: λ°œμƒν•œ μ—λŸ¬ 객체
  • reset(): μ—λŸ¬ μƒνƒœλ₯Ό μ΄ˆκΈ°ν™”ν•˜κ³  μ»΄ν¬λ„ŒνŠΈλ₯Ό μž¬λ Œλ”λ§

λ°˜λ“œμ‹œ 'use client'κ°€ μ„ μ–Έλ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.


🌐 3. global-error.tsx – μ „μ—­ μ—λŸ¬ 처리

루트 λ ˆμ΄μ•„μ›ƒμ—μ„œ λ°œμƒν•œ μ—λŸ¬(초기 λ Œλ”λ§ 쀑 μ—λŸ¬ λ“±)λŠ” global-error.tsxλ₯Ό 톡해 μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ‚¬μš© μ˜ˆμ‹œ

// app/global-error.tsx
'use client';

export default function GlobalError({ error, reset }: { error: Error; reset: () => void }) {
  return (
    <html>
      <body>
        <h1>앱에 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.</h1>
        <p>{error.message}</p>
        <button onClick={() => reset()}>ν™ˆμœΌλ‘œ</button>
      </body>
    </html>
  );
}
  • λ£¨νŠΈμ— μžˆμ–΄μ•Ό ν•˜λ©°, <html>κ³Ό <body> νƒœκ·Έλ₯Ό 포함해야 ν•©λ‹ˆλ‹€.
  • νŽ˜μ΄μ§€ μ „ν™˜ μ‹œ μžλ™ λ³΅κ΅¬λ˜μ§€ μ•ŠμœΌλ―€λ‘œ μˆ˜λ™ reset() ν•„μš”

πŸ” μ—λŸ¬ 볡ꡬ 흐름

λ°˜μ‘ν˜•

error.tsx λ˜λŠ” global-error.tsxμ—μ„œ reset() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈλŠ” λ‹€μ‹œ μš”μ²­λ˜λ©° 초기 μƒνƒœλ‘œ λŒμ•„κ°‘λ‹ˆλ‹€.

<button onClick={() => reset()}>λ‹€μ‹œ μ‹œλ„</button>
  • API μ„œλ²„ μΌμ‹œμ  λ‹€μš΄, DB μ—°κ²° μ‹€νŒ¨ λ“± μΌμ‹œμ  였λ₯˜ 볡ꡬ에 효과적
  • μƒˆλ‘œκ³ μΉ¨ 없이 μ‚¬μš©μžμ—κ²Œ λΉ λ₯Έ 리트라이 κΈ°λŠ₯ 제곡

πŸ“ μ‹€μ „ ꡬ쑰 μ˜ˆμ‹œ

app/
β”œβ”€β”€ layout.tsx
β”œβ”€β”€ global-error.tsx        ← μ „μ—­ μ˜ˆμ™Έ 처리
β”œβ”€β”€ not-found.tsx           ← 전체 404 처리
└── dashboard/
    β”œβ”€β”€ page.tsx
    β”œβ”€β”€ error.tsx           ← dashboard μ „μš© μ˜ˆμ™Έ 처리
    └── not-found.tsx       ← dashboard μ „μš© 404 처리

βœ… μš”μ•½

ν•­λͺ© μ—­ν•  μœ„μΉ˜

notFound() 404 μœ λ„ ν•¨μˆ˜ μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ λ‚΄λΆ€
not-found.tsx 404 νŽ˜μ΄μ§€ 폴더 λ˜λŠ” 루트
error.tsx 라우트 λ‹¨μœ„ μ—λŸ¬ 경계 νŽ˜μ΄μ§€ 폴더 λ‚΄
global-error.tsx μ „μ—­ μ—λŸ¬ 경계 (λ ˆμ΄μ•„μ›ƒ μˆ˜μ€€) app/ 루트
reset() μ—λŸ¬ μƒνƒœ μ΄ˆκΈ°ν™” 및 μž¬λ Œλ”λ§ error μ»΄ν¬λ„ŒνŠΈ λ‚΄λΆ€

 

Next.js, App Router, μ—λŸ¬ 핸듀링, μ—λŸ¬ 경계, not-found.tsx, error.tsx, global-error.tsx, reset ν•¨μˆ˜, μ˜ˆμ™Έ 처리, μ‚¬μš©μž κ²½ν—˜, SEO μ΅œμ ν™” 10개