URL Shortener와 Rate Limiter로 보는 시스템 디자인
URL Shortener와 Rate Limiter를 통해 시스템 디자인이 정답 컴포넌트 암기가 아니라 제약과 트레이드오프를 다루는 절차임을 정리합니다. 코드 생성, 제한 알고리즘, 저장소 역할, 운영 함정을 비교하며 두 케이스가 어떻게 다르게 풀리는지 살펴봅니다.
Script Companion
오디오와 함께 스크립트 보기
- 01
시스템 디자인에서 중요한 것은 정답을 맞히는 일이 아니라, 제약을 분해하고 트레이드오프를 설명하는 일입니다. Redis로 캐시하면 된다는 말만으로는 부족합니다. 캐시 미스율, hot key, 데이터 영속성, 비용 제약 안에서 왜 Redis인지 정량으로 말할 수 있어야 합니다. URL Shortener와 Rate Limiter는 작아 보이지만, 실제로는 scale, throughput, 일관성, 비용이 서로 다르게 얽히는 대표 케이스입니다.
- 02
이 문서가 강조하는 방식은 특정 제품 레시피가 아니라 6단계 분해 절차입니다. 먼저 요구사항을 좁히고, 다음으로 20,000 redirect QPS, 6B URL, p99 5ms 미만 같은 용량 수치를 세웁니다. 그 뒤 API와 모델, 아키텍처, 확장 전략, 운영 함정을 순서대로 검토합니다. 핵심은 각 결정 옆에 언제 깨지는가를 붙이는 습관입니다. 새 문제를 만나도 같은 절차로 다른 답을 만들 수 있어야 합니다.
- 03
URL Shortener에서 첫 번째 핵심 결정은 단축 코드 생성 방식입니다. auto-increment id를 Base62로 바꾸면 충돌은 없지만, 공개 코드가 순차적으로 노출되어 다른 사용자의 URL을 열거하기 쉬워집니다. 또한 단일 DB sequence가 write path의 중앙 병목이 될 수 있습니다. 그래서 문서는 Snowflake와 Base62 조합을 기본값으로 둡니다. Snowflake는 중앙 sequence 병목을 없애는 분산 친화 메커니즘이고, public code는 HMAC 같은 방식으로 난독화해 열거 공격을 막아야 합니다.
- 04
URL Shortener의 또 다른 축은 저장소와 읽기 부하입니다. 이 케이스는 read:write 100:1인 read-heavy 시스템으로 다뤄집니다. Redis는 부속 캐시 역할을 맡고, PG가 primary storage로 남아야 합니다. Redis만 두고 PG 없이 가는 방식은 Cache as primary의 함정입니다. Redis는 캐시이지 URL의 진짜 영속 저장소가 아니며, AOF 복구가 가능해도 불완전하고 메모리 가격이 디스크보다 훨씬 비싸다는 점이 문서의 경고입니다.
- 05
Rate Limiter에서는 질문이 달라집니다. 여기서 핵심 결정은 저장 코드가 아니라 제한 알고리즘입니다. Sliding Window Counter는 API 기본 선택지로 제시되고, Token Bucket은 burst를 허용해야 할 때 선택할 수 있습니다. 여러 gateway가 같은 키를 갱신하는 분산 환경에서는 Redis Lua script가 원자성을 확보하는 메커니즘입니다. Redis를 붙였더라도 GET, 계산, SET을 분리하면 동시 요청 사이 race가 생기므로, 컴포넌트 이름보다 갱신 방식이 중요합니다.
- 06
Rate Limiter의 실패 모드는 작은 구현 차이에서 바로 드러납니다. API 서버별 in-memory counter를 쓰면 노드가 10개가 되는 순간 사용자당 한도가 최대 10배까지 새어 나갈 수 있습니다. RFC 6585가 429 Too Many Requests를 정의하더라도, 대량 공격 상황에서는 429를 하나하나 반환하는 것 자체가 자원을 소모할 수 있습니다. 그래서 연결 drop 같은 대안이 나을 수 있다는 경고가 붙습니다. fail-open과 fail-closed도 의식적으로 정해야 하는 운영 결정입니다.
- 07
운영 함정은 두 케이스를 가로질러 반복됩니다. 첫째, 용량 추정을 감으로 하면 이후 sharding 결정까지 모두 흔들립니다. 월 100M URL 같은 수치는 DAU, 사용자당 URL 수, 30일 같은 기반 수치로 설명되어야 하고, 운영 후에는 실측과 추정을 매월 비교해야 합니다. 실측이 추정에서 30% 이상 벗어나면 capacity plan을 다시 계산해야 합니다. 둘째, 10B row도 안 되었는데 sharding부터 도입하면 re-balancing과 cross-shard query 문제가 당장의 이득보다 커집니다.
- 08
정리하면 두 케이스의 차이가 시스템 디자인의 패턴을 보여줍니다. URL Shortener는 영속 데이터, read-heavy 부하, DB primary 구조가 중심이고, Rate Limiter는 휘발성 제한 상태, 비교적 균형 잡힌 요청 처리, Redis primary 역할이 중심입니다. 같은 Redis라도 한쪽에서는 부속 캐시이고, 다른 한쪽에서는 의도된 primary 역할입니다. 그래서 같은 6단계를 적용하더라도, 코드 생성, 알고리즘, 저장소 역할, 실패 대응은 문제의 제약에 따라 다르게 결정됩니다.
같은 레이어
L9에서 이어 듣기
- 설계 원칙을 운영 가능한 코드로 잇기 길이 미정
- Clean Architecture의 의존성 규칙 길이 미정
- DDD 기본기: 도메인 언어와 경계 설계 길이 미정
- Twelve-Factor App 운영 원칙 길이 미정
- CAP과 일관성으로 보는 분산 시스템 선택 길이 미정
- MSA 패턴, 분리의 이득과 운영 비용 길이 미정
- Saga Pattern: 로컬 커밋과 역순 보상 길이 미정
- CQRS와 이벤트 소싱의 운영 경계 길이 미정
- TDD와 테스트 피라미드로 설계하는 테스트 전략 길이 미정
- 대규모 웹 크롤러의 큐, 정중함, 중복 제거 길이 미정
- API 계약으로 안전하게 서비스 경계를 진화시키기 길이 미정