TanStack Query란 무엇인가?

요즘 프론트엔드 구직시장에 요구사항으로 항상 써져있는 문구가 있다

tanstack-query 사용 가능자!!!

tanstack-query는 서버 상태를 클라이언트에서 효율적으로 가져오고, 캐싱하고, 동기화하며 관리할 수 있는 도구이다.

한마디로 서버 상태 관리에 특화된 라이브러리!

뭐길래 요래 유명한지 한번 알아보자

TanStack Query (formerly known as React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes fetching, caching, synchronizing and updating server state in your web applications a breeze.

TanStack Query(예전 이름 React Query)는 웹 앱에서 데이터를 가져오는 데 필요한 라이브러리라고 흔히 말하는데, 좀 더 기술적으로 말하면 서버 상태를 가져오고, 캐싱하고, 동기화하고, 업데이트하는 걸 엄청 쉽게 만들어준다.

설치 방법

@tanstack/react-query 를 설치해준다

npm i @tanstack/react-query

사용방법 useQuery - 데이터 가져오기

모든 라이브러리들이 그렇듯 최상위 컴포넌트에서 앱을 감싸주면 된다
패키지를 다룰 때 이 패키지를 순수 프레임워크로만 만든다고 생각하면 너무나 당연한 절차다

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

const queryClient = new QueryClient();

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

기존에 특정 API를 useHook으로 만들어서 사용하고 있었는데
이 파일의 코드에 한번 tanstack-query를 적용해보자

변경 전

  • useEffect로 API 호출
  • 데이터, 로딩 상태, 에러를 반환
import { useState, useEffect } from "react";
import { Content } from "@/types/index";
import api from "@/api/config";

const useFetchContents = () => {
  const [contents, setContents] = useState<Content[]>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<any | null>(null);

  useEffect(() => {
    const fetch = async () => {
      try {
        const { data } = await api.get<Content[]>("/assignments");
        setContents(data);
      } catch (err: any) {
        setError(err.response?.data?.error || err.message);
      } finally {
        setLoading(false);
      }
    };

    fetch();
  }, []);

  return {
    data: contents,
    loading,
    error,
  };
};

export default useFetchContents;

변경 후

import { useQuery } from "@tanstack/react-query";
import { Content } from "@/types/index";
import api from "@/api/config";

const useFetchContents = () => {
  const { data, isLoading, error } = useQuery<Content[], Error>({
    queryKey: ["contents"], // Unique key for the query
    queryFn: async () => {
      const { data } = await api.get<Content[]>("/assignments");
      return data;
    },
  });

  return {
    data: data ?? [],
    loading: isLoading,
    error,
  };
};

export default useFetchContents;

기존 커스텀 훅 파일에 적용을 해서 불필요한 리턴을 하고 있는데,
간단한 구조라면 아래처럼 컴포넌트에서 직접 사용하는 것도 가능하다

const MyComponent = () => {
  const { data, isLoading, error } = useQuery<Content[], Error>({
    queryKey: ["contents"], // Unique key for the query
    queryFn: async () => {
      const { data } = await api.get<Content[]>("/assignments");
      return data;
    },
  });

  return <div>my-component</div>
};

코드가 많이 간결해졌다

  • 데이터를 가져올 때는 TanStack Query의 useQuery 훅을 사용한다.
  • useQuery는 데이터, 로딩 상태(isLoading), 에러(error)를 반환하므로 직관적이고 효율적이다.
  • queryKey: 쿼리를 고유하게 식별하는 키다. 배열 형태로 지정하며, 이를 통해 캐싱과 데이터 관리를 최적화한다. 동일한 키일 경우 캐시된 데이터를 재사용한다.
  • queryFn: 데이터를 실제로 가져오는 비동기 함수다. 이 예제에서는 API 호출을 통해 데이터를 가져오며, 반환된 데이터는 useQuery가 관리한다.

useQuery 옵션 정리 (with default 값)

실무에서는 쿼리의 동작을 상황에 맞게 조절해야 할 때가 많다.
그럴 때 사용되는 주요 옵션들과 기본값을 함께 정리해보자.

옵션설명기본값
enabled조건부로 쿼리 실행 여부를 제어true
staleTime데이터가 stale로 간주되기까지 걸리는 시간(ms)0
cacheTimeunmount 후에도 데이터를 유지하는 시간(ms)300000 (5분)
refetchOnWindowFocus창이 다시 포커스될 때 refetchtrue
retry요청 실패 시 재시도 횟수3
refetchInterval일정 주기로 refetch (ms 단위)false (사용 안 함)
select쿼리 결과를 변형할 수 있는 함수없음
onSuccess, onError쿼리 성공/실패 시 실행되는 콜백없음

✨ 예시

const { data, isLoading } = useQuery({
  queryKey: ["todos"],
  queryFn: fetchTodos,
  enabled: !!userId, // 조건부 실행
  staleTime: 1000 * 60 * 5, // 5분 간 fresh
  cacheTime: 1000 * 60 * 10, // 10분 동안 메모리 유지
  refetchOnWindowFocus: false, // 창 포커스 시 refetch 안함
  retry: 2, // 실패 시 2회까지 재시도
});