useInfiniteQuery
는 파라미터 값만 변경하여 동일한 **useQuery
**를 무한정 호출할 때 사용한다. - 무한 스크롤과 아주 찰떡이라할 수 있겠다.
// 사용 방법 자체는 useQuery 와 동일하다. 기존 queryKey, queryFn 사용 const res = useInfiniteQuery(queryKey, queryFn);
pageParam
은 **useInfiniteQuery
**가 현재 어떤 페이지에 있는지를 확인할 수 있는 파라미터 값이다.
// useInfiniteQuery의 옵션 중 getNextPageParam은 undefined를 포함한 falsy한 값을 받으면 다음 페이지가 없다고 판단하고 요청 중단함 const spaceQueryData = useInfiniteQuery( spaceKeys.lists(spaceFilter), ({ pageParam = '' }: QueryFunctionContext) => spaceFilter === 'visited' ? getVisitedSpaceList(pageParam) : getMySpaceList(pageParam), { getNextPageParam: ({ cursorPaginator }) => // 다음 페이지를 호출할 때 사용 될 pageParam (cursorPaginator.hasNext ? cursorPaginator.next : undefined), keepPreviousData: false, onSuccess: (infiniteData) => { // useInfiniteQuery의 반환 값에서 data는 page 별로 응답 데이터가 누적되는 형태이기 때문에 flatMap을 통해 평탄화 infiniteData && setSpaces(infiniteData.pages.flatMap((data) => data.spaces)); }, } );
현재 서버로부터 페이징된 스페이스 목록을 가져오려면 서버가 hasNext
(boolean)값과 next
(string)의 키값을 가진 특정 해시값을 내려준다. hasNext
가 true
라면 next
가 존재하게되고 false
라면 빈스트링을 내려준다. 클라이언트는 그냥 hasNext
가 true
라면 다음 페이지 요청으로 next
와 limit
을 요청한다면 다음 페이지에 해당하는 스페이스 목록을 응답으로 내려주는 방식을 사용하고 있다.
이후 다음 페이지에 해당하는 응답을 받았다면 기존에 상태로 갖고 있던 스페이스목록 배열에 flatMap을 통해 합쳐주고 해당 리스트를 보여주는 방식으로 구현하였다.
출처: [Udemy] React Query : React로 서버 상태 관리하기 - Bonnie Schulkin
![Untitled.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/7389fef3-9494-4d5c-ac5a-54cb3325511a/83ceae1e-6455-4963-9577-ff3f95b212eb/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45FSPPWI6X%2F20241204%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20241204T161321Z&X-Amz-Expires=3600&X-Amz-Signature=e8f258de7522a6aaddca3d7d6da69a38f39690aa2dd45b663a8fb56270067e01&X-Amz-SignedHeaders=host&x-id=GetObject)
getNextPageParam
과 fetchNextPage
는 공통적으로 다음 페이지에 있는 데이터를 조회해올 때 사용한다.
getNextPageParam
반환 값이 다음 API 호출할 때의 pageParam
으로 들어가며 getNextPageParam
에서 undefined
를 반환하면 마지막 page라는 것을 의미하며 더 이상 API 호출을 하지 않게 된다.
이 외에 많은 옵션들(getPreviousPageParam, fetchPreviousPage, hasNextPage, hasPreviousPage 등등…)이 있으니 공식문서를 확인해보자.
// React Query를 이용한 유저 목록 API 호출 함수 const userKeys = { all: ['users'] as const, lists: () => [...userKeys.all, 'list'] as const, list: (filters: string) => [...userKeys.lists(), { filters }] as const, details: () => [...userKeys.all, 'detail'] as const, detail: (id: number) => [...userKeys.details(), id] as const, }
useInfiniteQuery 관련 글들을 찾아보다 유지보수성을 좀 더 생각한 방법을 발견해서 이번에 적용해보고자했다.
export const spaceQueryKeys = { /** * 내 스페이스 리스트를 가져오는 API(맵 에셋 업로드시 사용) */ MY_SPACE_LIST_GET: 'my-spaces', /** * 맵 탬플릿을 가져오는 API(내 스페이스 목록 > 템플릿 고르기 * */ MAP_TEMPLATE_LIST_GET: 'map-templates', PURCHASED_MAP_TEMPLATE_LIST_GET: 'purchased-map-templates', EDU_PURCHASED_MAP_TEMPLATE_LIST_GET: 'edu-purchased-map-templates', DELETE_VISIT_HISTORY: 'visit-history-delete', COPY_SPACE: 'my-spaces-copy', /** * 배너 리스트를 위한 키 */ MAIN_BANNERS: 'main-banners', SPACE_CATEGORIES_GET: 'space-categories', }; /** * 모든 스페이스 목록 API query key */ export const spaceKeys = { all: ['spaces'] as const, lists: (spaceFilter: SpaceFilterType) => [...spaceKeys.all, spaceFilter, 'list'] as const, };
기존 프로젝트에서 처럼 상수로 보관할 수도 있겠지만, 유지보수성을 높이기 위해 좀 더 객체스럽게 보관할 수도 있겠다 싶어 적용해보았다.
이는 좀 더 논의가 필요하겠다?정도인 듯. 매번 객체화를 하는데 구조를 어떻게 짜는게 좋을지 고민하니 괜히 시간이 더 걸릴 때가 있다. 쿼리키를 중복작성하여 같은 기능에 다른 키값을 공유하는 일은 좀 적어질 것 같은 느낌적인 느낌이랄까…?