UUID 개요
UUID(Universally Unique Identifier)는 128비트 길이의 식별자로, RFC 4122에 의해 표준화되어 있습니다. 일반적으로 8-4-4-4-12 형식의 32자 16진수 문자열로 표현됩니다(예: 550e8400-e29b-41d4-a716-446655440000). UUID는 중앙 서버 없이도 충돌 가능성이 극히 낮은 고유 식별자를 생성할 수 있어, 분산 시스템에서 기본키, 세션 ID, 파일명 등에 널리 사용됩니다.
2024년 RFC 9562가 발표되면서 UUID v6, v7, v8이 공식 표준으로 추가되었습니다. 이 가이드에서는 실무에서 가장 많이 사용되는 v1, v4, v7을 중심으로 비교합니다.
UUID v1: 타임스탬프 + MAC 주소
UUID v1은 현재 시각(100나노초 단위)과 생성 노드의 MAC 주소를 조합하여 생성됩니다. 시간 기반이므로 자연적인 시간순 정렬이 가능하고, 같은 노드에서 생성된 UUID의 순서를 보장합니다.
하지만 두 가지 심각한 단점이 있습니다. 첫째, MAC 주소가 UUID에 직접 포함되므로 생성 장비를 식별할 수 있어 프라이버시 문제가 발생합니다. 둘째, 시간 정보가 UUID의 상위 비트에 연속적으로 배치되지 않아 B-tree 인덱스에서 정렬 순서가 시간순과 일치하지 않습니다. 이러한 이유로 새로운 프로젝트에서 v1은 권장되지 않습니다.
UUID v4: 순수 랜덤
UUID v4는 122비트를 무작위 값으로 채워 생성합니다. 나머지 6비트는 버전(4)과 RFC 변형 정보에 사용됩니다. 구현이 단순하고 개인 정보를 포함하지 않아 현재 가장 널리 사용되는 버전입니다.
충돌 확률은 이론적으로 2^122(약 5.3 × 10^36) 분의 1로, 초당 10억 개를 생성해도 중복이 발생할 확률이 무시할 수 있는 수준입니다. JavaScript의 crypto.randomUUID(), Python의 uuid.uuid4(), Java의 UUID.randomUUID() 등 대부분의 언어에서 기본 제공합니다.
단점은 완전한 랜덤이므로 시간 정보가 없어 생성 순서를 알 수 없고, B-tree 인덱스에서 삽입 위치가 랜덤하게 분산되어 페이지 분할(page split)이 빈번하게 발생한다는 점입니다. 이는 대량 INSERT 워크로드에서 성능 저하의 주요 원인이 됩니다.
UUID v7: 시간순 정렬 가능한 랜덤
UUID v7은 2024년 RFC 9562로 표준화된 최신 버전으로, v4의 랜덤성과 v1의 시간순 정렬 장점을 결합했습니다. 상위 48비트에 Unix 타임스탬프(밀리초)를 배치하고, 나머지 비트를 랜덤으로 채웁니다.
이 구조 덕분에 동일 밀리초 내에서도 랜덤 부분이 충돌을 방지하면서, 시간순으로 자연 정렬됩니다. B-tree 인덱스에서 새로운 UUID가 항상 트리의 오른쪽 끝에 삽입되므로, v4 대비 인덱스 성능이 크게 개선됩니다. PostgreSQL 17, MySQL 8.0.33+ 등 최신 데이터베이스에서 v7 생성 함수를 기본 제공하기 시작했습니다.
주의할 점은 타임스탬프가 밀리초 해상도이므로, 초당 수만 건 이상의 생성에서는 랜덤 부분의 충돌 가능성을 고려해야 한다는 것입니다. 하지만 74비트의 랜덤 공간이 있어 실무에서는 문제가 되지 않습니다.
버전별 비교 요약
생성 원리: v1은 시간+MAC, v4는 순수 랜덤, v7은 시간+랜덤입니다. 시간순 정렬: v1은 제한적(비트 배치 문제), v4는 불가능, v7은 완벽 지원합니다. 프라이버시: v1은 MAC 주소 노출, v4와 v7은 안전합니다. 인덱스 성능: v1과 v7은 양호, v4는 대량 삽입 시 저하됩니다. 표준 지원: v1과 v4는 RFC 4122(2005년), v7은 RFC 9562(2024년)입니다.
데이터베이스 기본키로서의 UUID
B-tree 인덱스와 UUID
대부분의 관계형 데이터베이스는 기본키에 B-tree 인덱스를 사용합니다. 자동 증가(AUTO_INCREMENT) 정수 키는 항상 트리의 오른쪽 끝에 삽입되므로 페이지 분할이 최소화됩니다. UUID v4는 랜덤이므로 트리의 임의 위치에 삽입되어 빈번한 페이지 분할을 유발하고, 이는 I/O 증가와 인덱스 단편화로 이어집니다.
UUID v7은 시간순이므로 자동 증가 키와 유사한 패턴으로 삽입됩니다. 벤치마크에서 v7은 v4 대비 INSERT 처리량이 2~5배 높은 것으로 보고되며, 인덱스 크기도 더 작게 유지됩니다.
저장 최적화
UUID를 CHAR(36)으로 저장하면 36바이트를 차지하지만, BINARY(16)으로 저장하면 16바이트로 줄일 수 있습니다. MySQL에서는 UUID_TO_BIN()과 BIN_TO_UUID() 함수를 사용하여 변환할 수 있으며, PostgreSQL은 네이티브 UUID 타입이 16바이트로 자동 저장합니다.
저장 크기의 차이는 인덱스 크기와 조인 성능에 직접 영향을 미칩니다. 수천만 건 이상의 테이블에서는 BINARY(16) 또는 네이티브 UUID 타입 사용을 권장합니다.
ULID: UUID의 대안
ULID(Universally Unique Lexicographically Sortable Identifier)는 UUID v7과 유사한 개념으로, 상위 48비트에 밀리초 타임스탬프, 하위 80비트에 랜덤 값을 배치합니다. Crockford Base32로 인코딩하여 26자의 문자열로 표현되며, 대소문자를 구분하지 않고 사전순 정렬이 가능합니다.
UUID v7이 RFC 표준으로 채택되기 전에 시간순 정렬 가능한 ID가 필요한 프로젝트에서 많이 사용되었습니다. 현재는 UUID v7이 공식 표준이므로, 신규 프로젝트에서는 v7을 우선 고려하되, 기존 ULID를 사용 중인 시스템에서는 마이그레이션 비용 대비 이점이 크지 않으므로 유지하는 것이 합리적입니다.
프로젝트별 선택 가이드
신규 웹 서비스 — UUID v7을 권장합니다. 시간순 정렬로 인덱스 성능이 우수하고, 프라이버시도 안전합니다. 라이브러리 지원이 부족한 언어에서는 v4를 사용하세요.
마이크로서비스 간 통신 — UUID v4가 가장 간단합니다. 대부분의 언어/프레임워크에서 기본 제공하며, 구현 복잡도가 가장 낮습니다.
로그/이벤트 추적 — UUID v7 또는 ULID를 권장합니다. 시간순 정렬이 로그 분석과 디버깅에 유리합니다.
보안 토큰 — UUID는 식별자이지 비밀 값이 아닙니다. 인증 토큰이나 세션 비밀에는 crypto.getRandomValues()나 전용 토큰 라이브러리를 사용하세요.
레거시 시스템 연동 — 기존 시스템이 특정 UUID 버전을 사용하고 있다면 호환성을 유지하세요. 불필요한 마이그레이션은 비용 대비 이점이 작습니다.
UUID 생성 도구
빠른 테스트나 더미 데이터 생성이 필요할 때 UUID 생성기를 활용하세요. 1~100개의 UUID v4를 일괄 생성하고, 대문자 변환이나 대시 제거 옵션을 적용할 수 있습니다. 생성된 UUID의 텍스트 해시가 필요하면 텍스트 해시 생성기를, 타임스탬프 관련 작업에는 타임스탬프 변환기를 활용하세요.