docs(clean-architecture-backend-template): 제1부가 채택한 것만 글감으로 남기고 다시 고른다
글감 1,001개 중 제1부(§3~§11) 앵커를 하나라도 가진 것은 112개뿐이었다. 나머지 889개는
제2부 모듈 분석 65편의 절 제목에서 나온 것이고, 그것이 재판정이 필요했던 이유다.
주제 44 → 16 (43개가 독자 질문 없이 있었다. 지금은 전부 있다)
글감 1,001 → 123 (제1부 앵커 112 + 제1부가 채택했는데 비어 있던 자리 11)
후보 965 → 1,088 · PENDING 905 → 0
error 3,042 → 0
내려온 889개는 후보 대장에 KEEP_IN_SSOT 로 남는다 — 버린 것이 아니라 분석에 남기고 독립
기록으로 만들지 않기로 한 것이다. 그 글감을 받치던 기록 파일 828개는 지웠다. 계약이 정본이고,
파일이 남아 있다는 이유로 계약에서 뺀 주제가 되살아나면 안 된다. 이력에는 그대로 있다 —
git checkout a0ca2bb -- <경로>.
제1부가 채택했는데 글감이 없던 자리 열하나를 채웠다: mongo high-water mark 가 재전달 이벤트를
삼킨 P1, admin plane 이 가드만 켜고 서비스는 켜지 않은 것과 그 짝인 결정, 실패 어휘 세 층과
SQLState 매트릭스 병합 규칙, 부하 아래에서만 새는 admission 경계, 발행 증거와 완료 판정의
분리, keyset·JSONB 결정 둘.
Concept 17개에 basis-version 을 채우고, 계약 제목과 기록 제목이 갈라져 있던 23건을 기록 쪽에
맞췄다. candidateScope 에 excludedAnchorPattern 을 적어 제2부 앵커만 가진 글감이 다시 올라올
수 없게 한다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a0ca2bb72a
commit
1f04117bbf
-114
@@ -1,114 +0,0 @@
|
||||
---
|
||||
kind: CONCEPT
|
||||
slug: when-conditions-are-evaluated
|
||||
title: 조건부 빈의 평가 시점 — 파싱 시점과 등록 시점
|
||||
topic: assembly-ownership
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: concept:when-conditions-are-evaluated
|
||||
evidenceCapturedOn: 2026-09-01
|
||||
assets:
|
||||
- key: when-conditions-are-evaluated
|
||||
file: ../../../final/evidence/rendered/when-conditions-are-evaluated.svg
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/when-conditions-are-evaluated.txt
|
||||
source:
|
||||
- 원본 분석 절은 final/document.md#a05 §14.4 이다.
|
||||
---
|
||||
|
||||
# 조건부 빈의 평가 시점 — 파싱 시점과 등록 시점
|
||||
|
||||
같은 `@ConditionalOnBean`이라도 그 클래스가 자동설정으로 등록되는지 `@Import`로 들어오는지에 따라 평가 시점이 다르다. 그 차이가 조건을 항상 거짓으로 만들 수 있다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **ConditionalOnBean(DataSource)이 클래스 파싱 시점에 평가되어 여덟 빈이 사라졌다**
|
||||
이 개념이 실제로 문제가 된 사례다.
|
||||
- **ConditionalOnBean은 조건이 만족될 수 있는지까지 확인해야 한다**
|
||||
이 개념에서 나온 확인 규칙이다.
|
||||
- **ConditionalOnBean 사슬의 실제 평가 순서를 확인하지 않았다**
|
||||
현재 리비전에 대한 미해결 질문이다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
`@ConditionalOnBean`은 그 클래스가 **언제 평가되는가**에 따라 답이 달라진다. `@AutoConfiguration`으로 등록되면 다른 자동설정 이후에 평가되지만, plain `@Configuration`이 `@Import`로 들어오면 **클래스가 파싱되는 동안 — 대상 빈 정의가 존재하기 전에** 평가된다.
|
||||
|
||||
## 등록 방식이 평가 시점을 정한다
|
||||
|
||||
:::evidence key="when-conditions-are-evaluated" alt="분석 문서 final/document.md#a05 에서 이 기록의 근거 절을 그대로 잘라낸 16줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md#a05 발췌 — 16줄" zoom="true"
|
||||
:::
|
||||
|
||||
## 이 저장소가 그 함정을 밟았다
|
||||
|
||||
여덟 빈이 조용히 사라졌다.
|
||||
|
||||
## 남은 회피 방법 둘
|
||||
|
||||
검증기를 주입받지 않고 직접 생성하기, 그리고 조건을 루트로 올리기.
|
||||
|
||||
:::note
|
||||
|
||||
현재 리비전의 각 조건부 빈이 어느 시점에 평가되는지는 ConditionEvaluationReport로 확인하지 않았다
|
||||
|
||||
:::
|
||||
|
||||
## 두 시점
|
||||
|
||||
`@ConditionalOnBean`은 "이 타입의 빈 정의가 이미 등록되어 있는가"를 묻는다. 그 질문의 답은 언제 묻느냐에 달라진다.
|
||||
|
||||
| 클래스가 들어오는 경로 | 조건이 평가되는 시점 |
|
||||
|---|---|
|
||||
| 자동설정 (`.imports`) | 자동설정 순서에 따라 등록 단계에서 |
|
||||
| `@Import` | 그것을 import 하는 클래스가 파싱될 때 |
|
||||
| 컴포넌트 스캔 | 스캔 단계에서 |
|
||||
|
||||
`@Import`로 들어오는 클래스가 문제다. 부모 설정이 파싱되는 시점에 자식 클래스의 클래스 수준 조건이 함께 평가되는데, 그 시점에는 자동설정이 만들 빈 정의가 아직 존재하지 않는다.
|
||||
|
||||
## 이 저장소가 겪은 형태
|
||||
|
||||
```java
|
||||
/**
|
||||
* <p>This class used to carry {@code @ConditionalOnBean(DataSource.class)}. It is imported by
|
||||
* {@code PersistenceJpaRootAutoConfiguration} rather than auto-configured, so that condition was
|
||||
* evaluated while the class was parsed — before the datasource bean definition existed — and was
|
||||
* therefore false in every real deployment. All eight beans below silently disappeared, and nothing
|
||||
* reported it because nothing depended on any of them. It surfaced only when the datasource
|
||||
* validator was finally wired to a caller and the JPA Compose lane answered "No qualifying bean".
|
||||
*/
|
||||
```
|
||||
|
||||
두 문장이 이 개념의 핵심이다. 조건이 모든 실제 배포에서 거짓이었다는 것, 그리고 아무것도 그것을 보고하지 않았다는 것.
|
||||
|
||||
보고되지 않은 이유가 특히 중요하다. 사라진 여덟 빈에 의존하는 것이 없었기 때문이다. 의존이 있었다면 미충족 의존성으로 시끄럽게 실패했을 것이다.
|
||||
|
||||
## 해결 방향은 순서가 아니라 제거였다
|
||||
|
||||
```java
|
||||
/**
|
||||
* <p>The condition is removed rather than reordered: this class is reached only through the JPA
|
||||
* root, which already carries the master switch, so "is there a datasource" has been answered yes
|
||||
* by the time it is parsed. A bean here that needs one takes it as a parameter, and a missing
|
||||
* datasource with the switch on is then a loud failure — which is the outcome that was wanted,
|
||||
* rather than the layer vanishing.
|
||||
*/
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
조건을 옮기거나 순서를 바꾸는 대신, 그 조건이 이미 답해진 지점으로 도달 경로를 제한하고 조건 자체를 없앴다. 그리고 필요한 의존은 파라미터로 받게 해서, 없을 때 조용히 사라지는 대신 시끄럽게 실패하게 만들었다.
|
||||
|
||||
:::
|
||||
|
||||
## 확인 방법
|
||||
|
||||
정적으로는 두 가지를 본다.
|
||||
|
||||
1. 그 클래스가 `.imports`에 있는가, 아니면 다른 클래스가 `@Import` 하는가
|
||||
2. 조건이 요구하는 빈을 누가 언제 등록하는가
|
||||
|
||||
런타임으로는 `debug=true` 부팅의 조건 평가 리포트가 답한다. `Negative matches` 항목의 사유 문자열이 "빈 정의 없음"인지 "타입 자체가 없음"인지를 구별해 준다.
|
||||
|
||||
<!-- body:end -->
|
||||
Reference in New Issue
Block a user