React Query

소개

React Query 사용에 대한 설명
Server 컴포넌트 기준으로 작성 되었다.

React Query 간략 소개

  • Fetching Cache 처리를 위해 사용 (불필요한 API 호출 방지)
  • 기본적으로 useQuery를 사용
  • SSR로 사용시 prefetchQuery 사용

QueryClientProvider

GetQueryClient.tsx

  • React Query를 사용하는 QueryClient를 커스터마이징한다.
typescript
import { QueryClient, defaultShouldDehydrateQuery, isServer } from "@tanstack/react-query"; function makeQueryClient() { return new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000, }, dehydrate: { // include pending queries in dehydration shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === "pending", }, }, }); } let queryClient: QueryClient | undefined = undefined; export function getQueryClient() { if (isServer) { // Server: always make a new query client return makeQueryClient(); } else { if (!queryClient) queryClient = makeQueryClient(); return queryClient; } }

Providers.tsx

typescript
import { QueryClientProvider } from '@tanstack/react-query' import { getQueryClient } from '@/app/GetQueryClient'. // 위 파일 import type * as React from 'react' export default function QueryProvider({ children }: { children: React.ReactNode }) { const queryClient = getQueryClient() return ( <QueryClientProvider client={queryClient}> {children} </QueryClientProvider> ) }
Note

전역 관리를 해야하여 app/layout.tsx에 children에 감싸준다
<QueryProvider>{children}</QueryProvider>

Query Option 관리법

queries.board.tsx

typescript
const queryKeys = { all: ["boards"] as const, detail: (boardId: number) => [...queryKeys.all, boardId] as const, detailComments: (boardId: number) => [...queryKeys.detail(boardId), "comments"] as const, }; const queryOptions = { all: () => ({ queryKey: queryKeys.all, queryFn: async () => { const response = await fetch("http://localhost:8080/api/boards"); return await response.json(); }, }), detail: (boardId: number) => ({ queryKey: queryKeys.detail(boardId), queryFn: async () => { const response = await fetch(`http://localhost:8080/api/boards/${boardId}`); return await response.json(); }, }), comments: (boardId: number) => ({ queryKey: queryKeys.detailComments(boardId), queryFn: async () => { const response = await fetch(`http://localhost:8080/api/boards/${boardId}/comments`); return await response.json(); }, }), }; export default queryOptions;

사용방법

typescript
import { useQuery } from '@tanstack/react-query'; import queryOptions from '@/service/board/queries.board'; // boards/page.tsx export function BoardPage() { const info = useQuery(queryOptions.all()); return <></>; } // boards/[id]/page.tsx export function BoardDetailPage({boardId} : any) { const info = useQuery(queryOptions.detail(boardId)); return <></>; }

prefetchQuery

  • useQuery와 사용방식은 거의 동일
  • 기존 useQuery는 Client 컴포넌트 단위에서만 사용이 가능하며, prefetchQuery는 Server 컴포넌트에서 사용하기 위함

Server Component

  • prefetchQuery로 미리 데이터를 가져온 후 HydrationBoundary를 이용하여,
    dehydratequeryClient를 가져오도록 한다.
typescript
import { useQuery } from '@tanstack/react-query'; import queryOptions from '@/service/board/queries.board'; // boards/page.tsx export function BoardPage() { const queryClient = getQueryClient(); queryClient.prefetchQuery(queryOptions.all()); return <> <HydrationBoundary state={dehydrate(queryClient)}> <BoardList /> </HydrationBoundary> </>; } // boards/[id]/page.tsx export function BoardDetailPage({boardId} : any) { const queryClient = getQueryClient(); queryClient.prefetchQuery(queryOptions.detail(boardId)); return <> <HydrationBoundary state={dehydrate(queryClient)}> <BoardDetails boardId={boardId} /> </HydrationBoundary> </>; }

Client Component

  • useSuspenseQuery를 사용해서, 화면이 불러오면서 잠시 깨지는 현상을 나타나지 않게 한다.
typescript
'use client' import React from 'react' import { useSuspenseQuery } from '@tanstack/react-query' import queryOptions from '@/service/board/queries.board'; export function BoardList() { const { data } = useSuspenseQuery(queryOptions.all()) return <></>; } export function BoardDetails({boardId}: any) { const { data } = useSuspenseQuery(queryOptions.detail(boardId)) return <></>; }