터미널을 열고 uuidgen을 다섯 번 치거나, 아무 사이트나 열어서 새로고침을 다섯 번
하거나. UUID가 필요한 순간은 대개 다른 일을 하던 중간이라 둘 다 거슬린다.
주소창 옆 아이콘 하나로 끝내려고 확장 프로그램을 만들었고, 만들면서 “그 중간"이
구체적으로 어떤 상황들이었는지 정리해봤다.
1. 시드 데이터에 한 번에 여러 개
마이그레이션 테스트용 fixture를 손으로 쓰는 중이라고 하자. 한 개씩 받아서 붙여넣는 게 제일 짜증나는 패턴이다. 개수를 지정해서 한 번에 받는다.
2dab98b3-4321-413b-886f-ed096d6ad0aa
233ab7b6-c6f9-4ec7-91de-47fa20c752eb
cbcdb763-fd23-4525-b658-1e65b95bdbf7
173c9bc9-fe1a-4ba5-b9be-a7552b413006
255bcfa2-ddfe-42a5-9b20-df99524963ed
2. 컬럼이 CHAR(32)라 하이픈이 들어가면 안 될 때
MySQL에서 UUID를 CHAR(36) 대신 CHAR(32)로 잡아둔 스키마를 꽤 본다.
받아온 값에서 하이픈을 지우는 건 1초짜리 일이지만, 하루에 다섯 번 하면 다섯 번
거슬린다. 체크박스로 처리했다.
그대로 ddafde73-9a65-4471-8c75-ba5c6576567d (36자)
하이픈 제거 ddafde739a6544718c75ba5c6576567d (32자)
대문자 DDAFDE73-9A65-4471-8C75-BA5C6576567D
대문자 옵션은 SQL Server의 UNIQUEIDENTIFIER나 .NET Guid.ToString() 출력과
눈으로 대조할 때 쓴다. 값이 같은데 대소문자가 달라서 “다른가?” 하고 한 번 멈추는
일이 없어진다.
3. 형식만 맞으면 되는 API 경로 파라미터
GET /api/users/{id} 를 404 응답 모양만 보려고 호출할 때, id는 유효한 v4 형식
이기만 하면 된다. 라우터의 UUID 밸리데이터를 통과해야 404가 나오지, 아무 문자열이나
넣으면 400이 나와서 보려던 걸 못 본다.
GET /api/users/2580af44-7ec3-46a1-972a-8a6de19c973f
이 값은 버전 니블(4)과 variant 비트(9)까지 규격대로 들어간다. 직접 확인했다 —
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/ 통과.
4. 삽입 순서대로 정렬돼야 하는 키라면 v7
여기가 v4/v7 토글이 있는 이유다. v4는 전부 난수라 정렬하면 삽입 순서와 무관하게 섞인다. v7은 앞 48비트가 밀리초 타임스탬프라 시간순으로 선다.
Node v23에서 각각 10,000개를 뽑아 인접쌍 역전 수를 셌다:
v4 26ms 동안 10,000개 역전쌍 5,007 / 9,999 (50.1%)
v7 23ms 동안 10,000개 역전쌍 0 / 9,999
v4의 50%는 운이 나빠서가 아니다. 난수 두 개를 비교하면 절반은 작게 나오는 게 당연하다. v7의 0은 그냥 타임스탬프 덕이 아니라 같은 밀리초 안에서도 순서를 유지하도록 rand_a 12비트에 카운터를 넣었기 때문이다. 카운터 없이 만들면 같은 밀리초에 몰린 값끼리는 다시 난수 비교가 되어 역전이 생긴다.
연속으로 뽑은 5개를 보면 앞 13자가 같은 걸 볼 수 있다. 그게 타임스탬프이고, 그 뒤가 카운터다.
01a12684-43b8-75cb-8769-bda79bd13033
01a12684-43b8-75cc-bd97-eaa2dbb8b77c
01a12684-43b8-75cd-a63e-e54b62e37394
01a12684-43b8-75ce-8971-3821124fbf5f
01a12684-43b8-75cf-99e6-776146029799
75cb → 75cc → 75cd → 75ce → 75cf. 정렬해도 순서가 그대로다.
v7 자체의 함정 — 같은 밀리초 정렬, 라이브러리마다 다른 구현 방식, PostgreSQL 18의 선택 — 은 분량이 꽤 돼서 따로 정리해뒀다: UUID v7 monotonicity .
권한은 0개다
manifest.json에 permissions 키 자체가 없다. 빈 배열이 아니라 키가 없다.
host_permissions도, content_scripts도, background도 없다.
1{
2 "manifest_version": 3,
3 "name": "UUID Generator — v4 & v7",
4 "action": { "default_popup": "popup.html" },
5 "homepage_url": "https://withuse.io/uuid"
6}
그래서 설치할 때 “이 사이트의 모든 데이터를 읽고 변경” 같은 경고가 뜨지 않는다.
난수는 crypto.getRandomValues로 로컬에서 만들고, 네트워크 호출은 소스 전체에
한 줄도 없다(fetch·XMLHttpRequest grep 0건). 복사는 MV3에서 사용자 제스처가
있으면 clipboardWrite 권한 없이 navigator.clipboard.writeText()로 된다 —
이건 만들면서 알게 된 것이고, 아직도 이 권한을 선언하는 코드가 많다.
npm에서 가져온 것도 없다. UUID 생성 로직은 직접 썼고, 그래서 심사자가 읽는 소스가 곧 배포되는 소스다. 대신 값을 치렀다 — v7 카운터를 처음에 틀리게 짜서 같은 밀리초 정렬이 깨져 있었고, 테스트를 돌려보고서야 찾았다. 성숙한 라이브러리를 썼으면 없었을 버그다. 몇백 줄짜리 순수 로직이라 그 거래를 받아들였다.
웹스토어: UUID Generator — v4 & v7 브라우저에서 바로 쓰려면: withuse.io/uuid
