Edge Function
엣지 함수
엣지 서버에서 실행되는 서버리스 함수입니다. 전 세계 수백 개 PoP(Point of Presence)에서 코드가 실행되어 사용자에게 가장 가까운 위치에서 초저지연 응답을 제공합니다.
엣지 함수
엣지 서버에서 실행되는 서버리스 함수입니다. 전 세계 수백 개 PoP(Point of Presence)에서 코드가 실행되어 사용자에게 가장 가까운 위치에서 초저지연 응답을 제공합니다.
Edge Function의 동작 원리는 기존 서버리스와 다릅니다. AWS Lambda는 특정 리전(예: ap-northeast-2)에서만 실행되지만, Edge Function은 전 세계 200개 이상의 엣지 로케이션에서 동시에 배포되어 실행됩니다. 사용자 요청은 DNS를 통해 가장 가까운 엣지로 라우팅됩니다.
V8 Isolate 기반 실행이 핵심 기술입니다. Cloudflare Workers와 Deno Deploy는 Chrome V8 엔진의 Isolate를 사용합니다. 컨테이너나 VM 대신 경량 격리 환경에서 실행되어 콜드 스타트가 0ms에 가깝습니다. 기존 Lambda의 100-500ms 콜드 스타트와 비교하면 획기적인 차이입니다.
제한된 런타임 환경을 이해해야 합니다. Edge Function은 대부분 Web API(fetch, Request, Response, crypto 등)만 지원합니다. Node.js의 fs, net, child_process 같은 시스템 API는 사용할 수 없습니다. 이는 보안과 성능을 위한 의도적인 설계입니다.
상태 관리 방법이 독특합니다. 엣지 함수는 기본적으로 stateless지만, KV Store(Cloudflare KV), Durable Objects, Edge Config(Vercel) 등 엣지 전용 스토리지를 통해 상태를 관리할 수 있습니다. 이들은 전 세계적으로 복제되어 어느 엣지에서든 접근 가능합니다.
주요 사용 사례는 다양합니다. A/B 테스트(요청별 버킷 할당), 인증/인가(JWT 검증), 지역화(국가별 콘텐츠), 이미지 최적화(리사이징, 포맷 변환), Bot 방어(CAPTCHA 삽입), API 게이트웨이(레이트 리미팅, 요청 변환) 등이 있습니다.
// Cloudflare Workers - 완전한 API 예제
// wrangler.toml과 함께 배포
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// 라우팅
switch (url.pathname) {
case '/api/geo':
return handleGeo(request);
case '/api/kv':
return handleKV(request, env);
case '/api/cache':
return handleCache(request, ctx);
default:
return new Response('Not Found', { status: 404 });
}
},
};
// 1. 지리 정보 기반 응답
function handleGeo(request) {
const cf = request.cf || {};
const geoData = {
ip: request.headers.get('CF-Connecting-IP'),
country: cf.country,
city: cf.city,
region: cf.region,
timezone: cf.timezone,
latitude: cf.latitude,
longitude: cf.longitude,
asn: cf.asn,
colo: cf.colo, // 엣지 데이터센터 코드
};
return Response.json(geoData, {
headers: {
'Cache-Control': 'private, no-store',
},
});
}
// 2. KV Store 활용 - 글로벌 키-값 저장소
async function handleKV(request, env) {
const url = new URL(request.url);
const key = url.searchParams.get('key');
if (request.method === 'GET') {
// KV에서 읽기 (전 세계 엣지에 캐시됨)
const value = await env.MY_KV.get(key, { type: 'json' });
if (!value) {
return Response.json({ error: 'Key not found' }, { status: 404 });
}
return Response.json({
key,
value,
source: 'edge-kv',
});
}
if (request.method === 'PUT') {
const body = await request.json();
// KV에 쓰기 (60초 후 전 세계 동기화)
await env.MY_KV.put(key, JSON.stringify(body), {
expirationTtl: 3600, // 1시간 후 만료
metadata: { updatedAt: Date.now() },
});
return Response.json({ success: true, key });
}
return new Response('Method not allowed', { status: 405 });
}
// 3. Cache API 활용 - 응답 캐싱
async function handleCache(request, ctx) {
const cache = caches.default;
const cacheKey = new Request(request.url, request);
// 캐시 확인
let response = await cache.match(cacheKey);
if (response) {
// 캐시 HIT - 새 헤더 추가하여 반환
const newHeaders = new Headers(response.headers);
newHeaders.set('X-Cache', 'HIT');
return new Response(response.body, {
status: response.status,
headers: newHeaders,
});
}
// 캐시 MISS - 오리진에서 가져오기
const originResponse = await fetch('https://api.example.com/data');
const data = await originResponse.json();
response = Response.json(data, {
headers: {
'Cache-Control': 'public, max-age=60',
'X-Cache': 'MISS',
},
});
// 백그라운드에서 캐시 저장
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
}
// Deno Deploy - Edge Function 예제
// deploy: deployctl deploy --project=my-app main.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
// KV Store (Deno KV - 전역 분산 데이터베이스)
const kv = await Deno.openKv();
serve(async (request: Request) => {
const url = new URL(request.url);
// 라우팅
if (url.pathname === "/api/counter") {
return handleCounter(request);
}
if (url.pathname === "/api/user") {
return handleUser(request);
}
if (url.pathname === "/api/stream") {
return handleStream();
}
return new Response("Hello from Deno Deploy!", {
headers: { "Content-Type": "text/plain" },
});
});
// 1. Atomic Counter with Deno KV
async function handleCounter(request: Request): Promise {
const key = ["counters", "visits"];
if (request.method === "POST") {
// Atomic increment - 동시성 안전
const result = await kv.atomic()
.sum(key, 1n) // BigInt로 카운트
.commit();
if (!result.ok) {
return Response.json({ error: "Conflict" }, { status: 409 });
}
}
// 현재 값 읽기
const entry = await kv.get(key);
const count = entry.value?.value ?? 0n;
return Response.json({
count: count.toString(),
timestamp: new Date().toISOString(),
});
}
// 2. 사용자 세션 관리
async function handleUser(request: Request): Promise {
const url = new URL(request.url);
const userId = url.searchParams.get("id");
if (!userId) {
return Response.json({ error: "Missing user ID" }, { status: 400 });
}
const key = ["users", userId];
if (request.method === "GET") {
const entry = await kv.get(key);
if (!entry.value) {
return Response.json({ error: "User not found" }, { status: 404 });
}
return Response.json(entry.value);
}
if (request.method === "PUT") {
const body = await request.json();
await kv.set(key, {
...body,
updatedAt: Date.now(),
}, {
expireIn: 24 * 60 * 60 * 1000, // 24시간 후 만료
});
return Response.json({ success: true });
}
return new Response("Method not allowed", { status: 405 });
}
// 3. Streaming Response - 실시간 데이터
function handleStream(): Response {
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
for (let i = 0; i < 5; i++) {
const data = {
event: "update",
data: { count: i, time: Date.now() },
};
controller.enqueue(
encoder.encode(`data: ${JSON.stringify(data)}\n\n`)
);
await new Promise((r) => setTimeout(r, 1000));
}
controller.close();
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
});
}
┌─────────────────────────────────────────────────────────────────┐
│ Edge Function 플랫폼 비교 │
└─────────────────────────────────────────────────────────────────┘
┌──────────────────┬─────────────────┬─────────────────┬──────────────────┐
│ 항목 │ Cloudflare │ Vercel Edge │ Deno Deploy │
│ │ Workers │ Functions │ │
├──────────────────┼─────────────────┼─────────────────┼──────────────────┤
│ 런타임 │ V8 Isolate │ V8 Isolate │ V8 Isolate │
│ 언어 │ JS/TS/WASM │ JS/TS │ JS/TS │
│ 엣지 로케이션 │ 300+ │ 100+ │ 35+ │
│ 콜드 스타트 │ 0ms │ ~0ms │ 0ms │
│ 실행 제한 │ 10-30ms CPU │ 30s (Pro) │ 50ms CPU │
│ 메모리 │ 128MB │ 128MB │ 512MB │
├──────────────────┼─────────────────┼─────────────────┼──────────────────┤
│ 상태 저장소 │ KV, D1, R2 │ Edge Config │ Deno KV │
│ │ Durable Objects │ Vercel KV │ │
│ DB 연결 │ Hyperdrive │ Edge Functions │ 직접 연결 │
│ 캐싱 │ Cache API │ Next.js Cache │ 수동 구현 │
├──────────────────┼─────────────────┼─────────────────┼──────────────────┤
│ 무료 티어 │ 100K req/day │ 100K req/month │ 1M req/month │
│ 가격 (유료) │ $5/10M req │ $20/month~ │ $10/5M req │
└──────────────────┴─────────────────┴─────────────────┴──────────────────┘
콜드 스타트 비교:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ AWS Lambda ███████████████████████████ 100-500ms │
│ Lambda@Edge ██████████████████ 50-200ms │
│ Cloudflare Workers █ 0-5ms │
│ Vercel Edge █ 0-5ms │
│ Deno Deploy █ 0-5ms │
│ │
└─────────────────────────────────────────────────────────────────┘
Edge Function 적합 사용 사례:
✅ 적합한 경우:
- 인증/인가 (JWT 검증, API Key 체크)
- A/B 테스트 (요청별 버킷 할당)
- 지역화 (국가별 리다이렉트, 언어 감지)
- 캐시 레이어 (stale-while-revalidate)
- Bot 감지 및 차단
- 요청/응답 변환
❌ 부적합한 경우:
- 복잡한 비즈니스 로직 (CPU 집약적)
- 대용량 파일 처리
- 장시간 실행 작업
- 레거시 Node.js 의존성 필요
- 데이터베이스 직접 연결 (일부 제한)
"API 응답 중 70%가 동일한 데이터를 반환하는데, 매번 Lambda까지 가는 건 비효율적이에요. Cloudflare Workers에서 KV로 캐싱하면 평균 응답 시간을 200ms에서 20ms로 줄일 수 있습니다. 캐시 미스 시에만 오리진으로 가도록 하죠."
"JWT 검증을 엣지 함수로 옮기면 인증 실패 요청이 백엔드까지 도달하지 않아요. Vercel Middleware에서 토큰 검증하고, 만료된 토큰은 바로 401 반환하면 서버 부하가 확 줄어듭니다. jose 라이브러리가 엣지에서도 잘 동작해요."
"국가별로 다른 가격 정책을 적용해야 하는데, 엣지 함수에서 request.cf.country로 국가를 감지하고 Price API 응답을 변환하면 됩니다. 클라이언트 코드 변경 없이 지역화가 가능하고, GDPR 대응도 엣지에서 처리할 수 있어요."
Edge Function은 CPU 시간이 10-50ms로 제한됩니다. 복잡한 연산, JSON 파싱, 암호화 등은 빠르게 제한에 도달합니다. 무거운 작업은 백엔드로 위임하세요.
fs, net, child_process 등 Node.js 전용 API는 사용할 수 없습니다. npm 패키지도 순수 JS만 동작합니다. 배포 전 호환성을 반드시 확인하세요.
전통적인 DB 커넥션 풀링이 불가능합니다. HTTP 기반 DB(PlanetScale, Neon, Supabase)나 Cloudflare D1, Deno KV 같은 엣지 친화적 솔루션을 사용하세요.
요청 필터링, 캐시 레이어, 인증 검사는 엣지의 최적 사용 사례입니다. ctx.waitUntil()로 비동기 작업을 백그라운드 처리하고, Cache API로 응답을 캐싱하면 성능을 극대화할 수 있습니다.