
URL 단축
URL 리디렉션
비기능 요구사항
초당 평균 쓰기 연산:
1억 ÷ 24 ÷ 3,600
≈ 1,160회/초
10:1로 가정1,160 × 10
= 11,600회/초
1억 × 365 × 10
= 3,650억 개
3,650억 × 100바이트
= 36.5TB
클라이언트는 서버가 제공하는 API 엔드포인트를 통해 URL 단축 시스템과 통신함.
이번 설계는 REST 스타일로 구성함.
POST /api/v1/data/shorten
GET /api/v1/shortUrl
Location 헤더에 포함Location 헤더에 지정된 원래 URL로 이동
두 상태 코드는 모두 리디렉션에 사용되지만 의미와 활용 방식에 차이가 있음.
Location 헤더의 URL로 영구적으로 변경되었음을 의미Location 헤더에 지정된 다른 URL에서 임시로 처리됨을 의미URL 리디렉션을 구현하는 가장 직관적인 방법은 해시 테이블을 사용하는 것임.
해시 테이블에는 다음과 같은 쌍을 저장함.
<단축 URL, 원래 URL>
원래 URL 조회:
원래 URL = hashTable.get(단축 URL)
조회한 원래 URL을 HTTP 301 또는 302 응답의 Location 헤더에 넣어 전송함.
긴 URL을 단축 코드로 변환할 때는 단축 코드가 가능한 한 고르게 분포하도록 해야 함.
다만 고정 길이의 단축 코드를 사용하면 서로 다른 긴 URL이 같은 단축 코드로 변환되는 충돌 가능성을 완전히 없앨 수 없음.
예를 들어 7자리 단축 코드를 사용하면 출력 가능한 코드 수는 유한함.
62⁷ = 약 3.52조 개
따라서 다음 조건이 필요함.
또한 단축 코드에서 원래 URL을 직접 복원하는 방식이 아님.
단축 코드
→ 데이터베이스 또는 캐시 조회
→ 원래 URL 반환
원래 URL은 별도 저장소에 반드시 보관해야 함.
모든 <단축 URL, 원래 URL> 쌍을 메모리의 해시 테이블에 저장하기에는 메모리 용량이 제한적이고 비용도 큼.
따라서 전체 데이터는 관계형 데이터베이스에 영구 저장함.
관계형 데이터베이스는 메모리 해시 테이블보다 느리지만, 단축 코드에 인덱스를 설정하면 전체 테이블을 탐색하지 않고 빠르게 조회 가능함.
예시:
SELECT long_url
FROM urls
WHERE short_code = ?;
데이터베이스만 사용하는 것이 아니라 Redis 등의 캐시를 함께 사용함.

조회 흐름:
단축 URL 요청
→ 캐시 조회
→ 캐시 적중: 원래 URL 반환
→ 캐시 미스: 데이터베이스 조회
→ 조회 결과를 캐시에 저장
→ 원래 URL로 리디렉션
데이터베이스
캐시
편의상 단축 URL의 고유 코드 부분을 hashValue라고 부름.
hashValue는 다음 문자로 구성함.
0~9a~zA~Z사용 가능한 전체 문자 수:
10 + 26 + 26
= 62개
10년 동안 약 3,650억 개의 URL을 생성할 예정이므로 다음 조건을 만족하는 최소 n을 구해야 함.
62ⁿ ≥ 3,650억
계산 결과:
62⁶ = 56,800,235,584
약 568억
62⁷ = 3,521,614,606,208
약 3.52조
따라서 필요한 최소 길이는 7자리임.
단축 코드를 생성하는 방식은 다음 두 가지로 나눌 수 있음.
Base62는 해시 함수가 아니라 숫자를 62진수 문자열로 표현하는 인코딩 방식임.
예시:
hash(longURL)
→ 충돌 발생
hash(longURL + "1")
→ 다시 확인
hash(longURL + "2")
→ 다시 확인
즉, 충돌이 발생하면 원래 URL에 사전에 정한 문자열이나 salt를 추가한 뒤 다시 해싱함.
단축 코드 자체에 문자열을 계속 덧붙이는 방식은 7자리 고정 길이 요구와 맞지 않으므로 사용하지 않음.

해시 후 충돌 해소 방식에서는 생성한 단축 코드가 이미 존재하는지 확인해야 함.
기본 흐름:
단축 코드 생성
→ 데이터베이스 존재 여부 확인
→ 충돌 시 코드 재생성
→ 다시 데이터베이스 확인
여러 서버가 동시에 같은 단축 코드를 생성할 가능성에 대비해 데이터베이스에 유일 제약을 설정할 수 있음.
UNIQUE(short_code)
블룸 필터는 데이터베이스를 완전히 대체하는 것이 아니라 불필요한 데이터베이스 조회를 줄이는 보조 자료구조임.
블룸 필터의 판단 결과:
없음
→ 확실히 존재하지 않음
있음
→ 존재할 수도 있음
처리 흐름:
새 단축 코드 생성
→ 블룸 필터 확인
따라서 블룸 필터를 사용하면 확실히 존재하지 않는 코드에 대한 데이터베이스 조회를 줄일 수 있음.
Base62는 유일한 숫자 ID를 62개의 문자로 구성된 짧은 문자열로 변환하는 방식임.
사용 문자:
0-9
a-z
A-Z
유일한 숫자 ID 생성
→ 숫자 ID를 Base62로 변환
→ 단축 코드 생성
예를 들어 숫자 ID가 125라면 이를 Base62 문자열로 변환하여 짧은 코드로 표현 가능함.

| 구분 | 해시 후 충돌 해소 | 유일 ID의 Base62 변환 |
|---|---|---|
| 입력값 | 긴 URL | 유일 숫자 ID |
| 충돌 가능성 | 있음 | ID가 유일하면 없음 |
| 충돌 검사 | 필요 | 불필요 |
| DB 조회 | 충돌 확인을 위해 필요 | URL 저장 및 조회에 사용 |
| 별도 ID 생성기 | 불필요 | 필요 |
| 코드 예측 가능성 | 상대적으로 낮음 | 순차 ID 사용 시 높음 |
| 구현 복잡도 | 충돌 처리 필요 | ID 생성기 관리 필요 |

기본 레코드:
ID | shortURL | longURL
ID는 전역적으로 유일해야 함.
Base62는 숫자 ID와 단축 코드 사이의 가역적인 변환임.
따라서 두 가지 데이터 모델을 사용할 수 있음.
ID | short_code | long_url | created_at
조회 과정:
short_code
→ 데이터베이스 조회
→ long_url 반환
ID | long_url | created_at
조회 과정:
short_code
→ Base62 디코딩
→ numeric ID
→ 데이터베이스 조회
→ long_url 반환
이 방식은 short_code를 별도 컬럼으로 저장하지 않아도 됨.
Base62 방식에서는 입력으로 사용할 숫자 ID가 전역적으로 유일해야 함.
선택 가능한 방식:
auto_increment현재 요구사항은 평균 초당 약 1,160개의 쓰기 요청임.
사용하는 데이터베이스와 하드웨어가 해당 쓰기 부하를 감당할 수 있다면 단일 데이터베이스의 auto_increment나 sequence로도 구현 가능함.
여러 데이터베이스에서 독립적으로 ID를 발급해야 한다면 Snowflake 등의 분산 ID 생성 방식을 고려해야 함.
클라이언트
→ 긴 URL 전송
→ 유일 ID 생성
→ Base62 변환
→ DB에 ID와 원래 URL 저장
→ 단축 URL 반환
클라이언트가 단축 URL 요청
→ 캐시에서 원래 URL 조회
→ 캐시 적중 시 즉시 리디렉션
→ 캐시 미스 시 DB 조회
→ 조회 결과 캐시 저장
→ 301 또는 302 응답 반환