Erdmates Logo
Erdmates
블로그 목록으로
UUIDPostgreSQL기본 키인덱스

bigint, UUID v4, UUID v7: 기본 키 고르는 법 (PostgreSQL 18 uuidv7() 포함)

bigint·UUID v4·UUID v7 기본 키의 장단점을 비교하고, PostgreSQL 18에 들어온 uuidv7()로 달라진 점과 상황별 선택 기준을 정리합니다.

2026년 10월 12일7 분 읽기

기본 키 하나가 스키마 전체에 퍼진다

기본 키(Primary Key)는 테이블 하나만의 문제가 아닙니다. 그 키를 참조하는 모든 외래 키 컬럼이 같은 타입을 갖게 되고, 인덱스·조인·API 주소·로그까지 그 값을 들고 다닙니다. 그래서 기본 키 타입은 나중에 바꾸기 가장 어려운 설계 결정 중 하나입니다.

실무에서 가장 많이 고르는 후보는 세 가지입니다.

  • bigint — 데이터베이스가 1, 2, 3… 순서대로 매기는 정수
  • UUID v4 — 128비트 중 122비트가 무작위인 식별자
  • UUID v7 — 앞부분에 생성 시각(밀리초)을 담아 시간순으로 정렬되는 UUID

2025년 9월 25일 정식 출시된 PostgreSQL 18에 uuidv7() 함수가 들어오면서, 세 번째 선택지를 확장 모듈 없이 쓸 수 있게 됐습니다. 각각의 장단점과 고르는 기준을 정리합니다.

bigint: 작고 빠르지만 번호는 데이터베이스가 매긴다

PostgreSQL에서는 GENERATED ALWAYS AS IDENTITY로 선언합니다. 예전의 serial보다 SQL 표준에 맞는 방식입니다.

CREATE TABLE customers (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  email text NOT NULL UNIQUE
);

장점

  • 8바이트로 UUID(16바이트)의 절반입니다. 기본 키 인덱스와 이를 참조하는 외래 키 인덱스가 모두 작아집니다.
  • 새 값이 항상 인덱스의 오른쪽 끝에 붙어서 쓰기가 효율적입니다.
  • 사람이 읽고 말하기 쉽습니다("주문 1042번").

단점

  • 번호를 데이터베이스가 매기므로 저장하기 전에는 ID를 알 수 없습니다. 오프라인 앱이나 여러 서비스가 각자 레코드를 만들어 나중에 합치는 구조에서는 불편합니다.
  • 주소에 그대로 노출하면 다음 번호를 추측하기 쉽고, 전체 개수나 증가 속도가 드러납니다.
  • 여러 데이터베이스의 데이터를 합치면 번호가 충돌합니다.

UUID v4: 어디서든 만들 수 있지만 인덱스에 흩어진다

CREATE TABLE customers (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),  -- PostgreSQL 18의 uuidv4()와 같은 함수
  email text NOT NULL UNIQUE
);

장점

  • 애플리케이션·클라이언트·다른 서비스 어디서든 미리 만들 수 있고, 사실상 충돌하지 않습니다.
  • 추측할 수 없어서 주소에 노출해도 다른 레코드를 짐작하기 어렵습니다.

단점

  • 값이 무작위라서 새 행이 B-tree 인덱스의 아무 위치에나 들어갑니다. 쓰기가 많은 테이블에서는 인덱스 페이지가 자주 쪼개지고, 최근에 쓴 데이터가 메모리 캐시에 모이지 않아 디스크 I/O가 늘어납니다.
  • 16바이트라 bigint보다 인덱스가 큽니다. 외래 키가 많을수록 차이가 커집니다.

MySQL(InnoDB)처럼 테이블 자체를 기본 키 순서로 저장하는(클러스터드 인덱스) 엔진에서는 이 무작위 삽입의 영향이 테이블 전체에 미칩니다.

UUID v7: 시간순으로 정렬되는 UUID

UUID v7은 2024년 5월 발표된 RFC 9562에 정의된 형식입니다. 128비트 중 앞의 48비트에 Unix 시각(밀리초)을 넣고, 버전·변형 비트를 뺀 나머지를 무작위 값으로 채웁니다. RFC 9562는 가능하면 UUIDv1·v6 대신 UUIDv7을 쓰라고(SHOULD) 권합니다.

예를 들어 019535d9-3df7-79fb-b466-fa907fa17f9e에서 앞의 16진수 12자리(019535d9-3df7)가 생성 시각이고, 그다음 7이 버전입니다.

앞부분이 시각이므로 나중에 만든 값이 대체로 더 큽니다. 그래서 새 행이 bigint처럼 인덱스의 오른쪽 끝에 모이고, UUID v4의 무작위 삽입 문제가 크게 줄어듭니다. 동시에 어디서든 만들 수 있고 무작위 부분 때문에 추측하기 어렵다는 UUID의 장점은 그대로입니다.

주의할 점

  • ID만 보면 생성 시각이 드러납니다. RFC 9562도 보안 고려 사항에서 타임스탬프가 아주 작은 공격 표면이 된다고 적고 있습니다. 가입 시각처럼 드러나면 곤란한 정보라면 외부에 보여 주는 ID를 따로 두세요.
  • 크기는 v4와 같은 16바이트입니다.

PostgreSQL 18에서 쓰는 법

PostgreSQL 18부터는 확장 없이 바로 쓸 수 있습니다.

CREATE TABLE orders (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  customer_id uuid NOT NULL REFERENCES customers(id),
  total_amount numeric(12, 2) NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now()
);

-- ID에 담긴 생성 시각과 버전 확인
SELECT id,
       uuid_extract_timestamp(id) AS id_created_at,
       uuid_extract_version(id)   AS version
FROM orders
ORDER BY id DESC
LIMIT 10;

PostgreSQL 18의 UUID 관련 함수는 다음과 같습니다.

함수설명
uuidv7()시간순 UUID v7 생성. uuidv7(interval)로 기록할 시각을 그만큼 옮길 수 있음
uuidv4()gen_random_uuid()와 같은 무작위 v4 생성(버전을 드러낸 별칭)
uuid_extract_timestamp(uuid)v1·v7에서 생성 시각 추출(다른 버전은 NULL)
uuid_extract_version(uuid)버전 번호 확인

uuid_extract_timestamp()가 있다고 created_at 컬럼을 없애지는 마세요. ID는 애플리케이션에서 미리 만들 수도 있고 시각을 옮겨 만들 수도 있어서, 업무에서 쓰는 생성 시각과 항상 같지는 않습니다.

MySQL에서는

MySQL 8.4 문서 기준으로 UUID()는 버전 1 UUID를 만들고, UUID v7을 만드는 내장 함수는 없습니다(네이티브 지원 요청이 MySQL 버그 트래커에 등록돼 있습니다). 현실적인 방법은 두 가지입니다.

  • 애플리케이션에서 UUID v7을 만들어 BINARY(16) 컬럼에 저장
  • UUID()를 쓴다면 UUID_TO_BIN(UUID(), 1)로 저장 — 두 번째 인자 1은 시간 부분의 순서를 바꿔 값이 순차에 가깝게 들어가도록 합니다

어느 쪽이든 문자열(CHAR(36))로 저장하면 두 배 이상 커지므로 바이너리로 저장하는 편이 좋습니다.

상황별로 고르기

상황추천
데이터베이스 하나, ID를 외부에 보여 주지 않음bigint identity
클라이언트·여러 서비스가 ID를 미리 만들어야 함, 데이터 병합UUID v7
주소·API에 ID가 노출되지만 생성 시각은 드러나도 괜찮음UUID v7
생성 시각까지 숨겨야 하는 값(초대 링크, 비밀번호 재설정 토큰 등)UUID v4 또는 별도 무작위 토큰

이미 UUID v4로 운영 중이라면 새 테이블부터 v7을 쓰는 것으로 충분한 경우가 많습니다. 기존 키를 바꾸면 참조하는 모든 외래 키를 함께 바꿔야 하므로 효과와 비용을 먼저 따져 보세요.

ERD를 그릴 때 확인할 것

  • 외래 키 타입은 참조하는 기본 키와 같아야 합니다. uuid와 text, bigint와 integer가 섞이면 조인할 때마다 형 변환이 일어나거나 제약 조건을 만들 수 없습니다.
  • 기본 키 전략은 테이블마다 따로 정하기보다 스키마 전체에 한 가지 원칙을 두는 편이 실수를 줄입니다. 예외(외부 공개용 토큰 등)는 별도 컬럼으로 분리하세요.
  • 기본 키 인덱스 외에 자주 조회하는 외래 키에도 인덱스를 두세요. 키가 16바이트라면 인덱스 크기도 함께 고려해야 합니다.

키 타입을 먼저 정하고 관계를 그리면 외래 키 컬럼의 타입 불일치를 미리 발견할 수 있습니다. 관련 내용은 외래키와 테이블 관계 완전 정복과 데이터베이스 인덱스 완전 가이드에서 더 자세히 다룹니다.

Erdmates로 바로 시작하세요

AI 기반 ERD 에디터로 데이터베이스 설계를 시작하세요. 무료로 사용할 수 있습니다.

무료 데모 체험무료로 시작하기

관련 기사

AI 에이전트가 읽는 데이터베이스: MCP 시대의 스키마 설계 원칙

AI 에이전트가 MCP로 데이터베이스에 직접 접속하는 일이 늘고 있습니다. 에이전트가 스키마를 잘못 해석하지 않도록 이름·주석·제약 조건을 설계하고, 권한을 안전하게 나누는 방법을 정리합니다.

PostgreSQL 19 미리보기: 스키마 설계와 운영에 영향을 주는 변화 (베타 4 기준)

정식 출시를 앞둔 PostgreSQL 19의 변경 사항 가운데 테이블 설계·마이그레이션·운영에 직접 영향을 주는 것을 공식 릴리스 노트(베타 4) 기준으로 정리합니다. 베타에서 되돌려진 기능과 업그레이드 전 확인할 동작 변화도 함께 봅니다.

pgvector로 RAG용 벡터 테이블 설계하기: 차원, 인덱스, 필터링까지

PostgreSQL에서 pgvector로 문서 임베딩을 저장할 때의 테이블 구조, 벡터 타입별 차원 제한, HNSW 인덱스, 메타데이터 필터링 설계를 pgvector 0.8 기준으로 정리합니다.

Erdmates Logo
Erdmates

AI 기반 ERD 협업 에디터로 자연어로 데이터베이스를 설계하고 팀과 실시간으로 협업하세요.

서비스

  • 기능 소개
  • 요금제
  • 데모
  • SQL → ERD 변환

리소스

  • 블로그
  • 도움말
  • 문의하기

법적 고지

  • 이용약관
  • 개인정보처리방침
  • 제3자 제공
상호: Produx|대표: 최정회
주소: 강원특별자치도 원주시 솔우물2길 4-1 (무실동)
이메일: master@erdmates.com
개인정보관리책임자: 최정회|호스팅 서비스 제공자: Amazon Web Services

© 2026 Produx. All rights reserved.