[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
---
|
||||
id: backend-edge-runtime-deep
|
||||
title: Edge Runtime — Workers / Deno Deploy / Vercel Edge
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [backend, edge, vibe-coding]
|
||||
tech_stack: { language: "TS", applicable_to: ["Backend"] }
|
||||
applied_in: []
|
||||
aliases: [Cloudflare Workers, Deno Deploy, Vercel Edge, edge function, V8 isolates, low latency global]
|
||||
---
|
||||
|
||||
# Edge Runtime Deep
|
||||
|
||||
> Lambda 보다 빠른, region 가 가까운. **Cloudflare Workers (V8), Deno Deploy, Vercel Edge, AWS Lambda@Edge**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- V8 isolate: Lambda 보다 작은 (5ms cold start).
|
||||
- 200+ city deploy.
|
||||
- Web standard API (Fetch, Crypto).
|
||||
- Stateless (state 가 KV / D1).
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Cloudflare Workers
|
||||
```ts
|
||||
export default {
|
||||
async fetch(req: Request, env: Env, ctx: ExecutionContext) {
|
||||
return new Response('Hello from edge');
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
```bash
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
### Hono on Workers
|
||||
```ts
|
||||
import { Hono } from 'hono';
|
||||
|
||||
const app = new Hono();
|
||||
app.get('/', (c) => c.text('Hello'));
|
||||
app.get('/users/:id', (c) => c.json({ id: c.req.param('id') }));
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
→ Express 비슷. 작은 (10 KB).
|
||||
|
||||
### KV (Workers)
|
||||
```ts
|
||||
const value = await env.MY_KV.get('key');
|
||||
await env.MY_KV.put('key', JSON.stringify(data), { expirationTtl: 3600 });
|
||||
```
|
||||
|
||||
→ Eventually consistent. Edge 친화 (1 sec global propagation).
|
||||
|
||||
### D1 (SQLite at edge)
|
||||
```ts
|
||||
const r = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(id).first();
|
||||
```
|
||||
|
||||
→ SQLite. 최신 read 만 region (write 가 1 region).
|
||||
|
||||
### R2 (S3-compatible)
|
||||
```ts
|
||||
await env.MY_BUCKET.put('file.jpg', buffer);
|
||||
const obj = await env.MY_BUCKET.get('file.jpg');
|
||||
```
|
||||
|
||||
→ Egress 무료.
|
||||
|
||||
### Durable Objects (stateful)
|
||||
```ts
|
||||
export class Counter {
|
||||
state: DurableObjectState;
|
||||
|
||||
constructor(state: DurableObjectState) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
async fetch(req: Request) {
|
||||
let count = await this.state.storage.get<number>('count') ?? 0;
|
||||
count++;
|
||||
await this.state.storage.put('count', count);
|
||||
return new Response(String(count));
|
||||
}
|
||||
}
|
||||
|
||||
// Worker
|
||||
const id = env.COUNTER.idFromName('global');
|
||||
const obj = env.COUNTER.get(id);
|
||||
const r = await obj.fetch(req);
|
||||
```
|
||||
|
||||
→ Stateful + globally addressable. Chat / game.
|
||||
|
||||
### Hyperdrive (DB pool)
|
||||
```toml
|
||||
[[hyperdrive]]
|
||||
binding = 'HYPERDRIVE'
|
||||
id = '...'
|
||||
```
|
||||
|
||||
```ts
|
||||
import postgres from 'postgres';
|
||||
const sql = postgres(env.HYPERDRIVE.connectionString);
|
||||
const users = await sql`SELECT * FROM users`;
|
||||
```
|
||||
|
||||
→ Edge 가 Postgres 직접. Pool / cache 자동.
|
||||
|
||||
### Deno Deploy
|
||||
```ts
|
||||
Deno.serve((req) => new Response('Hello'));
|
||||
```
|
||||
|
||||
```bash
|
||||
deno deploy --project=my-app main.ts
|
||||
```
|
||||
|
||||
### Vercel Edge
|
||||
```ts
|
||||
// app/api/hello/route.ts
|
||||
export const runtime = 'edge';
|
||||
|
||||
export async function GET(req: Request) {
|
||||
return new Response('Hello');
|
||||
}
|
||||
```
|
||||
|
||||
### Bun
|
||||
```ts
|
||||
Bun.serve({
|
||||
port: 3000,
|
||||
fetch(req) {
|
||||
return new Response('Hello');
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
→ Self-host. Edge 가 아님 (single region).
|
||||
|
||||
### Limits
|
||||
```
|
||||
Cloudflare Workers:
|
||||
- 1 MB code (50 MB free plan).
|
||||
- 10 ms CPU (free), 30 sec (paid).
|
||||
- 128 MB memory.
|
||||
- No Node API (npm 일부).
|
||||
|
||||
Lambda@Edge:
|
||||
- 1 MB code.
|
||||
- 5 sec timeout.
|
||||
- 128 MB.
|
||||
|
||||
Deno Deploy:
|
||||
- 다른 limits.
|
||||
```
|
||||
|
||||
→ Lambda 보다 strict.
|
||||
|
||||
### Web standards
|
||||
```ts
|
||||
// fetch, crypto, URL, Headers, Response
|
||||
const r = await fetch(url);
|
||||
const hash = await crypto.subtle.digest('SHA-256', data);
|
||||
const u = new URL('https://example.com/path?x=1');
|
||||
```
|
||||
|
||||
→ Browser-compatible. Node 만 안 됨.
|
||||
|
||||
### Node compat (CF)
|
||||
```toml
|
||||
compatibility_flags = ['nodejs_compat']
|
||||
```
|
||||
|
||||
```ts
|
||||
import { readFile } from 'node:fs/promises';
|
||||
// → 일부 Node API.
|
||||
```
|
||||
|
||||
→ 점진적 추가.
|
||||
|
||||
### Cold start
|
||||
```
|
||||
Workers: 5 ms (V8 isolate).
|
||||
Lambda: 200-1000 ms (container).
|
||||
Deno Deploy: ~10 ms.
|
||||
|
||||
→ Edge 가 훨씬 빠름.
|
||||
```
|
||||
|
||||
### Deploy
|
||||
```bash
|
||||
# Workers
|
||||
wrangler deploy
|
||||
|
||||
# Vercel
|
||||
vercel --prod
|
||||
|
||||
# Deno
|
||||
deno deploy
|
||||
```
|
||||
|
||||
→ Atomic, global, secs.
|
||||
|
||||
### Routing
|
||||
```
|
||||
Cloudflare:
|
||||
my-app.example.com/* → worker.
|
||||
|
||||
Vercel:
|
||||
/api/* → edge function.
|
||||
```
|
||||
|
||||
### Observability
|
||||
```ts
|
||||
console.log('processed');
|
||||
// → Wrangler tail / Workers Analytics.
|
||||
```
|
||||
|
||||
→ Production 의 Datadog / Sentry 통합.
|
||||
|
||||
### Rate limit
|
||||
```ts
|
||||
// Cloudflare Rate Limiting Rule (Dashboard).
|
||||
// 또는 Worker:
|
||||
const ip = req.headers.get('cf-connecting-ip');
|
||||
const key = `rl:${ip}`;
|
||||
const count = await env.KV.get(key);
|
||||
if (Number(count) > 100) return new Response('Rate limit', { status: 429 });
|
||||
await env.KV.put(key, String(Number(count ?? 0) + 1), { expirationTtl: 60 });
|
||||
```
|
||||
|
||||
### Cron (Workers)
|
||||
```toml
|
||||
[triggers]
|
||||
crons = ['0 9 * * *']
|
||||
```
|
||||
|
||||
```ts
|
||||
export default {
|
||||
async scheduled(event, env, ctx) {
|
||||
await dailyJob();
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Queue (Workers)
|
||||
```ts
|
||||
// Producer
|
||||
await env.MY_QUEUE.send({ taskId: '...' });
|
||||
|
||||
// Consumer
|
||||
export default {
|
||||
async queue(batch, env) {
|
||||
for (const msg of batch.messages) {
|
||||
await process(msg.body);
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
→ Edge 의 message queue.
|
||||
|
||||
### WebSocket (Workers + DO)
|
||||
```ts
|
||||
class ChatRoom extends DurableObject {
|
||||
async fetch(req: Request) {
|
||||
if (req.headers.get('upgrade') === 'websocket') {
|
||||
const pair = new WebSocketPair();
|
||||
this.handleSession(pair[1]);
|
||||
return new Response(null, { status: 101, webSocket: pair[0] });
|
||||
}
|
||||
}
|
||||
|
||||
handleSession(ws: WebSocket) {
|
||||
ws.accept();
|
||||
ws.addEventListener('message', (e) => {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ Real-time chat at edge.
|
||||
|
||||
### Cost
|
||||
```
|
||||
Cloudflare Workers Free:
|
||||
- 100k req / day.
|
||||
- $5 / month for 10M.
|
||||
|
||||
Vercel Edge:
|
||||
- 1M / month free.
|
||||
- $20 / month after.
|
||||
|
||||
Deno Deploy:
|
||||
- 1M / month free.
|
||||
|
||||
→ Edge 가 traditional Lambda 보다 cheap (volume).
|
||||
```
|
||||
|
||||
### Use case
|
||||
```
|
||||
- API gateway / proxy.
|
||||
- Auth (verify JWT, OIDC).
|
||||
- Rate limit / WAF.
|
||||
- Image transform (CF Image, Vercel Image).
|
||||
- A/B test routing.
|
||||
- Static site (Pages).
|
||||
- Realtime (DO + WS).
|
||||
- AI inference (Cloudflare Workers AI).
|
||||
```
|
||||
|
||||
### Cloudflare Workers AI
|
||||
```ts
|
||||
const r = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
|
||||
prompt: 'Hello',
|
||||
});
|
||||
```
|
||||
|
||||
→ Edge 의 LLM. 작은 model.
|
||||
|
||||
### vs Lambda
|
||||
```
|
||||
Lambda: 더 mature, 큰 ecosystem, 모든 language.
|
||||
Edge: 빠름, 작은, 가벼운.
|
||||
|
||||
→ Heavy compute = Lambda.
|
||||
HTTP / 작은 = Edge.
|
||||
```
|
||||
|
||||
### Limits 의 함정
|
||||
```
|
||||
- Long task (>30 sec) = timeout.
|
||||
- Big bundle = limit.
|
||||
- Native module = 안 됨.
|
||||
- 큰 file (100 MB+) = R2.
|
||||
- Persistent connection = DO.
|
||||
```
|
||||
|
||||
### When?
|
||||
```
|
||||
✓ Global low-latency.
|
||||
✓ 작은 / simple logic.
|
||||
✓ Auth proxy.
|
||||
✓ A/B routing.
|
||||
|
||||
✗ Heavy compute (ML training).
|
||||
✗ Long-running (>5 min).
|
||||
✗ Complex Node app.
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 작업 | 추천 |
|
||||
|---|---|
|
||||
| Auth gateway | Cloudflare Workers |
|
||||
| Vercel + Next | Edge function |
|
||||
| Deno-native | Deno Deploy |
|
||||
| Stateful | DO |
|
||||
| DB pool at edge | Hyperdrive / Neon HTTP |
|
||||
| Local dev | wrangler dev / bun |
|
||||
| Heavy compute | Lambda / VM |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Big bundle**: limit.
|
||||
- **Long task**: timeout.
|
||||
- **Persistent connection (no DO)**: 깨짐.
|
||||
- **State in worker memory**: lost.
|
||||
- **Heavy compute**: timeout.
|
||||
- **Node-only API**: 안 됨.
|
||||
- **Per-request DB connection**: pool 폭발.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Cloudflare Workers + Hono = 가장 modern.
|
||||
- DO 가 stateful.
|
||||
- Hyperdrive 가 DB pool.
|
||||
- Web standards (fetch, crypto) only.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Backend_Edge_Functions]]
|
||||
- [[Backend_Hono_Modern]]
|
||||
- [[DB_Serverless_Edge]]
|
||||
Reference in New Issue
Block a user