GUID 버전 해설: v1–v7의 차이와 선택 가이드
2026-07-09
태그: Windows · GUID · 가이드 · QuickGUID
UUID 라이브러리를 열면 uuid1(), uuid3(), uuid4(), uuid5(), 그리고 점점 더 uuid7()이 보입니다. 사양은 여덟 개의 버전을 정의합니다. 실제로는 어떤 것을 써야 할까요?
짧은 대답: 거의 모든 것엔 v4, 데이터베이스 기본 키엔 v7, 같은 입력이 항상 같은 GUID를 내야 할 때는 v5. 이 글의 나머지는 그 이유를 설명합니다 — 바이트 구성, 각 버전이 존재하는 진짜 이유, 언어별 코드 요약표와 함께.
GUID가 처음이시라면 먼저 GUID란 무엇인가?에서 구조와 고유성의 기초를 잡으세요. 이 글은 그 기술적 동반자로, 각 버전을 더 깊이 파고듭니다.
GUID/UUID 버전 전경
각 버전은 같은 128비트를 채우는 서로 다른 레시피입니다. 버전 니블(세 번째 세그먼트의 첫 16진 숫자)이 어떤 레시피를 썼는지 알려줍니다.
| 버전 | 생성 원천 | 정렬 가능 | 전형적 용도 |
|---|---|---|---|
| v1 | 시간 + MAC | 예 | 레거시 시스템 (프라이버시 위험) |
| v2 | DCE Security | 예 | 드묾, 전문 용도 |
| v3 | MD5 네임스페이스 해시 | 아니오 | 사용 중단 (v5 사용) |
| v4 | 무작위 | 아니오 | 일반 기본값 |
| v5 | SHA-1 네임스페이스 해시 | 아니오 | 결정론적 ID |
| v6 | 재정렬된 v1 시간 | 예 | v1의 직접 대체 |
| v7 | 밀리초 타임스탬프 | 예 | DB 기본 키 |
| v8 | 벤더 정의 | 커스텀 | 전문 용도 |
v1, v4, v5, v7이 실사용의 99%를 덮습니다. 하나씩 살펴봅시다.
v1: 시간과 MAC 주소
v1은 RFC 4122의 원조 레시피입니다. 세 가지를 128비트에 채워 넣습니다:
| time_low (32) | time_mid (16) | ver+time_hi (16) | var+clk_seq (16) | node/MAC (48) |- 60비트 타임스탬프 — 1582-10-15(그레고리력 채용일) 이후 100나노초 간격. 타임스탬프는
time_low,time_mid,time_hi에 걸쳐 쪼개집니다. - 14비트 클록 시퀀스 — 같은 클록 틱 안에서 생성된 두 GUID, 또는 시스템 시계가 뒤로 돌아간 경우를 구별하는 카운터.
- 48비트 노드 — 네트워크 카드의 MAC 주소.
MAC 주소 문제
노드 필드는 하드웨어 지문입니다. 게시하는 모든 v1 GUID가 여러분 머신의 MAC 주소를 실어 나릅니다 — 그리고 IP 주소와 달리 MAC 주소는 전역적으로 고유하고 영속적이도록 설계되었습니다. 이건 진짜 프라이버시 유출입니다.
이론이 아닙니다. 연구자들은 파일 메타데이터에 박힌 v1 GUID를 읽어 문서를 만든 머신까지 역추적할 수 있음을 보였습니다. Microsoft 스스로 이게 시연된 뒤 Office 원격측정에서 v1 생성을 제거했습니다. 현대 OS도 같은 이유로 Wi-Fi 네트워크에 보여주는 MAC을 무작위화합니다.
결론: 새 코드에서는 v1을 피하세요. 레거시 시스템, COM 인터페이스, 일부 하드웨어 인접 도구에 살아남습니다 — 하지만 오늘날 직접 생성할 이유는 없습니다.
v2: DCE Security
v2는 사실상 결코 마주치지 못할 버전입니다. v1의 배치(타임스탬프 + MAC)를 가져와 그 위에 POSIX UID/GID — DCE(Distributed Computing Environment) 보안 모델의 로컬 사용자 및 그룹 식별자 — 를 올려, UUID가 고유성과 함께 접근 제어 정보도 실어 나르게 합니다.
v2는 레거시 엔터프라이즈 시스템의 DCE/RPC 인증과 ACL을 위해 존재합니다. 명시적으로 DCE Security를 사용하는 소프트웨어를 유지보수하지 않는 한, v2를 생성하거나 소비할 일은 없을 것입니다. 이 항목은 버전 목록을 완전하게 두고, 언젯가 그 니블에서 '2'가 보일 때 그 의미를 헤매지 않도록 하려는 것뿐입니다.
v3: 사용 중단된 이유
v3는 SHA-1 대신 MD5를 쓴다는 점만 빼면 v5와 동일합니다. MD5는 실용적인 충돌 공격이 있습니다 — 서로 다른 두 입력이 같은 해시를 내도록 만들 수 있습니다. 그게 적대적 환경에서 v3의 결정성 보증을 깹니다.
RFC 4122는 하위 호환을 위해 v3를 남겨뒀지만 v5가 대체했습니다. 오늘날 v3를 생성할 이유는 없습니다. 야생에서 v3를 만나면 레거시로 취급하세요.
v4: 무작위 기본값
v4는 사실상 모든 “GUID 생성” 호출 [guid]::NewGuid(), Guid.NewGuid(), crypto.randomUUID(), uuid.uuid4(), NEWID()이 돌려주는 것입니다. 128비트 중 122비트를 암호학적으로 강한 무작위로 채웁니다(나머지 6비트는 버전·변형 마커).
타임스탬프도, MAC도, 머신 상태도 없음 — 순수 무작위. 그래서 v4는 노출해도 안전하고, 병렬화는 자명하며, 현실적인 어떤 부하에도 충분한 충돌 내성을 가집니다. 충돌 수학의 전모(그리고 “10억 년”이라는 떠도는 주장이 생일 역설을 왜 틀리게 읽는지)는 허브 글을 보세요.
결론: 이것이 기본값입니다. 다른 걸 고를 구체적 이유가 없다면 v4를 고르세요.
v5: 결정론적 네임스페이스 ID
이건 대부분 개발자가 들어는 봤지만 실제로 써본 적 없는 버전입니다 — 그런데 진짜 쓸모 있는 문제를 해결합니다.
v5는 같은 입력에서 매번 같은 GUID를 도출하게 해 줍니다. 네임스페이스 UUID와 이름 문자열을 주면 결정론적 식별자를 돌려줍니다:
import uuid
# 같은 입력 → 같은 GUID, 매 실행, 모든 머신에서
dns_guid = uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')
url_guid = uuid.uuid5(uuid.NAMESPACE_URL, 'https://prunelab.net/blog/guid-explained')
print(dns_guid) # 항상 cf14e0a3-... (같은 값)
print(dns_guid == uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')) # True작동 방식
내부적으로 v5는 네임스페이스 UUID 바이트와 이름의 UTF-8 바이트를 이어 붙이고, 결과에 SHA-1을 돌리고, 앞 16바이트를 취한 뒤 버전(5)·변형 비트를 찍습니다. 네임스페이스 자체도 UUID입니다 — NAMESPACE_DNS, NAMESPACE_URL, NAMESPACE_OID, NAMESPACE_X500이 미리 정의되어 있지만, 어떤 UUID든 네임스페이스로 쓸 수 있습니다.
v5가 알맞은 도구일 때
- 재현 가능한 테스트 픽스처 — 실행 간 안정적인 테스트 데이터 ID를 생성해서, 실패한 테스트가 항상 같은 엔티티를 가리키게 하기.
- URL 유래 식별자 — 데이터베이스 조회 없이 URL을 GUID로 바꾸기. 같은 URL에 독립적으로
uuid5(NAMESPACE_URL, url)을 계산한 두 시스템은 항상 일치합니다. - 멱등 중복 제거 — 같은 레코드를 두 번 가져오면 GUID가 자기 자신과 충돌하므로, 키만으로 중복을 감지할 수 있습니다.
v4와의 핵심 대조: v4는 호출마다 고유값을 돌려줍니다(같은 입력이어도); v5는 같은 입력에 안정적인 값을 돌려줍니다. 둘은 교환 가능하지 않습니다.
결론: 입력 문자열에서 재현 가능하게 도출되는 GUID가 필요할 때마다 v5를 쓰세요.
v6: 아마 필요 없는 v1 수정판
v6은 v1과 정확히 같은 데이터 — 같은 타임스탬프, 같은 클록 시퀀스와 MAC — 를 담지만, 값이 자연스럽게 연대순으로 정렬되도록 타임스탬프 비트를 재배열합니다. v1에서는 타임스탬프가 어색하게 쪼개져(low, mid, high) 있지만, v6에서는 high-first로 저장되어 문자열의 사전식 정렬이 시간 정렬과 같습니다.
v6은 2024년에 v7과 함께 RFC 9562로 표준화되었습니다. 주로 v1의 클록·MAC 모델에 이미 묶인 시스템용 직접 마이그레이션 경로로 존재합니다: 기존 타임스탬프 로직은 그대로 두고 비트 배치만 다시 쓰면 됩니다.
새 시스템에서는 v7이 거의 항상 더 나은 선택입니다. v7은 밀리초 타임스탬프(v1/v6의 100ns 그레고리력 계산보다 단순)를 쓰고, MAC 주소를 박지 않으며, 더 깔끔한 무작위 채우기를 가집니다. v6은 “생성 모델을 바꾸지 않고 v1을 어떻게 현대화하나”에 답하고, v7은 “앞으로 뭘 써야 하느냐”에 답합니다.
v7: 데이터베이스를 위한 시간 정렬
v7은 v4 이후 가장 중요한 추가입니다. v4가 데이터베이스에서 만드는 문제를 해결합니다.
데이터베이스에서의 v4 문제
데이터베이스가 GUID를 기본 키로 쓰면, 그 키는 B+ 트리 인덱스를 떠받칩니다. B+ 트리는 삽입이 대략 정렬되어 있을 때 효율을 유지합니다 — 새 값이 오른쪽 끝에 가서 기존 페이지를 채웁니다. 무작위 v4 GUID는 전체 키 공간에 흩어지므로, 매 삽입마다 무작위 페이지를 칩니다. 큰 테이블에서는 이게 끊임없는 페이지 분할, 인덱스 파편화, 캐시 스래시, 쓰기 증폭을 일으킵니다. SQL Server의 해결책(NEWSEQUENTIALID())은 시퀀싱을 누출하는 독점적이고 이식성 없는 핵입니다.
v7의 구성
| unix_ts_ms (48) | ver (4) | rand_a (12) | var (2) | rand_b (62) |- 48비트 밀리초 Unix 타임스탬프 — 선행 필드라 값이 시간에 따라 증가합니다.
- 4비트 버전 —
7로 설정. - 12비트 rand_a — 무작위, 또는 같은 밀리초 안에서 순서를 매기는 단조 카운터로 선택적 사용.
- 2비트 변형 — 표준 RFC 9562 마커.
- 62비트 rand_b — 무작위.
타임스탬프가 앞에 오기 때문에, 연속으로 생성된 v7 GUID는 자연스럽게 정렬됩니다: 인덱스 삽입이 오른쪽 끝에 모이고, 페이지가 깔끔하게 채워지며, B+ 트리는 자동증가 정수처럼 행동합니다 — 그러면서도 중앙 조정자 없이 전역 고유합니다.
v7이 가치 없을 때
- 인메모리 스토어(Redis, memcached) — B+ 트리 없음, 파편화 비용 없음. v4면 충분.
- 작은 테이블 — 수백만 행 아래에서는 v4와 v7의 차이가 보이지 않습니다.
- 비키 열 — GUID가 저장된 속성이지 인덱스 키가 아니면 순서는 무관합니다.
- 불투명해야 하는 GUID — v7은 생성 밀리초를 누출하고, 보안에 민감한 맥락에서는 바람직하지 않을 수 있습니다.
결론: GUID가 진짜 데이터베이스의 클러스터형 기본 키면 v7을 쓰세요. 그 외엔 v4.
v8: 벤더 정의
v8은 비상구입니다: RFC 9562는 버전 니블이 8이고 변형 비트가 올바른 한 벤더가 자체 비트 배치를 정의하도록 허용합니다. 그 밖의 모든 것 — 타임스탬프 폭, 무작위 원천, 인코딩 — 은 구현자 마음입니다.
v8은 특정 이유로 커스텀 UUID 스키마를 설계한 시스템(예: 라우팅을 위해 샤드 ID나 노드 ID를 UUID에 구워 넣는 경우)에서만 볼 수 있습니다. 그런 시스템을 구축하지 않는다면 v8은 무관합니다; 구축 중이라면 규칙은 이 글의 어떤 것이 아니라 당신 자신의 스키마가 정의합니다.
실제로 어떤 버전을 써야 할까?
의사결정 가이드:
- “일반 용도의 ID 생성?” → v4. 90% 경우의 답입니다.
- “DB 기본 키, 또는 시간 정렬 필요?” → v7. 특히 큰 테이블의 클러스터형 인덱스에서.
- “같은 입력에 매번 같은 GUID 필요?” → v5. 문자열에서의 결정론적 매핑.
- “v1을 기대하는 레거시 시스템 작업?” → v1(호환성 유지), 단 v7 마이그레이션 계획.
- “누가 v3를 쓰라고 했다?” → 안 됩니다. 대신 v5를(v3의 MD5는 망가졌습니다).
- “가장 새 게 좋다.” → 그건 이유가 아닙니다. v7은 새롭고 동시에 진짜 문제(DB 정렬)를 해결합니다; v6은 새롭고 동시에 주로 v1 마이그레이션 보조입니다. 신규성이 아니라 유스케이스로 고르세요.
여전히 망설이면 v4를 쓰세요. 기본값이 있는 데는 이유가 있습니다.
버전과 언어별 GUID 생성
| 언어 / 도구 | v4(기본값) | v5(결정론적) | v7(정렬형) |
|---|---|---|---|
| PowerShell | [guid]::NewGuid() | — | — |
| C# / .NET | Guid.NewGuid() | — | Guid.CreateVersion7() (.NET 9+) |
| Python | uuid.uuid4() | uuid.uuid5(uuid.NAMESPACE_URL, 'name') | uuid.uuid7() (3.14 beta) / 라이브러리 |
| JavaScript | crypto.randomUUID() | — | (npm uuid v11+) |
| Node.js (uuid 라이브러리) | uuid.v4() | uuid.v5('name', NAMESPACE) | uuid.v7() |
| SQL Server | NEWID() | — | — |
| PostgreSQL | gen_random_uuid() | — | (확장) |
몇 가지 메모: 표준 라이브러리에 v7 네이티브 지원은 2026년 현재 여전히 자리잡는 중입니다. Node.js uuid 패키지(v11+)와 .NET 9가 네이티브로 제공하고, Python은 3.14에서 uuid.uuid7()을 추가하며; 오래된 런타임은 라이브러리가 필요합니다. v5 지원은 거의 보편적인데, 엔트로피 원천 없이 순수 SHA-1 수학이기 때문입니다.
자주 묻는 질문
v7이 v4를 기본값에서 대체하나요?
아니오. v4가 일반 기본값으로 남습니다. v7은 GUID가 정렬된 기본 키일 때만 이깁니다 — 다른 대부분의 용도(세션 토큰, 요청 ID, 콘텐츠 해시, DB 밖의 모든 것)에서 v4의 무작위성은 단점이 아니며, v7의 선행 타임스탬프는 이득 없이 타이밍 정보만 누출할 것입니다.
v1은 여전히 안전하게 쓸 수 있나요?
아니오. v1은 네트워크 카드의 MAC 주소를 박습니다 — 영속적인 하드웨어 지문입니다. 연구자들이 문서에 박힌 GUID로 머신을 추적한 뒤, Microsoft 스스로 Office 원격측정에서 v1을 제거했습니다. 새 코드에서는 v1을 완전히 피하세요; 레거시 v1은 v7로의 마이그레이션을 계획하세요.
v3와 v5의 차이는?
둘 다 결정론적 네임스페이스 해시입니다(같은 입력 → 같은 GUID). v3는 MD5, v5는 SHA-1을 씁니다. MD5에는 실용적 충돌 공격이 있어 v3는 무결성 목적에서 망가진 것으로 간주되고 v5로 대체되었습니다. 오늘날 v3를 생성할 이유는 없습니다.
v4 GUID를 v7로 변환할 수 있나요?
아니오. 버전은 바꿔 끼울 수 있는 꼬리표가 아닙니다 — 각 버전은 완전히 다른 생성 로직을 씁니다. v4에는 추출할 타임스탬프가 없어서, “v7을 얻는” 유일한 방법은 새로 하나 생성하는 것입니다. v4 기본 키 열을 v7로 마이그레이션해야 한다면, 실제로는 새 열을 추가하고 다시 쓰는 것입니다.
.NET은 v7을 네이티브로 지원하나요?
네. Guid.CreateVersion7()이 .NET 9(2024년 11월 출시)에 추가되었습니다. .NET 8 이전이라면 NuGet 패키지 GenVault나 UUIDv7을 쓰거나, 업그레이드하세요.
QuickGUID: v4·v7 생성, 모든 형식 변환
일상적인 GUID 작업에 QuickGUID는 전체 워크플로를 덮는 Windows 네이티브 도구 상자입니다:
- 일괄 생성 v4 또는 v7, 한 번에 최대 1,000개
- 10+ 형식 변환: 표준, 하이픈 없음, 중괄호, Base64, C 바이트 배열,
DEFINE_GUID매크로 — 모두 실시간 미리보기 - 스마트 추출: 로그나 소스 코드를 붙여 넣으면 모든 GUID를 자동으로 찾아 변환
- 실시간 데코레이터: 따옴표, 접두/접미사, 대/소문자 — 전역 토글하고 바로 복사
완전 무료, 100% 오프라인.
맺음말
버전 시스템이 있는 건, 서로 다른 일이 서로 다른 보증을 요구하기 때문입니다: v4는 추적 불가능한 무작위성을, v5는 재현성을, v7은 정렬성을 줍니다. 기본은 v4, 데이터베이스 키에서는 v7, 같은 입력에 같은 GUID가 필요할 땐 v5를 쓰세요.
GUID가 처음이신가요? GUID란 무엇인가?에서 구조, 하이픈 형식, 그리고 왜 128비트면 실전에서 충돌을 논제외로 만들기에 충분한지의 기초를 먼저 보세요.