React-Query는 리액트 애플리케이션에서 서버 상태 가져오기, 캐싱, 동기화 및 업데이트를 보다 쉽게 다룰 수 있도록 도와주는 라이브러리이다. 클라이언트 상태와 서버 상태를 명확히 구분하기 위해 만들어졌다.
BoilerPlate란 최소한의 변경으로 여러 곳에서 재사용되지만, 반복적인 코드로 인해 많은 양의 코드를 양산한느 것을 말한다.
useQuery는 v5부터 인자로 단 하나의 객체만 받는다. 그 중에 첫 번째 인자가 queryKey, queryFn가 필수 값이다.
import { useQuery } from '@tanstack/react-query';
배열로 지정해줘야 한다.queryKey를 기반으로 쿼리 캐싱을 관리하는 것이 핵심이다.useQuery({ queryKey: ['todo', 5], ... })
promise를 반환하는 함수를 넣어줘야 한다.const { data, error, status, fetchStatus, isLoading, isError, isFetching } = useQuery ({
queryKey: [QUERY_KEY], // 식별자
queryFn: QUERY_FUNCTION // HTTP 요청 함수 (Promise를 반환하는 함수)
useQuery를 통해 data, error, fetchStatus, isLoading, isError, isFetching 등 다양한 상태를 사용할 수 있다. queryFn에 Promise를 반환하는 HTTP 요청 함수를 지정하고, queryKey에는 해당 요청을 식별하는 키를 지정한다. queryKey를 통해 HTTP 요청을 식별하고 캐싱에 활용할 수 있다.
stale은 용어 뜻대로 썩은이라는 의미이다. 즉, 최신 상태가 아니라는 의미이다.
fresh에서 stale 상태로 변경되는 데 걸리는 시간이다. 만약 staleTime이 3000이면 fresh 상태에서 3초 뒤에 stale로 변환한다.0이기 때문에 일반적으로 fetch 후에 바로 stale이 된다.fresh는 뜻 그대로 신선한이라는 의미이다. 즉, 최신 상태라는 의미이다.
inactive 상태일 때 캐싱 된 상태로 남아있는 시간이다.gcTime만큼 유지된다.가비지 콜렉터로 수집된다.inactive 된 시점을 기준으로 캐시 데이터 삭제를 결정한다.5분이다. SSR환경에서는 Infinity이다.React-Query에서 기본적으로 서버에서 데이터를 Get 할 때는 useQuery를 사용한다. 만약 서버의 data를 Post, Patch, Put, Delete와 같이 수정하고자 한다면 이때는 useMutation을 이용한다. 즉, R(read)는 useQuery, CUD(Create, Update, Delete)는 useMutation을 사용한다.
import { useMutation } from '@tanstack/react-query';
const mutation = useMutation ({
mutationFn: MUTATION_FUNCTION,
onMutate() {
/*...*/
},
onSuccess(data) {
console.log(data);
},
onError(err) {
console.log(err);
},
onSettled() {
/*...*/
},
});
const submitHandler = () => {
mutate({/* MUTATION_FUNCTION으로 넘길 파라미터 */});
<ReactQueryProviders>{children}</ReactQueryProviders>로 덮여씌워 쿼리를 제공해야하기 때문에 ReactQueryProviders 컴포넌트를 만들어야한다.
// /src/utils/react-query-provider.tsx
'use client'
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { useState } from "react";
export default function ReactQueryProviders({
children,
} : React.PropsWithChildren) {
const [client] = useState(
new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false, // 윈도우가 다시 포커스 되었을 때 데이터를 refetch
refetchOnMount: false, // 데이터가 stale 상태이면 컴포넌트가 마운트 될 때 refetch
retry: 1, // API 요청 실패시 재시도 하는 옵션 (설정값 만큼 재시도)
},
},
}),
);
return (
<QueryClientProvider client={client}>
{children}
</QueryClientProvider>
)
}
📌queryClient 설정에 useState를 사용하는 이유📌
useState를 활용하는 것은 React Query의 QueryClientProvider 설정에서 특별한 경우의 선택이라 할 수 있다. useState를 적용하면 참조 동일성을 유지하며, 또한 React Query와 React가 예상하는 상태 관리를 조절하고 컴포넌트 렌더링과 상태 업데이트를 조절하기 위함이다.
최적화와 관련해 React Query는 참조 동일성을 유지하게 하는 초기 설정을 처리하려 한다. 이렇게 하면 초기 설정을 최적화하고 상태를 효율적으로 관리할 수 있다. 또한, React Query는 렌더링 성능과 데이터 캐싱에 대한 다양한 최적화를 제공하므로 이러한 최적화를 활용해 웹 애플리케이션의 성능을 향상시킬 수 있다.
// src/app/layout.tsx
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import RecoilRootProvider from '../utils/RecoilRootProvider'
import ReactQueryProviders from '@/utils/ReactQueryProvider'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<ReactQueryProviders>
<RecoilRootProvider>
{children}
</RecoilRootProvider>
</ReactQueryProviders>
</body>
</html>
)
}
위에서 만든 ReactQueryProviders로 감싸주면 세팅이 끝이 난다!
참고
TanStack Query(React)
React Query
[React] React Query v5 (TanStack Query) 사용해 보기
Next.JS에 리액트 쿼리 적용하기