본문 바로가기
IT/Cloud

[Cloud] Cloudflare Workers로 서버리스 API 및 엣지 컴퓨팅 구축 가이드

by 수누다 2026. 4. 28.

서버 없이 전 세계에서 실행되는 API? Cloudflare Workers 이야기

솔직히 말씀드리면, 처음 Cloudflare Workers를 접했을 때 "이게 뭔 소리지?" 싶었거든요. 서버리스(Serverless)라는 개념 자체는 알고 있었는데, "엣지에서 실행된다"는 말이 딱 와닿지 않았습니다. 그러다 실제로 간단한 API 하나를 올려봤는데... 서울에서 요청하면 서울 근처 데이터센터에서, 미국에서 요청하면 미국 데이터센터에서 응답이 오는 거예요. 레이턴시(Latency, 응답 지연)가 확 줄어드는 게 느껴졌습니다. 그때 "아, 이래서 엣지 컴퓨팅이구나" 하고 감이 왔죠.

홈랩에서 이것저것 돌리다 보면 늘 고민이 생기잖아요. 외부에서 접근 가능한 간단한 API가 필요한데, 서버 하나 띄우자니 유지보수가 귀찮고, 클라우드 VM 쓰자니 비용이 아깝고. 그 틈새를 Cloudflare Workers가 정말 잘 채워줬습니다. 오늘은 제가 실제로 Workers로 서버리스 API를 구축하면서 겪은 경험을 바탕으로, 처음 시작하시는 분들도 따라할 수 있게 정리해 드릴게요.

▲ Cloudflare Workers의 엣지 컴퓨팅 구조 — 전 세계 데이터센터에서 코드가 실행되는 개념도


Cloudflare Workers가 뭔지 쉽게 풀어보기

엣지 컴퓨팅(Edge Computing)이란?

일반적인 클라우드 서버는 특정 리전(Region, 지역)에 고정되어 있어요. 예를 들어 AWS ap-northeast-2(서울 리전)에 서버를 올리면, 미국 사용자가 접속할 때는 태평양을 건너서 요청이 오가야 하죠. 이게 레이턴시의 주범입니다.

엣지 컴퓨팅은 이 문제를 뒤집어서 생각한 거예요. "서버를 중앙에 두지 말고, 사용자 가까이에 두자!" 라는 발상이죠. Cloudflare는 전 세계 200개 이상의 도시에 데이터센터(PoP, Point of Presence)를 운영하고 있는데, Workers 코드가 이 모든 위치에서 실행됩니다. 쉽게 말해, 사용자가 어디에 있든 가장 가까운 서버에서 응답이 오는 거예요.

Workers의 핵심 특징

  • V8 엔진 기반: Node.js가 아닌 V8 Isolate(아이솔레이트) 방식으로 실행됩니다. 컨테이너보다 훨씬 빠르게 시작해요 (콜드 스타트 거의 없음)
  • JavaScript / TypeScript 지원: 프론트엔드 개발자분들도 친숙하게 쓸 수 있어요
  • Workers KV: 엣지에서 사용 가능한 키-값(Key-Value) 스토리지
  • 무료 티어 존재: 하루 10만 요청까지 무료 (최신 정보는 Cloudflare 공식 가격 페이지 확인 권장)
  • 배포가 초간단: Wrangler CLI 명령어 하나로 전 세계 배포 완료
구분 전통적 서버 일반 서버리스 (Lambda 등) Cloudflare Workers
실행 위치 단일 리전 단일 리전 전 세계 엣지
콜드 스타트 없음 수백ms ~ 수초 거의 없음 (0ms 수준)
관리 부담 높음 낮음 매우 낮음
런타임 자유 Node.js, Python 등 V8 (JS/TS/Wasm)
무료 티어 없음 있음 있음

환경 설정: Wrangler CLI 설치부터 시작

본격적으로 시작해볼게요. Wrangler는 Cloudflare Workers 개발을 위한 공식 CLI(Command Line Interface) 도구입니다. 이게 없으면 아무것도 못 하니까 먼저 설치부터요.

1단계: Node.js 및 Wrangler 설치

Node.js 16 이상이 설치되어 있다는 전제 하에 진행합니다.

# Wrangler CLI 전역 설치
npm install -g wrangler

# 설치 확인
wrangler --version

# Cloudflare 계정 로그인 (브라우저가 열립니다)
wrangler login

로그인하면 브라우저에서 Cloudflare 대시보드로 이동해서 권한 허용을 요청해요. 그냥 허용 누르시면 됩니다. 처음에 이 과정이 좀 낯설었는데, 익숙해지면 진짜 편하더라고요.

2단계: 새 Workers 프로젝트 생성

# 새 프로젝트 생성 (대화형 설정)
npm create cloudflare@latest my-api-worker

# 프로젝트 디렉토리로 이동
cd my-api-worker

생성 과정에서 몇 가지 질문이 나오는데, "Hello World" 템플릿 선택하고, TypeScript 쓸지 JavaScript 쓸지 고르면 됩니다. 저는 보통 TypeScript를 선택하는 편이에요. 타입 안정성이 있으니까요.

프로젝트 구조는 이렇게 됩니다:

my-api-worker/
├── src/
│   └── index.ts        # 메인 Worker 코드
├── wrangler.toml       # Worker 설정 파일
├── package.json
└── tsconfig.json

실전 구현: 서버리스 REST API 만들기

이제 진짜 코드를 써볼게요. 단순한 "Hello World"는 재미없으니까, 실제로 쓸 만한 간단한 REST API를 만들어보겠습니다. 라우팅(Routing)과 메서드 분기를 포함한 구조예요.

▲ Wrangler를 이용한 로컬 개발 환경 — wrangler dev 명령으로 로컬에서 Workers를 테스트할 수 있어요

기본 라우팅이 포함된 API Worker

// src/index.ts

export interface Env {
  // 나중에 KV 바인딩을 여기 추가할 거예요
  MY_KV: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);
    const path = url.pathname;
    const method = request.method;

    // CORS 헤더 설정
    const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type',
    };

    // Preflight 요청 처리
    if (method === 'OPTIONS') {
      return new Response(null, { headers: corsHeaders });
    }

    // 라우팅 처리
    if (path === '/api/hello' && method === 'GET') {
      return Response.json(
        { message: '안녕하세요! Cloudflare Workers에서 응답합니다.', timestamp: Date.now() },
        { headers: corsHeaders }
      );
    }

    if (path === '/api/items' && method === 'GET') {
      return handleGetItems(env, corsHeaders);
    }

    if (path === '/api/items' && method === 'POST') {
      return handlePostItem(request, env, corsHeaders);
    }

    // 404 처리
    return Response.json(
      { error: 'Not Found', path },
      { status: 404, headers: corsHeaders }
    );
  },
};

// GET /api/items — KV에서 아이템 목록 조회
async function handleGetItems(env: Env, headers: Record<string, string>): Promise<Response> {
  const data = await env.MY_KV.get('items', 'json') as string[] | null;
  return Response.json(
    { items: data ?? [] },
    { headers }
  );
}

// POST /api/items — KV에 아이템 추가
async function handlePostItem(
  request: Request,
  env: Env,
  headers: Record<string, string>
): Promise<Response> {
  const body = await request.json() as { name: string };

  if (!body.name) {
    return Response.json(
      { error: 'name 필드가 필요합니다' },
      { status: 400, headers }
    );
  }

  const existing = await env.MY_KV.get('items', 'json') as string[] | null;
  const items = existing ?? [];
  items.push(body.name);

  await env.MY_KV.put('items', JSON.stringify(items));

  return Response.json(
    { success: true, items },
    { status: 201, headers }
  );
}

코드 보시면 구조가 꽤 직관적이죠? fetch 함수가 모든 HTTP 요청의 진입점이고, URL 경로와 HTTP 메서드로 분기처리를 합니다. Node.js의 Express 같은 느낌이에요.

Workers KV 설정하기

Workers KV는 엣지에서 사용 가능한 분산 키-값 스토리지예요. 데이터베이스를 별도로 운영하지 않아도 간단한 데이터를 저장하고 조회할 수 있거든요. 쓰기는 약간의 전파 지연이 있지만, 읽기는 엣지에서 바로 처리되니까 빠릅니다.

# KV 네임스페이스(Namespace) 생성
wrangler kv:namespace create MY_KV

# 로컬 개발용 프리뷰 네임스페이스도 생성
wrangler kv:namespace create MY_KV --preview

명령어 실행 후 출력되는 ID를 wrangler.toml에 붙여넣어야 합니다.

# wrangler.toml
name = "my-api-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[[kv_namespaces]]
binding = "MY_KV"          # 코드에서 env.MY_KV로 접근
id = "여기에_실제_KV_ID_입력"
preview_id = "여기에_프리뷰_KV_ID_입력"

💡 : binding 이름이 TypeScript 인터페이스의 Env에 선언한 이름과 일치해야 해요. 처음에 이게 안 맞아서 에러가 났었는데, 삽질 좀 했습니다 ㅎㅎ

로컬에서 테스트하기

# 로컬 개발 서버 실행
wrangler dev

# 기본적으로 http://localhost:8787 에서 실행됩니다

# 다른 터미널에서 API 테스트
curl http://localhost:8787/api/hello

# POST 테스트
curl -X POST http://localhost:8787/api/items \
  -H "Content-Type: application/json" \
  -d '{"name": "테스트 아이템"}'

# GET으로 확인
curl http://localhost:8787/api/items

로컬에서 잘 돌아가면 이제 배포할 차례예요!

# 전 세계 배포 (진짜 이 명령어 하나예요)
wrangler deploy

# 배포 후 URL이 출력됩니다
# 예: https://my-api-worker.your-subdomain.workers.dev

🎉 배포 완료! 이 URL로 전 세계 어디서든 접근 가능합니다. 처음에 이게 진짜로 되는 건지 믿기지 않아서 VPN으로 미국, 일본, 유럽 각각에서 테스트해봤는데 전부 잘 됐더라고요.


⚠️ 주의사항 및 실제 겪은 트러블슈팅

좋은 것만 얘기하면 재미없죠. 실제로 쓰다 보면 꼭 한 번씩 걸리는 것들이 있어요.

문제 1: Workers KV 쓰기 전파 지연

KV에 데이터를 쓰고 바로 읽으면 이전 값이 나오는 경우가 있어요. KV는 최종 일관성(Eventual Consistency) 모델이라서, 쓰기 작업이 전 세계 엣지에 전파되는 데 시간이 걸립니다. 공식 문서에 따르면 최대 60초 정도 걸릴 수 있어요. 로컬 개발 환경에서는 이게 즉시 반영되는 것처럼 보이는데, 프로덕션에서는 다를 수 있습니다.

해결책: 쓰기 직후 즉시 읽기가 필요한 로직은 KV 대신 Durable Objects(내구성 있는 객체, 강한 일관성 보장)를 고려하세요. 단순 캐시나 설정값 저장에는 KV가 딱 좋습니다.

문제 2: CPU 시간 제한

Workers는 요청당 CPU 시간 제한이 있어요. 무료 티어는 요청당 10ms, 유료 플랜은 더 길어요. 무거운 연산(이미지 처리, 복잡한 암호화 등)은 Workers에 맞지 않습니다. 저도 처음에 이미지 리사이징 로직을 Workers에 넣으려다 바로 한계에 부딪혔어요.

해결책: Workers는 가볍고 빠른 로직에 최적화되어 있거든요. 무거운 작업은 백엔드 서버로 위임하고, Workers는 게이트웨이(Gateway, 진입 관문) 역할만 맡기는 패턴이 좋습니다.

문제 3: Node.js API 호환성 이슈

Workers는 Node.js 런타임이 아니에요. V8 Isolate 기반이라 Node.js 전용 API(예: fs, path, crypto 일부)가 기본적으로 없어요. npm 패키지 중에도 Node.js 내장 모듈에 의존하는 것들은 Workers에서 안 돌아갈 수 있습니다.

해결책: wrangler.tomlnode_compat = true를 추가하면 일부 Node.js API 폴리필(Polyfill, 하위 호환 구현체)이 활성화됩니다. 그래도 안 되는 경우엔 Workers 환경에 맞는 대안 라이브러리를 찾아야 해요.

# wrangler.toml에 추가
node_compat = true

문제 4: 환경 변수 관리

API 키 같은 민감한 정보를 코드에 하드코딩하면 절대 안 되죠. Workers에는 Secrets(시크릿) 기능이 있어요.

# 시크릿 등록 (값은 프롬프트로 입력)
wrangler secret put MY_API_KEY

# 등록된 시크릿 목록 확인
wrangler secret list
// 코드에서 env로 접근
export interface Env {
  MY_KV: KVNamespace;
  MY_API_KEY: string;  // 시크릿도 Env에 선언
}

// 사용 예시
const apiKey = env.MY_API_KEY;

결과 확인: 대시보드에서 모니터링하기

배포 후 Cloudflare 대시보드에서 Workers 섹션을 보면 꽤 유용한 정보들을 확인할 수 있어요.

▲ Cloudflare Workers 대시보드 — 요청 수, 에러율, CPU 시간 등 실시간 모니터링이 가능해요

  • 요청 수(Requests): 분/시간/일별 요청량 확인
  • 에러율(Error Rate): 4xx, 5xx 에러 비율
  • CPU 시간: 요청당 평균 CPU 사용 시간
  • 실시간 로그: wrangler tail 명령으로 실시간 로그 스트리밍 가능
# 실시간 로그 스트리밍
wrangler tail

# 특정 Worker 지정
wrangler tail my-api-worker

로그에서 요청 경로, 응답 코드, 실행 시간 등이 다 나와요. 디버깅할 때 꽤 유용하더라고요. 처음에 이 기능 몰라서 console.log 찍어가며 고생했는데, 알고 나서 "진작 쓸걸" 했죠.

커스텀 도메인 연결

기본 제공되는 *.workers.dev 도메인 말고, 본인 도메인을 연결할 수도 있어요. Cloudflare에 도메인이 등록되어 있다면 Workers 설정에서 Route(라우트, 트래픽 경로)를 추가하면 됩니다.

# wrangler.toml에 라우트 추가
[[routes]]
pattern = "api.yourdomain.com/*"
zone_name = "yourdomain.com"

이렇게 하면 api.yourdomain.com으로 들어오는 모든 요청이 Workers로 처리돼요. 깔끔하죠?


마무리: Workers가 잘 맞는 상황 vs 아닌 상황

▲ Cloudflare Workers 적합/비적합 사용 사례 요약 — 어떤 상황에서 Workers를 선택해야 할지 한눈에 보기

몇 달 써보면서 느낀 건, Workers는 만능이 아니에요. 잘 맞는 상황이 있고, 억지로 쓰면 오히려 복잡해지는 상황도 있더라고요.

✅ Workers가 잘 맞는 경우

  • CORS 프록시, API 게이트웨이
  • A/B 테스트, 기능 플래그(Feature Flag) 처리
  • 간단한 인증/인가(Authentication/Authorization) 미들웨어
  • 정적 사이트의 동적 기능 추가 (Jamstack 패턴)
  • 봇 차단, 요청 필터링
  • 지리적으로 분산된 캐시 레이어

⚠️ 다른 선택지를 고려해야 할 경우

  • 무거운 연산이나 긴 실행 시간이 필요한 작업
  • 강한 데이터 일관성이 필요한 트랜잭션 처리
  • 파일 시스템 접근이 필요한 경우
  • 기존 Node.js/Python 등 런타임 의존성이 많은 경우

자주 묻는 질문 (FAQ)

Q. Workers KV와 일반 데이터베이스의 차이는?
A. Workers KV는 읽기에 최적화된 분산 스토리지예요. 복잡한 쿼리나 관계형 데이터가 필요하다면 Cloudflare D1(SQLite 기반) 이나 외부 DB를 연동하는 게 낫습니다.
Q. 무료로 얼마나 쓸 수 있나요?
A. 공식 문서 기준 무료 티어는 하루 10만 요청까지 제공합니다. 정확한 현행 조건은 Cloudflare 공식 가격 페이지에서 반드시 확인하세요. 자주 바뀌는 편이거든요.
Q. TypeScript 말고 다른 언어도 되나요?
A. WebAssembly(웹어셈블리)로 컴파일되는 언어라면 이론상 가능해요. Rust로 Workers를 작성하는 사례도 있습니다. 다만 생태계는 JS/TS가 가장 풍부해요.

오늘 다룬 내용이 Cloudflare Workers로 서버리스 API와 엣지 컴퓨팅을 시작하는 데 도움이 됐으면 좋겠어요. 다음 글에서는 Cloudflare D1(서버리스 SQLite 데이터베이스)과 Workers를 연동해서 더 완성도 있는 API를 만드는 방법을 다룰 예정입니다. Workers KV만으로는 아쉬울 때 딱 좋은 조합이거든요.

궁금한 점이나 직접 해보다가 막히는 부분 있으시면 댓글로 남겨주세요. 같이 삽질해봅시다! 😄