next_js
web

2025-04-10

완성 모습

목표

내 웹사이트에서 어떤 버튼을 클릭하면 회원가입, 로그인, 내프로필 등 ‘유저’와 ‘권한’과 관련된 페이지들을 모달로 만들고 싶었다. 그런데 어떤 모달이냐 하면 …

  1. 뒷배경으로 이전 페이지가 보여야 한다. 다른 말로 이전 페이지 위에 레이어가 올라가서 이전 페이지를 부분적으로 가려야 한다.
  2. 독립된 url을 가져야 한다. 예를 들어, 회원가입 페이지의 링크를 복사해서 전달하면 받는 사람이 회원가입 페이지를 받아야 한다.

아예 새로운 route이면서 뒤에 이전 route의 화면이 보이게 하려면 어떻게 해야 할까?

Next.js에서는 Parallel RouteIntercepting Route를 이용할 수 있나 본데, 후자는 특정 페이지에서만 나타나는 모달을 만들 때 쓰는 것 같았다. 내 경우는 어느 페이지에서든 열 수 있는 모달이기 때문에 Parallel route로 만들기로 했다.

Parallel Route

  • 사실 이 프로젝트에서 이전에도 쓴 적 있었는데 필요 없어져서 지웠었다.

Parallel Route는 동시에 두 개 이상의 Route를 한 페이지에 보여주는 방법이다. url에는 마지막 Route가 담긴다. 그렇기 때문에 현재 url에 안 담겨있는 나머지 페이지에 내용이 없어질 경우(예를들어 새로고침을 해서 전체를 다시 렌더링할때) 뭘 보여주고 있을지를 뜻하는 default.tsx를 만들어 줘야 한다. 이 예시를 보면 이해가 된다

Next.js에서는 컴포넌트의 자식 컴포넌트를 Prop으로 받을 수 있는데 보통의 자식컴포넌트는 {children}으로 쓰면 되지만 Parallel Route는 폴더명에 @를 붙이고 그 폴더명을 slot에 사용해야 한다. e.g. @modal, `{modal}

모달 route와 layout

@modal폴더 안에 내가 모달로 만들고싶은 route들을 넣었다. 그 모달들은 동일한 레이아웃을 가져야 하기 때문에 /user/로 묶고 layout.tsx 파일을 만들어줬다. 그 layout.tsx는 이렇게 구성했다.

'use client'

import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { Suspense } from "react";

export default function Layout({children}: {children: React.ReactNode}) {
	const router = useRouter();

	useEffect(() => {
		const onEsc = (e: KeyboardEvent) => {
			if (e.key === 'Escape') router.back()
		}
		window.addEventListener('keydown', onEsc)
		return () => window.removeEventListener('keydown', onEsc)
	}, [router])

	return(
		<Suspense>
			<div
				className="fixed inset-0 bg-black/20 p-4 flex items-center justify-center z-50"
				onClick={() => router.back()}
			>			
				<dialog
					open
					className="w-[44rem] h-[32rem] bg-white p-4"
					onClick={e => e.stopPropagation()}
			>				
					{children}
				</dialog>
			</div>
		</Suspense>
	);
}

설명

  • dialog에 {children}넣어서 @modal안에있는 route들이 들어가게 함.
  • dialog를 감싸는 div는 배경이다. css로 투명하게 만든다. 그리고 onClick에 뒤로가기를 넣는다.
  • Suspense로 감싼 이유는 children으로 들어가는 page.tsx들에 useSearchParams가 쓰이기 때문이다. 참고

주의: @modal/page.tsx만들어야 한다.

모달 열고 닫기: useRouter()

그렇게 만든 모달 route의 페이지에 들어가고 나갈 때는 next/navigationuseRouter()를 사용한다.

router.push()써서 모달을 열고 router.before()써서 이전 화면으로 돌아가(=모달 닫기)면 된다.