🔥 페이지네이션과 지연 쿼리

398자
5분

TanStack Query에서는 페이지 정보를 쿼리 키에 포함시키는 것만으로도 페이지네이션 데이터를 쉽게 처리할 수 있습니다. 다음과 같이 간단히 구현할 수 있죠:

const result = useQuery({
  queryKey: ['projects', page],
  queryFn: fetchProjects,
})
 
typescript

하지만 이 간단한 예제를 실행해보면 이상한 점을 발견할 수 있습니다. 각 새로운 페이지를 완전히 새로운 쿼리로 취급하기 때문에 UI가 성공 상태와 대기 상태를 오가며 깜빡입니다. 이는 사용자 경험을 해치는 요소입니다.

안타깝게도 많은 도구들이 이런 방식으로 작동하지만, TanStack Query는 다릅니다! 이 문제를 해결하기 위해 TanStack Query는 'placeholderData'라는 멋진 기능을 제공합니다.

placeholderData를 활용한 개선된 페이지네이션 쿼리

페이지 인덱스나 커서를 증가시키는 상황을 생각해봅시다. useQuery를 그대로 사용해도 기술적으로는 문제없이 작동하지만, 각 페이지나 커서에 대해 새로운 쿼리가 생성되고 파괴되면서 UI가 성공 상태와 대기 상태 사이를 오갑니다.

placeholderData에 (previousData) => previousData 함수나 TanStack Query에서 제공하는 keepPreviousData 함수를 설정하면 다음과 같은 이점을 얻을 수 있습니다:

  1. 쿼리 키가 변경되더라도 새 데이터를 요청하는 동안 마지막으로 성공적으로 가져온 데이터를 사용할 수 있습니다.
  2. 새 데이터가 도착하면 이전 데이터가 자연스럽게 새 데이터로 교체됩니다.
  3. isPlaceholderData 값을 통해 현재 쿼리가 제공하는 데이터의 상태를 알 수 있습니다.

다음은 이를 적용한 예제 코드입니다:

import { keepPreviousData, useQuery } from '@tanstack/react-query'
import React from 'react'
 
function Todos() {
  const [page, setPage] = React.useState(0)
 
  const fetchProjects = (page = 0) =>
    fetch('/api/projects?page=' + page).then((res) => res.json())
 
  const { isPending, isError, error, data, isFetching, isPlaceholderData } =
    useQuery({
      queryKey: ['projects', page],
      queryFn: () => fetchProjects(page),
      placeholderData: keepPreviousData,
    })
 
  return (
    <div>
      {isPending ? (
        <div>로딩 ...</div>
      ) : isError ? (
        <div>오류 발생: {error.message}</div>
      ) : (
        <div>
          {data.projects.map((project) => (
            <p key={project.id}>{project.name}</p>
          ))}
        </div>
      )}
      <span>현재 페이지: {page + 1}</span>
      <button
        onClick={() => setPage((old) => Math.max(old - 1, 0))}
        disabled={page === 0}
      >
        이전 페이지
      </button>{' '}
      <button
        onClick={() => {
          if (!isPlaceholderData && data.hasMore) {
            setPage((old) => old + 1)
          }
        }}
        // 다음 페이지가 있는지 확인될 때까지 '다음 페이지' 버튼 비활성화
        disabled={isPlaceholderData || !data?.hasMore}
      >
        다음 페이지
      </button>
      {isFetching ? <span> 로딩 ...</span> : null}{' '}
    </div>
  )
}
 
typescript

placeholderData를 활용한 무한 쿼리 결과의 지연 처리

placeholderData 옵션은 useInfiniteQuery 훅과도 완벽하게 작동합니다. 이를 통해 무한 쿼리 키가 시간에 따라 변경되더라도 사용자가 계속해서 캐시된 데이터를 볼 수 있도록 할 수 있습니다. 이 방법은 일반적이지는 않지만, 매우 유용한 기능입니다.

이렇게 TanStack Query는 페이지네이션과 무한 스크롤과 같은 복잡한 데이터 로딩 패턴을 쉽게 구현할 수 있도록 도와줍니다. placeholderData를 활용하면 사용자 경험을 크게 개선할 수 있으며, 데이터 로딩 과정에서 발생할 수 있는 UI의 불필요한 깜빡임을 방지할 수 있습니다.