블로그에서 포스트를 클릭하면 모달로 열리고, URL도 바뀌는 UX를 구현했다.
기존 모달의 문제
일반적인 모달 (useState, overlay-kit 등)의 문제:
- URL이 안 바뀌는다 → 공유 불가
- 새로고침하면 모달 닫힘 → 상태 유실
- SEO에 불리 → 모달 내용 크롤링 안 됨
디자이너 사이트는 이미지 클릭 시 모달로 상세가 열리는 게 핵심 UX였다. 하지만 링크 공유도 되어야 했다.
Parallel Routes + Intercepting Routes
Next.js App Router의 이 기능으로 해결했다.
폴더 구조
src/app/ ├── @modal/ # Parallel Route (slot) │ ├── (.)archive/[id]/ # Intercepting Route │ │ └── page.tsx # 모달로 열릴 때 │ └── default.tsx # 모달 없을 때 ├── archive/ │ ├── page.tsx # 목록 │ └── [id]/ │ └── page.tsx # 직접 접근 시 전체 페이지 └── layout.tsx
핵심 컵셈:
@modal: Parallel Route slot 이름 (@로 시작)
(.): 같은 레벨의 라우트를 인터셉트
동작 방식
1. 목록에서 클릭 시
/archive 에서 포스트 클릭 ↓ URL: /archive/123 으로 변경 ↓ (.)archive/[id]/page.tsx 가 인터셉트 ↓ 모달로 표시 (배경에 목록 유지)
2. URL 직접 접근 또는 새로고침
/archive/123 직접 접속 ↓ 인터셉트 안 됨 ↓ archive/[id]/page.tsx 로 렌더링 ↓ 전체 페이지로 표시
구현 코드
Root Layout
// src/app/layout.tsx export default function RootLayout({ children, modal, }: { children: React.ReactNode; modal: React.ReactNode; // @modal slot }) { return ( <html> <body> {children} {modal} </body> </html> ); }
default.tsx (필수!)
// src/app/@modal/default.tsx export default function Default() { return null; }
왜 필수인가?
Parallel Routes는 현재 URL과 매칭되지 않을 때
default.tsx를 찾는다. 없으면 404가 뜬다.예:
/archive 페이지에서는 @modal/(.)archive/[id]가 매칭 안 되므로 default.tsx가 렌더링됨.모달 페이지
// src/app/@modal/(.)archive/[id]/page.tsx import { Modal } from '@/components/modal'; import { PostDetail } from '@/widgets/PostDetail'; export default async function PostModal({ params }: { params: { id: string } }) { const post = await getPostById(params.id); return ( <Modal> <PostDetail post={post} /> </Modal> ); }
전체 페이지
// src/app/archive/[id]/page.tsx import { PostDetail } from '@/widgets/PostDetail'; export default async function PostPage({ params }: { params: { id: string } }) { const post = await getPostById(params.id); return ( <main> <PostDetail post={post} /> </main> ); }
포인트:
PostDetail 컴포넌트를 공유해서 모달/페이지 둘 다 동일한 UI.인터셉트 규칙
패턴 | 의미 |
(.) | 같은 레벨 |
(..) | 한 레벨 위 |
(..)(..) | 두 레벨 위 |
(...) | 루트부터 |
이 프로젝트에서는
@modal과 archive가 같은 레벨이므로 (.)를 사용.결과
- 목록에서 클릭 → 모달로 열림 + URL 변경
- URL 공유 →
/archive/123링크로 공유 가능
- 새로고침 → 전체 페이지로 보임
- SEO → 각 포스트가 별도 URL을 가짐
디자이너 사이트의 모달 UX를 유지하면서 URL 기반 네비게이션까지 얻을 수 있었다.

