지난 주말, 랜딩 페이지에서 들어오는 폼 제출을 받아줄 공개 엔드포인트가 필요했습니다. 화려할 것 없이 — JSON POST를 받고, 필드 몇 개를 검증하고, 적당한 응답을 돌려주면 되는 정도. 전체 트래픽이라고 해봐야 일주일에 50건쯤. 월 20달러짜리 서버에 신용카드를 등록하고 싶지도 않았고, 새로운 인프라 도구를 배우거나 세 줄 고칠 때마다 CI 파이프라인을 기다리고 싶지도 않았죠. 시작하고 10분 만에 API가 300개 넘는 도시에 떠 있었고, 비용은 0달러였습니다. 이 글이 그 과정을 그대로 따라간 기록입니다.

Cloudflare Workers의 무료 티어는 진짜로 후합니다: 하루 100,000 요청, 가입에 신용카드 불필요, 그리고 코드가 한 리전이 아닌 엣지에서 실행됩니다. 웹훅 리시버, 폼 핸들러, 링크 단축기, JSON 프록시처럼 사이드 프로젝트가 보통 필요로 하는 작은 API들에는 배포해두고 잊어버려도 될 정도예요. 이 가이드는 "Cloudflare 계정 없음" 상태에서 "curl에 응답하는 라이브 URL"까지를 약 10분 안에 끝냅니다. 진짜 엔드포인트 하나를 만들 거예요: POST를 받아 페이로드를 검증하고 깔끔한 응답을 돌려주는 JSON 검증기입니다.

무료로 받는 것(과 못 받는 것)

Cloudflare Workers의 무료 플랜은 하루 100,000 요청과 요청당 10밀리초의 CPU 시간을 줍니다. 빠듯하게 들리지만, 대부분의 JSON 엔드포인트는 — 본문 파싱하고, 약간의 일을 하고, 응답 반환 — 1ms도 안 걸려서 끝납니다. 10ms 한도는 CPU 시간이지 벽시계 시간이 아니므로, 800ms 걸리는 업스트림 fetch도 코드 입장에서는 대부분 I/O를 기다리는 시간이라서 CPU로는 약 1ms 정도로 잡힙니다.

무료 플랜에서 못 받는 것: 고처리량 영구 저장소 (Workers KV는 자체적인 무료 쿼터가 있는데, 대부분의 경우엔 후하지만 요청 수와는 별개입니다), 큰 응답 페이로드, 분당 1회보다 자주 도는 cron 트리거. 데이터베이스가 필요 없는 전형적인 "JSON 받고, 뭔가 하고, JSON 반환" 서비스라면 벽에 부딪힐 일이 없습니다.

가입 시 신용카드 불필요. 계정을 만들 때 Cloudflare는 이메일과 비밀번호만 묻습니다 — 그게 끝입니다. 대시보드가 나중에 유료 제품을 위한 결제 수단을 추가하라고 안내하긴 하지만, 무료 Workers 티어는 아무것도 필요 없고 뜻밖의 청구서가 날아오지도 않습니다.

Node.js와 Wrangler 설치

Wrangler는 Workers용 Cloudflare CLI입니다. npm으로 설치하면 되고 — Node.js 18 이상이 필요합니다. 이미 Node가 깔려 있다면 한 줄이면 끝납니다:

bash
npm install -g wrangler

# Verify
wrangler --version
# Should print 3.x or higher

이제 로그인하세요. OAuth용으로 브라우저가 열리고, Cloudflare 계정이 없다면 만들어주고, 자격 증명을 로컬에 저장합니다:

bash
wrangler login

# Opens https://dash.cloudflare.com/oauth/authorize ...
# After you approve: "Successfully logged in."

프로젝트 뼈대 만들기

명령어 하나면 합리적인 기본값으로 프로젝트 뼈대가 잡힙니다 — 샘플 fetch 핸들러, wrangler.toml 설정, 그리고 로컬 개발 서버까지 미리 연결되어 있죠:

bash
npm create cloudflare@latest free-json-api

# When prompted, pick:
#   "Hello World" Worker
#   JavaScript (or TypeScript — your call)
#   No deploy yet (we'll do that ourselves)

cd free-json-api

제너레이터가 필요한 모든 것을 한 폴더에 떨궈줍니다. src/index.js를 여세요 — Worker가 실행하는 파일이 바로 이겁니다. 기본 Hello World 대신 더 쓸모 있는 것으로 내용을 갈아엎을 거예요.

JSON API 작성하기

JSON POST를 받아서 필수 필드 두 개를 검증하고 깔끔한 응답을 돌려주는 완성된 Worker입니다. 흔한 실패 모드들을 — 잘못된 메서드, 깨진 JSON, 누락된 필드 — 일반적인 500으로 터지는 대신 제대로 처리합니다:

js
export default {
  async fetch(request) {
    if (request.method !== 'POST') {
      return Response.json(
        { error: 'POST only' },
        { status: 405 },
      );
    }

    let body;
    try {
      body = await request.json();
    } catch {
      return Response.json(
        { error: 'Body must be valid JSON' },
        { status: 400 },
      );
    }

    const { email, message } = body;
    if (typeof email !== 'string' || typeof message !== 'string') {
      return Response.json(
        { error: 'email and message are required (strings)' },
        { status: 422 },
      );
    }

    return Response.json({
      ok: true,
      receivedAt: new Date().toISOString(),
      echo: { email, message },
    });
  },
};

짚고 갈 게 두 가지 있습니다. Response.json()은 객체를 직렬화하고 Content-Type: application/json을 자동으로 설정해주므로 헤더를 따로 챙길 필요가 없습니다. 그리고 request.json()을 try/catch로 감싸는 건 Worker를 만들 때 가장 중요한 단 하나의 습관입니다 — 이게 없으면 깨진 페이로드는 여러분 코드에서 나오는 유용한 400 대신 Cloudflare 런타임의 알 수 없는 500을 돌려줍니다.

로컬에서 테스트하기

Wrangler에는 Cloudflare가 프로덕션에서 쓰는 것과 같은 V8 isolate를 돌리는 로컬 개발 서버가 들어 있습니다. 이렇게 시작하세요:

bash
npm run dev

# ⛅️ wrangler 3.x
# [wrangler:inf] Ready on http://localhost:8787

다른 터미널에서 curl로 때려보세요. 해피 패스 먼저, 그다음 일부러 망가뜨린 페이로드로 — 스택 트레이스 대신 깔끔한 JSON 에러가 보여야 합니다:

bash
# Happy path
curl -X POST http://localhost:8787 \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","message":"hello"}'

# {"ok":true,"receivedAt":"2025-10-15T...","echo":{...}}

# Broken JSON (note the missing closing brace)
curl -X POST http://localhost:8787 \
  -H "Content-Type: application/json" \
  -d '{"email": "ava"'

# {"error":"Body must be valid JSON"}

# Missing required field
curl -X POST http://localhost:8787 \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]"}'

# {"error":"email and message are required (strings)"}
팁: 응답이 터미널에서 이상해 보이면 JSON Formatter에 붙여넣어서 보기 좋게 출력하고 살펴보세요. 낯선 API를 다룰 땐 저도 하루에 스무 번씩 이렇게 합니다.

전 세계로 배포 — 명령 한 줄

로컬에서 잘 돌아가면 전역으로 올리세요:

bash
npx wrangler deploy

# Total Upload: 1.18 KiB / gzip: 0.55 KiB
# Uploaded free-json-api (1.34 sec)
# Published free-json-api (4.21 sec)
#   https://free-json-api.<your-subdomain>.workers.dev
# Current Deployment ID: ...

이 URL이 이제 모든 Cloudflare 데이터 센터에서 살아있습니다. 베를린에서 온 요청은 프랑크푸르트에, 도쿄에서 온 요청은 도쿄에, 상파울루에서 온 요청은 상파울루에 닿습니다 — 같은 코드, 추가 설정 없음. 번들은 1.2KiB였습니다. 로컬에서 썼던 똑같은 curl 명령으로 라이브 URL을 테스트해보세요 — localhost:8787만 Wrangler가 출력해준 workers.dev 호스트네임으로 바꾸면 됩니다.

언제쯤 무료 티어를 졸업하게 될까

한참 동안은 그럴 일이 없습니다. 하루 100k 요청은 24시간 평균으로 초당 약 1.15 요청 — 대부분의 사이드 프로젝트, 내부 도구, 초기 단계 제품이 처리하는 양보다 훨씬 여유롭습니다. 보통 무료 티어를 벗어나게 만드는 첫 요인은 요청 수가 아니라 저장소예요: KV, R2, D1에 많이 쓰기 시작하면 거기 자체 쿼터가 따로 있습니다. 유료 플랜은 월 5달러부터 시작하고 1천만 요청, 요청당 더 많은 CPU, 그리고 트래픽 급증에 대비한 신용카드 안전망을 제공합니다. 대부분의 프로젝트는 끝까지 필요 없습니다.

다음 할 일

여기서 배포한 건 가장 작은 쓸모 있는 Worker입니다. 진짜 프로덕션 엔드포인트라면 몇 가지 습관이 더 필요해요 — 브라우저 호출자를 위한 CORS, API 키를 위한 시크릿 관리, 느린 업스트림을 위한 엣지 캐싱, 업스트림 서비스를 프록시하는 전략 같은 것들. 이 글이 끝나는 지점에서 이어지는 Cloudflare Workers와 JSON — 실용 가이드에서 그 부분을 다뤘습니다.

JSON을 다루는 Worker를 만들 때 제가 항상 열어두는 도구 몇 가지: 응답을 보기 좋게 출력할 때의 JSON Formatter, 페이로드가 정확히 왜 거부됐는지 알아낼 때의 JSON Validator, 그리고 필드 추출 로직을 코딩 전에 계획할 때의 JSON Path입니다. JSON 포맷 자체는 RFC 8259에 명세되어 있고 — 실제 사용자에게서 들어오는 이상한 입력을 다루기 시작하면 한 번 훑어볼 가치가 있습니다.

더 깊은 Worker 패턴들 — KV 저장소, Durable Objects, 스케줄 트리거, AI 추론 바인딩 — 에는 Cloudflare의 Workers examples gallery가 제가 찾은 것 중에 가장 좋은 자료입니다. 다음 단계에 복붙해 쓸 수 있는 레시피 모음집입니다.

마무리

Cloudflare Workers는 진짜 사이드 프로젝트에 실제로 쓸 만한 보기 드문 무료 티어입니다. 시작에 신용카드 필요 없고, 하루 100k 요청, 그리고 명령어 한 줄에 들어가는 배포 스토리. 이 글의 패턴 — JSON 받고, 검증하고, JSON 반환 — 은 대부분의 API가 하는 일의 상당 부분을 커버합니다. 인프라가 귀찮아 보여서 작은 API 출시를 미루고 있었다면, 이게 3년 전의 제가 택했더라면 좋았을 길입니다.