모바일 웹에서 모달을 사용하면서 마주했던 문제에 대해 정리를 해두려 한다.
두 가지 문제가 있었는데, 모달을 띄웠을 때 뒷배경이 스크롤 되는 문제와 뒤로 가기 시 브라우저가 닫혀버리는 문제이다.
스크롤 동작 막기
뒷 배경이 스클로 되는 문제는 웹에서도 자주 다루기 때문에 간단하게 넘어가겠다.
이벤트 버블링으로 인해 스크롤 이벤트가 body까지 전달되어 스크롤이 되는 현상으로 body에서 이를 제어해주기만 하면 된다.
{isOpen && <MyModal onClose={handleModalOpen} />}
예를 들어 open state를 통해 렌더되는 모달이 있다 가정한다. (handleModalOpen은 setOpen 제어 함수이다.)
open이 true가 될 때 body의 스크롤 동작을 막아주고, false가 될 때 다시 열어주면 될 것 같다.
useEffect(() => {
if (isOpen) document.body.style.overflow = 'hidden';
else document.body.style.removeProperty('overflow');
}, [oepn]);
이렇게 하면 간단하게 모달 바깥의 스크롤을 막아줄 수 있다.
여러 컴포넌트에서 사용할 생각이라면 이렇게 훅으로 만들어도 된다.
import { useEffect } from 'react';
const useScrollLock = (isLocked) => {
useEffect(() => {
if (isLocked) document.body.style.overflow = 'hidden';
else document.body.style.removeProperty('overflow');
return () => {
document.body.style.removeProperty('overflow');
};
}, [isLocked]);
};
export default useScrollLock;
뒤로가기 동작 제어하기
웹에서는 모달을 닫기 위해 뒤로 가기 동작을 잘 하지 않아 컨트롤 할 일이 거의 없지만, 모바일에서는 모달을 닫기 위해 뒤로 가기를 누르는 패턴은 자연스럽다. 이걸 별도로 처리해주지 않으면 브라우저에서 이전 페이지로 동작하거나 닫혀버리기 때문에 제어가 필요하다.
약간의 편법을 사용하여 이걸 해결할 수 있다. 모달이 열릴 때 브라우저 히스토리를 강제로 하나 추가해준다. 그럼 뒤로 가기 시 이전 페이지로 이동하는 대신 내가 강제로 주입한 히스토리 하나를 pop하게 된다. 그리고 다른 동작으로 모달이 닫힐 때도 이걸 제거해주면 된다.
뒤로 가기를 제어하는 popstate 이벤트를 등록해준다.
useEffect(() => {
const handlePopState = () => setIsOpen(false);
window.addEventListener('popstate', handlePopState);
return () => {
window.removeEventListener('popstate', handlePopState);
};
}, []);
이렇게 하면 뒤로가기 시 state를 변경시켜 모달을 닫게 할 수 있고,
const handleModal = useCallback((open) => {
if (open) window.history.pushState(null, '');
else window.history.replaceState(null, '');
setIsOpen(open);
}, []);
해당 함수를 통해 히스토리를 주입하고, 제외할 수 있다
useEffect를 사용하지 않은 이유는 뒤로 가기 동작으로 state 변화가 일어났을 때는 히스토리를 빼 줄 필요가 없기 때문이다.
보통 모달을 이중으로 설계하는 경우는 많이 없기 때문에 이것도 훅으로 만들어서 사용했다.
import { useState, useEffect, useCallback } from 'react';
function useModalWithHistory(initialState = false) {
const [isOpen, setIsOpen] = useState(initialState);
const handleOpen = useCallback((open) => {
if (open) window.history.pushState(null, '');
else window.history.replaceState(null, '');
setIsOpen(open);
}, []);
useEffect(() => {
const handlePopState = () => setIsOpen(false);
window.addEventListener('popstate', handlePopState);
return () => {
window.removeEventListener('popstate', handlePopState);
};
}, []);
return [isOpen, handleOpen];
}
export default useModalWithHistory;
이렇게 설계하면 모바일 웹에서 뒤로 가기 동작을 쉽게 제어할 수 있다.
const [isOpen, handleOpen] = useModalWithHistory(false);
이렇게 사용할 수 있고, 이제 브라우저 히스토리를 생각하지 않고 handleOpen을 통해 모달을 여닫아 주면 된다.
'개발일기 > Web' 카테고리의 다른 글
[Open Graph] 메타 태그로 오픈 그래프 사용하기 (feat. 카카오톡 링크 공유 시 미리보기) (2) | 2024.11.19 |
---|---|
vercel에 한글 도메인 적용하기 (0) | 2024.11.18 |
[React] 카카오 맵 커스텀을 해보았다. (feat. 마커 인포 꾸미기) (3) | 2024.11.13 |
[React] vercel에 API KEY 등록하기 (feat. api key 숨기기) (0) | 2024.11.11 |
[React] react-beautiful-dnd로 컴포넌트 drag&drop 구현하기 (0) | 2023.11.13 |