---
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]]
