Files
document-haness/docs/TechLog/tech-log-studio/seams-no-test-crosses/case/case-two-pods-crashlooped-with-no-test-starting-the-context.md
T
DongHyeonkaandClaude Opus 5 0650d91def docs(TechLog): 설명 뒤에 붙은 평가·예고·되풀이를 걷어낸다
rewriting-technical-prose-naturally 를 서브에이전트 셋으로 나눠 56편에 적용했다.
ai-tells.md 의 첫 절대로 다른 표현으로 바꾸는 대신 문장을 통째로 지웠다.

  설명한 것의 중요성을 다시 평가하는 꼬리   19
  이미 설명한 것을 추상어로 되풀이           19
  독자에게 읽는 법을 지시하거나 오해를 가정   9
  자료가 뒷받침하지 않는 덧붙인 이득          4

문서군 전체의 문형 편중도 풀었다 — 함께 27→7(한 묶음), 그대로 22→12(두 묶음),
하게 된다 1→0. 한 편에서 세 번 반복되던 「같은 병이 ~에서도 났다」와 두 기록에
같은 문장으로 있던 세 쌍을 갈랐다.

계약 제목 「여덟 자리」가 본문의 「여덟 곳」과 어긋나 있었다. 제목이 spatial-metaphor
규칙에도 걸리므로 계약과 기록을 함께 「여덟 곳」으로 맞췄다.

검사 넷 전부 통과한다 — check_prose 56편 error 0 · check_body PASS ·
check_evidence --repo 문제 없음 · verify-tech-log-tree error 0 warn 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 16:03:16 +09:00

90 lines
5.0 KiB
Markdown

---
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 규칙으로 막은 것
D20 규칙을 세웠다. 스캔되는 컴포넌트는 생성자가 하나이거나, 여럿이면 그중 하나에 `@Autowired` 가 붙어야 한다. 결함을 되돌려 규칙이 실제로 멈추는 것을 확인한 뒤 커밋했다.
## D20 이 보지 않는 것
D20 은 생성자 쪽만 본다. 클래스패스에 남은 옛 라이브러리 타입을 import 하는 것은 이 규칙이 잡지 않는다. 그 경로를 막는 검사는 아직 없고, 컨테이너가 뜰 때 알게 된다.
<!-- body:end -->