개발을 하다 보면 회원 ID, 주문 번호, 파일명을 만들 때 단골손님처럼 등장하는 녀석이 있습니다. 바로 UUID입니다.
60447385-d6e1-4541-9457-466d3a43657c
그런데 왜 선배 개발자들은 이 예쁜(?) 하이픈을 굳이 빼버리거나, .toLowerCase()를 꾸역꾸역 호출하는 걸까요? 오늘은 UUID의 탄생 배경부터 인간의 직관을 아득히 뛰어넘는 충돌 확률, 그리고 실무 최적화 팁까지 재미있게 풀어봅니다.
UUID가 도대체 뭔데 이렇게 유세야?
UUID(Universally Unique Identifier)는 직역하면 ‘전 우주적으로 고유한 식별자’입니다. 128비트(16바이트)짜리 거대한 숫자 조합이죠.
데이터베이스를 처음 배울 때 ID를 1, 2, 3, 4... 같은 자동 증가(Auto-Increment) 숫자로 지정합니다. 중앙 서버가 한 대라면 제일 편하죠. 그런데 서비스가 커져 서버가 수십 대로 늘어나고 전 세계에 DB가 분산된다면?
- 서울 서버: “나 지금 500번 회원 가입 시켰어!”
- 뉴욕 서버: “어? 나도 방금 500번 가입시켰는데?!”
이런 충돌을 막으려면 번호표만 나눠주는 중앙 서버를 따로 둬야 하는데, 이게 또 병목(bottleneck)의 원인이 됩니다. UUID는 이 문제를 우아하게 해결합니다. **“중앙 서버에 물어보지 말고, 각자 방구석에서 번호를 뽑아도 절대 안 겹치게 하자”**가 UUID의 핵심 철학입니다.
”그래도 겹치면 어떡해요?” — 확률의 세계
가장 흔히 쓰이는 UUID v4는 무작위(Random) 숫자 기반으로 생성됩니다. 랜덤이라는 말을 들으면 개발자 특유의 불안감이 엄습하죠. “만약에, 진짜 만약에 전 세계 누군가와 내 UUID가 겹치면 어쩌지?”
UUID v4의 무작위 비트 수
UUID는 128비트이지만, 그 중 6비트는 버전/변형(variant) 식별용으로 고정됩니다. 실제로 무작위로 선택되는 비트는 122비트로, 가능한 조합 수는 2¹²² ≈ 5.3 × 10³⁶가지입니다.
불안해하실 여러분을 위해 현실의 기적들과 확률을 비교해 봤습니다.
| 사건 | 대략적인 확률 | 직관적인 느낌 |
|---|---|---|
| 길 가다 벼락 맞기 (연간) | 약 10⁻⁶ (100만분의 1) | “이번 생은 조심하자” |
| 로또 1등 당첨 | 약 1.23 × 10⁻⁷ (814만분의 1) | “매주 사도 안 됨” |
| 선거 1·2위 동일 득표 | 약 1.69 × 10⁻⁹ (약 59억분의 1) | “뉴스 대서특필될 기적” |
| UUID v4 단 2개가 충돌할 확률 | 약 1.9 × 10⁻³⁷ | 숫자 단위 ‘간(澗)‘의 영역 |
로또 1등에 당첨되는 동시에 그 자리에서 벼락을 맞는 일이 연속으로 3번 일어나도, UUID 두 개가 충돌할 확률보다는 훨씬 높습니다.
데이터가 쌓이면요? — 생일 역설(Birthday Paradox)
데이터가 누적될수록 충돌 확률이 올라가는 것은 사실입니다. 하지만 매초 10억 개씩 UUID를 85년 동안 쉬지 않고 생성해서 약 270경(2.7 × 10¹⁸) 개가 쌓여야, 비로소 ‘선거 동일 득표’ 수준의 충돌 확률에 도달합니다.
결론: “지구가 멸망할 확률이 더 높으니 중복 걱정은 내려놓자.”
실무에서는 왜 ‘변형’해서 쓸까?
안전한 건 알겠는데, 왜 소문자로 바꾸고 하이픈을 빼서 쓸까요? 여기엔 시니어들의 눈물겨운 최적화 노하우가 녹아 있습니다.
왜 대문자가 아니라 소문자?
UUID 공식 표준인 RFC 4122는 대소문자를 구분하지 않는다고 하지만, 문자열로 다룰 때는 소문자 출력을 권장(SHOULD) 합니다.
어떤 시스템은 대문자로, 다른 시스템은 소문자로 UUID를 반환한다면 컴퓨터는 두 문자열을 다른 값으로 판단합니다. 이로 인한 정합성 오류를 원천 차단하려면 처음부터 소문자로 못 박아두는 게 정신 건강에 좋습니다. URL이나 파일 경로에 들어갈 때도 소문자가 훨씬 안전하고요.
왜 귀찮게 하이픈(-)을 제거할까?
UUID를 그대로 쓰면 36글자이지만, 하이픈 4개를 빼면 32글자로 줄어듭니다.
- 디스크·메모리 절약: 수억 건이 쌓이는 대규모 시스템에서는 단 4글자를 줄이는 것만으로 수 GB의 DB 용량과 인덱스 메모리를 아낄 수 있습니다. 돈이 굳는 거죠.
- 코드 안전성: 프로그래밍 언어나 SQL에서 하이픈(
-)은 ‘빼기(minus)’ 연산자로 오해받기 딱 좋습니다. 식별자로 UUID를 쓸 때 하이픈이 있으면 쿼리가 터지거나 에러가 날 수 있어, 미리 빼버리는 편이 안전합니다.
코드 없이 UUID 뚝딱 만드는 법 (OS별)
테스트 데이터 입력, API 호출 샘플, DB 시드(seed) 작업 등에서 UUID 한두 개가 급하게 필요할 때가 있습니다. 굳이 코드를 짜거나 사이트를 찾아 헤매지 않아도 터미널 한 줄로 해결됩니다.
macOS
uuidgen 명령이 기본 내장되어 있습니다. 단, 기본 출력이 대문자라는 점에 주의하세요.
# 기본 (대문자 + 하이픈)
uuidgen
# → 3F2504E0-4F89-11D3-9A0C-0305E82C3301
# 소문자로
uuidgen | tr '[:upper:]' '[:lower:]'
# → 3f2504e0-4f89-11d3-9a0c-0305e82c3301
# 소문자 + 하이픈 제거 (DB 저장용)
uuidgen | tr -d '-' | tr '[:upper:]' '[:lower:]'
# → 3f2504e04f8911d39a0c0305e82c3301
Linux
uuidgen은 util-linux 패키지에 포함되어 있으며, 대부분의 배포판에 기본 설치되어 있습니다. macOS와 달리 기본 출력이 소문자입니다.
# 기본 (소문자 + 하이픈)
uuidgen
# → 3f2504e0-4f89-11d3-9a0c-0305e82c3301
# 하이픈 제거
uuidgen | tr -d '-'
# → 3f2504e04f8911d39a0c0305e82c3301
# 설치가 안 되어 있다면 (Debian/Ubuntu)
# sudo apt install uuid-runtime
커널이 직접 제공하는 방법도 있습니다.
cat /proc/sys/kernel/random/uuid
Windows (PowerShell)
PowerShell에 내장된 .NET 타입을 그대로 활용합니다.
# 기본 (소문자 + 하이픈)
[guid]::NewGuid().ToString()
# → 3f2504e0-4f89-11d3-9a0c-0305e82c3301
# 하이픈 제거 (포맷 코드 'N')
[guid]::NewGuid().ToString('N')
# → 3f2504e04f8911d39a0c0305e82c3301
어디서나 — Python 한 줄
Python이 설치된 환경이라면 OS에 상관없이 동일하게 쓸 수 있습니다.
# 소문자 + 하이픈
python3 -c "import uuid; print(uuid.uuid4())"
# 소문자 + 하이픈 제거 (.hex 속성)
python3 -c "import uuid; print(uuid.uuid4().hex)"
macOS uuidgen은 대문자가 기본값
macOS의 uuidgen은 다른 OS와 달리 대문자를 기본으로 출력합니다. 복사해서 바로 쓸 때 정합성 문제가 생길 수 있으니, 습관적으로 | tr '[:upper:]' '[:lower:]'를 붙여두는 게 안전합니다.
실무자를 위한 UUID 활용 가이드
프로젝트에 UUID를 도입한다면 이 세 가지만 기억하세요.
- 가독성이 중요하다면 (디버깅 로그 등): 표준 포맷 그대로(
-포함, 소문자) 사용하세요. - 성능과 저장 공간이 중요하다면 (DB PK 등): 하이픈을 제거한 32글자 소문자로 저장하거나, DB가 지원하는
UUID전용 타입(내부적으로 16바이트 이진수 저장)을 적극 활용하세요. - 인덱스 성능이 걱정된다면: 무작위 UUID v4는 생성될 때마다 값이 튀어 DB 인덱스(B-Tree) 파편화를 유발합니다.
UUID v4의 대안 — UUID v7 & ULID
순서 보장이 필요하다면 UUID v7 또는 ULID를 검토하세요. 두 방식 모두 타임스탬프 기반으로 단조 증가(monotonically increasing)하여 B-Tree 인덱스 효율이 훨씬 좋습니다. 특히 UUID v7은 RFC 9562로 공식 표준화되어 주요 언어·DB 라이브러리의 지원이 빠르게 확산되고 있습니다.
한 줄 요약
UUID 충돌 걱정은 우주선에 치일 걱정만큼 쓸데없으니, 소문자 + 하이픈 제거 습관으로 성능을 아끼자. 인덱스까지 챙기고 싶다면 UUID v7이나 ULID도 한번 들여다보시길.
지금 하시는 프로젝트에서는 UUID를 어떤 방식으로 관리하고 계신가요? 댓글로 의견을 공유해 주세요!