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

๐Ÿ“‹ ๊ฐœ์š”

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]/page.tsx ์—์„œ (.) ์ด ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์€?

  • A) ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์˜ ํŒŒ์ผ ์ฐธ์กฐ
  • B) ๊ฐ™์€ URL ์„ธ๊ทธ๋จผํŠธ ๋ ˆ๋ฒจ์˜ ๊ฒฝ๋กœ ์ธํ„ฐ์…‰ํŠธ
  • C) ํ•œ ๋‹จ๊ณ„ ์ƒ์œ„ ๊ฒฝ๋กœ ์ธํ„ฐ์…‰ํŠธ
  • D) ๋ฃจํŠธ ๊ฒฝ๋กœ๋ถ€ํ„ฐ ์ธํ„ฐ์…‰ํŠธ

โœ… ์ •๋‹ต: B

์˜ค๋‹ต ํ•ด์„ค:

  • C โ€” (..) ๊ฐ€ ํ•œ ๋‹จ๊ณ„ ์ƒ์œ„
  • D โ€” (...) ๊ฐ€ ๋ฃจํŠธ๋ถ€ํ„ฐ

๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: . ํ•˜๋‚˜ = ๊ฐ™์€ ๋ ˆ๋ฒจ, .. ๋‘ ๊ฐœ = ํ•œ ๋‹จ๊ณ„ ์œ„.


Q2. Instagram ์Šคํƒ€์ผ URL ๋ชจ๋‹ฌ์—์„œ ๋ชจ๋‹ฌ์„ ๋‹ซ์œผ๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋Š”๊ฐ€?

  • A) useState๋กœ isOpen = false
  • B) router.push('/posts')
  • C) router.back()
  • D) ๋ชจ๋‹ฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ null๋กœ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”

โœ… ์ •๋‹ต: C

ํ•ด์„ค: router.back()์ด ํžˆ์Šคํ† ๋ฆฌ ์Šคํƒ์„ ๋’ค๋กœ ์ด๋™์‹œํ‚ค๋ฉด์„œ Intercepting Route๊ฐ€ ํ•ด์ œ๋˜์–ด @modal ์Šฌ๋กฏ์ด default.tsx(null)๋กœ ๋Œ์•„๊ฐ€. router.push()๋Š” ์ƒˆ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ๋™์ž‘ ๋ฐฉ์‹์ด ๋‹ฌ๋ผ.

๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๋ชจ๋‹ฌ ๋‹ซ๊ธฐ = ๋’ค๋กœ๊ฐ€๊ธฐ(back). ํžˆ์Šคํ† ๋ฆฌ ์Šคํƒ์„ ์ด์šฉํ•˜๋Š” ๊ฑฐ์•ผ.


Q3. ์นœ๊ตฌ์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?

Intercepting Routes๊ฐ€ "๊ฐ™์€ URL์ธ๋ฐ ๋‹ค๋ฅด๊ฒŒ ๋ณด์—ฌ์ค€๋‹ค"๋Š” ๊ฒŒ ์–ด๋–ค ์˜๋ฏธ์ธ์ง€ ๋น„์œ ๋กœ ์„ค๋ช…ํ•ด๋ด.

์˜ˆ์‹œ ๋‹ต๋ณ€:

"๋„์„œ๊ด€ ๋น„์œ ์•ผ. ๋„์„œ๊ด€ ๋‚ด๋ถ€ ์—˜๋ฆฌ๋ฒ ์ดํ„ฐ(Next.js ๋„ค๋น„๊ฒŒ์ด์…˜)๋ฅผ ํƒ€๊ณ  3์ธต ์—ด๋žŒ์‹ค์— ๊ฐ€๋ฉด ์—ด๋žŒ์‹ค ์‚ฌ์„œ๊ฐ€ ์ฑ…์„ ๋ฐ”๋กœ ๋ณด์—ฌ์ค˜(๋ชจ๋‹ฌ). ๊ทผ๋ฐ ๊ฑด๋ฌผ ์ •๋ฌธ์œผ๋กœ ์ง์ ‘ 3์ธต์œผ๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉด(์ง์ ‘ URL ์ ‘๊ทผ) ๊ทธ๋ƒฅ ์—ด๋žŒ์‹ค ์ „์ฒด๊ฐ€ ๋ณด์—ฌ(์ „์ฒด ํŽ˜์ด์ง€). ๊ฐ™์€ ์ฃผ์†Œ์ธ๋ฐ ์–ด๋–ป๊ฒŒ ์™”๋А๋ƒ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ๊ฑฐ์•ผ."


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

์˜ค๋Š˜์€ ์ •๋ง ๋„ฅ์ŠคํŠธ ๋ผ์šฐํŒ…์˜ ๋ํŒ์™•์ด๋ผ ๋ถˆ๋ฆฌ๋Š” 'Parallel & Intercepting Routes' ๋ฅผ ๋ฐฐ์šฐ๋ฉด์„œ ๋จธ๋ฆฌ๊ฐ€ ์–ด์งˆ์–ด์งˆํ–ˆ์–ด. (.) ์ด๋‚˜ (..) ๊ฐ™์€ ์ƒ์†Œํ•œ ๋ฌธ๋ฒ•๋“ค์„ ๋ณด๋ฉด์„œ "์ด๊ฒŒ ๋Œ€์ฒด ๋ญ์•ผ?" ์‹ถ์—ˆ๋Š”๋ฐ, ์ธ์Šคํƒ€๊ทธ๋žจ ๊ฐ™์€ ๋ณต์žกํ•œ ๋ชจ๋‹ฌ ๊ตฌ์กฐ๋ฅผ ์ด๋ ‡๊ฒŒ ์šฐ์•„ํ•˜๊ฒŒ ํ’€ ์ˆ˜ ์žˆ๋‹ค๋‹ˆ ์ •๋ง ๋†€๋ผ์› ์–ด.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "๋ณต์žกํ•œ UI ์ƒํƒœ ๊ด€๋ฆฌ์— ๋งค๋ชฐ๋˜์ง€ ๋ง์ž. Parallel & Intercepting Routes ๋ฅผ ํ™œ์šฉํ•ด URL๊ณผ UI๋ฅผ ์ผ์น˜์‹œํ‚ค๋ฉด, ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ๊นจ์ง€์ง€ ์•Š๋Š” ๊ฒฌ๊ณ ํ•œ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค!"

์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋„์„œ๊ด€ ์—˜๋ฆฌ๋ฒ ์ดํ„ฐ ๋น„์œ ๋ฅผ ๋“ค์–ด ์„ค๋ช…ํ•ด ์ฃผ์‹ค ๋•Œ, ์ธํ„ฐ์…‰ํŒ… ๋ผ์šฐํŠธ๊ฐ€ ์™œ '๊ฐ€๋กœ์ฑ„๊ธฐ' ์ธ์ง€ ๋‹จ๋ฒˆ์— ์ดํ•ด๊ฐ€ ๊ฐ€๋”๋ผ. ๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฑธ ๋„˜์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋””์„œ ์ ‘์†ํ•˜๋“  ์ผ๊ด€๋œ ๊ฒฝํ—˜์„ ์ฃผ๋Š” ๊ฒŒ ์–ผ๋งˆ๋‚˜ ์ค‘์š”ํ•œ์ง€ ๊นจ๋‹ฌ์•˜์–ด. ์˜ค๋Š˜ ๋„ˆ๋ฌด ๊ณ ์ƒํ•œ ๋‚˜ ์ž์‹ ์—๊ฒŒ ์‹œ์›ํ•œ ์•„์ด์Šค ์•„๋ฉ”๋ฆฌ์นด๋…ธ ํ•œ ์ž” ์‚ฌ์ค˜์•ผ์ง€! ๋‚ด์ผ์€ ๋” '์šฐ์•„ํ•œ' ๋ผ์šฐํŒ…์„ ์„ค๊ณ„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋  ๊ฑฐ์•ผ! ๐Ÿฃ


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