- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다 - 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5 (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를 techviz 로 만들었다 - 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs 돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다. Concept 이 인용한 코드가 SSOT 에 없어 뺐다 - candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
9.6 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, body, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | body | assets | evidence | source | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | analysis-finding-a05-f010 | 정본이라던 코디네이터는 그 포트를 구현하지 않고 다른 클래스가 구현한다 | multitenancy-isolation | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:analysis-finding-a05-f010 | 2026-09-04 | case-analysis-finding-a05-f010.body.md |
|
|
|
정본이라던 코디네이터는 그 포트를 구현하지 않고 다른 클래스가 구현한다
JpaTransactionAutoConfiguration:37 주석이 정본 경계로 지목한 메서드는 PolicyTransactionPort 의 것인데, 같은 문장이 아래에 만들어지는 재시도 코디네이터로 이어진다. 그 포트를 실제로 구현하는 것은 어댑터 쪽 SpringTransactionPort 이고 코디네이터는 자기 실행기와 짝을 이룬다.
관계
- 중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다 이 사례가 속한 구조다. 두 스택이 다 조립되고 어느 쪽이 정본인지가 갈린다.
- 재시도 코디네이터는 빈이지만 그것을 어디에도 적용하지 않는다 같은 코디네이터의 배선 공백을 다룬 기록이다.
- 같은 개념의 두 어휘가 공존하면 하나를 죽은 것으로 표시한다 이 사례가 어기는 규칙이다. 둘 다 살아 있고 어느 쪽도 죽은 것으로 표시되지 않았다.
문제
정본 트랜잭션 경계가 어디인지는 이 리프의 구조를 읽는 출발점이다.
자동설정 주석이 그 경계를 지목한다. 그 지목이 실제 구현 관계와 맞는지 확인했다.
결론
그 포트를 구현한다고 선언한 파일은 하나뿐이다. 없는 이름으로 같은 검색식을 걸어도 0 이 나오므로, 상위 인터페이스로 같은 검색을 걸어 21 을 받아 검색이 도는 것을 확인했다.
그 클래스가 자기 자바독에서 설명하는 것은 모드별 템플릿과 격리 수준 고정이다. 재시도라는 낱말이 거기 없다.
자동설정이 만드는 빈은 여섯이다 — CommitFailureClassifier, SpringJpaTransactionExecutor 둘, FullTransactionRetryCoordinator 둘, RetrySleeper. 그 목록에 포트 구현이 없다.
즉 주석이 한 문장 안에서 두 스택을 잇고 있다. 앞부분이 지목한 포트는 어댑터 쪽 클래스가 구현하고, 뒷부분이 설명하는 코디네이터는 자동설정 쪽 실행기와 짝을 이룬다.
네 이름의 main 참조 파일 수가 모두 4 개다. 어느 쪽도 죽어 있지 않다.
저장소 자신도 이것을 문제로 적어 두었다. docs/reviews/2026-08-14-jpa-module-code-review.md:119 가 트랜잭션 권위와 애플리케이션 계약이 두 벌이라는 것을 P1 로 매기고 PolicyTransactionPort 단일 facade 로 통합하자고 적는다.
동작이 틀리는 것은 아니다. 두 스택이 각각 돈다. 비용은 다음에 고치는 사람이 어느 쪽을 고쳐야 하는지 주석만으로는 알 수 없다는 데 있다.
검증 환경
OpenJDK : 21.0.12 확인 방식 : 자동설정 주석 인용, implements PolicyTransactionPort 검색을 없는 이름 자기시험과 대조 이름과 함께 계수, 구현체의 선언과 자바독 인용, 자동설정이 만드는 빈 목록 전수, 네 이름의 main 참조 파일 수 계수와 그중 하나의 파일 목록, 저장소 문서가 같은 것을 어떻게 적었는지 전수 검색 소스 수정 : x
재현 조건
- 자동설정에서 정본 경계를 지목하는 주석을 인용한다.
- 그 포트를 구현한다고 선언한 클래스를 찾고, 없는 이름과 대조 이름으로 같은 검색을 함께 건다.
- 구현체의 선언과 클래스 자바독을 인용한다.
- 자동설정이 만드는 빈을 전부 나열한다.
- 두 스택의 이름 넷을 main 참조 파일 수로 센다.
- 저장소 문서에서 그 포트 이름이 나오는 자리를 전부 찾는다.
본문
JpaTransactionAutoConfiguration 은 JPA 트랜잭션 스택을 조립한다. 그 파일의 주석이 정본 경계가 어디인지 적어 둔다.
주석이 지목한 경계와 실제 구현 관계
:::evidence key="analysis-finding-a05-f010" alt="저장소 루트에서 돌린 정적 검색 출력 84줄. 먼저 JpaTransactionAutoConfiguration 2855번 줄이 실리는데 37번 줄 주석이 PolicyTransactionPort.inTransaction 을 정본 경계로 지목하고 아래의 재시도 코디네이터를 함께 설명한다. 이어서 PolicyTransactionPort 를 구현한다고 선언한 클래스가 SpringTransactionPort 31번 줄 하나로 나오고, 없는 이름으로 건 자기시험이 0 개, 대조로 센 implements TransactionPort 가 21 개다. 그 구현체의 선언과 자바독이 2440번 줄로 실리는데 모드별로 미리 만든 트랜잭션 템플릿을 쓰고 모두 READ_COMMITTED 로 고정한다고 적는다. 그 아래에 자동설정이 만드는 빈들이 나오는데 CommitFailureClassifier 와 SpringJpaTransactionExecutor 둘과 FullTransactionRetryCoordinator 둘과 RetrySleeper 이고, 기본 최대 재시도 경과 시간이 30 초로 선언돼 있다. 두 스택을 참조하는 main 파일 수는 PolicyTransactionPort 와 FullTransactionRetryCoordinator 와 SpringJpaTransactionExecutor 와 SpringTransactionPort 가 각각 4 개이고, PolicyTransactionPort 를 참조하는 네 파일이 이름으로 나열된다. 마지막으로 문서 쪽 서술이 나오는데 코드 리뷰 문서가 두 벌의 트랜잭션 계약이 공존한다는 것을 P1 로 적고 PolicyTransactionPort 를 단일 facade 로 통합하자고 적는다." caption="자동설정 주석이 지목한 정본 경계 · 그 포트를 구현한 클래스 하나와 자기시험과 대조 · 구현체의 선언과 자바독 · 자동설정이 만드는 빈 목록 · 두 스택의 main 참조 수 · 코드 리뷰 문서가 같은 것을 P1 로 적은 자리 — 84줄 · exit 0" zoom="true"
:::
:37 주석은 PolicyTransactionPort.inTransaction(TransactionRequest, Supplier) 를 지목하고, 같은 문장이 아래의 재시도 코디네이터를 함께 설명한다.
그 포트를 구현한 클래스는 하나다
implements PolicyTransactionPort 를 가진 파일은 SpringTransactionPort:31 하나다. 없는 이름으로 같은 검색식을 걸면 0 이 나오므로, 그 1 이 검색 운은 아닌지 확인하려고 implements TransactionPort 를 세면 21 이 나온다.
SpringTransactionPort 는 adapter/outbound/persistence-jpa 에 있고 @Component 다. 자바독 :26~:28 은 모드별로 미리 만든 TransactionTemplate 을 쓰고 모두 READ_COMMITTED 로 고정한다고 적는다. 미리 만드는 이유는 가변 템플릿 경합 때문이라고 README 를 가리킨다.
재시도는 그 자바독 어디에도 없다.
자동설정이 만드는 것은 다른 스택이다
빈 목록은 여섯이다. :61 의 CommitFailureClassifier, :66 과 :82 의 SpringJpaTransactionExecutor, :99 와 :107 의 FullTransactionRetryCoordinator, :114 의 RetrySleeper 다.
포트 구현을 만드는 빈이 없다. SpringTransactionPort 는 @Component 로 스캔되어 들어온다.
그래서 :37 주석은 한 문장 안에서 두 계통을 잇는다. 앞이 가리키는 포트는 어댑터 쪽이 구현하고, 뒤가 설명하는 코디네이터는 같은 파일이 만드는 실행기와 짝이다.
두 스택 다 살아 있다
PolicyTransactionPort 와 FullTransactionRetryCoordinator 와 SpringJpaTransactionExecutor 와 SpringTransactionPort 의 main 참조 파일 수가 모두 4 개다.
PolicyTransactionPort 를 참조하는 넷은 그 인터페이스 자신과 SpringPolicyTransactionPort 와 SpringTransactionPort 와 JpaTransactionAutoConfiguration 이다.
어느 쪽에도 폐기 표시가 없다.
저장소 자신이 이미 적어 둔 것
docs/reviews/2026-08-14-jpa-module-code-review.md:31 은 애플리케이션이 이미 쓰는 포트와 새 JPA 실행기·AOP 계약이 두 벌로 존재한다고 적는다.
:119 는 그것을 JPA-007 로 번호 매겨 P1 · High 로 두고 PolicyTransactionPort 단일 facade 로 통합하자고 적는다. :427 은 템플릿의 정본 경계를 그 포트의 inTransaction 으로 삼자고 적는다.
즉 이 상태는 발견되지 않은 것이 아니라 결정이 내려지지 않은 것이다.
원문과 갈리는 자리
원문은 문서와 소스 주석이 코디네이터가 포트를 구현한다고 설명하는데 실제 구현체는 다른 클래스라고 적었다. 실제 주석은 그보다 조금 다르다. :37 은 코디네이터가 그 포트를 구현한다고 적지 않고, 정본 경계로 포트를 지목한 뒤 아래 코디네이터를 이어서 설명한다. 두 계통이 한 문장에 붙어 있어서 읽는 사람이 그렇게 이해하게 된다.
원문이 적지 않은 것은 저장소가 이미 이 중복을 JPA-007 로 기록하고 통합 방향까지 정해 두었다는 것이다.
확인하지 못한 것
실제 요청이 두 계통 중 어느 쪽을 더 자주 지나는지 세지 않았다.
그 문장이 쓰였을 때는 맞았는지 커밋을 되짚지 않았다.
JPA-007 이 어느 쪽으로 결정됐는지 추적하지 않았다.
JPA-007 이 어느 쪽으로 결정됐는지 추적하지 않았다.