소개
React Query 사용에 대한 설명
Server 컴포넌트 기준으로 작성 되었다.
React Query 간략 소개
- Fetching Cache 처리를 위해 사용 (불필요한 API 호출 방지)
- 기본적으로 useQuery를 사용
- SSR로 사용시 prefetchQuery 사용
QueryClientProvider
GetQueryClient.tsx
- React Query를 사용하는 QueryClient를 커스터마이징한다.
typescriptimport { 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
typescriptimport { 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
typescriptconst 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;
사용방법
typescriptimport { 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를 이용하여,
꼭dehydrate로queryClient를 가져오도록 한다.
typescriptimport { 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 <></>; }