컴퓨터들이 네트워크로 막 연결되기 시작했을 때, 진짜 단순하지만 엄청 중요한 문제가 생겼어: 서로 다른 지역에 있는 두 대의 기기가 똑같은 아이디를 만들지 않게 하려면 어떻게 해야 할까?
상상해봐: 한 서버는 도쿄에 있고, 다른 하나는 베를린에 있는데, 둘 다 각자 오브젝트용 아이디를 만들어. 만약 우연히 같은 ID를 만들면 — 데이터가 섞이거나 덮어써지고, 시스템이 망가질 수도 있어.
그래서 1990년대, 분산 시스템이랑 네트워크 프로토콜이 엄청 발전하던 시절, Microsoft랑 Open Software Foundation(OSF)에서 GUID라는 아이디어가 나왔어 — Globally Unique Identifier, 즉 글로벌 유니크 아이디지.
GUID(ISO 표준에서는 UUID, Universally Unique Identifier)는 128비트 숫자야. 예를 들면 이런 식으로 보여:
550e8400-e29b-41d4-a716-446655440000
이건 디지털 지문 같은 거야: 너무 길고 랜덤이라서, 두 개가 겹칠 확률이 거의 0에 가까워.
이렇게 만들어져:
- 시간,
- 랜덤 숫자,
- 기기의 MAC 주소(옛날 버전에서),
- 그리고 심지어 crypto hash 함수까지.
이게 왜 중요했냐면, GUID 덕분에 중앙 서버나 조율 없이, 락이나 지연 없이 유니크 아이디를 만들 수 있었거든. 이건 진짜 구세주였지, 예를 들면:
- 분산 데이터베이스,
- 네트워크 프로토콜,
- 문서 관리 시스템,
- 그리고 당연히 요즘 API들.
PostgreSQL에서 UUID
GUID/UUID는 어디서나 써 — PostgreSQL부터 클라우드 서비스까지. 얘네는 현대 인터넷의 숨은 건축가야: 없었으면 이렇게 쉽게 세상을 연결 못 했을걸.
사실 이건 그냥 엄청 긴 랜덤 숫자 16바이트짜리인데, 16진수로 적혀 있어. 그냥 진짜 긴 정수라고 보면 돼.
이 데이터 타입은 시스템 전체에서 값의 유니크함을 보장해야 할 때 딱이야. 중앙에서 아이디를 만들어주는 서버에 의존할 필요도 없고. 분산 시스템이나, 여러 서버에서 데이터가 만들어질 때 특히 유용하지.
그냥 INTEGER 쓰면 안 돼?
생각할 수 있지, UUID 대신 그냥 자동 증가하는 숫자 쓰면 되지 않나? 한번 파보자:
글로벌 유니크함: 만약 DB가 여러 서버에서 돌아가면, INTEGER로 유니크 보장하기 힘들어. UUID면 이 문제 끝.
보안 & 데이터 숨기기: UUID는 예측하기가 훨씬 어려워. 순차 번호보다 정보 유출 위험이 적지(예: /users/1, /users/2 이런 거).
분산 시스템: 시스템 여러 군데서 데이터가 만들어지면, 자동 증가 INTEGER는 복잡한 동기화 없인 못 써.
또 UUID의 장점은, 여러 프로그래밍 언어나 데이터 저장 시스템에서 표준으로 지원된다는 거야.
UUID의 장점
주요 장점들:
- 유니크함: 데이터가 여러 서버나 시스템에서 만들어져도 아이디가 겹치지 않아.
- 유연함: 프라이머리 키, 포린 키, 기타 등등 다 쓸 수 있어.
- 확장성: 분산 DB 작업할 때 편해.
근데 UUID도 단점이 있어:
- 사이즈:
UUID는 메모리에서 16바이트 먹고,INTEGER는 4바이트밖에 안 써. - 읽기 어려움: 숫자보다 읽고 외우기 힘들지.
PostgreSQL에서 UUID 생성하기
내장 함수 gen_random_uuid()
PostgreSQL 13버전부터는 gen_random_uuid()라는 내장 함수가 있어서 랜덤 UUID를 바로 만들 수 있어. 이 함수는 유니크 아이디를 리턴하고, UUID 타입 컬럼에 값으로 쓸 수 있지.
예시:
SELECT gen_random_uuid();
결과:
d17fc23b-22e5-4fcb-bf86-1b4c766d77b7
pgcrypto 익스텐션 설치 확인하기 (PSQL 1-12용)
PostgreSQL 13 이전 버전에서는 gen_random_uuid()를 쓰려면 pgcrypto 익스텐션을 설치해야 해. 회사에서 이거 쓰라고 하면, 아래 명령어로 해결할 수 있어:
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
이렇게 하면 랜덤 UUID 생성 가능!
UUID를 포린 키로 쓰기
UUID는 테이블끼리 관계 만들 때도 편하게 쓸 수 있어. 예를 들어, 유저 테이블이 있다고 해보자:
| id | name | |
|---|---|---|
| d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | Alice | alice@example.com |
| a1d3e15a-abc1-4b51-a320-2d4c859f7467 | Bob | bob@example.com |
| 3c524998-5c24-4e73-836d-a4c6bb3cafcd | Charlie | charlie@example.com |
orders 테이블 만들기
이제 orders 테이블을 만들어보자. 여기서 user_id는 users 테이블의 id를 참조하는 포린 키야.
| order_id | user_id | order_date |
|---|---|---|
| 1a5b7d9c-b1a2-4f8e-9e7a-0a1111111111 | d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | 2024-10-15 10:00:00 |
| 2b6c8e0d-c2b3-5a9f-af8b-1b2222222222 | a1d3e15a-abc1-4b51-a320-2d4c859f7467 | 2024-10-15 10:05:00 |
| 3c7d9f1e-d3c4-6baf-bc9c-2c3333333333 | 3c524998-5c24-4e73-836d-a4c6bb3cafcd | 2024-10-15 10:10:00 |
| 4d8eaf2f-e4d5-7cb0-cdab-3d4444444444 | d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | 2024-10-15 10:15:00 |
| 5e9fb030-f5e6-8dc1-debc-4e5555555555 | a1d3e15a-abc1-4b51-a320-2d4c859f7467 | 2024-10-15 10:20:00 |
user_id 필드는 users 테이블의 id랑 연결돼 있어서, 유저와 주문을 쉽게 연결할 수 있어.
JOIN으로 데이터 뽑기
users랑 orders 테이블이 어떻게 연결되는지 보자:
SELECT
u.id AS user_id,
u.name,
o.order_id,
o.order_date
FROM users u
JOIN orders o ON u.id = o.user_id;
결과:
| user_id | name | order_id | order_date |
|---|---|---|---|
| d17fc23b-22e5-4fcb-bf86-1b4c766d77b7 | Alice | a1d3e15a-abc1-4b51-a320-2d4c859f7467 | 2024-10-20 12:34:56 |
UUID 주요 사용 시나리오
유저랑 주문 아이디: 분산 시스템에서, 데이터가 여러 소스에서 들어올 때.
API용 마크: UUID는 REST API에서 엔티티 식별자로 자주 써.
글로벌 데이터 동기화: 예를 들어, 여러 서버에서 데이터가 모일 때.
자주 하는 실수 & 주의점
UUID를 직접 만들려고 하기: 실수 피하려면 gen_random_uuid() 같은 내장 함수 쓰는 게 좋아.
과도한 사용: 확장 안 할 로컬 테이블엔 그냥 자동 증가 INTEGER 쓰는 게 나아.
사이즈: UUID는 공간을 더 차지해서, 특히 인덱스 만들 때 쿼리 성능에 영향 줄 수 있어.
GO TO FULL VERSION