UUID v4 vs v7: ID에 어떤 버전을 써야 하나

랜덤 v4 UUID와 시간순 정렬되는 v7 UUID는 서로 다른 문제를 해결합니다. 왜 v7이 대체로 더 나은 기본 키인지, v4가 여전히 적절한 경우는 언제인지, 버전 비트가 실제로 무엇을 뜻하는지 설명합니다.

새 행, 새 파일, 또는 큐에 들어갈 메시지에 식별자가 필요합니다. 가장 먼저 손이 가는 것은 UUID입니다. 128비트이고, 중앙 카운터 없이 어디서든 생성할 수 있으며, 거의 확실하게 고유합니다. 하지만 "UUID"가 하나의 대상을 가리키는 것은 아닙니다. 어떤 버전을 고르느냐에 따라 값이 정렬되는 방식, 데이터베이스 키로서의 동작, 그리고 무엇이 노출되는지가 달라집니다. 2026년에 중요한 두 가지는 v4(랜덤)와 v7(시간순)이며, 둘은 서로 바꿔 쓸 수 없습니다.

UUID가 실제로 무엇인가

UUID는 128비트이며, 보통 8-4-4-4-12 묶음의 32자리 16진수로 표기합니다: 0190b8e4-.... 이 중 6비트는 고정 오버헤드입니다. 4비트는 버전(어떤 생성 방식이 만들어 냈는지)을, 2비트는 변형(레이아웃 표준)을 나타냅니다. 나머지 122비트가 버전마다 달라지는 부분으로, 랜덤 노이즈이거나, 타임스탬프이거나, 둘의 혼합입니다. UUID를 읽을 때 버전 숫자는 13번째 16진수 문자입니다. 그 자리가 4이면 v4, 7이면 v7입니다.

0190b8e4-5e3a-7c2d-9f10-3a4b5c6d7e8f
                ^ version (7)

그 한 자리가 전부입니다. 아래 내용은 모두 나머지 비트를 무엇이 채우는지, 그리고 그것이 왜 중요한지에 관한 것입니다.

v4: 랜덤

v4 UUID는 버전과 변형 비트가 찍힌 122비트의 랜덤값입니다. 구조도, 타임스탬프도, 머신 식별자도 없습니다. 암호학적으로 안전한 소스에서 나온 엔트로피일 뿐입니다. 이 덕분에 두 가지 강력한 성질이 생깁니다. 어떤 조율도 없이 어디서든 하나를 생성할 수 있고, 직전 값으로부터 다음 값을 추측할 수 없습니다.

비용은 v4를 데이터베이스 기본 키로 쓸 때 드러납니다. 대부분의 데이터베이스는 기본 키를 B-tree에 저장하는데, 이는 행을 디스크상에서 키 순서로 유지합니다. 랜덤 키는 무작위 위치에 떨어지므로, 삽입이 인덱스 전체에 쓰기를 흩뜨리고, 페이지 분할을 일으키며, 버퍼 캐시를 마구 들쑤십니다. 바쁜 테이블에서는 처리량과 저장 공간에 측정 가능한 손해가 됩니다. 인덱스가 단편화되고 정렬된 인덱스보다 빠르게 커집니다. 수천 행 정도라면 결코 느끼지 못하겠지만, 수백만 건의 삽입이 되면 느끼게 됩니다.

그래서 v4는 추측 불가능성이 핵심일 때 옳은 선택입니다. URL에 들어가는 비밀번호 재설정 토큰, 열거되기를 원치 않는 공개 객체 ID, API 키가 그렇습니다. 쓰기가 많은 기본 키의 기본값으로는 잘못된 선택입니다.

v1과 시간을 거쳐 온 우회로

v7 이전에 시간 기반 선택지는 v1이었습니다. 60비트 타임스탬프에 머신의 MAC 주소를 더한 것입니다. 두 가지가 v1을 가라앉혔습니다. MAC 주소는 어떤 머신이 그 ID를 만들었는지 노출하고, 타임스탬프 바이트가 뒤섞인 순서로 저장되어 v1 값은 문자열이나 바이트로 시간순 정렬되지 않습니다. v1은 "시간을 인코딩한다"는 문제는 풀었지만 "정렬 가능하다"는 문제는 풀지 못했고, 사람들이 실제로 원했던 성질은 후자였습니다. v1은 레거시로 취급하면 됩니다.

v7: 시간순

v7 UUID는 48비트 Unix 타임스탬프를 밀리초 단위로 앞에 두고, 나머지를 랜덤값으로 채웁니다. 타임스탬프가 가장 상위 부분이고 빅엔디언으로 저장되기 때문에, v7 값은 원시 바이트 기준으로 시간순 정렬됩니다. 따라서 문자열 형태로도 시간순 정렬됩니다. 순서대로 생성된 두 ID는 그 순서대로 정렬됩니다.

이 한 가지 변화가 B-tree 문제를 해결합니다. 새 키가 이전 키보다 크므로, 삽입이 흩어지지 않고 인덱스의 오른쪽 가장자리 근처에 덧붙습니다. v4의 "어디서든 생성, 조율 불필요" 성질을 유지하면서도(뒤따르는 랜덤 비트가 같은 밀리초 안의 충돌 가능성을 여전히 사라질 만큼 낮게 만듭니다) 인덱스 지역성과 공짜 시간순 정렬을 얻습니다. 기본 키에서는 이 조합이 v7이 기본 권장안이 된 이유입니다.

v4 v7
내용 122 랜덤 비트 48비트 ms 타임스탬프 + 랜덤
시간순 정렬 가능 아니오 예 (바이트 및 문자열 순서)
PK로서 인덱스 친화성 아니오 (쓰기 흩뜨림) 예 (거의 순차적)
생성 시각 노출 아니오
적합한 용도 불투명/추측 불가 토큰 데이터베이스 기본 키

v7이 감수하는 트레이드오프

v7이 정렬 가능한 것은 바로 생성 시각을 담기 때문이고, 그것이 동시에 유일한 누출입니다. v7 UUID를 가진 누구든 그것이 언제 생성됐는지를 밀리초 단위까지 읽을 수 있습니다. 기본 키에서는 대체로 무해하고 종종 유용합니다. 타이밍을 노출하고 싶지 않은 공개 토큰에서는, 즉 쿠폰 코드, 초대 링크, 열거 가능에 가까운 무엇이든, 그 누출은 v4에 머무를 이유가 됩니다. 버전은 그 ID가 무엇에 노출되는지를 보고 고르는 것이지 습관으로 고르는 것이 아닙니다.

ULID를 본 적이 있다면, 그것은 v7과 같은 발상입니다. 밀리초 타임스탬프 접두사에 랜덤값을 더한 것이며, v7의 표준화보다 앞섰고, 다른 텍스트 인코딩(Crockford base32, 26자)을 씁니다. v7은 RFC가 인정한 형태이고 일반 UUID로 상호 운용되므로, 새 작업이라면 ULID를 그 위에 고를 이유는 거의 없습니다.

읽고 생성하기

UUID를 살펴볼 때는 버전 숫자를 먼저 보고, 그다음에 나머지가 무엇을 뜻하는지 판단합니다. v7의 앞쪽 바이트는 타임스탬프로 디코딩되지만, v4의 바이트는 무엇으로도 디코딩되지 않습니다. 생성 측면에서는 최신 런타임이 따라잡고 있습니다. 브라우저와 Node의 crypto.randomUUID()는 여전히 v4를 내놓고, v7은 라이브러리(npm의 uuid, 다른 생태계의 uuid6/uuid7)를 통해, 그리고 점점 데이터베이스에서 직접 제공됩니다. 저희 UUID 생성기는 두 버전을 모두 브라우저에서 만들어 주므로, 의존성을 끌어오지 않고도 한 묶음을 복사할 수 있습니다.

두 버전 모두 하지 못하는 한 가지는 무언가를 인증하는 것입니다. UUID는 식별자이지, 검증할 수 있는 비밀이 아닙니다. 당신이 발급했음을 증명하는 토큰이 필요하다면 그것은 서명이며, JWT 서명 알고리즘에서 다룹니다. 그리고 UUID는 해시가 아닙니다. 콘텐츠를 지문화하려고 UUID에 손이 간다면, 해싱, 암호화, 인코딩의 구분이 당신에게 필요한 것입니다.

요약하면, 기본 키와 대량으로 저장하는 무엇이든에는 v7에 손을 뻗고, ID가 공개되고 생성 시각이 누구의 관심사도 아니어야 할 때는 v4를 씁니다.