패럴렐 라우트(Parallel Route)란?
병렬 라우트: 하나의 화면 안에서 여러 개의 페이지 컴포넌트를 병렬로 렌더링할 수 있는 패턴
- 소셜 미디어 서비스나 관리자 대시보드처럼 복잡한 페이지 구조를 렌더링하는 데 적합합니다.
1) 슬롯
패럴렐 폴더 안에 @폴더 이름으로 생성 가능
병렬로 렌더링될 페이지 컴포넌트를 보관하는 폴더
- app/parallel/@슬롯/page.tsx : 해당 경로 안의 페이지 컴포넌트는 자신의 부모 레이아웃 컴포넌트에서 props로 전달됩니다.
- @슬롯 : URL 주소에는 영향을 주지 않음. 따라서 parallel/@sidebar로 접속해도 페이지가 렌더링되지 않습니다.
// src/app/parallel/layout.tsx
import Link from "next/link";
import { ReactNode } from "react";
export default function Layout({
children, // 기본적인 페이지도 props로 전달됨
sidebar, // 추가적으로 전달 가능한 props
feed,
}: {
children: ReactNode;
sidebar: ReactNode;
feed: ReactNode;
}) {
return (
<div>
<div>
<Link href="/parallel">parallel</Link>
<Link href="/parallel/setting">parallel/setting</Link>
</div>
<br />
{sidebar}
{feed}
{children}
</div>
);
}
💡 parrallel 적용 안될 떄
Next.js의 버그로 인해 .next 폴더를 삭제한 후 npm run dev를 실행하면 정상적으로 작동합니다.
2) 슬롯 하위 페이지 등록
// src/app/parallel/@sidebar/page.tsx
export default function Page() {
return <div>@sidebar</div>;
}
// src/app/parallel/@feed/page.tsx
export default function Page() {
return <div>@feed</div>;
}
// src/app/parallel/@feed/setting/page.tsx
export default function Page() {
return <div>@feed/setting</div>;
}
- 이렇게 하면 /parallel/setting으로 이동 시 sidebar와 children 슬롯은 그대로 유지되지만 feed 슬롯만 변경됩니다.
- 페이지 이동 후 전달받지 못한 슬롯의 상태는 유지됨 (404가 발생하지 않음).
- 하지만 새로고침하면 404 오류가 발생할 수 있습니다.
- 슬롯들이 이전의 페이지를 유지하게 되는 건, 링크 컴포넌트를 이용하여 브라우저 측에서 클라이언트 사이드 렌더링 방식으로 페이지를 이동할 때에만 한정되기 때문
- 이를 방지하려면 각 슬롯별 default 페이지를 설정해야 합니다.
// src/app/parallel/@sidebar/default.tsx
export default function Default() {
return <div>@sidebar/default</div>;
}
// src/app/parallel/default.tsx
export default function Default() {
return <div>/parallel/default</div>;
}
이렇게 default.tsx를 추가하면 새로고침해도 404 오류 없이 기본 페이지가 렌더링됩니다.
✅ 페럴렐 라우트 공식문서 링크
Rendering: Server-side Rendering (SSR)
Rendering: Server-side Rendering (SSR) | Next.js
Use Server-side Rendering to render pages on each request.
nextjs.org
인터셉팅 라우트 (Intercepting Route)란?
사용자가 특정 경로로 접속할 때 , 요청을 가로채어 원래 페이지 대신 원하는 페이지를 렌더링하는 라우팅 패턴
- 초기 접속 요청이 아닐 때만 동작합니다.
- 즉, **클라이언트 사이드 렌더링(CSR)**으로 페이지를 이동할 때 동작합니다.
- ex. 인스타그램에서 게시글을 클릭하면 모달이 뜨고, 뒤로 가기하면 원래 탐색하던 화면이 유지됨.
1) 인터셉팅 페이지 생성
(.)폴더명을 사용하면 해당 경로를 인터셉트할 수 있습니다.
- (.) 점 한 개 : 동일한 경로에서 인터셉트
- (..) 점 두 개 : 상위 경로에서 인터셉트
- (..)(..) 점 두 개 두 번 : 상위의 상위 경로 인터셉트
- (...) 점 세 개 : app 폴더 바로 아래의 어떤 폴더든 인터셉트
// src/app/(.)book/[id]/page.tsx
import BookPage from "@/app/book/[id]/page";
import Modal from "@/components/modal";
export default function Page(props: any) {
return (
<div>
가로채기 성공!
<Modal>
<BookPage {...props} />
</Modal>
</div>
);
}
// src/components/modal.tsx
"use client"; // 클라이언트 컴포넌트로 선언
import { ReactNode, useEffect, useRef } from "react";
import style from "./modal.module.css";
import { createPortal } from "react-dom";
import { useRouter } from "next/navigation";
export default function Modal({ children }: { children: ReactNode }) {
const dialogRef = useRef<HTMLDialogElement>(null);
const router = useRouter();
useEffect(() => {
if (!dialogRef.current?.open) {
// 모달이 닫혀있다면 자동으로 열기
dialogRef.current?.showModal();
// 모달이 뜨자마자 스크롤을 맨 위로 이동
dialogRef.current?.scrollTo({ top: 0 });
}
}, []);
// createPortal: 브라우저에 존재하는 DOM 요소에 고정적으로 이 모달 요소들을 렌더링
return createPortal(
<dialog
onClose={() => router.back()} // 닫히면 뒤로 가기
onClick={(e) => {
if ((e.target as any).nodeName === "DIALOG") {
router.back(); // 모달 배경 클릭 시 뒤로 가기
}
}}
className={style.modal}
ref={dialogRef}
>
{children}
</dialog>,
document.getElementById("modal-root") as HTMLElement
);
}
패럴렐 & 인터셉팅 라우트 함께 활용하기
아래와 같은 폴더 구조를 사용하면 패럴렐 라우트와 인터셉팅 라우트를 결합하여 더 유연한 UI를 구현할 수 있습니다.
- 이렇게 하면 인터셉팅 된 도서 상세 페이지를 모달로 띄우면서도 기존 페이지(피드, 사이드바)를 유지할 수 있습니다.
'Frontend > Next.js' 카테고리의 다른 글
[SEO] 렌더링 방식과 SEO, Hydration의 관계 (0) | 2025.04.12 |
---|---|
[Next.js/App Router] 12. 최적화 및 SEO 설정 및 배포하기 (0) | 2025.02.14 |
[Next.js/AppRouter] 10. 서버 액션 (Server Actions) (0) | 2025.02.11 |
[Next.js/App Router] 9. 스트리밍과 에러 핸들링 (0) | 2025.02.10 |
[Next.js/App Router] 8. 풀 라우트 캐시 (Full Route Cache) (0) | 2025.02.09 |