--- title: "official-doc / ULID — Universally Unique Lexicographically Sortable Identifier (공식 Spec)" source_type: official-doc url: https://github.com/ulid/spec archive_url: related_branches: [feature-resource-identifier-contract] related_projects: [ca-skeleton] tags: [official-doc, ca-skeleton, data-modeling, api-design] created: 2026-05-31 vendor: ulid/spec (alizain — original author) last_reviewed: 2026-05-31 --- # official-doc / ULID Spec — Universally Unique Lexicographically Sortable Identifier > Layer: `raw/official-docs/` — ULID 공식 사양(spec) 원문 발췌 및 출처 기록. > 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 별도 작성. 원본은 raw 에 영구 보관. ## Parent / 활용 branch (필수) > 이 자료는 **혼자 존재하지 않는다.** 아래 branch 의 구현 결정 근거로서 보관됨. | Branch | 이 자료가 정당화하는 결정 | |---|---| | [[raw/branch-notes/feature-resource-identifier-contract]] | D1 (resource ID 기본 형식 후보로서 ULID 26자 base32), D2 (Crockford base32 charset — I/L/O/U 제외), D3 (base32 case-insensitive + URL-safe 특성), D7 (48bit millisecond timestamp 평문 노출 — timestamp leak 위험 범위 정의), D10 (lexicographic 단조 정렬 → DB B-tree index 단편화 완화 근거) | ## 출처 / Source - 원본 URL: https://github.com/ulid/spec - 아카이브 URL: (미입력) - 저자 / 조직: alizain (original author), ulid GitHub org - 발행일: (최초 commit 이후 지속 관리 — pinned spec) - 마지막 확인일: 2026-05-31 ## 왜 저장했는지 / Why archived ca-skeleton 의 resource ID 기본 형식 결정(D1)에서 ULID 가 유력 후보로 거론된다. 이 자료는 ULID 의 공식 사양(인코딩 형식, 타임스탬프 노출, 단조 정렬 보장, 바이너리 레이아웃)을 원문 그대로 기록하여, D1/D2/D3/D7/D10 결정의 verbatim 근거를 제공한다. ## 핵심 인용 / Key quotes (verbatim, 5개) > [§Spec header — bullet list, line 28] "Canonically encoded as a 26 character string, as opposed to the 36 character UUID" > [§Specification → Components → Timestamp, lines 105-107] "48 bit integer" / "UNIX-time in milliseconds" / "Won't run out of space 'til the year 10889 AD." > [§Specification → Encoding, line 129] "Crockford's Base32 is used as shown. This alphabet excludes the letters I, L, O, and U to avoid confusion and abuse." > [§Specification → Sorting, line 115] "The left-most character must be sorted first, and the right-most character sorted last (lexical order). The default ASCII character set must be used. Within the same millisecond, sort order is not guaranteed" > [§Specification → Monotonicity, lines 137-139] "When generating a ULID within the same millisecond, we can provide some guarantees regarding sort order. Namely, if the same millisecond is detected, the `random` component is incremented by 1 bit in the least significant bit position (with carrying)." > [§Specification → Binary Layout and Byte Order, line 177] "The components are encoded as 16 octets. Each component is encoded with the Most Significant Byte first (network byte order)." ## Claims Extracted / 추출된 주장 > 이 자료가 **직접 말하는 것만** claim 으로 분리한다. ca-skeleton 에 적용한 결론은 여기 쓰지 않는다. | Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove | |---|---|---|---|---|---| | ULID-C1 | ULID 는 128비트 식별자를 26자 Crockford base32 문자열로 인코딩한다 (UUID 의 36자 대비 shorter) | [§header] "Canonically encoded as a 26 character string, as opposed to the 36 character UUID" | `official-standard` | ULID spec 을 따르는 모든 구현체 | 특정 언어 라이브러리가 이 길이를 올바르게 구현함을 증명하지 않음 | | ULID-C2 | ULID 의 타임스탬프 컴포넌트는 48비트 정수이며 Unix millisecond epoch 이다 | [§Timestamp] "48 bit integer" / "UNIX-time in milliseconds" | `official-standard` | ULID spec 준수 구현체 | 48bit 노출이 특정 privacy 위험을 야기함을 spec 이 직접 주장하지 않음; D7 위험 평가는 별도 분석 필요 | | ULID-C3 | Crockford base32 알파벳은 I, L, O, U 를 제외하여 혼동과 오용을 방지한다; case-insensitive 특성이 spec 에 명시됨 | [§Encoding] "Crockford's Base32 is used as shown. This alphabet excludes the letters I, L, O, and U to avoid confusion and abuse." / [§header] "Case insensitive" | `official-standard` | Crockford base32 인코딩을 사용하는 ULID | case-insensitive 동작이 모든 DB/HTTP layer 에서 자동 적용됨을 증명하지 않음; normalize 정책은 구현 결정 | | ULID-C4 | ULID 는 lexicographic 정렬(leftmost-first, ASCII)을 보장하나, 동일 밀리초 내에서는 보장 없음 | [§Sorting] "The left-most character must be sorted first, and the right-most character sorted last (lexical order). The default ASCII character set must be used. Within the same millisecond, sort order is not guaranteed" | `official-standard` | ULID 문자열 비교·정렬 전반 | lexicographic 정렬이 DB index 단편화를 완화함을 spec 이 직접 증명하지 않음; DB 성능 영향은 별도 벤치마크 필요 | | ULID-C5 | Monotonic factory 는 동일 밀리초 내 ULID 생성 시 random 컴포넌트를 최하위 비트에서 1 증가(carrying)하여 단조 정렬을 보장한다 | [§Monotonicity] "if the same millisecond is detected, the `random` component is incremented by 1 bit in the least significant bit position (with carrying)." | `official-standard` | monotonic generator API 를 사용하는 ULID 구현체 | 기본(non-monotonic) ULID factory 가 동일 밀리초 내 정렬을 보장하지 않음; 구현체가 monotonic factory 를 기본 노출하는지는 각 라이브러리 doc 확인 필요 | | ULID-C6 | ULID 바이너리 레이아웃은 16 옥텟, Most Significant Byte first (network byte order) 로 인코딩된다 | [§Binary Layout] "The components are encoded as 16 octets. Each component is encoded with the Most Significant Byte first (network byte order)." | `official-standard` | binary(16) 컬럼 저장 또는 UUID ↔ ULID 변환 시 | JavaScript 구현체가 binary format 을 아직 미구현했다고 spec 이 주기적으로 언급 (note 참조); 모든 라이브러리가 binary layout 을 지원하는지 별도 확인 필요 | ### Strength 허용값 이 문서의 모든 claim 은 ULID 원저자가 관리하는 GitHub 공개 spec 에서 직접 인용하였으므로 `official-standard` 로 분류. ## Usage Boundaries / 적용 경계 - **이 자료가 직접 증명하는 것:** - `ULID-C1`: ULID 는 26자 Crockford base32 이며 128bit 식별자 (UUID 와 동일 비트 수) - `ULID-C2`: 타임스탬프가 48bit millisecond Unix epoch — D7 timestamp leak 위험의 spec 근거 - `ULID-C3`: 알파벳이 I/L/O/U 제외 32자이며 case-insensitive — D2/D3 charset 근거 - `ULID-C4`: 문자열 lexicographic 정렬 보장 (동일 ms 내 제외) — D10 DB index 정렬 성능 주장의 전제 - `ULID-C5`: Monotonic factory 의 동작 정의 — D10 에서 단조 증가 보장이 필요한 경우의 근거 - `ULID-C6`: Binary(16) 레이아웃 정의 — DB primary key binary(16) 저장 정책(D10)의 format 근거 - **이 자료가 증명하지 않는 것:** - DB B-tree index 단편화 완화 효과 (정량 벤치마크 필요 — UUID v4 vs ULID 비교 데이터는 별도 자료) - 특정 Java ULID 라이브러리(예: `de.huxhorn.sulky:sulky-ulid`, `com.github.f4b6a3:ulid-creator`)의 구현 품질 또는 thread-safety - ULID 의 timestamp leak 이 GDPR/CCPA 위반을 구성하는지 (법적 해석은 별도 분석) - monotonic factory 를 기본 제공하는지 여부 (라이브러리마다 API 다름) - PostgreSQL `uuid` native 타입과 ULID 26자 varchar 저장의 성능 차이 - UUID v7 (RFC 9562) 과 ULID 의 timestamp 인코딩 방식 차이 (RFC 9562 별도 자료 필요) - **ca-skeleton 에 적용하려면 추가 확인이 필요한 것:** - Java ULID 라이브러리 선택 (API 안정성, 활성 유지보수, monotonic factory 노출 방식) - Spring/Hibernate 에서 ULID 26자를 `varchar(26)` vs `binary(16)` 중 어느 컬럼 타입으로 저장할지 - JPA `@GeneratedValue` 커스텀 generator 구현 방식 (D5 architecture layer 결정 전제) - URL path 에서 대소문자 normalize 의무 여부 (RFC 3986 §2.3 + D3 결정과 연동) ## 메모 / Notes - spec README 가 JavaScript 구현체를 canonical reference 로 명시하지만, binary format 은 "not yet implemented in JavaScript" 라고 적혀 있음. 다른 언어 구현체(Java, Go 등)는 binary layout 구현 여부가 다름. - `1.21e+24 unique ULIDs per millisecond` 는 spec 의 bullet 항목이나, 이것은 80bit random 의 수학적 최대치이지 monotonic factory 의 실제 처리량 한계와 다름 (monotonic factory 는 2^80 을 넘으면 exception). - Crockford base32 알파벳 문자열: `0123456789ABCDEFGHJKMNPQRSTVWXYZ` (32자, 대문자 기준) — self-grep line 132. - 최대 유효 ULID: `7ZZZZZZZZZZZZZZZZZZZZZZZZZ` (spec 명시, line 171). 이보다 큰 값은 모든 구현체가 reject 해야 함. - ULID 의 Prior Art 로 Instagram sharding ID (2011) 와 Firebase pushID (2015) 를 spec 이 언급. ## Related / 관련 - 같은 결정 영역의 다른 공식 자료 (예정): - [[raw/official-docs/rfc9562-uuid.md]] — UUID v4/v7 공식 스펙 (RFC 9562) - [[raw/official-docs/cuid2-spec.md]] — CUID2 timestamp-free 식별자 spec - [[raw/official-docs/crockford-base32-spec.md]] — Crockford base32 원 사양 - [[raw/official-docs/rfc3986-uri-generic-syntax.md]] — URI 허용 charset / case sensitivity 규칙 (D3 근거) - 이 자료를 인용한 wiki 요약: (생성 전)