우당탕탕
React Query 도입하며 겪은 2026년 캐싱 변화와 주의할 점 본문
제가 최근에 React Query를 프로젝트에 도입하면서 생각보다 캐싱 문제로 삽질을 좀 했어요. 그런데 더 재미있는 건, 2026년에 들어와서 작년과는 다르게 내부 동작이나 API가 조금씩 바뀐 부분이 있어서 그에 맞춰 업데이트를 안 하면 예상치 못한 버그가 발생한다는 점이었죠.
이번 글에서는 제가 직접 겪은 캐싱 관련 문제들을 중심으로 2026년 현재 React Query에서 달라진 점부터, 도입하면서 주의해야 할 함정들, 그리고 실제 코드 예제까지 꼼꼼히 다뤄볼게요.
개발 환경 / 버전 정보
2026년 6월 기준으로 제가 사용한 React Query 버전은 @tanstack/react-query v5.0.0이고, React는 18.2.0입니다. 참고로 이 버전부터는 내부적으로 캐싱 전략과 관련해서 기본 동작이 일부 변경되었어요.
React Query 도입하면서 알게 된 캐싱 함정 관련 정보
React Query 캐시 정책, 2026년에 이렇게 바뀌었어요
사실 캐싱이 React Query의 핵심이긴 한데, 이번 버전부터 기본 staleTime과 cacheTime의 기본값이 달라졌더라고요. 예전에는 기본 staleTime가 0이라서 데이터가 항상 fresh하지 않다고 간주해 리페치가 빈번했는데, 지금은 기본 staleTime이 1000ms로 설정되어 있어요. 작년 코드 기준으로 만들었다면 이 차이 때문에 리렌더나 리페치 동작이 달라질 수밖에 없죠.
또 최근에 React Query는 cacheTime 기본값을 5분에서 10분으로 늘렸는데, 그 영향으로 컴포넌트가 언마운트되어도 캐시 데이터가 더 오래 남아서 네트워크 요청 횟수가 줄어드는 장점이 있지만, 오래된 데이터가 화면에 보일 위험성도 커졌어요.
이렇게 하면 캐싱 헷갈리는 부분을 줄일 수 있어요
저도 캐시 전략 때문에 한참 고민하다가 명확한 기준을 세웠는데, 실제 코드에서 어떻게 안정적으로 쓰는지 아래 예시를 봐주세요.
import { useQuery } from '@tanstack/react-query';
// 1초 동안은 fresh 상태로 리페치 안 함
// 캐시는 10분 유지해서 네트워크 부담 감소
const fetchUser = async () => {
const res = await fetch('/api/user');
if (!res.ok) throw new Error('네트워크 에러');
return res.json();
};
export function UserProfile() {
const { data, error, isLoading } = useQuery(['user'], fetchUser, {
staleTime: 1000, // 1초 동안 fresh
cacheTime: 10 * 60 * 1000, // 캐시 10분 유지
refetchOnWindowFocus: false
});
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러 발생: {error.message}</div>;
return <div>안녕하세요, {data.name}님!</div>;
}
이렇게 기본값에 의존하지 말고 캐싱 기준을 명확히 지정하면 예상외의 리페치나 UI 깨짐을 방지할 수 있어요. 특히 staleTime과 cacheTime이 밀접하게 연관되어 있으니 꼭 함께 고민해보셔야 합니다.
React Query 도입하면서 알게 된 캐싱 함정 관련 정보
의외로 헷갈리던 캐시 무효화 방법
제일 해맸던 게 바로 캐시 무효화였는데요, 예전에는 queryClient.invalidateQueries('key')가 기본 동작이었지만, 2026년 버전에서는 옵션에 따라 비동기 상태 변화가 더 엄격해졌어요. 즉, 무효화 직후 바로 리페치가 보장되지 않거나, 옵티미스틱 업데이트와 충돌하는 경우가 있어서 신경 써야 하더라고요.
게다가 refetchOnMount 기본값도 변경돼서, 컴포넌트가 다시 마운트될 때 캐시가 stale하다고 판단되면 자동 리페치가 일어나는데, 이걸 모르고 있으면 의도치 않게 네트워크가 더 많이 호출돼 버립니다.
실제 삽질했던 캐싱 문제와 해결 과정
제가 한 프로젝트에서 캐시를 오래 유지하려다 보니, 업데이트 후 화면이 최신 상태로 안 보이는 이슈가 있었어요. 처음엔 서버에서 데이터가 안 온다고 생각했는데, 캐시가 10분 동안 안 날아가면서 staleTime이 길어서 리페치가 안 된 거였어요.
// 문제 코드 예시
queryClient.invalidateQueries(['posts']);
// 하지만 아래 옵션들이 기본값이라 자동 리페치가 보장되지 않았음
const { data } = useQuery(['posts'], fetchPosts, {
staleTime: 5 * 60 * 1000,
cacheTime: 10 * 60 * 1000,
refetchOnMount: 'always', // 이걸 빼먹으면 최신화 안 됨
});
수정해서 refetchOnMount: 'always' 옵션을 넣고 나니, 캐시 무효화 후 컴포넌트가 다시 마운트될 때마다 최신 데이터가 제대로 갱신되는 걸 확인했습니다.
React Query 도입하면서 알게 된 캐싱 함정 관련 정보
심화: 옵티미스틱 업데이트와 캐시 동기화 노하우
옵티미스틱 업데이트는 UI 반응성을 높이지만, 캐시를 직접 조작해야 하기에 캐시 동기화 문제를 잘 관리해야 해요. React Query 5버전부터는 useMutation에서 반환하는 onMutate, onSettled 훅을 활용해 캐시를 안전하게 업데이트하는 방법이 권장됩니다.
const mutation = useMutation(updatePost, {
// 서버 호출 전에 캐시 잠시 변경
onMutate: async newPost => {
await queryClient.cancelQueries(['posts', newPost.id]);
const previousPost = queryClient.getQueryData(['posts', newPost.id]);
queryClient.setQueryData(['posts', newPost.id], newPost);
return { previousPost };
},
// 실패했을 때 캐시 복구
onError: (err, newPost, context) => {
queryClient.setQueryData(['posts', newPost.id], context.previousPost);
},
// 성공하거나 실패 후 무조건 리페치
onSettled: newPost => {
queryClient.invalidateQueries(['posts', newPost.id]);
}
});
이렇게 하면 UI가 즉각 반응하면서도 서버 상태와 캐시 상태가 꼬이지 않아 훨씬 안정적이에요. 저도 이 패턴으로 바꾼 뒤에 캐시 관련 버그가 눈에 띄게 줄었고, 네트워크 통신도 효율적으로 관리할 수 있었답니다.
자주 헷갈리는 질문들
Q. 2025년에 작성한 React Query 캐싱 코드를 그대로 써도 괜찮나요?
A. 100%는 아니에요. 위에서 설명한 staleTime, cacheTime, refetchOnMount 기본값 변경 때문에 예상치 못한 리페치 동작 차이가 있을 수 있어요. 꼭 최신 문서와 변경 로그를 확인해서 캐시 정책을 명확히 재설정하세요.
Q. 캐시가 너무 오래 유지돼서 데이터가 불일치하는데 어떻게 해결하나요?
A. cacheTime 값을 줄이고 staleTime도 적절히 조정하세요. 또는 캐시 무효화를 철저히 하거나 refetchOnWindowFocus, refetchOnReconnect 옵션을 켜서 백그라운드 상태 변화에 따라 자동 갱신되게 하는 방식을 추천해요.
Q. 서버 상태 동기화가 잘 안 돼서 옵티미스틱 업데이트가 부담스러워요.
A. 옵티미스틱 업데이트는 사려 깊게 써야 해요. 먼저 onMutate에서 캐시를 미리 변경하고, 실패 시 복구하는 패턴을 꼭 활용하고, 너무 복잡하면 기본 무효화 방식으로 돌아가는 것도 괜찮습니다.
제가 직접 겪어서 확실히 말씀드릴 수 있는 건, 2026년 기준 React Query 캐싱 기본값과 내부 동작들이 예전과 달라진 부분을 모르고 쓰면 정말 복잡한 버그가 생겨요. 그래서 캐싱 전략을 명확히 하고, 공식 문서 최신 버전을 자주 참고하는 게 가장 중요하더라고요.
이번 경험 덕분에 React Query 캐싱에 대한 이해도가 훨씬 좋아졌고, 덕분에 앞으로는 좀 더 안정적인 데이터 관리가 가능해 졌습니다. 만약 아직 2026년 바뀐 점을 반영하지 않은 상태라면 꼭 이번 기회에 한번 점검해보세요.
'언어 > JavaScript' 카테고리의 다른 글
| JavaScript 클로저, 실전에서 헷갈렸던 부분 단계별로 정리했어요 (0) | 2026.09.24 |
|---|---|
| TypeScript 제네릭 타입 추론할 때 제가 헷갈렸던 체크리스트 (0) | 2026.09.15 |
| Zustand vs Redux 직접 써보고 고른 상태관리 선택 기준 (0) | 2026.09.01 |
| React useEffect 의존성 배열 때문에 헤매다 깨달은 점들 (0) | 2026.08.31 |
| Next.js 13 App Router로 마이그레이션하며 알게 된 비용 차이와 경험 (0) | 2026.08.29 |
