--- id: cs-snowflake-id-generation title: ID 생성 — UUID v7 / Snowflake / KSUID category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [cs, id, uuid, snowflake, vibe-coding] tech_stack: { language: "TS", applicable_to: ["Backend"] } applied_in: [] aliases: [UUID v7, Snowflake, KSUID, ULID, NanoID, time-ordered ID, sortable] --- # ID Generation > AUTO_INCREMENT = 한계 (분산 X). UUID v4 = 안 정렬. **UUID v7 / Snowflake / KSUID = 시간 정렬 + 분산 OK + 인덱스 friendly**. ## 📖 핵심 개념 - 시간 정렬: 최신 INSERT 가 인덱스 끝 (페이지 수 적게). - 분산 생성: 중앙 service 없이 다중 노드. - 비밀: UUID v4 가 추측 불가 (URL 안전). - 길이: 16-26 byte. ## 💻 코드 패턴 ### UUID v7 (modern, 표준 RFC 9562) ```ts import { v7 as uuidv7 } from 'uuid'; const id = uuidv7(); // "01918fec-d2c5-7000-8aab-1234abcd" // 첫 48 bit = unix ms timestamp → 시간 정렬 ``` ```sql -- Postgres 17+ SELECT uuidv7(); ``` → UUID v4 호환 + 시간 정렬. **2026 권장 디폴트**. ### Snowflake (Twitter) ``` 64-bit: 41 bit: timestamp (ms since epoch) 10 bit: machine ID 12 bit: sequence (per ms) → ms 당 4096 ID, 1024 machines. ``` ```ts class Snowflake { private seq = 0; private lastMs = 0; constructor(private machineId: number) {} next(): bigint { const now = Date.now(); if (now === this.lastMs) { this.seq++; if (this.seq >= 4096) { // wait for next ms while (Date.now() === this.lastMs); return this.next(); } } else { this.seq = 0; } this.lastMs = now; return (BigInt(now) << 22n) | (BigInt(this.machineId) << 12n) | BigInt(this.seq); } } ``` → 64-bit (BigInt). 빠르고 정렬. machine ID 충돌 위험. ### KSUID (Segment) ```ts import { KSUID } from 'ksuid'; const id = KSUID.randomSync().string; // "0ujsswThIGTUYm2K8FjOOfXtY1K" // 27 글자, 첫 4 byte = 32-bit unix seconds, 16 byte random ``` → 27 char, sortable, URL-safe. ### ULID ```ts import { ulid } from 'ulid'; const id = ulid(); // "01ARZ3NDEKTSV4RRFFQ69G5FAV" // 26 글자, 첫 10 = ms timestamp, 16 = random ``` → KSUID 비슷, 더 짧음. ### NanoID (random, secure) ```ts import { nanoid } from 'nanoid'; const id = nanoid(); // 21 char const short = nanoid(10); // 10 char ``` → URL-safe, 빠름. 단 시간 정렬 X. ### CUID2 ```ts import { createId } from '@paralleldrive/cuid2'; const id = createId(); ``` → Collision-resistant + URL-safe + 시간 정렬. ### Postgres column type ```sql -- UUID CREATE TABLE orders ( id UUID PRIMARY KEY DEFAULT uuidv7() -- PG 17+ ); -- 또는 string CREATE TABLE orders ( id TEXT PRIMARY KEY -- KSUID / ULID / NanoID ); -- BIGINT (Snowflake) CREATE TABLE orders ( id BIGINT PRIMARY KEY ); ``` ### 인덱스 영향 ``` v4 UUID: random → 매 INSERT 가 인덱스 random page → 큰 cache miss. v7 UUID: 시간 정렬 → 항상 끝 page → 빠름. AUTO_INCREMENT: 같지만 분산 X. ``` → 큰 테이블 = v7 가 v4 보다 INSERT 5-10x 빠름. ### 보안 ``` v4: 122-bit random → 추측 불가. URL 안전. v7: 첫 48 bit = 시간 노출 → 정확 시각 추적 가능. (random 80-bit 도 함께 — collision 안전) ``` → Public ID = v4 또는 짧은 nanoid. Internal = v7. ```ts // 두 단계 — internal sortable + public random { id: 'uuid-v7', publicId: 'nanoid' } ``` ### 분산 서비스 (Sonyflake / IdGen) - Snowflake 변형, machine ID 자동 할당. - Redis SETNX 또는 etcd. ```ts class Sonyflake { // sub-second precision, larger machine bits // machine ID = MAC 주소 last 16 bit } ``` ### Sequence database (PG / MySQL) ```sql -- PG sequence CREATE SEQUENCE orders_id_seq START 1; SELECT nextval('orders_id_seq'); -- 단일 PG = bottleneck. 분산 X. ``` ### Encrypted ID (Hashids / Sqids) ```ts import Sqids from 'sqids'; const sqids = new Sqids({ minLength: 8 }); const encoded = sqids.encode([userId]); // "yWBV0AVL" const decoded = sqids.decode(encoded); // [userId] ``` → Auto-increment 숨기기. URL 짧음 + 의미 있는 short link. ## 🤔 의사결정 기준 | 상황 | 추천 | |---|---| | 새 프로젝트 디폴트 | UUID v7 | | Public URL ID | NanoID / KSUID | | 분산 서비스 (Twitter scale) | Snowflake | | Schema 호환 (UUID 컬럼) | UUID v7 | | 짧은 ID 필요 | NanoID 10-12 | | 정렬 + 짧음 | KSUID / ULID | ## ❌ 안티패턴 - **UUID v4 + 큰 테이블**: 인덱스 성능 추락. - **AUTO_INCREMENT 분산 환경**: collision / sync. - **Snowflake machine ID hard-code**: 충돌. 자동 할당. - **System clock backwards (NTP)**: ID 충돌. monotonic clock. - **추측 가능 ID public 노출 (1, 2, 3)**: enumeration 공격. - **ID = 비즈니스 의미**: ABC-2026-0001 같은 — 변경 어려움. - **여러 system 다른 형식**: 통일 (모두 UUID 또는 모두 KSUID). ## 🤖 LLM 활용 힌트 - 새 = UUID v7 (PG 17+ 또는 라이브러리). - Public URL = NanoID. - Distributed scale = Snowflake. - 시간 정렬 + URL-safe = KSUID / ULID. ## 🔗 관련 문서 - [[DB_Index_Strategy]] - [[DB_Sharding_Strategies]] - [[Security_OWASP_Top_10_Practical]]