Files
llm-wiki/raw/official-docs/tanstack-query-persistence-hydration-official.md
T

8.2 KiB

title, source_type, url, archive_url, related_branches, related_projects, tags, created
title source_type url archive_url related_branches related_projects tags created
TanStack Query — persistQueryClient / dehydrate·hydrate official-doc https://tanstack.com/query/latest/docs/framework/react/plugins/persistQueryClient
feature-frontend-cache-tier-cross-tab-invalidation-contract
ca-skeleton-frontend
frontend
server-state
cache
persistence
tanstack-query
2026-07-28

TanStack Query — persistQueryClient / dehydrate·hydrate

Layer: raw/ — 외부 자료의 원문 발췌·출처 기록.

활용 branch (필수, 최소 1개+)

Branch 이 자료가 정당화하는 결정
[[raw/branch-notes/feature-frontend-cache-tier-cross-tab-invalidation-contract]] version 불일치 시 부분 복원 없이 전량 폐기(D1), 복원과 렌더의 순서(D6), 영속 대상 선별과 직렬화 책임(D7)

출처

왜 저장했는지

이 repo 의 기존 TanStack 발췌(raw/official-docs/tanstack-query-server-state-official)는 server state 원칙만 담고 있어 캐시 영속을 다루지 않는다. CachePersistencePort 를 설계하면서 세 가지를 확인해야 했다. (a) 라이브러리가 버전 불일치 캐시를 부분 복원하는지 폐기하는지, (b) 복원이 렌더와 경합하는지, (c) 어떤 query 가 기본적으로 영속 대상인지. 세 가지 모두 공식 문서가 직접 답한다.

핵심 인용

[§persistQueryClient — buster] "If the cache that is found does not also have that buster string, it will be discarded."

[§persistQueryClient — maxAge] "The max-allowed age of the cache in milliseconds. If a persisted cache is found that is older than this time, it will be silently discarded (defaults to 24 hours)"

[§persistQueryClient — How it works] "If data is found to be any of the following: 1. expired (see maxAge) 2. busted (see buster)... the persister removeClient() is called and the cache is immediately discarded."

[§persistQueryClient] "createSyncStoragePersister and createAsyncStoragePersister throttle this action to happen at most every 1 second to save on potentially expensive writes."

[§persistQueryClient — PersistQueryClientProvider] "However, restoring is asynchronous, because all persisters are async by nature, which means that if you render your App while you are restoring, you might get into race conditions if a query mounts and fetches at the same time."

[§hydration — dehydrate] "It only includes currently successful queries by default."

[§hydration — dehydrate Options] "shouldDehydrateQuery — Whether to dehydrate queries." / "Defaults to only including successful queries"

[§hydration — dehydrate Returns] "This result is not in serialized form, you need to do that yourself if desired"

[§hydration — hydrate Limitations] "If the queries you're trying to hydrate already exist in the queryCache, hydrate will only overwrite them if the data is newer than the data present in the cache."

추출된 주장

Claim ID Claim (이 자료가 직접 말하는 것) Evidence quote Strength Applies to Does not prove
C1 저장된 캐시의 buster 문자열이 일치하지 않으면 그 캐시는 폐기된다 [§buster] "If the cache that is found does not also have that buster string, it will be discarded." official-reference persistQueryClient 복원 경로 buster 를 무엇으로 구성해야 하는지 — 문자열 내용은 소비자가 정한다
C2 buster 불일치·만료 시 removeClient() 가 호출되고 캐시는 즉시 폐기된다 — 부분 복원 경로가 없다 [§How it works] "the persister removeClient() is called and the cache is immediately discarded" official-reference 복원 실패 처리 폐기가 사용자에게 고지되어야 한다는 뜻은 아님
C3 maxAge 기본값은 24시간이며 초과분은 조용히 폐기된다 [§maxAge] "it will be silently discarded (defaults to 24 hours)" official-reference 라이브러리 기본값 24시간이 우리 계약의 TTL 이라는 뜻은 아님 — 우리 값은 FE-REG-STORAGE 가 정한다
C4 번들 제공 persister 는 write 를 최대 1초에 1회로 throttle 한다 [§persistQueryClient] "throttle this action to happen at most every 1 second to save on potentially expensive writes" official-reference createSyncStoragePersister·createAsyncStoragePersister 우리 custom adapter 의 적정 간격이 1초라는 뜻은 아님 — 번들 구현의 선택값이다
C5 복원은 비동기이며, 복원 중 렌더하면 query mount·fetch 와 경합이 발생할 수 있다 [§PersistQueryClientProvider] "if you render your App while you are restoring, you might get into race conditions if a query mounts and fetches at the same time" official-reference 모든 async persister 경합이 항상 눈에 보이는 오류로 나타난다는 뜻은 아님
C6 dehydrate 는 기본적으로 성공한 query 만 포함한다 [§dehydrate] "It only includes currently successful queries by default." official-reference 영속 대상 선별 error/pending query 를 영속하면 안 된다는 금지 규칙까지는 아님 — 기본값일 뿐
C7 dehydrate 결과는 직렬화된 형태가 아니며 직렬화는 소비자 책임이다 [§dehydrate Returns] "This result is not in serialized form, you need to do that yourself if desired" official-reference 영속 adapter 어떤 직렬화 형식을 써야 하는지
C8 hydrate 는 이미 캐시에 있는 query 를 더 새로운 데이터일 때만 덮어쓴다 [§hydrate Limitations] "hydrate will only overwrite them if the data is newer than the data present in the cache" official-reference 복원 시 병합 "더 새롭다"의 판정 기준이 무엇인지

적용 경계

  • 이 자료가 직접 증명하는 것:
    • C1·C2: 버전 불일치 캐시의 처리는 부분 복원이 아니라 전량 폐기가 라이브러리의 기존 동작이다
    • C5: 복원과 렌더를 겹치면 경합이 생기므로 gating 이 필요하다
    • C6~C8: 영속 대상 기본 선별, 직렬화 책임 위치, 복원 시 병합 규칙
  • 이 자료가 증명하지 않는 것:
    • 파티션 키를 releaseId·configSchemaVersion·apiContractVersion 세 값으로 구성해야 한다는 것 — C1 은 buster 가 문자열이라는 것만 말한다. 구성은 project decision 이다
    • CachePersistencePortQueryCachePort 와 분리해야 한다는 것 — 문서는 단일 queryClient 를 전제한다
    • 우리 adapter 의 throttle 값 (C4 는 번들 persister 의 선택값)
  • 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
    • C3 의 24시간 기본값과 FE-REG-STORAGEQUERY_CACHE_SNAPSHOT TTL 이 충돌하지 않는지
    • C8 의 "newer" 판정이 우리 파티션 폐기 규칙보다 먼저 적용되는지 (폐기가 우선이면 무관)

메모

  • 인용 1 해석 후보 (미검증): C1+C2 는 branch 의 D1("불일치 시 복원하지 않고 폐기, 부분 복원 금지")이 자체 발명이 아니라 채택한 라이브러리의 기존 동작과 같은 방향임을 보여준다. 다만 세 값 tuple 구성은 여전히 project-local 이다.
  • 인용 2 해석 후보 (미검증): C5 때문에 CachePersistencePort.restore() 는 bootstrap 단계에서 await 되어야 하고, 복원 실패는 부팅을 막지 않고 메모리 캐시로 진행해야 한다(§8.2 CACHE_PERSISTENCE_FAILURE 와 정합).
  • 추가로 봐야 할 동일 출처 페이지: createAsyncStoragePersister, broadcastQueryClient experimental plugin

관련

  • 같은 주제 다른 official-doc: [[raw/official-docs/tanstack-query-server-state-official]], [[raw/official-docs/mdn-broadcastchannel-storage-event]]
  • 이 자료를 인용한 wiki 요약: 생성 전