대부분의 프로젝트는 기본 키로 다음 중 하나를 사용한다.
- AUTO_INCREMENT (Long)
- UUID v4 (랜덤)
- Snowflake (시간 기반 64bit ID)
이번 프로젝트에서는 Postgres + Hibernate 환경에서 UUID v7을 선택했다.
왜 굳이 v7을 선택했는지, 그리고 Snowflake와는 무엇이 다른지 정리해본다.
기존 UUID v4의 문제점
UUID v4는 완전 랜덤이다.
550e8400-e29b-41d4-a716-446655440000
장점
- 충돌 확률 거의 없음
- 분산 환경에서 안전
- 중앙 서버 불필요
단점
- 완전 랜덤 → B-Tree 인덱스에 불리
- insert 시 page split 빈번
- 인덱스 단편화 발생
- 대량 트래픽 환경에서 성능 저하
Postgres나 MySql 기본 인덱스는 B-Tree라 순차 증가 값에 최적화되어 있다.
그러나 랜덤 값(v4)은 매번 트리 중간에 삽입되기 때문에 성능상 불리하다.
UUID v7란?
UUID v7은 2024년 RFC 9562에서 공식화된 최신 UUID 버전
- 앞부분 = 시간 기반
- 뒷부분 = 랜덤
# UUID V7 구조
48bit Unix epoch milliseconds + 랜덤 비트
- 시간 순 정렬 가능
- 인덱스 친화적
- 충돌 확률 매우 낮음
- Snowflake처럼 동작하지만 표준 UUID 형식 유지
Snowflake란
Snowflake ID는 Twitter에서 만든 분산 ID 생성 전략
# SnowFlake 구조
timestamp (41bit)
workerId (10bit)
sequence (12bit)
특징
- 시간 순 정렬 가능
- 분산 환경 지원
- 중앙 DB 없이 ID 생성
- Long 타입 (64bit)
장점
- 인덱스 친화적
- AUTO_INCREMENT처럼 정렬됨
- 높은 성능
단점
- 중앙 시계 의존 (Clock rollback 문제)
- worker id 관리 필요
- 구현 복잡도 존재
- DB 독립적이지만 시스템 설계 필요
왜 Snowflake 대신 UUID v7을 선택했는가?
1️⃣ 표준 기반
- UUID v7은 RFC 공식 표준이지만 Snowflake는 구현 전략일 뿐 표준이 아니다.
2️⃣ 운영 복잡도 감소
- Snowflake는 worker id 관리, 시계 동기화 문제, 인프라 고려 필요로 즉 ID 생성이 시스템 설계 일부가 된다.
- 그러나 UUID v7은 그냥 생성하면 끝
3️⃣ 만드려는 시스템이 Snowflake를 "관리할 가치가 있는가?"판단 필요
- Snowflake는 이런 경우에 빛난다
- 초고트래픽
- 수십~수백 노드
- ID를 long으로 유지해야 함
- Kafka key 등에서 8byte 이점 중요
- 글로벌 스케일
- 반면, 이런 경우라면?
- 모놀리식
- 혹은 소규모 MSA
- 운영 복잡도 최소화가 목표
4️⃣ Hibernate와 쉽게 통합 가능하여 구현의 간단함
- 의존성 주입
implementation("com.github.f4b6a3:uuid-creator:5.3.3")
- 엔티티에 사용 코드
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User extends BaseEntity {
@Id
@Column(nullable = false, updatable = false)
private UUID id;
@PrePersist
void init() {
if (id == null) {
id = UuidCreator.getTimeOrderedEpoch(); // = UUID v7
}
}
}
UUID v7의 단점은 없는가?
- 128bit라 Snowflake(64bit)보다 크다.
- 사람이 보기 어렵다.
- 로그 추적 시 가독성 낮음
| 항목 | Snowflake | UUID v7 |
| 타입 | bigint | uuid |
| 길이 | 64bit | 128bit |
| 시간 정렬 | O | O |
| 분산 지원 | O | O |
| worker 관리 | 필요 | 불필요 |
| 표준 | 비표준 | RFC 표준 |
| DB 독립성 | O | O |
| 인덱스 성능 | 좋음 | 좋음 |
크기 문제 해결 방법
- 128bit라는 크기를 해결하기 위해 MySQL에서는 UUID를 저장하기 위한 방식이 3가지 존재한다.
- MySQL에는 PostgreSQL처럼 전용 UUID 타입이 없기 때문
| 방식 | 타입 | 크기 | 특징 |
| 문자열 저장 | CHAR(36) | 36 bytes | 가독성 좋음, 느림 |
| 압축 저장 | BINARY(16) | 16 bytes | 빠름, 공간 절약 |
| 함수 기반 | UUID_TO_BIN() | 16 bytes | 정렬 최적화 가능 |
BINARY(16) 장점
- 16바이트로 저장
- 인덱스 작아짐
- 비교 빠름
- 캐시 효율 증가
- 이러한 이유들로 MySQL에선 BINARY(16)이 사실상 정석
id BINARY(16) NOT NULL PRIMARY KEY
그렇다면 PostgreSQL에서 크기 해결법은?
- Postgres에는 전용 uuid 타입이 있어 내부적으로 16바이트로 저장함
- 인덱스 최적화
- 함수 내장
- 타입 안정성 보장
결론
Snowflake는 더 강력하지만, ID 생성 전략이 인프라 설계의 일부가 된다.
UUID v7은 Snowflake의 시간 정렬 장점을 유지하면서도, 표준 기반이며 운영 부담이 없기 때문에
이번 프로젝트는 운영 단순성과 표준 기반 설계를 우선했다.
'Spring Boot' 카테고리의 다른 글
| Spring Boot 4에서 외부 API 호출 무엇을 선택해야 할까? (0) | 2026.03.27 |
|---|---|
| Lettuce VS Redisson 분산 락 (0) | 2026.02.13 |
| 🚚 허브 간 최단 경로 탐색 캐시 스탬피드 (Cache Stampede) 발생+해결 (1) | 2026.01.19 |
| 🚚 허브 간 최단 경로 탐색 알고리즘 구현 + Kakao Map Api 연동 (Spring Boot) (0) | 2025.12.06 |
| DDD의 페이징 로직 (Spring Boot) (0) | 2025.12.02 |