넥스트js15 + 수파베이스로 프로젝트를 진행하면서 클라이언트 캐시와 서버 캐시를 공유하고 싶었다.
그래서 dehydrate + HydrationBoundary 적략을 선택하고, 서버 데이터를 캐실 하는 것은 unstable_cache를 쓰고 SSR fetch 결과를 서버에 캐싱했다.
그리고 서버 캐시와 클라이언트 캐시(탠스택 쿼리)에 각각 태그키와 쿼리키를 만들어 적용했다.
하지만 이때 페이지네이션이 적용된 리스트 API에 다음과 같은 문제가 생겼다.
revalidateTag('logList') 이런 식으로는 모든 페이지 캐시가 무효화되지 않음// 쿼리 키
logKeys.list(params) 
// → ['log', 'list', '1', '10', 'latest']
// 태그 키
cacheTags.logList(params)
// → 'log:list:1:10:latest'
위처럼 파라미터를 포함한 태그 키를 쓰면,
revalidateTag('log:list:1:10:latest') // ← 특정 페이지만 무효화됨
→ 다른 페이지는 무효화 안 됨
1. 태그 키는 "상위 그룹" 중심으로 작성한다
// 캐시 등록 시
tags: [logKeys.list(params), 'log:all'] // 'log:all': 필터링과 무관한 상위 그룹 키
2. unstable_cache의 tags에 모든 데이터의 상위 그룹 태그를 함께 등록한다
export async function getLogs(params: LogsParams) {
  const queryKey = logKeys.list(params);
  const tagKey = cacheTags.logList(params);
  return unstable_cache(
    () => fetchLogs(params),
    [...queryKey].map((v) => v ?? ''),
    {
      tags: [tagKey, 'log:all'], // 핵심 포인트
      revalidate: 300,
    }
  )();
}
// 로그 추가/삭제/수정 후 전체 리스트 무효화
revalidateTag('log:all');
모든 조건의 로그 리스트 캐시가 한 번에 무효화됨
// 쿼리 키는 조건 기반
['log', 'list', '2', '10', 'latest']
// 태그 키는 범용
'log:all'