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비트 너비로 두 구성 요소로 나뉩니다:

타임스탬프무작위
01ARZ3NDEKTSVE4RRFFQ69G5FAV
48비트——밀리초 단위 Unix 타임스탬프. 1ms 정밀도로 생성 시간을 인코딩합니다. 10889년까지 커버합니다.80비트——암호학적으로 안전한 무작위 데이터. 각 ULID에 대해 새로 생성됩니다(단조 모드를 사용하지 않는 한).

26개의 Crockford Base32 문자로 인코딩됩니다: 처음 10자는 타임스탬프를, 마지막 16자는 무작위 구성 요소를 나타냅니다.

Crockford Base32 알파벳

ULID는 Crockford의 Base32 인코딩을 사용하며 36개의 영숫자 중 32개를 사용합니다. 알파벳: 0123456789ABCDEFGHJKMNPQRSTVWXYZ

왜 이 32자인가요?
0123456789ABCDEFGHJKMNPQRSTVWXYZ

4자가 의도적으로 제외됩니다:

  • IL은 제외——숫자 1과 혼동하기 쉬움
  • O는 제외——숫자 0과 혼동하기 쉬움
  • U는 제외——생성된 ID에서 우발적인 불쾌한 단어 방지
  • 인코딩은 대소문자 구분 없음——01ARZ3NDEKTSV4RRFFQ69G5FAV01arz3ndektsv4rrffq69g5fav는 같은 ULID

Crockford Base32는 16진수(32기호 대 16)보다 효율적이고 Base64(+ / = 문자 없음, 대소문자 구분 없음)보다 읽기 쉽습니다.

ULID 대 UUID

ULID와 UUID는 모두 128비트 식별자를 나타내지만 인코딩과 설계 목표가 크게 다릅니다:

속성ULIDUUID
형식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 안전한 식별자를 생성하지만 설계 목표가 다릅니다:

속성ULIDNanoID
타임스탬프예——48비트 Unix ms아니오
정렬 가능아니오
기본 길이26자21자
엔트로피80비트(무작위 구성 요소)~126비트
알파벳Crockford Base32(32자)URL 안전 Base64(64자)
길이 사용자 정의아니오예(임의 길이)
사용 사례시간 순서 ID, DB 기본 키무작위 토큰, 단축 URL, API 키

시간 순서가 필요할 때 ULID를 선택하세요. 짧은 무작위 문자열에서 최대 엔트로피가 필요할 때 nanoid를 선택하세요.

데이터베이스에서 ULID 사용

ULID는 요구 사항에 따라 여러 방법으로 데이터베이스에 저장할 수 있습니다:

CHAR(26)로 저장
가장 간단한 접근법. 사전순 정렬 순서가 유지됩니다. 이진 저장보다 약간 크지만 사람이 읽을 수 있고 디버깅이 쉽습니다.
BINARY(16)로 저장
압축 저장을 위해 ULID를 16바이트 이진 표현으로 디코딩합니다. 애플리케이션 코드에서 인코딩/디코딩이 필요합니다. 정렬 순서가 유지됩니다.
PostgreSQL
CHAR(26)로 저장하거나 이진 저장을 위해 bytea 타입을 사용합니다. 네이티브 ULID 타입은 없지만 CHAR(26)의 사전순 순서가 올바르게 작동합니다.
MySQL / MariaDB
효율적인 저장을 위해 CHAR(26) CHARACTER SET ascii를 사용합니다. BINARY(16)의 이진 저장도 작동하고 공간을 절약합니다.
SQLite
TEXT로 저장합니다. SQLite의 기본 텍스트 비교는 ULID의 사전순 설계로 인해 ULID를 올바르게 정렬합니다.
MongoDB
문자열 필드로 저장합니다. ULID의 사전순 정렬 가능성은 MongoDB의 문자열 비교와 자연스럽게 작동합니다.

코드 예제

ULID 생성에는 ulid 라이브러리가 필요합니다(JavaScript, Python, Go, Rust 등에서 사용 가능):

JavaScript (ulid npm)
// 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)
Python (python-ulid)
# 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
PostgreSQL
-- 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;
JavaScript (pure — no dependencies)
// 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)
}

자주 묻는 질문

ULID는 전 세계적으로 고유한가요?
예——매우 높은 확률로. 80비트 무작위 구성 요소는 각 밀리초, 각 생성기당 2^80 ≈ 1.2 × 10^24개의 가능한 값을 제공합니다. 같은 밀리초 내 다른 생성기 간 충돌 확률은 실용적인 시스템에서 무시할 수 있습니다.
ULID는 UUID와 같은가요?
아닙니다——ULID와 UUID는 128비트 값의 다른 인코딩입니다. ULID는 UUID 형식(하이픈 16진)을 검증하는 시스템에서 UUID를 직접 대체할 수 없습니다.
ULID 단조 모드는 어떻게 작동하나요?
표준 모드에서는 각 ULID의 80비트 무작위 구성 요소가 새로 생성됩니다. 단조 모드에서는 같은 밀리초 내에 여러 ULID가 생성될 때 두 번째 이후 ID의 무작위 구성 요소가 이전 무작위 값 + 1이 됩니다.
ULID를 디코딩하여 생성 타임스탬프를 얻을 수 있나요?
예. 처음 10개의 Crockford Base32 문자가 48비트 Unix 밀리초 타임스탬프를 인코딩합니다. Crockford Base32 알파벳을 사용하여 이 문자들을 디코딩하고 결과를 밀리초 단위 Unix 타임스탬프로 해석하여 날짜로 변환합니다. 이 도구가 정확히 그것을 합니다.
ULID는 공식 표준인가요?
아닙니다. ULID는 ulid.github.io에서 관리되는 커뮤니티 사양입니다. UUID와 같은 IETF 표준이 아닙니다. UUID v7(RFC 9562, 2024)은 유사한 시간 순서 속성을 가진 IETF 표준화된 대안입니다.