ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React] Infinite Scroll 구현하기 : Intersection Observer API
    Frontend/React 2022. 2. 14. 04:22

    인피니티 스크롤이란 컴포넌트를 한 번에 불러오는 것이 아니라 스크롤 하면서새 컴포넌트가 렌더되는 것을 말한다.

    탐색 위주의 모바일 친화적이며, 컨텐츠를 소비하는 속성을 가진 웹사이트라면 인피니트 스크롤을 적용하는 것이 쾌적한 사용자 경험을 제공하는 데 도움이 된다.

     

    Intersection Observer API 

    The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.

     

    Intersection Observer API는 브라우저의 뷰포트와 대상 요소의 교차점을 관찰하며 변경 사항을 비동기적으로 관찰하는 방식을 제공한다. 쉽게 말하면 우리는 관찰 대상 요소를 정하고 뷰포트에 그 요소가 보여졌을 때 Intersection Observer를 통해 관찰할 수 있는 것이다. 이를 통해 지정한 요소가 화면에 나타나면 컴포넌트를 렌더링하도록 만들 수 있다. Intersection Observer는 다음과 같은 상황에서 유용하게 쓰일 수 있다.

     

    • 페이지를 스크롤할 때 이미지 또는 기타 콘텐츠를 lazy-loading위해
    • 스크롤하면서 점점 더 많은 콘텐츠가 로드 및 렌더링되는 무한 스크롤 웹 사이트 구현하기 위해
    • 광고 가시성을 확인하여 수익 계산을 위해
    • 사용자가 결과를 볼 수 있는지 여부에 따라 작업 또는 애니메이션 프로세스를 수행할지 여부를 결정하기 위해

     

    사용법

    Intersection Observer 옵션

    let options = {
      root: document.querySelector('#scrollArea'),
      rootMargin: '0px',
      threshold: 1.0
    }
    
    let observer = new IntersectionObserver(callback, options);
    • root

    대상의 가시성을 확인하기 위한 뷰포트로 사용되는 요소입니다. 관찰 대상의 상위 항목이어야 합니다. 지정되지 않았거나 브라우저 뷰포트인 경우 기본값 null입니다.

     

    • rootMargin

    루트 주변의 margin 속성.

     

    • threshold

    관찰자의 콜백이 실행되어야 하는 대상 가시성의 백분율을 나타내는 단일 숫자 또는 숫자 배열입니다. 가시성이 50% 표시를 통과할 때만 감지하려면 0.5 값을 사용할 수 있습니다. 

     

    Intersection Observer 타겟팅

    let target = document.querySelector('#listItem');
    observer.observe(target);

    관찰 대상 요소 지정하기 

     

    let callback = (entries, observer) => {
      entries.forEach(entry => {
        // Each entry describes an intersection change for one observed
        // target element:
        //   entry.boundingClientRect
        //   entry.intersectionRatio
        //   entry.intersectionRect
        //   entry.isIntersecting
        //   entry.rootBounds
        //   entry.target
        //   entry.time
      });
    };

    대상이 지정한 임계값을 충족할 때마다 IntersectionObserver 콜백이 호출됩니다. 콜백은 IntersectionObserverEntry객체 목록과 관찰자를 수신합니다.

     

    코드 활용

    내가 프로젝트에 활용한 무한 스크롤 코드는 다음과 같다.

    const SHOW_QUIZS_QUERY = gql`
    	...
    `;
    
    export default function QuizList() {
      const isQuizLoadEndVar = makeVar(false);
      const { loading, data, refetch, fetchMore } = useQuery<showQuizs>(
        SHOW_QUIZS_QUERY,
        {
        //한 번에 15개씩 불러오기
          variables: { take: 15 },
          onCompleted: () => {
            isQuizLoadEndVar(false);
          },
        }
      );  
      
      //대상요소 선택하기
      const loaderRef = useRef<any>();
      
      const handleObserver = useCallback(
        async (entries) => {
          const isQuizLoadEnd = isQuizLoadEndVar();
          if (isQuizLoadEnd) {
            return;
          }
          const target = entries[0];
          //화면에 대상 요소가 겹친다면
          if (data?.showQuizs && target.isIntersecting) {
            const lastId = data.showQuizs[data.showQuizs.length - 1].id;
            //더 불러오기
            const more = await fetchMore({
              variables: {
                lastId,
              },
            });
            //해당 요소가 마지막이면
            if (more?.data?.showQuizs?.length === 0) {
              //더 이상 불러오지 않게 한다
              isQuizLoadEndVar(true);
            }
          }
        },
        [data]
      );
    
      useEffect(() => {
      //관찰자의 콜백이 호출되는 상황을 제어하는 옵션
        const option = {
          root: null,
          rootMargin: "0px",
          threshold: 0,
        };
        //관찰자를 생성하고 handleObserver라는 콜백함수와 option이라는 인수를 받는다
        const observer = new IntersectionObserver(handleObserver, option);
        //대상 요소가 보이면 대상요소를 관찰한다
        if (loaderRef.current) {
          observer.observe(loaderRef.current);
        }
      }, [handleObserver]);
    
      return (
        <div className="grid gap-4 pb-4 sm:grid-cols-2 md:grid-cols-3 ">
            {data?.showQuizs?.map((post, index) => {
                return <Quiz key={index} post={post} />
            })}
            //Intersection Observer가 관찰할 대상 요소를 마지막에 빈 <div>로 넣기
            <div ref={loaderRef}></div>
        </div>
      );
    }

     

    느낀 점

    이렇게나 간단하게 설명했지만 사실 Intersection Observer API는 MDN에 어마어마하게 방대하게 소개되어있다. (긴글주의)

    하나씩 읽어보고 정리하면서 다시 한 번 공식 문서의 중요성을 깨달았다.

     

    참고문서

    https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

    반응형

    댓글

Designed by Tistory.