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:
DongHyeonka
2026-09-07 15:02:25 +09:00
co-authored by Claude Opus 5
parent a0ca2bb72a
commit 1f04117bbf
851 changed files with 5498 additions and 90638 deletions
@@ -1,55 +0,0 @@
---
kind: REFERENCE
slug: a-classifier-sees-only-what-the-engine-kept
title: 분류기는 엔진이 남긴 것만 볼 수 있다
topic: http-failure-classification
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: reference:a-classifier-sees-only-what-the-engine-kept
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
---
# 분류기는 엔진이 남긴 것만 볼 수 있다
## 목적
예외를 자기 범주로 번역하는 계층이 무엇을 분류할 수 있는지에 상한이 있다는 것을 잊고, 분기 순서를 고쳐서 해결하려 드는 것을 막는다.
## 규칙
1. 분류 가능한 것의 상한은 사슬에 남은 것이다
번역 계층은 그 아래 엔진이 버리지 않고 남긴 원인만 볼 수 있다. 사슬에 없는 원인은 어떤 순회 순서로도 분류되지 않는다.
2. 이름 하나가 여러 엔드포인트로 풀리면 마지막 것만 남을 수 있다
Apache HttpClient 5 의 다중 주소 연결 루프는 마지막이 아닌 주소의 실패를 삼킨다. 호스트명이 여러 주소로 풀리면 호출자에게 도달하는 것은 마지막 주소의 오류뿐이다.
3. 분류를 고치기 전에 입력을 먼저 확인한다
잘못된 분류를 보면 분기 순서부터 의심하게 된다. 순서를 바꾸기 전에 사슬을 출력해서 원인이 실제로 거기 있는지 본다.
4. 테스트는 주소를 고정한다
실패 분류를 검증하는 테스트가 호스트명을 쓰면, 그 테스트는 이름 해석이라는 통제되지 않은 변수를 함께 검증한다. 루프백 주소를 직접 쓰거나 주소 패밀리를 고정한다.
## 적용 조건
이름 하나가 여러 엔드포인트로 풀리는 모든 클라이언트 : DNS A 와 AAAA, 서비스 디스커버리, 다중 브로커 부트스트랩
실패 분류가 재시도 안전성이나 보안 판정으로 이어지는 곳 : 특히 중요
## 예외
모든 주소가 같은 이유로 실패하면 마지막 오류가 대표성을 가지므로 문제가 되지 않는다. 강등은 패밀리별 또는 엔드포인트별 실패 양상이 다를 때만 일어난다.
## 예시
인증서가 신뢰 불가면 두 주소 패밀리 모두 TLS 에서 실패하고, 마지막 오류도 핸드셰이크 예외라 분류가 맞는다.
한 패밀리는 TLS 를 거절하고 다른 패밀리는 연결이 거부되면, 영구 실패가 일시적 연결 실패로 보고된다.
## 관계
- **붉은 테스트를 제품 결함으로 읽은 오진**
이 규칙을 끌어낸 사례다. 사슬을 출력하기 전까지는 분기 순서가 원인으로 보였다.
- **원인 사슬은 가장 구체적인 분류가 이기도록 순회한다**
이 규칙과 짝을 이룬다. 그 규칙은 사슬 안에서의 선택을, 이 규칙은 사슬 자체의 한계를 다룬다.
@@ -1,58 +0,0 @@
---
kind: REFERENCE
slug: retryability-needs-both-idempotency-and-category
title: 재시도 가능성은 멱등성과 실패 범주를 함께 봐야 정해진다
topic: http-failure-classification
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: reference:retryability-needs-both-idempotency-and-category
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
---
# 재시도 가능성은 멱등성과 실패 범주를 함께 봐야 정해진다
## 목적
한 축만 보고 재시도를 정해서, 비멱등 요청을 반복하거나 영구 실패를 무한히 되풀이하는 것을 막는다.
## 규칙
1. 두 입력의 곱이다
범주가 재시도 가능하고 동시에 요청이 재시도 안전할 때만 재시도한다. 범주만 보면 비멱등 요청을 재시도하고, 멱등성만 보면 영구 실패를 반복한다.
2. 전송되지 않았다는 증거는 멱등성 요구를 완화한다
아무것도 서버에 닿지 않았음이 증명되면 그 시도는 없던 일이므로 멱등성을 묻지 않아도 된다. 다만 그 증거는 증명일 때만 쓴다.
3. 증거는 승격하지 않는다
일반적인 엔진 입출력 실패를 전송되지 않음으로 올리지 않는다. 모르는 것은 모르는 채로 둔다. 추측해서 올린 판정이 중복 결제를 만든다.
4. 응답이 전달되기 시작했으면 재시도하지 않는다
첫 바이트가 호출자에게 전달된 뒤에는 범주와 무관하게 재시도가 막힌다.
5. 정책의 화이트리스트로 terminal 판정을 되살리지 않는다
화이트리스트는 어떤 범주가 재시도될 수 있는지를 넓히지, 이 실패에 대한 판정을 뒤집지 않는다.
## 적용 조건
HTTP gRPC 메시징 클라이언트의 재시도 결정
멱등성 여부가 요청마다 다른 경로
## 예외
분류기가 terminal 로 표시한 실패는 정책으로 되살릴 수 없다.
## 예시
이 저장소는 전송되지 않음 증거를 별도 축으로 두어 완화를 표현한다. 재시도 컨텍스트에 HTTP 메서드가 없고, 멱등성 키가 전송 여부까지 요구하며, 첫 바이트 전달은 되돌릴 수 없는 래치다.
부분 응답은 안전하게 멱등인 요청에 대해서만 재시도된다. 그 외에는 원격 결과 불명으로 남긴다.
## 관계
- **전송 실패를 단계와 범주 두 축으로 모델링한다**
이 규칙의 입력을 만드는 모델이다.
- **재시도 안전성은 증거에 기반해 판정한다**
이 규칙을 채택한 프로젝트 결정이다.
@@ -1,62 +0,0 @@
---
kind: REFERENCE
slug: verify-runtime-shape-at-runtime
title: 런타임의 모양에 대한 주장은 런타임에서 확인한다
topic: http-failure-classification
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: reference:verify-runtime-shape-at-runtime
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
---
# 런타임의 모양에 대한 주장은 런타임에서 확인한다
## 목적
코드를 읽어 얻은 추론이 관측으로 굳어지는 것을 막는다. 특히 실패하는 테스트를 결함의 증거로 읽는 순간을 막는다.
## 규칙
1. 벤더 라이브러리의 런타임 동작은 읽어서 알 수 없다
이 라이브러리는 예외를 이렇게 감쌀 것이다, 이 게이트가 이 값을 읽을 것이다, 이 경로가 프로덕션 기본값이다. 이런 문장은 전부 가설이며 확인 전까지 판정의 근거가 될 수 없다.
2. 붉은 테스트는 조사의 시작점이지 결론이 아니다
실패하는 테스트는 무언가 어긋났다는 사실만 말한다. 무엇이 어긋났는지는 별도 측정이다. 테스트 이름이 가리키는 대상이 곧 원인인 경우가 오히려 드물다.
3. 대조는 변수 하나만 바꾼다
같은 픽스처에서 호스트 문자열만 바꾸는 식의 대조가 가장 값싸고 결정적이다. 두 가지를 함께 바꾸면 결과를 해석할 수 없다.
4. 소스를 고치지 않고 확인할 방법이 대개 있다
테스트 런타임 클래스패스를 얻어 jshell 로 재현하고, 리플렉션으로 private 메서드를 부르고, 조건만 바꿔 두 번 돌린다. 애플리케이션 소스를 건드리지 않고도 관측이 된다.
5. 확인 비용은 분 단위다
이 프로젝트에서 판정 번복 한 건과 자기 교정 세 건이 전부 이 형태였고, 넷 다 측정 하나로 갈렸다.
## 적용 조건
프레임워크 드라이버 클라이언트 라이브러리의 런타임 동작에 의존하는 모든 판정
실패하는 테스트를 근거로 결함을 보고하려는 순간
관측 없이 심각도를 P1 으로 올리려는 순간
## 예외
소스가 저장소 안에 있고 그 경로가 테스트로 고정돼 있으면 읽기로 충분하다. 이 규칙은 벤더 코드의 동작에 대한 것이다.
## 예시
분기 순서를 읽고 예외 사슬의 모양을 단정했다. 사슬을 출력하니 달랐다.
레지스트리에 검사가 없다고 단정했다. 빌드 파일의 주석이 가리키는 세 곳을 따라가니 있었다.
전역 승인 게이트가 임계값을 읽을 것이라고 가정했다. 그 가드는 해당 필드를 읽지 않았다.
## 관계
- **붉은 테스트를 제품 결함으로 읽은 오진**
이 규칙을 끌어낸 사례다. 네 건 중 가장 비쌌던 것이다.
- **분류기는 엔진이 남긴 것만 볼 수 있다**
같은 사례에서 나온 짝 규칙이다. 이쪽은 방법을, 저쪽은 대상을 다룬다.
@@ -1,61 +0,0 @@
---
kind: REFERENCE
slug: walk-the-cause-chain-most-specific-wins
title: 원인 사슬은 가장 구체적인 분류가 이기도록 순회한다
topic: http-failure-classification
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: reference:walk-the-cause-chain-most-specific-wins
verifiedOn: # 이 기록은 이번 회차에 실행 확인을 하지 않았다
note: 이 규칙을 처음 끌어낸 사례는 나중에 철회되었다. httpclient 의 mTLS 테스트 3건이 실패한 원인은 분기 순서가 아니라 테스트 픽스처의 호스트명이었고, 그 저장소의 분류기는 이 실패의 사례가 아니다. 규칙 자체는 다른 근거로 유효하다.
---
# 원인 사슬은 가장 구체적인 분류가 이기도록 순회한다
## 목적
라이브러리가 구체적 원인을 일반적 예외로 감쌌을 때, 바깥 타입만 보고 분류해서 정확한 판정을 잃는 것을 막는다.
## 규칙
1. 바깥 타입 하나로 분류하지 않는다
Spring 이 엔진 예외를 감싸고 드라이버가 자기 예외를 다시 감싼다. 바깥 타입만 매칭하면 증명 가능한 판정이 모호한 판정으로 내려간다.
2. 기준은 순서가 아니라 구체성이다
이 사슬에서 가장 구체적인 분류가 이기는가를 묻는다. 구현은 두 가지다. 구체적 분기를 앞으로 옮기거나, 사슬 전체를 훑어 최선의 매치를 고른다.
3. 사이클 안전을 확보한다
이미 본 예외를 IdentityHashMap 으로 표시하거나 최대 깊이를 둔다. 순환 참조는 실제로 나타난다.
4. 벤더별 곁가지를 따라간다
getCause 만으로는 부족하다. SQLException 의 getNextException 처럼 벤더가 따로 두는 연결 고리가 있다.
5. 이 규칙이 실패의 원인이 아닐 수도 있다
잘못된 분류를 봤을 때 순서를 의심하기 전에, 그 원인이 사슬에 실제로 있는지 먼저 확인한다.
## 적용 조건
드라이버나 클라이언트 예외를 자기 범주로 번역하는 모든 분류기
특히 전송 계층 : 감싸기가 흔하다
## 예외
바깥 예외가 실제로 더 구체적인 경우가 있다. 그때는 순서가 아니라 우선순위 표가 필요하고, 그 표를 테스트로 고정해야 한다.
## 예시
이 저장소의 mongo 실패 추출기와 bulk 실패 추출기는 IdentityHashMap 으로 방문 표시를 두고 사슬을 훑는다.
jpa 의 SQL 상태 해석기는 getNextException 을 따라간다.
httpclient 의 분류기는 사슬 전체를 훑는다. 주석이 이유를 적는다. 감싸인 ConnectException 도 요청이 전송되지 않았음을 증명하므로, 바깥 타입만 매칭하면 증명 가능한 NOT_SENT 를 모호한 SENT_NO_RESPONSE 로 낮추게 된다.
## 관계
- **분류기는 엔진이 남긴 것만 볼 수 있다**
이 규칙의 상한을 정한다. 사슬에 없는 원인은 어떤 순회로도 분류되지 않는다.
- **붉은 테스트를 제품 결함으로 읽은 오진**
이 규칙을 잘못 적용한 기록이다.