65 lines
4.3 KiB
Markdown
65 lines
4.3 KiB
Markdown
---
|
|
title: error / archunit-no-uuid-random-trace-id-false-positive-2026-06-01
|
|
source_type: error-note
|
|
status: raw
|
|
related_branches: [feature-resource-identifier-contract]
|
|
related_projects: [ca-skeleton]
|
|
tags: [error, ca-skeleton, architecture, archunit, identifier, trace-id, scoping]
|
|
created: 2026-06-01
|
|
status_label: resolved
|
|
---
|
|
|
|
# error: archunit-no-uuid-random-trace-id-false-positive-2026-06-01
|
|
|
|
> Layer: `raw/errors/` — 작업 중 마주친 단일 실패·트러블슈팅 기록. 원본은 raw에 영구 보관한다.
|
|
|
|
## Parent / 부모
|
|
|
|
- [[raw/branch-notes/feature-resource-identifier-contract]] — D17 `no_uuid_random_in_controller` rule이 *resource id 생성*이 아닌 *correlation/trace id 생성*까지 잡는 false positive를 냈다.
|
|
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl ArchUnit guardrail의 scope 정밀도 이슈다.
|
|
|
|
## 증상 / Symptom
|
|
|
|
- 에러 메시지 (원문 그대로):
|
|
```text
|
|
Rule 'D5/D17 no_uuid_random_in_controller: ...' was violated (1 times):
|
|
Method <dev.caskeleton.adapter.web.filter.RequestLoggingFilter.doFilterInternal(...)>
|
|
calls method <java.util.UUID.randomUUID()> in (RequestLoggingFilter.java:43)
|
|
```
|
|
- 발생 컨텍스트: `cd src && ./gradlew :app-bootstrap:test` 실행 중 `CleanArchitectureTest.no_uuid_random_in_controller` 실패.
|
|
- 발생 시점: 2026-06-01 (D17 rule을 boundary suite에 추가한 직후 1차 실행)
|
|
- 재현 가능 여부: `always` — rule 대상 package를 `..adapter.web..`(광범위)로 두면 기존 `RequestLoggingFilter`가 항상 걸림.
|
|
|
|
## 재현 절차 / Reproduction
|
|
|
|
1. `no_uuid_random_in_controller`를 `noClasses().that().resideInAnyPackage("..adapter.web..", "..application..")`로 작성.
|
|
2. `cd src && ./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실행.
|
|
3. 기대 결과: resource id 생성만 차단.
|
|
4. 실제 결과: `RequestLoggingFilter`의 `X-Request-Id` 생성(`UUID.randomUUID()`)까지 위반으로 잡힘.
|
|
|
|
## 근본 원인 / Root cause
|
|
|
|
- 직접 원인: rule의 selector가 `..adapter.web..` 전체였는데, web filter는 controller가 아니며 correlation/trace id를 생성한다.
|
|
- 근본 원인: spec D17 **결정 텍스트**는 "controller / service / use case layer"로 한정했으나, §6 **reference 코드**는 광범위한 `..adapter.web..`를 썼다. 둘 사이의 미세 불일치가 구현 시 노출. 또한 trace/correlation id는 D18에서 *본 branch 범위 밖*(distributed-tracing-contract)으로 명시돼 있어, resource-id rule이 잡으면 안 되는 대상이었다.
|
|
- 트리거 조건: 기존 production filter가 합법적으로 `UUID.randomUUID()`를 trace id 용도로 사용 중.
|
|
|
|
## 해결 / Resolution
|
|
|
|
- 적용한 조치: selector를 D17 결정 텍스트에 맞춰 `..adapter.web..controller..` + `..application..`로 좁혔다. trace/correlation id 생성(web filter)은 의도적으로 scope 밖임을 rule `.as(...)` 설명에 명시.
|
|
- 검증 방법:
|
|
- `cd src && ./gradlew :app-bootstrap:test verifyCleanArchitectureDependencies` 성공.
|
|
- `cd src && ./gradlew check` 전체 성공.
|
|
- 잔여 위험 / 후속 작업: filter/interceptor가 *resource* id를 생성하는 안티패턴은 이 rule로는 안 잡힌다(범위 밖). 필요 시 distributed-tracing-contract 또는 별도 rule로 분리.
|
|
|
|
## 회고 / Lessons
|
|
|
|
- 빨리 감지하는 신호: 새 ArchUnit rule이 *기존* 합법 코드를 잡으면, rule이 틀렸을 가능성을 먼저 의심하고 위반 대상의 *의도*(여기선 trace id vs resource id)를 확인.
|
|
- 예방 체크리스트: rule selector는 spec의 "결정 텍스트"(좁은 의도)와 "reference 코드"(넓은 예시)가 다를 때 결정 텍스트를 따른다. id 생성 규칙은 *어떤 id*인지(resource / trace / session / idempotency) 항상 구분한다.
|
|
- 일반화된 교훈: 식별자 거버넌스 규칙은 "ID 종류"별로 책임 branch가 다르다 — 한 rule이 모든 `UUID.randomUUID()`를 잡으면 cross-domain false positive가 난다.
|
|
|
|
## Related / 관련
|
|
|
|
- 관련 branch note: [[raw/branch-notes/feature-resource-identifier-contract]]
|
|
- 관련 형제 branch: [[raw/branch-notes/feature-boundary-validation-mapping-contract]] (ArchUnit suite 호스팅), [[raw/branch-notes/feature-distributed-tracing-contract]] (trace id 책임)
|
|
- 파생 blog 글감: [[raw/blog-topics/identifier-governance-rule-scoping-by-id-kind-2026-06-01]]
|