Files
llm-wiki/raw/branch-notes/chore-ulid-to-uuidv7.md
T

13 KiB

title, source_type, status, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, branch, parent_branch, git_branch, related_projects, tags, created, target_merge, status_label, contract_packet_sha256
title source_type status id kind project work_item inherits refines overrides depends_on contract_packet branch parent_branch git_branch related_projects tags created target_merge status_label contract_packet_sha256
branch / chore-ulid-to-uuidv7 (ID 생성 전략 ULID → UUIDv7) branch-note raw BR-CA-SKELETON-CHILD-F1674A3D branch-child ca-skeleton-operational-contract WI-CA-SKELETON-OPERATIONAL-CONTRACT-046
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-RANDOM-001@1
1 chore-ulid-to-uuidv7 feature-resource-identifier-contract refactor/ulid-to-uuidv7
ca-skeleton
nplus1-presentation-prep
branch
ca-skeleton
data-modeling
persistence
api-design
ulid
uuid-v7
2026-07-08 develop in-progress 59f232e47d66c49ed76c4e3ffa50cab20877fe73c40037b9e3d49d1b9707b984

branch: chore-ulid-to-uuidv7 — ID 생성 전략 ULID → UUIDv7

Layer: raw/branch-notes/ — ca-tmpl의 엔티티 식별자 생성을 ULID에서 UUIDv7로 교체. [raw/branch-notes/feature-resource-identifier-contract]를 개정하는 계약-레벨 변경. 실제 git 브랜치: refactor/ulid-to-uuidv7. 위키 파일명은 prefix 규칙상 chore-. ADR: ca-tmpl:docs/choice/0001-id-strategy-ulid-to-uuidv7.md. status_label: in-progress (구현 완료 · ./gradlew check 전량 GREEN 로컬 검증됨 (2026-07-08) · 사용자 커밋/머지 대기).

부모

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: ULID format·PostgreSQL uuid persistence·SecureRandom test가 통과한다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1 database는 PostgreSQL 16 단일 stack이다 DB 컬럼을 native uuid로 유지하고 문자열/생성 전략만 UUIDv7로 바꾼다. raw/project-notes/ca-skeleton-operational-contract
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-RANDOM-001@1 ULID·idempotency key·token 생성의 random source는 SecureRandom이다 UUIDv7 generator와 금지 rule이 project random-source 경계를 유지하게 한다. raw/project-notes/ca-skeleton-operational-contract

브랜치 지역 결정

기존 branch-local 결정은 아래 ## Decision Evidence Map의 D-01~D-05가 소유한다. 부모 branch는 legacy packet이라 pinned project decision을 별도로 제공하지 않는다.

선언한 예외

  • 없음.

목표

ULID의 시간정렬은 밀리초 타임스탬프 기준이라 다중 인스턴스 환경에서 같은 ms 내 순서를 보장하지 못하고, 표준 타입이 아니라 커스텀 라이브러리(ulid-creator)+Crockford 코덱+ULID↔UUID 변환 매퍼가 필요했다. UUIDv7(RFC 9562) 은 표준 java.util.UUID이면서 상위 48비트가 ms 타임스탬프라 ULID와 동일한 인덱스 지역성을 유지한다 → 표준화 + 커스텀 의존 제거가 목적(성능 개선이 아님).

범위

포함 범위 (69파일)

  • 생성기: com.github.f4b6a3:ulid-creator:5.2.3com.github.f4b6a3:uuid-creator:6.1.1, UlidCreator.getMonotonicUlid()UuidCreator.getTimeOrderedEpochPlus1()(UUIDv7, 모노토닉 변형 — 구 getMonotonicUlid() 의 밀리초-내 단조증가 의도를 보존; jar javap 로 API 확인). build.gradle 2곳 + 락파일 재생성(app-bootstrap sampleFixture config 는 canBeResolved=falseresolveAndLockAll 이 못 만져서 stale ulid-creator 줄을 수동 병합 제거).
  • 코덱/팩토리 리네임: UlidCodecUuidCodec(+ Spock spec), UlidPosterIdFactory/UlidWorkLogIdFactory/UlidOutboxEventIdFactoryUuid*.
  • 도메인 ID 값객체: 정규식 26자 Crockford ULID → 36자 표준 UUID. PosterId/WorkLogId javadoc 갱신.
  • 매퍼: Ulid.from(id).toUuid()UUID.fromString(id.value()), Ulid.from(uuid).toString()uuid.toString() (변환 소멸, java.util.UUID stdlib).
  • : 컨트롤러 toId, ID 시리얼라이저 — ULID 대문자 정규화 → UUID 소문자 canonical.
  • ArchUnit: NO_UUID_RANDOM_IN_CONTROLLER의 FQN com.github.f4b6a3.ulid.UlidCreatorcom.github.f4b6a3.uuid.UuidCreator(UUID.randomUUID 금지는 유지). NO_LONG_ID_PK 주석(D10) 갱신.
  • 문서: identifier·sample-portfolio CLAUDE.md/README, ContractSnapshots, 테스트 19개(픽스처 26자→36자).
  • 불변: DB uuid 컬럼(128비트) 그대로 — 상위 비트만 v7 레이아웃.

제외 범위

  • PostgreSQL column type 변경과 data migration은 수행하지 않는다.
  • local test 결과를 prod 성능 또는 다중 인스턴스 순서 보장으로 승격하지 않는다.

근거 (필수, 최소 1개+)

Source 정당화하는 결정
raw/official-docs/rfc9562-uuid D-01의 UUIDv7 layout과 timestamp-ordered identifier 정의를 뒷받침한다.
raw/official-docs/ulid-spec 기존 ULID format·monotonic semantics와 UUIDv7 전환 전후 경계를 비교한다.

TODO

  • UUIDv7 generator·codec·factory·mapper·fixture 전환 — 등급: actually-implemented
  • ./gradlew check와 monotonic 1000-loop 검증 — 등급: locally-verified
  • 부모 identifier 계약 D3/D10과 project WI-046 completion text 갱신 — 등급: planned
  • 사용자 commit·merge와 downstream 소비자 확인 — 등급: needs-confirmation

진행 중 메모

  • 구현과 local 검증은 끝났지만 부모 계약과 project registry는 아직 ULID 문구를 소유한다.
  • N+1 branch는 변경 계기만 제공하며 이 branch의 project owner나 work-item dependency가 아니다.

결정 사항

  • 2026-07-08 D-01: resource identifier canonical form을 ULID에서 UUIDv7로 바꾼다. / 이유: native UUID wire/storage shape와 generator 표준화를 맞춘다. / 대안: ULID 유지, UUIDv4. / 근거: raw/official-docs/rfc9562-uuid, raw/official-docs/ulid-spec.
  • 2026-07-08 D-03: UUIDv7 generator는 millisecond 내 단조 증가 의도를 보존하는 getTimeOrderedEpochPlus1()을 사용한다. / 검증: local API inspection과 loop test.
  • 2026-07-08 D-05: PostgreSQL native uuid column은 유지하고 변환 mapper만 제거한다. / 근거: inherited database decision.

결정-근거 매핑

Decision ID Decision Supporting Claims Evidence Strength Open Risk
D-01 ULID → UUIDv7 채택 ADR docs/choice/0001; 사용자 결정(다중 인스턴스 순서 한계 + 비표준). UUIDv7 상위 48비트 ms = ULID와 동일 지역성 Strong API 브레이킹(26→36자) — 다운스트림/스냅샷 갱신 필요
D-02 UUIDv4는 기각 랜덤 PK = B-tree 단편화(페이지분할·캐시지역성↓), 쓰기多 테이블 성능 후퇴 Strong 없음(발표 시연 소재로 별도 활용)
D-03 생성 = uuid-creator getTimeOrderedEpochPlus1() (모노토닉 UUIDv7) jar javap 로 메서드 시그니처 확인 + check GREEN. getTimeOrderedEpoch()(비-모노토닉) 대신 Plus1 선택 = 구 getMonotonicUlid() 의도(ms-내 단조증가) 미러 Strong (검증됨) 없음 — 모노토닉 문자열 정렬 1000-loop 테스트 GREEN
D-04 ArchUnit FQN ulid→uuid 교체(생성 위치 강제 유지) CleanArchitectureTest NO_UUID_RANDOM_IN_CONTROLLER 수정 + suite GREEN Strong (검증됨) 없음
D-05 저장 스키마 불변(native uuid) 매퍼만 변환 제거, 마이그레이션 무수정 Strong 없음

구현 가이드

Anchor 적용 검증
identifier adapter UuidCreator.getTimeOrderedEpochPlus1()으로 ID를 생성하고 domain은 factory port만 사용한다. monotonic 1000-loop와 adapter test
web/serialization UUID canonical lowercase 36자를 입력·출력 계약으로 사용한다. wire test와 snapshot scrubber
persistence UUID.fromString/UUID.toString을 사용하고 native uuid column을 유지한다. mapper/integration test와 migration diff 없음 확인
architecture rule controller direct generation 금지를 UuidCreator FQN 기준으로 유지한다. CleanArchitectureTest

엣지·실패·의존

  • 실패·엣지 경로: 26자 ULID consumer가 남아 있으면 path parsing과 snapshot contract가 깨진다. downstream fixture와 wire contract를 함께 갱신한다.
  • 실패·엣지 경로: native uuid column까지 변경하면 불필요한 data migration이 생긴다. schema는 유지한다.
  • 다른 계약 의존: raw/branch-notes/feature-resource-identifier-contract의 D3·D5·D10·D17과 project WI-CA-SKELETON-OPERATIONAL-CONTRACT-046을 소비한다.

검증해야 할 주장

리팩터 GREEN 검증 완료 (2026-07-08, ./gradlew check BUILD SUCCESSFUL 4m31s, 200 tasks). 아래는 확정 결과:

  1. ./gradlew check 전량 GREEN(spotless/checkstyle/spotbugs/errorprone/ArchUnit/verifyDependencyLocks/verifyPublicPathSnapshot/verifyCleanArchitectureDependencies/all tests + Testcontainers 통합 + sampleOffTest). locally-verified.
  2. uuid-creator:6.1.1 resolve + 락 재생성(resolveAndLockAll --write-locks) 성공. UUIDv7 메서드 = getTimeOrderedEpochPlus1()(jar javap 확인, 모노토닉). locally-verified.
  3. 테스트 픽스처(26자 ULID → 36자 canonical UUID 0190bd6e-7c3e-7abc-8def-0123456789ab 등) 전부 갱신 + 의미 보존: WorkLogIdTest.rejectsCrockfordUlidFormat 는 구 26자 형식이 이제 거부됨을 증명하는 회귀가드로 신설. 모노토닉 1000-loop 문자열 정렬 테스트 GREEN. property 테스트(jqwik)는 canonical UUID 생성기로 재작성. locally-verified.
  4. API 브레이킹(ID 문자열 26→36자) — 와이어 테스트 .value(ID) 새 UUID로 일치, ContractSnapshots 스크러버 정규식 ULID→UUID 로 교체(엔티티 id 가 스냅샷에 새면 계속 스크럽됨). 커밋된 .approved.txt 스냅샷은 volatile 필드만 <scrubbed> 라 영향 없음. locally-verified.
  5. canonical 계약 개정 미완(후속): raw/branch-notes/feature-resource-identifier-contract D3(canonical form)·D10(저장 변환) 결정 텍스트를 wiki/registries에서 UUIDv7로 갱신 필요. planned.

다음 단계

  1. 에이전트 구현 완료 → check 전량 GREEN(69파일 변경: 57 M · 6 D · 6 새파일(?? Uuid* 리네임 대상)) 검증됨(2026-07-08).
  2. feed 파운데이션 가이드 §0.2/§2.1/§4.4를 최종 UUIDv7 패턴(getTimeOrderedEpochPlus1)으로 갱신.
  3. 사용자 커밋 → develop 머지 → lab/nplus1-highlight-feed 반영 → Task 0.
  4. raw/branch-notes/feature-resource-identifier-contract canonical(D3/D10) 개정(후속).

마주친 문제

묶음 (이 branch에서 파생된 자료)

오류 기록 (이 branch 작업 중 발생)

면접 준비

블로그·채용공고 연계 글감

관련 일일 노트

  • 연결된 daily-note 없음.

완료 후 정리

  • PR 링크: 없음 — 사용자 commit 대기.
  • 리뷰 메모: local full check와 identifier-specific regression은 통과했고 부모 계약 갱신은 남아 있다.
  • 머지 결과 / 배포 환경: local verification만 완료, staging/prod 검증 없음.
  • wiki 추출 대상: UUIDv7 generator·wire canonical form·native uuid persistence 유지의 locally-verified 결과.
  • 추출하지 않을 항목: parent canonical/registry 갱신 전 project-wide 완료 주장과 prod 성능 주장.