우당탕탕
React Query 도입하면서 알게 된 캐싱 함정과 체크리스트 본문
React Query를 도입할 때, 캐싱이 똑똑하게 작동할 거라 믿었는데 막상 써보니 예상치 못한 함정들이 많더라고요. 저도 이 부분에서 한참 삽질해서 시간을 좀 잡아먹었거든요.
이 글에서는 제가 겪은 React Query 캐싱 문제들과, 실제 구현하면서 꼭 챙겨야 하는 ✅ 체크리스트를 중심으로 정리해봤어요. 캐시 무효화부터 데이터 일관성, 그리고 쿼리 키 관리법까지, 한 번에 끝내고 싶다면 끝까지 읽어보세요.
개발 환경 / 버전 정보
제가 사용한 React Query 버전은 @tanstack/react-query v4.29.1이고, React 18 최신 버전이에요. TypeScript도 함께 쓰고 있습니다.
React Query 도입하면서 알게 된 캐싱 함정 관련 정보
React Query 캐시 기본 이해하고 쓰기
사실 이 부분이 가장 헷갈렸는데, React Query는 쿼리 키를 기반으로 데이터를 캐싱하고, 내부적으로 stale 상태인지 fresh 상태인지 관리해요. 그런데 이걸 모르고 무조건 refetch나 invalidate를 남발하면 캐싱 효과가 거의 없거든요.
기본 쿼리 사용법은 아래 코드처럼 간단한데, 여기서 쿼리 키 정말 신경 써야 하더라고요.
const { data, error, isLoading } = useQuery(['todos'], fetchTodos);
'todos'라는 키 하나만으로 캐시를 관리하는데, 만약 어떤 조건이나 페이지 번호 같은 게 있다면 키에 꼭 포함해야 해요. 그렇지 않으면 캐시가 중복 재사용돼서 데이터 이상 현상이 발생합니다.
함정 빠지기 쉬운 캐싱 체크리스트 ✅
- 쿼리 키에 모든 변수를 포함했나요? 조건, 페이지, 필터, 정렬 등 화면에서 다르게 보일 수 있는 값은 키에 포함해야 캐시가 꼬이지 않아요.
- 불필요한 refetch가 너무 많지는 않은가요? 기본 staleTime이 너무 짧으면 자주 네트워크 콜이 발생해 효율이 떨어져요. 상황에 맞게 staleTime, cacheTime 조절이 필수입니다.
- 데이터 변경 시 캐시 무효화를 시켜주고 있나요? mutation 이후 invalidateQueries로 관련 쿼리 캐시를 업데이트해야 데이터 일관성이 맞아요.
- 쿼리 데이터가 예상보다 오래 유지되고 있진 않나요? cacheTime 설정을 안 건드리면 기본 5분간 캐시가 남는데, 이때 오래된 데이터 노출 주의!
- 동일한 API라도 쿼리 키가 다르면 캐시가 따로 관리되는 점 이해했나요? 예를 들어, ['todos', page]랑 ['todos', filter] 키가 다르면 분리된 캐시가 만들어집니다.
React Query 도입하면서 알게 된 캐싱 함정 관련 정보
mutation 후 캐시 업데이트 이렇게 했어요
사실 여기서 제가 꽤 오래 허덕였는데, 데이터를 수정하고 나서 캐시를 적절히 무효화해줘야 최신 상태를 화면에 반영할 수 있어요.
가장 기본적인 패턴은 invalidateQueries를 사용하는 것인데, 이렇게요.
const queryClient = useQueryClient();
const mutation = useMutation(updateTodo, {
onSuccess: () => {
// 캐시된 todos 쿼리 무효화
queryClient.invalidateQueries(['todos']);
},
});
이렇게 하면 업데이트 후 자동으로 최신 데이터를 불러오기 때문에 UI가 깔끔하게 갱신돼요.
여기서 막혔던 부분도 있었어요
제가 당황했던 대표적인 에러는 캐시가 영 안 바뀌어서 뭔가 이상하다 했는데, 알고 보니 쿼리 키가 다르게 설정되어 완전히 다른 캐시가 만들어진 경우였어요.
// 문제: page 값을 빼먹어서 모든 페이지가 같은 캐시로 처리됨
useQuery(['todos'], fetchTodosPage(page));
// 올바름: page도 키에 포함시켜야 함
useQuery(['todos', page], fetchTodosPage(page));
이 에러 찾느라 한참 디버깅했는데, 쿼리 키 설계가 얼마나 중요한지 절감했어요.
React Query 도입하면서 알게 된 캐싱 함정 관련 정보
심화: 쿼리 캐시 직접 수정하는 팁
React Query는 mutate 이후 무조건 refetch하지 않고, 캐시 데이터를 직접 수정하는 방법도 제공하는데 이것도 유용해요. 특히 UI를 바로 반영해야 할 때 좋아요.
const queryClient = useQueryClient();
const mutation = useMutation(updateTodo, {
onSuccess: (updatedTodo) => {
queryClient.setQueryData(['todos'], (oldData) => {
if (!oldData) return;
return oldData.map(todo => todo.id === updatedTodo.id ? updatedTodo : todo);
});
},
});
이렇게 하면 refetch 없이도 UI가 즉시 바뀌는 게 엄청 편하더라고요. 다만 캐시 구조를 잘 파악하고 있어야 안전하게 업데이트 가능해요.
자주 물어보시는 것들
Q. staleTime과 cacheTime 차이가 뭔가요?
A. staleTime은 데이터를 fresh 상태로 유지하는 시간이고, cacheTime은 캐시가 메모리에 남아있는 기간이에요. staleTime 지나면 자동 refetch 대상이 되고, cacheTime 지나면 아예 캐시 삭제됩니다.
Q. queryClient.invalidateQueries를 여러 개 키에 써도 되나요?
A. 네, 가능합니다. 다만 무분별하게 쓰면 네트워크 낭비가 될 수 있으니 꼭 필요한 쿼리만 골라서 무효화하는 게 좋아요.
Q. 쿼리 키에 배열 대신 객체를 써도 되나요?
A. React Query 공식 문서에선 쿼리 키로 배열 사용을 권장해요. 객체는 참조가 바뀔 때마다 새 키로 인식해서 캐시가 불필요하게 분리될 위험이 있어요.
React Query는 한 번만 도입해도 프론트엔드 데이터 관리가 확실히 편해지는데, 캐시 관리는 생각보다 꼼꼼하게 살펴봐야 하더라고요. 이번 체크리스트 덕분에 저도 실수 줄이고 안정적으로 쓸 수 있게 됐어요. 다음 프로젝트에 바로 써보시면 좋겠습니다.
📌 함께 보면 좋은 글
'언어 > JavaScript' 카테고리의 다른 글
| JavaScript 클로저, 저도 처음엔 헷갈렸던 실전 사례들 (0) | 2026.07.22 |
|---|---|
| React useMemo와 useCallback, 진짜 필요할 때만 쓰는 법 알려드려요 (0) | 2026.07.18 |
| React useEffect 의존성 배열 직접 써보고 알게 된 진짜 비용 차이 (0) | 2026.07.04 |
| React useMemo와 useCallback, 진짜 필요할 때만 사용하는 체크리스트 (0) | 2026.06.23 |
| Next.js 13 App Router 마이그레이션하면서 꼭 확인한 체크리스트 (0) | 2026.06.22 |
