이 프로젝트는 https://uxpirates.xyz/ 에 대한 내용입니다!
진행했던 프로젝트에서 화면은 모달창으로 띄워야하는데, 각 모달창에서 링크 공유를 누르면 링크가 공유되고, 공유된 링크에 접속하면 그 모달창이 그대로 띄워져 있어야 하는 기능을 구현해야 했습니다.
결론적으로 제가 설계한 방식은, 홈 화면에 있는 여러개의 미리보기 컴포넌트
중 하나를 클릭하면 해당 컴포넌트의 id를 param으로 가지는 url로 이동하도록 하는 것이었습니다.
겉으로 보기에는 페이지 이동이 아닌 모달창으로 보여져야 했기 때문에, url은 이동시키되 화면은 모달로 구현해야 했던 것이었는데,
아래 gif에서 볼 수 있는 것처럼 Home 컴포넌트에서 여러 개의 ColumnPreview
를 클릭하면 모달 형태로 세부 정보를 표시하고, URL이 /detail/:id
형식으로 변경되도록 구현해주었습니다.
하지만 크게 3가지의 문제가 발생했는데,
이 문제를 해결하기 위해 여러 가지 시도를 해보았고, 결국 어떻게 설계하고 구현했는지를 이번 포스팅에서 작성해보려 합니다.
가장 먼저 시도한 방법은 모달을 열기 전에 현재 스크롤 위치를 저장하고, 모달을 닫을 때 저장된 위치로 복원하는 방식이었습니다. 이를 위해 window.scrollY
값을 저장하고 복원하는 코드로 다음과 같이 구현했습니다:
const savedScrollTop = useRef(0);
const handleOpenModal = () => {
savedScrollTop.current = window.scrollY; // 현재 스크롤 위치 저장
setIsOpenedDetailModal(true);
};
const handleCloseModal = () => {
setIsOpenedDetailModal(false);
setTimeout(() => {
window.scrollTo({ top: savedScrollTop.current, behavior: "smooth" }); // 스크롤 복원
}, 0);
};
문제점
pushState
) 후 DOM이 리렌더링되면서 브라우저가 스크롤을 0으로 강제로 이동.모달이 열려 있을 때 배경 스크롤을 막기 위해 document.body
의 overflow
스타일을 조정했습니다:
useEffect(() => {
if (isOpenedDetailModal) {
document.body.style.overflow = "hidden"; // 배경 스크롤 막기
} else {
document.body.style.overflow = "auto"; // 배경 스크롤 복원
}
}, [isOpenedDetailModal]);
문제점
overflow: hidden
만으로는 스크롤 위치를 유지할 수 없었음.모달을 열 때 URL이 변경되므로, URL 변경 후 스크롤 위치를 강제로 복원하는 방식도 시도했습니다:
useEffect(() => {
if (selectedDetailId) {
// 모달 열기 시 기존 위치 유지
setTimeout(() => {
window.scrollTo({ top: savedScrollTop.current });
}, 0);
}
}, [selectedDetailId]);
문제점
style.top
을 활용한 고정위 시도들에서 발생한 문제를 근본적으로 해결하기 위해, 스크롤 위치를 저장한 후 document.body
의 스타일 속성을 활용하여 스크롤을 고정하고 복원하는 방식을 최종적으로 선택했습니다.
window.scrollY
로 저장.document.body.style.position
을 fixed
로 설정하여 스크롤을 고정.style.top
에 현재 스크롤 값을 음수로 설정하여 고정된 위치에서도 화면이 움직이지 않도록 처리.document.body.style.position
과 style.top
값을 초기화하여 원래 상태로 복원.window.scrollTo
로 복원.여러 방식을 거친 끝에 최종적으로 배포된 코드의 일부입니다!
useEffect(() => {
if (isOpenedDetailModal || isMobileMenuOpen) {
const scrollY = window.scrollY; // 현재 스크롤 위치 저장
document.body.style.position = "fixed";
document.body.style.top = `-${scrollY}px`;
document.body.style.left = "0";
document.body.style.right = "0";
document.body.style.width = "100%";
} else {
const scrollY = parseInt(document.body.style.top || "0", 10) * -1; // 스크롤 복원
document.body.style.position = "";
document.body.style.top = "";
window.scrollTo({ top: scrollY, behavior: "auto" });
}
}, [isOpenedDetailModal, isMobileMenuOpen]);
결론적으로 모달을 열고 닫을 때 스크롤 위치가 유지되었고, 열기 전/후의 위치를 완벽하게 유지시킬 수 있었습니다.
그리고 배경 스크롤을 고정해주었고, 모바일 환경에서도 문제 없이 잘 동작하는 것을 확인할 수 있었습니다!!
최종 구현된 모습은 아래 gif와 같습니다.