React에서 웹브라우저 뒤로가기 동작 
커스텀 하기

sumi-0011·2024년 1월 9일
1

⏰ 10MM 개발기

목록 보기
1/5
post-thumbnail

시간을 기록하는 페이지를 만들며,
해당 페이지에 이탈하는 경우 사용자에게 특정 모달을 보여주어야하는 요구사항이 있었습니다 👀

이탈하는 경우는 여러가지가 있겠지만,
그중 브라우저의 뒤로가기 버튼이나, 스와이프 등으로 뒤로 나가지는 현상을 막아야 했는데,
이때 브라우저의 뒤로가기 이벤트를 커스텀 하기 쉽게 custom hook을 만들어 보았습니다.

Next.js : 13.4
React : 18v

요구사항

브라우저의 뒤로가기 이벤트가 실행이 되면, 기본 동작을 막고 원하는 동작을 실행해야합니다.

뒤로가기 동작을 어떻게 방지할 수 있을까?

const browserPreventEvent = (event: () => void) => {
  history.pushState(null, '', location.href);
  event();
};

해당 코드에서는 history.pushState 의 인자로 현재 페이지(location.href)를 넘겨줌으로써 현재 페이지를 히스토리 스택에 추가합니다.

그래서 사용자가 뒤로가기 버튼을 눌렀을 때,
뒤로가기한 페이지가 현재 페이지 주소와 같기 때무에 페이지는 그 전 페이지로 돌아가지 않습니다.

대신, popstate 이벤트가 발생하여 browserPreventEvent 메소드가 재 실행되고,
현재 페이지를 다시 히스토리 스택에 쌓은 후 사용자가 원하는 동작 event 메소드를 실행합니다.

🤔 history.pushState 메소드가 무엇인가요?
history.pushState 메소드는 브라우저의 히스토리 스택에 새로운 상태를 추가합니다.이 메소드는 페이지를 새로고침하거나 로딩하지 않고도 URL을 변경할 수 있게 해주는 HTML5 History API의 일부입니다.

🤔 popstate 이벤트가 왜 발생할까요?
history.pushState 메서드를 사용하여 새로운 히스토리 엔트리를 추가하면, 실질적으로 브라우저의 히스토리 스택에 현재 페이지 위치가 두 번 존재하게 됩니다.
따라서, 사용자가 뒤로가기 버튼을 클릭하면 실제로는 동일한 url로 이동하게 되지만, 브라우저는 히스토리 엔트리가 변경되었다고 판단하여 popstate 이벤트를 발생시키게 됩니다.


useEffect(() => {
		const handlePopstate = () => { 
      browserPreventEvent(customBack);
    };
    history.pushState(null, '', location.href);
    window.addEventListener('popstate', handlePopstate);
    return () => {
      window.removeEventListener('popstate', handlePopstate);
    };
  }, []);

우선, 컴포넌트가 마운트 될 때 useEffect를 한번 실행합니다.

이때, 현재 페이지의 url을 히스토리 스택에 쌓습니다. → 이로인해 뒤로가기 버튼을 클릭해도 이전 페이지로 돌아가지 않습니다.

뒤로가기 버튼이 클릭되면, popstate 이벤트가 발생되며 browserPreventEvent 메소드를 실행합니다.
browserPreventEvent 에서는 현재페이지를 히스토리 스택에 쌓은 후 커스톰 동작을 실행합니다.

컴포넌트가 언마운트 되면, 클린업 함수를 통해 popstate 이벤트 리스너를 제거합니다.

🤔 컴포넌트가 언마운트 될 때 popstate 이벤트 리스너를 제거하는 이유가 무엇일까요?
컴포넌트가 언마운트될 때 이벤트 리스너를 제거하는 것은 메모리 누수를 방지하기 위한 중요한 작업입니다.
만약, 이벤트 리스너를 제거하지 않는다면,
해당 이벤트 리스너가 해당 컴포넌트에 대한 참조를 유지하고 있기 때문에,
가비지 컬렉터가 컴포넌트를 메모리에서 없애지 못해 메모리 누수가 발생할 수 있습니다.

🚨 이벤트 리스너를 제거할 때 익명함수를 쓰면 제대로 제거가 될까?
⇒ NO!!

addEventListenerremoveEventListener 메서드는 이벤트 리스너를 추가하고 제거할 때 정확히 동일한 함수를 참조해야하는데, 익명함수를 사용하게 된다면 addEventListenerremoveEventListener에서 참조하는 함수가 서로 다르게 됩니다.
따라서, 하나의 함수를 정의하고, addEventListenerremoveEventListener가 동일한 함수를 참조하도록 하여야합니다.

전체 코드

const browserPreventEvent = (event: () => void) => {
  history.pushState(null, '', location.href);
  event();
};

function useCustomBack(customBack: () => void) {
  const browserPreventEvent = (event: () => void) => {
    history.pushState(null, '', location.href);
    event();
  };

  useEffect(() => {
		const handlePopstate = () => { 
      browserPreventEvent(customBack);
    };
    history.pushState(null, '', location.href);
    window.addEventListener('popstate', handlePopstate);
    return () => {
      window.removeEventListener('popstate', handlePopstate);
    };
  }, []);
}

기존에 있는 코드에서 일부 개선한 내용입니다.

해당 코드는 뒤로가기 동작만을 커스텀 하는데 문제는 없습니다.
단, 제가 구현하려고 하였던 뒤로가기시 모달을 띄우는 과정에서는 약간의 문제가 있어 새로운 포스팅을 작성하였습니다.
포스트링크

profile
안녕하세요 😚

0개의 댓글