설치·접속: PostgreSQL 설치와 접속
부제: 외부 시스템의 (namespace, 이름)을 UUID로 매핑하는데, 재실행해도 중복 없이 동일한 ID가 나와야 할 때
UUID 하면 보통 랜덤(v4)만 떠올린다. 코어의 gen_random_uuid()가 호출마다 다른 값을 준다.
SELECT gen_random_uuid() AS v4_a, gen_random_uuid() AS v4_b;
v4_a | v4_b
--------------------------------------+--------------------------------------
635ed2ec-6eb8-4b37-9b73-2371f7bfeede | 6d145333-8f26-4a7d-9b2c-fa7e5a731090
(1 row)새 행에 그냥 PK를 뿌릴 때는 이게 맞다. 문제는 "외부 주문 ext-42를 항상 같은 UUID로 부르고 싶다" 같은 결정론적 매핑이다. v4로는 매번 새 값이 나와 매핑 테이블 없이는 재현이 안 된다. v5는 namespace UUID와 이름 문자열을 SHA-1로 버무려 UUID를 만든다 — 같은 입력이면 언제나 같은 출력이다.
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
SELECT uuid_generate_v5(uuid_ns_url(), 'https://forcloud.tistory.com') AS call1;
SELECT uuid_generate_v5(uuid_ns_url(), 'https://forcloud.tistory.com') AS call2;
call1
--------------------------------------
4da6bac6-b141-53da-af4d-ce20f4ff3c60
(1 row)
call2
--------------------------------------
4da6bac6-b141-53da-af4d-ce20f4ff3c60
(1 row)
두 번 불러도 4da6bac6-...-c60으로 동일하다. (버전 니블이 5인 것도 보인다.) 이름이 하나만 달라져도 값이 완전히 갈린다.
SELECT uuid_generate_v5(uuid_ns_url(), 'https://example.com') AS other;
other
--------------------------------------
4fd35a71-71ef-5a55-a9d9-aa75c889a6d0
(1 row)이 결정성 덕분에 매핑 테이블 없이 외부 자연키를 UUID로 안정적으로 접는다. 같은 소스 이름으로 두 번 INSERT해도 PK가 같으니 ON CONFLICT DO NOTHING으로 멱등(idempotent)하게 만든다.
CREATE TABLE map (id uuid primary key, source_name text);
INSERT INTO map(id, source_name)
VALUES (uuid_generate_v5(uuid_ns_url(), 'order:ext-42'), 'order:ext-42')
ON CONFLICT (id) DO NOTHING;
-- 같은 매핑을 한 번 더
INSERT INTO map(id, source_name)
VALUES (uuid_generate_v5(uuid_ns_url(), 'order:ext-42'), 'order:ext-42')
ON CONFLICT (id) DO NOTHING;
SELECT count(*) AS rows_after_two_inserts FROM map;
rows_after_two_inserts
------------------------
1
(1 row)두 번 넣었지만 행은 하나다. 함정: v5는 SHA-1 기반이라 암호학적 비밀값 파생용이 아니다(그건 pgcrypto). 그리고 이름이 같으면 결과가 같으므로, 충돌을 원치 않는 순수 유니크 ID에는 오히려 v4를 써야 한다. 결정성은 "재현"이 목적일 때만 장점이다.
이렇게도 쓴다
well-known namespace 상수 — URL/DNS/OID/X500. 같은 이름이라도 namespace가 다르면 다른 UUID가 나온다.
SELECT uuid_ns_url() AS ns_url, uuid_ns_dns() AS ns_dns;
-- ns_url = 6ba7b811-9dad-11d1-80b4-00c04fd430c8
-- ns_dns = 6ba7b810-9dad-11d1-80b4-00c04fd430c8
v4만 필요하면 확장 없이 코어 함수를 쓴다 — PK DEFAULT로 지정. (조합: DEFAULT)
CREATE TABLE evt (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), body text);
내 도메인 namespace를 하나 정해두고 그 아래로 이름을 매핑한다 — 조직 전용 결정론적 ID 체계.
-- 도메인 root를 DNS namespace로 v5 생성해 그걸 하위 매핑의 namespace로 재사용
SELECT uuid_generate_v5(uuid_ns_dns(), 'forcloud.tistory.com') AS my_namespace;
v3(MD5 기반)도 같은 결정론적 특성 — 해시 알고리즘만 다르다. 신규 설계는 보통 v5(SHA-1) 권장.
SELECT uuid_generate_v3(uuid_ns_url(), 'order:ext-42') AS v3;
시간 순 정렬이나 시퀀셜 삽입 지역성이 중요하면 v4/v5의 랜덤성이 인덱스 단편화를 부른다. 그럴 땐 시간 정렬형 UUID(v7 계열) 쪽을 검토한다. (경계)
'명령어 > DB' 카테고리의 다른 글
| [PostgreSQL] WITH RECURSIVE 심화 — 조직도/그래프 순회와 CYCLE 절로 순환 탐지 (0) | 2026.07.22 |
|---|---|
| [PostgreSQL] tablefunc crosstab — (연,월,수량) 행을 연×월 피벗 리포트로 (0) | 2026.07.22 |
| [PostgreSQL] seg — 오차 범위째 저장하는 측정값 구간 타입 (0) | 2026.07.22 |
| [PostgreSQL] citext — 대소문자 무시 타입으로 이메일 UNIQUE를 건다 (0) | 2026.07.22 |
| [PostgreSQL] isn — 잘못된 ISBN을 DB단에서 원천 차단한다 (0) | 2026.07.22 |