[리액트] 무한스크롤 - IntersectionObserver

Jang Seok Woo·2022년 2월 27일
0

리액트

목록 보기
38/58

scroll

스크롤이벤트를 attachment하여 현재 스크롤위치를 계산해서

맨 끝에 스크롤되었을 때 다음 데이터를 fetch하는 방법을 사용한다.

스크롤마다 한번의 이벤트가 발생하므로(거의 픽셀단위) 성능이슈를 예방하기 위해 쓰로틀링 적용을 고려해야한다.

getBoundingClientRect()

스크롤방식처럼 1픽셀 1이벤트 + 쓰로틀링을 구현하지 않고 같은 기능을 구현할 수 있다.

위치를 계산하는건 동일하지만 사각형 영역을 계산해 요소가 어디 위치해있는지를 확인하는 방법으로 getBoundingClientRect() 메소드를 사용한다

Infinite Scroll에 적용해본다면, 특정요소가 viewport 에 존재하는지 판단해 데이터 fetch를 결정할 수 있다.

여기서 viewPort란 사용자가 보고있는 페이지의 영역이다.

따라서 getBoundingClientRect는, 현재 보고있는 영역내에 이벤트를 트리거할 요소가 존재하는지 확인하기 위한 위치연산 함수인 것이다.

그러나 이 과정에서 reflow가 발생하여 스크롤로 구현하는 방법과 마찬가지로 성능이슈가 생길 수 있다.

요소의 위치, 크기를 계산하고 위치시키는 reflow를 수행하지 않는 IntersectionObserver 를 사용함으로써 이러한 문제를 해결할 수 있다.

IntersectionObserver

viewport와 특정 요소가 교차하는지를 관찰하여 특정 %이상겹쳐졌을 때 겹쳐졌음을 감지하는 기능을 제공하는 웹 API다.

사용자가 보고있는 영역이 빨간색이고 이벤트를 트리거할 요소가 하단의 InterSectRef라고 했을 때,

50%겹쳐졌을 때 이벤트를 트리거한다고 작성을 했다면 아래 그림의 시점에서 새로운 데이터를 불러오는 요청을 보내도록 구현할 수 있다.

매우 간단하게 !

구현 방법

먼저 초기화

어떤 요소를 계속 관찰해야하기 때문에 observer를 생성해주어야한다.

이 옵저버를 초기화할 때 options 인자를 전달하는데,

options은 특정요소와 viewport가 얼마만큼 겹쳤을 때 겹쳤다고 할지와 관찰대상의 부모요소를 지정하는 객체이다.

threshold가 바로 영역의 %를 뜻하는 프로퍼티이다. 0.5면, 50%겹쳐있을 때 콜백이 실행된다.

가장 중요한 것은 '무엇을' 관찰할지 인데 Js에서는 dom 선택을 사용해 할 수 있지만, 리액트는 load이전까지 html이 비어있으므로

useRef를 사용해 dom을 선택하도록 한다.

observe 메소드를 사용해 관찰할 target dom을 지정해서 영역 겹침 감지를 시작하도록 하자.

이를 코드로 구현하면 다음과 같다.

const options = { 
  root: null, 
//기본 null, 관찰대상의 부모요소를 지정 
  rootMargin: "20px", 
// 관찰하는 뷰포트의 마진 지정 
  threshold: 1.0, 
// 관찰요소와 얼만큼 겹쳤을 때 콜백을 수행하도록 지정하는 요소 
}; 
 useEffect(() => { 
				const observer = new IntersectionObserver(handleObserver, options); 
				if (interSectRef.current){ 
                  	observer.observe(interSectRef.current); 
                                         }
return () => observer.disconnect(); 
}, [handleObserver]);

interSectRef 요소를 관찰할 것이며 viewport와 1.0(100%) 겹쳐졌을 때 겹쳐짐이 true로 set될 것이다.

options로 초기화, target을 관찰하기 시작했다면 다음은 교차되었을 때 특정 행위를 수행할 콜백을 작성하는 것이다.

위의 코드에서 콜백은 handlerObserver 이며 이는entries라는 배열인자를 받는다.

IntersectionObserver은 비동기적으로 수행되므로 useEffect에서 clean up에 disconnect를 사용해 관찰을 중지시킬 수 있도록 한다.

entires

observer는 하나 또는 여러 대상들을 관찰할 수 있는데 관찰타겟을 entries라는 배열로 받는다.

관찰대상을 여러개 지정했으면 각 요소의 정보들이 담겨있다. -> 무슨 태그고,, 어느위치에 있고 현재 겹쳐져있는지,,, 등등

observe의 대상이 intersectRef 하나이기에 0번째 요소가 관찰 대상이된다.

콜백

threshold가 한 방향 그리고 다른 방향으로 교차될 때 실행된다. (위에서 아래, 아래에서 위, 혹은 좌 -> 우, 우->좌 일때를 얘기하는 듯함)

 const handleObserver = useCallback(async (entries) => {
    const target = entries[0];
    if (target.isIntersecting) {
      console.log("is InterSecting");
      setPage((prev) => prev + 1);
    }
  }, []);

target과 뷰포트가 1.0, 즉 100% 겹쳐졌을 때 isIntersecting이 true로 되고 if조건에 만족해 다음 페이지를 불러오도록 state값을 더했다.

  useEffect(async () => {
    await fetchData(page);
  }, [page]);

교차될 때마다 page값을 증가시켜 useEffect가 호출되어 다음페이지에 대한 요청을 호출한다.

생각했던 것 보다 구현에 있어서 어려움이 크게 있진 않았다.
다만 발생할 수 있는 문제점(비동기함수 clean up)등을 잘 체크하며 구현하는 것이 중요하다고 생각된다..

출처 : https://watermelonlike.tistory.com/153

profile
https://github.com/jsw4215

0개의 댓글