๐ญ Next.js ์ฌํ 5์ฅ: Parallel Routes & Intercepting Routes โ URL์ด ์ด์์๋ ๋ชจ๋ฌ์ ๋น๋ฐ
๐ ๊ฐ์
Parallel Routes์ Intercepting Routes์ ๊ณ ๊ธ ํ์ฉ โ ๋ณต์กํ ๋ชจ๋ฌยทํญ UI ๊ตฌํ ์ ๋ต์ ๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ Parallel Routes โ ํ๋์ ํ๋ฉด์ ์ฌ๋ฌ ์ฌ๋กฏ ๋ฐฐ์นํ๊ธฐ ๐ข
- ๐ฏ Intercepting Routes โ URL์ ์ ์งํ๊ณ ์ปจํ ์คํธ๋ง ๋ฐ๊พธ๊ธฐ ๐ก
- ๐ ๋์ ์กฐํฉ: URL์ ๊ฐ์ง ๋ชจ๋ฌ (Instagram ์คํ์ผ) ๐ด
- ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 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>์ด ๋ฐฉ์์ ๋ฌธ์ :
- URL์ด ์ ๋ฐ๋์ด โ ๋ชจ๋ฌ์ ์น๊ตฌ์๊ฒ ๊ณต์ ํ ์ ์์
- ์๋ก๊ณ ์นจํ๋ฉด ์ฌ๋ผ์ ธ โ UX ์ผ๊ด์ฑ ์์
- ๋ค๋ก๊ฐ๊ธฐ๊ฐ ์ด์ํด โ ๋ชจ๋ฌ ๋ซ๋ ๊ฒ ์๋๋ผ ์ด์ ํ์ด์ง๋ก ๊ฐ๋ฒ๋ฆผ
- 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๋ ์ปดํฌ๋ํธ ์ํ๊ฐ ์๋๋ผ ๋ผ์ฐํ ์ํ๋ก ์ค๊ณํ๋ค."
๋ค์์ ๋ชจ๋ฌ ์๊ตฌ์ฌํญ์ ๋ฐ์ผ๋ฉด ๋จผ์ "์ด ํ๋ฉด์ ์ ํญ์ผ๋ก ์ด์ด๋ ์๋ฏธ๊ฐ ์๋๊ฐ"๋ฅผ ๋ฌป๊ฒ ๋ค. ์๋ฏธ๊ฐ ์๋ค๋ฉด ์๋ณธ ํ์ด์ง, ์ธํฐ์ ํธ ํ์ด์ง, ์ฌ๋กฏ ๊ธฐ๋ณธ๊ฐ์ ํจ๊ป ์ค๊ณํด์ ์ฌ์ฉ์ ํ์คํ ๋ฆฌ์ ํ๋ฉด์ด ์ด๊ธ๋์ง ์๊ฒ ๋ง๋ค๊ฒ ๋ค.