Parallel Routes
URL 세그먼트를 담당하는 폴더에 @를 붙여 여러 페이지 경로를 렌더링할 수 있게하는 기능
아래 사진처럼 한 페이지에 두가지 페이지 요소를 띄우는 등의 작업을 할 수 있게 함
대시보드나 소셜 사이트의 피드, 상품 상세 페이지 모달 띄우기 같은 보여줘야하는 정보량이 많은 동적인 섹션에 유용하게 사용할 수 있음
모달 실습
1) (root)/@modal/page.tsx 생성
const ModalPage = () => {
return (
<div className="w-[300px] h-[300px] bg-blue-400">
modal 영역
</div>
);
};
export default ModalPage;
2) layout의 props에 modal: React.ReactNode를 추가
export default function Layout({
children,
modal // 추가
}: Readonly<{
children: React.ReactNode;
modal: React.ReactNode; // 요기 추가
}>) {
return (
<>
{ * 생략 * }
{ modal } { * 추가 * }
{ children }
</>
);
}
한 페이지에 동시에 다른 페이지가 렌더링되고 있는 것을 확인할 수 있음!
어떤 의미에서 평행하다는 걸까???
현재 @modal 은 (root)의 page.tsx와 같은 레벨의 경로이다. ((root)/page.tsx, (root)/@modal/page.tsx)
(root)에는 루트 페이지에서 타고들어갈 수 있는 product/[id]가 있는데, (root)/@modal/product/[id]를 생성해주면 (root)/product/[id] 와 같은 url로 인식되어 두 페이지를 병렬로 렌더링할 수 있게 된다!!!
따라서 (root)/product/[id]/page.tsx를 그대로 유지하면서 (root)/@modal/product/[id]/page.tsx에 정의한 UI를 추가로 띄울 수 있게 된다.
// (root)/@modal/product/[id]/page.tsx
const ProductDynamicPage = () => {
return (
<div className='w-[300px] h-[300px] bg-pink-300'>
Product Detail에서 띄우고싶은 모달
</div>
);
};
export default ProductDynamicPage;
default.tsx
현재 product/[id]/page.tsx는 (root)와 (root)/@modal 이 동일하게 가지고 있지만, /cart/page.tsx는 루트 경로에만 있다.
이 상태에서 Link 태그를 이용해 소프트 네비게이션으로 이동하면, 문제가 없지만 만약 url 로 직접 접속하는 등 새로고침이 일어나는 경우 modal에대한 경로를 제대로 인식할 수 없어 404 오류가 발생한다.
이를 방지하기위해 모든 페이지를 병렬적으로 만들어줄수도 있겠지만, 페이지가 너무 많은 경우에는 너무 효율적이지 못한 작업이다. 이 때 @modal (parallel route)에 default.tsx를 생성해 해결할 수 있다!
// (root)/@modal/default.tsx
const Default = () => {
return null;
};
export default Default;
이렇게 해주면 병렬적으로 선언되지 않은 모든 페이지에서 기본적으로 null을 반환하기 때문에 url로 접속 혹은 새로고침 (hard navigation) 하더라도 에러 없이 동작하는 것을 확인할 수 있다.
하지만 Link 태그로 soft navigation을 했을 때 의도하지 않은 모달이 떠있는 기묘한 현상이 발생한다.
soft navigation 시 볼 수 있는 병렬적인 페이지와 hard navigation시 볼 수 있는 페이지를 구분하기위해 intersecting routes를 활용하는 경우가 많다.
Intersecting Routes
위의 예시에서 (root)/page.tsx에서 상품 목록 클릭시 -> 상품의 디테일 정보를 보여주는 모달을 띄우고 -> 모달에서 실제로 이동하는 버튼을 눌렀을 때 hadr navigation을 발생시켜 실제 디테일 페이지로 이동하는 예시를 구현해보자
1) 폴더구조
app
├── product
│ ├── [id]
│ │ └── page.tsx <-- 실제 상품 상세 페이지 (product/[id])
├── @modal
│ └── (.)product
│ └── [id]
│ └── page.tsx <-- 모달로 띄울 상품 정보 페이지
└── layout.tsx <-- 공통 레이아웃 (모달 포함)
└── page.tsx <-- 상품 목록 페이지
2) 공통 레이아웃에서 modal을 표시할 공간을 만들어준다
// app/layout.tsx
import { ReactNode } from 'react';
export default function RootLayout({
children,
modal,
}: {
children: ReactNode;
modal: ReactNode;
}) {
return (
<html lang="en">
<body>
<div>
{modal} {/* 모달을 렌더링할 영역 */}
{children} {/* 메인 콘텐츠 영역 */}
</div>
</body>
</html>
);
}
3) (root)의 페이지에서 상품을 클릭했을 때 product 페이지로 이동하게 함
- 이 때 Link를 이용한 이동은 soft navigation에 해당하므로 @modal이 가로채 (root)/product/[id]/page.tsx가 아닌 (root)/@modal/(.)product/[id]/page.tsx로 이동한다
// app/page.tsx
import Link from 'next/link';
export default function ProductListPage() {
// 생략
return (
<div>
<h1>Products</h1>
<ul>
{products.map((product) => (
<li key={product.id}>
{/* 클릭하면 모달로 상품 정보 표시 */}
<Link href={`/product/${product.id}`}>
{product.name} (Click for details in modal)
</Link>
</li>
))}
</ul>
</div>
);
}
4) 모달에 클릭했을 때 실제 디테일 페이지로 이동하는 기능을 추가
- location.reload() 를 이용해 product/[id] url에서 새로고침하여 hard navigation이 일어나게 한다.
'use client'
import {useRouter} from "next/navigation";
const ProductDynamicPage = ({params}: {params: {id: string}}) => {
const router = useRouter();
const handleToPage = () => {
location.reload();
}
const handleCloseModal = () => {
router.back();
}
return (
<div className='flex gap-5 fixed top-[50%] right-[50%] w-[300px] h-[300px] bg-pink-300'>
Product Detail 모달
<button className='rounded bg-amber-200' onClick={handleToPage}>상세 페이지로 이동</button>
<button className='rounded bg-amber-200' onClick={handleCloseModal}>모달 닫기</button>
</div>
);
};
export default ProductDynamicPage;
'Next.js' 카테고리의 다른 글
[Next.js/Tanstack-Query] Next에서 prefetch하기 (1) | 2024.10.01 |
---|---|
[Next.js] Auth와 Middleware (0) | 2024.09.30 |
[Next.js] Caching (1) | 2024.09.28 |
[Next.js] Router Handler & Server Action (0) | 2024.09.27 |
[Next.js] Asset 최적화 (1) | 2024.09.27 |