SSR 메타데이터와 본문의 중복 조회 줄이기

이 글의 목차 7개 항목
  • 분야: DB
  • 기술 스택: TypeScript · Cloudflare D1 · SQLite · AsyncLocalStorage
  • 관련 기술: SSR 메타데이터 · 요청 범위 메모이제이션 · Promise 공유 · 콘텐츠 revision

문제와 원인

메타데이터와 본문이 같은 이론 경로를 각각 조회할 때마다 D1에서 revision을 읽었다. 캐시 미적중에서는 첫 조회의 revision·콘텐츠 읽기 2회에 두 번째 revision 읽기 1회가 추가됐다. 캐시 적중에서도 revision 확인 2회가 남았다.

수정 전 코드

readPublicTheoryPage()

const revision = await readContentCacheRevision();
return readPublicContentCache({
  namespace: "public-theory-page",
  key: pathname,
  revision,
  loader: () => readUncachedPublicTheoryPage(route),
});

수정 후 코드

AsyncLocalStorage의 요청별 Map에 경로를 키로 넣고, work()가 반환한 Promise를 즉시 저장했다. 메타데이터와 본문은 진행 중인 작업을 함께 기다린다. 실패한 Promise는 제거해 재시도를 허용하고, 다음 HTTP 요청은 새 Map으로 revision을 다시 확인한다.

readPublicTheoryPage()

revision 확인부터 콘텐츠 캐시 조회까지 같은 작업으로 감싼다.

return memoizeD1Request(`public-theory-page:${pathname}`, async () => {
  const revision = await readContentCacheRevision();
  return readPublicContentCache({
    namespace: "public-theory-page", key: pathname, revision,
    loader: () => readUncachedPublicTheoryPage(route),
  });
});

memoizeD1Request()

export function memoizeD1Request<T>(key: string, work: () => Promise<T>) {
  const memo = scope.getStore()?.requestMemo;
  if (!memo) return work();
  const existing = memo.get(key);
  if (existing) return existing as Promise<T>;
  const pending = work();
  memo.set(key, pending);
  void pending.catch(() => {
    if (memo.get(key) === pending) memo.delete(key);
  });
  return pending;
}

검증 결과

수정 전후 원본 이론 조회 서비스·계측 모듈을 Node.js·SQLite에서 같은 기준 DB·경로로 실행했다. 요청마다 메타데이터·본문 순서로 2번 호출하고 D1 호환 어댑터 카운터와 X-DB-Ops 헤더를 대조했다. Cloudflare Wrangler의 로컬 D1에서도 같은 호출 감소를 확인했다.

조건수정 전 D1 호출수정 후 D1 호출결과
콘텐츠 캐시 미적중3회2회1회 감소·33.3%
새 요청의 캐시 적중2회1회1회 감소·50.0%
다음 요청 전 제목·revision 변경3회2회변경 제목 반영

메타데이터·본문의 전체 페이지 값은 전후 모두 일치했고, 초기 페이지의 직렬화 SHA-256도 같았다. 모든 비교 조건에서 계측 헤더와 어댑터 카운터가 일치했다.

Local Explorer의 d1_all·d1_batch 노드는 각각 D1 실행 호출 1회다. 수정 전 미적중 요청에는 d1_all·d1_batch·d1_all 세 노드가 있었다.

수정 전 미적중 요청: D1 호출 노드 3개원본 보기 (새 창)

수정 전 미적중 요청: D1 호출 노드 3개

100%원본 보기 (새 창)

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

수정 후 같은 조건의 요청에는 d1_all·d1_batch 두 노드만 남았다.

수정 후 미적중 요청: D1 호출 노드 2개원본 보기 (새 창)

수정 후 미적중 요청: D1 호출 노드 2개

100%원본 보기 (새 창)

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