Skip to content

[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 8장 URL 단축기 설계를 읽고..

Notifications You must be signed in to change notification settings

jjunhub/short-url-generator

Repository files navigation

개요

[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 8장 URL 단축기 설계를 읽고 난 뒤에, 이를 직접 구현해보려고 한다. 다만 해당 장에서 제공하는 기능 요구사항을 조금은 변경하여 고려해볼 예정이다.

더 자세한 내용

  1. 서버 설계 스토리 : https://jjunhub.tistory.com/11
  2. 서버 구현 스토리 : https://jjunhub.tistory.com/12

프로젝트 기술 스택

  • Spring Boot 3.4.2
  • Java 17
  • Spring Data JPA
  • H2 Database
  • Lombok
  • MySQL 8.0
  • Redis

서비스 설명

요약

사용자가 입력한 원본 URL을 단축된 URL로 변경하여 제공하는 서비스이다. 이 서비스는 하루에 10억명의 방문자가 존재하며, 서로 다른 원본 URL을 10억개 이상 저장할 수 있어야한다. 사용자는 단축된 URL로 접속할 경우, 원본 URL로 redirect된다. 사용자가 단축된 URL로 접속할 때마다, 그 횟수를 날짜별로 저장한다. 사용자는 shortUrlId를 통해서 전체 URL 주소와 기타 정보를 확인할 수 있다. 사용자는 각 shortUrlId 별 일자 별 통계를 제공받을 수 있다.

기능 요구 사항

  1. 사용자가 입력한 원본 URL을 단축된 URL로 변경하여 제공한다.
    이 때 단축된 URL은 다음과 같은 구조를 띈다. “서버 호스트 주소 + /r + /{shortUrlId}”
    예시 : www.google.com/test/abcdedasdasdsads -> www.jjunhub.com/s/1abd2ef
  2. 사용자가 하나의 원본 URL로 여러 번 단축된 URL로 변경을 시도한다면, 항상 다른 shortUrlId 값이 제공되어야한다.
  3. shortUrlId는 alphanumberic한 문자열로 구성되어야하며 최대한 짧은 값을 가져야한다.
  4. 사용자는 단축된 URL로 접속할 경우, 원본 URL로 redirect 된다.
  5. 사용자가 단축된 URL로 접속할 경우, 그에 대한 횟수를 날짜별로 저장한다.
  6. 사용자는 shortUrlId를 통해서 전체 URL 주소와 기타 정보를 확인할 수 있다.
  7. 사용자는 각 shortUrlId 별 일자 별 통계를 제공받을 수 있다. 이는 shortUrlId와 날짜 정보를 통해서 요청을 받는다.

API 명세서

  1. POST /s

    • Feature
      • 사용자는 해당 API를 통해 원본 URL을 입력하면, 단축된 URL을 제공받는다.
      • 서버 측에서는 해당 API를 통해 shortUrlId를 생성하고, 원본 URL과 함께 DB에 저장해야한다.
      • 서버 측에서는 shortUrlId를 생성할 때, alphanumberic한 문자열로 생성해야한다.
      • 서버 측에서는 shortUrlId를 생성할 때, 중복되지 않는 값을 생성해야한다.
      • 서버 측에서는 shortUrlId를 생성할 때, 가능한 짧은 값을 생성해야한다.
    • Request
      {
          "originalUrl": "https://jjunhub.tistory.com/10"
      }
    • Response
      {
         "data" : {
            "shortUrlId": "ab1267c",
            "originalUrl": "https://jjunhub.tistory.com/10",
            "createdAt": "2021-06-07T11:38:16+0000"
          }
      }
  2. GET /r/{shortUrlId}

    • Feature
      • 사용자는 해당 API를 통해 원본 URL로 redirect된다.
      • 서버 측에서는 해당 API로 접속하는 횟수를 통계 데이터로 저장해야한다.
    • Request
      • None
    • Response
      • originalUrl로 Redirect 302
  3. GET /g/{shortUrlId}

    • Feature
      • 사용자는 해당 API를 통해서 원본 URL과 기타 정보를 확인할 수 있다.
    • Request
      • None
    • Response
      {  
        "data" : {
          "shortUrlId": "ab1267c",
          "originalUrl": "https://jjunhub.tistory.com/10",
          "createdAt": "2021-06-07T11:38:16+0000"
        }
      }
  4. GET /st/{shortUrlId}?date=2025-02-26

    • Feature
      • 사용자는 해당 API를 통해 각 shortUrlId date에 해당하는 통계 정보를 획득할 수 있다.
    • Request
      • None
    • Response
      {
        "data" : {
          "shortUrlId": "abc",
          "date": "2025-02-26",
          "clickCount": 100
        }
      }

도메인 로직 설계

1. shortUrlId 생성 알고리즘

  • 중복되지 않는 shortUrlId를 생성할 때, 어떤 알고리즘을 사용할 것인가?
  • 10억개 이상 저장될 수 있으며, 숫자와 문자로 이루어진 shortUrlId를 생성해야 한다.
  • 숫자와 문자로 이루어진 shortUrlId는 09, az, A~Z의 문자로 구성되므로 총 10 + 26 + 26 = 62개의 문자로 구성된다.
  • 따라서 Base62 Encoding을 사용하여 shortUrlId를 생성하는 것이 적절하다고 판단하였다.
  • Base62 Encoding은 한 자리마다 약 62개의 문자를 사용하므로, 8자리의 shortUrlId를 생성하면 62^8 = 218,340,105,584,896(21조)개의 문자를 생성할 수 있다.
  • 21조 / 10억 = 21,834일 동안 서비스를 운영할 수 있고, 이는 약 60년에 해당하므로 충분한 시간이다.
  • 따라서 10억개 이상을 저장해야하는 구조에 적합하도록 8자리의 shortUrlId를 생성하여 사용자에게 제공하였다.

2. shortUrlId 중복 생성 방지

  1. (Random 값 + Salt) + Hashing + Base62 Encoding
    • Random 값과 Salt를 결합하여 Hashing과 Base62 인코딩을 진행하고 그 결과가 DB에 이미 존재하지 않는다면 shortUrlId로 저장하고, 존재한다면 다시 Random 값을 생성하여 Hashing을 진행한다.
    • 장점: shortUrlId의 길이가 거의 일정하게 유지되고, 사용자가 추측하기 어렵다.
    • 단점: 충돌 해결을 위해, hashing + salt를 여러 번 진행해야 할 수도 있다.
  2. DB의 Unique ID + Base62 Encoding
    • DB에서 unique ID를 불러와서 Base62로 encoding하여 shortUrlId를 생성한다.
    • 장점: 충돌이 발생할 확률이 매우 낮다.
    • 단점: shortUrlId의 길이가 일정하지 않고, unique ID가 일정하게 증가한다면 사용자가 추측하기 쉬울 수 있다.

두 가지 방법 중, 1번 방법을 선택하여 구현하였다. DB에 unique ID를 불러오기 위해서는 여러 서버를 위해 unique ID를 관리하는 테이블이 추가로 필요하다. 따라서 1번 방법을 선택하여 구현하였다.

3. shortUrlId 날짜별 통계

shortUrlId의 날짜별로 통계를 저장해야하는데 어떻게 저장할 것인가?

  • 날짜별로 통계를 저장하기 위해서는 날짜별로 shortUrlId의 클릭 수를 저장해야 한다.
  • 이를 위해서는 날짜별로 shortUrlId의 클릭 수를 저장할 수 있는 DB 스키마를 설계해야 한다.
  • 날짜별로 shortUrlId의 클릭 수를 저장하는 DB 스키마는 다음과 같다.
    • short_url_id: varchar(30)
    • click_date: date
    • click_count: bigint
  • 이 과정에서 (shortUrlId, date)를 복합키 형태의 PK로 설정하여 데이터를 저장한다.
  • 복합키로 인해서, 중복되지 않는 데이터를 저장할 수 있으며 클러스터드 인덱스를 사용하여 조회 성능을 향상시킬 수 있다.

4. clickCount 통계 집계

shortUrlId의 클릭 수를 날짜별로 저장하고, 이를 통계로 집계해야하는데 어떻게 집계할 것인가?

  • redis에 저장된 shortUrlId의 클릭 수를 약 1시간마다 집계하여, DB에 저장한다.
  • 이를 위해서는 shortUrlId의 클릭 수를 집계하는 스케줄러를 도입했다.

About

[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 8장 URL 단축기 설계를 읽고..

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages