---
title: "분산 시스템에서 쓰는 ID 형식 총정리 — UUID, ULID, Snowflake, NanoID 비교"
description: "분산 시스템 환경에서 요구사항에 맞는 최적의 식별자를 선택할 수 있도록 UUID v7, ULID, Snowflake, NanoID 등 주요 ID 형식의 특징과 장단점을 비교하고 상황별 선택 가이드를 제시함"
date: 2026-05-18
updated: 2026-05-18T17:29:37.930Z
tags: [분산시스템, 데이터베이스, UUID, ULID, NanoID, 식별자]
canonical: https://blog.wooncloud.com/posts/11
---

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

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

## 한눈에 보는 비교표

| 형식 | 길이 | 정렬 | 충돌 위험 | URL Safe | 보안 | 비고 |
|---|---|---|---|---|---|---|
| Auto-increment INT | 4-8 byte | ✅ | DB 단일 | ✅ | ❌ 예측 가능 | 단일 DB 최적, 분산 X |
| UUID v1 | 36자 | ⚠ 부분 | 매우 낮음 | ✅ | ⚠ MAC 노출 | 잘 안 씀 |
| UUID v4 | 36자 | ❌ | 사실상 0 | ✅ | ✅ 완전 랜덤 | 가장 일반적 |
| UUID v7 | 36자 | ✅ | 매우 낮음 | ✅ | ✅ | RFC 9562 (2024) 차세대 표준 |
| ULID | 26자 | ✅ | 매우 낮음 | ✅ | ✅ | UUID v7 의 전신 |
| KSUID | 27자 | ✅ | 매우 낮음 | ✅ | ✅ | Segment 가 만든 ULID 변형 |
| Snowflake | 64bit | ✅ | 노드 ID 충돌 시 | ✅ | ❌ 예측 가능 | Twitter, Instagram |
| NanoID | 21자 | ❌ | 매우 낮음 | ✅ | ✅ | 짧고 빠름 |
| CUID2 | 24자 | ❌ | 매우 낮음 | ✅ | ✅ | 해시 기반 |
| Mongo ObjectId | 24자 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 |
| MongoDB | ObjectId |

## 마무리

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

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

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

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