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 |
|
1 | chore-ulid-to-uuidv7 | feature-resource-identifier-contract | refactor/ulid-to-uuidv7 |
|
|
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) · 사용자 커밋/머지 대기).
부모
- raw/branch-notes/feature-resource-identifier-contract — D3 canonical form, D5 no-direct-gen, D10 ULID↔uuid 저장, D17 no-long-PK를 소유하며 이 브랜치가 D3/D10을 UUIDv7 기준으로 정제한다.
- 트리거: raw/branch-notes/experiment-nplus1-highlight-feed — N+1 피드 도메인 파운데이션 가이드 작성 중 ID 규약(ULID) 재검토에서 파생. 그쪽 §0.2가 이 결정을 참조.
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
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.3→com.github.f4b6a3:uuid-creator:6.1.1,UlidCreator.getMonotonicUlid()→UuidCreator.getTimeOrderedEpochPlus1()(UUIDv7, 모노토닉 변형 — 구getMonotonicUlid()의 밀리초-내 단조증가 의도를 보존; jar javap 로 API 확인). build.gradle 2곳 + 락파일 재생성(app-bootstrapsampleFixtureconfig 는canBeResolved=false라resolveAndLockAll이 못 만져서 staleulid-creator줄을 수동 병합 제거). - 코덱/팩토리 리네임:
UlidCodec→UuidCodec(+ Spock spec),UlidPosterIdFactory/UlidWorkLogIdFactory/UlidOutboxEventIdFactory→Uuid*. - 도메인 ID 값객체: 정규식 26자 Crockford ULID → 36자 표준 UUID.
PosterId/WorkLogIdjavadoc 갱신. - 매퍼:
Ulid.from(id).toUuid()→UUID.fromString(id.value()),Ulid.from(uuid).toString()→uuid.toString()(변환 소멸,java.util.UUIDstdlib). - 웹: 컨트롤러
toId, ID 시리얼라이저 — ULID 대문자 정규화 → UUID 소문자 canonical. - ArchUnit:
NO_UUID_RANDOM_IN_CONTROLLER의 FQNcom.github.f4b6a3.ulid.UlidCreator→com.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
UUIDwire/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
uuidcolumn은 유지하고 변환 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
uuidcolumn까지 변경하면 불필요한 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). 아래는 확정 결과:
- ✅
./gradlew check전량 GREEN(spotless/checkstyle/spotbugs/errorprone/ArchUnit/verifyDependencyLocks/verifyPublicPathSnapshot/verifyCleanArchitectureDependencies/all tests + Testcontainers 통합 + sampleOffTest).locally-verified. - ✅
uuid-creator:6.1.1resolve + 락 재생성(resolveAndLockAll --write-locks) 성공. UUIDv7 메서드 =getTimeOrderedEpochPlus1()(jar javap 확인, 모노토닉).locally-verified. - ✅ 테스트 픽스처(26자 ULID → 36자 canonical UUID
0190bd6e-7c3e-7abc-8def-0123456789ab등) 전부 갱신 + 의미 보존:WorkLogIdTest.rejectsCrockfordUlidFormat는 구 26자 형식이 이제 거부됨을 증명하는 회귀가드로 신설. 모노토닉 1000-loop 문자열 정렬 테스트 GREEN. property 테스트(jqwik)는 canonical UUID 생성기로 재작성.locally-verified. - ✅ API 브레이킹(ID 문자열 26→36자) — 와이어 테스트
.value(ID)새 UUID로 일치,ContractSnapshots스크러버 정규식 ULID→UUID 로 교체(엔티티 id 가 스냅샷에 새면 계속 스크럽됨). 커밋된.approved.txt스냅샷은 volatile 필드만<scrubbed>라 영향 없음.locally-verified. - ⏳ canonical 계약 개정 미완(후속): raw/branch-notes/feature-resource-identifier-contract D3(canonical form)·D10(저장 변환) 결정 텍스트를 wiki/registries에서 UUIDv7로 갱신 필요.
planned.
다음 단계
- ✅ 에이전트 구현 완료 →
check전량 GREEN(69파일 변경: 57 M · 6 D · 6 새파일(?? Uuid* 리네임 대상)) 검증됨(2026-07-08). - feed 파운데이션 가이드 §0.2/§2.1/§4.4를 최종 UUIDv7 패턴(
getTimeOrderedEpochPlus1)으로 갱신. - 사용자 커밋 → develop 머지 →
lab/nplus1-highlight-feed반영 → Task 0. - raw/branch-notes/feature-resource-identifier-contract canonical(D3/D10) 개정(후속).
마주친 문제
- Gradle strict lock 갱신이 non-resolvable
sampleFixture의 stale entry를 제거하지 못했다.
묶음 (이 branch에서 파생된 자료)
오류 기록 (이 branch 작업 중 발생)
- raw/errors/gradle-strict-lock-stale-entry-non-resolvable-config-2026-07-08 —
resolveAndLockAll이canBeResolved=false인sampleFixtureconfig 를 건너뛰어 app-bootstrap lockfile 에 staleulid-creator줄이 남은 문제 + 수동 병합 해결.
면접 준비
- 새 질문 없음 — 식별자 생성/거버넌스 면접 소재는 기존 raw/interviews/clean-architecture-identifier-generation 이 이미 커버(UUIDv7 vs ULID 인덱스 지역성 각도는 그 노트 갱신 시 반영).
블로그·채용공고 연계 글감
- 별도 신규 글감 없음 — ULID→UUIDv7 표준화·인덱스 지역성 각도는 이 branch note 자체가 entry point 이며 기존 raw/blog-topics/ulid-crockford-base32-excluded-letters-2026-06-01 클러스터에 속함.
관련 일일 노트
- 연결된 daily-note 없음.
완료 후 정리
- PR 링크: 없음 — 사용자 commit 대기.
- 리뷰 메모: local full check와 identifier-specific regression은 통과했고 부모 계약 갱신은 남아 있다.
- 머지 결과 / 배포 환경: local verification만 완료, staging/prod 검증 없음.
- wiki 추출 대상: UUIDv7 generator·wire canonical form·native
uuidpersistence 유지의 locally-verified 결과. - 추출하지 않을 항목: parent canonical/registry 갱신 전 project-wide 완료 주장과 prod 성능 주장.