TanStack Query는 서버 상태를 어떻게 관리할까?
1. TanStack Query의 핵심 개념
TanStack Query는 서버 상태를 관리하기 위해 Query와 Mutation이라는 두 가지 개념을 사용한다.
- Query: 서버에서 데이터를 가져오는 역할 (useQuery 사용)
- Mutation: 데이터를 변경하는 역할 (useMutation 사용)
이를 활용해 서버와 클라이언트 간의 상태를 일관성 있게 유지할 수 있다.
또한, Query Key라는 개념을 통해 데이터를 구분하고 관리한다.
Query Key는 특정 데이터를 식별하는 고유한 키이며, 동일한 Query Key를 가진 데이터는 캐싱되어 효율적으로 관리된다.
2. TanStack Query의 내부 동작 원리
1) 요청 및 응답 흐름
TanStack Query의 useQuery가 실행되면 다음과 같은 과정이 진행된다.
- Query Key를 기반으로 캐시 확인 → 동일한 요청이 이미 캐시에 있다면, 캐시된 데이터를 반환
- 캐시에 없는 경우, Fetch 요청 실행 → 서버에서 데이터를 가져옴
- 응답 데이터 캐싱 → 가져온 데이터를 Query Cache에 저장
- Stale Time을 기준으로 데이터 상태 관리 → 일정 시간이 지나면 데이터가 자동으로 갱신됨
const { data, isLoading, error } = useQuery(['user', userId], () => fetchUser(userId));
위 코드에서 ['user', userId]는 Query Key로, 같은 Key를 가진 요청은 중복되지 않고 캐시에서 처리된다.
2) 캐싱과 데이터 저장 방식
TanStack Query는 내부적으로 Query Cache라는 저장소를 사용해 데이터를 보관한다.
기본적으로 다음과 같은 캐싱 전략을 따른다.
- Stale Time: 데이터가 신선한 상태를 유지하는 시간 (기본값: 0초 → 즉시 만료)
- Cache Time: 사용되지 않는 데이터를 메모리에 유지하는 시간 (기본값: 5분)
이를 통해 불필요한 네트워크 요청을 방지하고, 데이터의 최신성을 유지할 수 있다.
const { data } = useQuery(['posts'], fetchPosts, { staleTime: 10000, cacheTime: 600000 });
위 코드에서는 10초 동안 데이터가 신선한 상태(Stale Time)로 유지되며, 사용되지 않더라도 10분 동안(Cache Time) 캐시에 남아있게 설정했다.
✍🏻캐시된 데이터를 언제 갱신할까?
- invalidateQueries를 사용하면 필요할 때 강제로 데이터 새로고침이 가능하다.
const queryClient = useQueryClient();
queryClient.invalidateQueries(['posts']); // 서버에서 다시 가져옴
- refetchOnWindowFocus: false 설정하면 다시 창을 활성화할 때 자동 갱신되지 않는다.
useQuery(['posts'], fetchPosts, {
refetchOnWindowFocus: false,
});
- staleTime을 설정하면, 해당 시간이 지나기 전까지는 항상 캐시 데이터를 사용한다.
useQuery(['posts'], fetchPosts, {
staleTime: 1000 * 60 * 5, // 5분 동안은 캐시 데이터만 사용
});
3) 데이터 동기화 및 자동 리페칭
TanStack Query는 사용자의 행동에 따라 자동으로 데이터를 동기화한다.
- Window Focus 시 리페칭: 사용자가 브라우저 탭을 다시 열면 최신 데이터 자동 요청
- 네트워크 복구 시 리페칭: 오프라인 상태였다가 다시 연결되면 최신 데이터 요청
- Refetch 옵션: 특정 이벤트(ex 버튼 클릭)에서 refetch를 호출하여 데이터를 갱신 가능
const { data, refetch } = useQuery(['profile'], fetchProfile, { refetchOnWindowFocus: true });
<button onClick={() => refetch()}>다시 불러오기</button>
4) 중복 요청 방지 및 배치 처리
TanStack Query는 동일한 Query Key를 가진 중복 요청을 자동으로 방지한다. 또한, 여러 개의 Mutation을 배치 처리(batch processing)하여 성능을 최적화할 수 있다.
const mutation = useMutation(updateUser, {
onMutate: (newData) => {
queryClient.setQueryData(['user', newData.id], newData);
}
});
이렇게 하면 네트워크 요청을 보내기 전에 로컬 상태를 먼저 업데이트하여 빠른 응답성을 제공할 수 있다.
5) 오프라인 지원 및 백그라운드 동기화
기본적으로 캐시는 브라우저가 닫히거나 새로고침하면 사라진다. 하지만, TanStack Query는 persistQueryClient 기능을 제공하여 오프라인 환경에서도 데이터를 유지할 수 있다. 네트워크가 복구되면 자동으로 최신 데이터를 동기화하는 기능도 포함되어 있다.
persistQueryClient({ queryClient, storage: window.localStorage });
이 기능을 사용하면 네트워크가 끊겨도 (브라우저 창을 닫아도) 이전 데이터를 유지하면서, 연결이 복구될 때 자동으로 최신 데이터를 요청할 수 있다.
3. TanStack Query가 제공하는 이점
✅ TanStack Query의 주요 장점
- 자동 캐싱 및 중복 요청 방지 → 불필요한 네트워크 요청 최소화
- Stale Time, Cache Time을 통한 유연한 데이터 관리 → 성능 최적화
- 자동 동기화 및 백그라운드 데이터 갱신 → 항상 최신 데이터 유지
- 오프라인 지원 및 오류 복구 기능 → 사용자 경험 개선
🔥 기존 방식 대비 차이점
과거에는 Redux, Context API 등을 사용해 서버 상태를 직접 관리해야 했지만, TanStack Query는 이를 자동화하여 개발자의 부담을 줄인다. Redux와 비교하면, TanStack Query는 비즈니스 로직 없이 API 호출만으로 상태를 관리할 수 있어 코드가 간결하다는 장점이 있다.