본문 바로가기
프로그래밍/React

[React] Intersection Observer API 사용한 Infinite Scroll 만들기(without React Hook)

by 소꿍 2021. 4. 8.

한번에 데이터를 모두 불러오는 것이 아닌,

화면을 스크롤할 때마다 데이터를 추가로 불러오는 Intinite Scroll을 구현해 보았다.

 

Infinite Scroll 구현 방법

  • scroll event: 스크롤이 끝까지 내려가는 경우 데이터 추가로 불러오도록 구현하는 방법
                       throttle 등을 추가로 구현해 최적화 할 필요가 있음
  • Intersection Observer API 사용

 

찾아보니 Intersection Observer API를 활용한 예제가 많고 편리한 듯 하여 Intersection Observer를 사용하기로 했다.

 

일부 브라우저에서는 Intersection Observer 지원이 안 된다고 하지만, Explorer 외에는 거의 지원이 되는 것 같다.

Explorer는 MS에서도 지원을 종료하고 있고,

지원이 되지 않는 브라우저를 위한 Intersection Observer Polyfill도 있으니 사용에 문제는 없을 것 같다.

(지원 브라우저는 아래 링크의 Browser Compatibility를 참고)

developer.mozilla.org/ko/docs/Web/API/IntersectionObserver

 

IntersectionObserver - Web API | MDN

IntersectionObserver Intersection Observer API 의 IntersectionObserver 인터페이스는 대상 요소와 그 상위 요소 혹은 최상위 도큐먼트인 viewport와의 교차 영역에 대한 변화를 비동기적으로 감지할 수 있도록

developer.mozilla.org

 

아직 React 초보라 React Hook은 공부를 안 했는데...

React Hook을 사용한 예제가 많아서 한참 헤매다 Hook을 사용하지 않고 구현했다.

 

 

Intersection Observer의 원리를 대략 설명하면 이렇다.

 

화면 안에 어떤 target을 정해서 observer가 관찰하고 있다가

그 target이 어떤 기준을 충족하면 callback 함수를 실행하고,

이 callback 함수에서 내가 원하는 동작(데이터 추가로 불러오기)을 하도록 한다.

 

나는 화면 아래로 스크롤하다가 데이터가 끝나는 부분(화면 하단)을 만나면 데이터를 추가로 불러오는 것이 목적이다.

이를 위해 화면 하단에 target으로 삼을 <div> 태그를 만들고 Observer가 이 <div>태그를 관찰하도록 연결시키면,

화면에 내가 지정한 기준(options)을 만족하는 target(<div>)이 나타날 때 데이터를 추가로 불러오는 callback 함수를 실행시킨다.

 

모르는 것이 많아 ref는 어디에서 만들어야 되는지 등 정말 기본적인 것부터 헤맸어서.. 기록해둔다.

 

 

구현한 코드

1. constructor에서 React.createRef()로 ref를 생성한다.

constructor(props) {
  super(props);
  this.targetRef = React.createRef();
}

 

2. target으로 삼을 <div> 태그를 화면 하단에 만들기

이때 ref에 this.targetRef를 전달한다.

render() {
  return (
    <div>
      <div ref={this.targetRef}></div>
    </div>
  );
}

 

3. Intersection Observer 생성해 target을 observe()로 연결하기

componentDidMount()에서 _createObserver() 함수를 호출해 Intersection Observer를 생성하도록 했다.

componentDidMount() {
    this._createObserver();
}

_createObserver() {
    let observer;
    // observer의 option을 설정
    let options = {
      root: null,
      rootMargin: '0px 0px 20px 0px',
      threshold: 0,
    };
    
    // observer 객체를 생성하면서 callback함수와 option을 전달
    observer = new IntersectionObserver(this._checkIntersect, options);
    
    // target을 설정
    let target = this.targetRef.current;
    
    // target을 observe()
    observer.observe(target);
};

 

4. Intersecting 시 실행할 동작을 callback 함수에 넣어주기

여기서 entry.isIntersecting을 사용했는데, 이외에도

entry.boundingClientRect, entry.intersectionRatio, entry.intersectionRect,

entry.rootBounds, entry.target, entry.time 등이 있다.

// observe에 전달한 callback 함수
_checkIntersect = (entries, observer) => {
  entries.forEach(entry => {
    // Intersecting이 발생하면 _fetchItems() 함수를 호출
    if (entry.isIntersecting)
      this._fetchItems();
  });
};

 

5. _fetchItems()에서 데이터를 추가로 불러온다.

 

 

 

 

※ 잘못된 내용이 있으면 수정하겠습니다.

 


참고한 글

throttle 관련 참고할만한 글

댓글