[React Query]React-Query v5에 대해 알아보기 (+Next.js에서 React Query 세팅하기)

최지수·2024년 2월 10일
post-thumbnail

📚React-Query

React-Query는 리액트 애플리케이션에서 서버 상태 가져오기, 캐싱, 동기화 및 업데이트를 보다 쉽게 다룰 수 있도록 도와주는 라이브러리이다. 클라이언트 상태와 서버 상태를 명확히 구분하기 위해 만들어졌다.

💡React-Query 장점

  • MobX나 Redux를 사용하다 보면 BoilerPlate 형태의 코드가 많이 발생하게 되는데, React-Query는 비교적 코드의 양이 적고 구조가 단순하여 추후 유지 보수가 용이하다.
  • Caching을 통해 애플리케이션의 속도를 향상시킨다.
  • 동일한 데이터에 대한 중복 요청을 제거한다.
  • 오래된 데이터의 상태를 파악하여 updating을 지원한다.
  • Garbage Collection(GC)을 이용하여 서버 쪽 데이터 메모리를 관리해준다.
  • React Hooks와 유사한 인터페이스를 제공한다.
  • 비동기 과정을 선언적으로 관리할 수 있다.

BoilerPlate란 최소한의 변경으로 여러 곳에서 재사용되지만, 반복적인 코드로 인해 많은 양의 코드를 양산한느 것을 말한다.



📗useQuery

useQuery는 v5부터 인자로 단 하나의 객체만 받는다. 그 중에 첫 번째 인자가 queryKey, queryFn가 필수 값이다.

import useQuery

import { useQuery } from '@tanstack/react-query';

queryKey

  • useQuery의 queryKey는 배열로 지정해줘야 한다.
  • 이는 단일 문자열만 포함된 배열이 될 수도 있고, 여러 문자열과 중첩된 객체로 구성된 복잡한 형태일 수도 있다.
  • useQuery는 queryKey를 기반으로 쿼리 캐싱을 관리하는 것이 핵심이다.
  • 만약, 쿼리가 특정 변수에 의존한다면 배열에다 이어서 줘야한다.
useQuery({ queryKey: ['todo', 5], ... })

QueryFn

  • useQuery의 queryFn은 promise를 반환하는 함수를 넣어줘야 한다.

useQuery 사용하기

const { data, error, status, fetchStatus, isLoading, isError, isFetching } = useQuery ({
  queryKey: [QUERY_KEY],   // 식별자
  queryFn: QUERY_FUNCTION  // HTTP 요청 함수 (Promise를 반환하는 함수)
  • data: 쿼리 함수가 리턴한 Promise에서 resolved된 데이터
  • error: 쿼리 함수에 오류가 발생한 경우 쿼리에 대한 오류 객체
  • status: data(data가 있는지 없는지에 대한 상태를 의미), 쿼리 결과 값에 대한 상태를 표현하는 status는 문자열 형태로 3가지의 값이 존재
    • pending: 쿼리 데이터가 없고, 쿼리 시도가 아직 완료되지 않은 상태
    • error: 에러 발생했을 때 상태
    • success: 쿼리 함수가 오류 없이 요청 성공하고 데이터를 표시할 준비가 된 상태
  • fetchStatus: queryFn에 대한 정보를 나타냄 (queryFn 요청이 진행중인지 아닌지에 대한 상태를 의미)
    • fetching: 쿼리가 현재 실행중인 상태
    • paused: 쿼리를 요청했지만 잠시 중단된 상태(network mode와 연관)
    • idle: 쿼리가 현재 아무 작업도 수행하지 않는 상태
  • isLoding: 캐싱 된 데이터가 없을 때 즉, 처음 실행된 쿼리일 때 로딩 여부에 따라 true/false로 반환됨
  • isFetching && isPending와 동일
  • isFetching: 캐싱 된 데이터가 있더라도 쿼리가 실행되면 로딩 여부에 따라 true/false로 반환

useQuery를 통해 data, error, fetchStatus, isLoading, isError, isFetching 등 다양한 상태를 사용할 수 있다. queryFn에 Promise를 반환하는 HTTP 요청 함수를 지정하고, queryKey에는 해당 요청을 식별하는 키를 지정한다. queryKey를 통해 HTTP 요청을 식별하고 캐싱에 활용할 수 있다.

staleTime(number | Infinity)

stale은 용어 뜻대로 썩은이라는 의미이다. 즉, 최신 상태가 아니라는 의미이다.

  • staleTime은 데이터가 fresh에서 stale 상태로 변경되는 데 걸리는 시간이다. 만약 staleTime이 3000이면 fresh 상태에서 3초 뒤에 stale로 변환한다.
  • fresh 상태일 때는 쿼리 인스턴스가 새롭게 mount 되어도 네트워크 요청(fetch)이 일어나지 않는다.
  • staleTime의 기본값은 0이기 때문에 일반적으로 fetch 후에 바로 stale이 된다.

gcTime(number | Infinity)

fresh는 뜻 그대로 신선한이라는 의미이다. 즉, 최신 상태라는 의미이다.

  • 데이터가 사용하지 않거나 inactive 상태일 때 캐싱 된 상태로 남아있는 시간이다.
  • 쿼리 인스턴스가 unmount 되면 데이터는 inactive 상태로 변경되며, 캐시는 gcTime만큼 유지된다.
  • gcTime이 지나면 가비지 콜렉터로 수집된다.
  • gcTime이 지나기 전에 쿼리 인스턴스가 다시 mount 되면 데이터를 fetch하는 동안 캐시 데이터를 보여준다.
  • gcTime은 staleTime과 관계없이, 무조건 inactive 된 시점을 기준으로 캐시 데이터 삭제를 결정한다.
  • gcTime의 기본값은 5분이다. SSR환경에서는 Infinity이다.


📘useMutation

React-Query에서 기본적으로 서버에서 데이터를 Get 할 때는 useQuery를 사용한다. 만약 서버의 data를 Post, Patch, Put, Delete와 같이 수정하고자 한다면 이때는 useMutation을 이용한다. 즉, R(read)는 useQuery, CUD(Create, Update, Delete)는 useMutation을 사용한다.

import useMutation

import { useMutation } from '@tanstack/react-query';

useMutation 사용하기

const mutation = useMutation ({
  mutationFn: MUTATION_FUNCTION,
  onMutate() {
    /*...*/
  },
  onSuccess(data) {
    console.log(data);
  },
  onError(err) {
    console.log(err);
  },
  onSettled() {
    /*...*/
  },
});

const submitHandler = () => {
  mutate({/* MUTATION_FUNCTION으로 넘길 파라미터 */});


⚒️Next.js에서 React-Query 세팅하기

1. ReactQueryProviders 만들기

<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는 렌더링 성능과 데이터 캐싱에 대한 다양한 최적화를 제공하므로 이러한 최적화를 활용해 웹 애플리케이션의 성능을 향상시킬 수 있다.

2. 루트 레이아웃에 적용하기

// 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에 리액트 쿼리 적용하기

profile
오늘보다 내일 더 성장하는 개발자🌱

0개의 댓글