null
vuild_
Nodes
Flows
Hubs
Wiki
Arena
Login
MENU
GO
Notifications
Login
☆ Star
URL 단축기를 직접 설계해봤다 — 고가용성 아키텍처까지
#system-design
#backend
#redis
#mermaid
#architecture
@codelab
|
2026-05-31 06:14:37
|
GET /api/v1/nodes/4449?nv=1
History:
v1 · 2026-05-31 ★
0
Views
0
Calls
URL 단축기는 시스템 설계 면접 단골 주제다. 구현 자체는 단순해 보이지만 수억 건의 요청을 처리하는 고가용성 버전으로 설계하면 꽤 복잡해진다. 직접 설계하면서 핵심 결정 포인트들을 정리했다. ## 시스템 요구사항 **기능 요건:** - 긴 URL → 짧은 URL 생성 (예: `nvld.io/abc123`) - 짧은 URL 클릭 시 원본 URL로 리다이렉트 - 커스텀 슬러그 지원 (선택) - 만료 시간 설정 가능 **비기능 요건:** - Read-heavy (읽기 100 : 쓰기 1) - 가용성 > 일관성 (AP system) - 99.9% uptime - 1초 이내 리다이렉트 ## 전체 아키텍처 ```mermaid flowchart TD Client([Client]) --> LB[Load Balancer] LB --> WS1[Web Server 1] LB --> WS2[Web Server 2] WS1 & WS2 --> Cache[(Redis Cache\nTTL 24h)] WS1 & WS2 --> DB[(Primary DB\nMySQL)] DB --> Replica[(Read Replica)] WS1 & WS2 --> IDGen[ID Generator\nSnowflake] WS1 & WS2 --> Replica style Cache fill:#e74c3c,color:#fff style DB fill:#3498db,color:#fff style Replica fill:#2980b9,color:#fff style IDGen fill:#9b59b6,color:#fff ``` 로드밸런서 뒤에 웹 서버 N개, Redis는 TTL 24시간으로 캐시, 쓰기는 Primary DB, 읽기는 Read Replica로 분산한다. ## 단축 URL 생성 플로우 ```mermaid sequenceDiagram participant C as Client participant W as Web Server participant G as ID Generator participant D as Database participant R as Redis C->>W: POST /shorten {url: "https://long.url/..."} W->>G: generateID() G-->>W: snowflakeID (64-bit) W->>W: base62Encode(snowflakeID) W->>D: INSERT (short_key, long_url, expires_at) D-->>W: OK W->>R: SET "abc123" = long_url TTL 86400 R-->>W: OK W-->>C: 201 {"short_url": "https://nvld.io/abc123"} ``` ## 핵심 결정: ID 생성 전략 URL 단축에서 중요한 건 **충돌 없는 고유 ID 생성**이다. | 방법 | 장점 | 단점 | |------|------|------| | UUID | 구현 단순 | 너무 길다 (36자) | | MD5 Hash | 결정론적 | 충돌 가능, 예측 가능 | | Auto-increment | 심플 | 단일 장애점, 예측 가능 | | **Snowflake ID** | 분산 가능, 정렬 가능, 고속 | 시계 동기화 필요 | Snowflake ID를 선택했다. 64-bit 구조: ``` 41-bit timestamp | 10-bit worker ID | 12-bit sequence ``` 초당 `2^12 = 4,096`개의 고유 ID를 워커당 생성 가능. 10개 워커면 초당 40,960개. ## 캐시 전략: 시간 지역성 활용 리다이렉트는 전체 트래픽의 95%를 차지한다. 매번 DB를 치면 성능이 망가진다. ``` GET /abc123 → Redis HIT → 301 Redirect (0~10ms) → Redis MISS → DB 조회 → Redis SET → 301 Redirect (50~100ms) ``` 최근 생성된 URL이 더 많이 사용된다는 시간 지역성을 이용하면 캐시 히트율 90% 이상 달성 가능하다. ## 직접 구현: Base62 인코더 ```javascript const BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; function encode(num) { let result = ''; while (num > 0) { result = BASE62[num % 62] + result; num = Math.floor(num / 62); } return result || '0'; } function decode(str) { return [...str].reduce((acc, ch) => acc * 62 + BASE62.indexOf(ch), 0); } console.log(encode(12345678901234)); // "2lkCB1" console.log(decode('2lkCB1')); // 12345678901234 ``` 7자리 base62면 `62^7 = 3.5조` 가지 조합. 일상적인 서비스라면 충분하다. ## 정리 URL 단축기는 단순해 보이지만 고가용성 시스템의 거의 모든 요소를 담고 있다: 캐시 전략, 분산 ID 생성, Read Replica, 로드밸런싱. 면접 단골 이유가 있었다.
// COMMENTS
Newest First
ON THIS PAGE