[Next.js] Intercepting Routes๋ž€?

jinyยท2025๋…„ 11์›” 23์ผ

๊ธฐ์ˆ  ๋ฉด์ ‘

๋ชฉ๋ก ๋ณด๊ธฐ
76/78

๐Ÿ—ฃ๏ธ Intercepting Routes๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ผ์šฐํŒ…์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

  • ์˜๋„: ์ง€์›์ž๊ฐ€ Intercepting Routes๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์šฐํŒ…์„ ์ œ์–ดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ‰๊ฐ€
  • ํŒ
    • Intercepting Routes์˜ ๊ฐœ๋…์„ ์„ค๋ช…ํ•œ๋‹ค.
    • ์ ‘๊ทผ ๋ฐฉ๋ฒ•์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ผ์šฐํŒ…์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•œ๋‹ค.
    • Intercepting Routes๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ๋ฅผ ๋– ์˜ฌ๋ ค ๋ณธ๋‹ค.
  • ์ฃผ์–ด์ง„ ๋‹ต์•ˆ (๋ชจ๋ฒ” ๋‹ต์•ˆ)

    Intercepting Routes๋Š” ์‚ฌ์šฉ์ž์˜ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์— ๋”ฐ๋ผ ๋ผ์šฐํŒ…์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
    ์ด๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ๋ผ์šฐํŒ… ๋™์ž‘์„ ์œ ์—ฐํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ฐ€์žฅ ํ”ํ•œ ํ™œ์šฉ ์‚ฌ๋ก€๋กœ๋Š” ์ธ์ฆ, ๊ถŒํ•œ ๋ถ€์—ฌ, A/B ํ…Œ์ŠคํŠธ ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ํŠน์ • ํŽ˜์ด์ง€์— ์ ‘๊ทผํ•˜๋ ค ํ•  ๋•Œ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋””๋ ‰์…˜ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋‹ค๋ฅธ ๋ฒ„์ „์˜ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•˜๋Š” A/B ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Next.js์—์„œ๋Š” ์ด ๊ธฐ๋Šฅ์„ middleware๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    Next.js์˜ middleware๋Š” ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๊ณ , ์š”์ฒญ์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋””๋ ‰์…˜ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    ์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•œ ๊ฒฝ๋กœ์— ๋Œ€ํ•ด ํŠน์ • ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“ ๊ฐœ๋… ์ •๋ฆฌ

๐ŸŒŸ Intercepting Routes๋ž€?

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์— ์žˆ๋Š” ๋ผ์šฐํŠธ๋ฅผ ํ˜„์žฌ ๋ ˆ์ด์•„์›ƒ ์•ˆ์—์„œ ๋กœ๋“œํ•ด์„œ,
์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค๋ฅธ ์ปจํ…์ŠคํŠธ๋กœ ์ด๋™ํ•˜์ง€ ์•Š๊ณ ๋„ ๊ทธ ๋ผ์šฐํŠธ ๋‚ด์šฉ์„ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค€๋‹ค.

๋Œ€ํ‘œ ์˜ˆ์‹œ๊ฐ€ ํ”ผ๋“œ โ†’ ์‚ฌ์ง„ ๋ชจ๋‹ฌ์ด๋‹ค.

  • /feed์—์„œ ์‚ฌ์ง„์„ ํด๋ฆญํ•˜๋ฉด, Next.js๊ฐ€ ์‹ค์ œ๋กœ๋Š” /photo/123 ๋ผ์šฐํŠธ๋ฅผ ์ฝ์–ด์˜ค์ง€๋งŒ,
  • URL์€ /feed ์ปจํ…์ŠคํŠธ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ /feed ์œ„์— ์‚ฌ์ง„ ์ƒ์„ธ ๋ชจ๋‹ฌ์„ ๋„์šฐ๋Š” ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

๋ฐ˜๋Œ€๋กœ

  • ๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ์ฐฝ์— ์ง์ ‘ /photo/123์„ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜,
  • /photo/123 ๋งํฌ๋ฅผ ๋ฐ”๋กœ ์—ด๊ฑฐ๋‚˜,
  • ๋ชจ๋‹ฌ ์—ด๋ฆฐ ์ƒํƒœ์—์„œ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด,

์ด๋ฒˆ์—๋Š” ๋ชจ๋‹ฌ์ด ์•„๋‹ˆ๋ผ /photo/123 ์ „์ฒด ํŽ˜์ด์ง€๊ฐ€ ๋ Œ๋”๋ง๋˜๊ณ , ์ด๋•Œ๋Š” ๊ฐ€๋กœ์ฑ„๊ธฐ(interception)๊ฐ€ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

์ฆ‰, Interception Routes์˜ ๊ฐœ๋…์„ ๋‹ค์‹œ ์ •๋ฆฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์†Œํ”„ํŠธ ๋„ค๋น„๊ฒŒ์ด์…˜(ํด๋ผ์ด์–ธํŠธ ๋‚ด ์ด๋™)์ผ ๋•Œ๋งŒ ๋‹ค๋ฅธ ๋ทฐ(๋ชจ๋‹ฌ/์˜ค๋ฒ„๋ ˆ์ด)๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ ,
ํ•˜๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜(์ง์ ‘ URL, ์ƒˆ๋กœ๊ณ ์นจ)์ผ ๋•Œ๋Š” ์›๋ž˜ ๋ผ์šฐํŠธ ์ „์ฒด ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ผ์šฐํŒ… ํŒจํ„ด


๐ŸŒŸ ์–ธ์ œ ์“ฐ๋Š” ๊ธฐ๋Šฅ์ธ๊ฐ€?

  1. URL์ด ์žˆ๋Š” ๋ชจ๋‹ฌ/์˜ค๋ฒ„๋ ˆ์ด๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ
    • ๋กœ๊ทธ์ธ ๋ชจ๋‹ฌ, ์‚ฌ์ง„ ์ƒ์„ธ ๋ชจ๋‹ฌ, ์ƒํ’ˆ ์ƒ์„ธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์‚ฌ์ด๋“œ๋ฐ” ๋“ฑ
    • ์‚ฌ์šฉ์ž๊ฐ€ ๊ณต์œ  ๊ฐ€๋Šฅํ•œ URL(/login, /photo/[id], /products/[id])์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  1. ํ˜„์žฌ ํŽ˜์ด์ง€ ์ปจํ…์ŠคํŠธ(๋ชฉ๋ก/ํ”ผ๋“œ)๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ์ƒ์„ธ๋ฅผ ๋„์šฐ๊ณ  ์‹ถ์„ ๋•Œ
    • /feed์—์„œ ์‚ฌ์ง„ ๋ชจ๋‹ฌ์„ ์—ด์–ด๋„ ๋’ค์— ํ”ผ๋“œ๊ฐ€ ์œ ์ง€๋œ๋‹ค.
    • ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋‹ฌ์„ ๋‹ซ์•˜์„ ๋•Œ "์ด์ „ ํŽ˜์ด์ง€๋กœ ์ด๋™"์ด ์•„๋‹ˆ๋ผ, ํ”ผ๋“œ๋งŒ ๋‚จ๊ฒŒ ๋˜๋Š” UX๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์ข‹๋‹ค.
  1. ๋’ค๋กœ/์•ž์œผ๋กœ ์ด๋™๊ณผ ๋ชจ๋‹ฌ์˜ ์—ฐ๋™
    • ๋’ค๋กœ ๊ฐ€๊ธฐ โ†’ ๋ชจ๋‹ฌ์ด ๋‹ซํžˆ๊ณ , ํ”ผ๋“œ๋Š” ์œ ์ง€๋œ๋‹ค.
    • ๋‹ค์‹œ ์•ž์œผ๋กœ ๊ฐ€๊ธฐ โ†’ ๋ชจ๋‹ฌ์ด ๋‹ค์‹œ ์—ด๋ฆฐ๋‹ค.
  1. SEO & ์ ‘๊ทผ์„ฑ ์ธก๋ฉด
    • /photo/[id], /login ๊ฐ™์€ ์‹ค์ œ ํŽ˜์ด์ง€๊ฐ€ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—,
    • ๊ฒ€์ƒ‰ ์—”์ง„์ด๋‚˜ JS ๋น„ํ™œ์„ฑํ™” ํ™˜๊ฒฝ์—์„œ๋Š” ๋ชจ๋‹ฌ์ด ์•„๋‹Œ ์ „์ฒด ํŽ˜์ด์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๐ŸŒŸ ์„ธ๊ทธ๋จผํŠธ, ์Šฌ๋กฏ

๋ณธ๊ฒฉ์ ์œผ๋กœ Intercepting Routes์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ธฐ ์ „์—, ์„ธ๊ทธ๋จผํŠธ(segment)์™€ ์Šฌ๋กฏ(slot)์˜ ๊ฐœ๋…์„ ์ •๋ฆฌํ•ด ๋ณด์ž.

  1. ์„ธ๊ทธ๋จผํŠธ (Segment)
    • ์ •์˜
      • ํด๋” ํ•˜๋‚˜๊ฐ€ URL์˜ ๊ฒฝ๋กœ ์กฐ๊ฐ(segment) ํ•˜๋‚˜์— ๋Œ€์‘๋œ๋‹ค.
      • ์ฆ‰, app ํด๋” ์•„๋ž˜์˜ ๊ฐ ํด๋”๊ฐ€ ๋ผ์šฐํŠธ ์„ธ๊ทธ๋จผํŠธ(route segment)์ด๊ณ , ์ด๊ฒŒ ๊ทธ๋Œ€๋กœ URL ์„ธ๊ทธ๋จผํŠธ๋กœ ๋งคํ•‘๋œ๋‹ค.
    • ์˜ˆ์‹œ
      app
      โ”œ page.tsx              ->  URL: /
      โ”œ feed
      โ”‚  โ”” page.tsx           ->  URL: /feed
      โ”” photo
         โ”” [id]
            โ”” page.tsx        ->  URL: /photo/:id
      • feed ํด๋” โžก๏ธ feed ์„ธ๊ทธ๋จผํŠธ โžก๏ธ URL /feed
      • photo ํด๋” โžก๏ธ photo ์„ธ๊ทธ๋จผํŠธ โžก๏ธ URL /photo
      • [id] ํด๋” โžก๏ธ ๋™์  ์„ธ๊ทธ๋จผํŠธ โžก๏ธ URL /photo/123์—์„œ 123 ๋ถ€๋ถ„
    • ์„ธ๊ทธ๋จผํŠธ = ๊ฒฝ๋กœ(URL) ํ•œ ์นธ์— ๋Œ€์‘๋˜๋Š” ํด๋” ์ด๋ฆ„
    • URL /blog/[slug]๋Š” ์„ธ๊ทธ๋จผํŠธ 3๊ฐœ๋กœ ๊ตฌ์„ฑ๋จ
      • / (๋ฃจํŠธ)
      • blog
      • [slug] (๋™์  ์„ธ๊ทธ๋จผํŠธ)
    • ์—ฌ๊ธฐ์„œ app์€ ์„ธ๊ทธ๋จผํŠธ๊ฐ€ ์•„๋‹ˆ๊ณ  ๊ทธ๋ƒฅ ๋ฃจํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ์ด๋‹ค.
    • ๊ทธ๋ž˜์„œ "feed ์„ธ๊ทธ๋จผํŠธ"๋ผ๊ณ  ํ•˜๋ฉด app/feed ํด๋”์™€ URL /feed ์กฐํ•ฉ์„ ํ†ตํ‹€์–ด ๋งํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  1. ์Šฌ๋กฏ (Slot)
    • ์ •์˜
      • Parallel Routes๋ฅผ ์œ„ํ•ด ๋งŒ๋“  ์ด๋ฆ„ ๋ถ™์€ ์˜์—ญ
      • @ํด๋”๋ช…์œผ๋กœ ํด๋”๋ฅผ ๋งŒ๋“ค๋ฉด ํ•˜๋‚˜์˜ ์Šฌ๋กฏ์ด ๋˜๊ณ , ๊ฐ™์€ ๋ ˆ๋ฒจ์˜ layout.tsx๋กœ props์ฒ˜๋Ÿผ ์ „๋‹ฌ๋˜์–ด ํ•จ๊ป˜ ๋ Œ๋”๋ง๋œ๋‹ค.
    • ์˜ˆ์‹œ
      app
      โ”œ layout.tsx
      โ”œ page.tsx                 // ๊ธฐ๋ณธ children
      โ”œ @analytics
      โ”‚  โ”” page.tsx              // analytics ์Šฌ๋กฏ
      โ”” @team
         โ”” page.tsx              // team ์Šฌ๋กฏ
      • @analytics โžก๏ธ analytics ์Šฌ๋กฏ
      • @team โžก๏ธ team ์Šฌ๋กฏ
      • app/layout.tsx๋Š” ์ด๋Ÿฐ ์‹์œผ๋กœ props๋ฅผ ๋ฐ›๋Š”๋‹ค.
        // app/layout.tsx
        export default function RootLayout({
          children,
          analytics,
          team,
        }: {
          children: React.ReactNode; // ๊ธฐ๋ณธ ๋ผ์šฐํŠธ
          analytics: React.ReactNode; // @analytics ์Šฌ๋กฏ
          team: React.ReactNode; // @team ์Šฌ๋กฏ
        }) {
          return (
            <html>
              <body>
                <header>ํ—ค๋”</header>
                <aside>{analytics}</aside>
                <main>{children}</main>
                <aside>{team}</aside>
              </body>
            </html>
          );
        }
    • ํŠน์ง•
      1. ์Šฌ๋กฏ ์ด๋ฆ„(@analytics, @team)์€ URL์— ์•ˆ ์ฐํžŒ๋‹ค.
        • ์Šฌ๋กฏ์€ ๋ผ์šฐํŠธ ์„ธ๊ทธ๋จผํŠธ๊ฐ€ ์•„๋‹ˆ๊ณ , URL ๊ตฌ์กฐ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.
        • ์˜ˆ: /@team/members๋Š” ์‹ค์ œ๋กœ /members๋กœ ์ ‘๊ทผ๋œ๋‹ค.
      2. ์Šฌ๋กฏ์€ ๋ ˆ์ด์•„์›ƒ ์•ˆ์˜ ์—ฌ๋Ÿฌ ์˜์—ญ์„ ๋ณ‘๋ ฌ๋กœ ๋ Œ๋”๋งํ•  ๋•Œ ์“ฐ๋Š” ์šฉ๋„์ด๋‹ค.
        • ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์™ผ์ชฝ์— ํŒ€ ๋ชฉ๋ก, ์˜ค๋ฅธ์ชฝ์— ๋ถ„์„ ์ฐจํŠธ ๊ฐ™์€ ๊ฑธ ๋™์‹œ์— ๋ณด์—ฌ์ฃผ๋Š” ๊ฒฝ์šฐ
        • ๋ฉ”์ธ ์ปจํ…์ธ  + ๋ชจ๋‹ฌ ์ปจํ…์ธ ๋ฅผ ๋‚˜๋ˆŒ ๋•Œ (children + @modal ์Šฌ๋กฏ)

๐ŸŒŸ ๊ธฐ๋ณธ ๊ทœ์น™

Intercepting Routes๋Š” ํด๋” ์ด๋ฆ„ ๊ทœ์น™์œผ๋กœ ์ •์˜ํ•œ๋‹ค.

  • (.): ๊ฐ™์€ ๋ ˆ๋ฒจ์˜ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ฐ€๋กœ์ฑ”
  • (..): ํ•œ ๋ ˆ๋ฒจ ์œ„์˜ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ฐ€๋กœ์ฑ”
  • (..)(..): ๋‘ ๋ ˆ๋ฒจ ์œ„์˜ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ฐ€๋กœ์ฑ”
  • (...): app ๋ฃจํŠธ(app) ๊ธฐ์ค€์œผ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ฐ€๋กœ์ฑ”

์ด ๊ทœ์น™์€ ํŒŒ์ผ ์‹œ์Šคํ…œ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ผ์šฐํŠธ ์„ธ๊ทธ๋จผํŠธ ๊ธฐ์ค€์ด๋‹ค.
@modal, @auth ๊ฐ™์€ Parallel Routes์˜ slot ํด๋”๋Š” ์„ธ๊ทธ๋จผํŠธ๋กœ ์ทจ๊ธ‰๋˜์ง€ ์•Š๋Š”๋‹ค.

๊ทธ๋ž˜์„œ ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ ˆ๋ฒจ์ด ๋‘ ์นธ ์œ„์—ฌ๋„, ์„ธ๊ทธ๋จผํŠธ ๊ด€์ ์—์„œ๋Š” ํ•œ ๋‹จ๊ณ„ ์œ„์ผ ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ ์ด ๊ธฐ๋Šฅ์€ App Router(/app)๋ฅผ ์œ„ํ•œ ๋ผ์šฐํŒ… ๋ฌธ์„œ์—๋งŒ ํฌํ•จ๋˜์–ด ์žˆ์–ด์„œ, Pages Router(/pages)์—์„œ๋Š” ์“ธ ์ˆ˜ ์—†๋‹ค.


๐ŸŒŸ ๋™์ž‘ ๋ฐฉ์‹

Intercepting Routes ๋™์ž‘์„ ์ดํ•ดํ•  ๋•Œ, Next.js์˜ ๋„ค๋น„๊ฒŒ์ด์…˜ ํƒ€์ž…์„ ๊ฐ™์ด ๋ณด๋Š” ๊ฒŒ ์ข‹๋‹ค.

  1. ์†Œํ”„ํŠธ ๋„ค๋น„๊ฒŒ์ด์…˜ (client-side, partial render)
    • <Link href="/photo/123"> ํด๋ฆญ์ด๋‚˜, router.push('/photo/123') ํ˜ธ์ถœ ๊ฐ™์€ ํด๋ผ์ด์–ธํŠธ ๋‚ด ์ด๋™์ผ ๋•Œ,
    • Next.js๋Š” ํ˜„์žฌ ๋ ˆ์ด์•„์›ƒ ์ปจํ…์ŠคํŠธ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ, ํ•ด๋‹น ์„ธ๊ทธ๋จผํŠธ์— ๋Œ€ํ•ด Intercepting Route์šฉ UI(์˜ˆ: ๋ชจ๋‹ฌ)๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค.
    • ์ด๋•Œ URL์€ /photo/123์œผ๋กœ ๋ฐ”๋€” ์ˆ˜๋„ ์žˆ๊ณ , ํ˜น์€ URL์„ /feed๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‚ด๋ถ€์ ์œผ๋กœ๋งŒ ๊ฐ€๋กœ์ฑŒ ์ˆ˜๋„ ์žˆ๋‹ค.
  1. ํ•˜๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜ (full-page load)
    • ๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ์ฐฝ์— URL์„ ์ง์ ‘ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜, ์™ธ๋ถ€์—์„œ /photo/123 ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๊ฑฐ๋‚˜, ์ƒˆ๋กœ๊ณ ์นจ(F5)๊ณผ ๊ฐ™์€ ํ•˜๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜์ผ ๋•Œ,
    • Next.js๋Š” intercepting์„ ๋ฌด์‹œํ•˜๊ณ , ํ•ด๋‹น ๋ผ์šฐํŠธ์˜ ์ •์‹ ํŽ˜์ด์ง€(app/photo/[id]/page.tsx ๋“ฑ)๋ฅผ ํ†ต์งธ๋กœ ๋ Œ๋”๋งํ•œ๋‹ค.
  • ํ”ผ๋“œ์—์„œ ์นด๋“œ ํด๋ฆญ โžก๏ธ ๋ชจ๋‹ฌ (Intercepted)
  • ์ฃผ์†Œ์ฐฝ์—์„œ ์ง์ ‘ /photo/123 ์ž…๋ ฅ โžก๏ธ ์ „์ฒด ํŽ˜์ด์ง€ (Non-intercepted)

๐ŸŒŸ ๊ตฌํ˜„ ์˜ˆ์ œ 1: /feed + /photo/[id] ๋ชจ๋‹ฌ

  • ๊ธฐ๋ณธ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ
    app
    โ”œโ”€ feed
    โ”‚  โ”œโ”€ page.tsx                 // ํ”ผ๋“œ ๋ชฉ๋ก ํŽ˜์ด์ง€
    โ”‚  โ””โ”€ (..)photo
    โ”‚     โ””โ”€ [id]
    โ”‚        โ””โ”€ page.tsx           // ์ธํ„ฐ์…‰ํŠธ๋œ ์‚ฌ์ง„ ๋ชจ๋‹ฌ์šฉ ํŽ˜์ด์ง€
    โ””โ”€ photo
       โ””โ”€ [id]
          โ””โ”€ page.tsx              // ์ „์ฒด ์‚ฌ์ง„ ์ƒ์„ธ ํŽ˜์ด์ง€ 
    • app/photo/[id]/page.tsx: ์›๋ž˜์˜ ์ „์ฒด ์ƒ์„ธ ํŽ˜์ด์ง€
    • app/feed/(..)photo/[id]/page.tsx: /feed ์ปจํ…์ŠคํŠธ ์•ˆ์—์„œ ๊ฐ€๋กœ์ฑ„์„œ ๋ณด์—ฌ์ค„ ๋ฒ„์ „ (๋ณดํ†ต ๋ชจ๋‹ฌ UI)
    • (..)photo: feed ์„ธ๊ทธ๋จผํŠธ ์•ˆ์—์„œ, ํ•œ ๋‹จ๊ณ„ ์œ„(..)๋กœ ์˜ฌ๋ผ๊ฐ€์„œ(app), ๊ทธ ๋ถ€๋ชจ ์•„๋ž˜์— ์žˆ๋Š” photo ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ฐ€๋กœ์ฑˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ

    • /feed/page.tsx: ์นด๋“œ์—์„œ /photo/[id]๋กœ ์ด๋™

      // app/feed/page.tsx
      import Link from "next/link";
      
      const photos = [
        { id: "1", title: "์‚ฌ์ง„ 1" },
        { id: "2", title: "์‚ฌ์ง„ 2" },
      ];
      
      export default function FeedPage() {
        return (
          <main>
            <h1>ํ”ผ๋“œ</h1>
            <ul>
              {photos.map((p) => (
                <li key={p.id}>
                  <Link href={`/photo/${p.id}`}>{p.title}</Link>
                </li>
              ))}
            </ul>
          </main>
        );
      }
    • /photo/[id]/page.tsx: ์ „์ฒด ํŽ˜์ด์ง€ ๋ฒ„์ „

      // app/photo/[id]/page.tsx
      interface Props {
        params: { id: string };
      }
      
      export default function PhotoPage({ params }: Props) {
        return (
          <main>
            <h1>์‚ฌ์ง„ ์ „์ฒด ํŽ˜์ด์ง€</h1>
            <p>ID: {params.id}</p>
            {/* ์ง„์งœ๋ผ๋ฉด ์—ฌ๊ธฐ์„œ fetch ํ•ด์„œ ์ด๋ฏธ์ง€/๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ณด์—ฌ์คŒ */}
          </main>
        );
      }
    • /feed/(..)photo/[id]/page.tsx: ํ”ผ๋“œ ์œ„์— ์–น๋Š” ๋ชจ๋‹ฌ ๋ฒ„์ „

      // app/feed/(..)photo/[id]/page.tsx
      "use client";
      
      import { useRouter } from "next/navigation";
      
      interface Props {
        params: { id: string };
      }
      
      export default function PhotoModalPage({ params }: Props) {
        const router = useRouter();
        
        const close = () => {
          router.back(); // ๋’ค๋กœ ๊ฐ€๊ธฐ = ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ
        };
        
        return (
          <div
            style={{
              position: "fixed",
              inset: 0,
              background: "rgba(0,0,0,0.5)",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
            }}
          >
            <div style={{ background: "white", padding: 24 }}>
              <button onClick={close}>๋‹ซ๊ธฐ</button>
              <h1>์‚ฌ์ง„ ๋ชจ๋‹ฌ (ID: {params.id})</h1>
            </div>
          </div>
        );
      }
      • ํ”ผ๋“œ์—์„œ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋ฉด(์†Œํ”„ํŠธ ๋„ค๋น„๊ฒŒ์ด์…˜),
        • /photo/[id]๋กœ ์ด๋™ํ•˜์ง€๋งŒ
        • Next.js๊ฐ€ app/feed/(..)photo/[id]/page.tsx๋ฅผ ์‚ฌ์šฉํ•ด์„œ /feed ๋ ˆ์ด์•„์›ƒ ์•ˆ์— ๋ชจ๋‹ฌ์„ ๋ Œ๋”๋งํ•œ๋‹ค.
      • ์ง์ ‘ /photo/[id]๋กœ ๋“ค์–ด๊ฐ€๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด,
        • app/photo/[id]/page.tsx ์ „์ฒด ํŽ˜์ด์ง€๊ฐ€ ๋ Œ๋”๋ง๋œ๋‹ค.
      • ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ์ด ๋ชจ๋‹ฌ์„ Parallel Routes์˜ @modal ์Šฌ๋กฏ๊ณผ ์กฐํ•ฉํ•ด์„œ ๋” ๊น”๋”ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํŒจํ„ด์„ ๋งŽ์ด ์“ด๋‹ค.

๐ŸŒŸ ๊ตฌํ˜„ ์˜ˆ์ œ 2: /login ๋ชจ๋‹ฌ (Parallel Routes + Intercepting)

  • /login ์ •์ƒ ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ

    // app/login/page.tsx
    import { Login } from "@/app/ui/login";
    
    export default function Page() {
      return <Login />;
    }
    • ์ด๊ฒŒ ์ƒˆ๋กœ๊ณ ์นจ/์ง์ ‘์ ‘์† ์‹œ ๋ณด์ด๋Š” ์ „์ฒด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์ด๋‹ค.
  • @auth ์Šฌ๋กฏ + default ๋งŒ๋“ค๊ธฐ

    // app/@auth/(.)login/page.tsx
    import { Modal } from "@/app/ui/modal";
    import { Login } from "@/app/ui/login";
    
    export default function Page() {
      return (
        <Modal>
          <Login />
        </Modal>
      );
    }
    • ํด๋” ์ด๋ฆ„์ด @auth/(.)login์ธ ์ ์ด ํ•ต์‹ฌ์ด๋‹ค.
    • (.)login์€ ๊ฐ™์€ ๋ ˆ๋ฒจ์˜ login ์„ธ๊ทธ๋จผํŠธ๋ฅผ ๊ฐ€๋กœ์ฑˆ๋‹ค๋Š” ๋œป์ด๋‹ค.
    • @auth๋Š” ์Šฌ๋กฏ(slot)์ด๋ผ ์„ธ๊ทธ๋จผํŠธ ๋ ˆ๋ฒจ ๊ณ„์‚ฐ์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋ ˆ์ด์•„์›ƒ์—์„œ @auth ์Šฌ๋กฏ ๋ Œ๋”๋ง

    // app/layout.tsx
    import Link from "next/link";
    
    export default function RootLayout({
      auth,
      children,
    }: {
      auth: React.ReactNode;
      children: React.ReactNode;
    }) {
      return (
        <html lang="ko">
          <body>
            <nav>
              <Link href="/login">๋กœ๊ทธ์ธ</Link>
            </nav>
            
            {/* ๋ชจ๋‹ฌ ์Šฌ๋กฏ */}
            {auth}
            
            {/* ์‹ค์ œ ํŽ˜์ด์ง€ */}
            {children}
          </body>
        </html>
      );
    }
    • /์—์„œ "๋กœ๊ทธ์ธ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด(์†Œํ”„ํŠธ ๋„ค๋น„๊ฒŒ์ด์…˜),
      • /login์œผ๋กœ ์ด๋™ + @auth/(.)login/page.tsx๊ฐ€ ๊ฐ€๋กœ์ฑ„์„œ <Modal><Login /></Modal> ๋ Œ๋”
      • URL์€ /login, ํ™”๋ฉด์€ ํ˜„์žฌ ํŽ˜์ด์ง€ + ๋กœ๊ทธ์ธ ๋ชจ๋‹ฌ
    • /login์„ ์ฃผ์†Œ์ฐฝ์— ์ง์ ‘ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด(ํ•˜๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜),
      • app/login/page.tsx์˜ ์ „์ฒด ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋ Œ๋”๋œ๋‹ค.
    • ๋ชจ๋‹ฌ์˜ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์—์„œ router.back()์„ ํ˜ธ์ถœํ•˜๋ฉด,
      • ๋’ค๋กœ๊ฐ€๊ธฐ ํžˆ์Šคํ† ๋ฆฌ๋กœ ๋Œ์•„๊ฐ€๋ฉฐ ๋ชจ๋‹ฌ์ด ๋‹ซํžŒ๋‹ค.
    • ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•  ๋•Œ๋Š” @auth์—์„œ null์„ ๋ฐ˜๋ณตํ•˜๋Š” ๋ผ์šฐํŠธ(์˜ˆ: @auth/page.tsx๋‚˜ @auth/[...catchAll]/page.tsx)๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ชจ๋‹ฌ์„ ๋ช…์‹œ์ ์œผ๋กœ ๋‹ซ์•„์•ผ ํ•œ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€