React

TanStack Query (React Query) 알아보기

startfront 2025. 4. 21. 15:17

TanStack Query (React Query)

웹 애플리케이션에서 서버 상태를 가져오고, 캐싱하고, 동기화하고, 업데이트하는 작업을 아주 쉽게 해주는 API.
기존에는 해당하는 데이터의 로딩, 에러, 캐싱, 갱신을 직접 관리했어야 했지만, React Query가 알아서 해준다.

 

 

  • 서버로부터 데이터를 가져오는 건 useQuery
  • 데이터 생성, 수정, 삭제 같은 작업은 useMutation
  • 쿼리의 결과를 자동으로 캐싱, 관리
  • QueryClientProvider를 앱 루트에 적용해줘야 캐싱 가능

설치

npm i @tanstack/react-query

 

queryClientprovider을 붙여주어야지 캐싱 가능

 

 

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourComponents />
    </QueryClientProvider>
  );
}

 

서버에서 데이터 가져오기 — useQuery

const { isPending, isError, data, error } = useQuery({
  queryKey: ['todos'],
  queryFn: fetchTodoList,
});

상태값

isPending 또는 status === 'pending' 쿼리 실행 중
isError 또는 status === 'error' 에러 발생
isSuccess 또는 status === 'success' 데이터 성공 반환 

 

queryKey 

데이터를 식별하고 관리하는 고유 값
  • 첫 번째 요소: 요청 자원명 (리소스 식별자)
  • 두 번째 이후: 쿼리를 다시 수행시킬 기준 (해당 값이 변하면 쿼리 다시 실행)
queryKey: ['user', userId]
=> 변할 가능성이 있는 값은 반드시 key에 넣어주어야 한다
 

useQuery 옵션 

queryKey 고유 쿼리 식별자 (배열 형태)
queryFn 데이터를 fetch하는 비동기 함수
enabled 자동 실행 여부 (false 시 수동 호출 가능)
select 데이터를 가공해서 반환
staleTime 데이터가 오래됐다고 간주하기 전까지의 시간 (ms)
cacheTime 메모리에 캐시를 유지하는 시간 (ms)
refetchOnWindowFocus 윈도우 포커스 시 다시 호출 여부 (true, false, 'always')
retry 실패 시 재시도 횟수/여부
onSuccess 성공 시 실행할 콜백
onError 실패 시 실행할 콜백

useMutation _ 데이터 생성/수정/삭제

const mutation = useMutation({
  mutationFn: (newTodo) => axios.post('/todos', newTodo),
  onSuccess: () => {
    // 성공했을 때 처리
  },
  onError: (error) => {
    // 실패했을 때 처리
  },
  onSettled: () => {
    // 성공이든 실패든 처리
  },
});

 

서버 상태를 변경하는 요청은 무조건 useMutation

=> 성공 시 해당하는 쿼리 캐싱을 무효화(invalidateQueries)해서 화면을 최신 상태로 유지 가능

 

useMutation 정리

mutationFn 서버 요청 함수
onSuccess 성공 시 콜백
onError 실패 시 콜백
onSettled 성공/실패 상관없이 실행

 

삭제용 Mutation 예제 

"use client";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { removeBook } from "@/service/books";  // 삭제 요청 API 함수
import { useRouter } from "next/navigation";

// queryClient 가져오기 (invalidateQueries 호출용)
const queryClient = useQueryClient();
const router = useRouter();

// 삭제 Mutation 선언
const removeMutation = useMutation({
  // 삭제 요청 함수 — isbn 값을 받아서 API 호출
  mutationFn: (isbn: string) => removeBook(isbn),

  // 성공 시 실행되는 콜백
  onSuccess: () => {
    alert("삭제 성공");

    // 캐싱된 'books' 목록 데이터 무효화 → 목록 새로고침되게
    queryClient.invalidateQueries(["books"]);

    // 삭제 후 목록 페이지로 이동
    router.push("/books");
  },

  // 실패 시 실행되는 콜백
  onError: () => {
    alert("삭제 실패");
  },
});

// 삭제 버튼 클릭 시 실행 함수
const handleRemove = (isbn: string) => {
  if (confirm("정말 삭제하시겠습니까?")) {
    //  삭제 mutation 호출
    removeMutation.mutate(isbn);
  }
};