- 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>
7.1 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | assets | evidence | source | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | a-customizer-that-discarded-the-bound-property | Flyway location customizer가 운영자가 바인딩한 값을 덮어썼다 | schema-ownership-and-capability-streams | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:a-customizer-that-discarded-the-bound-property | 2026-09-02 |
|
|
|
Flyway location customizer가 운영자가 바인딩한 값을 덮어썼다
커스터마이저는 벤더 위치를 넣어 주면서 위치 설정을 조건 없이 불렀다. Flyway 의 그 메서드는 더하는 것이 아니라 대체하고, 커스터마이저는 속성 바인딩이 끝난 뒤에 돈다.
관계
- 중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다 같은 설정을 두 주체가 정하려 한 사례다.
- 독립 Flyway 스트림과 baseline version 0 운영자가 위치 목록에 더하려던 것이 그 스트림들이다.
문제
Flyway 의 마이그레이션 위치는 두 곳에서 정해질 수 있다. 운영자가 속성으로 지정하거나, 벤더 설정이 커스터마이저로 넣어 주거나.
기본값을 주려는 코드와 값을 정하는 코드는 형태가 거의 같다. 둘을 가르는 것은 이미 값이 있는지 보느냐뿐이다.
결론
벤더 설정은 고쳤다. 고친 뒤로는 바인딩된 값이 비었을 때만 벤더 위치가 들어간다.
같은 이름의 빈이 sample 모듈에 하나 더 있는데, 그 빈은 아직 무조건 호출한다. 그 파일의 javadoc 은 이 기록과 반대되는 결론을 적는다.
증상이 왜 조용했는지와 두 구현의 차이는 본문이 다룬다.
검증 환경
Spring Boot : 4.0.8 마이그레이션 도구 : Flyway 확인 방식 : 두 커스터마이저 구현과 javadoc 대조, H2 쪽 부재 확인 소스 수정 : x
재현 조건
- PostgreSqlPersistenceConfig 의 커스터마이저 javadoc 을 읽는다. 어느 레인이 몇 개를 설정하고 몇 개를 적용했는지 적혀 있다.
- 그 파일의 현재 구현이 환경에서 속성을 읽고 비어 있을 때만 설정하는지 확인한다.
- SamplePostgreSqlPersistenceConfig 의 같은 이름 빈이 어떤 형태인지 확인한다.
- H2PersistenceConfig 가 커스터마이저를 두는지 확인한다.
본문
벤더의 마이그레이션 위치를 넣어 주는 커스터마이저가 위치 설정을 무조건 호출했다. Flyway 의 그 메서드는 더하는 것이 아니라 대체한다.
커스터마이저는 스프링이 속성 바인딩을 끝낸 뒤에 돈다. 운영자가 지정한 값은 읽히고 바인딩되고, 그다음에 버려졌다.
실패가 아니라 잘못된 성공이었다
운영자가 환경 변수로 마이그레이션 스트림들을 위치 목록에 더한다. Flyway 는 성공을 보고했고, 실제로 적용된 것은 벤더 스트림 하나였다. 없어지는 것은 운영자의 의도뿐이고 그것을 알려 주는 신호가 없었다.
일곱을 설정하고 하나를 적용했다
:::evidence key="a-customizer-that-discarded-the-bound-property" alt="코드베이스에서 벤더 설정의 커스터마이저 javadoc 과 현재 구현, 같은 빈 이름을 쓰는 sample 쪽 구현, H2 쪽 주석을 잘라낸 출력 48줄. 벤더 쪽은 값이 비어 있을 때만 설정하고 sample 쪽은 아직 무조건 호출한다는 것이 그 출력에 그대로 보인다." caption="벤더 커스터마이저 javadoc · 현재 구현 · sample 쪽 구현 · H2 쪽 주석 — 48줄 · exit 0" zoom="true" :::
javadoc 이 그 경우를 이름으로 적는다 — local-notification-ingest 다. 로컬에서 애플리케이션과 데이터베이스와 마이그레이션 단계들을 한 번에 띄우는 시작 레인이다.
그 레인은 위치 일곱 개를 설정했고 하나가 적용됐다. 나머지 여섯 스트림의 마이그레이션은 돌지 않았다.
같은 형태가 로컬 프로파일에도 있었다
javadoc 이 그 대비를 직접 든다. 로컬 프로파일 설정 파일이 값들을 리터럴로 고정해서 호출자가 공급한 모든 환경을 눌렀다.
그 파일은 지금 전부 자리표시자로 바뀌었고, 주석이 당시 상태를 남긴다 — 스모크 레인이 PostgreSQL 을 띄우고 애플리케이션을 거기 물리고 풀이 열리는 것과 헬스 체크가 통과하는 것을 보면서, 실제로는 이 파일이 고른 인메모리 데이터베이스를 상대하고 있었다는 것이다. 보고서가 둘을 구별하지 못했고, 그래서 실패가 아니라 거짓 초록이었다.
벤더 쪽 수정은 기본값 제공자가 되는 것이었다
지금 구현은 해석된 환경에서 그 속성을 읽고, 비어 있을 때만 벤더 위치를 설정한다. 운영자가 골랐으면 아무것도 하지 않는다.
javadoc 이 책임 이동까지 적는다 — 자기 위치를 지정한 배포는 벤더 위치를 포함할 책임을 진다. 위치를 지정함으로써 맡은 책임이라는 것이다.
sample 모듈의 같은 이름 빈은 고쳐지지 않았다
sample 모듈에 postgreSqlFlywayLocationCustomizer 라는 같은 이름의 빈이 있다. 환경을 받지 않고, 두 위치를 무조건 설정한다.
그 파일의 javadoc 은 이 기록과 정반대의 결론을 적는다 — locations(...) 가 대체하므로 이것이 속성이 아니라 유효한 정본이라는 것이다. 벤더 쪽 javadoc 이 같은 성질을 결함의 원인으로 적은 문장과 같은 사실을 두고, 한쪽은 고칠 이유로 쓰고 다른 쪽은 설계 근거로 쓴다.
sample 모듈은 분석 대상에서 제외된 모듈이라 원본 분석이 그 형태를 판정한 적이 없다. 코드가 그렇다는 것까지만 확인했다.
H2 쪽은 커스터마이저를 아예 두지 않는다
H2PersistenceConfig 는 커스터마이저를 등록하지 않고, 그 결정을 javadoc 에 굵게 적는다. 로컬 프로파일이 Flyway 를 끄고 Hibernate 가 엔티티에서 스키마를 만들기 때문에 H2 에 대응하는 마이그레이션 트리가 없다는 것이다.
세 설정이 같은 문제에 서로 다른 답을 갖고 있다 — 조건부로 기여하거나, 무조건 정하거나, 아예 두지 않거나.
확인하지 못한 것
여러 위치를 환경 변수로 지정해 재현해 보지는 않았다. sample 모듈은 분석 범위에서 제외된 모듈이라, 그 모듈의 같은 빈에 대한 판정은 원본 분석에 없다.