logo hsb.horse
← 블로그 목록으로 돌아가기

블로그

UUID v7 구현 가이드 : JavaScript, Go, Shell로 만들기

UUID v7의 비트 구조, version/variant의 비트 설정, JavaScript·Go·Shell에서의 최소 구현, 구현시의 체크 항목, RFC의 참조처를 1페이지로 정리. 시계열 정렬하기 쉬운 ID를 안전하게 도입하고 싶은 개발자용으로 생성 로직의 확인 포인트를 정리한 실천 가이드. 구현 전의 확인에 사용할 수 있다.

게시일: 수정일:

UUID v7 을 구현하고 싶을 때에 필요한 요점만을 정리한다.
이 기사에서는, UUID v7 의 비트 구성, version/variant 의 세우는 방법, JavaScript·Go·Shell에서의 최소 구현을 차례로 확인할 수 있도록 했다.

UUID v7의 요점

UUID v7 는 128 비트로, 선두에 밀리 세컨드 타임 스탬프를 갖는다.

필드비트 수역할
timestamp48유닉스 타임(ms)
ver40111(v7)
rand_a12랜덤 값
var210(variant)
rand_b62랜덤 값

이 구성에 의해, 생성 시각순으로 대략 정렬하기 쉬운 ID를 만들 수 있다.

구현시 공통 절차

  1. 먼저 16바이트를 난수로 채우기
  2. 처음 6바이트를 현재 시간(밀리초)으로 덮어쓰기
  3. value[6]의 상위 4비트에 0x7(version)을 설정한다
  4. value[8]의 상위 2비트에 10(variant)를 설정한다

JavaScript / TypeScript 구현 예

function uuidV7Bytes(): Uint8Array {
const value = new Uint8Array(16);
crypto.getRandomValues(value);
const timestamp = BigInt(Date.now());
value[0] = Number((timestamp >> 40n) & 0xffn);
value[1] = Number((timestamp >> 32n) & 0xffn);
value[2] = Number((timestamp >> 24n) & 0xffn);
value[3] = Number((timestamp >> 16n) & 0xffn);
value[4] = Number((timestamp >> 8n) & 0xffn);
value[5] = Number(timestamp & 0xffn);
value[6] = (value[6] & 0x0f) | 0x70;
value[8] = (value[8] & 0x3f) | 0x80;
return value;
}

Go 구현 예

package main
import (
"crypto/rand"
"fmt"
"time"
)
func uuidV7() ([16]byte, error) {
var value [16]byte
if _, err := rand.Read(value[:]); err != nil {
return value, err
}
timestamp := uint64(time.Now().UnixMilli())
value[0] = byte(timestamp >> 40)
value[1] = byte(timestamp >> 32)
value[2] = byte(timestamp >> 24)
value[3] = byte(timestamp >> 16)
value[4] = byte(timestamp >> 8)
value[5] = byte(timestamp)
value[6] = (value[6] & 0x0F) | 0x70
value[8] = (value[8] & 0x3F) | 0x80
return value, nil
}
func main() {
id, _ := uuidV7()
fmt.Printf("%x\n", id)
}

Shell Script 구현 예

#!/bin/sh
uuid_v7() {
timestamp=$(date +%s)000
timestamp_hi=$((timestamp >> 16))
timestamp_lo=$((timestamp & 0xFFFF))
rand_a=0x$(LC_ALL=C tr -dc '0-9a-f' < /dev/urandom | head -c4)
ver_rand_a=$((0x7000 | (0x0FFF & rand_a)))
rand_b_hi=0x$(LC_ALL=C tr -dc '0-9a-f' < /dev/urandom | head -c4)
var_rand_hi=$((0x8000 | (0x3FFF & rand_b_hi)))
rand_b_lo=$(LC_ALL=C tr -dc '0-9a-f' < /dev/urandom | head -c12)
printf "%08x-%04x-%04x-%04x-%s\n" \
"$timestamp_hi" "$timestamp_lo" "$ver_rand_a" "$var_rand_hi" "$rand_b_lo"
}
uuid_v7

POSIX date 에서는 밀리 세컨드를 직접 취급할 수 없기 때문에, 구현 환경에 따라서 취득 방법의 조정이 필요하게 된다.

구현 체크 항목

  • 동일 밀리초 내에서 대량 생성하는 경우의 충돌 리스크를 평가한다
  • 포맷(하이픈 첨부 문자열 or 16바이트)을 이용측에서 통일한다
  • DB의 인덱스 전략(시계열 정렬 전제)를 먼저 결정한다

관련 링크(사이트 내)

참고 자료(외부)

-RFC 9562: UUIDs -MDN: Crypto.getRandomValues() -Go標準ライブラリ: crypto/rand

요약

UUID v7의 구현은, 어느 언어에서도 「타임스탬프 임베디드 + version/variant 의 비트 조작」이 중심이 된다.
요구 사항에 따라 기존 라이브러리를 사용할지 최소 구현을 내제할지를 선택하면 된다.