서버리스 엣지 컴퓨팅 구현 가이드 — 글로벌 P99 30ms를 달성하는 실전 패턴 5가지
솔직히 처음 "엣지 컴퓨팅"이라는 단어를 들었을 때 CDN의 고급 마케팅 용어 정도로 생각했습니다. 그런데 막상 Cloudflare Workers로 JWT 인증 레이어를 하나 옮겨봤더니, 서울에서 뉴욕 사용자에게 응답하는 TTFB가 280ms에서 22ms로 뚝 떨어지는 걸 보고 생각이 완전히 바뀌었습니다. 이게 그냥 캐싱이 아니라 실제로 코드가 사용자 옆에서 실행되고 있던 거였죠.
2025년 기준으로 엣지 함수 도입은 전년 대비 287% 증가했고, 신규 애플리케이션의 56%가 엣지 함수를 하나 이상 활용하고 있습니다(출처: DEV Community 2025). 단순한 유행이 아니라 백엔드 아키텍처의 지형이 바뀌고 있는 것입니다. 이 글에서는 서버리스 엣지 컴퓨팅이 왜 이렇게 빠른지 원리부터 짚고, 실무에서 바로 쓸 수 있는 5가지 패턴과 함께 흔히 저지르는 실수까지 정리합니다.
핵심 개념
엣지가 빠른 이유 — V8 Isolate와 Wasm
전통적인 서버리스(AWS Lambda 등)는 특정 리전의 데이터센터에서 코드를 실행합니다. 한국 사용자가 us-east-1에 뜬 Lambda를 호출하면 지구 반 바퀴를 왕복하는 거고, 거기에 콜드 스타트까지 더해지면 체감 지연은 꽤 고통스럽습니다.
엣지 서버리스는 구조 자체가 다릅니다. 전 세계 수백 개의 PoP에서 코드를 실행하기 때문에 물리적 거리가 대폭 줄어듭니다. 격리 방식도 완전히 다르고요.
| 방식 | 격리 단위 | 콜드 스타트 | 메모리 오버헤드 |
|---|---|---|---|
| 전통 Lambda | Docker 컨테이너 | 100~1,000ms | 높음 |
| V8 Isolate (Workers 등) | V8 엔진 Isolate | 1ms 미만 ① | 극소 |
| WebAssembly (Wasm) | Wasm 모듈 | 거의 0 ② | 컨테이너 대비 1/5 |
① 워밍된 Isolate를 재사용하는 경우 기준입니다. 완전히 신규 Isolate를 생성할 때는 더 길 수 있습니다. ② Wasm 모듈이 이미 캐시된 경우 기준입니다. 최초 로드 시에는 초기화 시간이 추가됩니다.
Docker 컨테이너는 OS 레벨까지 격리하기 때문에 시작 비용이 큽니다. V8 Isolate는 Chrome 브라우저가 탭 간 코드를 격리하는 것과 같은 방식으로, 훨씬 가볍게 수천 개의 함수를 동시에 띄울 수 있습니다.
PoP(Point of Presence): 인터넷 서비스 제공업체가 사용자와 가까운 지점에 설치한 네트워크 접속 거점입니다. 엣지 컴퓨팅 플랫폼은 이 PoP에 컴퓨팅 노드를 배치해 지연을 줄입니다.
CDN과 엣지 컴퓨팅의 차이
저도 처음엔 헷갈렸는데, 핵심 차이는 "정적이냐 동적이냐"입니다.
- CDN: 미리 만들어둔 정적 파일(HTML, CSS, 이미지)을 가까운 노드에서 서빙
- 엣지 컴퓨팅: 인증, 개인화, A/B 테스트, API 라우팅 같은 동적 연산을 엣지에서 실행
2025년부터는 이 경계가 사실상 사라지고 있습니다. Cloudflare는 초당 4,600만 건 이상의 요청을 Workers로 처리하며 완전한 컴퓨팅 플랫폼으로 진화했고, Vercel도 Edge Functions를 Routing Middleware와 통합했습니다.
엣지 플랫폼 한눈에 비교 (2025년 4월 기준)
| 플랫폼 | 런타임 | PoP 수 | 특징 |
|---|---|---|---|
| Cloudflare Workers | V8 Isolate / Wasm | 330+ | KV, D1, R2, Durable Objects 생태계 |
| Vercel Edge Functions | V8 (Edge Runtime) | 100+ | Next.js 완전 통합 |
| Fastly Compute@Edge | Wasm (Rust 최적화) | 90+ | 엔터프라이즈급, 예측 가능한 실행 시간 |
| Deno Deploy | V8 Isolate | 35+ | TypeScript 네이티브, 빠른 Git 배포 |
| AWS Lambda@Edge | Node.js / Python | CloudFront PoP | AWS 생태계 통합, 긴 실행 시간 |
PoP 수는 각 플랫폼의 공식 문서 기준이며 변동될 수 있습니다. 최신 수치는 각 플랫폼 공식 문서에서 확인하시면 됩니다.
Cloudflare Workers 진입점 구조
이후 코드 예시는 대부분 Cloudflare Workers 기반입니다. Workers의 기본 진입점은 아래처럼 fetch 핸들러를 export default로 내보내는 구조입니다. Node.js의 http.createServer나 Express의 app.listen과 같은 역할이라고 보면 됩니다.
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// 모든 요청이 여기로 들어옵니다
return new Response('Hello Edge!');
}
};실전 적용
예시 1: 엣지 미들웨어로 JWT 인증 처리
실무에서 자주 맞닥뜨리는 상황인데, 인증 로직이 오리진 서버까지 들어온 다음에야 거부 응답을 내려보내는 구조입니다. 요청이 이미 데이터센터까지 도달했으니 당연히 느리고, 오리진에 불필요한 부하도 생깁니다.
엣지에서 토큰을 검증하면 미인증 요청을 오리진 도달 전에 차단할 수 있습니다. 핵심은 외부 서비스 호출 없이 Web Crypto API만으로 서명 검증이 가능하다는 점입니다.
// Web Crypto API로 HMAC-SHA256 서명 검증
async function verifyJWT(token: string, secret: string): Promise<boolean> {
try {
const [headerB64, payloadB64, sigB64] = token.split('.');
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['verify']
);
const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
const sig = Uint8Array.from(
atob(sigB64.replace(/-/g, '+').replace(/_/g, '/')),
c => c.charCodeAt(0)
);
return await crypto.subtle.verify('HMAC', key, sig, data);
} catch {
return false;
}
}
// Cloudflare Workers: JWT 검증을 엣지에서 처리
export default {
async fetch(request: Request, env: Env): Promise<Response> {
try {
const authHeader = request.headers.get('Authorization');
const token = authHeader?.split(' ')[1];
if (!token || !(await verifyJWT(token, env.JWT_SECRET))) {
return new Response('Unauthorized', { status: 401 });
}
return fetch(request);
} catch {
// 엣지 처리 실패 시 오리진으로 폴백
return fetch(request);
}
}
};| 코드 포인트 | 설명 |
|---|---|
crypto.subtle.importKey |
Web Crypto API — Node.js 없이 모든 엣지 런타임에서 사용 가능한 표준 API |
crypto.subtle.verify |
서명 검증을 외부 라이브러리 없이 처리, 네트워크 왕복 비용 없음 |
catch 폴백 |
엣지 노드 장애 시 오리진으로 요청을 그대로 전달 |
예시 2: 쿠키 기반 A/B 테스트 (Vercel Routing Middleware)
코드 배포 없이 실험 파라미터를 글로벌하게 밀리초 안에 갱신하고 싶을 때 엣지 미들웨어가 딱 맞습니다. 마케팅팀이 엔지니어 없이 독립적으로 실험을 돌릴 수 있게 된다는 게 실제로 팀 운영 방식을 바꿔놓더라고요.
// Vercel Routing Middleware: 쿠키 기반 A/B 분기
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
try {
const cookie = request.cookies.get('variant')?.value;
const bucket = cookie === 'B' ? 'B' : 'A';
return NextResponse.rewrite(
new URL(`/variants/${bucket}`, request.url)
);
} catch {
return NextResponse.next();
}
}
export const config = {
matcher: '/landing',
};Edge Config와 결합하면 배포 없이 실험 비율을 실시간으로 조정할 수 있습니다.
예시 3: Durable Objects로 실시간 협업 구현
무상태(Stateless)가 기본인 엣지에서 WebSocket 채팅방 같은 상태 있는 기능을 구현하려면 Cloudflare의 Durable Objects를 활용할 수 있습니다.
⚠️
WebSocketPair와webSocket: client응답 옵션은 Cloudflare Workers 전용 비표준 API입니다. Vercel이나 Deno Deploy 환경에서는 동작하지 않으며, 해당 플랫폼의 WebSocket 지원 방식을 별도로 확인하셔야 합니다.
// Cloudflare Durable Object로 WebSocket 채팅룸 유지
export class ChatRoom {
private sessions: WebSocket[] = [];
async fetch(request: Request): Promise<Response> {
try {
// WebSocketPair는 Cloudflare Workers 전용 API
const pair = new WebSocketPair();
const [client, server] = Object.values(pair);
this.sessions.push(server);
server.accept();
server.addEventListener('message', (e) => {
this.broadcast(e.data as string);
});
server.addEventListener('close', () => {
this.sessions = this.sessions.filter(ws => ws !== server);
});
server.addEventListener('error', () => {
this.sessions = this.sessions.filter(ws => ws !== server);
});
// webSocket 응답 옵션도 Cloudflare Workers 전용
return new Response(null, { status: 101, webSocket: client });
} catch (e) {
return new Response('WebSocket upgrade failed', { status: 500 });
}
}
private broadcast(message: string): void {
this.sessions.forEach(ws => {
if (ws.readyState === WebSocket.OPEN) ws.send(message);
});
}
}Durable Object는 지리적으로 가장 가까운 엣지 노드에 상태가 고정(pinning)되기 때문에, 같은 룸의 모든 사용자가 동일 노드를 바라보며 일관성을 유지합니다.
Durable Objects: Cloudflare Workers의 상태 유지 엔티티입니다. 특정 ID로 단일 인스턴스가 보장되어 분산 환경에서도 강한 일관성(strong consistency)을 제공합니다. WebSocket 연결 관리, 분산 락, 실시간 협업에 적합합니다.
예시 4: ESI 패턴 — 정적 껍데기 + 동적 데이터 하이브리드
ESI(Edge Side Includes)는 페이지를 정적 공통 영역과 동적 개인화 영역으로 나눠서 각각 다른 방식으로 처리하는 패턴입니다. 스트리밍 플랫폼처럼 대부분의 콘텐츠는 공통이지만 사용자별로 다른 추천 캐러셀을 보여줘야 할 때 특히 유용합니다.
// 정적 Shell은 CDN 캐시, 개인화 데이터만 엣지에서 주입
export default {
async fetch(request: Request): Promise<Response> {
try {
const [shellResponse, personalizedData] = await Promise.all([
caches.default.match('/shell.html'),
fetchPersonalizedContent(request),
]);
// 캐시 미스 시 오리진에서 직접 fetch
const shellText = shellResponse
? await shellResponse.text()
: await fetch('/shell.html').then(r => r.text());
const injected = shellText.replace(
'<!-- PERSONALIZED_SLOT -->',
personalizedData
);
return new Response(injected, {
headers: { 'Content-Type': 'text/html' },
});
} catch {
// 엣지 처리 실패 시 오리진으로 폴백
return fetch(request);
}
}
};캐시 히트율이 높은 정적 부분과 매번 다른 동적 부분을 분리하면, 캐싱의 이점과 개인화를 동시에 누릴 수 있습니다. 캐시 미스가 났을 때 오리진에서 폴백 fetch를 하는 부분도 실무에서 꼭 챙겨야 하는 부분입니다.
예시 5: 지역 기반 콘텐츠 라우팅
요청자의 위치에 따라 최적 오리진이나 콘텐츠를 다르게 내려줘야 할 때 엣지 미들웨어가 깔끔한 해법이 됩니다. GDPR 대응으로 EU 사용자에게는 EU 리전 오리진을 붙여줘야 할 때도 이 패턴을 썼었는데, 오리진 서버 로직을 전혀 건드리지 않아도 돼서 편했습니다.
// 요청자 국가에 따라 오리진 라우팅
export default {
async fetch(request: Request, env: Env): Promise<Response> {
try {
// Cloudflare Workers는 cf.country로 요청자 국가를 제공
const country = (request as any).cf?.country ?? 'US';
const originMap: Record<string, string> = {
KR: 'https://api-apac.example.com',
JP: 'https://api-apac.example.com',
DE: 'https://api-eu.example.com',
FR: 'https://api-eu.example.com',
};
const origin = originMap[country] ?? 'https://api-us.example.com';
const targetUrl = new URL(request.url);
targetUrl.hostname = new URL(origin).hostname;
return fetch(new Request(targetUrl.toString(), request));
} catch {
return fetch(request);
}
}
};| 코드 포인트 | 설명 |
|---|---|
request.cf?.country |
Cloudflare Workers가 자동으로 주입하는 GeoIP 데이터 |
originMap 폴백 |
매핑이 없는 국가는 기본 오리진으로 처리 |
catch 폴백 |
라우팅 실패 시 원본 요청을 그대로 전달해 서비스 단절 방지 |
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 초저지연 | 전 세계 사용자에게 5~30ms TTFB, P99 레이턴시 극적 개선 |
| Zero Cold Start | 워밍된 V8 Isolate 재사용으로 콜드 스타트 사실상 제거 |
| 자동 글로벌 확장 | 복잡한 멀티리전 배포 없이 자동 분산 |
| 비용 효율 | 요청 단위 과금, 유휴 비용 없음 |
| 보안 강화 | 오리진 노출 최소화, 엣지에서 DDoS·봇 선제 차단 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 실행 시간 제한 | Workers·Vercel Edge 최대 30초, Deno Deploy CPU 200ms | 장기 실행 작업은 오리진으로 위임 |
| 메모리 제한 | Cloudflare Workers 기준 128MB | Wasm 번들 50KB 이하 목표(Cloudflare 권장), 페이로드 최소화 |
| 제한된 런타임 API | fs, net 등 Node.js 표준 API 미지원 |
Web API 기반으로 재작성, Wasm 모듈 활용 |
| Eventual Consistency | 엣지 KV는 강한 일관성 보장 어려움 | 강한 일관성이 필요한 데이터는 Durable Objects 사용 |
| 디버깅 복잡도 | 분산 환경 로그 수집·추적 어려움 | Miniflare로 로컬 시뮬레이션, 분산 트레이싱 도입 |
| 벤더 락인 | Durable Objects 등 플랫폼 고유 기능 의존 시 이식성 저하 | Hono 같은 멀티 플랫폼 프레임워크로 추상화 |
Eventual Consistency(결과적 일관성): 즉시 일관성을 보장하지 않고, 시간이 지나면 모든 노드가 같은 값으로 수렴하는 방식입니다. 엣지 KV 스토어는 읽기 성능을 위해 이 방식을 채택합니다. 재고나 결제처럼 즉시 정확성이 중요한 데이터에는 Durable Objects를 사용하세요.
Miniflare: Cloudflare Workers를 로컬에서 시뮬레이션하는 도구입니다. KV, Durable Objects, R2까지 로컬에서 에뮬레이션할 수 있어 개발 피드백 루프를 크게 단축해 줍니다.
실무에서 가장 흔한 실수
-
엣지 함수 인스턴스에 상태를 직접 들고 있는 것: 요청마다 다른 인스턴스가 처리할 수 있어서 로컬 변수에 상태를 저장하면 일관성이 깨집니다. 상태는 반드시 KV, D1, Durable Objects 같은 외부 저장소로 분리하세요 — 인스턴스 간 공유는 보장되지 않습니다.
-
번들 크기를 신경 쓰지 않는 것:
moment.js하나가 Cloudflare Workers 권장 번들 크기(50KB)를 훌쩍 넘겨버립니다. 엣지에서는 번들이 클수록 초기화 비용이 늘어나므로date-fns같은 경량 대안이나 Web API 내장 기능을 활용하세요. -
오리진 폴백 경로를 빠뜨리는 것: 엣지 노드 장애 시 오리진으로 자동 폴백하는 회로 차단기 패턴이 없으면 전체 서비스가 영향을 받습니다. 위 코드 예시들처럼
try/catch후 오리진 직접 호출로 이어지는 폴백 경로를 반드시 확보하세요.
마치며
280ms에서 22ms로 TTFB가 떨어지는 경험을 한 번 해보면 "어디서 실행할지를 다시 설계하는 것"이 왜 중요한지 바로 납득이 됩니다. 모든 로직을 엣지로 옮길 필요는 없고, 인증·라우팅·A/B 테스트처럼 가볍고 빈번한 연산부터 시작해보시면 효과를 빠르게 확인할 수 있습니다.
지금 바로 시작해볼 수 있는 3단계:
-
Cloudflare Workers 무료 플랜으로 첫 배포를 경험해볼 수 있습니다.
pnpm create cloudflare@latest로 프로젝트를 생성하고,wrangler dev로 로컬 시뮬레이션을 돌린 뒤wrangler deploy로 실제 배포까지 30분 안에 해볼 수 있습니다. -
기존 서비스에서 JWT 검증 혹은 봇 차단 미들웨어 하나를 엣지로 옮겨보시면 좋습니다. 변경 범위가 작아서 리스크가 낮고, Before/After 레이턴시 비교로 팀 내 도입 근거를 만들기에 좋습니다.
-
상태가 필요한 시나리오가 생기면 Durable Objects + Hono 조합을 살펴보시면 됩니다. Hono는 Cloudflare, Deno, Bun에서 동일 코드로 동작하는 엣지 전용 경량 프레임워크여서, 플랫폼에 종속되지 않고 엣지 패턴을 익히는 데 적합합니다.
다음 글: Cloudflare D1과 Durable Objects로 엣지 퍼스트 풀스택 앱 설계하기 — 분산 SQLite 기반 데이터 모델링과 강한 일관성이 필요한 경계를 실전 예제로 정리합니다.
참고 자료
- Serverless Edge Computing: Taxonomy and Systematic Literature Review | arXiv 2025
- Serverless and Edge Are Eating the Backend in 2025 | DEV Community
- Edge Computing with Cloudflare Workers: Complete Guide 2026 | Calmops
- Cloudflare Durable Objects 공식 문서
- Cloudflare Workers 스토리지 옵션 가이드
- WebAssembly Goes Cloud-Native: Why 2025 Is the Year Wasm Dominates Edge & Serverless | Medium
- Deno Deploy vs Cloudflare Workers vs Vercel Edge Functions: Which Wins in 2025? | Medium
- Cloudflare Workers로 서버리스 애플리케이션 만들기 | Dale Seo Engineering Blog