개발자를 위한 SNS 웹페이지를 개발하면서 Tanstack Query를 사용해서 상태관리를 하기로 정해졌다. Tanstack Query를 선택하게 된 이유와 Tanstack Query가 무엇인지 알아본 내용에 대해 포스팅 하려한다!
React-Query가 react가 아닌 다른 프레임워크도 지원하기 시작하면서 Tanstack Query로 명칭을 바꾸었다.

공식문서에 위와같이 Tanstack query를 소개하고 있다.

또, 기존 상태 관리 라이브러리들 (ex. redux, recoli 등)은 비동기 및 서버 상태관리에 적합하지 않다고 소개하고 있다.
그렇다면 서버 상태가 무엇일까?
공식 홈페이지에서 서버 상태를 다음과 같이 얘기하고 있다.
서버 상태
- 클라이언트에서 제어나 소유하지 않은 공간인 서버에 저장되고 관리되는 데이터
- updating과 fetching을 위해 비동기 API가 필요한 데이터
- 데이터 소유권이 서버에 있어 여러 사용자가 공통으로 참조하는 데이터 ex) 공통으로 보는 게시글 등
- 서버의 소유권으로 인해 관리하지 않을 경우 오래된 데이터를 보게 될 수 있다.
이러한 서버 상태의 특성으로 인해 관리하는데 다음과 같은 어려움이 있다.
서버 상태 관리의 어려움
- 캐싱
- 동일한 데이터에 대한 여러 요청
- 백그라운드에서 오래된 데이터를 업데이트
- 데이터가 오래된 시점을 아는 어려움
- 페이지네이션 및 데이터 지연 로드에 대한 성능 최적화
Tanstack Query가 제공하는 기능을 사용하면 어려움 없이 효과적으로 서버상태를 관리 할 수 있다.
Tanstack Query 제공 기능
- 캐싱 기능
- 동일한 데이터에 대한 중복 요청을 단일 요청으로 통합
- 데이터의 오래된 시점을 파악해 백그라운드에서 데이터 업데이트
- 페이지네이션 및 데이터 지연 로드 성능 최적화 기능
- 서버 상태의 메모리 및 가비지 수집 관리
- 구조 공유를 사용해 쿼리 결과 메모화
function UserComponent({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
setIsLoading(true);
setError(null);
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error("Failed to fetch user");
}
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
fetchUser();
}, [userId]);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return null;
return <div>{user.name}</div>;
}
const fetchUser = async (userId) => {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error("Failed to fetch user");
}
return response.json();
};
function UserComponent({ userId }) {
const {
data: user,
isLoading,
error,
} = useQuery(["user", userId], () => fetchUser(userId));
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!user) return null;
return <div>{user.name}</div>;
}
첫번째 코드가 기존의 API 호출 로직이고, 두번째 코드가 Tanstack query를 사용한 API 호출 로직이다.
이처럼 Tanstack query를 사용하면 서버 API 호출 코드량도 줄일 수 있다.
공식 문서에서는 서버 상태관리 중 가장 어렵다고 얘기하는 캐싱에 대해서 간단히 알아보자.
캐싱
- 데이터를 한 번 받아온 후에 그 데이터를 불러온 저장소보다 가까운 곳에 임시로 저장하여, 필요시 더 빠르게 불러와서 사용하는 프로세스
캐싱이 안되어 있을 경우

캐싱이 되어있을 경우

캐싱이 안되어 있는 경우에는 데이터를 fetching하는 동안 사용자가 로딩 인디케이터를 보게 되지만,
캐싱이 되어 있으면 오래된 데이터를 보고 있어 사용자 경험이 더 좋다.
Tanstack query에는 핵심개념 3가지인 Query Client, Queries, Mutations가 존재한다.
Query Client
- 모든 쿼리에 대한 상태 및 캐시를 가지고 있는 클래스로 TanStack Query의 중심이다.
- 캐시 관리, 쿼리 상태 추적, 쿼리 무효화(invalidate) 등의 작업을 담당한다.
- queryClient.invalidateQueries나 queryClient.refetchQueries 등을 사용해 특정 데이터를 재요청하거나 캐시를 무효화해 최신 데이터를 가져오도록 제어한다.
- 애플리케이션 전역에서 하나의 QueryClient 인스턴스를 사용하며, 필요한 쿼리나 뮤테이션의 옵션과 상태를 전역으로 설정해 관리한다.
new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60,
retry: 1,
},
mutations: {
retry: 1,
},
},
})
Queries
- 데이터의 조회를 위한 함수로 서버에서 데이터를 가져와 캐싱한다.
- useQuery 훅을 사용해 서버에서 데이터를 가져오고, 상태에 따라 로딩 중, 성공, 에러 등의 상태를 쉽게 관리할 수 있다.
- queryKey를 사용해 특정 데이터를 구분하며, 동일한 키를 가진 쿼리는 캐시를 공유해 불필요한 요청을 방지한다.
const query = useQuery({
queryKey: ['todo'], // 캐시 관리를 위한 쿼리 키값
queryFn: fetchTodo // Promise를 반환하는 함수 (axios, fetch ...)
})
const {isPending, error, data} = query // 로딩 중, 성공, 에러 등의 상태
Mutation
- 서버 데이터의 생성, 수정, 삭제 작업을 담당한다.
- useMutation 훅을 사용해 서버의 상태를 변경하고, 성공/실패 시 후속 작업(optimistic update, 오류 처리 등)을 설정할 수 있다.
- 일반적인 CRUD 작업에서 useMutation을 통해 데이터 업데이트 후 queryClient를 활용해 관련 캐시를 갱신하거나 무효화하는 방식으로 사용된다.
const query = useMutation({
mutationFn: fetchTodo // Promise를 반환하는 함수 (axios, fetch ...)
})
Query 상태 흐름도

Tanstack Query의 상태 흐름도이다.
- fresh(최신) 데이터를 서버에서 가져온다.
- 설정해둔 StaleTime이 지나면 데이터가 Stale(오래된) 상태로 전환된다.
- Stale 데이터가 화면에서 사용되지 않으면 inactive 상태로 전환된다.
- Stale 데이터가 화면에서 사용 될 경우 refetching 이 진행된다.
- inactive 상태로 전환된 데이터는 gc(Garbage Collector)Time 지나기전 화면에서 사용되면 refetching 이 진행된다.
- inactive 상태로 전환된 데이터는 설정해둔 gc(Garbage Collector)Time 동안 사용되지 않는 경우 캐시에서 delete(삭제) 된다.
참고 문서
'Study' 카테고리의 다른 글
| [Tanstack-Query] 구체적인 쿼리키 설정으로 캐싱 최적화하기 (0) | 2025.03.04 |
|---|---|
| [Next 14] Next.js 프로젝트에 테스트 환경 구축하기 (jest, react-testing-library) (1) | 2025.02.10 |
| [React] Eslint 오류 해결하기 (no-else-return, jsx-a11y) (0) | 2024.10.15 |
| [React + TS] useRef 사용시 The expected type comes from property 'ref' which is declared here on type 에러 (+ useRef 3가지 정의) (1) | 2024.09.05 |
| [React + TS] react-router 사용해 페이지 라우터 적용하기 (0) | 2024.08.14 |