ULID 생성기
Generate lexicographically sortable unique IDs
생성 버튼을 클릭하여 ULID를 만드세요
ULID란 무엇인가요?
ULID(Universally Unique Lexicographically Sortable Identifier)는 고유하고 자연스럽게 정렬 가능한 128비트 식별자 형식입니다. 완전히 무작위인 UUID v4와 달리 ULID는 상위 비트에 밀리초 정밀도 Unix 타임스탬프를 내장합니다——나중에 생성된 ID가 이전 ID 뒤에 정렬됩니다.
ULID는 Crockford의 Base32 알파벳을 사용하여 인코딩되며 URL 안전하고 대소문자 구분 없고 시각적으로 모호한 문자가 없는 압축된 26자 문자열을 생성합니다.
ULID 사양은 Alizain Feerasta가 만들었으며 고유성과 시간 순서 모두가 중요한 분산 시스템, 이벤트 소싱, 데이터베이스 기본 키에서 널리 사용됩니다. ULID는 IETF 표준이 아닙니다——ulid.github.io에서 사용 가능한 커뮤니티 사양입니다.
ULID 구조
ULID는 128비트 너비로 두 구성 요소로 나뉩니다:
| 타임스탬프 | 무작위 |
|---|---|
| 01ARZ3NDEK | TSVE4RRFFQ69G5FAV |
| 48비트——밀리초 단위 Unix 타임스탬프. 1ms 정밀도로 생성 시간을 인코딩합니다. 10889년까지 커버합니다. | 80비트——암호학적으로 안전한 무작위 데이터. 각 ULID에 대해 새로 생성됩니다(단조 모드를 사용하지 않는 한). |
26개의 Crockford Base32 문자로 인코딩됩니다: 처음 10자는 타임스탬프를, 마지막 16자는 무작위 구성 요소를 나타냅니다.
Crockford Base32 알파벳
ULID는 Crockford의 Base32 인코딩을 사용하며 36개의 영숫자 중 32개를 사용합니다. 알파벳: 0123456789ABCDEFGHJKMNPQRSTVWXYZ
4자가 의도적으로 제외됩니다:
I와L은 제외——숫자1과 혼동하기 쉬움O는 제외——숫자0과 혼동하기 쉬움U는 제외——생성된 ID에서 우발적인 불쾌한 단어 방지- 인코딩은
대소문자 구분 없음——01ARZ3NDEKTSV4RRFFQ69G5FAV와01arz3ndektsv4rrffq69g5fav는 같은 ULID
Crockford Base32는 16진수(32기호 대 16)보다 효율적이고 Base64(+ / = 문자 없음, 대소문자 구분 없음)보다 읽기 쉽습니다.
ULID 대 UUID
ULID와 UUID는 모두 128비트 식별자를 나타내지만 인코딩과 설계 목표가 크게 다릅니다:
| 속성 | ULID | UUID |
|---|---|---|
| 형식 | Crockford Base32 | 하이픈 16진 |
| 길이 | 26자 | 36자 |
| 타임스탬프 | 48비트 Unix ms | 없음(v4) 또는 48비트 ms(v7) |
| 정렬 가능 | 예——사전순 | 아니오(v4) / 예(v7) |
| URL 안전 | 예(영숫자만) | 예(하이픈 포함) |
| 의존성 | 라이브러리 필요 | 네이티브(crypto.randomUUID) |
| DB 지원 | CHAR(26) 또는 BINARY(16)로 저장 | 네이티브 UUID 열 타입 |
| 사양 | 커뮤니티 사양(ulid.github.io) | RFC 4122 / RFC 9562 |
이미 UUID 생태계(PostgreSQL uuid 열, REST API, UUID 지원 ORM)에 있다면 보통 ULID보다 UUID v7이 더 적합합니다. 더 인간 친화적인 인코딩을 선호하고 전체 스택을 제어한다면 ULID가 탁월한 선택입니다.
ULID 대 nanoid
ULID와 nanoid는 모두 짧고 URL 안전한 식별자를 생성하지만 설계 목표가 다릅니다:
| 속성 | ULID | NanoID |
|---|---|---|
| 타임스탬프 | 예——48비트 Unix ms | 아니오 |
| 정렬 가능 | 예 | 아니오 |
| 기본 길이 | 26자 | 21자 |
| 엔트로피 | 80비트(무작위 구성 요소) | ~126비트 |
| 알파벳 | Crockford Base32(32자) | URL 안전 Base64(64자) |
| 길이 사용자 정의 | 아니오 | 예(임의 길이) |
| 사용 사례 | 시간 순서 ID, DB 기본 키 | 무작위 토큰, 단축 URL, API 키 |
시간 순서가 필요할 때 ULID를 선택하세요. 짧은 무작위 문자열에서 최대 엔트로피가 필요할 때 nanoid를 선택하세요.
데이터베이스에서 ULID 사용
ULID는 요구 사항에 따라 여러 방법으로 데이터베이스에 저장할 수 있습니다:
코드 예제
ULID 생성에는 ulid 라이브러리가 필요합니다(JavaScript, Python, Go, Rust 등에서 사용 가능):
// Using the 'ulid' npm package
import { ulid } from 'ulid'
const id = ulid() // "01ARZ3NDEKTSV4RRFFQ69G5FAV"
const id2 = ulid() // "01ARZ3NDEKXXXXXXXXXXXX..." (same ms, incremented random)
// With a custom timestamp
const id3 = ulid(1469918176385) // deterministic time portion
// Extract the timestamp back out
import { decodeTime } from 'ulid'
decodeTime(id) // → 1469918176385 (Unix ms)# Using python-ulid
from ulid import ULID
uid = ULID()
str(uid) # "01ARZ3NDEKTSV4RRFFQ69G5FAV"
uid.timestamp # 1469918176.385
uid.datetime # datetime(2016, 7, 30, 23, 36, 16, 385000, tzinfo=timezone.utc)
# Parse an existing ULID string
parsed = ULID.from_str("01ARZ3NDEKTSV4RRFFQ69G5FAV")
parsed.timestamp # 1469918176.385-- Store ULIDs as TEXT or CHAR(26)
CREATE TABLE events (
id CHAR(26) PRIMARY KEY DEFAULT gen_ulid(), -- if using a PG extension
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Or pass the ULID from your application layer
INSERT INTO events (id, name, created_at)
VALUES ('01ARZ3NDEKTSV4RRFFQ69G5FAV', 'user.signup', NOW());
-- Range queries are efficient because ULIDs sort chronologically
SELECT * FROM events
WHERE id > '01ARZ3NDEKTSV4RRFFQ69G5FAV'
ORDER BY id
LIMIT 20;// Pure browser / Deno / Node.js implementation (no dependencies)
const CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
function encodeTime(ms) {
let result = '', n = BigInt(ms)
for (let i = 9; i >= 0; i--) {
result = CROCKFORD[Number(n & 31n)] + result
n >>= 5n
}
return result
}
function encodeRandom(bytes) {
let n = 0n
for (const b of bytes) n = (n << 8n) | BigInt(b)
let result = ''
for (let i = 15; i >= 0; i--) {
result = CROCKFORD[Number(n & 31n)] + result
n >>= 5n
}
return result
}
function generateULID() {
const randomBytes = new Uint8Array(10)
crypto.getRandomValues(randomBytes)
return encodeTime(Date.now()) + encodeRandom(randomBytes)
}