새로 준비중인 공모전에서 팝업 페이지를 구현해보기로 했다.
추가적인 페이지 이동이 없고, 간단해보였기 때문이다. (근데 구현은 안 간단한 듯 하다.)

pc버전의 인스타그램 같은 창을 생각하면 이해가 쉬울 것 같다.
modal 없이도 배경 어둡게 + 팝업으로 띄운 것 같은 효과를 통해 모달의 느낌을 줄 수 있다.
종료조건
x버튼을 누르거나, esc를 누르거나, 팝업창 밖의 백그라운드를 누르면 종료되도록 하였다.
1.x버튼 누르기 : 그냥 버튼 컴포넌트를 만들면됨
2.esc누르면 종료: useEffect를 통해 Escape를 누르면 onClose()됨. 그리고 자식 페이지들에서 onclose시 navigate(-1)되도록 하였음.
3.백그라운드 누르면 종료: 가장 아래에 백그라운드 div를 만들어준다. 그리고 이 div를 누르면 onclose가 실행되도록 하였다.
그려지는 방식
각 레이어는 fixed + z-index를 이용해 화면 전체에 겹쳐 그려진다.
2층: 팝업 페이지
1 층: 백그라운드용 회색 페이지 (여기 누르면 종료)
0 층: 원래 페이지 ( 스크롤 방지 걸어줘야함)
이 이후로 팝업 페이지 내에서 추가적인 페이지 이동이 생기면, 3층 부터 추가로 그려진다.
활용 및 실코드


실제론 이런식으로 구현했다. 페이지 내에서 스크롤/페이지 이동도 아주 잘 된다 !
tailwind css를 통해 테두리도 둥글게 해보고,
Modal shell을 정의하여 여러 팝업페이지에 공통 header를 정의해줬다.
// ModalShell.jsx
import ...
export default function ModalShell({ onClose, children }) {
useEffect(() => { //모달이 실행되면, 원래 페이지의 스크롤을 막음.
const prev = document.body.style.overflow;
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = prev;
};
}, []);
useEffect(() => { //키 누르면 종료
const onKey = (e) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey); //keydown이벤트 리스너를 윈도우에 붙임.
}, [onClose]);
return (
//바깥 클릭시 닫힘
<div className="fixed inset-0 z-[9999]" onClick={onClose}>
{/* 시각용 오버레이 */}
<div className="absolute inset-0 bg-black/40" />
{/* 모달 래퍼*/}
<div className="absolute inset-0 flex items-center justify-center p-8">
{/* 모달 본체 클릭은 닫기 막음. */}
<div
className={[
"relative overflow-hidden rounded-[20px] bg-white shadow-xl",
"w-[1240px] h-[720px] max-w-[95vw] max-h-[95vh]",
].join(" ")}
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between px-8 border-b mt-6 border-white">
... //공통헤더
</div>
<div className="h-[calc(720px-56px)] overflow-y-auto">
{children} //모달페이지 내용
</div>
</div>
</div>
</div>
);
}
이후 다른 자식 팝업페이지에서 아래와같은 방식으로 ModalShell을 활용하면 된다.
import ModalShell from "./ModalShell.jsx";
...
export default function ChildrenModalPage() {
...
return (
<ModalShell onClose={close}>
...
</ModalShell>
);
}
배운점
이번 구현을 통해 단순히 “모달을 띄운다”는 기능도 이벤트 처리, 스크롤 제어, 레이어 구조 등 여러 요소를 함께 고려해야 한다는 걸 체감했다.
다음 단계로는
- 모달 스택 관리, 여러 모달이 겹칠 때 스크롤 처리, 접근성
등을 개선해볼 계획이다. 무조건 라이브러리 쓰는거보다 깡구현도 재밌는 점이 있는 듯 하다.