Cloudflare Workers vs AWS Lambda 비용 비교: Flutter 백엔드, 월 100만~1억 요청 구간 실측

Flutter 앱의 백엔드로 서버리스를 고르면서 “Cloudflare Workers vs AWS Lambda 비용 비교”를 검색해본 적이 있다면, 대부분의 글이 “Workers가 더 저렴하다”거나 “Lambda 생태계가 더 크다” 같은 뭉뚱그린 결론으로 끝나는 걸 봤을 것이다. 실제로는 워크로드 성격과 트래픽 규모에 따라 승자가 뒤바뀐다. 이 글은 Flutter 앱에서 흔히 만드는 세 가지 유스케이스 — 푸시 발송 서버(FCM/APNs 호출), 웹훅 핸들러(RevenueCat·Stripe·App Store Server Notifications 검증), 범용 API 서버 — 를 기준으로, 두 플랫폼의 과금 구조 차이와 API Gateway 추가 비용까지 반영해 월 100만/1,000만/1억 요청 구간의 실제 청구액을 직접 계산했다.
핵심 요약
- Cloudflare Workers는 CPU 사용 시간만 과금하고 외부 API 응답을 기다리는 대기시간은 요금에 잡히지 않는다. AWS Lambda는 대기시간까지 포함한 전체 실행시간(wall-clock) 을 메모리 용량에 비례해 과금한다.
- Lambda 요청+실행시간은 월 100만 건 + 40만 GB-초가 영구 무료(Always Free)다. Workers 무료 플랜은 일 10만 건(월 약 300만 건), 인보케이션당 CPU 10ms까지만 무료이고, 이 CPU 한도를 넘으면 최소 $5/월인 Paid 플랜으로 넘어가야 한다.
- Lambda를 HTTP로 노출할 때 API Gateway를 붙이면 HTTP API 기준 100만 건당 $1.00, REST API는 $3.50가 별도로 붙는다. 단일 엔드포인트라면 Lambda Function URL로 이 비용을 완전히 없앨 수 있다. Workers는 라우팅이 런타임에 내장돼 있어 애초에 이 항목 자체가 없다.
- 이 글의 가정(요청당 wall-clock 300ms, 실제 CPU 20ms, 메모리 256MB)에서는 API Gateway를 쓸 경우 월 약 550만 건, Function URL을 쓸 경우 월 약 1,100만 건을 기점으로 Workers가 더 저렴해진다. 그 아래 구간에서는 Lambda가 유리하다.
- 반대로 이미지 리사이즈 같은 CPU 연산형 함수는 저메모리(256MB) 설정 기준으로 전 구간에서 Lambda가 유리했다. 다만 CPU 파워 확보를 위해 메모리를 1.1GB~1.4GB 이상으로 올려야 하는 무거운 작업이라면 이 우위는 옅어진다.
과금 방식의 근본적 차이: CPU 시간 vs 전체 실행시간
두 플랫폼 비교에서 가장 먼저 이해해야 할 건 “무엇을 시간으로 잡아서 돈을 매기는가” 가 다르다는 점이다.
Cloudflare Workers는 Cloudflare 공식 요금 문서 기준으로 요청 수와 CPU 시간만 과금 대상이다. Worker가 fetch로 외부 API를 호출하고 응답을 기다리는 동안에는 V8 아이솔레이트가 실제로 명령어를 실행하는 게 아니라 대기 상태이므로, 이 구간은 청구서에 전혀 잡히지 않는다. 외부 API를 5번 연쇄 호출해서 전체 응답까지 2초가 걸리더라도, 그 안에서 실제 JS 코드가 실행된 시간(파싱, 직렬화, 조건 분기 등)이 20ms라면 과금되는 건 딱 20ms다. 인보케이션당 기본 CPU 상한은 30초, Paid 플랜에서 설정으로 최대 5분(300,000ms)까지 늘릴 수 있고, Cron Trigger·Queue 컨슈머는 최대 15분까지 허용된다.
AWS Lambda는 반대로 “핸들러 코드가 시작해서 응답을 반환할 때까지의 전체 시간” 을 wall-clock 기준으로 측정하고, 이걸 함수에 할당한 메모리 용량(GB) 과 곱해 GB-초 단위로 과금한다. 외부 API 응답을 기다리는 동안에도 그 실행 환경은 계속 “실행 중”으로 잡히기 때문에, 이 대기시간도 그대로 요금이 된다. 즉 같은 웹훅 핸들러라도 외부 API가 250ms 만에 응답하든 800ms 만에 응답하든, Lambda 입장에서는 그 차이가 고스란히 GB-초 비용 차이로 이어진다. Lambda의 실행시간은 1ms 단위로 반올림되고, 함수 타임아웃은 최대 900초(15분)이며, 메모리는 128MB~10,240MB 사이에서 1MB 단위로 설정할 수 있다. 참고로 1,769MB 지점이 vCPU 1개에 해당하는 컴퓨팅 파워로 알려져 있어, CPU를 많이 쓰는 함수는 흔히 이 근처나 그 이상으로 메모리를 올려서 배포한다.
이 구조 차이가 만드는 실무적 결론은 단순하다. 외부 API·DB 응답을 기다리는 시간이 긴 워크로드(I/O 대기형)일수록 Workers가 유리해지고, 순수 연산 시간이 긴 워크로드(CPU 연산형)일수록 두 플랫폼의 격차가 좁혀지거나 역전된다. Flutter 백엔드에서 흔히 만드는 푸시 발송 서버(FCM HTTP v1 API 호출)나 웹훅 핸들러(서명 검증 후 외부 API 호출)는 전형적인 I/O 대기형이다.
같은 로직, 다른 코드 골격
실제로 두 플랫폼에 배포하는 코드는 거의 똑같이 생겼다. 아래는 RevenueCat/Stripe류 웹훅을 받아 서명을 검증하고 FCM으로 푸시를 쏘는 핸들러를 Hono로 짠 Worker 버전이다.
// Cloudflare Worker (Hono)
import { Hono } from "hono";
type Bindings = { WEBHOOK_SECRET: string; FCM_PROJECT_ID: string };
const app = new Hono<{ Bindings: Bindings }>();
app.post("/webhooks/push", async (c) => {
const rawBody = await c.req.text();
const signature = c.req.header("X-Signature") ?? "";
if (!(await verifyHmacSignature(rawBody, signature, c.env.WEBHOOK_SECRET))) {
return c.text("invalid signature", 401);
}
const event = JSON.parse(rawBody);
const accessToken = await getFcmAccessToken(c.env); // 서비스 계정 JWT 교환
await fetch(
`https://fcm.googleapis.com/v1/projects/${c.env.FCM_PROJECT_ID}/messages:send`,
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: buildFcmMessage(event) }),
},
);
return c.json({ ok: true });
});
export default app;
Lambda + Function URL로 옮기면 트리거 방식만 바뀌고 로직은 그대로다.
// AWS Lambda (Node.js, Function URL 트리거)
import type { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda";
export const handler = async (
event: APIGatewayProxyEventV2,
): Promise<APIGatewayProxyResultV2> => {
const rawBody = event.body ?? "";
const signature = event.headers["x-signature"] ?? "";
if (!(await verifyHmacSignature(rawBody, signature, process.env.WEBHOOK_SECRET!))) {
return { statusCode: 401, body: "invalid signature" };
}
const payload = JSON.parse(rawBody);
const accessToken = await getFcmAccessToken();
await fetch(
`https://fcm.googleapis.com/v1/projects/${process.env.FCM_PROJECT_ID}/messages:send`,
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: buildFcmMessage(payload) }),
},
);
return { statusCode: 200, body: JSON.stringify({ ok: true }) };
};
코드 수준의 개발 경험은 사실상 동일하다. 이 글에서 다루는 차이는 전적으로 “이 요청 하나를 처리하는 데 걸린 시간을 누가 어떻게 재서 청구하느냐” 의 문제다.
무료 티어 비교: 후한 듯 보이지만 함정이 있는 두 회사의 공짜 티어
| 항목 | Cloudflare Workers (Free) | AWS Lambda (Always Free) |
|---|---|---|
| 요청 한도 | 10만 건/일 (월 약 300만 건) | 100만 건/월 |
| 컴퓨팅 한도 | 인보케이션당 CPU 10ms | 40만 GB-초/월 |
| 대기시간 과금 | 없음(애초에 대상이 아님) | 있음(wall-clock에 포함) |
| 유효기간 | 영구 무료 | 영구 무료(신규 계정 12개월 한정 크레딧과는 별개) |
| 한도 초과 시 | Paid 플랜($5/월~)으로 전환 필요 | 초과분부터 종량 과금 |
숫자만 보면 Lambda의 “월 100만 건 + 40만 GB-초 영구 무료”가 훨씬 관대해 보이지만, Workers Free 플랜의 진짜 함정은 요청 수가 아니라 “인보케이션당 CPU 10ms”라는 상한이다. 단순 JSON 파싱·응답 정도면 10ms 안에 끝나지만, 여기에 HMAC 서명 검증, Zod 같은 스키마 검증, JWT 디코딩까지 들어가면 실무 코드는 생각보다 쉽게 10ms를 넘긴다. 이 한도를 넘으면 요청 자체가 CPU 초과 에러로 끊기므로, 실제로 뭔가 로직이 있는 API·웹훅 핸들러라면 사실상 Paid 플랜 $5/월이 강제된다고 보는 게 맞다. 반대로 Lambda는 메모리와 시간을 넉넉히 잡아도 free tier 안에서 소화되는 트래픽이라면 정말 $0으로 운영할 수 있다.
API Gateway 함정: Lambda 라우팅에는 숨겨진 청구서가 따라온다
Lambda 함수 자체는 HTTP를 직접 이해하지 못한다. 브라우저나 Flutter 앱에서 HTTPS로 호출하려면 앞단에 뭔가를 붙여야 하는데, 이 선택이 총비용에 생각보다 큰 영향을 준다.
- API Gateway (HTTP API): 100만 건당 $1.00(첫 3억 건 구간), 그 이후 100만 건당 $0.90. 커스텀 도메인, 여러 Lambda를 경로별로 매핑하는 라우팅, JWT/Lambda 오소라이저, 사용량 플랜(API 키·쿼터) 등을 지원한다.
- API Gateway (REST API): 100만 건당 $3.50(첫 3.33억 건 구간). WAF 통합, 요청/응답 변환, 캐싱 등 더 무거운 기능이 있지만 그만큼 비싸다.
- 12개월 한정 무료 티어: 신규 AWS 계정은 HTTP API·REST API 각각 월 100만 건까지 12개월간 무료다. 이 글의 계산은 그 기간이 끝난 정상 과금 상태를 기준으로 한다.
- Lambda Function URL: 함수 하나에 전용 HTTPS 엔드포인트 하나를 무료로 붙이는 기능이다. 별도 과금이 전혀 없고 순수 Lambda 요청·실행시간 요금만 적용된다. 대신 함수 1개당 URL 1개(경로 기반으로 여러 Lambda를 하나의 API로 묶는 건 불가능), API 키·사용량 플랜·WAF 통합·요청 검증 같은 관리 기능은 없다.
Flutter 앱의 웹훅 핸들러나 푸시 발송 서버는 보통 엔드포인트 하나(또는 소수)로 충분한 단일 목적 함수다. 이런 경우 API Gateway의 라우팅·사용량 관리 기능이 애초에 필요 없으므로 Function URL로 충분하다. 반대로 여러 개의 Lambda 함수를 하나의 API 아래 경로별로 묶어 외부 파트너에게 API 키를 발급하고 쿼터를 관리해야 하는 범용 API 서버라면 API Gateway가 필요하다.
Cloudflare Workers는 이 구분 자체가 없다. wrangler.toml의 라우트 설정이나 Worker 코드 안의 라우터(Hono, itty-router 등)로 얼마든지 여러 경로를 처리해도 요청당 과금은 동일하다.
# wrangler.toml — Workers는 라우팅에 별도 과금 항목이 없다
name = "push-webhook-worker"
main = "src/index.ts"
compatibility_date = "2025-01-01"
routes = [
{ pattern = "api.example.com/webhooks/*", zone_name = "example.com" },
{ pattern = "api.example.com/push/*", zone_name = "example.com" }
]
트래픽 구간별 청구서 시뮬레이션: 월 100만/1,000만/1억 요청
아래 계산은 다음 가정을 기준으로 한다. 실제 값은 서비스마다 다르므로 뒤에 나오는 공식에 직접 대입해서 확인하는 걸 권한다.
- Lambda 메모리 256MB(웹훅·경량 API에서 흔한 설정), 아키텍처는 x86_64 기준
- 시나리오 A (I/O 대기형) — 웹훅 핸들러·푸시 발송 서버: 요청당 wall-clock 300ms, 그중 실제 CPU 사용 20ms(나머지 280ms는 외부 API 응답 대기)
- 시나리오 B (CPU 연산형) — 이미지 리사이즈·PDF 생성 등: 요청당 wall-clock 200ms, CPU 사용 180ms(대기 거의 없음)
- Lambda 쪽은 API Gateway 12개월 무료 티어가 끝난 정상 과금 상태를 기준으로 하고, HTTP API(REST API보다 저렴한 쪽)로 계산
시나리오 A: I/O 대기형 (웹훅 핸들러, 푸시 발송 서버)
| 월 요청 수 | Workers (Paid $5~) | Lambda 컴퓨팅만 | Lambda + Function URL | Lambda + API Gateway(HTTP API) |
|---|---|---|---|---|
| 100만 | $5.00 | $0.00 | $0.00 | $1.00 |
| 1,000만 | $8.40 | $7.63 | $7.63 | $17.63 |
| 1억 | $71.40 | $138.13 | $138.13 | $238.13 |
시나리오 B: CPU 연산형 (이미지 리사이즈, 서명·암호화 등)
| 월 요청 수 | Workers (Paid $5~) | Lambda 컴퓨팅만 | Lambda + Function URL | Lambda + API Gateway(HTTP API) |
|---|---|---|---|---|
| 100만 | $8.00 | $0.00 | $0.00 | $1.00 |
| 1,000만 | $40.40 | $3.47 | $3.47 | $13.47 |
| 1억 | $391.40 | $96.47 | $96.47 | $196.47 |
두 표를 나란히 보면 흥미로운 지점이 보인다. 시나리오 A(I/O 대기형)에서는 저트래픽 구간에서 Lambda가 압도적으로 싸다가, 트래픽이 늘어날수록 Workers가 역전한다. 반대로 시나리오 B(CPU 연산형)에서는 이 256MB 가정 기준으로 전 구간에서 Lambda가 더 싸다. 다음 섹션에서 그 이유와 정확한 손익분기 지점을 짚는다.
워크로드 성격에 따라 승자가 바뀌는 이유
계산에 쓴 공식을 그대로 공개한다. 자신의 워크로드 숫자를 대입해 직접 재계산해볼 수 있다.
Workers 월 비용(USD) = 5
+ max(0, 요청수_백만 - 10) × 0.30
+ max(0, 요청당_CPU_ms × 요청수_백만 - 30) × 0.02
Lambda 컴퓨팅 비용(USD) = max(0, 요청수_백만 - 1) × 0.20
+ max(0, GB초_총합 - 400,000) × 0.0000166667
GB초_총합 = 요청수 × 메모리(GB) × 평균_wall_clock_초
이 공식에 시나리오 A의 숫자를 대입해 두 곡선이 만나는 지점을 풀면, 컴퓨팅 비용만 비교했을 때 약 1,100만 건/월에서 Workers가 Lambda를 앞지르기 시작한다. 여기에 API Gateway(HTTP API, 100만 건당 $1.00)를 더하면 Lambda 쪽 비용 곡선의 기울기가 가팔라지면서 손익분기점이 약 550만 건/월로 거의 절반 앞당겨진다. 즉 “API Gateway를 쓰느냐 Function URL을 쓰느냐”라는 선택 하나가, CPU 시간 과금 대 wall-clock 과금이라는 근본적인 차이 못지않게 총비용에 영향을 준다.
시나리오 B(CPU 연산형)에서 Lambda가 계속 이기는 이유는 단순하다. 256MB(0.25GB)라는 낮은 메모리 설정에서는 GB-초 단가가 매우 작아서(요청당 0.2초 × 0.25GB × $0.0000166667 ≈ $0.00000083), Workers의 CPU-ms 단가(요청당 180ms × $0.02/100만ms ≈ $0.0000036)보다 오히려 저렴해지기 때문이다. 하지만 이 결과는 메모리 256MB라는 가정에 강하게 의존한다. 같은 공식으로 역산하면, 메모리를 대략 1.1GB~1.4GB 이상으로 올려야 하는 워크로드(실제로 sharp 같은 라이브러리로 이미지 리사이즈를 할 때 충분한 CPU 파워를 위해 512MB~1,769MB를 쓰는 경우가 흔하다)에서는 이 우위가 옅어지거나 사라지고, 트래픽이 커질수록 다시 Workers 쪽으로 무게추가 넘어간다. “CPU 연산형이니 무조건 Lambda”라고 단정하기 전에 실제 배포할 메모리 설정을 대입해봐야 하는 이유다.
놓치기 쉬운 부가 비용: 서브리퀘스트 한도와 데이터 전송료
푸시 발송 서버는 요청 하나당 FCM·APNs 같은 외부 API를 여러 번 호출하는 구조가 많다. Cloudflare는 이걸 “서브리퀘스트”로 별도 과금하지 않지만(요청 수에 잡히는 건 인바운드 요청 하나뿐), 동시에 나갈 수 있는 서브리퀘스트 수에는 한도가 있다. 공식 문서 기준 Free 플랜은 50건/요청, Paid 플랜은 기본 1,000건/요청(2026년 2월 변경 이후 기본값이 상향되어 최대 10,000건까지, 설정으로 최대 1,000만 건까지 확장 가능) 이다. 팬아웃으로 수백 개 디바이스 토큰에 개별 요청을 날리는 구조라면 이 한도를 미리 확인해야 한다.
또 하나 실무에서 자주 빠뜨리는 항목이 데이터 전송(egress) 비용이다. AWS는 리전 바깥으로 나가는 응답 트래픽에 GB당 $0.09가 별도로 붙는다(API Gateway·Lambda 공통으로 적용되는 일반 AWS 데이터 전송 요금). Cloudflare Workers 요금 체계에는 이런 별도의 응답 egress 과금 항목 자체가 없다. 응답 페이로드가 크지 않은 웹훅·푸시 유스케이스에서는 미미한 차이지만, 큰 JSON을 반환하는 API 서버라면 트래픽이 커질수록 이 항목도 누적된다.
결론: Flutter 푸시 발송 서버·웹훅 핸들러엔 무엇을 써야 하나
- 월 100만 건 이하의 초기~중소 규모: Lambda + Function URL이 사실상 $0에 가깝다(요청·실행시간 모두 영구 무료 티어 안에 들어온다). Workers는 Paid 플랜 최소 $5가 고정비로 발생하므로 이 구간은 Lambda가 유리하다. 다만 실제 CPU 사용량이 인보케이션당 10ms를 넘지 않는 정말 가벼운 로직이라면 Workers Free 플랜(월 약 300만 건까지 무료)으로도 $0를 유지할 수 있다.
- 월 500만~1,100만 건 구간(이 글의 가정 기준): 라우팅 방식에 따라 승자가 갈린다. API Gateway를 붙이면 이미 이 구간부터 Workers가 앞서고, Function URL만으로 충분한 단일 엔드포인트 구조라면 1,100만 건까지는 Lambda가 근소하게 유리하다.
- 월 1,000만 건을 넘는 스케일(대규모 정기 푸시 캠페인, 대량 웹훅 팬아웃): I/O 대기형 워크로드라면 거의 항상 Workers가 유리하다. CPU 시간만 과금하는 구조가 대기시간이 긴 워크로드에서 본질적으로 유리하기 때문이다.
- 이미지 리사이즈처럼 진짜 CPU를 많이 쓰는 함수: 저메모리(256MB~512MB) 설정이라면 어느 스케일에서든 Lambda가 유리한 경우가 많다. 단, 충분한 CPU 파워 확보를 위해 메모리를 1.1GB 이상으로 올려야 한다면 이 우위는 옅어진다.
- 라우팅 레이어를 반드시 먼저 점검하라. Lambda를 API Gateway 없이 Function URL로 노출할 수 있는지부터 확인하는 것만으로 손익분기점이 거의 두 배(약 550만 건 → 약 1,100만 건) 뒤로 밀린다. 이건 CPU 과금이니 wall-clock 과금이니 하는 구조적 차이보다 즉시 적용 가능한, 가장 손쉬운 비용 절감 지점이다.
결국 “Cloudflare Workers vs AWS Lambda 비용 비교”에 정답은 없다. 트래픽 규모, 요청당 대기시간과 CPU 시간의 비율, 그리고 라우팅 레이어 선택 — 이 세 변수를 이 글의 공식에 직접 대입해보는 것이 유일하게 정확한 답이다.