Post

React-Query(TanStack-Query)를 사용해보자! (2) - 무한스크롤

📌시작하며

무한 스크롤 사용자가 페이지 하단에 도달 할 때 새로운 콘텐츠를 로딩하여 사용자에게 콘텐츠를 끊임없이 제공할 수 있도록 하는 방식

무한 스크롤은 아마 한 번씩은 다들 경험해 본 방식일 것이다.🤔 한 번에 많은 양을 로딩하기에 시간이 오래 걸릴 경우, 분할해서 로딩해 줄 수도 있고, 사용자의 호기심을 자극해 사용자가 더 오래 애플리케이션에 머물 수 있도록 돕는 역할을 하기도 한다.

이번 프로젝트에서 Tanstack Query v5를 통해 이런 무한 스크롤을 구현해서, 해당 내용을 기록할 겸 포스팅을 작성한다.

✅전체 구현 흐름

graph LR; B(사용자가 스크롤)--> C(intersectionObserver를 이용해
일정 위치에 도달하는지 확인); C-->D(useInfiniteQuery 작동)

이때 ‘일정 위치에 도달’ 하는 부분은 intersectionObserver을 사용해 구현 할 수 있다.

✅intersectionObserver

mdn 사이트를 참고하며 이해해보자.

IntersectionObserver는 Intersection Observer API의 인터페이스로, 요소의 가시성 변화를 비동기적으로 감시하는 도구다. 한 번 생성한 이후에는 설정을 변경할 수 없어서, 이미 생성된 감지기는 지정했던 요소의 가시성 비율의 감지에만 사용할 수 있지만, 하나의 감지기가 다수의 대상을 한꺼번에 주시할 수 있다.

즉, intersectionObserver을 사용해 사용자가 문서 하단에 도달했는지 확인할 수 있다는 것이다!

✅react-intersection-observer

라이브러리를 이용하면 훨씬 편하게 구현할 수 있다.

✅useInfiniteQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const {
  fetchNextPage,
  fetchPreviousPage,
  hasNextPage,
  hasPreviousPage,
  isFetchingNextPage,
  isFetchingPreviousPage,
  ...result
} = useInfiniteQuery({
  queryKey,
  queryFn: ({ pageParam }) => fetchPage(pageParam),
  initialPageParam: 1,
  ...options,
  getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
    lastPage.nextCursor,
  getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) =>
    firstPage.prevCursor,
})

전체적인 내용은 이전 포스팅에서 살펴본 useQuery와 비슷하지만, useInfiniteQuery에서 주로 사용되는 옵션들을 한 번 살펴보자.

➡️useInfiniteQuery Option

옵션설명
queryFn (필수)데이터를 요청하는 데 사용하는 함수. QueryFunctionContext를 받고, resolve 혹은 error를 throw하는 promise를 반환해야 한다.
initialPageParam (필수)페이지를 가져올 때 사용할 기본 페이지(첫 번째 페이지) 매개변수
getNextPageParam (필수)새로운 데이터를 받을 때마다 마지막 페이지와 모든 페이지의 전체 배열, 페이지 매개 변수 정보가 전달된다. 다음 페이지의 매개 변수를 반환해야 하며, 다음 페이지가 없는 경우에는 undefined, null을 반환해 표시할 수 있다.
getPreviousPageParam (필수)새로운 데이터를 받을 때마다 첫 번째 페이지와 모든 페이지의 전체 배열, 페이지 매개 변수 정보가 전달된다. 이전 페이지가 없는 경우에는 undefined, null을 반환해 표시할 수 있다.
maxPages무한 쿼리 데이터에 저장할 최대 페이지 수를 뜻한다. 최대 페이지 수에 도달해 새 페이지를 가져오면 지정된 방향에 따라 페이지 배열에서 첫 번째 또는 마지막 페이지가 제거된다. maxPages가 undefined이거나 0인 경우에는 페이지 수가 무제한임을 뜻한다. 만약, 최대 페이지 값이 0보다 큰 경우에는 양방향으로 페이지를 가져올 수 있도록 getNextPageParam 및 getPreviousPageParam을 올바르게 정의해야 한다.

➡️useInfiniteQuery Return

PropertyDescription
data.pages모든 페이지를 포함하는 배열이다.
data.pageParams모든 페이지 매개 변수를 포함하는 배열이다.
isFetchingNextPagefetchNextPage로 다음 페이지를 가져오는 동안 true가 된다.
isFetchingPreviousPagefetchPreviousPage로 이전 페이지를 가져오는 동안 true가 된다.
fetchNextPage다음 “페이지”의 결과를 가져오며, getNextPageParam과 함께 사용된다.
fetchPreviousPage이전 “페이지”의 결과를 가져온다.
hasNextPage가져올 다음 페이지가 있는 경우 true가 된다.
hasPreviousPage가져올 이전 페이지가 있는 경우 true가 된다.
isRefetching백그라운드에서 refetch가 진행 중인 경우에 true가 된다. (이 상황은 초기 pending 상태나 다음 페이지 또는 이전 페이지의 fetching 경우를 뜻하는 것이 아니다.)

✅프로젝트 내에서 구현 방식

supbase를 이용하고 있기 때문에 기본 틀에서 api 요청 후 supabase에서 일정 갯수의 데이터를 받아온 다음 return 해주는 부분이 추가 되었다.

graph LR; B(사용자가 스크롤)--> C(intersectionObserver를 이용해
일정 위치에 도달하는지 확인); C-->D(useInfiniteQuery 작동 / API 요청); D-->F(supabase에서 값 받아오기);

📩마무리

사실 무한 스크롤 내용 학습 당시에 v4 내용으로 학습했더니(23.09에 촬영된 강의), 실제 사용할 때는 이미 v5로 업데이트(23.10 업데이트) 되어 있었다.😅

아예 처음부터 다시 학습할 내용은 아니었지만, 아무리 최근 강의더라도 기술은 끊임없이 업데이트 된다는 걸 다시 한 번 느끼게 됐던 계기였다.😎

🗂️참고 사이트

This post is licensed under CC BY 4.0 by the author.