Designing a Shorten URL System - URL 단축 서비스 시스템 디자인
What is a Short URL service?
문제 이해하기 - Understanding the Problem
‘문제 이해하기’ 단계는 본격적으로 설계를 하기 전에 서비스가 어떤 목적으로 설계되어야 하고 중점이 되어야 하는지를 이해하는 단계 입니다.
What is a Short URL service?
Bit.ly와 같은 URL 단축 서비스는 긴 URL을 짧고 공유하기 쉬운 링크로 변환 해줍니다. 이 짧은 링크는 사용자를 원래의 긴 주소로 리다이렉션하며, 필요에 따라 만료 기간이나 사용자 지정 별칭(alias) 기능도 제공합니다.
이 문제는 시스템 디자인 인터뷰에서 가장 자주 나오는 초급 문제 중 하나로 시스템 디자인 컴포넌트에 대한 이해와 지식을 실제로 어떻게 적용하게 되는지 알 수 있도록 해줄 것 입니다.
하지만 단순히 짧은 링크를 만드는 데서 끝나지 않습니다.
수십억 개의 링크와 수억 건의 일일 요청을 처리하는 수준으로 확장 하려면 다양한 아키텍처 결정과 트레이드오프를 고려해야 합니다.
이 글에서는 기초적인 흐름부터 시작해, 실제 운영 수준의 설계까지 단계적으로 다뤄보겠습니다. 초심자에게 친절하면서도, 심화 내용을 함께 살펴볼 예정입니다.
기능적 요구사항 - Functional Requirements
‘기능적 요구사항 Functional Requirements’ 은 시스템 디자인 설계에 앞서서 MVP 시스템에서 어떤 기능을 설계에 반영할 지를 정의하는 단계 입니다. 여기서 정의된 기능적 요구사항들을 바탕으로 비기능적 요구사항(Non-Functional Requirements) 을 정의하게 됩니다.
핵심 기능 Core Requirements
사용자는 긴 URL 을 제출하면 짧은 URL 을 받을 수 있어야 합니다.
(선택) 사용자 지정 별칭(alias) 을 설정할 수 있어야 합니다. (예:
bit.ly/myalias)(선택) 짧은 URL 에 만료 일자를 설정할 수 있어야 합니다.
생성된 짧은 URL 로 접속 시, 원래의 긴 URL 로 Redirect 되어야 합니다.
제외 범위
‘제외 범위’ 단계는 시스템 디자인에서 목적에 따라 당장 필요하지 않은 기능, 불필요한 기능을 정의하는 단계입니다.
사용자 계정 및 인증
클릭 분석 기능
QR 코드 생성 기능
비기능적 요구사항, Non-Functional Requirements
핵심 요구사항 Core Requirements
생성되는 짧은 코드(short code)는 중복 없이 고유해야 합니다.
리디렉션은 지연 없이 빠르게 처리되어야 합니다. (예: < 100ms)
시스템은 매우 높은 가용성을 가져야 합니다. (예: > 99.99%)
10억 개 이상의 링크와 1억 DAU(Daily Active Users)를 처리할 수 있어야 합니다.
읽기(read) 요청이 매우 많은 구조에 최적화되어야 합니다.
(예시: 읽기:쓰기 비율 = 1000:1)
제외 범위 (Out of scope)
스팸 필터링 및 악성 URL 차단 기능
데이터센터 간 글로벌 레이트 리밋
이 시스템은 극단적으로 읽기 중심(read-heavy) 입니다.
짧은 링크 하나가 생성될 때마다 수천 번의 데이터 읽기(read)가 발생합니다.
따라서 전체 아키텍처는 이 비대칭성을 중심으로 설계되어야 합니다.
코어 엔티티 정의 - Defining the Core Entities
이제 시스템을 구성하는 핵심 도메인 객체(Entity)를 정의해보겠습니다.
설계의 기반이 되는 구조이므로, 각 필드가 어떤 의미를 가지는지 간단히 설명합니다.
ShortURL
short_code고유 식별자. 예:
abc123
long_url사용자가 입력한 원본 긴 URL
created_at, timestamp생성 시각 (타임스탬프)
expiration_date, optional(선택) 링크의 만료일
custom_alias: optional(선택) 사용자가 지정한 별칭 (예시:
myalias)
User (선택/확장 엔티티)
향후 유저 기반의 클릭 분석, 링크 관리 기능을 도입할 경우 필요한 엔티티입니다.
예를 들어, 특정 사용자가 생성한 링크 목록을 조회하거나, 사용자별 통계를 저장할 때 활용할 수 있습니다.
현재 설계에서는 익명 사용자를 기본 가정하므로,
핵심적으로 필요한 것은 ShortURL 엔티티 하나뿐입니다.
API Signatures
‘API Signatures‘ 단계는 시스템 디자인 설계에 있어서 중요한 부분 중 하나라고 할 수 있습니다. 이 API 시그니처를 바탕으로 High-Level 시스템 디자인의 흐름과 어떤 서비스 컴포넌트가 필요한지 정의할 수 있습니다. 또한 기능적 요구사항과 비기능적 요구사항을 충족시키기 위해 어떤 API 를 어떻게 구현할 지 정의하여야 합니다.
// 1. ShortURL 단축 URL 을 생성하는 API 입니다.
Create a short URL
POST /urls
요청 Request
{
"long_url": "https://shortlong.com/very/long/url",
"custom_alias": "shorten", // optional
"expiration_date": "2025-12-31" // optional
}
응답 Response
{
"short_url": "https://short.ly/shorten"
}
// 2. 원래의 URL 로 리다이렉션을 하도록 하는 API 입니다.
GET /{short_code}
→ HTTP 302 Found
Location: https://example.com/very/long/url
🛠 Alias Conflict 문제 - 어떻게 해결할까?
사용자가 직접 custom_alias를 지정했을 때,
이미 같은 별칭이 다른 URL에 사용되고 있다면 충돌이 발생합니다.
예시
A 사용자가
bit.ly/myalias를 생성B 사용자가 동일한
myalias로 새 URL을 요청
이 경우 서버는 409 Conflict 에러를 반환해야 하며,
명확하고 친절한 메시지가 동반되어야 합니다.
1. DB 레벨 고유 제약조건 (UNIQUE)
custom_alias 필드를 UNIQUE 제약조건으로 설정하고,
POST 시도 시 DB에서 자동으로 충돌 감지를 수행합니다.
장점
race condition 최소화
단점
애플리케이션 쪽에서 충돌 메시지를 별도로 처리해야 함
2. 트랜잭션 재시도/사전 검사 흐름
사전 조회: DB에서 해당 alias가 존재하는지 확인
INSERT 시도: DB 제약조건에 따라 삽입
충돌 감지
사전 검사 때 발견 → 곧바로 409 반환
유효성 검사 통과 후 삽입 시 충돌 → 트랜잭션 에러 → 409 응답
이를 통해 경합 상황에서도 일관성 있는 응답을 제공합니다.
3. 대체 alias 자동 추천
별칭 충돌 시, 다음과 같은 방안을 추가합니다
서버에서
myalias123,myalias_link,myalias-1등의 추천 alias 자동 생성프론트엔드에서 실시간 추천 UI 제공 (Axios+API를 통해 충돌 상태 확인)
UX 향상 및 사용자의 재시도 부담 역전이 가능합니다.
4. 분산 시스템 대응 - 키 생성 서비스 활용
custom_alias는 사용자의 지정 방식이므로 분산 충돌은 거의 없음
하지만 생성되는 short_code(자동 생성)가 많아지면,
전용 키 생성 서비스(Key generation service, KGS) 또는 ZooKeeper 기반 locking을 통해 충돌 리스크를 방지할 수 있습니다
<별칭 충돌 처리 전략>
예약어 차단:
admin,help등의 예약어는 사용 불가별칭 유효성 검사: 허용된 문자만 통과
DB UNIQUE 제약 + 트랜잭션 처리: 중복 충돌 방지
사전 검사 + 재시도 로직: 경합 발생 시 안정 대응
자동 추천 alias 제공: UX 강화
(대규모 분산 대응) KGS 또는 ZooKeeper 기반 키 관리: 충돌 억제
High-Level Design
Short URL 생성 흐름
사용자가
POST /urls요청을 통해 긴 URL(그리고 선택적으로 별칭 또는 만료일)을 전송합니다.서버는 다음과 같은 과정을 거칩니다.
전달받은 URL이 유효한 형식인지 검증합니다.
별칭(
custom_alias)이 포함되어 있다면, DB에서 중복 여부를 확인합니다.별칭이 없다면, base62로 인코딩된 카운터 또는 Hash ID Generation 서비스를 통해 고유한
short_code를 생성합니다.생성된
(short_code → long_url)매핑을 데이터베이스에 저장합니다.이후 Redis에 캐시하여 빠른 리디렉션을 대비합니다.
최종적으로 생성된
short_url을 클라이언트에게 반환합니다.
Redirect 흐름
사용자가
short.ly/abc123와 같은 짧은 URL을 클릭하면, 브라우저는 서버에GET /abc123요청을 보냅니다.서버는 다음과 같이 처리합니다.
먼저 Redis 캐시에서 해당
short_code를 조회합니다.
→ 있다면, 즉시 원래 URL로 리디렉션(HTTP 302) 합니다.없다면 DB를 조회하여 원본 URL을 찾고, 결과를 Redis에 캐싱한 뒤 리디렉션합니다.
만약 해당 링크가 만료된 경우,
404 Not Found또는 “링크 만료” 안내 페이지를 반환합니다.리디렉션에는 HTTP 302 (임시 리디렉션)을 사용하여,
브라우저가 캐시하지 않고 항상 서버를 거치게 합니다.
이렇게 하면 추후 링크 수정이나 만료 처리에도 유연하게 대응할 수 있습니다.
Redis 캐시 TTL 전략
Short URL 시스템은 극단적으로 데이터 읽기(read)가 많은 구조입니다.
이를 고려해 우리는 Redis를 주요 캐시 계층으로 사용하며, 다음과 같은 전략으로 TTL(Time To Live, 만료 시간)을 설정합니다.
1. 기본 TTL 설정
만약
short_url에 별도로 설정된 만료일(expiration_date)이 없다면,
Redis에서는 기본 TTL (예: 30일) 을 설정합니다.이 전략은 자주 사용되는 링크는 캐시에 계속 남아 있고,
사용되지 않는 링크는 자연스럽게 제거되도록 해줍니다.
# 예시: Redis SET 명령어로 TTL 설정
redis.set(short_code, long_url, ex=2592000) # 30일 = 2,592,000초2. 사용자 지정 만료일과 TTL 동기화
사용자가 명시적으로 만료일을 설정한 경우,
Redis에서도 해당 만료일까지 TTL을 설정합니다.이 경우, DB의 expiration_date와 캐시 TTL을 동기화하여
만료된 링크가 캐시에 남아있지 않도록 보장합니다.
ttl_seconds = (expiration_date - now).total_seconds()
redis.set(short_code, long_url, ex=ttl_seconds)3. 갱신 정책 (Cache-aside + Lazy Expiration)
사용자가 자주 방문하는 short URL은 TTL이 만료되더라도
DB에서 재조회 후 Redis에 다시 캐시됩니다.이를 통해 Hot URL은 항상 캐시에 머무르게 되고,
Cold URL은 자연스럽게 사라지며 메모리 사용도 최적화됩니다.
이 전략은 “Cache-aside pattern” 혹은 “Lazy loading” 방식이라고도 부릅니다.
4. TTL이 중요한 이유
만료된 링크가 Redis에 남아 있으면 유효하지 않은 리디렉션이 발생할 수 있습니다.
Redis TTL은 시스템이 데이터 유효성 + 메모리 효율을 자동으로 관리하게 해주며,
특히 링크가 자주 생성되지만 소수만 반복적으로 사용되는 경우, 캐시 관리를 단순화해줍니다.
딥다이브 - Deep Dives
1. 데이터베이스가 다운되면 어떻게 대응할 것인가?
Redis가 warm cache 상태라면, 대부분의 요청은 그대로 처리 가능
백엔드 복구 전략
DB 복제(replication): 마스터 + 복제본 구성
정기 백업 + 장애 조치(failover) 설정
심각한 장애 시, fallback 캐시 또는 CDN 캐시로 리디렉션 유지 가능
2. 10억 개 URL과 1억 DAU를 어떻게 스케일링할 것인가?
한 row당 저장 크기 ≈ 500바이트 → 10억 row = 약 500GB
이는 현대 SSD 기반 DB(PostgreSQL, DynamoDB 등)에서 충분히 수용 가능
추가 스케일링 방법
short_codeprefix 또는 ID 모듈(Mod) 값을 기준으로 DB 샤딩(sharding)캐시 계층(예: Redis)은 수평 확장 가능한 클러스터 모드로 설정
3. 리다이렉션을 어떻게 빠르게 처리할 것인가?
Redis를 hot path 캐시로 활용
Key:
short_code, Value:long_urlTTL이 설정된 링크는 해당 만료일로 Redis에도 TTL 설정
캐시 미스(cache miss) 시 → DB 조회 후 Redis에 재캐싱
스케일링 전략
Redis 클러스터 모드 또는 Sharded Redis 활용
DB는 읽기 복제본(Read Replica) 구성하여 캐시 미스 시 부하 분산
전체 트래픽의 90% 이상이 읽기 요청 → Redis hit rate가 99% 이상이어야 설계 목표 달성
4. 최적화 전략 - 카운터 배치 처리
각 앱 서버가 Redis에서 1000개 단위의 카운터 블록을 미리 가져와 로컬에 저장
요청마다 Redis를 호출하지 않고, 로컬에서 카운터 사용
블록이 소진되면 새 배치 요청
장점: Redis 부하 감소 + 단축 ID 생성 속도 향상
5. 어떻게 고유한 short code를 생성할 것인가?
Base62 인코딩된 전역 카운터 (추천 방식)
Redis에 전역 카운터를 저장하고, 요청 시 원자적 증가(
INCR)이 값을 Base62로 인코딩하여 짧고 고유한 short code 생성
예:
123456 → bUK4d장점: 중복 없음, 생성 속도 빠름, 예측 가능한 구조
해시 기반 (예: MD5, SHA256)
긴 URL을 해시로 변환해 short code 생성
단점: 해시 충돌 가능성 존재 → DB 중복 검사 필요
짧게 자를수록 충돌 확률 증가
무작위 문자열 생성
UUIDv4 또는
crypto.randomBytes()사용DB에서 중복 여부 확인 필수
장점: 빠르고 분산 가능
단점: 충돌 방지 위해 체크 필요 + 비교적 긴 문자열 생성됨
단순한 시스템처럼 보여도, 짧은 URL 하나에 수천 번의 접근이 발생하는 구조에서는
고유성 보장, 빠른 캐시 응답, 대용량 저장, 장애 대응 등 다양한 트레이드오프를 고려해야 합니다. 이 모든 요소가 조화롭게 작동할 때 진짜 확장 가능한 Short URL 시스템이 완성됩니다.
Final Design
Summary & Takeaways
이 시스템은 읽기(Read) 중심 구조이므로,
Redis 캐시 활용과 수평 확장(Horizontal Scaling) 이 성능 최적화의 핵심입니다.Base62 인코딩된 전역 카운터 + 배치 처리(batch allocation) 전략을 사용하면,
대규모 환경에서도 충돌 없이 고유한 short code를 안정적으로 생성할 수 있습니다.리디렉션에는 HTTP 302 (임시 이동) 코드를 사용함으로써,
브라우저 캐시를 방지하고 링크 변경이나 만료 등 서버 측 제어권을 유지할 수 있습니다.저장소는 PostgreSQL 또는 DynamoDB 단일 인스턴스에서도
복제(replication)와 함께 수십억 개의 URL 매핑을 감당할 수 있을 만큼 충분히 확장 가능합니다.전체 설계는 높은 가용성(High Availability), 낮은 지연 시간(Low Latency), 그리고 향후의 기능 확장(예: 클릭 분석, 사용자 계정 통합)까지 고려한 견고하고 유연한 구조를 지향합니다.
다음 글 - Google YouTube 시스템 디자인 (유료 구독자 전용)
다음 콘텐츠에서는 다음과 같은 내용을 다룹니다.
유튜브가 어떻게 10억 명의 사용자를 처리하는지
동영상 스트리밍 시스템의 캐싱 전략과 서버 간 부하 분산 방법
ML 시스템 디자인 설계 방법
시스템 디자인 인터뷰 전략
시스템 디자인 해외 인터뷰를 위한 영어 문장들 그리고 참고 인터뷰 영상 공유
GPT가 설계한 시스템과 실제 유튜브 시스템 설계 두 개를 비교하면서 GPT 를 사용해서 설계하는 것이 과연 옳은가에 대한 것을 추가로 다루어 보겠습니다.
이 외에도 다양한 내용을 다루며, 모든 콘텐츠는 실제 인터뷰와 실무 설계에 활용 가능한 아키텍처 전략을 중심으로 작성됩니다.
추가적으로, Imgur 이미지 호스팅 서비스 시스템 디자인을 무료 구독자분들을 위해 제공될 예정입니다.





안녕하세요.
궁금한점이 있습니다.
“전체 트래픽의 90% 이상이 읽기 요청 → Redis hit rate가 99% 이상이어야 설계 목표 달성”
왜 캐시히트율 99% 이상이 설계목표 달성인지가 궁금합니다. 업계에서 자주 사용되는 기준인건지 아니면 지연없이 빠른 처리(<100ms)를 달성하려면 이 정도 수치여야 하는건지 궁금합니다.