Posts
웹 복귀, 이탈 이벤트 감지하기

웹 복귀, 이탈 이벤트 감지하기

visibilityChange로 웹 페이지의 복귀, 이탈 이벤트를 다뤄보자.

Overview

사용자가 웹 페이지를 떠날 때 react-query의 refetch를 중단하거나, 다른 탭에서 다시 복귀했을 경우 클라이언트 데이터를 최신화 해주어야할 상황이 있다. 이러한 기능이 필요할 때 사용 할 수 있는 visibilityStatevisibilitychange를 알아보자.

웹 페이지에서 사용자에게 실제로 페이지를 떠날 것인지 묻는 확인 대화 상자 표시는 beforeunload 이벤트를 사용한다.

웹 페이지에서 visibility 다루기

Document 객체에 내장된 Document.visibilityState는 다음과 같이 document의 가시성 1 상태를 반환한다.

  • visible: 페이지가 최소화 되지 않은 창(브라우저)에서의 선택된 탭.
  • hidden: document가 background-tap(다른 탭)이거나, 최소화 된 창의 일부이거나, OS 화면 잠금이 활성 상태임을 의미.

Document.visibilityState의 값이 변경되면 visibilitychange 이벤트가 발생하여 웹 페이지 가시성에 따른 동작을 처리할 수 있다. 즉, visibilitychange 이벤트를 통해 Document.visibilityState의 변경을 감지한다.

Usages
// 1. addEventListener()에 이벤트 이름 전달하기
document.addEventListener('visibilitychange', (event) => {
  console.log(document.visibilityState);
});
 
// 2. 이벤트 처리기 속성 사용하기
document.onvisibilitychange = (event) => {
  console.log(document.visibilityState);
};

React에서 사용하기

React에서 웹 가시성 로직을 구현하기 위해서 useEffect를 활용한다.

useEffect(() => {
  const handleVisibilityChange = () => {
    // 페이지 복귀
    if (document.visibilityState === 'visible') {
      console.log('복귀');
    }
 
    // 페이지 이탈
    if (document.visibilityState === 'hidden') {
      console.log('이탈');
    }
  };
 
  document.addEventListener('visibilitychange', handleVisibilityChange);
 
  return () => {
    // 메모리 누수 방지
    document.removeEventListener('visibilitychange', handleVisibilityChange);
  };
}, []);

컴포넌트 라이프사이클 중 언마운트 시에 이벤트 제거를 통해 이벤트가 반복적으로 쌓이는 문제를 방지한다.

Custom hook

이를 활용해 React에서 웹 가시성 상태를 출력하는 커스텀 훅을 만들 수 있다.

usePageVisibility.ts
import { useEffect, useState } from 'react';
 
type VisibilityState = 'visible' | 'hidden';
 
const usePageVisibility = <VisibilityState>() => {
  const [visibilityStatus, setVisibilityStatus] = useState(document.visibilityState);
 
  useEffect(() => {
    const handleVisibilityChange = () => {
      setVisibilityStatus(document.visibilityState);
    };
 
    document.addEventListener('visibilitychange', handleVisibilityChange);
 
    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, []);
 
  return { visibility: visibilityStatus };
};
Usage of usePageVisibility.ts
const MyComponent = () => {
  const { visibility } = usePageVisibility();
 
  return (
    <div>
      <p>Current visibility state: {visibility}</p>
    </div>
  );
};

Footnotes

  1. 눈에 띄는 정도