Claude Code 캐싱 전략: HTTP, CDN, Service Worker, Redis 실전 가이드
실제 앱에서 Claude Code로 HTTP, CDN, Service Worker, Redis 캐시와 무효화 절차를 설계하는 방법.
캐시는 앱을 빠르게 만드는 마법 스위치가 아닙니다. 실제 서비스에서는 HTTP 헤더, CDN, Service Worker, Redis, 프로세스 메모리가 각각 다른 위치에서 다른 수명으로 데이터를 보관합니다. Claude Code에 단순히 “캐시를 추가해 줘”라고 요청하면 속도는 올라갈 수 있지만, 오래된 가격, 잘못된 재고, 사용자별 데이터 노출 같은 문제가 생길 수 있습니다.
이 글은 Claude Code가 코드를 수정하기 전에 넘겨줄 수 있는 캐시 설계 기준을 정리합니다. Masa가 작은 Express 앱과 콘텐츠 사이트 검증 환경에서 확인했을 때도, 가장 큰 효과는 Redis를 먼저 넣는 것이 아니라 “어느 계층에 무엇을 몇 초 동안 둘지”를 먼저 정한 데서 나왔습니다.
flowchart LR
User[Browser] --> Http[HTTP cache]
Http --> SW[Service worker Cache API]
SW --> CDN[CDN or edge cache]
CDN --> App[Node or app server]
App --> Redis[Redis or app cache]
App --> DB[(Database)]
먼저 정해야 할 것
Claude Code가 구현을 시작하기 전에 네 가지를 정합니다.
- 같은 응답을 어떤 사용자끼리 공유해도 되는가
- 데이터가 얼마나 오래되어도 업무상 문제가 없는가
- 업데이트 후 어떤 key, URL, cache tag를 지울 것인가
- 캐시 사고가 났을 때 누가 어떤 순서로 되돌릴 것인가
해시가 붙은 JavaScript, CSS, 이미지는 길게 캐시할 수 있습니다. 반대로 결제 정보, 계정 설정, 로그인 후 HTML은 공유 캐시에 올리면 안 됩니다. MDN의 HTTP caching은 브라우저 전용 private cache와 CDN 같은 shared cache의 차이를 설명합니다.
캐시 계층 비교
| 계층 | 적합한 데이터 | TTL 기준 | 무효화 방식 | 흔한 실패 |
|---|---|---|---|---|
| 브라우저 HTTP 캐시 | 이미지, CSS, JS, 짧은 공개 API | 1분에서 1년 | 파일명 변경, ETag, Cache-Control | API가 오래 캐시되어 화면이 낡음 |
| CDN 또는 edge | 상품 목록, 글 HTML, OGP 이미지 | 30초에서 1일 | URL, 태그, 배포 시 purge | 로그인 HTML이 모든 사용자에게 공유됨 |
| Service Worker Cache API | 오프라인 페이지, 앱 셸, 저빈도 JSON | 버전 기준 | 릴리스 때 캐시명 변경 | 오래된 worker가 예전 번들을 계속 제공 |
| Redis 또는 앱 캐시 | DB 집계, 외부 API, 랭킹 | 10초에서 1시간 | key 설계, 업데이트 이벤트, TTL | 운영 Redis에서 KEYS 사용 |
| 프로세스 메모리 | 설정값, 짧은 feature flag 복사본 | 몇 초에서 몇 분 | 재시작 또는 명시적 clear | 여러 인스턴스 값이 달라짐 |
이 표를 CLAUDE.md에 넣어 두면 Claude Code가 새 라우트를 만들 때도 같은 기준을 따릅니다. 프로젝트 규칙 작성은 CLAUDE.md best practices도 참고하세요.
사용 사례1: Express에서 HTTP 캐시 헤더 설정
가장 먼저 손볼 캐시는 Redis가 아니라 응답 헤더인 경우가 많습니다. Cache-Control은 브라우저와 CDN에 응답을 얼마나 저장해도 되는지 알려줍니다. 세부 지시자는 MDN의 Cache-Control header를 확인하면 됩니다.
아래 코드는 server.js로 저장해 바로 실행할 수 있습니다.
npm install express
node server.js
// server.js
const express = require("express");
const app = express();
function cacheControl(req, res, next) {
const path = req.path;
if (path.startsWith("/assets/")) {
res.set("Cache-Control", "public, max-age=31536000, immutable");
return next();
}
if (path.startsWith("/api/private/")) {
res.set("Cache-Control", "no-store");
return next();
}
if (path.startsWith("/api/public/")) {
res.set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=600");
res.set("Vary", "Accept-Encoding");
return next();
}
res.set("Cache-Control", "no-cache");
next();
}
app.use(cacheControl);
app.use("/assets", express.static("public/assets"));
app.get("/api/public/products", (_req, res) => {
res.json({ items: ["book", "template", "consultation"], generatedAt: new Date().toISOString() });
});
app.get("/api/private/me", (_req, res) => {
res.json({ userId: "demo-user", plan: "team" });
});
app.listen(3000, () => {
console.log("http://localhost:3000");
});
핵심은 공개 데이터와 개인 데이터를 URL 단계에서 분리하는 것입니다. /api/private/에는 no-store를 붙여 브라우저와 CDN이 저장하지 않게 합니다. 공개 API는 브라우저 TTL을 짧게 두고, s-maxage로 CDN에는 조금 더 오래 둘 수 있습니다.
Claude Code에는 “인증된 응답은 반드시 no-store, 공개 API만 s-maxage 허용”이라고 명시하세요.
사용 사례2: Redis getOrSet 헬퍼
Redis는 반복되는 DB 쿼리나 외부 API 호출을 줄이는 데 좋습니다. 안전한 기본값은 cache-aside입니다. 먼저 Redis를 보고, 없으면 원본에서 읽은 뒤 TTL과 함께 저장합니다.
npm install redis
// cache.js
const { createClient } = require("redis");
const redis = createClient({
url: process.env.REDIS_URL || "redis://localhost:6379",
});
let connecting;
async function client() {
if (redis.isOpen) return redis;
if (!connecting) connecting = redis.connect();
await connecting;
return redis;
}
async function getOrSet(key, ttlSeconds, loader) {
const r = await client();
const cached = await r.get(key);
if (cached !== null) {
return JSON.parse(cached);
}
const fresh = await loader();
await r.set(key, JSON.stringify(fresh), { EX: ttlSeconds });
return fresh;
}
async function invalidate(keys) {
const r = await client();
if (keys.length > 0) {
await r.del(keys);
}
}
module.exports = { getOrSet, invalidate };
// products.js
const { getOrSet, invalidate } = require("./cache");
async function loadProductsFromDb() {
return [
{ id: "p1", name: "Prompt Templates", price: 500 },
{ id: "p2", name: "Claude Code Consultation", price: 15000 },
];
}
async function getPublicProducts() {
return getOrSet("products:list:v1", 300, loadProductsFromDb);
}
async function updateProduct(productId, patch) {
console.log("update db", productId, patch);
await invalidate(["products:list:v1", `products:item:${productId}:v1`]);
}
module.exports = { getPublicProducts, updateProduct };
운영 환경에서 KEYS products:*는 피하세요. key가 많아지면 Redis를 막을 수 있습니다. 알려진 key 목록, 관련 key Set, 또는 SCAN 기반 관리 명령을 사용하는 편이 안전합니다.
사용 사례3: Service Worker Cache API 버전 관리
Service Worker는 브라우저 요청을 가로챌 수 있습니다. 함께 쓰는 Cache API는 오프라인 페이지, 앱 셸, 정적 파일 저장에 유용합니다. 단, 캐시 이름이 바뀌지 않으면 배포 후에도 오래된 JavaScript가 계속 제공될 수 있습니다.
// public/sw.js
const CACHE_VERSION = "claude-code-cache-v2026-06-01";
const STATIC_CACHE = `${CACHE_VERSION}:static`;
const PRECACHE_URLS = ["/", "/offline.html", "/assets/app.css"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches
.open(STATIC_CACHE)
.then((cache) => cache.addAll(PRECACHE_URLS))
.then(() => self.skipWaiting())
);
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches
.keys()
.then((names) =>
Promise.all(
names
.filter((name) => !name.startsWith(CACHE_VERSION))
.map((name) => caches.delete(name))
)
)
.then(() => self.clients.claim())
);
});
self.addEventListener("fetch", (event) => {
const request = event.request;
if (request.method !== "GET") return;
event.respondWith(
caches.match(request).then((cached) => {
if (cached) return cached;
return fetch(request)
.then((response) => {
if (response.ok && new URL(request.url).pathname.startsWith("/assets/")) {
const copy = response.clone();
caches.open(STATIC_CACHE).then((cache) => cache.put(request, copy));
}
return response;
})
.catch(() => caches.match("/offline.html"));
})
);
});
클라이언트 엔트리에서 등록합니다.
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js");
});
}
릴리스마다 CACHE_VERSION을 바꾸고, activate에서 오래된 캐시를 삭제하도록 Claude Code 작업 조건에 적어 두세요.
사용 사례4: Claude Code 캐시 감사 prompt
Claude Code는 단일 helper를 쓰는 것보다 저장소 전체의 캐시 동작을 점검할 때 더 유용합니다. 공식 Claude Code common workflows는 조사, 수정, 검증을 작은 루프로 나누는 방식을 권장하며, 캐시 작업에도 잘 맞습니다.
당신은 Web 애플리케이션의 캐시 감사 담당자입니다.
이 저장소에서 HTTP 헤더, CDN 전제, Service Worker, Redis, 프로세스 메모리 캐시를 사용하는 위치를 조사하세요.
출력:
1. 캐시 계층별 저장 데이터
2. TTL과 무효화 조건
3. 개인 정보나 인증 응답이 공유 캐시에 들어갈 위험
4. stale data가 보일 수 있는 구체적 화면
5. 최소 수정안과 검증 명령
제약:
- 추정은 추정이라고 표시
- 기존 프로젝트 패턴을 따름
- 큰 리팩터링은 제안만 하고 바로 적용하지 않음
이 prompt는 반복 사용 가능한 컨텍스트 캐시입니다. 매번 정책을 다시 설명하지 말고 템플릿이나 CLAUDE.md에 넣고, 현재 diff만 추가로 전달하세요.
실전 시나리오
상품 카탈로그나 템플릿 스토어에서는 공개 상품 카드를 CDN에 짧게 두고, DB 목록은 Redis에 5분 저장할 수 있습니다. 상품을 수정하면 목록 key와 해당 CDN URL을 함께 지웁니다.
관리 대시보드에서는 매출, PV, 전환율 집계를 30초에서 5분 캐시할 수 있습니다. 권한, 개인 알림, 결제 데이터는 private 또는 no-store가 기본입니다.
문서와 블로그에서는 글 HTML을 edge에 짧게, 해시가 붙은 정적 파일을 길게, 오프라인 셸을 Service Worker가 관리하는 구성이 좋습니다. ClaudeCodeLab처럼 콘텐츠가 많은 사이트에 잘 맞습니다.
외부 API에서는 날씨, 환율, SaaS 요금제 상태 같은 데이터를 Redis로 짧게 흡수합니다. 단, 일부 API는 약관상 캐시를 제한하므로 Claude Code에 공식 문서 확인을 포함시키세요.
흔한 함정
가장 위험한 실수는 로그인 후 HTML을 CDN에 캐시하는 것입니다. Cookie에 따라 응답이 바뀌면 기본은 private 또는 no-store입니다. CDN 속도가 필요하면 공유 가능한 셸과 사용자별 API를 분리합니다.
Vary 누락도 자주 발생합니다. 언어, 압축, 디바이스, 인증 상태에 따라 응답이 달라지면 URL을 분리하거나 올바른 Vary를 보내야 합니다.
Redis에서는 캐시 스탬피드가 문제입니다. 인기 key가 동시에 만료되면 많은 요청이 한꺼번에 DB로 갑니다. 랜덤 TTL, 짧은 lock, stale-while-revalidate 방식의 fallback을 고려하세요.
Service Worker는 삭제된 파일을 계속 살릴 수 있습니다. 캐시 이름을 버전 관리하고 activate에서 과거 캐시를 지우며, 긴급 unregister 절차도 준비해야 합니다.
무효화 runbook
- 영향 범위를 상품, 글, 사용자, 전체 앱 중 하나로 정합니다.
- DB 쓰기를 먼저 완료하고, 실패하면 캐시를 지우지 않습니다.
- Redis는 알려진 key, 관련 key Set, 또는
SCAN으로 삭제합니다. - CDN은 URL 또는 tag 단위로 purge합니다. 전체 purge는 마지막 수단입니다.
- Service Worker 캐시 버전을 올리고 예전 캐시 삭제를 확인합니다.
curl -I, 브라우저 DevTools, Redis hit rate 로그로 검증합니다.- 사고 중에는 TTL을 줄이거나 민감한 라우트를
no-store로 바꾼 뒤 점진적으로 되돌립니다.
이 runbook을 Claude Code의 완료 조건으로 넣으세요. 팀 작업 습관은 Claude Code productivity tips와 함께 정리하면 좋습니다.
ClaudeCodeLab 교육, 템플릿, 상담
이 전략을 팀에서 반복 가능한 workflow로 만들고 싶다면 ClaudeCodeLab products and templates에서 CLAUDE.md, review prompt, audit prompt의 틀을 먼저 맞추세요. 실제 저장소의 CDN, Redis, 권한, review 규칙까지 함께 설계해야 한다면 Claude Code training and consultation을 이용할 수 있습니다.
공식 자료는 MDN의 HTTP caching, Cache API, Cache-Control, Anthropic의 Claude Code common workflows를 확인하세요.
이 글의 내용을 실제로 시험한 결과, Masa의 검증 앱에서는 정적 파일 재요청이 줄고, 공개 API는 CDN용 TTL로 더 안정적이었으며, Redis getOrSet 덕분에 불필요한 DB 읽기를 로그에서 확인하기 쉬워졌습니다. 반면 Service Worker는 버전 삭제를 넣지 않으면 오래된 CSS가 남았습니다. 캐시의 핵심 가치는 단순한 속도가 아니라, 어떤 값이 어디에서 오래되어도 되는지 명확히 쓰는 데 있습니다.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Claude Code Permission Receipt Pattern: 권한, 증거, 롤백을 남기는 운영
Claude Code 작업마다 허용 범위, 승인 경계, 검증 명령, 롤백 메모, Gumroad와 상담 CTA 확인을 남기는 permission receipt 패턴입니다.
Claude Code/Codex 안전 Agent Harness 설계: 권한, 검증, 롤백
Claude Code와 Codex를 안전하게 운영하기 위한 Agent Harness를 권한 정책, 실행 계획, 검증, 복구 계층으로 설계합니다.
Claude Code 서브에이전트 실전 가이드: 기사와 코드 작업을 안전하게 위임하기
Claude Code 서브에이전트로 기사와 코드 작업을 안전하게 나누는 방법. 위임 규칙, 프롬프트, 실패 사례를 정리합니다.