๐Ÿš€ Nextjs15 App Router ์‹œ์ž‘ํ•˜๊ธฐ 3

โ€ข

โšก ๋ผ์šฐํŠธ ์„ธ๊ทธ๋จผํŠธ ์˜ต์…˜

Next.js์˜ ํŽ˜์ด์ง€์™€ ๋ ˆ์ด์•„์›ƒ์—์„œ ๋™์ž‘์„ ์ œ์–ดํ•˜๋Š” ์„ธ๊ทธ๋จผํŠธ ์˜ต์…˜๋“ค์ด์—์š”. dynamic์€ ํŽ˜์ด์ง€์˜ ๋ Œ๋”๋ง ๋ฐฉ์‹์„, revalidate๋Š” ์บ์‹œ ๊ฐฑ์‹  ์ฃผ๊ธฐ๋ฅผ, fetchCache๋Š” ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ์ „๋žต์„ ์„ค์ •ํ•ด์š”. runtime์€ ์‹คํ–‰ ํ™˜๊ฒฝ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”.

์„ธ๊ทธ๋จผํŠธ ์˜ต์…˜ ์„ค์ •
// ํŽ˜์ด์ง€ ์ƒ๋‹จ์— ์œ„์น˜
export const dynamic = "";
export const revalidate = 60; // ์ดˆ ๋‹จ์œ„๋กœ ์„ค์ •
export const fetchCache = "force-cache";
export const runtime = "nodejs"; // or "edge"

์ฃผ์˜ ์‚ฌํ•ญ

force-static ์„ค์ •์€ ๋™์  ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ํŽ˜์ด์ง€์—์„œ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์–ด์š”. Edge runtime์€ ์ œํ•œ๋œ Node.js API๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•˜์„ธ์š”!

๐Ÿ“ฆ ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํ„ฐ ์บ์‹œ

๋ธŒ๋ผ์šฐ์ € ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋˜๋Š” ์ž„์‹œ ์ €์žฅ์†Œ์˜ˆ์š”. Router Cache๋Š” prefetch๋œ ํŽ˜์ด์ง€ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ €์žฅํ•˜๊ณ , Server Cache๋Š” React Server Component ํŽ˜์ด๋กœ๋“œ๋ฅผ ์ €์žฅํ•ด์š”. useSelectedLayoutSegment()๋‚˜ usePathname() ๊ฐ™์€ ํ›…์„ ์‚ฌ์šฉํ•  ๋•Œ๋„ ์ด ์บ์‹œ๋ฅผ ํ™œ์šฉํ•ด์š”.

์บ์‹œ ๋ฌดํšจํ™” ์˜ˆ์‹œ
router.refresh(); // ์ „์ฒด ๋ผ์šฐํŠธ ์ƒˆ๋กœ๊ณ ์นจ
router.push("/path", { forceOptimisticNavigation: false }); // ์ตœ์ ํ™” ๋„ค๋น„๊ฒŒ์ด์…˜ ๋น„ํ™œ์„ฑ

๐Ÿš€ ์ŠคํŠธ๋ฆฌ๋ฐ๊ณผ ์„œ์ŠคํŽœ์Šค

React 18์˜ Streaming SSR์„ Next.js์—์„œ ๊ตฌํ˜„ํ•œ ๊ธฐ๋Šฅ์ด์—์š”. ์ดˆ๊ธฐ HTML์„ ๋น ๋ฅด๊ฒŒ ์ „์†กํ•˜๊ณ , React Server Components ํŽ˜์ด๋กœ๋“œ๋Š” ์ ์ง„์ ์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•ด์š”.

ํŽ˜์ด์ง€ ๋ ˆ๋ฒจ ์ŠคํŠธ๋ฆฌ๋ฐ
// app/posts/loading.tsx
export default function Loading() {
  return <LoadingSkeleton />;
}
 
// ์ปดํฌ๋„ŒํŠธ ๋ ˆ๋ฒจ ์ŠคํŠธ๋ฆฌ๋ฐ
import { Suspense } from "react";
 
export default function Posts() {
  return (
    <Suspense fallback={<LoadingSkeleton />}>
      <SlowPostList /> {/* ๋น„๋™๊ธฐ ์ปดํฌ๋„ŒํŠธ */}
    </Suspense>
  );
}

โš ๏ธ ๋ณ‘๋ ฌ ๋ผ์šฐํŠธ์™€ ์ธํ„ฐ์…‰ํŒ…

๋ณ‘๋ ฌ ๋ผ์šฐํŠธ(@modal)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋…๋ฆฝ์ ์ธ ๋ผ์šฐํŠธ๋ฅผ ๋™์‹œ์— ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ธํ„ฐ์…‰ํŒ… ๋ผ์šฐํŠธ๋Š” ํ˜„์žฌ ๋ ˆ์ด์•„์›ƒ ๋‚ด์—์„œ ๋‹ค๋ฅธ ๋ผ์šฐํŠธ์˜ ์ฝ˜ํ…์ธ ๋ฅผ ํ‘œ์‹œํ•  ๋•Œ ์œ ์šฉํ•ด์š”.

๋ณ‘๋ ฌ ๋ผ์šฐํŠธ์™€ ์ธํ„ฐ์…‰ํŒ…
// app/@modal/(..)posts/[id]/page.tsx
// (..) ํ‘œ๊ธฐ๋กœ ์ƒ์œ„ ๊ฒฝ๋กœ ์ธํ„ฐ์…‰ํŠธ
export default function PostModal({ params }) {
  return <PostDetail id={params.id} />;
}

โš ๏ธ ์—๋Ÿฌ ํ•ธ๋“ค๋ง๊ณผ ๋ฆฌ์ปค๋ฒ„๋ฆฌ

error.tsx๋Š” ํŠน์ • ์„ธ๊ทธ๋จผํŠธ์˜ ์—๋Ÿฌ๋ฅผ, global-error.tsx๋Š” ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์น˜๋ช…์ ์ธ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•ด์š”. not-found.tsx๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด 404 ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆํ•  ์ˆ˜ ์žˆ์–ด์š”.

์„ธ๊ทธ๋จผํŠธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
// app/posts/error.tsx
"use client";
 
export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div>
      <h2>๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค!</h2>
      <button onClick={() => reset()}>๋‹ค์‹œ ์‹œ๋„</button>
    </div>
  );
}
๊ธ€๋กœ๋ฒŒ ์—๋Ÿฌ ์ฒ˜๋ฆฌ
// app/global-error.tsx
export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <html>
      <body>
        <h2>์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค</h2>
        <button onClick={() => reset()}>๋‹ค์‹œ ์‹œ๋„</button>
      </body>
    </html>
  );
}

๐Ÿ“ ์ •๋ฆฌ

1๏ธโƒฃ dynamic, revalidate, fetchCache, runtime ๋“ฑ์˜ ์„ธ๊ทธ๋จผํŠธ ์˜ต์…˜์œผ๋กœ ํŽ˜์ด์ง€์˜ ๋™์ž‘์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด์š”!

2๏ธโƒฃ Router Cache์™€ Server Cache๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€ ์„ธ๊ทธ๋จผํŠธ์™€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•ด์š”.

3๏ธโƒฃ React 18์˜ Streaming SSR์„ ํ™œ์šฉํ•ด ์ดˆ๊ธฐ HTML์„ ๋น ๋ฅด๊ฒŒ ์ „์†กํ•˜๊ณ  React Server Components ํŽ˜์ด๋กœ๋“œ๋Š” ์ ์ง„์ ์œผ๋กœ ์ „๋‹ฌํ•ด์š”.

4๏ธโƒฃ @modal๊ณผ ๊ฐ™์€ ๋ณ‘๋ ฌ ๋ผ์šฐํŠธ๋กœ ๋…๋ฆฝ์ ์ธ ๋ Œ๋”๋ง์„, (..) ํ‘œ๊ธฐ๋กœ ๋‹ค๋ฅธ ๋ผ์šฐํŠธ์˜ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€๋กœ์ฑ„์„œ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์–ด์š”.

5๏ธโƒฃ error.tsx์™€ global-error.tsx๋กœ ์„ธ๊ทธ๋จผํŠธ๋ณ„ ๋˜๋Š” ์ „์—ญ ์—๋Ÿฌ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ๋ณต๊ตฌํ•  ์ˆ˜ ์žˆ์–ด์š”.

์™ธ๋ถ€ ๋งํฌ