본문으로 건너뛰기
ZEKILO Dev
생성

UUID v4와 v7, ULID 차이: DB 키는 무엇을 쓸까

2026.10.03 업데이트 · 5분 읽기

UUID v4는 전부 난수이고, UUID v7과 ULID는 앞부분이 시각이라 생성 순서대로 정렬됩니다. 구조와 DB 인덱스에 주는 영향, 생성 시각이 드러나는 문제를 비교해 어떤 ID를 쓸지 정리했습니다.

UUID·ULID 생성 바로 사용하기

새 테이블의 기본 키라면 UUID v7을 먼저 고려하세요. RFC 9562에 정의된 표준이고, 생성 시각 순서로 정렬되어 새 행이 인덱스의 한쪽에 모입니다. 만든 시각이 드러나면 안 되는 ID에는 UUID v4를, 같은 방식의 26자 짧은 문자열이 필요하면 ULID를 씁니다. 세 가지 모두 UUID·ULID 생성에서 만들고, 이미 있는 값도 분석할 수 있습니다.

한눈에 비교

항목 UUID v4 UUID v7 ULID
정의 RFC 9562 5.4절 RFC 9562 5.7절 ULID 명세
크기 128비트 128비트 128비트
구성 난수 122비트, 버전·변형 Unix 밀리초 48비트, 나머지 74비트, 버전·변형 Unix 밀리초 48비트, 난수 80비트
문자열 36자(16진수와 하이픈) 36자(16진수와 하이픈) 26자(Crockford Base32)
생성 순서 정렬 안 됨 됨 됨
생성 시각 노출 없음 밀리초까지 드러남 밀리초까지 드러남

v7의 74비트는 기본적으로 난수입니다. RFC 9562는 이 자리 일부를 카운터나 밀리초보다 작은 시간 값으로 써도 된다고 허용합니다.

시각이 들어가는 자리

UUID는 16진수 32자를 8-4-4-4-12로 나눠 적습니다. 셋째 묶음의 첫 글자가 버전이고, 넷째 묶음의 첫 글자에는 변형 비트가 들어가 RFC 9562의 UUID라면 8·9·a·b 중 하나입니다.

v7은 앞 16진수 12자리가 Unix 밀리초입니다. 2026-01-01T00:00:00.000Z(한국 시간 오전 9시), 즉 1767225600000 ms를 넣으면 다음과 같습니다.

Unix 시간(ms)   1767225600000  =  0x019b76daa800

UUID v7         019b76da-a800-7…          시각 다음에 버전 7
ULID            01KDVDNA00…               같은 시각을 Base32 10자로
UUID v4         5962b53c-5d43-4d66-944c-1a3d4c7b6e7c   전부 난수, 시각 없음

v4 줄은 crypto.randomUUID()로 만든 값 하나이고, 만들 때마다 달라집니다. 반대로 019b76da-a800-7000-8000-000000000000을 도구의 분석 탭에 붙여 넣으면 버전 7, 변형 RFC 9562, 시각 2026-01-01T00:00:00.000Z가 나옵니다.

ULID는 앞 10자가 시각 48비트, 뒤 16자가 난수 80비트입니다. 글자는 Crockford Base32(0123456789ABCDEFGHJKMNPQRSTVWXYZ)로, 헷갈리기 쉬운 I·L·O·U를 빼고 대소문자를 구분하지 않습니다. 가장 큰 자리가 앞에 오므로 문자열로 정렬하면 시간순이 됩니다.

DB 인덱스에서 차이가 나는 이유

RFC 9562는 시간순 버전을 추가한 이유로 인덱스 지역성을 듭니다. v4처럼 시간순이 아닌 UUID는 연달아 만든 값이 인덱스에서 서로 가깝지 않아 삽입이 무작위 위치에서 일어나고, B-트리 계열 구조의 성능에 큰 영향을 줄 수 있다는 설명입니다. v7이나 ULID는 새 키가 이전 키보다 거의 항상 크기 때문에 삽입이 인덱스의 한쪽 끝에 모입니다.

RFC 6.11절은 이 차이가 실제 환경에서 10배 이상일 수 있다고 적었습니다. 이 글에서 측정한 값이 아니라 RFC의 설명이며, 실제 효과는 테이블 크기와 저장 엔진, 인덱스가 메모리에 얼마나 올라가는지에 따라 달라집니다. 이미 운영 중인 테이블을 바꾸려면 먼저 측정해 보세요.

  • 문자열이 아니라 128비트 값으로 저장하세요. RFC 6.13절은 36자 문자열이 288비트를 차지하므로 가능하면 128비트 이진 값으로 저장하라고 권합니다. DB에 UUID 타입이 있으면 그것을 씁니다.
  • 가능하면 DB가 만들게 하세요. RFC는 DB 하나를 쓰는 애플리케이션이라면 DB에서 생성한 UUID가 순서를 지키기에 유리하다고 설명합니다. 2026년 10월 기준 PostgreSQL 18 문서에는 uuidv7() 함수가 uuidv4()와 함께 실려 있습니다.

같은 밀리초 안의 순서

시각은 밀리초 단위이므로 같은 밀리초에 만든 ID의 순서는 시각 뒤의 비트가 정합니다.

  • UUID v7: RFC 9562 6.2절은 순서를 지키는 방법으로 고정 길이 카운터, 단조 증가 난수, 더 정밀한 시계 값을 소개합니다. 어느 것을 쓸지, 쓰지 않을지는 생성기마다 다릅니다.
  • ULID: 명세는 같은 밀리초 안의 정렬 순서를 보장하지 않으며, 단조 생성기를 쓰면 난수 부분을 1씩 올려 순서를 지킨다고 설명합니다.

서로 다른 서버에서 만든 ID는 각 서버의 시계에도 좌우됩니다. 시간 기반 ID의 순서는 「대략 생성 순서」로만 보고, 정확한 순서가 필요하면 시퀀스나 시각 컬럼을 따로 두세요. RFC도 UUID를 가급적 해석하지 말고 불투명한 값으로 다루라고 권하므로, 생성 시각은 키에서 꺼내 쓰지 말고 created_at 같은 컬럼에 저장하는 편이 좋습니다.

v4를 계속 써야 할 때

v7과 ULID는 ID를 본 사람에게 만든 시각을 알려 줍니다. 공개 URL에 들어가는 ID라면 가입 시각이나 계정 사이의 선후가 드러날 수 있습니다. RFC 9562는 이를 아주 작은 공격 표면이라고 표현하면서, 보안과 관련된 용도에 UUID가 필요하면 v4를 쓰라고 권합니다.

다만 어떤 UUID도 비밀 값이 아닙니다. RFC는 UUID가 추측하기 어렵다고 가정하지 말고, 가지고 있기만 하면 접근이 허용되는 값으로 쓰지 말라고 명시합니다. 세션 토큰, 비밀번호 재설정 링크, API 키에는 전용 난수 토큰을 쓰고 서버에서 권한을 확인하세요.

상황 알맞은 선택
새 테이블의 기본 키, 이벤트·로그 ID UUID v7
공개되는 ID인데 생성 시각을 숨겨야 할 때 UUID v4
짧고 URL에 넣기 쉬운 시간순 문자열 ULID
문제없이 쓰고 있는 기존 v4 키 그대로 둠(둘 다 같은 UUID)

ZEKILO Dev로 만들기

  1. UUID·ULID 생성에서 UUID v4·UUID v7·ULID 중 하나를 고릅니다.
  2. 개수(1개부터 1,000개까지)와 대문자, 하이픈, 중괄호·따옴표 감싸기를 정합니다.
  3. 이미 있는 값은 분석 탭에 붙여 넣어 버전과 변형, 들어 있는 시각(v1·v6·v7·ULID)을 확인합니다.

값은 브라우저의 암호 API로 이 기기에서 만들며 어디로도 보내지 않습니다.

정리

  • UUID v4는 난수 122비트입니다. 순서도, 시각 정보도 없습니다.
  • UUID v7과 ULID는 앞 48비트가 밀리초 시각이라 생성 순서로 정렬되고 새 인덱스 항목이 한곳에 모입니다.
  • 시간순 ID는 생성 시각을 드러냅니다. 그것이 문제라면 v4를 쓰고, 어떤 UUID도 비밀 값으로 쓰지 마세요.
  • UUID는 128비트 값으로 저장하고, 정확한 순서가 필요하면 시각 컬럼을 따로 둡니다.

이 글과 관련된 도구

출처와 기준

다른 가이드