UUID · ULID 생성기
UUID v4·v7, ULID, Nano ID를 원하는 개수만큼 생성합니다. v7과 ULID는 생성 시각을 되꺼낼 수도 있습니다.
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
v7 UUID와 ULID는 앞부분에 타임스탬프가 들어 있어 생성 시각을 되꺼낼 수 있습니다.
네 가지 식별자의 차이
| 형식 | 길이 | 정렬 가능 | 특징 | |---|---|---|---| | UUID v4 | 36자 | 아니오 | 완전 무작위. 가장 널리 쓰임 | | UUID v7 | 36자 | 예 | 앞 48비트가 타임스탬프 | | ULID | 26자 | 예 | 짧고 URL에 그대로 넣기 좋음 | | Nano ID | 21자 | 아니오 | 가장 짧음. URL 친화적 |
UUID v4와 v7 중 무엇을 쓸까
데이터베이스 기본키라면 v7입니다.
v4는 완전 무작위라 새 행이 인덱스 여기저기에 흩어져 삽입됩니다. B-트리 인덱스에서 이는 페이지 분할을 계속 일으켜 삽입 성능을 떨어뜨리고 인덱스를 비대하게 만듭니다. 테이블이 커질수록 차이가 벌어집니다.
v7은 앞 48비트가 밀리초 타임스탬프라 생성 순서대로 정렬됩니다. 새 행이 항상 인덱스 끝에 붙어 순차 증가 정수와 비슷한 지역성을 얻습니다.
반대로 생성 시각이 노출되면 곤란한 값이라면 v4를 써야 합니다. v7은 언제 만들어졌는지 누구나 읽을 수 있습니다. 초대 코드나 공개 URL에 들어가는 식별자라면 이 점을 고려하세요.
UUID가 겹칠 가능성
v4는 122비트의 무작위 값을 갖습니다. 경우의 수가 약 5.3 × 10³⁶입니다.
초당 10억 개씩 생성해도 충돌 확률이 50%에 도달하려면 85년이 걸립니다. 실무에서 충돌을 걱정할 필요는 없습니다.
단, 이는 난수원이 충분히 무작위할 때의 이야기입니다. Math.random() 기반으로 UUID를 만드는 구현은 실제로 충돌한 사례가 있습니다. 이 도구는 브라우저의 암호학적 난수 생성기(crypto.getRandomValues)를 씁니다.
ULID의 장점
ULID는 26자 Crockford Base32 문자열입니다.
- 짧습니다. UUID 36자보다 10자 짧고 하이픈이 없어 URL에 그대로 넣기 좋습니다.
- 정렬됩니다. 앞 10자가 타임스탬프라 사전순 정렬이 곧 시간순입니다.
- 헷갈리지 않습니다. Crockford Base32는
I,L,O,U를 제외합니다. 숫자 1과 대문자 I, 숫자 0과 대문자 O를 혼동할 일이 없어 사람이 전화로 불러 주거나 손으로 옮겨 적을 때 유리합니다.
순차 증가 정수(auto increment)를 안 쓰는 이유
정수 기본키는 인덱스 효율이 가장 좋지만 몇 가지 문제가 있습니다.
정보가 새어 나갑니다. /orders/1523이라는 URL은 "지금까지 주문이 1,523건"임을 알려 줍니다. 경쟁사에 사업 규모를 노출하는 셈입니다. 또 번호를 하나씩 바꿔 가며 남의 데이터에 접근을 시도하는 공격의 표적이 됩니다.
분산 환경에서 어렵습니다. 여러 서버나 샤드가 동시에 ID를 만들면 조율이 필요합니다. UUID와 ULID는 서로 통신하지 않고도 겹치지 않는 ID를 만들 수 있습니다.
병합이 어렵습니다. 두 데이터베이스를 합칠 때 정수 키는 반드시 충돌합니다.
저장 방식
UUID를 데이터베이스에 넣을 때는 저장 타입이 성능을 좌우합니다.
- PostgreSQL: 전용
uuid타입이 있습니다. 16바이트로 저장됩니다. - MySQL:
BINARY(16)이 권장됩니다.CHAR(36)으로 저장하면 36바이트를 쓰고 인덱스도 두 배 이상 커집니다.
문자열로 저장하는 것은 편하지만 비용이 큽니다. 행이 수천만 개가 되면 차이가 확연히 드러납니다.
함께 확인하면 좋은 것
되돌릴 수 없는 지문이 필요하다면 해시 생성기를, v7이나 ULID에서 꺼낸 시각을 다루려면 타임스탬프 변환기를 이용하세요.