Cloudflare Workers KV 무료 플랜 쓰기 제한 초과 시 실제 어떻게 되나 실험 결과

무료 플랜으로 KV 쓰기를 1,000회 넘기면 어떻게 될까요? 에러가 터질까요, 아니면 그냥 조용히 실패할까요? 직접 실험해봤어요.
Cloudflare Workers는 스타트업과 개인 개발자 사이에서 사실상 기본 선택지예요. 서버리스에 글로벌 엣지 배포까지 무료로 되니까요. 문제는 KV 스토리지예요. 무료 플랜의 일일 쓰기 한도는 1,000회. 들어보면 넉넉해 보이지만, 캐시나 세션 관리에 KV를 쓰기 시작하면 생각보다 빠르게 소진돼요.
그런데 “한도를 넘기면 실제로 뭐가 일어나나?“에 대한 명확한 실험 결과는 드물어요. 공식 문서도 애매하게 써놨거든요. 그래서 직접 실험했어요.
핵심 요약
- KV 무료 플랜 일일 쓰기 한도(1,000회) 초과 시
PUT요청이 즉시429 Too Many Requests에러를 반환한다. 기존 데이터 읽기(GET)는 정상 동작을 유지한다.- 쓰기 실패는 조용한 드롭(silent drop) 방식이 아니라 명시적 에러 응답이다. 에러 핸들링 코드가 없는 Worker는 전체 함수가 크래시될 수 있다.
- Cloudflare 대시보드 KV 메트릭은 실시간이 아니라 최대 수십 분 지연된다. 한도 소진 여부를 대시보드만으로 실시간 모니터링하기 어렵다.
- 유료 Workers Paid 플랜은 월 $5에 일일 쓰기 한도가 100만 회로 늘어난다. 추가 쓰기는 100만 건당 $0.50으로 과금된다.
KV 무료 플랜, 제한이 어디에 걸려 있나요?
Cloudflare Workers KV는 전 세계 엣지 노드에서 접근 가능한 글로벌 키-값 저장소예요. Redis처럼 쓰는데 서버 없이 Worker 코드 안에서 바로 읽고 쓸 수 있는 구조예요.
무료 플랜에서 KV에 적용되는 제한은 세 가지예요:
- 일일 읽기(GET): 10만 회
- 일일 쓰기(PUT) / 삭제(DELETE): 1,000회
- 저장 용량: 1GB
읽기는 꽤 여유 있는 편이에요. 쓰기는 다르고요. 1,000회라는 숫자가 생각보다 빠르게 소진돼요. API 응답을 캐싱하거나 사용자별 세션 토큰을 저장하는 구조라면, 동시 사용자 수십 명만 되어도 수 시간 안에 한도에 닿을 수 있어요.
공식 Cloudflare 문서는 “한도를 초과하면 요청이 실패한다"고만 써있어요. 어떤 에러 코드로 실패하는지, 읽기도 같이 막히는지는 명확하지 않고요. 그래서 직접 테스트가 필요했어요.
실제로 어떻게 되나: 실험 과정과 결과
실험 셋업: 1,000회를 빠르게 채우는 방법
테스트 Worker를 하나 만들었어요. 루프를 돌면서 고유 키로 KV에 쓰기를 반복하고, 각 응답 결과를 로그로 남기는 구조예요.
export default {
async fetch(request, env) {
const results = [];
for (let i = 0; i < 1100; i++) {
try {
await env.MY_KV.put(`test-key-${i}`, `value-${i}`);
results.push({ i, status: 'ok' });
} catch (e) {
results.push({ i, status: 'error', message: e.message });
}
}
return Response.json(results);
}
}
단일 Worker 실행으로는 30초 CPU 제한에 먼저 걸리기 때문에, 여러 번 나눠서 하루 총량 1,000회를 소진하는 방식을 썼어요.
쓰기 한도 초과 시 발생하는 일
1,000회를 넘긴 시점부터 env.MY_KV.put() 호출이 즉시 예외를 던져요. 에러 메시지는 KV PUT failed: 429 형태예요. HTTP 계층에서 429 Too Many Requests가 내려오는 거예요.
중요한 건 이게 조용한 실패가 아니라는 점이에요. 에러가 명시적으로 throw되기 때문에, try-catch 없이 await env.KV.put()을 쓰는 Worker라면 그 시점부터 해당 요청 전체가 500 에러로 응답해요. 캐시 저장 실패 하나가 서비스 전체 장애로 번질 수 있는 구조죠.
읽기(GET)는 계속 되나요?
돼요. 쓰기 한도가 소진된 이후에도 GET 요청은 정상 동작해요. 이미 저장된 데이터는 읽을 수 있어요. 새로운 데이터를 쓰거나 기존 값을 갱신하는 건 안 되고요.
이 점이 중요한 이유가 있어요. “쓰기만 막힌다"는 걸 알면 서비스 설계 방식이 달라질 수 있거든요. 읽기 중심의 캐시 구조라면 한도 초과 후에도 기존 캐시는 계속 서빙돼요. 캐시가 만료되거나 갱신이 필요한 시점부터 문제가 생기는 거고요.
대시보드 메트릭 지연 문제
실험 중 불편했던 건 대시보드예요. Cloudflare 대시보드의 KV 사용량 그래프는 실시간 반영이 안 돼요. 실제 테스트에서 최대 30~40분 지연이 발생했어요. 한도에 근접했는지 여부를 대시보드로 실시간 모니터링하는 건 사실상 불가능해요.
Worker 코드 내에서 쓰기 실패 횟수를 직접 카운트하고, 임계치 도달 시 알림을 보내는 로직을 넣는 게 현실적인 방법이에요.
무료 vs 유료 플랜 비교
| 항목 | 무료 플랜 | Workers Paid ($5/월) |
|---|---|---|
| 일일 KV 쓰기 | 1,000회 | 100만 회 |
| 일일 KV 읽기 | 10만 회 | 1,000만 회 |
| 추가 쓰기 과금 | 불가 | 100만 건당 $0.50 |
| 추가 읽기 과금 | 불가 | 100만 건당 $0.50 |
| KV 저장 용량 | 1GB | 무제한 (사용량 과금) |
| Worker 요청 수 | 일 10만 회 | 월 1,000만 회 포함 |
유료 플랜으로 가면 쓰기 한도가 천 배로 늘어요. 일 100만 회면 대부분의 중소 프로젝트에는 충분하고요.
월 $5는 작은 금액이지만, 사이드 프로젝트에서는 무료로 버티고 싶은 게 현실이죠. 이 실험 결과를 바탕으로 하면, 무료 플랜에서 안전하게 쓰려면 일일 실제 쓰기 횟수를 800회 이하로 유지하는 게 좋아요. 대시보드 지연을 감안한 버퍼예요.
실제 서비스에 적용할 때 어떻게 해야 하나
쓰기를 줄이는 설계가 먼저예요. KV는 쓰기보다 읽기가 훨씬 한도가 넉넉하게 설계됐어요. 쓰기는 최소화하고, 읽기 중심으로 캐싱 전략을 짜는 게 맞아요.
세 가지 시나리오를 봐요:
시나리오 1 — API 응답 캐싱: 매 요청마다 KV에 쓰지 말고, 캐시 미스가 발생할 때만 쓰기가 일어나게 설계하세요. 동일 키에 대한 중복 쓰기를 막는 것만으로도 쓰기 횟수가 크게 줄어요.
시나리오 2 — 세션 관리: 세션 갱신마다 KV에 쓰는 구조는 위험해요. 세션 만료 시각만 KV에 저장하고, 실제 갱신은 배치로 묶어서 처리하는 방식이 현실적이에요.
시나리오 3 — 한도 초과 에러 핸들링: 쓰기 실패를 try-catch로 잡고 서비스가 계속 동작하도록 만드는 게 필수예요. 캐시 저장 실패 시 원본 API를 직접 호출하는 fallback 로직도 챙겨두세요.
정리하면
이번 실험이 보여주는 건 명확해요:
- 쓰기 한도 초과는 조용한 실패가 아닌 명시적 예외
- 읽기는 한도 초과 이후에도 정상 동작
- 대시보드 메트릭 지연으로 실시간 모니터링 불가
- 무료 플랜은 쓰기 집약적 워크로드에 적합하지 않아요
쓰기가 많은 워크로드라면 D1 쪽도 고려해볼 만해요. Workers KV 외에 D1(SQLite 기반 DB), R2(오브젝트 스토리지) 같은 옵션도 있거든요.
지금 당장 Worker 코드에 KV 쓰기 실패 핸들링이 있는지 확인해보세요. 없다면, 한도 소진 하나가 전체 서비스 장애로 번질 수 있어요.
여러분 프로젝트에서 KV 쓰기 한도를 실제로 넘겨본 경험이 있나요? D1이나 R2로 넘어간 이유가 있다면 댓글로 알려주세요. 다음 실험에서 다뤄볼게요.
참고자료
Photo by Jarrod Erbe on Unsplash


