8.9 KiB
8.9 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| backend-edge-functions | Edge Functions — Cloudflare / Vercel / Deno Deploy | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
Edge Functions
사용자 가까이 (300+ region) 실행. Cloudflare Workers / Vercel Edge / Deno Deploy / Fastly Compute@Edge. V8 isolate (cold start ms), 작은 limit.
📖 핵심 개념
- V8 Isolate: process 안 — 매 request fast.
- Web Standard: Request / Response / fetch.
- Limits: CPU / memory / time 작음.
- Storage: KV / D1 / Durable Object / R2.
💻 코드 패턴
Cloudflare Workers
// src/index.ts
export interface Env {
DB: D1Database;
CACHE: KVNamespace;
BUCKET: R2Bucket;
}
export default {
async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === '/api/users/me') {
const userId = await getUserId(req);
const cached = await env.CACHE.get(`user:${userId}`, { type: 'json' });
if (cached) return Response.json(cached);
const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
ctx.waitUntil(env.CACHE.put(`user:${userId}`, JSON.stringify(user), { expirationTtl: 60 }));
return Response.json(user);
}
return new Response('Not found', { status: 404 });
},
};
# wrangler.toml
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-12-01"
[[d1_databases]]
binding = "DB"
database_name = "my-app"
database_id = "..."
[[kv_namespaces]]
binding = "CACHE"
id = "..."
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "uploads"
[observability]
enabled = true
wrangler dev
wrangler deploy
Vercel Edge Function
// app/api/users/route.ts
import { type NextRequest } from 'next/server';
export const runtime = 'edge';
export async function GET(req: NextRequest) {
const id = req.nextUrl.searchParams.get('id');
return Response.json({ id });
}
// 또는 standalone
// pages/api/edge.ts
export const config = { runtime: 'edge' };
export default function handler(req: Request) {
return new Response('Hello from edge');
}
Deno Deploy
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Hello from Deno Deploy'));
Deno.serve(app.fetch);
deployctl deploy --project=my-app src/index.ts
Bun on edge (Fly.io / Railway)
Bun = full Node API + Web Standard.
Fly / Railway 가 Bun runtime 지원.
Edge X but 가까운 region.
KV (Cloudflare)
// 빠른 read (eventually consistent globally)
await env.KV.put('key', 'value', { expirationTtl: 3600 });
const v = await env.KV.get('key');
const json = await env.KV.get('key', { type: 'json' });
// List
const list = await env.KV.list({ prefix: 'user:' });
// Stream large
const stream = await env.KV.get('large-file', { type: 'stream' });
→ Read 빠름 (각 region cache), write 글로벌 propagate (1-60s).
D1 (SQLite at edge)
const r = await env.DB.prepare('SELECT * FROM users WHERE email = ?')
.bind('a@b.com')
.first();
// Multi
const all = await env.DB.prepare('SELECT * FROM users WHERE status = ?')
.bind('active')
.all();
// Batch (transaction)
await env.DB.batch([
env.DB.prepare('INSERT INTO users VALUES (?, ?)').bind(id1, email1),
env.DB.prepare('INSERT INTO users VALUES (?, ?)').bind(id2, email2),
]);
Durable Objects (글로벌 state)
// Counter — 한 instance per name, 글로벌 단일
export class Counter {
state: DurableObjectState;
constructor(state: DurableObjectState) {
this.state = state;
}
async fetch(req: Request): Promise<Response> {
let count = (await this.state.storage.get<number>('count')) ?? 0;
count++;
await this.state.storage.put('count', count);
return Response.json({ count });
}
}
// Worker
export default {
async fetch(req: Request, env: Env) {
const url = new URL(req.url);
const name = url.searchParams.get('room') ?? 'default';
const id = env.COUNTER.idFromName(name);
const stub = env.COUNTER.get(id);
return stub.fetch(req);
},
};
→ Stateful — chat room, game session, rate limit.
R2 (S3-compatible storage)
const obj = await env.BUCKET.get('photo.jpg');
if (obj) return new Response(obj.body, { headers: { 'Content-Type': obj.httpMetadata?.contentType ?? '' } });
await env.BUCKET.put('upload.jpg', file, {
httpMetadata: { contentType: 'image/jpeg' },
});
await env.BUCKET.delete('old.jpg');
→ S3-compat + free egress.
Cron triggers
# wrangler.toml
[triggers]
crons = ["0 9 * * *"] # 매일 9시
export default {
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
await runDailyTask(env);
},
async fetch(req: Request, env: Env) { ... },
};
Queues (Cloudflare)
// Producer
await env.QUEUE.send({ orderId: '...', userId: '...' });
// Consumer
export default {
async queue(batch: MessageBatch, env: Env) {
for (const msg of batch.messages) {
await processOrder(msg.body);
msg.ack();
}
},
};
→ Decouple.
Limits (대략)
Cloudflare Workers:
- CPU: 30s (paid) / 10ms (free) per request
- Memory: 128 MB
- Subrequests: 1000
- Bundle: 10 MB
- Compute units / month: $5 = 10M+
Vercel Edge:
- CPU: 30s
- Memory: 128 MB
- Bundle: 1 MB
Deno Deploy:
- CPU: 50ms (per request)
- Memory: 512 MB
→ Long-running task = 다른 (Lambda / VM).
Edge 의 함정
1. CPU limit (10ms free) — 큰 work X.
2. Bundle size — Node module 일부 X.
3. Cold start — 거의 0 (V8 isolate).
4. Connection pool 어려움 (no persistent state).
5. 일부 Node API X (fs, child_process).
→ HTTP / KV / D1 만 사용.
Use cases (적합)
- API gateway (auth, rate limit, route)
- A/B test, geo redirect
- Image / response transformation
- Analytics ingestion
- Search index 호출
- Cache layer
- Webhook receiver
- Static site SSR
Use cases (안 적합)
- 큰 ML inference
- Long task (1 min+)
- Persistent connection (DB pool)
- File system 의존
- Large dependencies (Node-specific)
Multi-region database
Edge function 가 사용자 가까이.
DB 가 single region = 큰 latency.
해결:
- Read replica per region
- Hyperdrive (CF cache)
- Turso embedded replica
- 분산 DB (Spanner, Yugabyte)
Auth at edge
import { jwt } from 'hono/jwt';
app.use('/api/*', jwt({ secret: env.JWT_SECRET }));
// 또는 직접
async function verifyJwt(token: string, secret: string) {
const [header, payload, signature] = token.split('.');
// JWT verify (jose 같은 lib)
return JSON.parse(atob(payload));
}
Static + Edge function
Vercel / Cloudflare Pages:
- Static assets — CDN
- API routes — edge function
→ Most modern stack.
Streaming
export default {
async fetch() {
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
(async () => {
for (let i = 0; i < 5; i++) {
await writer.write(new TextEncoder().encode(`chunk ${i}\n`));
await new Promise(r => setTimeout(r, 1000));
}
writer.close();
})();
return new Response(readable);
},
};
→ SSE / streaming response.
Test (local)
wrangler dev # local + miniflare (Cloudflare emulator)
vercel dev
deno run --watch src/index.ts
Deploy
wrangler deploy --env production
vercel --prod
deployctl deploy --prod
Cost
Cloudflare Workers:
Free: 100K req/day
Paid: $5/month + $0.50 per million
Vercel:
Hobby: free
Pro: $20/month + execution time
→ 가장 cheap edge.
Comparison
Cloudflare:
+ 가장 빠름 (V8 isolate)
+ KV / D1 / R2 통합
+ Free tier 강
- Node API 제한
Vercel:
+ Next.js 통합 (best)
+ Frontend / API 통합
- 비싸 (큰 traffic)
Deno Deploy:
+ Deno native
+ Web Standard
- Smaller ecosystem
Fastly Compute@Edge:
+ Wasm 지원
+ 큰 enterprise
🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 빠른 API + 글로벌 | Cloudflare Workers |
| Next.js | Vercel Edge |
| Deno project | Deno Deploy |
| Wasm | Fastly / CF Workers |
| Long task | Lambda / VM |
| Big data | Container / VM |
❌ 안티패턴
- Edge 안 long task: timeout.
- Big bundle (큰 dep): limit.
- Node-specific (fs, net): 깨짐.
- DB persistent connection: HTTP driver.
- Edge 가 모든 답: 가까운 user 가 critical 시만.
- State in memory: cold isolate 에 잃음. KV / DO.
🤖 LLM 활용 힌트
- Cloudflare Workers + D1 + KV = 가장 강.
- Vercel Edge + Next.js = best DX.
- Web Standard API only.
- Cold start 거의 0.