-
[React] Infinite Scroll 구현하기 : Intersection Observer APIFrontend/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
반응형'Frontend > React' 카테고리의 다른 글
[React Router v6] Nested Routes에 대해 알아보자 (0) 2022.05.12 [React Router v6] Redirect에 대해 알아보자 (0) 2022.05.08 [React] CRA 프로젝트 배포 시 이미지 경로 설정에 대해 알아보자 (0) 2022.03.20 [React] 페이지 전환에 따라 css 조절하기 (0) 2022.03.17 [Next.js] CSR vs SSR (그리고 SSG) (0) 2022.02.20