본문 바로가기
2024/프로젝트

[food-app] React Query를 사용해 Infinite scroll 구현하기

by ye-jji 2024. 10. 20.

React Query의 Infinite scroll

: 무한 스크롤을 지원하고, 화면 스크롤을 통해 추가 데이터를 자동으로 로드할 수 있는 강력한 기능 제공

 data: Infinite Query 결과 데이터

 data.pages: 가져온 페이지들의 배열

 data.pageParams: 페이지를 가져오기 위한 페이지 매개 변수. 배열의 형태.

 fetchNextPage, fetchPreviousPage: 다음 페이지 및 이전 페이지의 데이터를 가져오는 함수

 getNextPageParam, getPreviousPageParam: 다음 및 이전 페이지에 대한 매개 변수를 생성하는 함수

 hasNextPage, hasPreviousPage: 다음 페이지 및 이전 페이지가 있는지 여부를 나타내는 불리언 값

 isFetchingNextPage, isFetchingPreviousPage: 다음 페이지 또는 이전 페이지의 데이터를 가져오는 동안 로딩 상태를 나타내는 불리언 값 

 

Javascript의 scroll 이벤트의 한계점

• Javascript에서 무한 스크롤 구현 방법: addEventListener()에 scroll 이벤트 이용해서 구현

• 또한, getBoundingClientRect() 메서드로 원하는 특정 위치에서 다음 페이지들을 가져오도록 구현

// 빈 리스트 선택
const listElem = document.querySelector('#infinite-list')

// 20개의 아이템 추가 함수
let nextItem = 1

const loadMore = function () {
  for(let i = 0; i < 20; i++) {
    let item = document.creatElement('li')
    item.innerText = 'List Item #' + nextItem++
    listelm.appendChild(item)
  }
}

// ul 리스트 바닥까지 스크롤 했는지 확인
listElm.addEventListener('scroll', function () {
  if (listElm.scrollTop + listElm.clientHeight >= listElm.scrollHeight) {
  loadMore()
  }
})

// 아이템 20개씩 더 가져오는 loadMore 함수 실행
loadMore()

 

• 하지만, 위 코드는 성능 문제를 발생시킴.

• scroll 이벤트: 단시간에 수백번 호출이 되며 동 기적으로 실행

• getBoundingClientRect 메서드: 계산을 할 때 마다 리플로우 현상이 일어남

• 해결방안: Intersection Observer를 사용해 비동기적으로 교차점 관찰

 

Intersection Observer

: 브라우저 viewport와 원하는 요소의 교차점을 관찰하며, 요소가 뷰포트에 포함되는지 아닌지 구별하는 기능

// IntersectionObserver 등록
const io = new IntersectionObserver(entries => {
  enties.forEach(entry => {
    // 관찰 대상이 viewport 안에 들어온 경우 'active' 클래스 추가
    if (entry.interSectionRatio > 0) {
      entry.target.classList.add('active')
    }
    // 그 외의 경우 'active' 클래스 제거
    else {
      entry.target.classList.remove('active')
    }
  })
})

// 관찰할 대상을 선언하고, 해당 속성을 관찰
const boxList = document.querySelectorAll('.box')
boxList.forEach(el => {
  io.observer(el)
})

• 비동기적으로 실행되기 때문에, 메인 스레드에 영향을 주지 않으면서 요소들의 변경사항 관찰

• Scroll 및 getBoundingClientRect의 성능 문제를 해결

• 또한, IntersectionObserverEntry 등의 속성을 활용해서 요소들의 위치를 알 수 있음

 

Intersection Observer 기본 문법

• Intersection Observer API는 다음과 같은 상황에 콜백 함수를 호출:

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

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

// observer 초기화
let io = new Intersection Observer(callback, options)

iio.observer(element) // 관찰 대상 등록

• IntersectionObserver() 생성자는 2개의 인수 (callback, options)를 갖는다.

• callback: 관찰할 대상 (target)이 등록되거나, 가시성에 변화가 생기면 실행. 두 개의 인수 (entries,

observer)를 갖는다.

• Options: 관찰이 시작되는 상황에 대한 옵션을 설정할 수 있음 (root, rootMargin, threshold)

 

Intersection Observer Callback: Entry 속성

• IntersectionObserverEntry는 읽기 전용의 여러가지 속성들을 포함

• boundingClientRect: 관찰 대상의 경계 사각형을 DOMRectReadOnly로 반환

• intersectionRect: 관찰 대상의 교차한 영역 정보를 DOMRectReadOnly로 반환

• intersectionRatio: 관찰 대상의 교차한 영역의 비율을 0.0과 1.0 사이의 숫자로 반환

• isIntersecting: 관찰 대상이 교차 상태인지 아닌지 반환(Boolean)

• rootBounds: 지정한 루트 요소의 사각형 정보를 DOMRectReadOnly로 반환

• target: 관찰 대상 요소(Element) 반환

• time: 변경이 발생한 시간 정보(DOMHighResTimeStamp) 반환

 

Intersection Observer Options

• Intersection Observer는 Options를 통해 관찰이 시작되는 상황에 대한 옵션을 설정할 수 있음

• root: 대상 객체(target)의 가시성을 확인할 때 사용되는 뷰포트 요소

• rootMargin: root 가 가진 바깥 여백(Margin). margin 값을 이용해 root 범위를 확장 / 축소할 수 있음

• Ex) “10px 20px 30px 40px” (top, right, bottom, left). 기본값은 0

• threshold: observer의 콜백이 실행될 대상 요소(target)의 가시성이 얼마나 필요한지 나타내는 값

 

Intersection Observer 메서드

• observe: 대상 요소 (target)의 관찰을 시작할 때 사용

• unobserve: 대상 요소의 관찰을 중지할 때 사용. 관찰을 중지할 하나의 대상 요소를 인수로 지정해야 함

• disconnect: IntersectionObserver 인스턴스가 관찰하는 모든 요소의 관찰을 중지할 때 사용

 

실제 구현 코드

https://github.com/yejify/food-app/commit/4be332927d91df9960360d2e9ce62476b99d0afe

 

[feat] 무한 스크롤 구현하기 · yejify/food-app@4be3329

yejify committed Oct 9, 2024

github.com