uuid: 유일 식별자 생성
Python uuid 모듈의 UUID 버전별 특성과 사용법을 정리합니다. uuid1/uuid3/uuid4/uuid5의 차이, UUID 객체 속성(hex/bytes/int), 이름 기반 UUID, 데이터베이스 기본 키 패턴, 보안 고려사항을 다룹니다.
지난 글에서 sqlite3로 관계형 데이터를 다뤘습니다. 이번 글에서는 데이터베이스 기본 키, 세션 토큰, 멱등 키 등 다양한 곳에서 쓰이는 UUID를 생성하는 uuid 모듈을 정리합니다. UUID는 128비트의 숫자로, 중앙 조율 없이도 전 세계적으로 유일한 식별자를 만들 수 있습니다.
UUID란?
UUID(Universally Unique Identifier)는 RFC 4122로 표준화된 128비트 식별자입니다. 32개의 16진수 숫자와 4개의 하이픈으로 구성됩니다.
550e8400-e29b-41d4-a716-446655440000
2^128가지(약 3.4 × 10^38)의 가능한 값이 있어, 초당 10억 개를 생성해도 충돌까지 수십억 년이 걸립니다.
UUID 버전별 생성 방법
UUID v4 — 완전 랜덤 (가장 많이 사용)
import uuid
uid = uuid.uuid4()
print(uid) # 550e8400-e29b-41d4-a716-446655440000 (매번 다름)
print(str(uid)) # UUID → str 변환
print(uid.hex) # 하이픈 없는 hex 문자열
print(uid.bytes) # 16바이트 bytes 객체
print(uid.int) # 128비트 정수
print(uid.version) # 4
v4는 암호학적으로 안전한 랜덤 값으로 생성됩니다.
UUID v1 — 타임스탬프 기반
uid1 = uuid.uuid1()
print(uid1.time) # 100나노초 단위 타임스탬프
print(uid1.node) # MAC 주소 (48비트)
v1은 생성 시각 순으로 정렬 가능하지만 MAC 주소가 노출되어 개인정보 문제가 있습니다. 시간순 정렬이 필요하다면 Python 3.14+의 uuid7()를 사용하거나, ulid-py 같은 라이브러리를 쓰세요.
UUID v3, v5 — 이름 기반 (재현 가능)
같은 입력에서 항상 같은 UUID를 생성합니다. v3는 MD5, v5는 SHA-1을 사용합니다.
import uuid
# 미리 정의된 namespace 상수
ns = uuid.NAMESPACE_URL
ns_dns = uuid.NAMESPACE_DNS
ns_oid = uuid.NAMESPACE_OID
# v5 (SHA-1, 권장)
uid_a = uuid.uuid5(ns, 'https://paldyn.com')
uid_b = uuid.uuid5(ns, 'https://paldyn.com')
print(uid_a == uid_b) # True — 항상 동일
# v3 (MD5, 하위호환용)
uid_c = uuid.uuid3(ns, 'https://paldyn.com')
같은 리소스에 항상 동일한 ID를 부여하거나, URL을 고정 UUID로 매핑할 때 유용합니다.
UUID 객체 파싱
import uuid
# 문자열 → UUID 객체
uid = uuid.UUID('550e8400-e29b-41d4-a716-446655440000')
print(uid.version) # 4 (자동 감지)
# hex 문자열 (하이픈 없음)
uid2 = uuid.UUID('550e8400e29b41d4a716446655440000')
# bytes에서 생성
uid3 = uuid.UUID(bytes=b'\x55\x0e...')
# 잘못된 형식 → ValueError
try:
uuid.UUID('not-a-uuid')
except ValueError as e:
print(e)
데이터베이스 기본 키로 사용
import uuid
from dataclasses import dataclass, field
@dataclass
class Order:
id: str = field(default_factory=lambda: str(uuid.uuid4()))
product: str = ''
quantity: int = 0
order1 = Order(product='노트북', quantity=1)
order2 = Order(product='마우스', quantity=2)
print(order1.id) # '6ba7b810-9dad-...'
print(order2.id) # '6ba7b811-9dad-...'
PostgreSQL, MySQL에는 UUID 전용 컬럼 타입이 있습니다. SQLite에서는 TEXT로 저장합니다.
멱등(idempotent) 키 패턴
같은 요청이 반복되더라도 한 번만 처리되도록 할 때 UUID를 요청 키로 씁니다.
import uuid
import requests
idempotency_key = str(uuid.uuid4())
response = requests.post(
'https://api.payment.example/charge',
json={'amount': 9900, 'currency': 'KRW'},
headers={'Idempotency-Key': idempotency_key},
)
네트워크 오류로 재요청해도 서버는 같은 키를 가진 요청을 중복 처리하지 않습니다.
보안 고려사항
- UUID v4 사용 권장: v1은 MAC 주소가 포함되어 서버 정보가 노출됩니다.
- 예측 불가:
uuid4()는os.urandom()을 기반으로 암호학적으로 안전합니다. - 비밀번호 초기화 토큰: UUID를 그대로 쓰지 말고
secrets.token_urlsafe()를 씁니다. UUID는 보안 토큰이 아닌 식별자 용도입니다.
import secrets
# 보안 토큰 (더 안전)
token = secrets.token_urlsafe(32)
# 식별자
identifier = str(uuid.uuid4())
지난 글: sqlite3: 내장 관계형 데이터베이스
다음 글: hashlib: 해시 함수와 암호화 기초
읽어주셔서 감사합니다. 😊