๊ธด URL์ ์งง์ ๋งํฌ๋ก ๋ณํํ๋ URL ๋จ์ถ ์๋น์ค
๋ฌธ์ ์ ์ ํ
URL์ด ๊ณต๊ฐ์ ๋ง์ด ์ฐจ์งํ๋ ๋ฌธ์ ํด๊ฒฐ๊ฐ๋ ์ฑ ํฅ์
URL ์์ฑ๋งํฌ ๊ด๋ฆฌ
URL ํด๋ฆญ ์, ์ง์ญ, ์๊ฐ ๋ฑ ๋ถ์ ๊ฐ๋ฅ๋ณด์ ๋ฐ ์ ๋ขฐ
URL์ ์จ๊ฒจ ์คํธ์ด๋ ์
์ฑ ๋งํฌ ์ฐจ๋จURL ๋จ์ถ
URL ์
๋ ฅ โ ์งง์ URL ์์ฑhttps://www.example.com/very/long/url/path โ bit.ly/abc123์ปค์คํ ์ต์
URL ๋ง๋ฃ ๊ธฐํ ์ค์ ๊ฐ๋ฅ๋ฆฌ๋๋ ์
URL ์ ์ ์ ์๋ณธ URL๋ก ์๋ ์ด๋URL: 10์ต ๊ฐ (10B)๊ฐ์
๊ณ์ฐ
์ด request = 10^8 ร 10 = 10^9 request/์ผ
์ฐ๊ธฐ TPS = (10^9 ร 0.1) / 10^5 = 1,000 TPS
์ฝ๊ธฐ TPS = (10^9 ร 0.9) / 10^5 = 9,000 TPS
๐ก Base64์
+์/๋ URL์์ ํน์ ๋ฌธ์๋ก ์ฌ์ฉ๋๋ฏ๋ก ์ ์ธํ 62๊ฐ ๋ฌธ์(0-9, a-z, A-Z) Base62๋ก ์ธ์ฝ๋ฉํฉ๋๋ค.
๋จ์ถ URL ์ ์ฅ
๋ฉํ๋ฐ์ดํฐ ํฌํจ
| ํญ๋ชฉ | ๊ฐ |
|---|---|
| ์ฐ๊ธฐ TPS | 1,000 TPS |
| ์ฝ๊ธฐ TPS | 9,000 TPS |
| ๋จ์ถ URL ์ ์ฅ | 6GB |
| ์ด ์ ์ฅ์ฉ๋ | 200GB |
๋ถ์ฐ ์ฒ๋ฆฌ
CAP ์ ๋ฆฌ
| ์์ ์ ํ | ๋ชฉํ ์๋ต ์๊ฐ |
|---|---|
| ๋ฆฌ๋๋ ์ (์ฝ๊ธฐ) | < 100ms |
| URL ์์ฑ (์ฐ๊ธฐ) | < 200ms |
P99 Latency
| Column | Type | Description | Constraints |
|---|---|---|---|
| id | BIGINT | Primary key, auto-increment | PRIMARY KEY |
| short_url | VARCHAR(10) | Shortened URL code (e.g., "abc123") | UNIQUE, NOT NULL, INDEX |
| original_url | TEXT | Original long URL | NOT NULL |
| user_id | BIGINT | User who created the URL | NULLABLE, INDEX |
| created_at | TIMESTAMP | Creation timestamp | NOT NULL, DEFAULT NOW() |
| expires_at | TIMESTAMP | Expiration timestamp | NULLABLE, INDEX |
| click_count | INT | Number of times accessed | DEFAULT 0 |
| is_deleted | BOOLEAN | Whether URL is deleted | DEFAULT FALSE |
| is_custom | BOOLEAN | Whether short_code is custom | DEFAULT FALSE |
POST /api/urls
Content-Type: application/json
Request
{
"original_url": "https://www.example.com/very/long/url/path",
"alias": "my-custom-link", // optional
"expires_at": "2025-12-31" // optional
}
Response
201 Created
{
"short_url": "https://bit.ly/abc123",
"original_url": "https://www.example.com/very/long/url/path",
"expires_at": "2025-12-31T23:59:59Z"
}
GET /{short_code}
Response
302 Found
Location: https://www.example.com/very/long/url/path
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/urls | Create shortened URL |
| GET | /{short_code} | Redirect to original URL |

์ฒ์ ์ค๊ณํ ์์คํ
๊ตฌ์กฐ๋ ์์ ๊ฐ์ต๋๋ค. ๊ธฐ๋ณธ์ ์ธ Load Balancer, API Servers, Redis Cache, ๊ทธ๋ฆฌ๊ณ Database๋ก ๊ตฌ์ฑ๋์ด ์์ต๋๋ค.
์ด ๊ตฌ์กฐ์์๋:
Load Balancer๊ฐ ํธ๋ํฝ์ API Server๋ค๋ก ๋ถ์ฐ,API Server๊ฐ Redis Cache์ Database๋ฅผ ์ง์ ์ฐธ์กฐ,์ ๊ตฌ์กฐ์์๋ ์ถฉ๋ถํ ํธ๋ํฝ์ ๊ฐ๋นํ ์ ์๊ฒ ์ง๋ง ๋ณด๋ค ๋ ๋ช ํํ๊ฒ ์ฝ๊ธฐ/์ฐ๊ธฐ๋ผ๋ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌ ๋ ๋ ๋ฆฝ์ ์ผ๋ก ํ์ฅํ๊ธฐ ์ํด์๋ ์๋์ ๊ฐ์ด ๊ฐ์ ๋ ์ ์์ ๊ฒ์ ๋๋ค:

๋ฐ์ ๋ ๊ตฌ์กฐ์ ํต์ฌ ๊ฐ์ ์ฌํญ:
Primary DB(์ฐ๊ธฐ)์ Read Replicas(์ฝ๊ธฐ) ๋ถ๋ฆฌ๋ก DB ๋ถํ ๋ถ์ฐRead Server๊ฐ Redis์ Read Replicas๋ฅผ ํจ์จ์ ์ผ๋ก ํ์ฉ์ญํ
SSL ์ข
๋ฃ ์ฒ๋ฆฌ๋ผ์ฐํ
POST /api/urls โ Write Servers
GET /{shortCode} โ Read Servers
Write Servers (์ฐ๊ธฐ ์๋ฒ)
URL ์์ฑ ์์ฒญ ์ฒ๋ฆฌPrimary DB์ ๋ฐ์ดํฐ ์ ์ฅRead Servers (์ฝ๊ธฐ ์๋ฒ)
Redis ์บ์ ์ฐ์ ์กฐํ์ญํ
Hot URLs (์์ฃผ ์ ๊ทผ๋๋ URL) ์บ์ฑ90%+ ์์ฒญ์ 1-5ms์ ์ฒ๋ฆฌDB ๋ถํ ๋ํญ ๊ฐ์์บ์ ์ ๋ต
Key: url:{short_code}
Value: {original_url}
TTL: 24์๊ฐ
Primary DB (Master)
Read Replicas (์ฝ๊ธฐ ๋ณต์ ๋ณธ)
Cache miss ์ ์ฝ๊ธฐ ์ฒ๋ฆฌPrimary DB ๋ถํ ๋ถ์ฐ1. Client โ Load Balancer
2. Load Balancer โ Read Server
3. Read Server โ Redis Cache
โโ Cache Hit โ ์ฆ์ ์๋ต (1-5ms)
โโ Cache Miss โ Read Replica ์กฐํ (50ms)
4. ์๋ณธ URL๋ก 302 ๋ฆฌ๋๋ ์
1. Client โ Load Balancer
2. Load Balancer โ Write Server
3. Write Server โ Primary DB ์ ์ฅ
4. Write Server โ Redis Cache ์ ์ฅ (์ ํ)
5. Primary DB โ Read Replicas ๋ณต์ (๋น๋๊ธฐ)
6. ๋จ์ถ URL ์๋ต
์บ์ฑ
Redis์์ ์ฒ๋ฆฌDB Replication
Replica๋ก ๋ถ์ฐ๋ก๋ ๋ฐธ๋ฐ์ฑ
์ํ ํ์ฅ
API ์๋ฒ ์ถ๊ฐ๋ก TPS ์ฆ๊ฐRead Replica ์ถ๊ฐ๋ก ์ฝ๊ธฐ ์ฑ๋ฅ ํฅ์๊ณ ๊ฐ์ฉ์ฑ
์ฑ๋ฅ
DB ๋ถํ ์ต์ํํ๋ก์ธ์ค
์๋ณธ URL
โ
Hash ํจ์ (MD5/SHA256)
โ
์ X์ ์ถ์ถ
โ
16์ง์ โ 10์ง์ ๋ณํ
โ
Base62 ์ธ์ฝ๋ฉ
โ
์ถฉ๋ ๊ฒ์ฌ
โโ ์ถฉ๋ ์์ โ ์ ์ฅ
โโ ์ถฉ๋ ๋ฐ์ โ ๋๋ค ๊ฐ ์ถ๊ฐ ํ ์ฌ์๋
์ฅ์
โ
๊ฐ์ URL์ ํญ์ ๊ฐ์ ๋จ์ถ ์ฝ๋
โ
๋ถ์ฐ ํ๊ฒฝ์ ์ ํฉ
โ
๊ตฌํ ๊ฐ๋จ
๋จ์
โ ์ถฉ๋ ๊ฐ๋ฅ์ฑ ์กด์ฌ
โ ์ถฉ๋ ์ ์ฌ์๋ ํ์
โ ์์ธก ๋ถ๊ฐ๋ฅํ ์๋ต ์๊ฐ
ํ๋ก์ธ์ค
์์ฒญ
โ
Counter์์ ๊ณ ์ ID ๋ฐ๊ธ
โ
Base62 ์ธ์ฝ๋ฉ
โ
์ ์ฅ (์ถฉ๋ ์์!)
์ฑ๋ฅ
Redis INCR ์ฑ๋ฅ: 100,000+ ops/sec
10,000 TPS ์๊ตฌ์ฌํญ: โ
์ถฉ๋ถํ ๊ฐ๋ฅ
์ฅ์
โ
์ถฉ๋ ์ ๋ ์์
โ
๋น ๋ฅธ ์ฑ๋ฅ
โ
์์ธก ๊ฐ๋ฅํ ์๋ต ์๊ฐ
๋จ์
โ ๏ธ ์์ฐจ์ ์ด๋ผ ์์ธก ๊ฐ๋ฅ
โ ๏ธ Redis๊ฐ ๋จ์ผ ์ฅ์ ์ (Redis Cluster๋ก ํด๊ฒฐ๊ฐ๋ฅ)
ID ๋ฐฐ์น๋ก ๋ฐ์์ฌ ๋์ Trade-off
๐ ์ฑ๋ฅ ๊ฐ์
Redis ํธ์ถ 1/N ๊ฐ์
๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ์์ ์ฆ์ ํ ๋น
๐ ๋น์ฉ
ID ๋ญ๋น (์๋ฒ ์ฅ์ ์ ๋ฏธ์ฌ์ฉ ID ์์ค)
๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐ
ID ์์์ Gap ๋ฐ์
โ๏ธ ๋ฐฐ์น ํฌ๊ธฐ๊ฐ ํต์ฌ: 100~1000๊ฐ ๊ถ์ฅ
๊ฐ์ ๋ฉด์ ์ฌ๋ก๋ก ๋ฐฐ์ฐ๋ ๋๊ท๋ชจ ์์คํ
์ค๊ณ ๊ธฐ์ด
Hello Interview