TanStack Query 최신성: staleTime은 언제 다시 확인할 수 있게 하는가
재고 사본을 30분마다 요청한다고 오해해 오래된 수량을 보여 주는 실패를 관찰하고, 허용 가능한 오차 시간과 다시 가져오기 계기를 분리한다.
목차
표준·구현·측정·해석 표시는 무엇인가요?
- 표준웹 표준이나 언어 명세가 정한 동작
- 구현특정 기술이나 브라우저가 실제로 구현한 동작
- 측정명시한 환경에서 직접 실행해 관찰한 결과
- 해석앞선 근거에서 도출한 설계 판단
- 미확인아직 공식 근거나 재현 결과를 확인하지 못한 내용
30초 요약
staleTime은 요청 주기가 아니다. 데이터를 받은 뒤 fresh, 즉 “아직 다시 확인하지 않아도 됨”으로 취급할
시간이다. 시간이 지나면 사본은 오래됨으로 분류되지만, 실제 요청에는 창 초점 복귀나 새 화면 구독 같은
계기가 따로 필요하다. 숫자는 모든 화면의 공통 관례가 아니라 잘못된 사본을 허용할 수 있는 시간에서
정한다.
이 글을 관통하는 상황: 서버 재고는 0인데 화면에는 3이 남는다
다음은 TanStack Query가 설치된 React 앱에서 실행할 수 있는 실험이다. 메모리 변수 serverStock이
가짜 서버 원본이고 fetchStock은 그 시점의 사본을 돌려준다. 다른 경로에서 재고 0으로 변경은
Query에 알리지 않고 원본만 바꾼다.
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
import { useState } from 'react'
const queryClient = new QueryClient()
let serverStock = 3
async function fetchStock() {
return { count: serverStock }
}
function StockScreen() {
const [serverNotice, setServerNotice] = useState('서버 재고: 3')
const stockQuery = useQuery({
queryKey: ['stock', 'coffee'],
queryFn: fetchStock,
staleTime: 30 * 60 * 1000,
refetchOnWindowFocus: true,
})
if (stockQuery.isPending) return <p>재고 읽는 중…</p>
if (stockQuery.isError) return <p>재고를 읽지 못했습니다.</p>
return (
<main>
<p>화면 재고: {stockQuery.data.count}</p>
<p>{serverNotice}</p>
<p>
마지막 수신: {new Date(stockQuery.dataUpdatedAt).toLocaleTimeString()}
</p>
<button
type="button"
onClick={() => {
serverStock = 0
setServerNotice('서버 재고: 0')
}}
>
다른 경로에서 재고 0으로 변경
</button>
</main>
)
}
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<StockScreen />
</QueryClientProvider>
)
}처음에는 화면과 서버 재고가 모두 3이다. 변경 버튼을 누르면 가짜 서버는 0이지만 Query 사본은 3이다.
변경 직후 다른 브라우저 탭으로 갔다 돌아와도 화면은 3으로 남는다. 30분 안에는 사본이 fresh이므로
refetchOnWindowFocus: true여도 초점 복귀 요청 조건을 만족하지 않기 때문이다.
Query는 키로 서버 읽기 사본을 식별한다
이 사례의 ['stock', 'coffee']는 커피 재고 사본을 가리킨다. serverStock이 바뀌었다는 사실을
Query가 자동으로 관찰하지는 못한다. 다시 요청하거나 알려 주는 경계가 필요하다.
fresh는 정확성 증명이 아니다
fresh는 “서버와 방금 비교해 같음”이 아니라 “현재 정책상 다시 확인하지 않아도 됨”이라는 클라이언트 분류다. 다른 사용자나 시스템이 서버 원본을 바꾸면 fresh 기간 안에도 실제 값은 달라질 수 있다.
재고를 보고 주문 가능 여부를 판단하는 화면에서 30분 오차가 허용되지 않는다면 이 숫자는 너무 길다. 반대로 거의 변하지 않는 국가 코드 표에 매 초 다시 확인하는 것도 불필요한 비용이 될 수 있다.
중요한 재고 제한은 주문 요청을 처리하는 서버가 다시 검증해야 한다. 짧은 staleTime도 네트워크 지연과
다른 변경을 없애지 못하므로 화면 사본만으로 서버의 최종 업무 규칙을 보장하지 않는다.
오래됨 판정과 다시 가져오기 계기는 다르다
staleTime이 끝나는 순간 타이머가 요청을 실행하는 것은 아니다. 계속 같은 창을 보고 있고 다른 계기도
없다면 오래됨으로 분류된 사본이 화면에 남을 수 있다. 일정 간격으로 반복 요청하는 polling이 필요하면 refetchInterval,
사용자가 당기는 새로고침, 서버가 보내는 변경 신호처럼 별도 방식을 검토한다.
실험에서는 판단과 계기를 짧게 확인할 수 있도록 staleTime만 5초로 바꾼다.
const stockQuery = useQuery({
queryKey: ['stock', 'coffee'],
queryFn: fetchStock,
staleTime: 5_000,
refetchOnWindowFocus: true,
})설정을 바꾼 뒤 브라우저 전체 새로고침으로 queryClient와 serverStock을 다시 3으로 초기화한다.
재고를 0으로 바꾸고 5초 넘게 기다린다. 다른 탭으로 이동했다 돌아오면 초점 복귀가
다시 가져오기 계기가 되고 화면 재고도 0으로 바뀐다. 5초는 동작 확인용이지 운영 권장값이 아니다.
허용할 오차 시간에서 staleTime을 정한다
다음 네 가지를 함께 적는다.
- 원본이 얼마나 자주 바뀌는가?
- 오래된 값으로 사용자가 잘못 결정했을 때 피해가 얼마나 큰가?
- 다시 요청하는 네트워크·서버 비용은 얼마인가?
- 저장 성공, 수동 새로고침처럼 값을 즉시 다시 확인할 사건이 있는가?
값이 자주 바뀐다는 이유만으로 항상 0이 정답은 아니다. 화면 진입과 초점 전환이 잦으면 요청량이
커질 수 있다. 반대로 피해가 큰 값은 UI 최신성 정책과 별개로 서버 작업 경계에서 다시 검증한다.
최신성과 메모리 보존을 섞지 않는다
따라서 오래된 재고 3을 화면에 보여 주면서 배경 요청으로 0을 받아 교체할 수 있다. 반대로 fresh 사본도
화면에서 더는 사용하지 않으면 이후 보존 정책의 대상이 된다. gcTime과 비활성 Query는 14편에서
별도로 다룬다.
React Native는 창 초점 대신 앱 상태를 연결한다
fresh와 stale의 시간 판정은 같아도 “사용자가 돌아왔다”는 신호는 플랫폼마다 다르다. Web의 창 초점 설정을 Native에 그대로 복사하지 않고 앱 활성화와 화면 탐색 경계를 검증한다.
같은 재고 변경을 전후로 검증한다
- 최초 화면 재고와 가짜 서버 재고가 모두 3인지 확인한다.
- 서버만 0으로 바꾼 직후 화면은 3으로 남는지 확인한다.
- 30분 설정에서는 변경 직후 초점 복귀로 요청 시각과 화면 재고가 바뀌지 않는지 확인한다.
- 5초 실험 설정에서는 5초 전 초점 복귀가 요청하지 않는지 확인한다.
- 5초가 지난 뒤 다시 초점을 복귀하면 수신 시각과 화면 재고가 0으로 바뀌는지 확인한다.
문제 서버 재고는 0인데 탭으로 돌아온 화면에는 3이 남음
잘못된 판단 staleTime 30분은 30분마다 자동 요청하는 주기임
관찰 변경 직후 초점 복귀에도 fresh 사본이라 다시 가져오지 않음
원인 오래됨 판정 시간과 실제 다시 가져오기 계기를 같은 것으로 봄
행동 허용 오차 시간으로 staleTime을 정하고 필요한 계기를 별도 선택
검증 fresh 구간은 재사용하고 stale 뒤 초점 복귀에서 서버 사본 갱신풀 리퀘스트에서는 다음을 확인한다.
- Query가 나타내는 서버 사본과
queryKey가 명확한가? staleTime을 일정 간격 반복 요청인 polling 간격으로 설명하지 않는가?- 업무가 허용하는 오래된 정보의 시간을 근거로 값을 정했는가?
- stale이 된 뒤 실제로 다시 가져올 계기가 있는가?
- UI 최신성과 서버의 최종 업무 검증을 혼동하지 않는가?
staleTime과gcTime의 책임을 섞지 않는가?- Web 창 초점과 Native 앱·화면 초점을 별도로 연결했는가?
기억할 질문은 하나다.
이 사본이 언제부터 다시 확인할 수 있는 오래된 값이 되는가?
다음 글에서는 화면에서 사용하지 않는 Query 사본을 얼마 동안 메모리에 남길지 다룬다.
Active recall
기억에서 꺼내 보기
답을 쓰지 않아도 됩니다. 먼저 머릿속으로 답한 뒤 펼쳐서 정답·이유·흔한 오해를 비교하세요.
01staleTime이 30분이면 TanStack Query가 정확히 30분마다 요청하는가?
정답
아니다. 30분 뒤 사본을 오래됨으로 분류할 뿐이며, 새 mount, 창 초점 복귀, 네트워크 재연결이나 명시적 다시 가져오기 같은 계기가 별도로 필요하다.
관련 설명 다시 읽기왜 그런가
시간 판정과 요청 실행 계기를 분리해야 머무르는 화면의 자동 갱신을 과대평가하지 않는다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
02재고가 최대 1분 정도만 오래되어야 하는데 staleTime을 30분으로 두면 어떤 실패가 생기는가?
정답
30분 동안 사본을 fresh로 보므로 그 안에 창으로 돌아와도 초점 기반 다시 가져오기가 실행되지 않아 바뀐 서버 재고를 놓칠 수 있다.
관련 설명 다시 읽기왜 그런가
fresh는 서버 값이 실제로 같다는 보장이 아니라 정책상 아직 다시 확인하지 않아도 된다는 분류다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
03staleTime 숫자는 어떤 질문에서 출발해야 하는가?
정답
이 화면의 사본이 서버 원본보다 얼마나 오래 달라도 사용자 결정과 업무 결과를 해치지 않는가에서 출발한다.
관련 설명 다시 읽기왜 그런가
변화 빈도, 잘못된 정보의 피해, 다시 확인 비용과 별도 서버 검증을 함께 보고 값을 정한다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
04오래된 사본이 되었다는 사실이 캐시에서 삭제됐다는 뜻인가?
정답
아니다. staleTime은 다시 확인할 수 있는 최신성 분류이고, 사용하지 않는 사본의 메모리 보존은 gcTime이 담당한다.
관련 설명 다시 읽기왜 그런가
stale Query도 화면에 계속 보일 수 있고 배경 다시 가져오기로 새 결과를 받을 수 있다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
출처와 검증 범위
아래 날짜는 링크를 마지막으로 열어 본 날입니다. 문서 상단의 검증일은 글의 설명과 적용 범위를 다시 확인한 날입니다.
- QueriesTanStack · 공식 문서 · 확인 2026-08-09
- useQueryTanStack · 공식 문서 · 확인 2026-08-09
- QueryClientTanStack · 공식 문서 · 확인 2026-08-09
- QueryClientProviderTanStack · 공식 문서 · 확인 2026-08-09
- Important DefaultsTanStack · 공식 문서 · 확인 2026-08-09
- React NativeTanStack · 공식 문서 · 확인 2026-08-09