-
뒤로가기/새로고침 시 '정말 나가시겠습니까?' 모달 만들기Studying/React 2023. 7. 18. 00:14
개요
Portfolly 프로젝트에서 '포트폴리오 작성 페이지' 구현 중, 에디터를 통한 게시물 작성 중에 뒤로가기/새로고침 클릭 시 현재 페이지를 정말 나갈 것인지 확인하는 경고 모달을 띄워야 했습니다.
브라우저 뒤로가기 이벤트
뒤로가기 버튼을 클릭했을 때 경고 모달을 띄우려면, 우선 뒤로가기 이벤트를 감지해야합니다.
window 객체는 활성 히스토리(active history)가 변하게 되면 popstate 이벤트를 발생시킵니다. 즉 뒤로가기, 앞으로가기를 클릭해서 직전/직후 히스토리로 이동하게 되면 발생한느 이벤트입니다.
현재 목표는 뒤로가기 버튼을 클릭했을 때 곧바로 페이지가 이동하지 않고, 한 번 경고 알람을 띄우는 것입니다.
어떻게 해야 뒤로가기 버튼을 클릭했는데도 뒤로 가지 않을 수 있을까요?
방법은 단순합니다. '뒤로 가기'라는 건 바로 직후의 히스토리로 이동하는 것이므로, 뒤로 가기 버튼을 누르는 순간 현재 페이지(에디터 페이지)를 히스토리에 담아 페이지를 제자리 걸음 하게 만드는 것입니다.
그러려면 세션 히스토리에 접근해야하는데, 다행히도 History API를 사용하여 간편하게 접근이 가능합니다.
History API는 세션 히스토리에 접근할 수 있는 다양한 메서드를 담은 객체입니다.
아래와 같은 코드를 통해 페이지를 강제로 이동시킬 수 있습니다.
/* 페이지 강제 이동 메서드 */ // 브라우저 뒤로 가기 window.histroy.back(); // 브라우저 앞으로 가기 window.histroy.forward(); // 지정한 위치로 가기 window.history.go(-1); // 한 페이지 뒤로 가기 window.history.go(2); // 두 페이지 앞으로 가기 window.history.go(-3); // 세 페이지 뒤로 가기
하지만 당장 사용해야 하는 것은 페이지 이동이 아닌 히스토리 조작입니다.
다음과 같은 두 개의 메서드를 통해 세션 히스토리에 접근해 스택을 변경시킬 수 있습니다.
/* 세션 히스토리 조작 메서드 */ history.pushState(state, title, url); // 세션 히스토리에 상태를 추가 history.replaceState(state, title, url); // 세션 히스토리 현재 상태를 변경
- pushState
- 세션 히스토리에 상태를 추가한다.
- 페이지 갱신 없이 url만 바꿔 페이지를 보여준다.
- 브라우저 뒤로 가기 버튼이 활성화 된다.
- replaceState
- 세션 히스토리에서 현재 상태를 변경한다.
- 현재 상태 또는 url을 업데이트 할 경우에 사용된다.
- 현재 상태를 변경하는 것이므로 뒤로가기는 할 수 없다.(히스토리 스택에 A > B 와 같이 쌓이는 게 아니라, A 자체가 B로 바뀌는 것이므로 뒤로가기 할 히스토리가 없을 경우 뒤로가기 불가능)
둘 중에서 사용할 메서드는 pushState 입니다.
기존에 브라우저는 페이지를 이동할 경우 window.onpopstate 이벤트를 발생시킵니다. 하지만 pushState로 히스토리에 상태를 추가해 이동할 경우에는 popstate가 발생하지 않고, 그 상태에서 뒤로가기/앞으로가기를 클릭해야 popState가 발생합니다.
이를 활용하면 다음과 같습니다.
1. pushState로 에디터 페이지 히스토리를 하나 추가한다.
2. 뒤로가기 했지만 제자리 걸음 하게 된다.
3. 뒤로가기 버튼 클릭으로 인해 발생하는 popstate 이벤트 핸들러로 경고 모달을 띄운다.
코드로 작성하면 다음과 같습니다.
import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; export default function usePreventGoBack() { const navigate = useNavigate(); // popstate 이벤트 리스너 const preventGoBack = (url?: string) => { const isGoBack = confirm('사이트에서 나가시겠습니까? 작성된 내용은 저장되지 않습니다.'); if(isGoBack && url){ navigate(url); return; } navigate(-2); }; useEffect(() => { history.pushState(null, "", location.href); // 1. 히스토리 하나 추가 window.addEventListener("popstate", ()=>preventGoBack); // 2. popstate 이벤트 리스너 등록 return () => { window.removeEventListener("popstate", ()=>preventGoBack); // 이벤트가 누적되지 않도록 삭제 }; }, []); return { preventGoBack }; }
window.addEventListner로 popstate 이벤트 리스너가 잘 등록되었는지 확인하고 싶다면 크롬 개발자 도구 콘솔창 용 api인 getEventListener를 사용하면 됩니다. (⚠️스크립트에서는 사용 불가능합니다!)
크롬 콘솔창을 열고 다음 코드을 입력하면 현재 등록된 이벤트 리스너를 확인할 수 있습니다.
getEventListeners(window) getEventListeners(document)
useEffect 반환 함수로 popstate 이벤트 리스너를 해제시킨 이유는 이를 해제하지 않을 시 이벤트 객체가 매번 누적되어 쌓이기 때문입니다. 반드시 이벤트 등록 후 해제하는 코드도 작성해줘야 합니다.
브라우저 새로고침 이벤트
뒤로가기와 마찬가지로 새로 고침 이벤트가 발생했을 때 동작할 핸들러를 작성합니다.
창이 닫히거나 새로고침을 할 때 window 객체는 beforeunload 이벤트를 발생시킵니다. 해당 이벤트는 리소스가 unload 되기 직전에 발생하며, 창닫힘/새로고침을 취소할 수 있는 기회를 제공합니다.
따라서 직접 경고문 모달을 띄워야 하는 뒤로가기와 달리, 경고문구도 자동으로 나타나고, event.preventDefault()로 단순하게 행동을 취소할 수 있습니다.
코드로 나타내면 다음과 같습니다.
import { useEffect } from 'react'; export default function usePreventRefresh() { const preventRefresh = (event: BeforeUnloadEvent) => { event.preventDefault(); event.returnValue = ""; }; useEffect(() => { window.addEventListener("beforeunload", preventRefresh); return () => { window.removeEventListener("beforeunload", preventRefresh); }; }, []); }
참고 사이트 & 함께 공부한 사이트
'Studying > React' 카테고리의 다른 글
로그인 정보를 리덕스로 관리하는 이유에 대하여 (0) 2023.08.02 [React-Quill] inline style 적용하기 (0) 2023.07.22 Custom Hook에 ref를 전달하기 (Feat. React-Quill) (0) 2023.07.14 [react-quill] 커스텀 이미지 핸들러 구현하기 (0) 2023.07.14 Warning: React does not recognize the ` ` prop on a DOM element (0) 2023.07.12 - pushState