What Is UUID? — Unique Identifier Principles, Versions, and Practical Usage
UUID란?
UUID(Universally Unique Identifier, 범용 고유 식별자)는 소프트웨어에서 정보를 고유하게 식별하기 위해 사용하는 128비트 값입니다. RFC 4122 표준으로 정의되어 있으며, 2024년에는 RFC 9562로 업데이트되어 새로운 버전(v6, v7)이 추가되었습니다.
UUID는 550e8400-e29b-41d4-a716-446655440000처럼 8-4-4-4-12 형식의 32개 16진수 문자와 4개의 하이픈으로 표현됩니다. 이 형식은 사람이 읽기에도, 시스템 간 전달에도 적합합니다.
UUID가 필요한 이유는 간단합니다. 분산 시스템에서 중앙 관리 서버 없이도 고유한 ID를 생성할 수 있기 때문입니다. 데이터베이스의 자동 증가 ID는 하나의 DB 서버에서만 유일성을 보장하지만, UUID는 전 세계 어디서든 독립적으로 생성해도 사실상 충돌하지 않습니다.
UUID의 구조
UUID는 128비트(16바이트)로 구성됩니다. 이를 16진수로 표현하면 32자리가 되며, 가독성을 위해 하이픈으로 5개 그룹으로 나눕니다.
550e8400-e29b-41d4-a716-446655440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
여기서 두 가지 특별한 위치가 있습니다:
- M (13번째 16진수 자리): UUID 버전을 나타냅니다. v4라면 이 자리가
4입니다. - N (17번째 16진수 자리): UUID **변형(variant)**을 나타냅니다. 현재 표준에서는
8,9,a,b중 하나입니다.
예를 들어 550e8400-e29b-41d4-a716-446655440000에서:
- 13번째 자리
4→ UUID v4 - 17번째 자리
a→ RFC 4122 변형
이 구조 덕분에 UUID만 보고도 어떤 버전으로 생성되었는지 즉시 파악할 수 있습니다.
UUID 버전별 비교
UUID는 생성 방식에 따라 여러 버전이 존재합니다. 각 버전은 고유한 특성과 용도를 가지고 있습니다.
v1 — 타임스탬프 + MAC 주소
시스템의 현재 시각과 네트워크 카드의 MAC 주소를 조합하여 생성합니다. 시간 순서대로 정렬이 가능하지만, MAC 주소가 노출되어 프라이버시 문제가 있습니다.
v3 — MD5 해시 기반
네임스페이스와 이름을 MD5로 해시하여 생성합니다. 같은 입력이면 항상 같은 UUID가 나오므로 **결정적(deterministic)**입니다.
v4 — 랜덤
122비트를 암호학적으로 안전한 난수로 채웁니다. 가장 널리 사용되며, 구현이 간단하고 프라이버시 문제가 없습니다.
v5 — SHA-1 해시 기반
v3과 동일한 방식이지만 SHA-1 해시를 사용합니다. v3보다 충돌 가능성이 낮아 v3 대신 v5 사용이 권장됩니다.
v6 — 정렬 가능한 타임스탬프
v1의 타임스탬프 필드를 재배치하여 사전순(lexicographic) 정렬이 가능하도록 개선했습니다. 데이터베이스 인덱스 성능에 유리합니다.
v7 — Unix 밀리초 + 랜덤
Unix 타임스탬프(밀리초 단위)와 난수를 결합합니다. v6보다 구현이 간단하면서도 시간 순서 정렬이 가능하여 최신 프로젝트에 가장 추천되는 버전입니다.
버전별 비교표
| 버전 | 생성 방식 | 정렬 가능 | 프라이버시 | 결정적 | 추천 용도 |
|---|---|---|---|---|---|
| v1 | 타임스탬프+MAC | O | X | X | 레거시 시스템 |
| v3 | MD5 해시 | X | O | O | URL 매핑 |
| v4 | 랜덤 | X | O | X | 범용 (가장 인기) |
| v5 | SHA-1 해시 | X | O | O | DNS, URL 네임스페이스 |
| v6 | 정렬 가능 타임스탬프 | O | O | X | DB 기본키 |
| v7 | Unix ms+랜덤 | O | O | X | 신규 프로젝트 DB 기본키 |
UUID v4 깊이 파기
가장 널리 사용되는 UUID v4를 좀 더 자세히 살펴보겠습니다.
UUID v4는 128비트 중 버전(4비트)과 변형(2비트)을 제외한 122비트를 순수 난수로 채웁니다. 이는 약 5.3 × 10³⁶ 가지의 가능한 조합을 만들어냅니다.
충돌 확률은 **생일 역설(Birthday Paradox)**을 통해 계산할 수 있습니다. 직관적으로 이해하기 어려울 수 있지만, 수학적으로 n개의 UUID를 생성했을 때 최소 하나의 충돌이 발생할 확률은 총 가능한 조합 수에 비해 매우 낮습니다.
구체적인 수치로 보면:
- 10억(10⁹) 개 생성: 충돌 확률 약 0.00000000000000001%
- 초당 10억 개를 생성하면서 86년간 지속: 약 50% 충돌 확률
- 현존하는 모든 컴퓨터가 동시에 생성해도: 실질적으로 충돌 불가능
따라서 실무에서 UUID v4의 충돌은 걱정할 필요가 전혀 없는 수준입니다.
UUID vs 자동 증가 ID
데이터베이스에서 기본키를 정할 때 UUID와 자동 증가(Auto Increment) ID를 두고 고민하는 경우가 많습니다.
| 비교 항목 | UUID | 자동 증가 ID |
|---|---|---|
| 고유성 범위 | 전역(글로벌) | 단일 테이블 |
| 예측 가능성 | 예측 불가 | 순차적(예측 가능) |
| 분산 생성 | 가능 | 중앙 DB 필요 |
| 크기 | 16바이트 | 4~8바이트 |
| 인덱스 성능 | 상대적으로 낮음(v4) | 높음(순차 삽입) |
| URL 노출 시 보안 | 안전 | 총 레코드 수 노출 |
| 데이터 병합 | 충돌 없음 | 충돌 가능 |
자동 증가 ID의 보안 문제: /api/users/1042 같은 URL은 전체 사용자 수가 약 1,042명임을 노출합니다. 또한 /api/users/1043, /api/users/1044를 순차적으로 호출하여 다른 사용자의 정보를 탈취하는 IDOR(Insecure Direct Object Reference) 공격에 취약합니다.
UUID를 사용하면 /api/users/550e8400-e29b-41d4-a716-446655440000처럼 되어 다른 사용자의 ID를 추측하는 것이 사실상 불가능합니다.
DB에서 UUID 사용
UUID를 데이터베이스 기본키로 사용할 때 성능을 최적화하는 방법을 알아봅니다.
MySQL
MySQL에서 UUID를 저장하는 두 가지 방법이 있습니다:
-- 방법 1: CHAR(36) — 하이픈 포함, 읽기 편하지만 공간 낭비
CREATE TABLE users (
id CHAR(36) PRIMARY KEY,
name VARCHAR(100)
);
-- 방법 2: BINARY(16) — 하이픈 제거 후 바이너리 저장, 성능 최적
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
name VARCHAR(100)
);
-- BINARY(16) 저장/조회
INSERT INTO users (id, name) VALUES (UNHEX(REPLACE(UUID(), '-', '')), 'John');
SELECT HEX(id) FROM users;
BINARY(16)은 CHAR(36) 대비 저장 공간 56% 절감, 인덱스 성능도 크게 향상됩니다.
PostgreSQL
PostgreSQL은 UUID를 위한 네이티브 타입을 제공합니다:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100)
);
PostgreSQL의 UUID 타입은 내부적으로 16바이트 바이너리로 저장되므로 별도의 최적화가 필요 없습니다.
UUID를 PK로 쓸 때 주의점
- v4의 랜덤 특성: B-tree 인덱스에서 무작위 삽입이 발생하여 페이지 분할이 잦아집니다. 대용량 테이블에서 성능 저하의 원인이 됩니다.
- 해결책: UUID v7을 사용하면 시간순 정렬이 되어 순차 삽입과 유사한 성능을 얻을 수 있습니다.
- 복합 인덱스: UUID 컬럼에 다른 컬럼과 복합 인덱스를 걸 때 UUID가 앞에 오면 효율이 떨어질 수 있습니다.
프로그래밍 언어별 UUID 생성
JavaScript
// 브라우저 & Node.js 18+
const uuid = crypto.randomUUID();
console.log(uuid); // "550e8400-e29b-41d4-a716-446655440000"
Python
import uuid
new_uuid = uuid.uuid4()
print(new_uuid) # 550e8400-e29b-41d4-a716-446655440000
# 하이픈 없는 형태
print(new_uuid.hex) # 550e8400e29b41d4a716446655440000
Java
import java.util.UUID;
UUID uuid = UUID.randomUUID();
System.out.println(uuid); // 550e8400-e29b-41d4-a716-446655440000
Go
import "github.com/google/uuid"
id := uuid.New()
fmt.Println(id) // 550e8400-e29b-41d4-a716-446655440000
모든 주요 언어에서 UUID v4 생성은 한 줄이면 충분합니다. 별도의 라이브러리 설치 없이 표준 라이브러리로 사용할 수 있는 경우가 대부분입니다(Go 제외).
GUID vs UUID
Microsoft 생태계에서는 UUID 대신 **GUID(Globally Unique Identifier)**라는 용어를 사용합니다. 결론부터 말하면, GUID와 UUID는 사실상 동일합니다.
- 둘 다 128비트 값
- 둘 다 같은 형식(8-4-4-4-12)
- 둘 다 RFC 4122 표준을 따름
- .NET의
Guid.NewGuid()는 UUID v4를 생성
// C# / .NET
Guid newGuid = Guid.NewGuid();
Console.WriteLine(newGuid); // 550e8400-e29b-41d4-a716-446655440000
역사적으로 Microsoft가 1990년대에 DCE(Distributed Computing Environment) 표준을 채택하면서 GUID라는 이름을 붙였고, 이후 IETF에서 UUID라는 표준명을 정립했습니다. 현재는 UUID가 표준 용어이며, GUID는 Microsoft 플랫폼에서의 관례적 명칭입니다.
실무 활용 사례
UUID는 소프트웨어 개발의 거의 모든 영역에서 활용됩니다. 대표적인 사례를 살펴봅니다.
1. API 요청 추적 (X-Request-Id)
마이크로서비스 아키텍처에서 하나의 사용자 요청이 여러 서비스를 거칩니다. 각 요청에 UUID를 부여하면 로그를 추적할 수 있습니다.
X-Request-Id: 7c4e92f8-3b1a-4d5e-9f8c-2a6b3d4e5f6a
2. 세션 ID
사용자 로그인 세션을 식별하는 데 UUID를 사용합니다. 순차적인 ID와 달리 다른 사용자의 세션을 추측할 수 없어 보안에 유리합니다.
3. 파일명 중복 방지
사용자가 업로드한 파일의 이름이 충돌하지 않도록 UUID를 파일명으로 사용합니다.
원본: profile.jpg → 저장: 7c4e92f8-3b1a-4d5e-9f8c-2a6b3d4e5f6a.jpg
4. 마이크로서비스 간 메시지 ID
Kafka, RabbitMQ 같은 메시지 큐에서 각 메시지에 UUID를 부여하여 중복 처리를 방지하고 추적성을 확보합니다.
5. 결제 트랜잭션 ID
결제 시스템에서 각 거래에 UUID를 할당합니다. 중복 결제를 방지하는 멱등성(idempotency) 키로도 활용되며, PG사와의 연동에서 고유한 주문 번호로 사용됩니다.
마무리
UUID는 단순해 보이지만, 분산 시스템의 근간을 이루는 중요한 개념입니다. 버전별 특성을 이해하면 상황에 맞는 최적의 선택을 할 수 있습니다:
- 범용 사용: UUID v4 (가장 간단하고 안전)
- DB 기본키: UUID v7 (정렬 가능, 인덱스 친화적)
- 결정적 생성: UUID v5 (같은 입력 → 같은 출력)
지금 바로 UUID가 필요하다면 UUID 생성기를 사용해보세요. 버튼 한 번으로 UUID v4를 즉시 생성할 수 있으며, 일괄 생성과 하이픈 제거 옵션도 지원합니다.