Files
tech-log-backend/src/adapter/outbound/identifier

adapter-identifier — 설계 결정 참조

비-IO 인프라 능력(capability) 어댑터 모듈. 패키지 루트: dev.caskeleton.adapter.identifier.

허용/금지 의존과 테스트 명령 같은 모듈 규칙CLAUDE.md 가 SSOT 다. 이 문서는 코드 주석에서 덜어낸 설계 결정의 근거를 모아둔 참조용 기록이다 — 코드를 읽다 "왜 이렇게 했나"가 궁금할 때 본다.

왜 별도 모듈인가 (adapter-outbound 와의 구분)

adapter-outbound 처럼 도메인 포트를 구현하는 driven/secondary 어댑터지만, 외부 시스템 연동이 없는(no external-system integration) 능력만 담는다: 식별자 생성/인코딩(UUIDv7), 같은 근거로 clock· crypto/random 소스. UUID id/코덱 능력은 인프라이지 아웃바운드 연동 지점이 아니므로, 이것을 adapter-outbound 밖에 둬야 그 모듈의 문서화된 의미("외부 HTTP / messaging / cache / notifications")가 유지된다.

UuidCodec

도메인 무관 UUID 변환 유틸. JDK java.util.UUID(RFC 9562 UUIDv7) 위에서 동작한다.

  • normalize(String)D3: 대소문자 무관 canonical UUID 입력을 받아 canonical 36자 소문자 형태로 반환. 형식 오류 UUID 에는 IllegalArgumentException. null 입력은 null 반환.
  • toUuid(String)D10: UUID 문자열 → 128-bit UUID (PostgreSQL uuid 컬럼용).
  • fromUuid(UUID)D10: 저장된 UUID → canonical 36자 소문자 UUID 문자열.

HmacUserPrincipalPseudonymizer

UserPrincipalPseudonymizerPort(application-core) 의 HMAC-SHA-256 구현.

알고리즘 SSOT

구체 알고리즘은 90일 회전 salt 로 키잉한 HMAC-SHA-256 이다. 이 클래스가 유일한 구현이며, 비-IO crypto 능력 어댑터로 이 모듈에 있고 app-bootstrap 이 싱글톤 빈으로 와이어링한다.

출력

비어있지 않은 rawPrincipal 에 대해 단방향·안정적인 256-bit HMAC 토큰을 64자 소문자 hex 로 반환. rawPrincipalnull 이거나 blank 면 null 반환.

Salt 출처

salt 는 app-bootstrapAPP_PRIVACY_PSEUDONYMIZATION_SALT 환경변수에서 공급한다(분류: secret, 회전 주기: 90일). 이 클래스는 salt 를 스스로 조달하지 않는다.

Thread safety

Mac 인스턴스는 thread-safe 하지 않다. 매 pseudonymize(String) 호출마다 새 Mac 을 생성하므로 공유 싱글톤 빈으로 안전하다. HmacSHA256 은 JDK 필수 알고리즘(JCA spec)이라 NoSuchAlgorithmException· InvalidKeyException 은 사실상 도달 불가능하며, 호출부에 checked exception 잡음을 남기지 않으려고 IllegalStateException 으로 감싼다.

Spring-free

이 모듈(adapter-identifier)은 설계상 Spring-free 다. 어노테이션이 없고, 빈 생성은 app-bootstrap 의 책임이다.

빌드 결정 (build.gradle)

Groovy / Spock (C2 테스트 형태)

순수 값-코덱 동작(UuidCodec)은 Groovy/Spock 스펙(src/test/groovy)으로 명세한다. core groovy 플러그인이 컴파일하고, 모든 서브프로젝트에 이미 켜진 JUnit Platform(useJUnitPlatform())에서 실행된다. 가드/계약 테스트(HmacUserPrincipalPseudonymizerTest — 생성자 가드, 정확한 예외/포맷 계약)는 설계상 Java(src/test/java)로 둔다. Spock 2.4 / Groovy 4.0 variant 를 쓰며, spock-core 가 groovy.jar 를 transitive 로 끌어오므로 data-driven where: 스펙에 다른 Groovy 모듈이 필요 없다.

implementation vs api

:application-coreimplementation 으로 선언한다(api 아님). adapter-identifier 가 자신의 public ABI 에 application-core 타입을 노출하지 않기 때문이다. 유일한 와이어링 소비자인 app-bootstrap 은 이미 자기 classpath 에 application-core 를 갖고 있다. 이 의존 edge 는 src/build.gradleallowedProjectDependencies['adapter-identifier'] 로 허용된다.

UTF-8 인코딩 고정

한국어(비-ASCII) Spock 스펙 메서드명은 소스를 UTF-8 로 읽어야만 컴파일·리포팅이 정상이다. 이 모듈이 비-ASCII 소스를 처음 갖는 모듈이라 컴파일 인코딩을 명시적으로 고정한다 — UTF-8 호스트에선 no-op 지만, 플랫폼 기본이 다른 fork(예: 한국어 Windows / MS949)에서 mojibake 빌드를 막는다. C2 가 더 많은 모듈로 퍼지면 root subprojects 블록(-parameters 옆)으로 승격한다.