🔑 UUID란 무엇인가? — 충돌 없는 식별자의 세계
개발을 하다 보면 이런 문자열을 자주 마주치게 됩니다.
550e8400-e29b-41d4-a716-446655440000이것이 바로 UUID(Universally Unique Identifier, 범용 고유 식별자)입니다. 하이픈으로 구분된 32개의 16진수 문자로 이루어진 이 값은, 세상 어디서 생성하더라도 같은 값이 나올 확률이 사실상 0에 가깝습니다.
UUID가 왜 필요할까요? 전통적인 데이터베이스에서는 1, 2, 3 같은 순차적인 숫자(Auto Increment)를 기본 키(Primary Key)로 사용했습니다. 단순하고 빠르지만, 분산 환경에서는 심각한 문제가 생깁니다. 서버 A에서 1번 레코드를 만들고, 서버 B에서도 1번 레코드를 만들면 충돌이 발생합니다. UUID는 이 문제를 단번에 해결합니다. 중앙 서버와 조율 없이도 각 노드에서 독립적으로 고유한 ID를 생성할 수 있기 때문입니다.
HMApps UUID 생성기를 사용하면 클릭 한 번으로 즉시 UUID를 생성하고 복사할 수 있습니다. 개발 중 테스트 데이터가 필요할 때, API 요청 추적 ID가 필요할 때, 파일명에 고유성이 필요할 때 바로 활용해 보세요.
📐 UUID의 구조 — 32자에 담긴 비밀
UUID는 총 128비트(16바이트)의 데이터를 16진수로 표현하며, 다음과 같은 구조를 가집니다.
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
│ │ │ │ │
│ │ │ │ └── 노드(48비트) — 버전에 따라 의미 다름
│ │ │ └───── 변형 비트(variant) — 표준 UUID는 10xx
│ │ └────────── M: 버전 번호 (1~8)
│ └─────────────── 시간 상위 비트 등 (버전에 따라 다름)
└───────────────────────── 시간 하위 비트 등 (버전에 따라 다름)각 섹션을 자세히 살펴보면:
- time_low (8자리): 시간의 하위 32비트 (v1의 경우)
- time_mid (4자리): 시간의 중간 16비트
- time_hi_and_version (4자리): 시간의 상위 12비트 + 버전 번호(M)
- clock_seq_hi_and_res (2자리): 클락 시퀀스 상위 6비트 + 변형 비트(N)
- clock_seq_low (2자리): 클락 시퀀스 하위 8비트
- node (12자리): 노드 식별자 (48비트)
UUID를 보면 버전을 즉시 알 수 있습니다. 세 번째 그룹의 첫 번째 자리가 버전 번호입니다. 550e8400-e29b-41d4-a716-...에서 4는 Version 4를 의미합니다.
🗂️ UUID 버전별 특징 — 어떤 버전을 써야 할까?
UUID에는 여러 버전이 있으며, 각각 생성 방식과 용도가 다릅니다. RFC 9562(구 RFC 4122)가 공식 표준입니다.
| 버전 | 생성 방식 | 특징 | 주요 용도 |
|---|---|---|---|
| v1 | 타임스탬프 + MAC 주소 | 시간순 정렬 가능, MAC 주소 노출 위험 | 레거시 시스템, 시간 추적 필요 시 |
| v2 | DCE Security | v1 + 도메인/UID 정보 포함, 거의 사용 안 함 | DCE 환경 (실무에서 거의 미사용) |
| v3 | 네임스페이스 + MD5 해시 | 결정적(deterministic), 같은 입력 → 같은 UUID | URL, DNS 기반 고유 ID 생성 |
| v4 | 무작위(랜덤) | 가장 널리 사용, 예측 불가능, 충돌 확률 극히 낮음 | 대부분의 일반 목적, DB PK |
| v5 | 네임스페이스 + SHA-1 해시 | 결정적, v3보다 해시 강도 높음 | URL, DNS 기반 (v3 대체) |
| v6 | 타임스탬프 재정렬 (v1 개선) | 시간순 정렬 가능, MAC 주소 노출 없음 | DB 인덱스 최적화가 필요한 경우 |
| v7 | 유닉스 밀리초 타임스탬프 + 랜덤 | 시간순 정렬 + 무작위성, 최신 표준 | 현대적 DB PK (권장) |
| v8 | 커스텀 (자유 형식) | 구현체가 자유롭게 정의 | 특수 목적 확장 |
2024년 이후 신규 프로젝트라면 v7을 적극 고려하세요. v4의 무작위성을 유지하면서도 시간순으로 정렬이 가능해 데이터베이스 인덱스 성능이 훨씬 좋습니다. 단, 지원하는 라이브러리를 확인해야 합니다.
💻 실제 코드에서 UUID 사용하기 — 언어별 예시
각 언어/프레임워크에서 UUID를 생성하는 방법을 알아봅시다.
JavaScript / TypeScript
// 브라우저 내장 (Web Crypto API) — v4
const uuid = crypto.randomUUID()
// 결과: '110e8400-e29b-41d4-a716-446655440000'
// Node.js 내장 (v14.17+)
import { randomUUID } from 'crypto'
const uuid = randomUUID()
// npm 패키지 'uuid' — 다양한 버전 지원
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid'
const idV4 = uuidv4()
const idV7 = uuidv7() // 시간순 정렬 가능Python
import uuid
# v4 (무작위)
uid = uuid.uuid4()
print(str(uid)) # 'a8098c1a-f86e-11da-bd1a-00112444be1e'
# v5 (네임스페이스 기반, 결정적)
uid5 = uuid.uuid5(uuid.NAMESPACE_URL, 'https://hmapps.kr')
print(str(uid5)) # 항상 같은 결과
# 문자열로 변환 시 하이픈 제거
uid_no_dash = uid.hex # '550e8400e29b41d4a716446655440000'Java / Kotlin
// Java
import java.util.UUID;
UUID uuid = UUID.randomUUID();
System.out.println(uuid.toString());
// "123e4567-e89b-12d3-a456-426614174000"
// Kotlin
val uuid = java.util.UUID.randomUUID()
println(uuid)
// Spring Boot + JPA에서 PK로 사용
@Entity
class User {
@Id
@GeneratedValue
private UUID id;
}Go
// go get github.com/google/uuid
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
id := uuid.New()
fmt.Println(id.String())
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
// v7 (시간순 정렬 가능)
idV7, _ := uuid.NewV7()
fmt.Println(idV7.String())
}SQL (데이터베이스)
-- MySQL / MariaDB
SELECT UUID();
-- '6ccd780c-baba-1026-9564-5b8c656024db'
-- PostgreSQL (uuid-ossp 확장)
SELECT gen_random_uuid(); -- v4
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
SELECT uuid_generate_v4();
-- PostgreSQL 테이블 정의
CREATE TABLE orders (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW()
);🗄️ 데이터베이스 PK로 UUID 사용 시 주의사항
UUID는 강력하지만, 무작위 UUID(v4)를 DB 기본 키로 쓸 때 알아야 할 함정이 있습니다.