이전에 LinkedIn에 한번 적었던 것처럼, Planetarium에서는 회사에서 전직원에게 ChatGPT 유료 버전 구독을 전액 지원해준다. 인당 월 20$로 스타트업에게는 절대로 작지 않은 비용이지만, 확실히 이를 도입하고 나서 생산성이 어마어마하게 상승했다.
데이터 분석에 필요한 SQL부터, 운영성에 업무에 필요한 SQL 작성, 그리고 UX writing까지 사용해보면 거의 무궁무진한 사용성을 보여주는 이 무시무시한 도구로, 한번 '개발자와 가상의 기획 리뷰를 해본다는 느낌으로 써볼까?' 라는 생각이 들어 이를 진행 해보았다.
어떤 일을 맡겼나?
내가 담당하는 서비스에는 Referral ID가 존재한다. 지금까지는 이를 URL에 파라메터로 넘겨주고 있었고, 해당 파라메터를 손으로 입력할 일은 없다고 생각하여 긴 자리수를 그대로 사용하고 있다.
그러나, 회사에서 준비하고 있는 '앞으로 다가올 개선 버전' 에서는 손으로 직접 Referral ID 를 입력하는 시나리오도 발생할 것으로 예상이 되어, "가능하다면 Human-readable 하고 짧은 코드를 만들 수 있었으면 좋겠다" 라는 생각이 들었다.
지금까지의 업무 방식이라고 하면, 함께 일하는 시니어 백엔드 엔지니어 중에서 이러한 업무를 해본 사람을 찾아가서 "친구 초대 코드를 최대한 짧게 만들어 보려고 하는데, 몇자까지 줄일 수 있을까요?" 라고 물어보고 자문을 구한다거나, 아니면 구글 검색 등을 통해서 Referral ID를 어떻게 만들 수 있는지 찾아보았을 것 같다. 혹은, 다른 웹서비스들은 어떻게 Referral ID를 관리하고 있는지 벤치마크 등을 해보지 않았을까?
그런데 당연히, 이 방식에는 몇가지 현실적인 단점이 있다.
그 중 대표적인 것이, "최대 몇자리 까지 줄일 수 있다" 라는 것을 '문서'로만 찾아보거나, 아니면 경험에 의존해서 알 수 있다는 것이 아닐까 싶은데, 운이 좋게 관련 경험이 풍부한 시니어 엔지니어가 있었다면 그 정답을 바로 알려줄 수도 있지만, 그런 상황이 아니라면? 아마 높은 확률로, 다른 서비스의 레퍼런스를 확인하고 그 모델을 따라가지 않았을까?
하지만 요즘 핫하다는 ChatGPT는 코딩까지 직접 해주는 도구가 아니었던가.
바로, ChatGPT와 함께 리서치를 시작해 보았다.
먼저 간단한 요구사항을 정리하면 다음과 같았다.
- 유저 사이에서 중복되는 Referral ID가 발생하지 않도록 UUID에서부터 코드를 생성한다.
- 반대로, 생성된 Referral ID를 다시 UUID로 변환할 수 있어야 한다.
과정
처음에는 대략적인 가설을 가지고 시작했다. 한 8자리~10자리 정도면 충분하지 않을까?
ChatGPT에게 요구사항으로, 다음과 같은 전제를 전달했다.
- Generate a unique referral ID based on UUID, and limit it to 8 or 10 digits.
- Create a referral code from a 36 character code that is unique and easy to remember.
- Use a combination of letters (case-insensitive) and numbers.
그리고 아래와 같은 코드를 얻었다.
num = int(code, 36)
def generate_referral_code(code):
base = 36
mod = 36**10
num = int(code, base)
result = ''
while len(result) < 10:
num = num // mod
remainder = num % 36
char = str(remainder) if remainder < 10 else chr(remainder + 55)
result += char
return result
그 뒤에 가장 중요한 질문인, 32자리인 uuid를 10개의 자리수로 줄이면 '동일한 Referral ID'를 얻을 확률에 대하여 계산해달라 요청했다.1억명이 가입한 경우를 가정해서 실제로 얼마가 중복되는지도 계산해 달라 요청했다. 물론 각각이 한줄 한줄의 프롬프트였었고, 아쉽게도 지금은 ChatGPT에서 Chat history를 제공해주지 않아 캡쳐를 하진 못했다. 대신 맨 마지막에, '이 모든 것을 정리하여 문서로 만들어줘' 라는 명령어를 쳤고, 맨 아래에 아래와 같은 요약을 ChatGPT에서 제공해주었다.
Probability of Collision:
- For a 10 character code using base-36 numbers, the probability of collision between two different 36 character codes mapping to the same 10 character code is around 0.00022%.
- If there are 100 million users who signed up for the service and each user has a unique 10 character referral code generated using base-36 numbers, the probability of two users having the same referral code is effectively zero (less than 1 in 10^15). However, in reality, it's possible that some users may have the same referral code due to the limited number of possible codes compared to the number of users.
해치웠나?
아쉽게도 1차 시도는 실패였다. ChatGPT가 작성해준 코드를 돌려 보았을 때, 어디가 잘못된 것인지는 모르겠어도 반대로 10자리 코드에서 uuid를 얻는 과정에서 계속 다른 값이 나왔다. (해당 코드는 Jupyter notebook에 돌려 보았다.) 생성된 코드에서 원래의 UUID를 얻지 못하니, 실패였다.
2차 시도는 그래서, 자리수를 내가 먼저 임의로 정하지 않고 '최대한 줄여달라'는 요청으로 프롬프트를 다시 시작해 보았다.
그리고 아래는, GPT4가 만들어준 답변들을 바탕으로 마지막에 "문서로 요약해줘" 라 입력한 결과이다.
소개
이 문서는 32자리 UUID를 base-62 인코딩을 사용하여 짧은 참조 ID로 압축하는 Python 스크립트를 제공합니다. 이 스크립트는 무작위 UUID를 생성하고, 이를 참조 ID로 압축하며, 압축을 해제하여 프로세스가 뒤집힐 수 있는지를 확인합니다.
방법:
- Base-n 인코딩 및 디코딩:
- 이 스크립트는 지정된 문자 집합을 사용하여 다른 진법으로 숫자를 변환하는 ****base_n_encode***와 base_n_decode 함수를 정의합니다.
- UUID 압축 및 해제:
- compress_uuid 함수는 UUID 문자열을 입력으로 받아 하이픈을 제거하고 결과적으로 생성된 16진수 숫자를 알파벳과 숫자(0-9, a-z, A-Z)로 이루어진 base-62 숫자로 변환합니다.
- decompress_uuid 함수는 참조 ID를 입력으로 받아 base-62 문자 집합을 사용하여 이를 16진수 숫자로 변환하고 하이픈으로 구분된 UUID 문자열로 형식화합니다.
- 충돌 확률 계산:
- 이 스크립트에는 생일의 역설 공식을 사용하여 참조 ID 충돌 확률을 계산하는 collision_probability 함수가 포함되어 있습니다.
Python code
import uuid
def base_n_encode(number, base, charset):
if number == 0:
return charset[0]
result = ""
while number > 0:
number, remainder = divmod(number, base)
result = charset[remainder] + result
return result
def base_n_decode(s, base, charset):
number = 0
for char in s:
number = number * base + charset.index(char)
return number
def compress_uuid(uuid_str):
hex_str = uuid_str.replace('-', '')
num = int(hex_str, 16)
base62_charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
return base_n_encode(num, 62, base62_charset)
def decompress_uuid(referral_id):
base62_charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
num = base_n_decode(referral_id, 62, base62_charset)
hex_str = format(num, 'x').zfill(32)
return f"{hex_str[:8]}-{hex_str[8:12]}-{hex_str[12:16]}-{hex_str[16:20]}-{hex_str[20:]}"
# Generate a random UUID
random_uuid = uuid.uuid4()
print(f"Original UUID: {random_uuid}")
# Compress the UUID into a referral ID
referral_id = compress_uuid(str(random_uuid))
print(f"Referral ID: {referral_id}")
# Decompress the referral ID back into a UUID
decompressed_uuid = decompress_uuid(referral_id)
print(f"Decompressed UUID: {decompressed_uuid}")
결과
이 스크립트는 UUID를 짧은 참조 ID로 성공적으로 압축하고 이를 다시 원래 UUID로 해제합니다. 그러나 생성된 참조 ID 수가 많아질수록 충돌 확률이 증가합니다. 많은 수의 사용자에게는 충돌 가능성을 줄이기 위해 더 긴 참조 ID를 사용하는 것이 권장됩니다.
그리고 아래는, 위 과정에서 위 코드가 맞는지 테스트를 위한 코드를 작성해 달라고 요청한 것이다.
import math
def collision_probability(n, shortened_code_length, base):
N = base ** shortened_code_length
prob = 1 - math.exp(-n**2 / (2 * N))
return prob
base = 62
shortened_code_length = 22
n_values = [1_000, 10_000, 100_000, 1_000_000, 10_000_000, 300_000_000_000_000]
for n in n_values:
prob = collision_probability(n, shortened_code_length, base)
print(f"Collision probability for {n} generated codes: {prob:.10f}")
결과는 아래와 같았다.
Collision probability for 1000 generated codes: 0.0000000000
Collision probability for 10000 generated codes: 0.0000000000
Collision probability for 100000 generated codes: 0.0000000000
Collision probability for 1000000 generated codes: 0.0000000000
Collision probability for 10000000 generated codes: 0.0000000000
Collision probability for 300000000000000 generated codes: 0.0000000000
마지막에, 위 코드를 우리 팀에서 메인으로 사용하는 Typescript로 바꾸어 달라는 요청까지 완벽하게 해주는 것을 보면서, 이제 기획자의 입장에서 무언가 기술 리뷰를 받고자 할 때, 이런 식으로 직접 검증을 해보는 것도 가능하구나 라는 결론에 도달했다.
답을 공개하자면, UUID를 중복 걱정 없이 최대한 줄일 수 있는 것은 base62로 인코딩을 바꾸어 22자까지 줄일 수 있다는 것이다. 처음에 이야기한 것처럼 관련 경험이 있는 시니어 엔지니어가 있었다면 '그건 최대가 22자까지 줄일 수 있다.' 라고 바로 답을 해줄 수 있었겠지만, 개발을 잘 모르는 PM 입장에서 상호 변환되는 동작하는 코드 형태로 간단하게 이를 알아볼 수 있다는 부분에서 정말 놀라웠다.
그리고 무엇보다, 중간에 개인적으로 python library를 설치하는 과정에서 좀 오류가 있어 library를 사용하지 않는 버전으로 개발해달라고 요청하느라 좀 시간이 오래걸렸던 것을 제외하면 이 모든 과정이 대략 30분 남짓하게 소요되었다는 것이다.
무서운 세상이다.
'서비스기획 이야기' 카테고리의 다른 글
휴대전화번호가 최악의 회원가입 수단인 이유: 클럽하우스 사례 (3) | 2021.02.22 |
---|---|
게임 회사가 IE에 대응하는 조금 색다른 방법 (0) | 2021.02.12 |
이루다 (스캐터랩 챗봇)의 논란과, 회사에 없는 데이터를 극복한 방법 (0) | 2021.01.14 |