조금조금 React - IntersectionObserver

Edwin·2023년 4월 17일
4

조금조금 REACT

목록 보기
24/31
post-thumbnail

MDN문서에 따르면, Intersection Observer API는 감시하고자 하는 요소가 다른 요소(viewport)에 들어가거나 나갈때 또는 요청한 부분만큼 두 요소의 교차부분이 변경될 때 마다 실행될 콜백 함수를 등록할 수 있게 한다. 즉, 사이트는 요소의 교차를 지켜보기 위해 메인 스레드를 사용할 필요가 없어지고 브라우저는 원하는 대로 교차 영역 관리를 최적화 할 수 있다. 비록 정확히 몇 픽셀이 겹쳐졌고 어떠한 픽셀이 겹쳐졌는지 Intersection Observer API 가 알려줄 수 없지만 "N% 정도 교차하는 경우 상호작용이 이루어져야 한다." 와 같은 더 일반적인 사용 사례를 다룰 수 있게 된다.

IntersectionObserver

말이 어렵지만 가장 흔하게 사용되는 사례는 아마도 무한스크롤을 구현할 때이다. 지난주부터 정리를 하고 싶었던 주제이지만, 100% 원하는 대로 동작하지 않고, 때에 따라서 다른 부분이 있어서 새로 코드를 구현하며 시도하며, 직면하는 이슈들을 여기에 기록해보고자 한다.

import React, { useEffect, useRef } from 'react';

function Test() {
  const ref = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(entries => {
      if (entries[0].isIntersecting) {
        alert('안녕');
      }
    }, { threshold: 0.01 });

    if (ref.current) {
      observer.observe(ref.current);
    }

    return () => {
      if (ref.current) {
        observer.unobserve(ref.current);
      }
    };
  }, []);

  return (
	<div>
		<div style={{ height: "100vh" }}>위는 빈 화면입니다.</div>
 		<div ref={ref} style={{ height: "100vh" }}>
   			 여기에 스크롤을 내리면 "안녕"이라는 알림이 뜹니다.
        </div>
  </div>
  );
}

export default Test;

useRef

위의 코드가 기본 코드이다. 먼저 DOM에 접근하기 위해서는 해당 요소를 참조해야 한다. 그러기 위해서는 useRef를 선언해 주고, 참조할 요소에 ref를 달아주면 된다. 여기서 나 같은 질문을 하는 사람이 있을 수 있다. ref = useRef(null)이 값이 존재하지 않는데, 어떻게 if (ref.current) 해당 조건문이 실행되는 가이다. 이는 useEffect에 대해서 이해로 넘어가야 한다.

useEffect

useEffect는 DOM이 다 그려진 다음에 사이드로 동작하는 훅이다. 그러기에 화면이 다 그려지고 난 뒤에 해당로직이 동작하게 되는 셈이다. 그러기에 ref = useRef(null)가 읽어질 때는 null 이었지만, 화면이 렌더링 된 후에는 해당 useRef가 참조하고 있는 요소를 가리키게 된다.

1) useRef에 DOM이 감지되면, entries[0]

코드를 보면 생성자 함수로 선언한 new IntersectionObserver가 동작하도록 선언하였다. 여기서 매개변수로 사용된 entries는 IntersectionObserver에서 감지하고 있는 모든 대상들을 의미한다. 배열에 담겨있다는 것은 특정한 대상에 대한 값을 가져올 수 있다는 것을 의미한다. 해당 코드에서는 IntersectionObserver에서 감지할 대상이 하나이기 때문에 entries[0]라 기록하였다.

2) IntersectionObserver, threshold

threshold는 ref가 참조하고 있는 DOM 요소가 화면에 노출되는 영역을 나타낸다. 이때 설정값은 0~1 사이의 값으로 영역을 지정할 수 있지만, 구체적인 px 단위로는 설정할 수 없다. 예를 들어서 { threshold: 0.1 } 을 입력하면, 감지되고 있는 ref 가 하단에서 10% 위치를 지나가면 동작한다. { threshold: 0.5 }는 화면에서 50% 위치를 지나가면 동작한다. 그러나 극단적으로 동작하고 싶다면, 필자는 { threshold: 0.01 } 이라 기록했다. 살짝만 보여도 IntersectionObserver를 감지하고 alert('안녕')을 실행할 것이다.

그러나 문제 없을까?

리액트에서 다른 라우터에서 스크롤을 최하단으로 이동했다가 해당 Test 컴포넌트로 왔다. 그랬더니 바로 alert('안녕')이 실행되었다. 이는 브라우저 특성 때문인데, ChatGPT의 답변을 들어보자.

리액트는 SPA(싱글페이지애플리케이션)이다.
이것을 망각해서는 안된다. 이 때문에 브라우저는 페이지의 스크롤 위치를 기억하고 있다. 이 때문에 다른 라우터로 이동하면 원하지 않는 시점에서 IntersectionObserver가 동작할 수 있다. 이를 방지하기 위해서는 스크롤 위치를 초기화하거나 관리하는 등의 추가적인 처리가 필요하다.

이를 위해서 처리한 방법은 간단하다. useEffect를 통해서 화면이 마운트되고 난 후에, window.scrollTo(0, 0)를 실행해 주는 것이다.

useEffect(() => {
  	window.scrollTo(0, 0);
  	...
},[])
profile
신학전공자의 개발자 도전기!!

0개의 댓글