이 부분에 대한 질문을 받아서 생각난 김에 정리해두기!
실제로 pongpong 마지막 평가 때 브라우저를 닫았다가 다시 서비스를 이용하려고 할 때 에러가 발생한 적이 있었다.
pongpong은 소켓통신과 REST API 통신을 함께 사용하는 서비스!
그 당시, 백엔드에서 처리한 걸로 기억이 나서 그렇게 답변했는데, 프론트에서 처리해야 한다는 얘기를 들어서 다시 한번 정리해본다!
useEffect()의 return을 통해 정리 함수 실행하기 (X)
브라우저 창이 닫히면 JS의 실행 컨텍스트가 종료된다.
따라서 브라우저 내 코드들은 더이상 실행될 수 없고, 따라서 정리 함수도 실행될 수 없다.
socket이 끊기므로 백엔드에서 이를 감지할수 있다 (O)
socket이 끊기면 백엔드에서 이를 감지하고, 연결중인 소켓 리스트에서 해당 클라이언트를 제거해준다.
(Socket.io 기준 서버로 disconnect 이벤트가 전송됨)
우리가 안되었던 부분도 백엔드 코드 상에서 끊어진 소켓을 그대로 연결하고 있어서 발생했던 문제.
그렇다면 프론트에서 처리해야 하는 것은 뭐가 있을까?
또 소켓 통신이 아닌 경우, 서버로 제대로 로그아웃을 전달하고 종료하려면 어떻게 해야 할까?
beforeunload 이벤트를 감지하고 로그아웃 처리하기 (O)
아마 윈도우가 그냥 닫히는 경우 로그아웃(토큰이 쿠키나 로컬스토리지에 있다면 삭제해주기) 처리를 해줘야 한다는 얘기인 것 같다!
서버 사이드에서는 소켓 disconnect 이후 적절한 처리를 해줄 수 있지만,
클라이언트 사이드에서는 그렇지 않다.
우리 프로젝트 같은 경우에는 엑세스 토큰은 메모리 상에 있어서 휘발이 되지만, 리프레시 토큰은 만료 시간이 남은 채로 쿠키에 남아있게 된다.
또한 다른 프로젝트는 엑세스 토큰 자체가 쿠키로 되어있다.
예전에 회원가입 정보 입력 중에 페이지 이탈에 대한 이벤트 처리에 대해 많이 고민하고 시도해봤지만, 결국 실패했던 경험이 있다.
이 기회에 그 부분까지 한번에 고민하고 테스트 해봤다.
before이 붙은 차이가 있듯이, unload는 언로딩 중의 이벤트이고
beforeunload는 unload가 되기 직전의 이벤트이다.
unload는 취소가 불가능하고, beforeunload는 취소가 가능하다고 되어있는데,
브라우저 호환성을 확인해보면 사실상 beforeunload도 취소가 안된다고 보는게 낫다.
(여기서의 취소는 event.preventDefault() = unload 취소의 가능 여부)
// 방법 1
window.onbeforeunload = () => {
return "";
};
// 방법 2
window.addEventListener("beforeunload", (e) => {
e.returnValue = "";
});
이 방법을 통해 드디어 새로고침하거나, 탭을 닫으려고 할 때 해당 콜백 함수를 등록해둔 페이지에서
(크롬 기준) “사이트에서 나가시겠습니까? / 새로고침 하시겠습니까?” + “변경사항이 저장되지 않을 수 있습니다.” alert이 뜨는 걸 확인할 수 있었다.
오래된 브라우저에서는 return하는 string으로 팝업 내용이 표시된다고 한다.
내 프로젝트의 경우 form이 존재하는 페이지를 언로딩할 때 입력중인 input 내용이 있을 때만 확인 alert을 띄우기 위해서, input을 핸들링하는 state들을 조건으로 걸어 return 해주는 방식으로 사용했다.
자 이제 해결이 다 된 것 같은데 문제가 또 있다!
해당 이벤트 콜백함수를 이렇게 작성했다.
window.onbeforeunload = () => {
logout();
};
문제 1. 새로고침할 때도 logout 처리가 된다.
문제 2. 창이 닫히는 경우 logout() 함수 내부의 api 요청을 기다리지 않고 종료가 된다.
문제 3. SPA에서는 뒤로 가기/앞으로 가기 버튼이나 NavBar 클릭을 통해 이동을 해도 페이지 이탈 이벤트가 발생하지 않는다.
이제 원하는 바를 완전히 구현하려면 이와 같은 문제 상황이 발생되는 점에 대해 고려해봐야겠다.