공개 응답 캐시 누락과 출처 간 키 충돌 해결

이 글의 목차 7개 항목

문제와 원인

공개 응답 캐시는 caches.default만 선택해 open() 전용 저장소에서 저장·조회를 건너뛰었다. 저장소가 연결된 경우에도 고정 출처 CACHE_ORIGIN으로 키를 만들어, namespace·콘텐츠 키·빌드가 같은 A·B 출처의 요청이 충돌했다. A 응답을 저장하면 B 요청에도 A 본문이 반환됐다.

수정 전 코드

sharedCache / cacheRequest

function sharedCache() {
  const storage = globalThis.caches;
  return storage?.default ?? null;
}

function cacheRequest(namespace: string, key: string) {
  const url = new URL(`/api/${namespace}`, CACHE_ORIGIN);
  url.searchParams.set("build", __BAEUMZIP_BUILD_SHA__.slice(0, 12));
  url.searchParams.set("key", key);
  return new Request(url, { method: "GET" });
}

수정 후 코드

sharedCache

default가 있으면 사용하고, 없으면 이름 있는 캐시를 open()으로 연다.

async function sharedCache() {
  const storage = globalThis.caches;
  if (storage?.default) return storage.default;
  if (!storage?.open) return null;
  return storage.open("baeumzip-public-responses").catch(() => null);
}

cacheRequest

키는 요청 URL의 origin을 유지하고 pathname을 캐시 전용 경로로 바꾼다. 기존 쿼리를 지운 뒤 빌드·콘텐츠 키를 넣으며, 조회·저장 양쪽에 요청 객체를 전달해 같은 생성 함수를 사용한다.

function cacheRequest(request: Request, namespace: string, key: string) {
  const url = new URL(request.url);
  url.pathname = `/__baeumzip-cache/${namespace}`;
  url.search = "";
  url.searchParams.set("build", __BAEUMZIP_BUILD_SHA__.slice(0, 12));
  url.searchParams.set("key", key);
  return new Request(url, { method: "GET" });
}

저장에는 Response.clone()을 사용해 호출자가 읽을 본문을 보존한다.

검증 결과

Node.js에서 수정 전후 원본 캐시 모듈을 Cache Storage 대역의 같은 공개 JSON 응답으로 실행하고, 저장소 2종·합성 출처 2개를 비교했다. Wrangler의 로컬 Cache API에서도 같은 모듈로 A 응답을 저장한 뒤 B 출처에서 조회했다.

입력 조건수정 전수정 후
default에 A 응답 저장·조회저장 1개, 적중저장 1개, 적중
open() 전용 저장소에 응답 저장·조회저장 0개, 미적중저장 1개, 적중
정상 저장·조회한 저장소 형태1/2종2/2종
A만 저장하고 B에서 같은 콘텐츠 키 조회A 본문 반환미적중
이어서 B 응답 저장기존 키 덮어쓰기, 키 1개A·B 각각 저장, 키 2개
B 저장 후 B에서 다시 조회B 본문 반환B 본문 반환
수정 전 B→A 응답, 수정 후 B→MISS원본 보기 (새 창)

수정 전 B→A 응답, 수정 후 B→MISS

100%원본 보기 (새 창)

확대한 이미지는 상하좌우로 이동할 수 있습니다. 키보드는 이미지 영역에 초점을 두고 방향키를 사용하세요. Esc로 닫습니다.