[developic] Intersection Observer로 무한 스크롤 구현

sue·2021년 5월 12일
0

developic project

목록 보기
12/28

Intersection Observer API

Intersection Observer API
타겟 요소와 상위 요소 또는 최상위 document의 viewport의 교차점에서 일어나는 변화를 비동기적으로 관찰하는 방법 제공

Scroll Event vs IntersectionObserver

window.addEventListener를 이용하는 스크롤 이벤트는 다음과 같은 방식으로 사용할 수 있었다.

 useEffect(() => {
    function onScroll() {
      if (
        window.scrollY + document.documentElement.clientHeight >
        document.documentElement.scrollHeight - 300
      ) {
        if (hasMorePosts && !loadPostsLoading) {
          dispatch({
            type: LOAD_POSTS_REQUEST,
          });
        }
      }
    }
    window.addEventListener('scroll', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
    };
  }, [hasMorePosts, loadPostsLoading]);

스크롤 이벤트가 발생하면 onScroll 함수를 호출하는데, document의 높이와 스크롤 위치 등을 계산해서 조건을 걸어주고, 해당 조건이면서 hasMorePost(불러올 포스트가 더 있을 경우)상태가 true일 때만 포스트를 로드하도록 하는 것이다.

그러나 스크롤 이벤트는 불필요한 호출이 너무 많이 일어나고, 동기적으로 실행되어 메인 스레드에 영향을 주기 때문에 debounce, throttle를 사용해 호출 수를 따로 제한해줄 필요가 있다.

또한 특정 지점을 관찰하기 위해 getBoundingClientRect() 함수를 사용하면 리플로우(reflow) 현상이 발생할 수 있다.

리플로우(reflow)

리플로우는 브라우저가 웹 페이지의 일부 또는 전체를 다시 그려야하는 경우 발생한다. 브라우저는 리플로우 및 리페인트로 인한 부정적인 영향(사용자 경험성, 브라우저 성능 저하 등등)을 줄이기 위해 스크립트에 필요한 변경사항의 대기열을 설정하고 일괄적으로 수행한다. 이렇게 하면 각각의 리플로우가 필요한 변경사항이 결합되어 하나의 리플로우로 계산된다.

그러다 때때로 스크립트는 브라우저가 리플로우 최적화를 하지 못하게 대기열을 비우고 모든 변경을 수행하게 만든다. 바로 다음과 같은 정보를 요청할 때다.

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. getComputedStyle(), or currentStyle in IE

위의 모든 것은 본질적으로 노드에 대한 스타일 정보를 요청하고 있으며,이를 수행할 때마다 브라우저는 최신 값을 제공해야 한다. 이렇게 하려면 모든 예약된 변경 사항을 적용하고 대기열을 비우고 리플로우를 수행해야 한다.

Intersection Observer API의 특징을 활용하면 이러한 문제를 해결할 수 있다.

  1. 비동기적으로 실행 => 메인 스레드에 영향을 주지 않으면서 변경사항 관찰 가능
  2. getBoundingClientRect()과 같은 메서드를 호출하지 않고 감시하고자 하는 요소와 다른 요소(viewport)간의 교차 영역 관리 가능

Intersection Observer 사용 방법

Intersection Observer API는 다음과 같은 상황에 호출되는 콜백을 생성하는 기능을 제공한다.

(1) 대상(target)으로 칭하는 요소가 기기 뷰포트나 특정 요소(이 API에서 이를 root 요소 혹은 root로 칭함)와 교차함

(2) observer가 최초로 타겟을 관측하도록 요청받을 때마다

intersection observer 생성

let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

intersection observer를 생성하기 위해서는 생성자 호출 시 콜백 함수를 제공해야 한다. 이 콜백 함수는 threshold가 한 방향 혹은 다른 방향으로 교차할 때 실행된다.

threshold: 1.0 은 대상 요소가 root에 지정된 요소 내에서 100% 보여질 때 콜백이 호출될 것을 의미한다.

callback

  • callback: 타겟 엘리먼트가 교차되었을 때 실행할 함수
    - entries: IntersectionObserverEntry 객체의 리스트. 배열 형식으로 반환하기 때문에 forEach를 사용해서 처리를 하거나, 단일 타겟의 경우 배열인 점을 고려해서 코드를 작성.
    - observer: 콜백함수가 호출되는 IntersectionObserver

options

  • root
    대상 객체의 가시성을 확인할 때 사용되는 뷰포트 요소. 이는 대상 객체의 조상 요소여야 한다. 기본값은 브라우저 뷰포트이며, root값이 null이거나 지정되지 않을 때 기본값으로 설정된다.

  • rootMargin
    root가 가진 여백. root 요소의 각 측면의 bounding box를 수축시키거나 증가시키며, 교차성을 계산하기 전에 적용된다. 기본값은 0.

  • threshold
    observer의 콜백이 실행될 대상 요소의 가시성 퍼센티지를 나타내는 단일 숫자 혹은 숫자 배열. 만일 50%만큼 요소가 보여졌을 때를 탐지하고 싶다면, 값을 0.5로 설정. 기본값 0(1픽셀이라도 보이자 마자 콜백이 실행)

관찰할 요소 타겟팅

관찰자를 만든 후에는 관찰 할 대상 요소를 제공해야 한다.

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

대상에 대해 지정된 임계 값을 충족 할 때마다 IntersectionObserver 콜백이 호출된다. 콜백은 IntersectionObserverEntry 객체 목록과 관찰자를 받는다.

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 Methods

IntersectionObserver.observe()
대상 요소에 대한 감시를 시작한다.

IntersectionObserver.unobserve()
특정 대상 요소를 감시하는 것을 중지한다.

IntersectionObserver.disconnect()
IntersectionObserver 가 어떤 대상이라도 감시하는 것을 중지한다.

IntersectionObserver.takeRecords()
모든 감시되는 대상의 배열 (IntersectionObserverEntry (en-US)) 을 리턴한다.

0개의 댓글