Cloudflare Workers 무료 플랜으로 서버리스 API 배포하기
AWS Lambda 프리티어가 종료된 후, 개인 프로젝트에서 갑작스런 비용 청구서가 날아와 당황했던 경험이 있다. 특히 예상치 못한 과금이 발생하면 작은 프로젝트라도 유지가 어려워지는데, 이때 Cloudflare Workers 무료 플랜이 대안으로 떠올랐다. 하루 10만 건 요청 처리와 10ms CPU 제한이라는 구체적인 조건을 처음 접했을 때는 “이 짧은 CPU 시간 안에 내 API가 제대로 동작할까?” 하는 의구심이 들었다. 실제로 Wrangler CLI 설치 중 인증 팝업이 뜨지 않거나, 배포 후 환경변수 값이 undefined가 되는 등 시행착오를 겪으며 공식 문서와 커뮤니티 게시글을 수십 번 뒤적였다. 지금은 이 과정을 거치며 얻은 실전 노하우를 바탕으로, 한국 환경에서 Cloudflare Workers를 활용해 서버리스 API를 안정적으로 운영하는 방법과 주의할 점들을 상세히 전달하려 한다.
1. Cloudflare Workers 무료 플랜 조건과 동작 원리
Cloudflare Workers 무료 플랜은 하루 요청 10만 건과 Worker당 CPU 시간 10ms라는 엄격한 제한이 있다. 처음엔 이 CPU 제한이 너무 짧아 보였지만, 월 단위로 환산하면 약 300만 건 요청을 처리할 수 있고, 경량 API를 운영하는 개인이나 소규모 프로젝트에는 충분한 수준이다. AWS Lambda 무료 플랜은 1년 한시 월 100만 건 무료 제공인데 반해, Cloudflare Workers는 기간 제한 없이 계속 무료라는 점이 장기 프로젝트 비용 관리에 큰 힘이 되었다.
기술적 배경을 보면, Cloudflare는 2024년 기준 전 세계 275개 이상의 데이터센터에 Workers를 분산 배포해, 사용자의 위치에 따라 가장 가까운 엣지 서버에서 JavaScript 코드를 실행한다. 서울에서 직접 테스트해 본 결과, 평균 응답 속도는 약 30ms로, AWS Lambda의 콜드 스타트 지연 100~500ms와 비교하면 체감 성능 차이가 크다. 다만 CPU 제한 덕분에 복잡한 이미지 처리, 긴 배치 작업 등에는 적합하지 않으므로, 간단한 API 응답, 인증, 데이터 조회 같은 ‘가벼운’ 작업에 집중하는 게 필수다.
2. 개발 환경 세팅: Wrangler CLI 설치 및 초기 설정
사전 준비 및 계정 생성
Cloudflare Workers는 JavaScript 혹은 TypeScript 기반으로 개발하며, Node.js 18 이상 환경이 필요하다. 제가 겪은 문제 중 하나는 Node.js 버전 차이로 빌드가 실패하는 상황이었다. node -v 명령어로 버전을 확인하지 않고 설치를 진행했다가, “unsupported engine” 오류가 발생해 한참 헤맸다. 이처럼 Node.js 버전은 반드시 체크해야 한다. 계정 생성은 신용카드 없이 이메일 인증만으로 가능해 접근성이 좋다. 다만, Wrangler CLI는 인증 토큰을 사용하기 때문에, 토큰 만료 시 새로 로그인해야 하는 점을 염두에 두어야 한다.
Wrangler CLI 설치 및 로그인 과정
npm을 이용해 Wrangler CLI를 전역 설치한 뒤, wrangler login으로 인증을 시도했다. 그런데 VPN을 켠 상태에서 인증 팝업이 뜨지 않아 한참을 원인을 찾았다. 터미널을 완전히 종료하고 재실행하거나, 브라우저의 쿠키와 캐시를 삭제한 후 다시 로그인하니 정상 작동했다. 이처럼 인증 과정에서 팝업이 차단되거나 세션이 만료되는 문제는 꽤 흔한 편이니, 환경 설정을 꼼꼼히 점검하는 습관이 필요하다.
npm install -g wrangler
wrangler login
새 Workers 프로젝트 생성 및 초기 구성
프로젝트를 생성할 때 npm create cloudflare@latest my-api-worker 명령어를 사용했다. 생성 과정에서 “Hello World” 템플릿과 JavaScript 선택은 기본이며, Git 저장소 초기화를 권장한다. 실제 프로젝트가 커질수록 버전 관리가 필수인데, 초기 설정 시 Git을 활성화해 두면 추후 코드 변경 이력을 관리하거나 협업 때 유용하다. 생성된 폴더 내 src/index.js가 API 엔드포인트 코드, wrangler.toml이 배포 설정 파일이다. 로컬 서버 실행은 wrangler dev로 하며, http://localhost:8787에서 빠르게 API를 테스트할 수 있다.
npm create cloudflare@latest my-api-worker
# 프로젝트 생성 시 선택:
# - "Hello World" Worker 템플릿
# - JavaScript
# - Git 초기화: Yes (권장)
cd my-api-worker
wrangler dev
3. 실전 API 작성 및 배포: Supabase 연동 JSON API 완성
1단계: 환경변수 등록으로 보안 강화
Supabase API 키와 URL을 코드에 직접 작성하면 보안에 취약하다. Wrangler CLI가 제공하는 시크릿 저장 기능을 꼭 활용해야 한다. 저도 처음엔 환경변수를 등록했는데, 배포 후 값이 undefined로 표시되어 금방 문제가 발생했다. 원인은 로그인 세션이 만료된 상태에서 명령어를 실행했기 때문이었다. wrangler login으로 인증을 갱신한 뒤, 다시 wrangler secret put으로 등록하니 문제없이 처리됐다. 세션 만료는 눈에 띄지 않아 종종 간과되므로, 환경변수가 작동하지 않을 때 가장 먼저 점검할 부분이다.
wrangler secret put SUPABASE_URL
# 프롬프트에 Supabase URL 입력
wrangler secret put SUPABASE_KEY
# Supabase anon 키 입력
2단계: API 코드 작성 시 주의할 점
API 엔드포인트 /articles에서 최신 기사 목록을 JSON으로 반환하는 예제 코드를 작성했다. Cloudflare Workers는 CORS 정책을 직접 처리해야 하므로, 사전 OPTIONS 요청에 대한 응답도 반드시 포함해야 한다. 개발 초기에 OPTIONS 요청을 누락해 브라우저에서 요청이 차단되는 문제를 겪었다. 그리고 Supabase 쿼리에서 필터링을 최대한 적용해, Worker 내 불필요한 데이터 가공을 줄이는 게 CPU 시간 제한(10ms)을 넘지 않는 핵심이었다. 특히, sentiment 쿼리 파라미터를 사용할 때 URL 인코딩을 빼먹으면 필터링이 정상 작동하지 않아, 데이터가 엉뚱하게 나오는 버그가 있었다. 이 부분은 꼭 encodeURIComponent 함수로 감싸야 한다.
// src/index.js
const CORS_HEADERS = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
"Content-Type": "application/json;charset=UTF-8",
};
async function fetchArticles(env, sentiment = null) {
let url = `${env.SUPABASE_URL}/rest/v1/articles?select=id,title,summary,sentiment,created_at&order=created_at.desc&limit=20`;
if (sentiment) url += `&sentiment=eq.${encodeURIComponent(sentiment)}`;
const response = await fetch(url, {
headers: {
"apikey": env.SUPABASE_KEY,
"Authorization": `Bearer ${env.SUPABASE_KEY}`,
},
});
if (!response.ok) {
throw new Error(`Supabase 오류: ${response.status}`);
}
return response.json();
}
export default {
async fetch(request, env) {
const { pathname, searchParams } = new URL(request.url);
// CORS preflight 처리
if (request.method === "OPTIONS") {
return new Response(null, { headers: CORS_HEADERS });
}
try {
// GET /articles
if (pathname === "/articles" && request.method === "GET") {
const sentiment = searchParams.get("sentiment");
const data = await fetchArticles(env, sentiment);
return new Response(JSON.stringify({ success: true, count: data.length, data }), {
headers: CORS_HEADERS,
});
}
// GET /health
if (pathname === "/health") {
return new Response(JSON.stringify({ status: "ok", timestamp: new Date().toISOString() }), {
headers: CORS_HEADERS,
});
}
// 404 처리
return new Response(JSON.stringify({ error: "Not Found" }), {
status: 404,
headers: CORS_HEADERS,
});
} catch (err) {
return new Response(JSON.stringify({ error: err.message }), {
status: 500,
headers: CORS_HEADERS,
});
}
},
};
3단계: 배포 과정과 도메인 연결 팁
배포는 wrangler deploy 한 줄로 간단히 끝났다. 명령어 실행 후 10초 이내에 배포가 완료됐고, 기본 도메인 https://my-api-worker.계정명.workers.dev가 자동으로 발급되었다. 개인 프로젝트라 기본 도메인으로 충분했지만, 이미 Cloudflare DNS를 사용 중이라 커스텀 도메인을 연결해봤다. 이때 DNS 설정 변경 후 전파 시간이 최대 몇 시간 걸려 즉시 접속이 안 되는 점이 곤혹스러웠다. 도메인 연결 후에는 적어도 1~2시간 정도 기다렸다가 접속 여부를 확인하는 걸 권한다.
wrangler deploy
4. 자주 마주치는 오류와 디버깅 방법
에러 ① CPU Time Exceeded — 10ms 제한 초과 문제
Cloudflare Workers 무료 플랜의 10ms CPU 시간 제한은 생각보다 엄격하다. 제가 작성한 초기 코드에서는 Supabase에서 받은 데이터를 Worker 안에서 추가로 필터링하고, 중첩 반복문을 사용하다가 Worker exceeded CPU time limit 에러가 자주 발생했다. 이 문제는 결국 Worker가 너무 무거운 작업을 하려고 해서 생긴 것이다. 해결책은 쿼리 단계에서 최대한 데이터를 걸러내고, Worker는 단순히 API 요청을 전달하고 결과를 반환하는 역할만 하도록 분리하는 것이었다. CPU 제한을 넘지 않도록 최대한 가벼운 로직으로 구성하는 습관이 필요하다.
에러 ② 환경변수 undefined 현상
배포 후 환경변수가 undefined가 되는 문제는 가장 흔한 장애 중 하나다. 로컬에서 잘 작동하던 코드가 갑자기 환경변수가 없다고 하니 당황스러웠다. 원인을 찾아보니 Wrangler 로그인 세션이 만료된 상태에서 시크릿을 등록했기 때문이었다. wrangler login으로 인증을 갱신하고, 다시 wrangler secret put으로 시크릿을 재등록하니 문제가 해결됐다. 또, wrangler.toml 내 변수 이름 오타도 의외로 자주 발생하는 문제이므로, 이름이 정확히 일치하는지 꼼꼼히 확인하는 것이 좋다.
5. AWS Lambda와 Cloudflare Workers 세밀 비교
서버리스 환경을 선택할 때 AWS Lambda와 Cloudflare Workers는 각기 다른 특성을 지닌다. 아래 표는 제 프로젝트에 맞춰 직접 비교해본 결과다. 기능과 사용 사례를 고려해 적절한 조합을 찾는 게 비용과 성능 모두를 잡는 핵심이다.
| 항목 | AWS Lambda | Cloudflare Workers |
|---|---|---|
| 무료 한도 | 월 100만 건, 1년 한정 | 하루 10만 건, 무기한 |
| 콜드 스타트 지연 | 100~500ms (환경 따라 다름) | 0~5ms (엣지 컴퓨팅 특성) |
| 지원 런타임 | Node.js, Python, Go, Java 등 다양 | JavaScript / TypeScript 전용 |
| 실행 시간 제한 | 최대 15분 | CPU 10ms (무료), 최대 30초 (유료) |
| 적합한 사용 사례 | 무거운 배치 작업, 긴 실행 프로세스 | 경량 API 게이트웨이, 엣지 캐싱, 빠른 응답 |
저는 데이터 가공 로직이 많은 배치 작업은 AWS Lambda에 맡기고, 외부 노출 API는 응답 속도와 비용 효율을 고려해 Cloudflare Workers로 분리해 운영하고 있다. 이렇게 역할을 나누니 비용 부담은 확실히 줄면서 서비스 응답성은 현저히 좋아졌다. 앞으로는 Workers 유료 플랜을 검토해 CPU 시간 한도를 늘려, 더 복잡한 로직도 엣지에서 처리하는 방식을 시도할 계획이다.