캐시 전략과 무효화 전략

김현준·2025년 6월 6일
0

넥스트JS 이모저모

목록 보기
16/23

문제

넥스트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_cachetags에 모든 데이터의 상위 그룹 태그를 함께 등록한다

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');

모든 조건의 로그 리스트 캐시가 한 번에 무효화됨

응용 팁

  • 캐시 키는 상세하게
  • 태그 키는 범용 그룹 단위로
    이렇게 분리하면 CSR + SSR 쿼리 불일치 문제나 hydration mismatch도 방지 가능하다.
// 쿼리 키는 조건 기반
['log', 'list', '2', '10', 'latest']

// 태그 키는 범용
'log:all'
profile
기록하자

0개의 댓글