WeakMap의 키를 “입력 배열의 참조 자체”로 쓰면 캐시 무효화 로직이 0줄이 된다 — 무효화를 참조 동등성에 위임.
const indexCache = new WeakMap<Item[], Map<string, string>>()
// name → id 역인덱스를 1회 빌드 (O(n))
const buildIndex = (items: Item[]) =>
new Map(items.map((it) => [it.name, it.id]))
function lookup(items: Item[], name: string): string | null {
let index = indexCache.get(items)
if (!index) {
index = buildIndex(items) // 캐시 미스일 때만 빌드
indexCache.set(items, index)
}
return index.get(name) ?? null
}
- 같은 참조로 재호출 → cache hit, O(1)
- 데이터가 새 참조로 교체(refetch 등) → 자동 miss → 새 index 빌드. 옛 index는 옛 items와 함께 GC 대상
- 키가 weak reference라 items가 어디서도 안 잡히면 entry도 같이 사라짐. 일반
Map이면 참조가 영구히 붙들려 메모리 누수
전제: 호출 측 items 참조가 stable해야 작동. 매번 [...data]·data.filter()로 새 배열을 넘기면 항상 miss라 무의미. (TanStack Query의 data/select 결과는 참조 안정성을 보장하므로 그대로 넘기면 OK)