기존의 모달 구현 방식은 useState를 활용하여 작업했었다.
const [isModalOpen, setIsModalOpen] = useState(false);
// 모달 열기
const openModal = () => setIsModalOpen(true);
// 모달 닫기
const closeModal = () => setIsModalOpen(false);
// 또는 const toggleModal = () => setIsModalOpen(prev => !prev);
그러나 해당 방식은 새로고침 시 모달이 사라지고 모달이 열린 상태의 URL을 공유할 수 없다.
Parallel Routes는 이러한 문제를 해결하여 이름그대로 병렬적인 라우트를 만들 수 있다.
모달을 위한 별도의 라우트를 만들어서 메인 페이지와 병렬적으로(Parallel) 관리할 수 있다.
예를들어, 특정 페이지에서 사용자가 버튼을 누르면 상품리스트 모달이 뜨고 상품 클릭 시 상세화면으로 이동한다면 폴더 구조는 아래와 같다.
app/
..list/
....page.tsx
....layout.tsx
....@modal/
.......default.tsx
.......products-modal/page.tsx
쿼리스트링 모달의 장점은 모달상태제어를 쿼리스트링으로 할 수 있으며, default.tsx 에서 여러 모달을 분기처리 할 수 있다는 점이다.
기본적으로 부모 layout에 {modal}을 두어야한다. 그 뒤는 아래의 과정 !
// default.tsx
"use client";
import { useSearchParams } from "next/navigation";
import ProductsModal from "@/app/list/@modal/products-modal/page";
const MODAL_COMPONENTS: Record<string, React.ReactNode> = {
"products-modal": <ProductsModal />,
};
export default function ModalSlot() {
const params = useSearchParams();
const modal = params.get("modal");
return MODAL_COMPONENTS[modal ?? ""] ?? null;
}
"use client";
import Link from "next/link";
import styles from "./styles.module.scss"
export default function ProductsModal() {
return (
<div className={styles.modal}>
<div className={styles.modalContent}>
<h2>쿼리 기반 모달!!</h2>
<Link href="/list" replace>닫기</Link> //쿼리기반이니 /list페이지로 닫기 처리
<ul>
<li><Link href="/list/1">상세 1</Link></li>
<li><Link href="/list/2">상세 2</Link></li>
</ul>
</div>
</div>
);
}
"use client";
import Link from "next/link";
export default function ListPage() {
return (
<div>
<h1>MODALS</h1>
<p><Link href="/list?modal=products-modal">쿼리기반 모달</Link></p>
</div>
);
}
그럼 위와 같이 href 내 경로를 지정해주면되는데, default.tsx 에 선언한 key값을 작성해주면 된다.