useNexusQuery
useNexusQuery
는 definition
으로 정의된 데이터를 클라이언트에서 가져오고, 캐시하고, 재검증하기 위한 훅입니다. 클라이언트 컴포넌트에서 데이터를 읽기 위해 설계되었습니다.
Import
import { useNexusQuery } from 'next-nexus/client';
시그니처 (Signature)
useNexusQuery<TData, TSelectedData = TData>(
definition: NexusDefinition<TData>,
options?: { ... }
): {
data: TSelectedData | undefined
headers: Headers | undefined
error: Error | null
isPending: boolean
isPendingBackground: boolean
isSuccess: boolean
isError: boolean
revalidate: () => Promise<void>
}
definition
:GET
메서드를 사용하는definition
객체.GET
이 아닌definition
이 전달되면 오류가 발생합니다.options
: 훅의 동작을 설정하는 객체. 아래 Options를 참고하세요.
핵심 개념 (Core Concepts)
하이드레이션 인식 페칭 (Hydration-Aware Fetching)
useNexusQuery
는 서버에서 페치되어 하이드레이션으로 클라이언트에 전달된 데이터를 인식합니다. 마운트 시점에 클라이언트 캐시에 데이터가 있으면, 새로운 요청 없이 즉시 렌더링하여 UI 깜빡임을 제거합니다.
Foreground/Background 재검증
revalidate()
함수가 호출되거나 마운트, 창 포커스 등 자동 재검증 이벤트가 발생할 때 훅이 자동으로 재검증 모드를 결정합니다.
- Foreground: 캐시된 데이터가 없거나,
keepStaleData
가false
이고 데이터가 만료(stale)된 경우. 이 때isPending
이true
가 됩니다. - Background: 만료된 데이터가 있고
keepStaleData
가true
(기본값)인 경우. 이 때isPending
은false
인 채로isPendingBackground
가true
가 됩니다. - 실행 안 함 (No-op): 신선한(fresh) 데이터가 있으면 요청을 수행하지 않습니다.
상태 플래그와 UX (State Flags and UX)
isPending
: Foreground 재검증 중에true
가 됩니다. 아직 데이터가 없을 때(isPending && !data
) 스피너와 같은 로딩 상태를 표시하는 데 사용하세요.isPendingBackground
: Background 재검증 중에true
가 됩니다. 만료된 데이터를 화면에 계속 보여주면서, ‘새로고침’ 버튼을 비활성화하는 등 미묘한 로딩 표시를 위해 사용하세요.
Options
route?
:string
- 요청을 Next.js 라우트 핸들러로 프록시합니다. 이 옵션이 제공되면 런타임은definition
의baseURL
을 지우고route
를 유효 엔드포인트로 사용합니다.enabled?
:boolean
(기본값:true
) -false
로 설정하면 훅이 데이터를 가져오거나 캐시 업데이트를 구독하지 않습니다.select?
:(data: TData) => TSelectedData
- 데이터를 선택하거나 변환하는 함수. 선택된 값이 변경될 때만 컴포넌트가 리렌더링됩니다.revalidateOnWindowFocus?
:boolean
(기본값:true
) - 창이 다시 포커스될 때 자동으로 재검증합니다.revalidateOnMount?
:boolean
(기본값:true
) - 데이터가 만료 상태일 때 마운트 시 자동으로 재검증합니다.keepStaleData?
:boolean
(기본값:true
) -true
이면 Background 재검증이 발생하는 동안 만료된 데이터가 화면에 유지됩니다.false
이면 Foreground 재검증 중에data
가undefined
가 됩니다.
예제 (Examples)
기본 사용법
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { productDefinition } from '@/api/productDefinition';
export function ProductListClient() {
const { data, isPending } = useNexusQuery(productDefinition.list);
if (isPending && !data) return <div>로딩 중...</div>;
const products = data ?? [];
return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}
select
옵션 사용하기
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { statsDefinition } from '@/api/statsDefinition';
export function TotalCount() {
const { data: count } = useNexusQuery(statsDefinition.summary, {
select: s => s.total,
});
return <div>총계: {count ?? 0}</div>;
}
route
오버라이드 사용하기
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { productDefinition } from '@/api/productDefinition';
export function ProductListViaRoute() {
const { data } = useNexusQuery(productDefinition.list, { route: '/api/products' });
return <div>{data?.length ?? 0}개 항목</div>;
}
캐시된 헤더 읽기
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { listWithTotalDefinition } from '@/api/productDefinition'; // 이 definition이 있다고 가정
export function ProductListWithTotal() {
const { data, headers } = useNexusQuery(listWithTotalDefinition);
const total = headers?.get('x-total-count');
return (
<div>
항목: {data?.length ?? 0} / 전체: {total ?? 'n/a'}
</div>
);
}
함께 보기
Last updated on