docs(TechLog): 글감 56개를 기록으로 쓴다
주제 13개 · Case 28 · Concept 5 · Reference 15 · Question 4 · Decision 4. 계약의 노드마다 종류가 요구하는 칸을 채우고, 본문이 있는 두 종류에는 SSOT 가 이미 그려 둔 도식 셋(value-boundaries · decision-path-404 · topic-variant-model)을 tech-log-studio/ 로 옮겨 붙였다. 새로 그린 그림은 없다. 검사 셋 전부 통과한다. check_body.mjs 56 편 중 본문이 있는 33 편 PASS check_prose.mjs 56 편 error 0 check_evidence.mjs --repo 포함 문제 없음 verify-tech-log-tree.py 프로젝트 5 · error 0 · warn 0 인용한 코드블록은 전부 SSOT 에서 찾아 대조했다. check_evidence.mjs 가 본문의 각 줄과 source 앵커와 계약 제목을 다시 확인한다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6955611439
commit
f6c825e858
+89
@@ -0,0 +1,89 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: the-composition-root-had-no-test
|
||||
title: 합성 루트에 테스트가 없어 공개 사이트 전체가 오류 화면이었다
|
||||
topic: seams-no-test-crosses
|
||||
topicName: 테스트가 지나지 않는 이음매
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
lastVerifiedOn: 2026-09-04
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
- final/document.md#§7.4
|
||||
- final/document.md#§7.3
|
||||
---
|
||||
|
||||
# 합성 루트에 테스트가 없어 공개 사이트 전체가 오류 화면이었다
|
||||
|
||||
공개 사이트 전체가 오류 화면이었다. 로그아웃 상태 방문자 — 공개 사이트의 전체 독자 — 가 브라우저에서 요청을 한 건도 내보내지 못했다. 세 결함이 겹쳐 있었고 각각이 다음 것을 가렸다. 게이트웨이 테스트도 화면 테스트도 이 이음매를 지나지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **이음매마다 그 이음매를 실제로 지나는 검사를 하나씩 둔다**
|
||||
합성 루트가 그 목록의 한 줄이다.
|
||||
- **매퍼가 null 을 돌려주고 호출부가 걸러 내, 기록이 조용히 사라졌다**
|
||||
스텁 때문에 보이지 않던 다른 매핑 결함이다.
|
||||
- **한 경계를 고쳤으면 값의 여정 끝에서 확인한다**
|
||||
이 사건이 그 규칙의 근거 하나다.
|
||||
|
||||
## 문제
|
||||
|
||||
공개 소스가 HTTP 어댑터로 바뀐 뒤 로그아웃 상태 방문자가 요청을 하나도 내보내지 못했다. 화면은 전부 오류였다.
|
||||
|
||||
이 경로는 그 주까지 브라우저에서 한 번도 돌지 않았다. 이전에는 정적 소스를 쓰고 있었다.
|
||||
|
||||
## 결론
|
||||
|
||||
세 결함이 겹쳐 있었고 앞의 것이 뒤의 것을 가리고 있었다.
|
||||
|
||||
1 : credential 을 붙이는 함수가 Studio 헬퍼에 먼저 묻고, 그 헬퍼가 자기 것이 아닌 프로파일에 null 을 준다. 아래 폴백이 세션을 읽고 인증되지 않은 것을 거절한다. 공개 읽기는 ANONYMOUS 프로파일이라 그 폴백으로 떨어졌다
|
||||
2 : 봉투 오류 생성기가 오류 코드를 Studio enum 에 고정해 세 표면이 공유했다. 공개와 관리는 각자 자기 계약에 enum 을 선언하므로 그들이 돌려준 모든 오류가 검증에 실패해 계약 위반으로 도착했다
|
||||
3 : not-found 경로가 봉투에 없는 필드를 읽고 있었다
|
||||
|
||||
엄격한 enum 을 잘못된 표면의 계약에 대고 검사해도 여전히 엄격해 보인다. 그래서 어떤 게이트도 잡지 못했다.
|
||||
|
||||
회귀 테스트가 실제 런타임 어댑터를 배포된 백엔드의 실제 404 본문에 대고 조립한다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
tech-log-frontend : 03986da · 7600711
|
||||
공개 소스 : 정적 픽스처에서 HTTP 어댑터로 전환한 직후
|
||||
확인 방식 : 실제 어댑터를 배포된 백엔드의 실제 404 본문에 대고 조립하는 회귀 테스트
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 로그아웃 상태로 공개 사이트를 연다
|
||||
2. 네트워크 탭에서 요청이 나가는지 본다
|
||||
3. 나가지 않으면 credential 을 붙이는 경로에서 어느 프로파일이 어떤 판정을 받는지 따라간다
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 세 결함이 서로를 가렸다
|
||||
|
||||
첫 번째만 고쳤을 때 요청이 나가기 시작했고, 그러자 두 번째가 드러났다. 두 번째를 고치니 세 번째가 나왔다.
|
||||
|
||||
`attachCredentials` 가 Studio 헬퍼에 먼저 묻는데, 그 헬퍼는 자기 것이 아닌 프로파일에 `null` 을 돌려준다. 그 아래 폴백이 세션을 읽고 인증되지 않은 것을 거절한다. 공개 읽기는 ANONYMOUS 프로파일을 선언하므로 그 폴백에 떨어졌다.
|
||||
|
||||
요청이 흐르자 두 번째가 나왔다. `envelopeError()` 가 `ApiError.code` 를 Studio enum 에 고정해 세 표면이 공유했다. 공개와 관리는 각자 자기 계약에 enum 을 선언하므로 그들이 돌려준 모든 오류가 검증에 실패해 `CONTRACT_VIOLATION` 으로 도착했다.
|
||||
|
||||
세 번째는 not-found 경로가 봉투에 없는 `status` 를 읽고 있던 것이다.
|
||||
|
||||
## 왜 어떤 게이트도 잡지 못했나
|
||||
|
||||
> 엄격한 enum 을 잘못된 표면의 계약에 대고 검사해도 여전히 엄격해 보인다.
|
||||
|
||||
검증은 돌고 있었고 통과하고 있었다. 다만 비교 대상이 틀렸다.
|
||||
|
||||
## 스텁이 이음매를 덮지 않는다
|
||||
|
||||
> 이 결함은 공개 소스가 HTTP 가 된 뒤에야 나타날 수 있었다. 이번 주까지 그 경로는 브라우저에서 한 번도 돌지 않았다. **스위트가 잡지 못한 이유는 게이트웨이와 화면을 검사할 뿐 합성 루트의 credential 결정은 검사하지 않기 때문이다 — 그 이음매에는 테스트가 없고, 이것이 그 대가다.**
|
||||
|
||||
같은 모양이 HTTP 매퍼에서도 났다. 게시한 질문의 공개 상세가 「요청을 처리하지 못했습니다」만 띄웠는데, 화면 테스트가 정적 픽스처 어댑터를 쓰므로 계약 모양을 한 번도 통과시키지 않았다. 계약 모양 그대로의 응답을 진짜 게이트웨이에 넣는 테스트를 넣으니 되돌려 보면 운영에서 난 것과 같은 오류로 실패한다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
이 회귀 테스트가 덮는 것은 공개 읽기 프로파일이다. 다른 프로파일의 합성 루트에도 같은 구멍이 있는지는 세지 않았다.
|
||||
|
||||
<!-- body:end -->
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: the-generator-dropped-four-contract-fields
|
||||
title: 생성기가 계약 필드 넷을 조용히 빠뜨렸다 — 파생 스펙에 남은 YAML alias 34곳
|
||||
topic: seams-no-test-crosses
|
||||
topicName: 테스트가 지나지 않는 이음매
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
lastVerifiedOn: 2026-09-04
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
- final/document.md#§7.6
|
||||
---
|
||||
|
||||
# 생성기가 계약 필드 넷을 조용히 빠뜨렸다 — 파생 스펙에 남은 YAML alias 34곳
|
||||
|
||||
계약에 있는 필드 넷이 생성된 모델에서 사라져 있었다. 파생 단계의 YAML alias 때문에 파서가 스키마 15개를 거절했고, 거절당한 스키마들은 전부 타입을 명시하고 있어 계약 결함처럼 보이지 않았다. 검증을 끄면 생성이 성공했고, 그렇게 만든 모델에 그 넷이 없었다. 컴파일은 통과한다 — 아직 아무도 그 필드를 안 쓰니까.
|
||||
|
||||
## 관계
|
||||
|
||||
- **이음매마다 그 이음매를 실제로 지나는 검사를 하나씩 둔다**
|
||||
생성기가 그 목록의 한 줄이다.
|
||||
- **계약과 구현은 서버와 화면 양쪽에서 전수 대조한다**
|
||||
이 부류를 계약 쪽에서 막는 짝이 되는 기준이다.
|
||||
- **결정에는 상세 화면이 없어 목록 항목이 문서 전체를 실어야 했다**
|
||||
계약의 칸이 화면까지 오지 못한 다른 사건이다.
|
||||
|
||||
## 문제
|
||||
|
||||
파생 스펙을 파서에 넣으면 스키마 15개를 「is not of type `object`」로 거절했다. 그 스키마들은 전부 `type: object` 를 명시하고 있어서 계약 결함처럼 보이지 않았다.
|
||||
|
||||
`validateSpec` 을 끄면 생성이 성공한다. 그렇게 만든 모델을 컴파일하면 통과한다.
|
||||
|
||||
## 결론
|
||||
|
||||
거절의 원인은 계약이 아니라 파생 단계였다. 변환들이 같은 `Map` 인스턴스를 여러 property 에 재사용했고 snakeyaml 이 그 지점을 anchor 와 alias 로 덤프했다. 파생 스펙에 alias 가 34곳 있었다.
|
||||
|
||||
검증을 끄고 만든 모델에서 사라진 필드 넷 :
|
||||
LatestEntry.publishedAt
|
||||
ProjectListItem.updatedAt
|
||||
SearchResultItem.matchedFields
|
||||
ReleaseListItem.changeTypes
|
||||
|
||||
컴파일이 통과한 이유는 아직 그 필드를 쓰는 코드가 없어서다.
|
||||
|
||||
덤프 직전 deep copy 로 노드 identity 를 끊어 alias 를 원천 차단하고, 남으면 빌드가 실패하도록 fail-closed 게이트를 뒀다. `validateSpec` 은 다시 켰다. 생성 모델 대조는 schema 이름에서 property 단위로 강화했다 — 이번 누락을 그 게이트가 통과시켰기 때문이다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
tech-log-backend : 365560e
|
||||
파생 : snakeyaml 덤프 · swagger-parser
|
||||
게이트 : verifyPublicGeneratedModels — schema 62개 · property 250개
|
||||
확인 방식 : 파생 스펙에서 alias 를 세고, 생성된 모델의 property 를 계약과 대조
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 파생 스펙에서 anchor 와 alias 를 찾는다
|
||||
2. `validateSpec` 을 켜고 생성한다 — 그 스키마들이 거절된다
|
||||
3. 끄고 생성한 뒤 모델의 property 를 계약과 하나씩 맞춘다
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 계약 결함처럼 보이지 않았다
|
||||
|
||||
파서가 거절한 스키마 15개는 전부 `type: object` 를 명시하고 있었다. 메시지는 「is not of type `object`」였다.
|
||||
|
||||
원인은 그 스키마가 아니라 파생 스펙의 표현이었다. 변환들이 같은 `Map` 인스턴스를 여러 property 에 재사용했고, snakeyaml 은 같은 인스턴스가 두 번 나오면 두 번째를 alias 로 덤프한다. 그 지점이 34곳이었다.
|
||||
|
||||
## 검증을 끄면 생성이 성공한다
|
||||
|
||||
`validateSpec` 을 끄고 생성하면 모델이 만들어진다. 다만 거절되던 스키마의 일부 필드가 빠진 채로 만들어진다.
|
||||
|
||||
빠진 것은 넷이었다 — `LatestEntry.publishedAt`, `ProjectListItem.updatedAt`, `SearchResultItem.matchedFields`, `ReleaseListItem.changeTypes`.
|
||||
|
||||
컴파일은 통과한다. 아직 아무도 그 필드를 안 쓰기 때문이다.
|
||||
|
||||
## 두 가지로 막았다
|
||||
|
||||
덤프 직전에 deep copy 로 노드 identity 를 끊었다. 같은 인스턴스가 두 번 나오지 않으면 alias 가 생기지 않는다. 그래도 남으면 빌드가 실패하도록 fail-closed 게이트를 뒀고, `validateSpec` 은 다시 켰다.
|
||||
|
||||
생성 모델 대조도 바꿨다. schema 이름만 세던 것을 property 단위로 강화했다. 이번 누락을 이름 대조가 통과시켰기 때문이다. 지금은 schema 62개와 property 250개를 센다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
지금 세는 수는 사람이 갱신한다. 계약이 줄어드는 방향의 누락은 이 게이트가 잡지 않는다.
|
||||
|
||||
<!-- body:end -->
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: the-sql-had-never-been-executed
|
||||
title: 그 SQL 은 한 번도 실행된 적이 없었다
|
||||
topic: seams-no-test-crosses
|
||||
topicName: 테스트가 지나지 않는 이음매
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
lastVerifiedOn: 2026-09-04
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
- final/document.md#§7.2
|
||||
---
|
||||
|
||||
# 그 SQL 은 한 번도 실행된 적이 없었다
|
||||
|
||||
작업본 삭제가 500 을 돌려줬다. 참조 검사가 없는 컬럼을 조회하고 있었다. 진짜 문제는 그 SQL 이 한 번도 실행된 적이 없다는 것이었다 — 표준 `check` 는 Testcontainers 를 띄우지 않으므로 persistence SQL 을 한 줄도 돌리지 않고 빌드가 통과한다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **이음매마다 그 이음매를 실제로 지나는 검사를 하나씩 둔다**
|
||||
persistence SQL 이 그 목록의 한 줄이다.
|
||||
- **파드가 두 번 CrashLoopBackOff 로 들어갔다**
|
||||
같은 「지나지 않은 이음매」의 다른 예다.
|
||||
- **삭제를 막는 이유 다섯 가지가 전부 같은 한 문장으로 나온다**
|
||||
이 참조 검사가 지금도 남긴 문제다.
|
||||
|
||||
## 문제
|
||||
|
||||
작업본을 지우려 하면 500 이 났다. 참조 검사가 `public_resource_projection.document_id` 를 조회하는데 그런 컬럼이 없다.
|
||||
|
||||
이 테이블은 하나로 case·question·project·release 를 모두 담기 때문에 종류와 식별자 두 컬럼으로 기록을 가리킨다.
|
||||
|
||||
## 결론
|
||||
|
||||
컬럼 이름 하나가 틀렸고, 그 쿼리가 한 번도 실행된 적이 없었다.
|
||||
|
||||
> 그 쿼리의 여섯 컬럼 중 다섯은 마이그레이션과 대조했다. 이 하나만 가정했고, 그것이 틀렸다.
|
||||
|
||||
표준 `check` 는 Testcontainers 를 띄우지 않는다. persistence SQL 은 한 번도 실행되지 않은 채 빌드가 통과하고, 컴파일도 단위 테스트도 컬럼 이름을 검증하지 못한다.
|
||||
|
||||
삭제 경로 전용 통합 테스트 태스크를 만들고 실패했던 그 쿼리를 포함해 여덟 시나리오를 실제 PostgreSQL 에서 돌린다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
tech-log-backend : 37f474a 이후
|
||||
DB : 실제 PostgreSQL (Testcontainers)
|
||||
빌드 : 표준 check 는 Testcontainers 를 띄우지 않음
|
||||
확인 방식 : 삭제 경로 전용 통합 테스트 태스크에서 여덟 시나리오 실행
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 어댑터의 SQL 에서 컬럼 이름을 하나 틀리게 적는다
|
||||
2. `./gradlew check` 를 돌린다 — 통과한다
|
||||
3. 삭제 경로 통합 테스트 태스크를 돌린다 — 그 쿼리에서 멈춘다
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 없는 컬럼을 조회했다
|
||||
|
||||
`public_resource_projection` 은 한 테이블이 case·question·project·release 를 모두 담는다. 그래서 기록을 가리킬 때 종류와 식별자 두 컬럼을 함께 쓴다. `document_id` 라는 컬럼은 없다.
|
||||
|
||||
참조 검사가 그 이름을 쓰고 있었고, 실행하면 500 이 났다.
|
||||
|
||||
## 다섯은 대조했고 하나는 가정했다
|
||||
|
||||
> 그 쿼리의 여섯 컬럼 중 다섯은 마이그레이션과 대조했다. 이 하나만 가정했고, 그것이 틀렸다.
|
||||
|
||||
## 그 SQL 은 한 번도 실행되지 않았다
|
||||
|
||||
컬럼 이름보다 더 큰 문제는 검사 구조였다. 표준 `check` 는 Testcontainers 를 띄우지 않으므로 persistence SQL 이 한 줄도 실행되지 않은 채 빌드가 통과한다.
|
||||
|
||||
컴파일은 SQL 문자열 안을 보지 않는다. 단위 테스트는 어댑터를 스텁으로 바꾼다. 컬럼 이름이 맞는지 묻는 검사가 어디에도 없었다.
|
||||
|
||||
## 전용 태스크로 여덟 시나리오를 돌린다
|
||||
|
||||
삭제 경로 전용 통합 테스트 태스크를 만들었다. 실패했던 그 쿼리를 포함해 여덟 시나리오를 실제 PostgreSQL 에서 돌린다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
이 태스크가 덮는 것은 삭제 경로다. 표준 `check` 는 여전히 Testcontainers 를 띄우지 않고, 새 어댑터 SQL 이 이 태스크에 등록되는지 보는 검사도 없다.
|
||||
|
||||
<!-- body:end -->
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: two-pods-crashlooped-with-no-test-starting-the-context
|
||||
title: 파드가 두 번 CrashLoopBackOff 로 들어갔다 — 어떤 테스트도 애플리케이션 컨텍스트를 띄우지 않았다
|
||||
topic: seams-no-test-crosses
|
||||
topicName: 테스트가 지나지 않는 이음매
|
||||
project: TechLog
|
||||
status: 게시 전
|
||||
lastVerifiedOn: 2026-09-04
|
||||
sourceRevision: tech-log@2026-09-02
|
||||
source:
|
||||
- final/document.md#§7.1
|
||||
- final/document.md#§6.5
|
||||
- final/document.md#§12.1
|
||||
---
|
||||
|
||||
# 파드가 두 번 CrashLoopBackOff 로 들어갔다 — 어떤 테스트도 애플리케이션 컨텍스트를 띄우지 않았다
|
||||
|
||||
파드가 두 번 CrashLoopBackOff 로 들어갔다. 한 번은 스캔되는 컴포넌트에 생성자가 둘이었고, 한 번은 이 빌드에 없는 Jackson 2 의 타입을 import 했다. 컴파일도 단위 테스트도 실제 PostgreSQL 위에서 도는 통합 테스트 26개도 전부 통과했다. 그중 어느 것도 애플리케이션 컨텍스트를 띄우지 않는다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **이음매마다 그 이음매를 실제로 지나는 검사를 하나씩 둔다**
|
||||
이 사건이 그 목록의 첫 줄이다.
|
||||
- **TypeScript 가 검사를 놓아 주는 네 곳**
|
||||
다른 언어에서 컴파일 통과가 반영의 증거가 아니었던 자매 사건이다.
|
||||
- **그 SQL 은 한 번도 실행된 적이 없었다**
|
||||
같은 「지나지 않은 이음매」의 다른 예다.
|
||||
|
||||
## 문제
|
||||
|
||||
컴포넌트 스캔이 생성자를 고르지 못하면 컨텍스트가 refresh 에 실패한다. 컨텍스트를 띄우는 테스트가 없으면 그 실패는 배포에서 처음 나타난다.
|
||||
|
||||
두 번째 사건은 import 였다. `JdbcProjectRepositoryAdapter` 가 Jackson 2 의 `ObjectMapper` 를 요구했는데 이 빌드는 Jackson 3 이다.
|
||||
|
||||
## 결론
|
||||
|
||||
두 건 다 컨텍스트가 뜰 때 처음 드러났다.
|
||||
|
||||
첫 번째 : 스캔되는 컴포넌트에 생성자 둘, `@Autowired` 없음
|
||||
두 번째 : Jackson 2 `ObjectMapper` 를 요구, 이 빌드는 Jackson 3
|
||||
|
||||
두 번째가 컴파일을 통과한 이유는 Jackson 2 타입이 어떤 전이 의존성을 통해 클래스패스에 남아 있어서다. 잘못된 import 가 정상적으로 해석된다.
|
||||
|
||||
첫 번째는 ArchUnit 규칙으로 막았다. 스캔되는 컴포넌트는 생성자가 하나이거나, 여럿이면 그중 하나에 `@Autowired` 가 붙어야 한다. 규칙이 실제로 잡는지 결함을 되돌려 확인했다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
tech-log-backend : ca63d7d · 0da7c7e
|
||||
런타임 : k3s 위의 파드
|
||||
Jackson : 이 빌드는 Jackson 3, 클래스패스에 Jackson 2 타입이 전이 의존성으로 남아 있음
|
||||
확인 방식 : ArchUnit 규칙을 결함으로 되돌려 실제로 빨개지는지 확인
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 스캔되는 컴포넌트에 생성자를 둘 만들고 `@Autowired` 를 붙이지 않는다
|
||||
2. `./gradlew check` 를 돌린다 — 통과한다
|
||||
3. ArchUnit D20 규칙을 켠 상태로 돌린다 — 그 컴포넌트를 짚는다
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 어떤 테스트도 컨텍스트를 띄우지 않았다
|
||||
|
||||
새 활동 어댑터가 생성자를 둘 갖고 있었다. 하나는 운영용, 하나는 테스트가 id 생성기를 넣기 위한 것이다. 둘 중 어느 것에도 `@Autowired` 가 없어 컴포넌트 스캔이 고르지 못했다.
|
||||
|
||||
> 컴파일도, 단위 테스트도, **실제 PostgreSQL 위에서 도는 통합 테스트 26개도 전부 통과했다. 그 어느 것도 애플리케이션 컨텍스트를 띄우지 않기 때문이다.** 운영에서 파드가 CrashLoopBackOff 로 들어갔고, 그때서야 드러났다.
|
||||
|
||||
## 클래스패스에 남은 옛 타입
|
||||
|
||||
두 번째 건은 import 였다. `JdbcProjectRepositoryAdapter` 가 `com.fasterxml.jackson.databind.ObjectMapper` 를 요구했다. 이 빌드는 `tools.jackson.databind` 를 쓰므로 그런 빈이 없고, 컨텍스트가 refresh 에 실패한다.
|
||||
|
||||
컴파일이 잡지 못한 이유는 어떤 전이 의존성이 Jackson 2 타입을 클래스패스에 올려 두어 import 가 정상적으로 해석되기 때문이다. 빈이 없다는 것은 컨텍스트를 띄워야 알 수 있다.
|
||||
|
||||
| 원인 | 왜 컴파일·테스트가 못 잡았나 | 커밋 |
|
||||
|---|---|---|
|
||||
| 스캔되는 컴포넌트에 생성자 둘, `@Autowired` 없음 | 어떤 테스트도 애플리케이션 컨텍스트를 띄우지 않는다 | `ca63d7d` |
|
||||
| Jackson 2 `ObjectMapper` 를 요구(이 빌드는 Jackson 3) | Jackson 2 타입이 전이 의존성으로 클래스패스에 남아 있어 import 가 정상 해석된다 | `0da7c7e` |
|
||||
|
||||
## 규칙 하나로 막은 절반
|
||||
|
||||
D20 규칙을 세웠다. 스캔되는 컴포넌트는 생성자가 하나이거나, 여럿이면 그중 하나에 `@Autowired` 가 붙어야 한다. 결함을 되돌려 규칙이 실제로 멈추는 것을 확인한 뒤 커밋했다.
|
||||
|
||||
## 막지 못한 절반
|
||||
|
||||
D20 은 생성자 쪽만 본다. 클래스패스에 남은 옛 라이브러리 타입을 import 하는 것은 이 규칙이 잡지 않는다. 그 경로를 막는 검사는 아직 없고, 컨테이너가 뜰 때 알게 된다.
|
||||
|
||||
<!-- body:end -->
|
||||
Reference in New Issue
Block a user