UUID 생성기
UUID를 생성합니다. 랜덤한 v4와 시간순 v7을 하나씩 또는 한꺼번에.
122비트 랜덤. 언제 만들어졌는지 아무것도 드러내지 않습니다.
생성 중…
UUID란 무엇이고 왜 존재하는가
UUID — Universally Unique Identifier, Microsoft 문서에서는 GUID라고도 합니다 — 는 익숙한 8-4-4-4-12 구분으로 32자리 16진수로 쓰이는 128비트 값입니다. 그 온전한 목적은 별개의 시스템이 서로 조율하지 않고 독립적으로 식별자를 만들면서도 결과가 충돌하지 않으리라 확신할 수 있게 하는 것입니다. 데이터베이스 자동 증가 열과 다른 점이 그것입니다: 두 대의 서버, 비행기 안에서 오프라인인 두 대의 모바일 기기, 그리고 백그라운드 작업이 누구의 허락도 구하지 않고 같은 순간에 레코드를 만들 수 있습니다.
여기서 고유성은 보장이 아니라 확률적입니다. 버전 4는 122비트를 난수에 맡기는데, 이는 수십억 개의 값을 만들어도 반복 확률이 디스크가 조용히 데이터를 손상할 확률보다 훨씬 낮을 만큼 큰 공간입니다. 실무에서는 고유한 것으로 다뤄도 됩니다. 수학이 약한 고리가 되지는 않습니다.
버전 4와 버전 7 — 중요한 선택
버전 4는 끝에서 끝까지 랜덤입니다. 언제 만들어졌는지, 누가, 어떤 순서로 만들었는지 아무 정보도 나르지 않습니다. 그것이 최대 강점인지 핵심 결함인지는 어디에 두느냐에 달렸습니다.
버전 7은 2024년 RFC 9562에서 표준화되었고, 처음 48비트를 밀리초 Unix 타임스탬프로 바꾸고 나머지를 난수로 채웁니다. 시간이 앞에 오고 값이 왼쪽에서 오른쪽으로 읽히므로, v7 식별자를 맨 텍스트로 정렬하면 시간순으로도 정렬됩니다.
이것은 겉모습만의 차이가 아닙니다. 데이터베이스는 기본 키를 정렬된 B-트리 인덱스에 둡니다. v7 키를 삽입하면 새 행이 트리의 오른쪽 끝, 이전 행 옆에 놓입니다 — 쓰는 페이지가 메모리에 남고 인덱스가 깔끔하게 자랍니다. v4 키를 삽입하면 쓸 때마다 무작위 위치에 놓여 데이터베이스가 매번 다른 페이지를 건드리고, 캐시 적중률이 떨어지고, 인덱스가 조각납니다. 크고 바쁜 테이블에서는 삽입 처리량 차이가 크며, v7이 기본 키에 이토록 빨리 채택된 이유입니다.
- 데이터베이스 기본 키, 이벤트·로그 식별자, 생성 시각으로 정렬하거나 범위 조회하고 싶은 것에는 v7을 고르세요.
- 식별자가 신뢰할 수 없는 곳에 나타나 아무것도 — 한 레코드가 다른 것보다 조금 먼저 만들어졌다는 사실조차 — 새어서는 안 될 때는 v4를 고르세요.
- 상관 ID, 멱등 키, 파일 이름처럼 순서가 문제되지 않는 곳에는 어느 쪽이든 괜찮습니다.
대가는 바로 그 새어 나감입니다: v7 식별자는 그것을 보는 이에게 만들어진 시각을 밀리초 단위로 알려 줍니다. 행 ID에는 대개 무해하고 종종 유용합니다. 비밀번호 재설정 토큰이나 공개 공유 링크에는 공개할 뜻이 없던 정보입니다 — 그리고 그것들은 애초에 UUID가 아니라 전용 시크릿 생성기에서 나온 랜덤 토큰이어야 합니다.
같은 밀리초 안의 순서
많은 v7 구현을 잡는 미묘한 점: 현대 하드웨어는 1밀리초에 하나보다 훨씬 많은 식별자를 냅니다. 두 값이 타임스탬프를 공유하면 그 순서는 뒤따르는 난수 비트가 정합니다 — 즉 무작위로. 촘촘한 루프로 쉰 개를 내면 대충만 정렬된 쉰 개의 값이 나와, v7을 고른 바로 그 성질을 잃습니다.
RFC 9562는 이것을 단조 카운터로 다루고, 이 도구는 그것을 구현합니다: 타임스탬프 직후의 12비트가 밀리초 안에서 위로 세므로, 배치가 대충이 아니라 엄격히 증가합니다. 카운터가 차면 — 같은 밀리초에 4096개를 넘으면 — 생성기는 되감아 앞선 것보다 앞에 정렬되는 값을 내는 대신 다음 밀리초를 빌립니다. NTP 보정으로 일어나는, 시계가 뒤로 가는 경우도 같은 방식으로 다룹니다.
v7 값을 왼쪽에서 오른쪽으로 읽으면, 0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b을 예로:
- 0190a1b2-c3d4 — 48비트 밀리초 Unix 타임스탬프. 앞에 오므로 텍스트 순서가 시간 순서입니다.
- 7 — 버전 니블로, 이것을 v4가 아니라 v7로 만드는 것입니다.
- e5f — 12비트 단조 카운터로, 단일 밀리초 안에서 증가합니다.
- 8 — 변형 비트로, RFC 9562가 모든 현대 UUID에 대해 고정합니다(늘 8, 9, a, b 중 하나).
- a9b-0c1d2e3f4a5b — 나머지 62비트로, 순전히 랜덤입니다.
UUID 저장과 사용
정규 형태는 소문자에 하이픈이 붙은 것이고, RFC 9562는 생성기가 바로 그것을 내야 한다고 말합니다 — 그래서 이 도구는 서식 옵션을 제공하지 않습니다. 실제로는 다른 표기도 만나게 됩니다: Microsoft 도구에서는 대문자에 중괄호로 감싸이고, 짧은 열을 원한 누군가가 하이픈을 뗀 형태도 있습니다. 모두 같은 128비트이고, 비교는 대소문자를 무시해야 합니다.
중요한 것은 저장입니다. UUID는 16바이트지만 텍스트 형태는 36자입니다 — 그래서 문자열로 저장하면 행에서, 그리고 더 중요하게는 그것을 포함하는 모든 인덱스에서 공간이 두 배 넘게 듭니다. 네이티브 타입이 있다면 그것을 쓰세요:
uuid -- PostgreSQL: 네이티브 16바이트 타입 BINARY(16) -- MySQL: 간결. CHAR(36)은 행마다 20바이트 낭비 uniqueidentifier -- SQL Server crypto.randomUUID() // JavaScript: v4만, 보안 컨텍스트 필요 uuid.uuid4() / uuid7() # Python: 표준 라이브러리 v4. v7은 라이브러리로
마지막 주의: UUID는 식별하지 인가하지 않습니다. 추측할 수 없다는 이유로, 그것을 담은 미공개 URL을 비공개로 여기고 싶어집니다. 하지만 식별자는 새어 나갑니다 — 로그, 브라우저 기록, 리퍼러 헤더, 스크린샷을 통해 — 그러니 정말로 지켜야 할 것에는 그 뒤에 여전히 진짜 권한 확인이 필요합니다.
자주 묻는 질문
- v4와 v7 중 무엇을 써야 하나요?
- 데이터베이스 기본 키와 생성 시각으로 정렬하고 싶은 것에는 v7을 쓰세요: 앞의 타임스탬프가 삽입을 인덱스 전체로 흩지 않고 끝에 모아 둡니다. 식별자가 언제 만들어졌는지를 포함해 아무것도 드러내면 안 될 때는 v4를 쓰세요.
- 두 UUID가 같아지는 일이 있나요?
- 가능하지만 사라질 듯 드뭅니다. 버전 4는 122비트 난수를 지니므로, 수십억 개의 값을 만든 뒤에도 충돌 확률이 저장소가 조용히 그것들을 손상할 확률보다 훨씬 낮게 남습니다.
- 버전 1, 3, 5는 어떻게 됐나요?
- 버전 1은 타임스탬프와 머신의 MAC 주소를 부호화해 하드웨어 신원을 새게 하고 브라우저에서는 아예 만들 수 없습니다. 버전 3과 5는 네임스페이스와 이름에서 MD5나 SHA-1로 UUID를 결정론적으로 이끌어 냅니다 — 같은 입력이 늘 같은 식별자를 내야 할 때 유용하지만, 새로 생성하는 것과는 다른 일입니다.
- UUID가 시크릿 토큰으로 쓸 만큼 안전한가요?
- v4 UUID는 추측할 수 없지만 v7은 생성 시각을 공공연히 부호화하고, 어느 쪽도 자격 증명으로 의도되지 않았습니다. 비밀번호 재설정, 세션 토큰, 공유 링크에는 전용 랜덤 시크릿을 생성하고, 식별자가 추측하기 어렵다는 데 기대지 말고 서버에서 권한을 확인하세요.
- 제 v7 배치가 다른 도구에서 완벽히 정렬되지 않는 이유는 무엇인가요?
- 많은 구현이 단조 카운터를 빼기 때문입니다. 여러 값이 밀리초를 공유하면 그 순서가 뒤따르는 난수 비트에 맡겨집니다. 이 도구는 RFC 9562의 카운터를 구현하므로 여기서 생성한 배치는 엄격히 증가합니다.
- UUID를 데이터베이스에 어떻게 저장해야 하나요?
- 네이티브 16바이트 타입이 있다면 그것으로 — PostgreSQL의 uuid, SQL Server의 uniqueidentifier, MySQL의 BINARY(16)입니다. 대신 36자 텍스트 형태로 저장하면 행에서, 그리고 그 열을 포함하는 모든 인덱스에서 쓰는 공간이 두 배 넘게 듭니다.
- 이것들이 당신의 서버에서 생성되나요?
- 아니요. 플랫폼의 암호학적 난수원인 crypto.getRandomValues를 써서 브라우저 안에서 생성됩니다 — crypto.randomUUID가 쓰는 것과 같습니다. 어느 값도 어디로 보내지지 않고, 페이지를 새로 고치면 완전히 새로운 한 벌이 나옵니다.