167 lines
4.6 KiB
Markdown
167 lines
4.6 KiB
Markdown
---
|
|
id: backend-geo-replication
|
|
title: Geo Replication — Multi-region / CDN / Edge
|
|
category: Coding
|
|
status: draft
|
|
source_trust_level: B
|
|
verification_status: conceptual
|
|
created_at: 2026-05-09
|
|
updated_at: 2026-05-09
|
|
tags: [backend, geo, multi-region, edge, vibe-coding]
|
|
tech_stack: { language: "TS / Cloudflare / AWS", applicable_to: ["Backend"] }
|
|
applied_in: []
|
|
aliases: [multi-region, geo-replicate, latency, CDN, edge function, R2, D1]
|
|
---
|
|
|
|
# Geo Replication
|
|
|
|
> 사용자 가까이 = 빠름. **CDN (정적) → Edge function (동적, low-latency) → DB (가장 어려움)**. Read replica 글로벌 분산. Write 는 보통 single region.
|
|
|
|
## 📖 핵심 개념
|
|
- Latency 기여도: DB > network round-trip > CDN miss.
|
|
- CDN: 정적 파일 — Cloudflare / Cloudfront.
|
|
- Edge function: 50+ region 에서 코드 실행 — Workers / Lambda@Edge.
|
|
- Multi-region DB: read replica / global DB (Spanner / CockroachDB).
|
|
|
|
## 💻 코드 패턴
|
|
|
|
### CDN cache
|
|
```
|
|
Static assets (JS / image / font)
|
|
→ Cloudflare / Cloudfront / Vercel Edge Network
|
|
→ 매 region cache, 자동.
|
|
```
|
|
|
|
```ts
|
|
// Cache-Control 강력
|
|
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
|
// hash filename (app.abc123.js) → immutable safe
|
|
```
|
|
|
|
### Edge function
|
|
```ts
|
|
// Cloudflare Worker
|
|
export default {
|
|
async fetch(req: Request, env: Env): Promise<Response> {
|
|
const url = new URL(req.url);
|
|
|
|
// KV (read replica 가까이)
|
|
const cached = await env.CACHE.get(url.pathname);
|
|
if (cached) return new Response(cached, { headers: { 'cache-control': 'max-age=60' } });
|
|
|
|
// Origin 으로 fetch (US east 가정)
|
|
const r = await fetch(`https://origin.example.com${url.pathname}`);
|
|
const text = await r.text();
|
|
await env.CACHE.put(url.pathname, text, { expirationTtl: 60 });
|
|
return new Response(text);
|
|
},
|
|
};
|
|
```
|
|
|
|
### Cloudflare D1 / Durable Objects (글로벌 SQLite)
|
|
```ts
|
|
const r = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(id).first();
|
|
// D1 = read replica 글로벌, write 는 primary
|
|
```
|
|
|
|
### Read replica 글로벌 (RDS / Aurora)
|
|
```
|
|
Primary (us-east-1)
|
|
├── Read replica (eu-west-1)
|
|
└── Read replica (ap-northeast-1)
|
|
```
|
|
|
|
```ts
|
|
function getReader(region: string): Pool {
|
|
if (region === 'eu') return euReader;
|
|
if (region === 'asia') return asiaReader;
|
|
return usReader;
|
|
}
|
|
|
|
const region = req.cf?.continent ?? 'NA';
|
|
const r = await getReader(region).query('SELECT ...');
|
|
```
|
|
|
|
⚠️ Replication lag — 강 일관성 필요한 read 는 primary.
|
|
|
|
### Global DB (Spanner / CockroachDB / YugabyteDB)
|
|
```sql
|
|
-- 자동 멀티 region replication
|
|
-- Write 도 가까운 region 에서 가능
|
|
CREATE TABLE orders (...) LOCALITY REGIONAL BY ROW;
|
|
```
|
|
|
|
비싸고 복잡. Truly global business 에만.
|
|
|
|
### User-affinity routing
|
|
```ts
|
|
// 사용자가 EU 면 EU region 으로 항상
|
|
function regionFor(user: User): Region {
|
|
return user.dataResidency ?? geoip(user.ip);
|
|
}
|
|
|
|
// DNS GeoDNS 또는 Anycast LB
|
|
```
|
|
|
|
### GDPR — 데이터 거주
|
|
```
|
|
EU 사용자 → EU region DB only
|
|
다른 region 으로 절대 복제 X
|
|
```
|
|
|
|
```ts
|
|
// Per-tenant region
|
|
const dbForTenant = (t: Tenant) => pools[t.region];
|
|
```
|
|
|
|
### Backup geo
|
|
```bash
|
|
# Cross-region backup (재해 대비)
|
|
aws s3 sync s3://primary-backup s3://eu-backup --source-region us-east-1 --region eu-west-1
|
|
```
|
|
|
|
### Static + dynamic 분리 (Vercel-style)
|
|
```
|
|
/api/* → 가장 가까운 edge function
|
|
/_next/static → CDN
|
|
/data → 동적 → primary region
|
|
```
|
|
|
|
### Latency-based routing
|
|
```
|
|
Route 53 latency-based
|
|
또는 Cloudflare Argo
|
|
```
|
|
|
|
자동으로 가까운 endpoint 로.
|
|
|
|
## 🤔 의사결정 기준
|
|
| 상황 | 추천 |
|
|
|---|---|
|
|
| 정적 사이트 | CDN 만 |
|
|
| Mostly read API | Edge function + CDN |
|
|
| 글로벌 사용자 + 동적 | Edge + global read replica |
|
|
| Strong consistency 글로벌 | Spanner / CockroachDB |
|
|
| GDPR 거주 | Region-pinned DB |
|
|
| 작은 / 시작 | Single region OK |
|
|
|
|
## ❌ 안티패턴
|
|
- **Single region prod + 글로벌 사용자**: 200ms+ latency.
|
|
- **Cross-region transaction 매번**: 큰 latency.
|
|
- **Replica lag 모니터링 없음**: 분 단위 stale.
|
|
- **CDN 안 써서 정적 매번 origin**: 비싸고 느림.
|
|
- **Edge function 에 DB 직접 connect (TCP)**: 매 invocation 소켓. HTTP / connection pool.
|
|
- **GDPR 무시 — 글로벌 복제**: 법적 위반.
|
|
- **Failover 안 테스트**: 진짜 disaster 시 작동 X.
|
|
|
|
## 🤖 LLM 활용 힌트
|
|
- 정적 = CDN 자동.
|
|
- 동적 read = edge + KV / read replica.
|
|
- Write = primary 만 + outbox.
|
|
- GDPR 시작부터 region-pin.
|
|
|
|
## 🔗 관련 문서
|
|
- [[DB_Read_Replica_Patterns]]
|
|
- [[Backend_API_Gateway_BFF]]
|
|
- [[Web_HTTP_Cache_Headers]]
|