본문으로 건너뛰기
wooncloud

분산 시스템에서 쓰는 ID 형식 총정리 — UUID, ULID, Snowflake, NanoID 비교

분산 시스템 환경에서 요구사항에 맞는 최적의 식별자를 선택할 수 있도록 UUID v7, ULID, Snowflake, NanoID 등 주요 ID 형식의 특징과 장단점을 비교하고 상황별 선택 가이드를 제시함

데이터베이스에 새 레코드를 만들 때 가장 먼저 고민하는 것. "식별자(ID)를 뭘로 쓸까?"

옛날엔 단순했다. AUTO_INCREMENT INT 하나로 끝. 그런데 분산 시스템, 마이크로서비스, 클라이언트 사이드 ID 생성이 일반화되면서 선택지가 폭발했다. 이 글에서는 실무에서 자주 마주치는 ID 형식을 정리한다.

한눈에 보는 비교표

형식길이정렬충돌 위험URL Safe보안비고
Auto-increment INT4-8 byteDB 단일❌ 예측 가능단일 DB 최적, 분산 X
UUID v136자⚠ 부분매우 낮음⚠ MAC 노출잘 안 씀
UUID v436자사실상 0✅ 완전 랜덤가장 일반적
UUID v736자매우 낮음RFC 9562 (2024) 차세대 표준
ULID26자매우 낮음UUID v7 의 전신
KSUID27자매우 낮음Segment 가 만든 ULID 변형
Snowflake64bit노드 ID 충돌 시❌ 예측 가능Twitter, Instagram
NanoID21자매우 낮음짧고 빠름
CUID224자매우 낮음해시 기반
Mongo ObjectId24자 hex사실상 0⚠ machine ID 추론MongoDB 기본

Auto-increment INTEGER

가장 오래된 방식. DB 가 자동으로 1, 2, 3... 부여한다.

  • 장점: 가장 짧음 (4-8 byte), 인덱스 효율 최강
  • 단점:
    • 분산 환경에서 만들 수 없음 (반드시 DB 에 INSERT 후 받아야 함)
    • URL 에 노출 시 enumeration 공격에 취약 (/orders/1, /orders/2 식으로 다음 ID 추측 가능)
    • 총 주문 수·성장률 같은 비즈니스 지표가 그대로 노출

언제 쓰나? 내부 시스템, 외부 노출 없는 단일 DB 환경.

UUID v4 (랜덤)

지금까지도 가장 흔히 쓰이는 표준 (RFC 4122).

550e8400-e29b-41d4-a716-446655440000
  • 장점:
    • 어디서나 생성 가능 (DB 호출 X)
    • 122bit 랜덤 → 충돌 사실상 0
    • 표준이라 모든 언어/DB 가 지원
  • 단점:
    • 36자로 김
    • 정렬 안 됨 → DB 인덱스에서 페이지 캐시 미스 증가
    • 랜덤하게 삽입되니 B-tree fragmentation 발생

PostgreSQL 에서는 gen_random_uuid() 로 생성한다.

UUID v7 (2024년 표준화)

UUID v4 의 단점인 인덱스 fragmentation 을 해결한 차세대 표준.

018e7eb3-0c5f-7d1a-a3e3-66c4d1a3a5b1
└──────timestamp─────┘└─────random─────┘

앞 48bit 가 milliseconds timestamp → 시간순 정렬 가능.

  • 장점:
    • UUID 형식 호환 (36자, hex)
    • 정렬 가능 → 인덱스 효율 ↑
    • RFC 9562 표준
    • PostgreSQL 17+, MySQL 9+ native 지원
  • 단점:
    • 아직 모든 라이브러리가 native 지원 안 함
    • 일부 환경에선 수동 구현 필요

신규 프로젝트라면 가장 유력한 후보.

ULID

UUID v7 의 영감이 된, 더 짧은 형식 (2016년 등장).

01ARZ3NDEKTSV4RRFFQ69G5FAV
└─timestamp─┘└─random───────┘
  • 장점:
    • 26자 (UUID 보다 짧음)
    • Crockford Base32 (대소문자 모호한 글자 제거 — I, L, O, U 제외)
    • 시간순 정렬
    • 사람 눈으로 비교 쉬움
  • 단점:
    • RFC 표준 아님
    • 라이브러리 품질 들쭉날쭉
    • 같은 ms 안에서 monotonic 보장하려면 별도 옵션 필요 (monotonicFactory)

prefix 패턴과 잘 어울린다. user_01ARZ3..., order_01ARZ4... 같은 식.

KSUID (Segment, 2017)

ULID 와 매우 유사한 형식.

  • 장점: 4 byte 초 단위 timestamp + 16 byte 랜덤, base62 인코딩 (숫자+영문 대소문자)
  • 단점: ULID 와 거의 동일 영역. 사용자 수가 적음

Snowflake (Twitter, 2010)

대용량 분산 시스템의 대표 ID 형식.

| 1bit |    41bit timestamp    | 10bit machine | 12bit sequence |
  • 장점:
    • 64bit 정수 → BIGINT 컬럼에 그대로 저장
    • 초당 4096 ID/머신 생성
    • 정렬·인덱스 효율 우수
  • 단점:
    • machine ID 사전 할당 필요 (Zookeeper 등)
    • 예측 가능 (보안성 ❌)
    • 시계 동기화 (clock skew) 이슈

언제 쓰나? Twitter, Discord, Instagram 처럼 초당 수천~수만 ID 가 필요한 시스템.

NanoID

URL 친화적인 짧은 ID.

V1StGXR8_Z5jdHi6B-myT
  • 장점:
    • 21자 (기본, 조절 가능)
    • UUID v4 대비 2배 빠른 생성
    • URL alphabet
  • 단점: 정렬 불가, 표준 아님

언제 쓰나? URL 단축, 슬러그, 공개 식별자.

CUID2 (2022)

해시 기반의 신형 ID.

  • 장점:
    • 외부에서 fingerprint·추측 매우 어려움
    • Horizontal scalability
    • Session 무관
  • 단점:
    • 정렬 불가
    • 생성 시간이 UUID 보다 느림
    • 검증 비용

언제 쓰나? 보안이 핵심이고 fingerprint 저항이 필요한 경우.

MongoDB ObjectId

MongoDB 의 기본 ID.

| 4 byte timestamp | 5 byte machine | 3 byte counter |
  • 장점: 12 byte (24자 hex), 자동 정렬, MongoDB 가 자동 부여
  • 단점: machine ID 노출로 약간의 정보 누설, MongoDB 외 환경에선 표준 아님

인덱스 효율은 왜 정렬이 중요한가?

UUID v4 처럼 랜덤한 ID 를 B-tree 인덱스에 삽입하면 다음 문제가 생긴다.

  1. 새 ID 가 인덱스 트리의 랜덤한 위치 에 들어감
  2. 디스크의 여러 페이지를 동시에 갱신해야 함
  3. 페이지 캐시 미스 → I/O 폭증
  4. B-tree 페이지 분할 (page split) 빈번

반면 UUID v7, ULID, Snowflake 처럼 시간순으로 증가하는 ID 는 항상 인덱스 트리의 끝에 추가되니, 직전에 갱신한 페이지를 그대로 쓰면 된다. 수십 배의 쓰기 성능 차이 가 날 수 있다.

선택 가이드

상황추천
표준 따라가고 시간순 정렬 원함UUID v7
짧은 ID + 시간순 + URL prefix 디자인ULID
표준 1순위, 정렬 무관UUID v4
URL 단축 / 슬러그NanoID
Fingerprint 저항이 핵심CUID2
고볼륨 분산 시스템 (Twitter/Discord 급)Snowflake
내부 단일 DB, 외부 노출 없음Auto-increment INT
MongoDBObjectId

마무리

ID 형식 선택은 요구사항에 따라 다르다. 단순한 정답은 없다. 핵심 질문은 세 가지다.

  1. 분산 환경인가? → 분산이면 INT 제외
  2. 외부에 노출되는가? → 노출되면 예측 가능한 형식 제외 (INT, Snowflake)
  3. 시간순 정렬이 필요한가? → 필요하면 UUID v7, ULID, Snowflake

새 프로젝트라면 UUID v7 부터 검토하자. 표준이고, 정렬 가능하고, 생태계도 빠르게 따라오고 있다. 라이브러리·DB 가 아직 준비 안 된 환경이라면 ULID 가 합리적 대안이다.

ID 는 한번 정하면 바꾸기 어렵다. 시작할 때 신중하게 고르자.