시스템 설계 초보자를 위한: 2026년 URL Shortener 설계 방법
Source: Dev.to
시스템 설계 인터뷰는 대부분의 개발자를 겁에 질리게 합니다. 질문은 개방형이고, 범위는 방대하며, 단 하나의 “정답”이 없습니다.
하지만 비밀은 이것입니다: 모든 시스템 설계 질문은 동일한 프레임워크를 따릅니다. 간단한 예시를 통해 이를 배우면, 어떤 주제에도 적용할 수 있습니다.
우리는 가장 고전적인 시스템 설계 질문인 URL 단축기를 학습 도구로 사용할 것입니다.
시스템 디자인이란?
시스템 디자인은 주어진 요구사항을 충족하기 위해 시스템의 아키텍처, 구성 요소, 모듈, 인터페이스 및 데이터 흐름을 정의하는 과정입니다.
면접에서 종종 다음과 같은 시스템을 설계하도록 요청받습니다:
- URL 단축기 (bit.ly)
- Twitter/X 타임라인
- YouTube
- Uber의 라이드 디스패치
- Netflix 비디오 스트리밍
The System‑Design Framework (6 Steps)
1. Clarify Requirements
2. Estimate Scale
3. Define the API
4. Design the Data Model
5. Draw the High‑Level Architecture
6. Deep Dive into Components
URL 단축 서비스를 예시로 각 단계를 적용해 보겠습니다.
Step 1: Clarify Requirements
Functional requirements (시스템이 수행해야 하는 기능)
- 긴 URL을 받아 짧은 URL을 생성한다 (예:
bit.ly/abc123). - 짧은 URL을 받아 원본 긴 URL로 리다이렉트한다.
- 짧은 URL은 설정 가능한 시간 이후에 만료될 수 있다 (선택 사항).
- 사용자는 커스텀 짧은 URL을 만들 수 있다 (선택 사항).
Non‑functional requirements (시스템이 얼마나 잘 수행해야 하는지)
- High availability – 99.9 % 이상의 가용성 유지.
- Low latency – 리다이렉트는
Takeaway: 이 시스템은 읽기 요청이 많으며, aggressive caching이 필요하고 저장소 요구량은 중간 수준입니다.
Step 3: Define the API
POST /api/v1/urls
Request:
{
"longUrl": "https://example.com/very/long/path",
"expiresAt": "2027-01-01"
}
Response:
{
"shortUrl": "https://bit.ly/abc123",
"expiresAt": "2027-01-01"
}
GET /{shortCode}
Response: 301 Redirect to longUrl
DELETE /api/v1/urls/{shortCode}
Response: 200 OK
301 vs 302 redirect
- 301 (Permanent) – 브라우저가 리다이렉트를 캐시 → 서버 요청 감소 → 비용 절감.
- 302 (Temporary) – 브라우저가 항상 서버에 요청 → 보다 정확한 분석 가능.
제품 요구에 맞게 선택합니다.
Step 4: Design the Data Model
Table schema (SQL example)
CREATE TABLE urls (
short_code VARCHAR(7) PRIMARY KEY,
long_url VARCHAR(2048) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP,
user_id BIGINT, -- nullable for anonymous users
click_count BIGINT DEFAULT 0
);
CREATE INDEX idx_short_code ON urls(short_code);
short_code가 기본 키입니다. 아래는 흔히 사용되는 세 가지 생성 전략입니다.
Short‑Code Generation Strategies
Option 1 – Hash + Truncate
import hashlib
def generate_short_code(long_url: str) -> str:
hash_value = hashlib.md5(long_url.encode()).hexdigest()
return hash_value[:7] # first 7 chars of MD5
문제점: 해시 충돌 가능성 (다른 URL이 동일한 짧은 코드가 됨).
Option 2 – Base62 Encoding of a Counter
BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode_base62(num: int) -> str:
if num == 0:
return BASE62[0] * 7
result = []
while num > 0:
result.append(BASE62[num % 62])
num //= 62
return ''.join(reversed(result)).zfill(7)
동작 방식: DB에서 카운터를 원자적으로 증가시킨 뒤 인코딩. 고유성을 보장합니다. ✅
Option 3 – Pre‑generated Keys (Key‑Generation Service)
keys_available테이블에 수백만 개의 고유 7자 문자열을 미리 채워 둡니다.- 짧은 URL이 필요할 때 키를 하나 가져와
keys_used로 이동합니다. - 충돌 위험을 완전히 없앨 수 있습니다.
Step 5: High‑Level Architecture
[Client]
│
▼
[Load Balancer]
│
├─── [Write Service] ──── [Primary DB]
│ │
└─── [Read Service] ──── [Redis Cache] ──── [Read‑Replica DB]
Write Path
- 클라이언트가 긴 URL을 POST.
- Write 서비스가 짧은 코드를 생성 (Base62 카운터 또는 KGS).
- 매핑을 Primary DB에 저장.
- 짧은 URL을 클라이언트에 반환.
Read Path
- 클라이언트가
/{shortCode}로 GET. - Read 서비스가 먼저
Redis캐시를 조회 (≈ 99 % 캐시 적중). - 캐시 미스 시, Read‑Replica DB에 질의.
- 301/302 리다이렉트를 수행.
Step 6: Deep Dive – Caching Strategy
읽기 요청이 초당 ~116 K 정도라면 DB만으로는 감당하기 어려우므로 Redis가 필수입니다.
import redis
cache = redis.Redis(host='localhost', port=6379)
CACHE_TTL = 3600 # 1 hour
def get_long_url(short_code: str) -> str | None:
# 1️⃣ Try cache first
cached = cache.get(f"url:{short_code}")
if cached:
return cached.decode()
# 2️⃣ Cac
he miss → query DB
long_url = db.query(
"SELECT long_url FROM urls WHERE short_code = %s", short_code
)
# 3️⃣ Populate cache for future hits
if long_url:
cache.setex(f"url:{short_code}", CACHE_TTL, long_url)
return long_url
Cache eviction policy: LRU (Least Recently Used). 80/20 규칙에 따르면, 전체 URL 중 약 20 %가 전체 트래픽의 약 80 %를 차지합니다 – 이 뜨거운 20 %를 캐시하세요.
Common Follow‑Up Questions
Q: How do you handle expired URLs?
- Lazy deletion: On read, check
expires_at; if past, return 410 Gone. - Background job: Periodically scan the table and delete expired rows (or move them to an archive).
Q: How would you support custom aliases?
- Validate the requested alias for uniqueness and profanity.
- Store it as
short_codedirectly; if a conflict occurs, return an error.
Q: How can you make the system globally distributed?
- Deploy read replicas and Redis caches in multiple regions.
- Use a geo‑DNS or anycast load balancer to route users to the nearest region.
- Employ a consistent‑hashing scheme for key generation to avoid collisions across data‑centers.
Q: How would you collect analytics (click counts, referrers, etc.)?
- Increment a
click_countcolumn asynchronously via a message queue (e.g., Kafka). - Store detailed events in a time‑series DB or data warehouse for later analysis.
Q: What if the short‑code space (7 chars, Base62) runs out?
- Switch to 8‑character codes (62⁸ ≈ 2.18 × 10¹⁴ possibilities).
- Or recycle unused/expired codes after a safe grace period.
TL;DR
- Clarify functional & non‑functional requirements.
- Estimate traffic, storage, and latency needs.
- Define a clean, versioned API.
- Model the data (URL ↔ short code) and decide on a generation strategy.
- Sketch a high‑level architecture with separate write/read services, a primary DB, read replicas, and a Redis cache.
- Dive into critical components (caching, expiration handling, analytics, global distribution).
Follow this framework and you’ll be able to tackle any system‑design interview – starting with the classic URL shortener. 🚀
ed URLs nightly
Q: 어떻게 남용을 방지합니까?
- IP당 속도 제한 (Redis 슬라이딩‑윈도우 카운터)
- 악성 URL 차단 (Safe Browsing API)
- 익명 사용자에 대한 CAPTCHA
Q: 어떻게 하루 10억 요청을 처리합니까?
- 읽기 복제본 추가
- 지리적 분산 (리다이렉트를 위한 CDN)
- 분산 캐시를 위한 일관성 해싱
Q: 사용자 정의 짧은 코드?
- 저장하기 전에 고유성 확인
- 예약/사용자 정의 코드를 위한 별도 테이블
면접관이 실제로 평가하는 것
- Communication – 생각을 명확히 설명할 수 있나요?
- Trade‑offs – 각 결정이 왜 내려졌는지 이해하고 있나요?
- Scale awareness – 구축하기 전에 추정합니까?
- Depth – 질문받을 때 최소 하나의 구성 요소에 대해 깊게 파고들 수 있나요?
완벽한 답변이 필요하지 않습니다. 대화를 주도하고, 합리적인 선택을 하며, 그 이유를 설명하는 것이 필요합니다.
연습 문제 (난이도 상승)
- URL Shortener (이 기사) – 여기서 시작
- Pastebin – 텍스트 공유
- Rate Limiter – 클래식 API 설계
- Twitter Timeline – 팬아웃 문제
- Design YouTube – 비디오 저장 + 스트리밍
- Design Uber – 실시간 매칭
시스템 디자인을 공부하면서 프리랜스 프로젝트와 클라이언트를 관리하고 있나요?
Freelancer OS 은 모든 것을 정리해 줍니다 — CRM, 프로젝트, 수입, 모두 Notion에. €19 일회성.