안녕하세요. 오픈놀 프론트엔드 개발자 제리님 입니다. 오픈놀의 미니인턴 사이트에서는 참여자가 신청을 할 수 있도록 신청양식을 작성하거나, 이력서를 작성하는 등의 사용자가 양식을 작성하는 일이 많습니다. 사용자는 실수로 양식을 작성하는 중 페이지를 이탈하게 되어 작성해둔 양식을 잃어버리는 경우가 많습니다. 특히, 이력서 처럼 장문의 글을 작성중에 실수로 페이지를 이탈하여 작성한 글을 잃어버리게 되었을때는 분노가 차오르게 됩니다.
이를 방지하기 위해 미니인턴 서비스에서 사용자가 양식을 작성하는 도중에 페이지를 이탈하는 것을 방지하기로 하였습니다. 여러 사이트를 사용하다보면 다음과 같은 팝업을 자주 보셨으리라고 생각합니다.
이는 양식을 작성한 후에 새로고침 혹은 다른 사이트로 이동하게 될 경우에 볼 수 있는 팝업입니다. 우선적으로 이를 구현하기 위해 window의 beforeunload 이벤트를 이용하여 쉽게 구현할 수 있습니다.
//* 새로고침 및 타 사이트 이동 방지
const handleBeforeunload = (e: BeforeUnloadEvent) => {
if (hasChanged) {
e.preventDefault();
e.returnValue = '';
return '';
}
return undefined;
};
useEffect(() => {
window.addEventListener('beforeunload', handleBeforeunload);
return () => {
window.removeEventListener('beforeunload', handleBeforeunload);
};
}, [hasChanged]);
하지만 새로고침과 타 사이트 이동이 아닌 클라이언트 측 네비게이션을 이용하여 라우팅을하게될 경우에는 팝업이 나타나지 않았습니다. 클라이언트 측 네비게이션 시에도 팝업이 나타나서 사용자의 이탈을 방지하길 원했고 구글링을 해보며 가능성과 방법을 찾았습니다. 에어비앤비에서 호스트 등록시 페이지에서 이탈을 시도할 경우 다음과 같은 팝업이 나타나는 것을 볼 수 있었습니다.
가능성을 확인하여 디자인팀에게 모달 디자인을 요청하여 다음과 같이 만들도록 하기로 하였습니다. 다음은 미니인턴의 이탈 방지 모달의 예시입니다.
팝업을 구현하기위한 고려해야할 요소는 다음과 같습니다.
1. 사용자가 양식을 입력하여 양식이 변경됨(hasChanged)
2. 사용자가 이동하기를 클릭함(confirmed)
3. 이동할 url(toUrl)
3. 모달 띄우기(useModal)
우선 사용자가 페이지를 이동하는 것을 감지하여야 합니다. 이를 위해서 'next/router'의 'routeChangeStart' 이벤트를 이용하여 라우트 변경시 이벤트헨들러를 실행합니다.
useEffect(() => {
window.addEventListener('beforeunload', handleBeforeunload);
Router.events.on('routeChangeStart', routeChangeStart);
Router.events.on('routeChangeError', routerChangeError);
return () => {
window.removeEventListener('beforeunload', handleBeforeunload);
Router.events.off('routeChangeStart', routeChangeStart);
Router.events.off('routeChangeError', routerChangeError);
};
}, [confirmed, hasChanged]);
이벤트헨들러는 인자로 이동하게될 url을 받게되고, 현재주소와 url을 비교하여 이동하는지 여부를 파악합니다. 이동을하게 될때 양식이 변경되었다면(hasChanged) 모달을 띄우게합니다. 이때 에러를 발생시켜 라우트 이동을 막을 수 있습니다.
const routeChangeStart = (url: string) => {
//? 다른 url로 가고 / 변경이 있고/ 승인이 안되었다면
if (
decodeURI(Router.asPath).split('?')[0] !== decodeURI(url).split('?')[0] &&
hasChanged &&
!confirmed
) {
setToUrl(url);
openModal();
Router.events.emit('routeChangeError');
throw 'Abort route change. Please ignore this error.';
}
};
spilt('?')를 사용하여 url 파라미터로 인한 불일치를 막습니다.
라우트 이동을 막았다면 사용자가 '이동하기'버튼을 클릭하여 페이지 이탈을 원할때 이동시키도록합니다. 이를위해 이동 할 url 을 state 에 저장해두도록 합니다. 그리고 사용자가 '이동하기'를 클릭하였음(confirmed)을 분기하기 위하여 confirmed 값을 state 에 저장합니다.
const [toUrl, setToUrl] = useState('');
const [confirmed, setConfirmed] = useState(false);
유저가 '이동하기' 버튼을 클릭하게 된다면 confirmed 를 true 로 바꾸게됩니다.
onClick={() => setConfirmed(true)}}
그리고 useEffect 를 사용하여 conffirmd 가 변경되었을 때 모달을 종료하고 이동하도록 합니다.
useEffect(() => {
if (confirmed) {
closePortal();
Router.push(toUrl);
}
}, [toUrl, confirmed]);
이렇게 이탈 방지 컴포넌트가 만들어지게 되었습니다.
저희는 이 이탈 방지 모달을 공통 컴포넌트로 만들어 여러곳에서 사용하도록 하였습니다.
이를 위해서 컴포넌트는 양식이 입력되었는지를 확인하는 hasChanged 값만을 props 로 받도록 하여 여러곳에서 사용할 수 있도록 하였습니다.
결과적으로, 여러 페이지에서 사용자의 실수로 인한 이탈을 방지하여 사용자의 불편함을 줄일 수 있게되었습니다. 미니인턴에서는 이러한 사용자의 불편함을 줄이기 위해 계속해서 방법을 찾아내고 있습니다.^^
오픈놀 미니인턴팀에 합류하세요!. 미니인턴은 채용중~
https://www.wanted.co.kr/wd/52637
미니인턴팀 응원합니다~