UUID는 중앙 관리자 없이도 겹치지 않는 식별자를 만들기 위한 128비트 값입니다. 서로 다른 서버가 각자 생성해도 충돌하지 않기 때문에, 데이터베이스가 채번해 주기를 기다릴 필요가 없고 오프라인 상태에서 만든 레코드도 나중에 그대로 동기화할 수 있습니다.
이 도구는 완전 난수 기반의 v4와 시간 순으로 정렬되는 v7을 만듭니다. v7은 2024년 RFC 9562로 표준이 된 비교적 새로운 버전으로, 앞 48비트에 생성 시각을 담아 문자열 정렬이 곧 생성 순서가 되도록 설계됐습니다. 데이터베이스 기본키로 쓸 때 v4보다 훨씬 유리한 이유가 여기에 있습니다.
사용 방법
- 버전 고르기 — 일반적인 용도라면 v4, 데이터베이스 기본키로 쓸 계획이라면 v7을 고르세요.
nil은 전부 0인 특수 값으로 '값 없음'을 명시할 때,max는 전부 f인 값으로 정렬 시 맨 끝을 표현할 때 씁니다. - 개수와 형식 정하기 — 최대 1000개까지 한 번에 만들 수 있습니다. 표준 형태(하이픈 포함), 하이픈 없는 32자,
urn:uuid:접두사, 중괄호로 감싼 형태 중에서 고르고 대소문자도 지정할 수 있습니다. 하이픈 없는 형태는 URL 경로에 넣을 때, 중괄호 형태는 마이크로소프트 계열 시스템에서 씁니다. - 복사하거나 파일로 받기 — 전체를 한 번에 복사하거나 줄 하나씩 개별 복사할 수 있고,
.txt파일로 내려받을 수도 있습니다. 테스트 데이터를 만들거나 시드 스크립트에 넣을 값이 필요할 때 대량 생성해 두면 편합니다. - 기존 UUID 해석하기 — 아래 UUID 해석에 값을 붙여넣으면 버전과 변형을 읽어 줍니다. v1·v6·v7이라면 안에 담긴 생성 시각까지 꺼내 보여 주므로, 로그에 남은 ID만으로 그 레코드가 언제 만들어졌는지 알 수 있습니다.
자주 묻는 질문
UUID가 정말 겹치지 않나요?
v4는 128비트 중 122비트가 난수입니다. 초당 10억 개씩 100년간 만들어도 충돌이 한 번 생길 확률이 50%에 못 미칩니다. 실무에서는 겹치지 않는다고 봐도 됩니다.
단, 이 계산은 난수원이 제대로 동작한다는 전제 위에 있습니다. 이 도구는 브라우저의 crypto.getRandomValues(암호학적 난수)를 쓰므로 문제가 없지만, 일부 언어의 기본 난수 생성기(Math.random, C의 rand)는 예측 가능해서 UUID 생성에 쓰면 안 됩니다. 라이브러리를 고를 때 암호학적 난수를 쓰는지 확인하세요.
v4와 v7 중 무엇을 써야 하나요?
데이터베이스 기본키라면 v7이 낫습니다. 대부분의 DB는 기본키로 B-트리 인덱스를 만드는데, v4는 값이 무작위라 새 레코드가 인덱스 곳곳에 흩어져 삽입됩니다. 그때마다 페이지가 쪼개지고 캐시가 무효화되면서 쓰기 성능이 떨어지고 인덱스 크기가 불필요하게 커집니다.
v7은 시간 순으로 증가하므로 항상 인덱스의 끝에 추가됩니다. 자동 증가 정수와 비슷한 삽입 패턴을 가지면서 분산 생성이 가능하다는 UUID의 장점은 그대로 유지합니다. 반대로 URL에 노출되는 공개 식별자라면 생성 시각이 드러나지 않는 v4가 더 적합할 수 있습니다.
v7에 시각이 들어 있으면 정보가 새는 것 아닌가요?
맞습니다. v7 UUID를 받은 사람은 그 레코드가 언제 만들어졌는지 밀리초 단위로 알 수 있습니다.
대부분은 문제가 되지 않습니다. 가입일이나 주문 시각은 어차피 화면에 표시되는 정보인 경우가 많습니다. 하지만 생성 시각 자체가 민감한 경우 — 예를 들어 비공개 문서의 작성 시점이나 경쟁사가 알면 안 되는 거래 타이밍 — 라면 외부에 노출되는 식별자로는 v4를 쓰고, v7은 내부 기본키로만 두는 방식을 권합니다.
같은 밀리초에 여러 개를 만들면 순서가 보장되나요?
이 도구에서는 보장됩니다. v7의 타임스탬프는 밀리초 단위라 같은 밀리초 안에서 여러 개를 만들면 앞 48비트가 동일해지고, 그 뒤가 난수라면 순서가 뒤섞입니다.
RFC 9562는 이 문제의 해법을 함께 정의해 두었고, 이 도구는 그중 '방법 1'을 씁니다. 타임스탬프 바로 뒤 12비트를 카운터로 써서 같은 밀리초 안에서는 1씩 증가시키는 방식입니다. 덕분에 1000개를 한 번에 만들어도 생성 순서대로 정렬됩니다.
알아두면 좋은 개념
128비트가 어떻게 나뉘는가
UUID의 36자 표기 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx 에서 M 자리는 버전을, N 자리의 상위 비트는 변형(variant)을 나타냅니다. 나머지가 버전별로 다르게 채워지는 실제 데이터입니다.
변형은 이 UUID가 어떤 표준을 따르는지 알려 줍니다. 요즘 만들어지는 것은 대부분 RFC 9562 변형(N이 8, 9, a, b 중 하나)입니다. 이 도구의 해석 기능은 이 두 자리를 읽어 버전과 변형을 판별하므로, 출처를 모르는 UUID가 어디서 온 것인지 짐작하는 데 쓸 수 있습니다.
저장할 때는 16바이트로
UUID를 VARCHAR(36) 으로 저장하는 것이 가장 흔한 실수입니다. 실제 데이터는 16바이트인데 문자열로는 36바이트를 쓰므로 두 배 이상 낭비되고, 인덱스 크기도 그만큼 커져 캐시 효율이 떨어집니다.
PostgreSQL에는 UUID 전용 타입이 있고, MySQL에서는 BINARY(16) 을 쓰면서 UUID_TO_BIN() / BIN_TO_UUID() 로 변환하면 됩니다. 특히 MySQL 8.0의 UUID_TO_BIN(uuid, 1) 은 v1의 시간 필드를 재배열해 정렬 가능하게 만들어 주는데, v7을 쓴다면 이미 정렬되므로 두 번째 인자 없이 그대로 저장하면 됩니다.
짧은 식별자가 필요할 때
UUID는 URL에 넣기에 깁니다. 36자는 주소를 지저분하게 만들고 사용자가 입력하기도 어렵습니다.
대안으로 같은 128비트를 Base64나 Base32로 인코딩해 22자 안팎으로 줄이거나, 처음부터 짧게 설계된 NanoID나 ULID를 쓰는 방법이 있습니다. ULID는 v7과 비슷하게 시간 순 정렬이 가능하면서 26자 Base32 표기를 씁니다. 다만 UUID는 거의 모든 언어와 데이터베이스가 기본 지원한다는 점이 큰 장점이므로, 짧아야 할 이유가 분명하지 않다면 UUID를 그대로 쓰는 편이 무난합니다.