useNexusInfiniteQuery
useNexusInfiniteQuery
는 ‘무한 스크롤’ 또는 ‘더 보기’ UI를 구축하기 위한 클라이언트 사이드 훅입니다. 페이지 파라미터, 데이터 페칭, 상태 집계를 관리합니다.
Import
import { useNexusInfiniteQuery } from 'next-nexus/client';
핵심 개념 (Core Concepts)
동작 방식
이 훅은 페이지 파라미터(예: 커서 또는 페이지 번호)를 인자로 받는 definition
팩토리 함수를 사용합니다. 이 함수를 순차적으로 호출하여 각 페이지를 가져옵니다. 사용자는 마지막으로 성공한 페이지의 데이터로부터 다음 페이지의 파라미터를 가져오는 방법을 알려주는 getNextPageParam
함수를 반드시 제공해야 합니다.
Foreground/Background 재검증
각 페이지의 재검증 로직은 useNexusQuery
와 동일합니다. 새 페이지를 가져오거나(revalidateNext
) 기존 페이지를 다시 가져올 때(revalidatePage
):
- Foreground: 페이지에 대한 캐시된 데이터가 없거나,
keepStaleData
가false
이고 데이터가 만료(stale)된 경우. 이 때isPending
상태가 됩니다. - Background: 만료된 데이터가 있고
keepStaleData
가true
인 경우. 이 때isPendingBackground
상태가 됩니다.
상태 플래그와 UX
isPending
: Foreground 페칭 중에true
가 됩니다. 초기 로딩 시 스켈레톤 UI나 ‘더 보기’ 버튼의 스피너와 같은 주요 로딩 상태를 표시하는 데 사용하세요.isPendingBackground
: Background 페칭 중에true
가 됩니다.hasNextPage
: 마지막 페이지에 대해getNextPageParam
함수가null
또는undefined
가 아닌 값을 반환했을 때true
가 됩니다.
시그니처 (Signature)
파라미터 (Parameters)
getDefinition
:(param) => NexusDefinition<TPage>
— 현재 페이지 파라미터를 받아GET
definition
을 반환하는 팩토리 함수.options
: 훅의 동작을 설정하는 객체. 아래 Options를 참고하세요.
반환 값 (Return Value)
다음을 포함하는 객체:
data
:{ pages: TPage[], pageParams: unknown[] } | undefined
headers
:Headers | undefined
(가장 최근 요청의 헤더)error
:Error | null
isPending
,isPendingBackground
,isSuccess
,isError
:boolean
hasNextPage
:boolean
revalidateNext
:() => Promise<void>
(다음 페이지를 가져옴)prefetchRef?
:React.Ref<HTMLDivElement>
(선패치를 활성화하기 위한 sentinel 엘리먼트의 ref)
Options
initialPageParam
: 가장 첫 페이지의 파라미터 (예: 페이지 번호는0
, 커서 기반은null
).getNextPageParam
:(lastPage: TPage, allPages: TPage[]) => param | null | undefined
— 다음 페이지의 파라미터를 반환하는 함수.null
또는undefined
를 반환하면 마지막 페이지임을 나타냅니다.keepPages?
:number
- 설정된 경우, 메모리 사용량을 제한하기 위해 가장 최근 N개의 페이지만 메모리에 유지합니다.revalidateOnMount?
:boolean
(기본값:true
)revalidateOnWindowFocus?
:boolean
(기본값:false
)keepStaleData?
:boolean
(기본값:true
)prefetchNextOnNearViewport?
:{ rootMargin?: string, threshold?: number }
- sentinel 엘리먼트가 뷰포트 근처에 있을 때 다음 페이지를 선패치(prefetch)합니다.
예제 (Example)
1. Definition 팩토리
먼저, 커서와 같은 페이지 파라미터를 받는 definition
을 만듭니다.
// src/api/productDefinition.ts (일부)
export interface InfiniteProduct {
products: { id: string; name: string }[];
nextCursor?: string | null;
}
export const productDefinition = {
infiniteList: (cursor: string | null) =>
createApiDefinition<InfiniteProduct>({
method: "GET",
endpoint: cursor ? `/products?cursor=${cursor}` : "/products",
client: {
tags: ["products", `cursor:${cursor ?? "first"}`],
revalidate: 300,
},
}),
};
2. 컴포넌트 사용법
그 다음, 컴포넌트에서 훅을 사용합니다.
"use client";
import { useNexusInfiniteQuery } from "next-nexus/client";
import { productDefinition } from "@/api/productDefinition";
export default function InfiniteProductList() {
const { data, hasNextPage, revalidateNext, prefetchRef, isPending } =
useNexusInfiniteQuery(productDefinition.infiniteList, {
initialPageParam: null,
getNextPageParam: (lastPage) => lastPage?.nextCursor ?? null,
prefetchNextOnNearViewport: { rootMargin: "200px" },
});
const allProducts = data?.pages.flatMap((page) => page.products) ?? [];
return (
<div>
<ul>
{allProducts.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
<button
onClick={() => revalidateNext()}
disabled={!hasNextPage || isPending}
>
{isPending ? "로딩 중…" : "더 보기"}
</button>
{/* 옵션: 선패치를 위한 sentinel 엘리먼트 */}
<div ref={prefetchRef} style={{ height: 1 }} />
</div>
);
}
함께 보기
Last updated on