๐ŸŽญ Next.js ์‹ฌํ™” 5์žฅ: Parallel Routes & Intercepting Routes โ€” URL์ด ์‚ด์•„์žˆ๋Š” ๋ชจ๋‹ฌ์˜ ๋น„๋ฐ€

2026๋…„ 4์›” 30์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

Parallel Routes์™€ Intercepting Routes์˜ ๊ณ ๊ธ‰ ํ™œ์šฉ โ€” ๋ณต์žกํ•œ ๋ชจ๋‹ฌยทํƒญ UI ๊ตฌํ˜„ ์ „๋žต์ž…๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 18๋ถ„ (์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 10๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ฒ (์‹ ์ž…): "์ธ์Šคํƒ€๊ทธ๋žจ ํ”ผ๋“œ์—์„œ ์‚ฌ์ง„ ํด๋ฆญํ•˜๋ฉด URL์ด /p/ABC123์œผ๋กœ ๋ฐ”๋€Œ๋ฉด์„œ ๋ชจ๋‹ฌ์ด ๋œจ๋Š”๋ฐ, ๊ทธ ์ƒํƒœ๋กœ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ๋ชจ๋‹ฌ ์—†์ด ์ „์ฒด ํŽ˜์ด์ง€๋กœ ์—ด๋ฆฌ์ž–์•„์š”. ์ด๊ฑฐ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ๊ฑฐ์˜ˆ์š”? useState๋กœ ๋ชจ๋‹ฌ ์—ด๊ณ  ๋‹ซ๋Š” ๊ฑด URL์ด ์•ˆ ๋ฐ”๋€Œ์–ด์„œ ๋งํฌ ๊ณต์œ ๊ฐ€ ์•ˆ ๋˜๊ณ ..."
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "๋ฐ”๋กœ Parallel Routes + Intercepting Routes ์กฐํ•ฉ์ด์—์š”. @modal ์Šฌ๋กฏ๊ณผ (..) ์ธํ„ฐ์…‰ํ„ฐ๋ฅผ ์กฐํ•ฉํ•˜๋ฉด, ๊ฐ™์€ URL์—์„œ ๋„ค๋น„๊ฒŒ์ด์…˜ํ–ˆ์„ ๋•Œ๋Š” ๋ชจ๋‹ฌ๋กœ, ์ง์ ‘ ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจํ–ˆ์„ ๋•Œ๋Š” ์ „์ฒด ํŽ˜์ด์ง€๋กœ ์ž๋™ ๋ถ„๊ธฐ๋ผ์š”. URL ๊ณต์œ ๋„ ๋˜๊ณ , ๋’ค๋กœ๊ฐ€๊ธฐ๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ณ ."

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
Parallel Routes(์Šฌ๋กฏ ๊ฐœ๋…) โ†’ Intercepting Routes(URL ๊ฐ€๋กœ์ฑ„๊ธฐ) โ†’ ๋‘ ๊ธฐ๋Šฅ ์กฐํ•ฉํ•œ URL ๋ชจ๋‹ฌ

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • @folder ๋ฌธ๋ฒ•์œผ๋กœ ๋ ˆ์ด์•„์›ƒ์— ๋…๋ฆฝ์ ์ธ ์Šฌ๋กฏ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค
  • (..)route ๋ฌธ๋ฒ•์œผ๋กœ URL์€ ์œ ์ง€ํ•˜๋ฉด์„œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค
  • Instagram/Pinterest ์Šคํƒ€์ผ์˜ URL์ด ์‚ด์•„์žˆ๋Š” ๋ชจ๋‹ฌ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€

์ผ๋ฐ˜์ ์ธ ๋ชจ๋‹ฌ ๊ตฌํ˜„์˜ ๋ฌธ์ œ์ ์„ ์ƒ๊ฐํ•ด๋ด:

// ๊ธฐ์กด ๋ฐฉ์‹: useState๋กœ ๋ชจ๋‹ฌ ์ œ์–ด
const [isOpen, setIsOpen] = useState(false)
<button onClick={() => setIsOpen(true)}>๊ฒŒ์‹œ๊ธ€ ๋ณด๊ธฐ</button>
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
  <PostDetail />
</Modal>

์ด ๋ฐฉ์‹์˜ ๋ฌธ์ œ:

  1. URL์ด ์•ˆ ๋ฐ”๋€Œ์–ด โ€” ๋ชจ๋‹ฌ์„ ์นœ๊ตฌ์—๊ฒŒ ๊ณต์œ ํ•  ์ˆ˜ ์—†์Œ
  2. ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์‚ฌ๋ผ์ ธ โ€” UX ์ผ๊ด€์„ฑ ์—†์Œ
  3. ๋’ค๋กœ๊ฐ€๊ธฐ๊ฐ€ ์ด์ƒํ•ด โ€” ๋ชจ๋‹ฌ ๋‹ซ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ด์ „ ํŽ˜์ด์ง€๋กœ ๊ฐ€๋ฒ„๋ฆผ
  4. SEO ๋ถˆ๊ฐ€ โ€” ๋ชจ๋‹ฌ ๋‚ด์šฉ์ด ํฌ๋กค๋ง ์•ˆ ๋จ

Parallel + Intercepting Routes ์กฐํ•ฉ์ด ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ:

  • ๋ชจ๋‹ฌ์ด ์—ด๋ฆด ๋•Œ URL์ด ์‹ค์ œ๋กœ ๋ณ€๊ฒฝ๋จ (/posts โ†’ /posts/123)
  • URL์„ ์ง์ ‘ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ „์ฒด ํŽ˜์ด์ง€๋กœ ์—ด๋ฆผ
  • ๋’ค๋กœ๊ฐ€๊ธฐํ•˜๋ฉด ๋ชจ๋‹ฌ๋งŒ ๋‹ซํž˜ (ํ”ผ๋“œ๋กœ ๋Œ์•„์˜ด)
  • ๋ชจ๋‹ฌ URL์„ ๊ณต์œ ํ•˜๋ฉด ๋ฐ›์€ ์‚ฌ๋žŒ๋„ ํ•ด๋‹น ๊ฒŒ์‹œ๊ธ€์— ์ง์ ‘ ์ ‘๊ทผ ๊ฐ€๋Šฅ

๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?
ํšŒ์‚ฌ ๊ฑด๋ฌผ์— ๋น„์œ ํ•˜๋ฉด: ๋ณดํ†ต ์‚ฌ๋ฌด์‹ค(page.tsx)์€ ํ•œ ์ธต์— ํ•˜๋‚˜์•ผ.
๊ทธ๋Ÿฐ๋ฐ Parallel Routes๋Š” ๊ฐ™์€ ์ธต์— ๋ฐฉ์ด ์—ฌ๋Ÿฌ ๊ฐœ์•ผ. ํŒ€์žฅ ๋ฐฉ์ด๋ž‘ ํšŒ์˜์‹ค์ด ๋™์‹œ์— ์—ด๋ ค์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ.

Intercepting์€ "ํšŒ์˜์‹ค ์˜ˆ์•ฝ"์ด์•ผ. ๋ณดํ†ต์€ 3์ธต ํšŒ์˜์‹ค ์ง์ ‘ ์ฐพ์•„๊ฐ€์•ผ ํ•ด. ๊ทผ๋ฐ ์˜ˆ์•ฝ ์‹œ์Šคํ…œ์„ ํ†ตํ•˜๋ฉด ํ˜„์žฌ ์žˆ๋Š” ๊ณณ(2์ธต ์‚ฌ๋ฌด์‹ค)์—์„œ ํ™”์ƒ ํšŒ์˜(๋ชจ๋‹ฌ)๋ฅผ ํ•  ์ˆ˜ ์žˆ์–ด. ๊ฐ™์€ ๋ฐฉ ๋ฒˆํ˜ธ(URL)์ธ๋ฐ ๋ณด์ด๋Š” ๊ฒƒ์ด ๋‹ฌ๋ผ.

Parallel Routes ์‹œ๊ฐํ™”:

app/
  layout.tsx          โ† { children } + { @team } + { @analytics } ์„ธ ์Šฌ๋กฏ์„ ๋™์‹œ์— ๋ Œ๋”
  page.tsx            โ† children ์Šฌ๋กฏ
  @team/
    page.tsx          โ† @team ์Šฌ๋กฏ ๋‚ด์šฉ
  @analytics/
    page.tsx          โ† @analytics ์Šฌ๋กฏ ๋‚ด์šฉ

Intercepting Routes ๊ธฐํ˜ธ:

๊ธฐํ˜ธ์˜๋ฏธ
(.)folder๊ฐ™์€ ๋ ˆ๋ฒจ ์ธํ„ฐ์…‰ํŠธ
(..)folderํ•œ ๋‹จ๊ณ„ ์œ„ ์ธํ„ฐ์…‰ํŠธ
(..)(..)folder๋‘ ๋‹จ๊ณ„ ์œ„ ์ธํ„ฐ์…‰ํŠธ
(...)folder๋ฃจํŠธ๋ถ€ํ„ฐ ์ธํ„ฐ์…‰ํŠธ

๐Ÿงฉ Parallel Routes โ€” ํ•˜๋‚˜์˜ ํ™”๋ฉด์— ์—ฌ๋Ÿฌ ์Šฌ๋กฏ ๋ฐฐ์น˜ํ•˜๊ธฐ ๐ŸŸข

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • @slot ํด๋” ๋ฌธ๋ฒ•์œผ๋กœ ๋ ˆ์ด์•„์›ƒ์— ๋…๋ฆฝ์ ์ธ ๋ณ‘๋ ฌ ์˜์—ญ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค
  • default.tsx๊ฐ€ ์™œ ํ•„์š”ํ•œ์ง€ ์ดํ•ดํ•œ๋‹ค

Parallel Routes๋Š” ํ•˜๋‚˜์˜ URL์—์„œ ์—ฌ๋Ÿฌ ๋…๋ฆฝ์ ์ธ ์˜์—ญ(์Šฌ๋กฏ)์„ ๋™์‹œ์— ๋ Œ๋”๋งํ•˜๋Š” ๊ธฐ๋Šฅ์ด์•ผ.

// app/(dashboard)/layout.tsx
// @team, @analytics ๋‘ ์Šฌ๋กฏ์„ props๋กœ ๋ฐ›์Œ
 
interface DashboardLayoutProps {
  children: React.ReactNode
  team: React.ReactNode       // @team ์Šฌ๋กฏ
  analytics: React.ReactNode  // @analytics ์Šฌ๋กฏ
}
 
export default function DashboardLayout({
  children,
  team,
  analytics,
}: DashboardLayoutProps) {
  return (
    <div className="dashboard">
      <main>{children}</main>
      <aside className="team-panel">{team}</aside>
      <section className="analytics-panel">{analytics}</section>
    </div>
  )
}
app/(dashboard)/
  layout.tsx           โ† ์œ„์˜ ๋ ˆ์ด์•„์›ƒ
  page.tsx             โ† children ์Šฌ๋กฏ (๊ธฐ๋ณธ ๋Œ€์‹œ๋ณด๋“œ ๋‚ด์šฉ)
  @team/
    page.tsx           โ† team ์Šฌ๋กฏ ๋‚ด์šฉ (ํŒ€์› ๋ชฉ๋ก)
    default.tsx        โ† โš ๏ธ ํ•„์ˆ˜! ์Šฌ๋กฏ์— ๋งค์นญ ํŽ˜์ด์ง€ ์—†์„ ๋•Œ fallback
  @analytics/
    page.tsx           โ† analytics ์Šฌ๋กฏ ๋‚ด์šฉ (ํ†ต๊ณ„)
    default.tsx        โ† โš ๏ธ ํ•„์ˆ˜!

default.tsx๊ฐ€ ์™œ ํ•„์ˆ˜์ธ๊ฐ€:

// app/(dashboard)/@team/default.tsx
// ์ด ํŒŒ์ผ์ด ์—†์œผ๋ฉด: ๋‹ค๋ฅธ ์Šฌ๋กฏ๋งŒ ์žˆ๋Š” URL๋กœ ๋„ค๋น„๊ฒŒ์ด์…˜ํ•  ๋•Œ Next.js๊ฐ€
// "์ด ์Šฌ๋กฏ์— ๋ญ˜ ๋ Œ๋”ํ•ด์•ผ ํ•˜์ง€?"๋ฅผ ๋ชจ๋ฅด๊ณ  ์—๋Ÿฌ๋ฅผ ๋ƒ„
 
export default function TeamDefault() {
  return null  // ๋˜๋Š” ๋กœ๋”ฉ ์Šค์ผˆ๋ ˆํ†ค, ๊ธฐ๋ณธ ์ปจํ…์ธ  ๋“ฑ
}

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
Parallel Routes์˜ @slot์€ ๋ ˆ์ด์•„์›ƒ์— ๋…๋ฆฝ์ ์ธ "ํ™”๋ฉด ๊ตฌ์—ญ"์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฑฐ์•ผ. ๊ฐ ๊ตฌ์—ญ์€ ์ž์ฒด ๋กœ๋”ฉ, ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์–ด.


๐ŸŽฏ Intercepting Routes โ€” URL์€ ์œ ์ง€ํ•˜๊ณ  ์ปจํ…์ŠคํŠธ๋งŒ ๋ฐ”๊พธ๊ธฐ ๐ŸŸก

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • (..)route ๋ฌธ๋ฒ•์œผ๋กœ ๋„ค๋น„๊ฒŒ์ด์…˜ ์‹œ URL์„ "๊ฐ€๋กœ์ฑ„์„œ" ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค
  • ์ง์ ‘ ์ ‘๊ทผ(์ƒˆ๋กœ๊ณ ์นจ)๊ณผ ๋„ค๋น„๊ฒŒ์ด์…˜ ์‹œ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋˜๋Š” ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•œ๋‹ค

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
/posts/123์œผ๋กœ ๋งํฌ ํด๋ฆญ ์‹œ์—๋Š” ๋ชจ๋‹ฌ๋กœ, URL ์ง์ ‘ ์ž…๋ ฅ ์‹œ์—๋Š” ์ „์ฒด ํŽ˜์ด์ง€๋กœ ๋ณด์—ฌ์ฃผ๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?

Intercepting Routes๋Š” Next.js ๋‚ด๋ถ€์—์„œ ๋„ค๋น„๊ฒŒ์ด์…˜ํ•  ๋•Œ URL์„ ๊ฐ€๋กœ์ฑ„์„œ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ธฐ๋Šฅ์ด์•ผ.

app/
  posts/
    page.tsx              โ† ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก (/posts)
    [id]/
      page.tsx            โ† ๊ฒŒ์‹œ๊ธ€ ์ „์ฒด ํŽ˜์ด์ง€ (/posts/123 ์ง์ ‘ ์ ‘๊ทผ ์‹œ)
  @modal/
    (.)posts/             โ† (.): ๊ฐ™์€ ๋ ˆ๋ฒจ(/posts) ์ธํ„ฐ์…‰ํŠธ
      [id]/
        page.tsx          โ† ๊ฒŒ์‹œ๊ธ€ ๋ชจ๋‹ฌ (๋ชฉ๋ก์—์„œ ํด๋ฆญ ์‹œ, URL์€ /posts/123)
    default.tsx           โ† ๋ชจ๋‹ฌ ์Šฌ๋กฏ ๊ธฐ๋ณธ๊ฐ’ (๋ชจ๋‹ฌ ์—†์„ ๋•Œ)
  layout.tsx              โ† { children } + { @modal } ์Šฌ๋กฏ ํฌํ•จ

์‹ค์ œ ๋™์ž‘ ํ๋ฆ„:

์‹œ๋‚˜๋ฆฌ์˜ค 1: /posts ํŽ˜์ด์ง€์—์„œ ๊ฒŒ์‹œ๊ธ€ ํด๋ฆญ
โ†’ URL: /posts โ†’ /posts/123
โ†’ Next.js: "์ง€๊ธˆ ๋‚ด๋ถ€ ๋„ค๋น„๊ฒŒ์ด์…˜์ด๊ณ , @modal ์Šฌ๋กฏ์— (.)posts/[id]๊ฐ€ ์žˆ๋„ค"
โ†’ ๋ Œ๋”: ๋ฐฐ๊ฒฝ(ํ”ผ๋“œ) + ๋ชจ๋‹ฌ(๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ)

์‹œ๋‚˜๋ฆฌ์˜ค 2: ๋ธŒ๋ผ์šฐ์ €์— /posts/123 ์ง์ ‘ ์ž…๋ ฅ
โ†’ URL: /posts/123
โ†’ Next.js: "์ง์ ‘ ์ ‘๊ทผ์ด๋‹ˆ ์ธํ„ฐ์…‰ํŠธ ์—†์ด ๊ทธ๋ƒฅ posts/[id]/page.tsx ๋ Œ๋”"
โ†’ ๋ Œ๋”: ๊ฒŒ์‹œ๊ธ€ ์ „์ฒด ํŽ˜์ด์ง€

์‹œ๋‚˜๋ฆฌ์˜ค 3: ๋ชจ๋‹ฌ ์ƒํƒœ์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ
โ†’ URL: /posts/123 โ†’ /posts
โ†’ ๋ชจ๋‹ฌ ๋‹ซํž˜, ํ”ผ๋“œ๋กœ ๋ณต๊ท€

๐Ÿš€ ๋‘˜์˜ ์กฐํ•ฉ: URL์„ ๊ฐ€์ง„ ๋ชจ๋‹ฌ (Instagram ์Šคํƒ€์ผ) ๐Ÿ”ด

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • Parallel Routes + Intercepting Routes๋ฅผ ์กฐํ•ฉํ•ด Instagram ์Šคํƒ€์ผ ๋ชจ๋‹ฌ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค
// app/layout.tsx โ€” @modal ์Šฌ๋กฏ ํฌํ•จ
interface RootLayoutProps {
  children: React.ReactNode
  modal: React.ReactNode  // @modal ์Šฌ๋กฏ
}
 
export default function RootLayout({ children, modal }: RootLayoutProps) {
  return (
    <html lang="ko">
      <body>
        {children}
        {modal}  {/* ๋ชจ๋‹ฌ์ด ์—ด๋ฆด ๋•Œ ์—ฌ๊ธฐ์— ๋ Œ๋”๋จ */}
      </body>
    </html>
  )
}
// app/@modal/(.)posts/[id]/page.tsx โ€” ์ธํ„ฐ์…‰ํŠธ๋œ ๋ชจ๋‹ฌ ๋ฒ„์ „
import { Modal } from '@/components/ui/Modal'
import { getPost } from '@/lib/dal'
 
export default async function PostModal({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const post = await getPost(id)
 
  return (
    <Modal>
      {/* ๋ชจ๋‹ฌ ๋‚ด์—์„œ ๋ณด์—ฌ์ค„ ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ ๋‚ด์šฉ */}
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </Modal>
  )
}
// components/ui/Modal.tsx โ€” ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ
'use client'
import { useRouter } from 'next/navigation'
 
export function Modal({ children }: { children: React.ReactNode }) {
  const router = useRouter()
 
  return (
    <div
      className="modal-backdrop"
      onClick={() => router.back()}  // ๋ฐฐ๊ฒฝ ํด๋ฆญ ์‹œ ๋’ค๋กœ๊ฐ€๊ธฐ โ†’ ๋ชจ๋‹ฌ ๋‹ซํž˜
    >
      <div
        className="modal-content"
        onClick={(e) => e.stopPropagation()}  // ๋‚ด์šฉ ํด๋ฆญ ์‹œ ๋‹ซํž˜ ๋ฐฉ์ง€
      >
        {children}
      </div>
    </div>
  )
}
// app/@modal/default.tsx โ€” ๋ชจ๋‹ฌ ์—†์„ ๋•Œ null ๋ Œ๋”
export default function ModalDefault() {
  return null
}

๐Ÿ’ฅ ์—๋Ÿฌ ํ•ด๊ฒฐ ์นดํƒˆ๋กœ๊ทธ


โŒ Parallel Route ์Šฌ๋กฏ์—์„œ default.tsx ์—†์–ด์„œ ์—๋Ÿฌ

์–ธ์ œ ๋‚˜์˜ค๋Š”๊ฐ€?

Error: Missing default export in @modal/default.tsx

์›์ธ: ์Šฌ๋กฏ์ด ์žˆ๋Š” ๋ ˆ์ด์•„์›ƒ์—์„œ ํ˜„์žฌ URL๊ณผ ๋งค์นญ๋˜๋Š” ์Šฌ๋กฏ ํŽ˜์ด์ง€๊ฐ€ ์—†์„ ๋•Œ default.tsx๊ฐ€ fallback.

ํ•ด๊ฒฐ์ฑ…: ๋ชจ๋“  @slot ํด๋”์— default.tsx ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด. ๋‚ด์šฉ์€ null์ด์–ด๋„ ๋จ.


โŒ Intercepting Routes๊ฐ€ ๋™์ž‘ ์•ˆ ํ•˜๊ณ  ๊ทธ๋ƒฅ ์ „์ฒด ํŽ˜์ด์ง€๋กœ ์ด๋™

์›์ธ: (..) ๊ธฐํ˜ธ์˜ ๋ ˆ๋ฒจ์ด ์ž˜๋ชป๋จ. ํŒŒ์ผ ์‹œ์Šคํ…œ ๋ ˆ๋ฒจ์ด ์•„๋‹Œ URL ์„ธ๊ทธ๋จผํŠธ ๋ ˆ๋ฒจ๋กœ ๊ณ„์‚ฐํ•ด์•ผ ํ•ด.

ํ•ด๊ฒฐ์ฑ…:

// URL: /posts/[id]๋ฅผ ์ธํ„ฐ์…‰ํŠธํ•˜๋ ค๋ฉด
// @modal ์Šฌ๋กฏ์ด / (๋ฃจํŠธ) ๋ ˆ๋ฒจ์— ์žˆ์œผ๋ฉด: (.)posts/[id]
// @modal ์Šฌ๋กฏ์ด /posts ๋ ˆ๋ฒจ์— ์žˆ์œผ๋ฉด: (.)/ ๋˜๋Š” ์ง์ ‘ [id] ํด๋”

๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

๐Ÿ“‹ ์ฃผ์š” ํŒŒ์ผ ๊ตฌ์กฐ ํŒจํ„ด

ํŒจํ„ดํŒŒ์ผ ๊ตฌ์กฐ๊ฒฐ๊ณผ
Parallel Route@slot/page.tsx๋ ˆ์ด์•„์›ƒ์—์„œ ์Šฌ๋กฏ์œผ๋กœ ๋™์‹œ ๋ Œ๋”
Intercepting(.)route/page.tsx๋„ค๋น„๊ฒŒ์ด์…˜ ์‹œ ์ธํ„ฐ์…‰ํŠธ, ์ง์ ‘ ์ ‘๊ทผ์€ ์›๋ณธ
URL ๋ชจ๋‹ฌParallel + Intercepting ์กฐํ•ฉInstagram ์Šคํƒ€์ผ ๋ชจ๋‹ฌ

โš ๏ธ ์ ˆ๋Œ€ ํ•˜์ง€ ๋ง ๊ฒƒ

์ƒํ™ฉโŒ ๋‚˜์œ ์˜ˆโœ… ์ข‹์€ ์˜ˆ
์Šฌ๋กฏ ํด๋ฐฑ ์—†์Œ@modal/default.tsx ์—†์Œ๋ฐ˜๋“œ์‹œ default.tsx ์ถ”๊ฐ€
๋ชจ๋‹ฌ์— ๋’ค๋กœ๊ฐ€๊ธฐ ์—†์Œrouter.push('/')router.back()

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. (.)posts/[id]์—์„œ (.) ๊ธฐํ˜ธ๊ฐ€ ๋œปํ•˜๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ธ๊ฐ€?

โœ… ์ •๋‹ต: ๊ฐ™์€ URL ์„ธ๊ทธ๋จผํŠธ ๋ ˆ๋ฒจ์˜ ๊ฒฝ๋กœ๋ฅผ ์ธํ„ฐ์…‰ํŠธํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: (.)๋Š” ํ˜„์žฌ ๋ ˆ๋ฒจ, (..)๋Š” ํ•œ ๋‹จ๊ณ„ ์œ„, (...)๋Š” ๋ฃจํŠธ ๊ธฐ์ค€ ์ธํ„ฐ์…‰ํŠธ๋‹ค. ์ด ๋ฌธ๋ฒ•์€ ํŒŒ์ผ ์‹œ์Šคํ…œ ์œ„์น˜๊ฐ€ ์•„๋‹ˆ๋ผ URL ์„ธ๊ทธ๋จผํŠธ ๊ธฐ์ค€์œผ๋กœ ํ•ด์„๋œ๋‹ค. ๊ทธ๋ž˜์„œ ํด๋” ๊นŠ์ด๋งŒ ๋ณด๊ณ  ํŒ๋‹จํ•˜๋ฉด ๋ชจ๋‹ฌ์ด ์ „์ฒด ํŽ˜์ด์ง€๋กœ ์—ด๋ฆฌ๋Š” ๋ฒ„๊ทธ๋ฅผ ๋งŒ๋“ค๊ธฐ ์‰ฝ๋‹ค.


Q2. URL์„ ๊ฐ€์ง„ ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ ๋ชจ๋‹ฌ์„ ๋‹ซ์„ ๋•Œ useState(false)๋ณด๋‹ค router.back()์ด ์ž์—ฐ์Šค๋Ÿฌ์šด ์ด์œ ๋Š”?

โœ… ์ •๋‹ต: ๋ชจ๋‹ฌ ์—ด๋ฆผ ์ƒํƒœ๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ํžˆ์Šคํ† ๋ฆฌ์™€ URL์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: Intercepting Routes ๋ชจ๋‹ฌ์€ ๋‹จ์ˆœํ•œ ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž๊ฐ€ /posts/1์ด๋ผ๋Š” ์ฃผ์†Œ๋กœ ์ด๋™ํ•œ ๊ฒฐ๊ณผ๋‹ค. ๋‹ซ์„ ๋•Œ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๋˜๋Œ๋ฆฌ๋ฉด @modal ์Šฌ๋กฏ์€ default.tsx๋กœ ๋Œ์•„๊ฐ€๊ณ  ๋ฐฐ๊ฒฝ ํ”ผ๋“œ๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์œ ์ง€๋œ๋‹ค. useState๋กœ๋งŒ ๋‹ซ์œผ๋ฉด URL๊ณผ ํ™”๋ฉด์ด ์–ด๊ธ‹๋‚œ๋‹ค.


Q3. ์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ํ”ผ๋“œ์—์„œ ๊ฒŒ์‹œ๊ธ€์„ ๋ˆ„๋ฅด๋ฉด ๋ชจ๋‹ฌ๋กœ ์—ด๋ฆฌ๊ณ , ์ƒˆ ํƒญ์—์„œ ๊ฐ™์€ URL์„ ์—ด๋ฉด ์ „์ฒด ์ƒ์„ธ ํŽ˜์ด์ง€๊ฐ€ ๋– ์•ผ ํ•œ๋‹ค. ์–ด๋–ค ํŒŒ์ผ ๊ตฌ์กฐ๊ฐ€ ํ•„์š”ํ• ๊นŒ?

โœ… ์ •๋‹ต: ์‹ค์ œ ์ƒ์„ธ ํŽ˜์ด์ง€ app/posts/[id]/page.tsx์™€, ํ”ผ๋“œ ์ปจํ…์ŠคํŠธ์—์„œ ๊ฐ€๋กœ์ฑŒ @modal/(.)posts/[id]/page.tsx๋ฅผ ํ•จ๊ป˜ ๋‘”๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: Intercepting Route๋Š” "์–ด๋””์„œ ์™”๋Š”์ง€"๊ฐ€ ์žˆ์„ ๋•Œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ณด์กดํ•œ๋‹ค. ์ง์ ‘ ์ง„์ž…์ด๋‚˜ ์ƒˆ๋กœ๊ณ ์นจ์—๋Š” ์›๋ณธ ์ƒ์„ธ ํŽ˜์ด์ง€๊ฐ€ ํ•„์š”ํ•˜๋‹ค. Parallel Route์˜ @modal ์Šฌ๋กฏ์—๋Š” default.tsx๋„ ๋‘ฌ์•ผ ๋งค์นญ๋˜์ง€ ์•Š๋Š” ์ˆœ๊ฐ„ null ์ƒํƒœ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ๋Œ์•„๊ฐ„๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜์€ ๋ชจ๋‹ฌ์„ ๊ทธ๋ƒฅ isOpen ์ƒํƒœ๋กœ๋งŒ ๋‹ค๋ฃจ๋˜ ์Šต๊ด€์—์„œ ํ•œ ๋‹จ๊ณ„ ๋ฒ—์–ด๋‚ฌ๋‹ค. ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ์ฒ˜๋Ÿผ ๊ณต์œ  ๊ฐ€๋Šฅํ•œ ํ™”๋ฉด์€ URL์ด ์ง„์งœ ์ƒํƒœ์ด๊ณ , ๋ชจ๋‹ฌ์€ ๊ทธ URL์„ ์–ด๋–ค ์ปจํ…์ŠคํŠธ์—์„œ ๋ณด์—ฌ์ค„์ง€ ๊ฒฐ์ •ํ•˜๋Š” ํ‘œํ˜„ ๋ฐฉ์‹์ด์—ˆ๋‹ค.

๐Ÿ’ก "๊ณต์œ ์™€ ์ƒˆ๋กœ๊ณ ์นจ์ด ํ•„์š”ํ•œ UI๋Š” ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ผ์šฐํŒ… ์ƒํƒœ๋กœ ์„ค๊ณ„ํ•œ๋‹ค."

๋‹ค์Œ์— ๋ชจ๋‹ฌ ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ฐ›์œผ๋ฉด ๋จผ์ € "์ด ํ™”๋ฉด์„ ์ƒˆ ํƒญ์œผ๋กœ ์—ด์–ด๋„ ์˜๋ฏธ๊ฐ€ ์žˆ๋Š”๊ฐ€"๋ฅผ ๋ฌป๊ฒ ๋‹ค. ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค๋ฉด ์›๋ณธ ํŽ˜์ด์ง€, ์ธํ„ฐ์…‰ํŠธ ํŽ˜์ด์ง€, ์Šฌ๋กฏ ๊ธฐ๋ณธ๊ฐ’์„ ํ•จ๊ป˜ ์„ค๊ณ„ํ•ด์„œ ์‚ฌ์šฉ์ž ํžˆ์Šคํ† ๋ฆฌ์™€ ํ™”๋ฉด์ด ์–ด๊ธ‹๋‚˜์ง€ ์•Š๊ฒŒ ๋งŒ๋“ค๊ฒ ๋‹ค.

๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ