UUID 완벽 정리: 의미, 종류(v1~v8), 장단점

By | 2026년 11월 12일
Table of Contents

UUID 완벽 정리: 의미, 종류(v1~v8), 장단점

데이터베이스 기본 키, 분산 시스템의 식별자, API 리소스 ID 등 개발하다 보면 UUID를 정말 자주 만나게 됩니다. 이 글에서는 UUID가 무엇인지, 어떤 버전이 있는지, 그리고 언제 써야 하고 언제 피해야 하는지 정리해 보겠습니다.


1. UUID란?

UUID(Universally Unique Identifier) 는 이름 그대로 "전 세계적으로 고유한 식별자" 입니다. 중앙 관리 서버 없이도 각자 독립적으로 생성한 ID가 서로 겹치지 않도록 설계된 128비트(16바이트) 값입니다.

Microsoft 진영에서는 같은 개념을 GUID(Globally Unique Identifier) 라고 부르며, 사실상 동일한 것으로 봐도 됩니다.

UUID 표준은 원래 RFC 4122(2005)에 정의되어 있었고, 2024년에 RFC 9562가 발표되면서 이를 대체했습니다. RFC 9562에서는 기존 v1~v5에 더해 v6, v7, v8이 새로 정식 추가되었습니다.

1.1 형식

UUID는 보통 32개의 16진수 문자를 하이픈(-)으로 구분한 8-4-4-4-12 형태의 36자 문자열로 표현합니다.

550e8400-e29b-41d4-a716-446655440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
  • M (version): 13번째 문자. UUID의 버전(1~8)을 나타냅니다. 위 예시는 4 → v4
  • N (variant): 17번째 문자. UUID의 레이아웃 규격을 나타냅니다. RFC 표준 UUID라면 8, 9, a, b 중 하나입니다.

즉, 128비트 중 일부(버전 4비트 + 변형 2비트)는 메타데이터로 쓰이고, 나머지 비트가 실제 고유성을 책임집니다.

1.2 특수 UUID

이름 값 용도
Nil UUID 00000000-0000-0000-0000-000000000000 "값 없음"을 나타낼 때
Max UUID ffffffff-ffff-ffff-ffff-ffffffffffff 최댓값 표현 (RFC 9562에서 추가)

2. UUID의 종류 (버전별 정리)

UUID v1 — 시간 + MAC 주소 기반

  • 구성: 60비트 타임스탬프(1582년 10월 15일부터 100ns 단위) + 클럭 시퀀스 + 노드 ID(보통 MAC 주소)
  • 장점: 생성 시각을 추출할 수 있고, 같은 장비에서는 충돌 가능성이 사실상 없음
  • 단점:
    • MAC 주소가 노출되어 생성한 장비를 추적할 수 있음 (개인정보·보안 이슈)
    • 타임스탬프 비트가 "하위 → 상위" 순서로 뒤섞여 배치되어 있어 문자열로 정렬해도 시간순이 되지 않음
예: 6ba7b810-9dad-11d1-80b4-00c04fd430c8

UUID v2 — DCE Security

  • v1을 변형해 POSIX UID/GID 같은 로컬 도메인 정보를 넣은 버전
  • 사용처가 극히 드물고, 대부분의 라이브러리가 지원하지 않습니다. 실무에서는 사실상 무시해도 됩니다.

UUID v3 — 이름 기반 (MD5)

  • 구성: 네임스페이스 UUID + 이름(문자열)을 MD5로 해시
  • 특징: 같은 네임스페이스와 같은 이름을 넣으면 항상 같은 UUID가 나옴 (결정적, deterministic)
  • 용도: URL, 도메인 이름 등 "이미 고유한 이름"을 UUID로 변환하고 싶을 때
  • 단점: MD5는 암호학적으로 취약하므로 새로 쓴다면 v5 권장

UUID v4 — 랜덤

  • 구성: 122비트를 난수로 채움 (버전·변형 비트 6비트 제외)
  • 현재 가장 널리 쓰이는 버전입니다.
  • 충돌 확률: 매초 10억 개씩 약 85년 동안 생성해야 충돌 확률이 50%에 도달할 정도로 낮습니다.
  • 단점: 완전히 무작위이므로 정렬 순서가 없고, DB 인덱스 성능에 불리합니다. (뒤에서 자세히 설명)
예: f47ac10b-58cc-4372-a567-0e02b2c3d479

UUID v5 — 이름 기반 (SHA-1)

  • v3와 같은 방식이지만 해시 알고리즘으로 SHA-1을 사용
  • 이름 기반 UUID가 필요하면 v3보다 v5를 권장합니다.
import uuid
uuid.uuid5(uuid.NAMESPACE_DNS, "example.com")
# cfbff0d1-9375-5685-968c-48ce8b15ae17

UUID v6 — 정렬 가능한 v1 (RFC 9562 신규)

  • v1과 같은 정보를 담되, 타임스탬프 비트를 상위 비트부터 순서대로 재배치
  • 그 결과 문자열/바이트 정렬 = 시간순 정렬이 됩니다.
  • 기존에 v1을 쓰던 시스템과의 호환을 위해 존재하며, 새 시스템이라면 보통 v7을 권장합니다.

UUID v7 — Unix 타임스탬프 + 랜덤 (RFC 9562 신규) ⭐

  • 구성: 상위 48비트 = Unix epoch 밀리초 타임스탬프, 나머지 = 랜덤(+ 선택적 카운터)
  • 장점:
    • 시간순으로 정렬 가능 → DB 인덱스 친화적
    • MAC 주소 같은 장비 정보가 없어 v1/v6보다 안전
    • 랜덤 비트가 충분해 충돌 걱정이 거의 없음
  • 최근 신규 프로젝트에서 가장 추천되는 버전입니다. PostgreSQL 18부터 uuidv7() 함수가 내장되는 등 생태계 지원도 빠르게 늘고 있습니다.
예: 018f3e5c-7b2a-7c4e-9a1d-3f6b8e2c1d4a
    └──────────┘
    타임스탬프(ms)

UUID v8 — 사용자 정의 (RFC 9562 신규)

  • 버전·변형 비트만 지키면 나머지 122비트를 자유롭게 설계할 수 있는 버전
  • 특수한 요구사항(자체 샤드 ID 포함, 다른 해시 알고리즘 사용 등)이 있을 때 사용
  • 상호운용성은 각자 책임지는 영역입니다.

버전별 요약표

버전 생성 방식 정렬 가능 결정적 프라이버시 추천 용도
v1 시간 + MAC ✗ ✗ ⚠️ MAC 노출 레거시
v2 DCE 보안 ✗ ✗ ⚠️ 사용 안 함
v3 MD5(이름) ✗ ✓ ✓ 레거시 이름 기반
v4 랜덤 ✗ ✗ ✓ 범용 ID, 토큰성 ID
v5 SHA-1(이름) ✗ ✓ ✓ 이름 → ID 변환
v6 시간(재배치) + 노드 ✓ ✗ ⚠️ v1 마이그레이션
v7 Unix ms + 랜덤 ✓ ✗ ✓ DB 기본 키 (신규 추천)
v8 사용자 정의 설계에 따라 설계에 따라 설계에 따라 특수 목적

3. UUID의 장점

3.1 중앙 조정 없이 고유한 ID 생성

DB의 AUTO_INCREMENT는 DB가 번호를 발급해 줘야 하지만, UUID는 애플리케이션, 클라이언트, 여러 서버 어디서든 독립적으로 생성할 수 있습니다. 분산 시스템, 마이크로서비스, 오프라인 앱 동기화 등에 유리합니다.

3.2 DB에 저장하기 전에 ID를 알 수 있음

INSERT 이전에 ID를 미리 만들 수 있으므로, 연관된 여러 엔티티를 한 번에 구성하거나 이벤트·메시지에 ID를 먼저 실어 보내기 쉽습니다.

3.3 데이터 병합이 쉬움

여러 DB나 샤드의 데이터를 합칠 때 정수 ID는 충돌하기 쉽지만, UUID는 그대로 합쳐도 겹치지 않습니다.

3.4 ID 추측(열거 공격) 방지

/users/1, /users/2 같은 순차 ID는 다른 리소스 ID를 쉽게 짐작할 수 있고, 전체 데이터 규모(가입자 수, 주문 수 등)도 노출됩니다. v4 UUID는 이런 추측을 어렵게 합니다.

⚠️ 단, UUID는 보안 토큰이 아닙니다. 추측하기 어렵다고 해서 권한 검사를 생략해서는 안 되며, v1·v6·v7은 생성 시각 같은 정보가 드러납니다. 비밀번호 재설정 토큰처럼 비밀성이 필요한 값은 별도의 암호학적 난수 토큰을 쓰세요.

3.5 표준화와 넓은 지원

RFC 표준이므로 거의 모든 언어와 DB가 UUID 생성·저장을 지원합니다. (PostgreSQL uuid 타입, Java java.util.UUID, Python uuid 모듈 등)


4. UUID의 단점

4.1 저장 공간이 큼

타입 크기
INT 4바이트
BIGINT 8바이트
UUID (바이너리) 16바이트
UUID (문자열 CHAR(36)) 36바이트 이상

기본 키가 커지면 이를 참조하는 모든 외래 키와 보조 인덱스도 함께 커집니다. 특히 MySQL InnoDB처럼 보조 인덱스가 기본 키 값을 함께 저장하는 구조에서는 영향이 큽니다.

👉 문자열 대신 바이너리로 저장하세요. (PostgreSQL uuid, MySQL BINARY(16) 등)

4.2 랜덤 UUID(v4)의 인덱스 성능 저하

B-Tree 인덱스는 값이 순서대로 들어올 때 가장 효율적입니다. v4처럼 완전히 무작위 값이 들어오면:

  • 인덱스의 아무 위치에나 삽입되어 페이지 분할(page split) 이 잦아짐
  • 메모리 캐시 효율(버퍼 풀 적중률)이 떨어짐
  • 결과적으로 INSERT 성능 저하와 인덱스 단편화

특히 클러스터드 인덱스를 쓰는 MySQL InnoDB, SQL Server에서 체감이 큽니다.

👉 해결책: UUID v7 사용. 시간순으로 증가하므로 순차 ID에 가까운 삽입 성능을 얻을 수 있습니다.

4.3 가독성이 떨어짐

f47ac10b-58cc-4372-a567-0e02b2c3d479는 사람이 읽고 말하고 기억하기 어렵습니다. 로그 분석, 고객 응대, 디버깅 시 불편합니다. 사용자에게 보여줄 주문번호 같은 값은 별도의 짧은 코드를 두는 경우가 많습니다.

4.4 정보 노출 가능성 (버전에 따라)

  • v1/v6: MAC 주소와 생성 시각 노출
  • v7: 생성 시각(밀리초) 노출 → "이 계정이 언제 생성됐는지"가 드러나는 것이 문제라면 주의

4.5 충돌이 "불가능"한 것은 아님

확률적으로 극히 낮을 뿐, 0은 아닙니다. 실제 문제는 대부분 난수 생성기가 부실할 때(시드 고정, VM 복제 직후 동일 상태 등) 발생합니다. 반드시 OS나 언어가 제공하는 안전한 난수원을 사용하는 표준 라이브러리를 쓰세요. 그리고 DB에는 UNIQUE/PRIMARY KEY 제약을 두어 만일의 충돌을 감지할 수 있게 하는 것이 좋습니다.


5. 어떤 걸 써야 할까? (실무 가이드)

ID를 이름(URL, 이메일 등)에서 항상 똑같이 만들어야 한다
 └─ YES → UUID v5

DB 기본 키로 쓸 것이다 / 생성 순서대로 정렬되면 좋겠다
 └─ YES → UUID v7  ⭐

생성 시각이 노출되면 안 된다 / 순서가 전혀 중요하지 않다
 └─ YES → UUID v4

기존 v1을 쓰는 시스템을 개선하고 싶다
 └─ YES → UUID v6 (또는 v7로 전환 검토)

한 줄 요약

  • 🆕 신규 프로젝트의 DB 키 → v7
  • 🎲 그 외 범용 랜덤 ID → v4
  • 🔁 이름 기반 결정적 ID → v5
  • 🚫 v1, v2, v3 → 새로 쓸 이유가 거의 없음

참고: UUID 외의 대안

이름 크기 특징
ULID 128비트 시간순 정렬 + Crockford Base32 26자 표현. v7과 개념이 비슷
Snowflake ID 64비트 트위터 고안. 타임스탬프 + 워커 ID + 시퀀스. 작지만 워커 ID 관리 필요
NanoID 가변 URL-safe 짧은 랜덤 문자열. 표준 UUID 형식은 아님

6. 언어별 생성 예시

Python

import uuid

uuid.uuid4()                                   # 랜덤
uuid.uuid5(uuid.NAMESPACE_URL, "https://example.com")  # 이름 기반
# uuid.uuid7()  # Python 3.14+ 에서 지원

JavaScript (브라우저 / Node.js)

// v4: 내장 API
crypto.randomUUID();

// v7: uuid 패키지 사용
import { v7 as uuidv7 } from 'uuid';
uuidv7();

Java

UUID id = UUID.randomUUID();   // v4
// v7은 java-uuid-generator 등 외부 라이브러리 사용

PostgreSQL

SELECT gen_random_uuid();  -- v4 (PostgreSQL 13+)
SELECT uuidv7();           -- v7 (PostgreSQL 18+)

CREATE TABLE orders (
    id uuid PRIMARY KEY DEFAULT uuidv7(),
    created_at timestamptz NOT NULL DEFAULT now()
);

MySQL

-- 문자열 대신 BINARY(16)으로 저장 권장
CREATE TABLE users (
    id BINARY(16) PRIMARY KEY
);
INSERT INTO users VALUES (UUID_TO_BIN(UUID(), 1));  -- v1을 시간순으로 재배치해 저장

7. 마무리

UUID는 "중앙 관리 없이 어디서든 고유한 ID를 만든다"는 강력한 장점이 있지만, 크기와 인덱스 성능이라는 비용이 따릅니다. 과거에는 이 비용 때문에 "UUID를 기본 키로 쓰지 말라"는 조언이 많았지만, 시간순 정렬이 가능한 UUID v7이 표준화되면서 그 단점이 상당 부분 해소되었습니다.

  • 정렬과 성능이 중요하면 v7
  • 순서 정보가 노출되지 않아야 하면 v4
  • 같은 입력에 같은 ID가 필요하면 v5

이 세 가지만 기억해도 실무에서 UUID를 고르는 데 충분합니다.


참고 자료

답글 남기기