본문 바로가기

개발 공부/React

React-query useQuery, useMutation

코드 출처 : https://github.com/Cluster-Taek/next14-boilerplate

 

user.ts

import { fetchApi } from './base';
import { IPageable } from '@/types/pageable';
import { IUser } from '@/types/user';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';

export interface IUsersParams {
  // paging params of json-server
  _page: number;
  _per_page: number;
}

export interface IUserCreateFormValue extends Record<string, unknown> {
  name: string;
}

export const useUsers = (params: IUsersParams) => {
  return useQuery<IPageable<IUser>>({
    queryKey: [`/api/users`, params],
  });
};

export const useCreateUserMutation = () => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: async (data: IUserCreateFormValue) => await fetchApi.post(`/api/users`, data),
    onSuccess: async () => {
      await queryClient.invalidateQueries({
        queryKey: ['/api/users'],
        refetchType: 'all',
      });
    },
  });
};

 

 

1. useQuery는 데이터 조회용

export const useUsers = (params: IUsersParams) => {
  return useQuery<IPageable<IUser>>({
    queryKey: [`/api/users`, params],
  });
};

 

  • useQuery는 데이터를 가져오는 용도로 사용됩니다.
  • useQuery는 React Query에서 데이터를 캐시하고, 자동으로 갱신하며, 서버 상태를 관리하는 데 사용됩니다.
  • 이 예제에서는 useQuery의 queryKey에 [/api/users, params]를 사용하여, 해당 쿼리가 이미 캐시된 데이터가 있으면 그것을 반환하고, 없다면 새로운 요청을 보내게 됩니다.
  • 기본적으로 Axios나 fetchApi와 같은 API 호출 유틸리티를 사용하여 데이터를 가져오는 방식은 React Query 내부에서 자동으로 처리합니다.
  • 즉, useQuery는 데이터를 가져올 때 API 호출을 내부에서 처리하며, 이 부분에서 fetchApi를 명시적으로 호출하지 않아도 되므로 fetchApi를 사용하지 않은 것입니다.

 

2. useMutation은 데이터 수정용

export const useCreateUserMutation = () => {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: async (data: IUserCreateFormValue) => await fetchApi.post(`/api/users`, data),
    onSuccess: async () => {
      await queryClient.invalidateQueries({
        queryKey: ['/api/users'],
        refetchType: 'all',
      });
    },
  });
};

 

  • useMutation은 데이터를 수정하거나 새로운 데이터를 생성할 때 사용됩니다.
  • useMutation에서는 데이터를 서버에 전송하는 작업이므로, fetchApi를 사용하여 직접 POST 요청을 보내고 있습니다.
  • mutationFn에서는 fetchApi.post를 호출하여 새로운 사용자를 생성하고, 이후 onSuccess에서 쿼리 무효화를 통해 캐시된 /api/users 데이터를 갱신합니다.
  • useMutation은 데이터 수정 후 후속 작업을 처리하는 데 사용되므로, fetchApi와 같은 직접적인 API 호출 유틸리티를 사용합니다.

차이점:

  • useQuery: 데이터를 읽고 가져오는 데 사용됩니다. 내부적으로 API 호출을 처리할 수 있지만, fetchApi를 명시적으로 사용할 필요는 없습니다.
  • useMutation: 데이터를 생성, 수정, 삭제하는 데 사용됩니다. mutationFn을 통해 명시적으로 API 호출 유틸리티를 사용하여 서버와 상호작용합니다.

 

query-provider.ts

'use client';

import { fetchApi } from '@/lib/base';
import { getClearObject } from '@/utils/utils';
import { QueryClient, QueryClientProvider, keepPreviousData } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useState } from 'react';

export const QueryProvider = ({ children }: { children: React.ReactNode }) => {
  const fetcher = async (url: string, params?: Record<string, unknown>) => {
    debugger;
    return fetchApi.get(url, params);
  };

  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            retry: false,
            staleTime: 1000 * 60,
            gcTime: 1000 * 60 * 5,
            queryFn: ({ queryKey }) =>
              fetcher(queryKey[0] as string, queryKey[1] ? getClearObject(queryKey[1]) : undefined),
            placeholderData: keepPreviousData,
          },
        },
      })
  );

  return (
    <QueryClientProvider client={queryClient}>
      <ReactQueryDevtools />
      {children}
    </QueryClientProvider>
  );
};
  • React Query의 글로벌 queryFn을 QueryClient의 defaultOptions에서 설정하고 있습니다.
  • 이 설정 덕분에 개별 useQuery에서 queryFn을 명시적으로 지정하지 않아도 전역적으로 정의된 queryFn이 실행됩니다.

 

queryKey에 /api/users가 들어가는 이유

  1. 코드에서 설정:
    • useUsers 함수 내부에서 queryKey: ['/api/users', params]로 명시적으로 지정했기 때문에, queryKey[0]에 /api/users가 들어갑니다.
  2. fetcher 함수와 연결:
    • QueryClient의 queryFn에서 이 queryKey를 받아, 첫 번째 요소(queryKey[0])를 URL로 사용하고 있습니다

동작 과정 요약

 

1.useQuery 호출

  • React Query는 queryKey를 캐시의 키와 queryFn의 입력으로 사용합니다.
     
useQuery({ queryKey: ['/api/users', params], });

 

2.queryFn 실행

  • 전역 queryFn에서 queryKey를 기반으로 데이터를 가져옵니다:
  • 여기서 queryKey[0]이 /api/users로 전달되어 URL로 사용됩니다.
fetcher(queryKey[0], queryKey[1])

 

3. 결과 반환

  • 데이터를 가져오면 React Query가 캐시를 업데이트하고, 컴포넌트는 그 데이터를 구독합니다.