Files
llm-wiki/raw/branch-notes/feature-developer-experience-contract.md

47 KiB
Raw Permalink Blame History

title, source_type, status, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
title source_type status branch parent_branch related_projects governing_docs tags created target_merge status_label id kind project work_item inherits refines overrides depends_on contract_packet contract_packet_sha256
branch / feature-developer-experience-contract branch-note raw feature-developer-experience-contract
ca-skeleton
wiki/projects/ca-tmpl/devops-ci-supply-chain-dx
branch
ca-skeleton
developer-experience
local-dev
2026-05-22 review BR-CA-SKELETON-OPERATIONAL-CONTRACT-033 project-work-item ca-skeleton-operational-contract WI-CA-SKELETON-OPERATIONAL-CONTRACT-033
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1
1 d507282d4a550e9385db2a647f07c608950ae223886a80132d45d1957d2a2aee

branch: feature-developer-experience-contract

Layer: raw/branch-notes/ — 실무자가 skeleton을 받아 바로 실행, 검증, 확장할 수 있는 local developer experience 기준을 정의합니다.

부모 (필수)

ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 §18 Control Plane Contract → Developer Experience 영역의 결정/근거/금지 사항을 정제한다. governing doc = wiki/projects/ca-tmpl/devops-ci-supply-chain-dx (DX 슬라이스).

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: fresh environment에서 ./gradlew bootstrap이 성공한다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1 신규 환경의 default 진입 명령은 ./gradlew bootstrap이다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1 build tool은 Gradle Groovy DSL이다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract

브랜치 지역 결정

기존 branch-local 결정은 아래 ## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.

Decision ID Decision Relation Supporting Claims Status

선언한 예외

Override ID Overrides Reason Approval Status

목표

좋은 skeleton은 구조가 훌륭한 것에서 끝나지 않습니다. 새 개발자가 로컬에서 빠르게 실행하고, sample contract를 확인하고, 실패 기준을 재현할 수 있어야 합니다.

  • 이슈:
  • PR:

범위

포함 범위

  • local bootstrap command.
  • .env.example 필수 key.
  • sample profile 실행/비활성화 기준.
  • Testcontainers 또는 local dependency 대체 기준.
  • smoke test command.
  • README/runbook link 기준.

제외 범위

  • IDE별 개인 설정.
  • cloud development environment 강제.
  • production deployment guide.

근거 (필수, 최소 1개+)

이 branch의 구현·설계 결정의 근거가 되는 외부 자료. 상세 비교는 §외부 근거 / 대안 조사 참조. company-tech-blog 인용은 company-case-study 강도이며 official-standard / official-vendor-doc 으로 격상 금지.

Source 정당화하는 결정
raw/official-docs/dx-testcontainers-java-best-practices D3·D10 — bootstrap step 2 의 local dependency 도구 + Spring Boot 3.1+ @ServiceConnection 기반 default integration test backend 근거 (TC-CORE-C1~C4 / TC-SPRING-C1 / TC-REUSE-C1)
raw/official-docs/dx-mise-asdf-tool-versioning D6 — JDK Temurin 21 LTS + .tool-versions/.sdkmanrc 핀 (도구를 강제 않고 파일 포맷을 강제하는 전략, DX-TV-C4/C5)
raw/official-docs/dx-devcontainer-spring-boot D9 — devcontainer 를 default 로 두지 않는 결정의 대안 평가 (DX-DC-C2 development-phase 한정 / DX-DC-C4 VS Code 한정 / DX-DC-C5)

근거 자료가 raw에 아직 없다면 먼저 raw-source-template 또는 lecture-note-template 으로 raw에 등록한 뒤 여기서 링크.

외부 근거 / 대안 조사 (2026-05-22 — Group G-E: Developer Experience)

본 branch의 ./gradlew bootstrap 5단계 + Temurin 21 LTS + Testcontainers integration + markdown-link-check 결정에 대한 외부 source.

  • 채택 결정 (Gradle bootstrap + Testcontainers + Temurin 21):
  • 검토한 대안:
    • 대안 1: make bootstrap — POSIX 표준이나 Windows 친화성 낮음
    • 대안 2: docker compose up only — bootstrap 5단계 합성 어려움
    • 대안 3: devcontainer (VSCode·Codespaces)raw/official-docs/dx-devcontainer-spring-boot (containers.dev spec, IDE 종속성 + bootstrap 5단계 진입점/smoke 미해결)
    • 대안 4: Nix flake — reproducibility 강점이나 Java 생태계 성숙도 낮음
  • 비교 핵심: Gradle bootstrap이 5단계 합성 가능 + Spring 생태계 정합. Testcontainers @ServiceConnection(Spring Boot 3.1+)이 integration test의 boilerplate 제거. devcontainer는 IDE 종속이라 CI/CD와 분리 필요. 보강 후보: .tool-versions(asdf/mise) vs .sdkmanrc(SDKMAN) 포맷 차이 — branch note "또는" 표현은 drift 위험, 단일 source로 좁힐 필요.

D8 의 markdown-link-check 선택이 UNSUPPORTED_DECISION 이었으므로 대안을 조사했다 (/branch-spec §5 자동조사). 3종 비교:

도구 Node 의존 유지보수 CI gate 비고
markdown-link-check (npm/tcort) 필수 (Docker 우회) 단일 메인테이너, v3.14.2 (2025-11) tcort/github-action-markdown-link-check JVM-only repo 에 Node 툴체인 추가 비용
lychee (Rust/lycheeverse) 없음 (단일 정적 바이너리) 활발 (3,700+ stars, v0.24.2 2026-05, 40+ 프로젝트) lycheeverse/lychee-action@v2.0.2+ (CVE-2024-48908 패치 핀 필수) 권고 — JVM/Gradle repo DX 마찰 최소
linkinator (npm/binary) npm 경로 필수 / 바이너리 옵션 활발 (v7.6.1 2026-02, Google Cloud SDK 사용) JustinBeckwith/linkinator-action@v1 Node 도입 시 후보
  • 조건부 권고: ca-tmpl 이 package.json/Node toolchain 미도입을 유지하는 한 → lychee (Node 의존 없음). Node 를 다른 이유로 도입하면 → linkinator. 기존 Docker-first/MegaLinter 파이프라인이면 → markdown-link-check.
  • archiving 상태 (deferred): 위 비교의 raw 검증 자료(wiki-source-summarizer ×6, official + case-study) archiving 은 사용자 승인 대기 중. 승인 시 controller 가 dispatch → 생성 후 D8 의 UNSUPPORTED_DECISION 라벨을 official-vendor-doc + company-case-study 로 격상. 미archiving 상태에서는 D8 을 "조사됨, raw 미archiving" 로 표기(추측 단정 금지).

TODO

TODO drained 2026-05-22 — 결정은 아래 "결정 사항" / "Decision Evidence Map" / "Decisionized Work Items" / "테스트 계약" 참조. bootstrap/.env.example/sample profile/Testcontainers-local dep/smoke command/README-wiki 연결 모두 표 또는 결정 라인으로 반영됨. 잔존 TODO 없음.

구현 현황 (2026-06-15 ground truth): 본 branch 는 documented-only / planned 단계 — ca-tmpl src/ 실 코드에 bootstrap Gradle task·.env.example·.tool-versions·markdown-link-check·smoke test·CI 모두 미작성. 실제로 존재하는 것은 Flyway migration(V1/V3/V4) + JDK 21 toolchain + 수동 @Container Testcontainers + sample-portfolio(test-scope, ArchUnit 격리)뿐. 단계별 grade 는 §구현 가이드, drift 는 §Audit & Findings.

2026-06-24 구현 결과: direct-owner 범위는 actually-implemented이며 Linux local에서 locally-verified됐다. ./gradlew bootstrap 5단계, README command drift gate, @ServiceConnection context tests, local-only Testcontainers reuse policy, lychee workflow가 코드에 존재한다. .env/sample runtime toggle/fresh-clone CI는 기존 위임 owner를 유지한다. lychee remote CI와 macOS/WSL2는 needs-confirmation이다.

진행 중 메모

  • 2026-06-15: ca-tmpl 코드 대조 결과 본 branch 의 다수 결정이 아직 미구현(planned) 이거나 이미 구현된 sibling branch 와 drift 함이 확인됨. 자동 rewrite 하지 않고 §Audit & Findings 에 정합 권고로 기록 (사용자 작성 결정 영역). 핵심 drift 5건: BOOTSTRAP_TASK_ABSENT, GRADLE_VERSION_DRIFT(8.x→실제 9.0.0), ENV_EXAMPLE_SUPERSEDED(.env.examplesrc/.env+verifyEnvKeys 로 sibling 이 이미 해소), SAMPLE_ENABLE_MECHANISM_DRIFT(@Profile 가정 → 실제 ArchUnit+env), SERVICECONNECTION_NOT_USED(@ServiceConnection 가정 → 실제 수동 @Container).
  • 2026-06-24: D3/D4/D8/D10을 구현했다. bootstrap 첫 실행에서 host 5432 collision과 slim JRE RNG provider 누락을 발견해 각각 internal-only DB network와 SplittableRandom composition bean으로 해결했다. ./gradlew bootstrap, ./gradlew test check, focused ServiceConnection tests를 local에서 검증했다.
  • 2026-06-30: CleanArchitectureTest.java의 자원 누수 경고 해결(@SuppressWarnings("resource") 추가 및 import 스타일 정리), README.md에서 누락되었던 feature-developer-experience-contract 식별자 복구로 테스트 통과 확인, Spring Boot 3.5.x EOL 경고 무시를 위한 VS Code settings.json 설정 반영. 추가로 Spring Boot 4.x 업그레이드 시 Testcontainers 2.0 라이브러리와의 마이그레이션 호환성을 면밀히 재평정한 spec 문서(docs/superpowers/specs/2026-06-30-testcontainers-2-0-migration-spec.md) 작성 완료.

결정 사항

  • 2026-05-22: local 실행은 prod-safe 기본값을 훼손하지 않는 별도 profile/env로만 허용.
  • 2026-05-22: sample fixture는 local/dev에서 쉽게 켤 수 있어야 하고 prod에서는 기본 비활성화.
  • 2026-05-22: bootstrap command 기본값은 ./gradlew bootstrap. 없으면 ./gradlew testdocker compose up wrapper를 제공.
  • 2026-05-22: README는 canonical wiki를 대체하지 않고, local start/smoke/adoption entrypoint만 제공.
  • 2026-05-22: OS 매트릭스 = Linux (Ubuntu 22.04+), macOS (Apple Silicon 우선), Windows (WSL2 only). CI는 Linux만, 개발자는 3개 OS 검증 의무.
  • 2026-05-22: JDK = Temurin 21 LTS. gradle-wrapper 8.x. .tool-versions 또는 .sdkmanrc로 핀.
  • 2026-05-22: bootstrap task 정의 = ./gradlew bootstrap = (1) ./gradlew compileTestJava (compile sanity) (2) docker compose up -d (local dependencies via Testcontainers config 또는 별도 compose file) (3) Flyway migrate (4) sample profile seed (5) smoke test 실행. 5단계 모두 통과 시 성공.
  • 2026-05-22: sample profile default = clone 직후 enabled. prod profile에서는 disabled (feature-sample-removal-adoption-contract와 일관).
  • 2026-05-22: link-rot 검증 = markdown-link-check (npm). CI에서 README + docs/ 전수 검사.
  • 2026-06-15 (위 항목 보강/대체 후보 — D8): link-rot 도구 재조사 결과 lychee (Node-free 단일 Rust 바이너리) 를 조건부 권고. 기존 markdown-link-check 결정은 Node 의존 비용 미평가였음(ca-tmpl 은 package.json 없는 JVM-only repo). 상세·트레이드오프: §외부 근거 2026-06-15 addendum + Decision Evidence Map D8.
  • 2026-06-15 (정합 메모 — gradle wrapper): 위 "gradle-wrapper 8.x" 결정은 ca-tmpl 실제 gradle/wrapper/gradle-wrapper.properties9.0.0 과 drift. 핀 전략(repo wrapper 로 Gradle 버전 고정)은 유효하나 버전 숫자는 9.0.0 으로 정정 필요(§Audit GRADLE_VERSION_DRIFT).

Work Item Contract

각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 기준 작성으로 남아 있으면 이 branch는 완료로 보지 않습니다.

field required rule
Decision yes 구현자가 선택해야 하는 기본값
Allowed yes 허용되는 예외와 조건
Forbidden yes 절대 금지되는 구현/문서 상태
Required registry update conditional error/env/header/log/metric/capability 변경 시 필수
Required contract test yes 계약 위반 시 실패해야 하는 테스트
Failure condition yes review/build에서 실패로 판정할 상태
Canonical extraction target yes wiki/projects 승급 위치

결정-근거 매핑

본 branch 의 각 결정을 raw source 의 Claim ID 로 매핑. 선택 조건(R2): 이 조건일 때 이 결정 / 다른 조건이면 어떤 대안. company-tech-blog 인용은 company-case-study 강도이며 official-standard / official-vendor-doc 으로 격상 금지.

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 local 실행은 prod-safe 기본값을 훼손하지 않는 별도 profile/env 로만 허용 항상 — local 편의를 위해 prod 기본값(error detail 노출·body logging 등)을 바꿔야 하면 별도 profile/env override 로만. 금지 대안: 단일 profile 로 local+prod 겸용(=prod-unsafe default 누출) (ca-tmpl 고유 정책; 외부 raw claim 없음. parent §9 Env-driven Runtime Config 에 정합) UNSUPPORTED_DECISION 외부 standard 부재 — 자체 정책으로만 정당화
D2 sample fixture 는 local/dev 기본 enabled, prod 기본 disabled clone 직후 교육/계약검증 목적이면 enabled; prod 배포 profile 이면 disabled. enable/disable 런타임 메커니즘 owner = feature-sample-removal-adoption-contract (본 branch 는 DX 진입점만, 위임) (feature-sample-removal-adoption-contract 와 연계; 본 branch 외부 raw 직접 claim 없음) UNSUPPORTED_DECISION (delegated) enable/disable (env APP_SAMPLE_ENABLED, registry-backed; 런타임 토글 코드 메커니즘은 owner 확정 대상 — 본 branch 가 단정 안 함) 는 sibling owner, 본 branch 미구현
D3 bootstrap 단일 entry point = ./gradlew bootstrap 5단계 (compileTestJava → docker compose up -d → Flyway migrate → sample profile seed → smoke test) skeleton 채택자가 single command first-run 을 원할 때 ./gradlew bootstrap; CI/스크립트가 단계별 제어 필요하면 각 sub-task 직접 호출. 대안: make(Windows 친화성↓, §외부근거 대안1) / docker compose up only(5단계 합성 불가, 대안2) raw/official-docs/dx-testcontainers-java-best-practices.md#TC-CORE-C1, #TC-CORE-C2, #TC-CORE-C3 official-vendor-doc (Testcontainers 가 integration test backend 로 적합함만 증명 — 5단계 합성 자체는 ca-tmpl 고유) bootstrap task 미존재(planned) — 실제 first-run 은 README cd src && ./gradlew bootRun. docker-compose 파일 3종 모두 0-byte(빈). Flyway 만 실존. 5단계 합성·smoke 미구현 (§Audit BOOTSTRAP_TASK_ABSENT)
D4 README 는 canonical wiki 를 대체하지 않고 local start/smoke/adoption entrypoint 만 제공 README 는 진입점(첫 실행/스모크/채택 절차)만; 개념·계약 설명이 필요하면 canonical wiki 로 링크. 금지 대안: README 를 별도 SSOT 로 운영(=canonical 과 drift) (ca-tmpl 고유 운영 규약; 외부 raw claim 없음) UNSUPPORTED_DECISION 외부 standard 부재. 실제 README 존재하나 bootstrap/smoke section 없음(bootRun 만) → planned 부분
D5 OS 매트릭스 = Linux (Ubuntu 22.04+), macOS (Apple Silicon 우선), Windows (WSL2 only). CI 는 Linux, 개발자는 3개 OS 검증 Linux = CI 필수 게이트; macOS Apple Silicon / Windows WSL2 = 개발자 로컬 검증 의무. 미지원 대안: native Windows(non-WSL2) (ca-tmpl 고유 정책; 외부 raw claim 없음) UNSUPPORTED_DECISION Apple Silicon arm64 emulation 비용은 Testcontainers 메모(TC raw §메모)에서 경고만 — 정량 근거 없음
D6 JDK = Temurin 21 LTS. gradle wrapper 핀(전략). .tool-versions 또는 .sdkmanrc 로 IDE/CLI 핀 JDK 강제는 2층: build 는 Gradle toolchain(JavaLanguageVersion.of(21)), IDE/CLI 는 .tool-versions/.sdkmanrc. 도구(mise/asdf/SDKMAN)는 강제 안 함 — 파일 포맷만 강제(DX-TV-C5). 단일 포맷 권장(둘 다 두면 drift) raw/official-docs/dx-mise-asdf-tool-versioning.md#DX-TV-C4, #DX-TV-C5 official-vendor-doc (asdf 의 .tool-versions 단일 spec 위치 정의) gradle wrapper 실제 = 9.0.0(노트 "8.x" 와 drift, §Audit GRADLE_VERSION_DRIFT). .tool-versions/.sdkmanrc/.mise.toml 미존재(planned) — 실존은 build.gradle toolchain 21 뿐. Temurin 21 EOL(DX-TV-C8)·mise↔asdf 호환(DX-TV-C5 "Does not prove")은 needs-confirmation
D7 sample profile default = clone 직후 enabled, prod profile disabled D2 와 동일 정책의 default 표현. enable/disable 코드 owner = feature-sample-removal-adoption-contract(APP_SAMPLE_ENABLED); 격리 owner = feature-sample-domain-contract-fixture(ArchUnit). 본 branch 는 위임 (sibling branch 와 일관성; 외부 raw claim 없음) UNSUPPORTED_DECISION (delegated) 실제 격리는 Spring @Profile 이 아니라 ArchUnit production_code_does_not_depend_on_sample_portfolio + test-scope (§Audit SAMPLE_ENABLE_MECHANISM_DRIFT)
D8 link-rot 검증 도구 — lychee(Node-free 단일 바이너리) 조건부 권고, 기존 markdown-link-check 대체 후보 Node toolchain 미도입 유지 → lychee(lycheeverse/lychee-action@v2.0.2+); Node 도입 시 → linkinator; 기존 Docker-first/MegaLinter → markdown-link-check (2026-06-15 조사) (조사됨 — §외부근거 2026-06-15 addendum; raw archiving deferred, 사용자 승인 대기) researched, raw 미archiving (이전 UNSUPPORTED_DECISION) lychee-action CVE-2024-48908 → v2.0.2+ pin 필수. Gradle exec task 래핑·로컬 바이너리 프로비저닝 미설계. archiving 전까지 official Claim ID 부재
D9 devcontainer 를 default 로 두지 않음 (IDE별 개인 설정 out-of-scope) IDE 통일이 팀 강제이고 VS Code/Codespaces 단일 환경이면 devcontainer 고려; 다IDE/CI 분리 필요하면 default 제외(현 결정). 근거: spec 은 development-phase 한정(DX-DC-C2), VS Code 한정 통합(DX-DC-C4) raw/official-docs/dx-devcontainer-spring-boot.md#DX-DC-C2, #DX-DC-C4, #DX-DC-C5 official-standard + official-vendor-doc devcontainer + ca-tmpl bootstrap 양립 시연 없음 — 채택 시 별도 검증 필요(Claims To Verify 참조)
D10 Testcontainers (@ServiceConnection) 를 default integration test backend 로 둠 Spring Boot 3.1+ integration test backend = Testcontainers; bootstrap 의 로컬 dependencydocker compose(test lifecycle ≠ bootstrap lifecycle, TC raw §메모). reuse 는 로컬 opt-in / CI off(TC-REUSE-C1) raw/official-docs/dx-testcontainers-java-best-practices.md#TC-CORE-C1, #TC-SPRING-C1, #TC-REUSE-C1 official-vendor-doc (core 정의) + needs-confirmation (@ServiceConnection verbatim·reuse property 명 미확정) 실제 코드는 @ServiceConnection 미사용 — 수동 @Container PostgreSQLContainer (OutboxAppend/OutboxPublisher/DistributedLock contract test). @ServiceConnection 전환은 planned (§Audit SERVICECONNECTION_NOT_USED). TC-SPRING-C1/TC-REUSE-C1/TC-SINGLETON-C1 모두 needs-confirmation
D11 runtime container의 outbox jitter RNG는 java.base 구현을 명시 주입 slim JRE에서도 startup이 필요하면 SplittableRandom; provider-specific algorithm이 필수면 runtime module 포함 대안 프로젝트 container stack trace + OutboxConfigTest RED/GREEN (외부 raw claim 없음) UNSUPPORTED_DECISION 알고리즘 품질/성능을 외부 공식 source로 재검토하지 않음. trade-off: provider portability를 startup 안정성보다 우선하지 않음
D12 local PostgreSQL은 host port를 publish하지 않고 Compose internal network에서만 사용 app container startup Flyway가 migration owner일 때 internal-only; host DB client가 필요하면 별도 override 프로젝트 docker compose config + port collision 재현 (외부 raw claim 없음) UNSUPPORTED_DECISION host-side DB tool 사용자는 explicit override 필요. trade-off: zero-conflict 기본값과 직접 접속 편의의 교환
D13 ArchCondition 초기화 시 발생하는 ECJ 자원 누수 경고(Resource leak)를 @SuppressWarnings("resource")로 억제 항상 — ArchCondition 익명 이너 클래스 정의 시 컴파일러의 오탐지로 인한 경고 해결 (프로젝트 빌드 경고 해결용; 외부 raw claim 없음) UNSUPPORTED_DECISION N/A
D14 Spring Boot 3.5.x EOL 경고를 VS Code settings.json에서 무시하도록 설정 항상 — Testcontainers 2.x 메이저 업그레이드로 인한 패키지 변경 등 파급 효과를 피하기 위해 3.5.16 버전을 유지하고 IDE 경고만 비활성화 (IDE 문제 경고 해결용; 외부 raw claim 없음) UNSUPPORTED_DECISION N/A

Decisionized Work Items

item Decision Allowed Forbidden Required test
bootstrap one command: ./gradlew bootstrap wrapper around docker compose/test multiple competing first-run docs bootstrap smoke
.env.example registry-complete safe local values comments for secret placeholders prod secrets in example env example check
sample profile local/dev enabled, prod disabled education profile prod sample endpoint sample profile smoke
README/wiki README entrypoint, wiki canonical README links canonical README as separate truth doc drift check

⚠️ 2026-06-15 정합 주의: 위 .env.example row 는 sibling raw/branch-notes/feature-env-driven-runtime-configuration D7(2026-06-08 B 결정)이 .env.example 미사용 + src/.env git-tracked 단일 소스 + verifyEnvKeys 3-way gate 로 이미 해소함. 본 branch 의 .env.example 결정은 superseded — DX coverage 상 "env template self-sufficiency" 관심사는 그 sibling 에 위임한다(§Coverage). 자동 삭제하지 않고 정합 권고만(§Audit ENV_EXAMPLE_SUPERSEDED).

DX Defaults (deprecated)

DX 결정 표 SSOT는 위 "Decisionized Work Items". 별도 DX Defaults 양식은 deprecated.

구현 가이드

결정(Decisions) 이 "무엇" 이라면 본 §는 "어디에 어떻게" 의 사전 명세. 각 sub-section 은 Decision ID + Supporting Claim ID 를 Trace. ca-tmpl src/ 실 코드 대조(2026-06-15)로 reality grade(actually-implemented/planned/documented-only/delegated)를 셀마다 표기 — 노트 자기보고가 아니라 코드 grep 으로 확정.

3-rule: R1 모든 detail 은 Decision+근거 도출 · R2 근거 없는 임의 detail 은 UNSUPPORTED_IMPL_DECISION+trade-off · R3 본 branch 결정 범위 밖은 위임(§Audit 에 이관 history).

1. bootstrap 단일 진입점 — ./gradlew bootstrap 5단계

Trace: D3 (5단계 정의) / dx-testcontainers#TC-CORE-C1~C3. anchor = ca-tmpl src/build.gradle(task 미존재) + README.md §로컬 실행 + docker-compose*.yml(3종) + src/adapter-persistence/.../db/migration/.

  • UNSUPPORTED_IMPL_DECISION: 5단계의 합성 메커니즘(단일 Gradle task vs Makefile vs compose wrapper)은 외부 raw 가 권고하지 않음. Gradle task 채택 trade-off: Spring 생태계 정합·step 별 exit code 분리 가능하나 Windows(WSL2 밖) 친화성은 make 보다 낮음(D5 WSL2 강제로 회피).
단계 구현 anchor (목표) ca-tmpl 실제 상태 (2026-06-15) grade
(1) compile sanity ./gradlew compileTestJava 표준 task 존재 actually-implemented
(2) local dependency 기동 docker compose up -d (별도 compose file) docker-compose.yml/.dev.yml/.local.yml 모두 0-byte(빈) planned
(3) Flyway migrate flyway-core + db/migration/V*.sql V1__idempotency_record / V3__outbox_event / V4__int_lock (+ sample V2__work_log) 실존, baseline-on-migrate: false actually-implemented
(4) sample profile seed sample-portfolio seed sample-portfolio 모듈 실존(test-scope), 런타임 seed/profile 토글은 sibling 위임 delegated → feature-sample-removal-adoption-contract
(5) smoke test ./gradlew ...smoke 또는 health probe smoke/Smoke task·class 미존재. health endpoint GET /api/healthcheck 는 존재 planned
합성: bootstrap task custom Gradle task 가 5단계 묶음 bootstrap task 미등록 (app-bootstrap모듈명이지 task 아님). 현 first-run = cd src && ./gradlew bootRun planned (§Audit BOOTSTRAP_TASK_ABSENT)

2. tool-version 핀 — Temurin 21 LTS + Gradle wrapper

Trace: D6 / dx-mise-asdf#DX-TV-C4,#DX-TV-C5. anchor = src/build.gradle java { toolchain { languageVersion = JavaLanguageVersion.of(21) } } + gradle/wrapper/gradle-wrapper.properties.

  • UNSUPPORTED_IMPL_DECISION: .tool-versions(asdf/mise) vs .sdkmanrc(SDKMAN) 단일 포맷 선택. 외부 raw 는 포맷 spec 만 정의, 어느 것을 ca-tmpl default 로 둘지는 미권고. trade-off: .tool-versions 가 사실상 표준(DX-TV-C5)이고 mise/asdf 양쪽이 읽으나 mise 100% 호환은 needs-confirmation; .sdkmanrc 는 SDKMAN 단독. → 단일 source 로 .tool-versions 권장(둘 다 두면 drift, §외부근거 보강후보).
항목 구현 anchor (목표) ca-tmpl 실제 상태 grade
build JDK 핀 Gradle toolchain 21 JavaLanguageVersion.of(21) 실존 actually-implemented
Gradle 버전 핀 repo wrapper 로 고정 wrapper 9.0.0 (노트 "8.x" 와 drift) actually-implemented (버전 숫자 정정 필요, §Audit GRADLE_VERSION_DRIFT)
IDE/CLI JDK 핀 .tool-versions 단일 포맷 .tool-versions/.sdkmanrc/.mise.toml 미존재 planned
Temurin 21 LTS EOL 명시 Adoptium support 페이지 인용 DX-TV-C8 needs-confirmation (별도 fetch 필요) planned

3. integration test backend — Testcontainers

Trace: D10 / dx-testcontainers#TC-CORE-C1,#TC-SPRING-C1,#TC-REUSE-C1. anchor = ca-tmpl src/app-bootstrap/.../contract/outbox/OutboxAppendTransactionalContractTest.java 등.

  • UNSUPPORTED_IMPL_DECISION: container 공유 전략(@ServiceConnection vs 수동 @Container singleton). 외부 raw 의 @ServiceConnection(TC-SPRING-C1)·singleton(TC-SINGLETON-C1) 인용이 needs-confirmation 이라 verbatim 미확정. trade-off: 실제 코드는 수동 @Container PostgreSQLContainer 채택(boilerplate 더 많으나 명시적). @ServiceConnection 전환은 Spring Boot reference 재fetch 후 별도.
항목 구현 anchor (목표) ca-tmpl 실제 상태 grade
integration backend Testcontainers @Testcontainers+@Container PostgreSQLContainer (Outbox/DistributedLock contract test) 실존 actually-implemented (수동 방식)
boilerplate 제거 @ServiceConnection (Spring Boot 3.1+) @ServiceConnection 미사용 planned (§Audit SERVICECONNECTION_NOT_USED)
reuse 정책 로컬 opt-in / CI off .testcontainers.properties·testcontainers.reuse.enable 미존재 planned
bootstrap vs test 분리 docker compose(bootstrap) ↔ Testcontainers(test) 별도 명시 compose 파일 빈 상태 → bootstrap 측 미구현 planned

Trace: D4(README 진입점) + D8(link-rot 도구) / D8 은 §외부근거 2026-06-15 addendum. anchor = ca-tmpl README.md(§로컬 실행/§테스트/§환경 변수 규칙) + (link-rot config 미존재).

  • UNSUPPORTED_IMPL_DECISION: link-rot 도구 선택(lychee vs markdown-link-check vs linkinator). 2026-06-15 조사로 lychee 조건부 권고하나 raw archiving deferred(승인 대기). README↔command drift 검사 메커니즘(verifyReadmeCommands Gradle task)은 본 branch 임의 설계 — trade-off: 자동 강제 가능하나 ```bash 블록 파싱 규칙은 ca-tmpl 고유.
항목 구현 anchor (목표) ca-tmpl 실제 상태 grade
README local entrypoint §로컬 실행 (첫 실행 명령) README 실존, cd src && ./gradlew bootRun + GET /api/healthcheck actually-implemented (단 bootstrap/smoke 미반영)
README↔command drift 검사 Gradle verifyReadmeCommands 미존재 planned
link-rot gate lychee(lycheeverse/lychee-action@v2.0.2+) CI 게이트 config·CI·package.json 모두 미존재 planned

5. 위임 관심사 (OUT_OF_BRANCH_SCOPE → 다른 owner)

본 branch DX 진입점 밖이지만 governing DX 관심사인 것 — 결정 영역이 sibling owner 에 있으므로 §구현 가이드에 detail 을 남기지 않고 위임(R3). 위임 history 는 §Audit & Findings.

위임 관심사 owner branch 실제 메커니즘 (ca-tmpl)
.env.example / env key self-sufficiency raw/branch-notes/feature-env-driven-runtime-configuration D7 src/.env(git-tracked) + verifyEnvKeys 3-way + env-keys.yaml SSOT. .env.example 미사용
sample enable/disable 런타임 토글 feature-sample-removal-adoption-contract env APP_SAMPLE_ENABLED(registry-backed) + prod_profile_must_be_false + sample-off smoke. 런타임 토글 코드 메커니즘은 owner 확정 대상(미구현) — 본 branch 가 단정 안 함
sample production 격리 raw/branch-notes/feature-sample-domain-contract-fixture D5 ArchUnit production_code_does_not_depend_on_sample_portfolio + test-scope(actually-implemented)
fresh-clone smoke CI job feature-ci-quality-gates-contract CI 미존재 — fresh-clone-smoke job planned

엣지·실패·의존

R4(깊이 게이트) 캡처용. 정상 경로 외 구현 중 부딪힐 실패/엣지/다른 계약 의존.

  • 실패·엣지 경로:
    • bootstrap (2) docker compose up -d — Docker daemon 미기동 시 즉시 fail-fast + 안내(현 compose 파일 빈 상태이므로 step 2 자체 미정의). 기대: exit ≠ 0 + "Docker 필요" 메시지.
    • bootstrap (3) Flyway — out-of-order migration / checksum mismatch 시 fail. baseline-on-migrate: false 이므로 빈 DB 가정; 기존 스키마 존재 시 baseline 충돌.
    • Apple Silicon (arm64) emulation — 일부 Testcontainers image 가 amd64-only 면 emulation → bootstrap 시간 증가(D5 "macOS Apple Silicon 우선" 과 충돌 가능, TC raw §메모 경고만, 정량 근거 없음 → Claims To Verify).
    • link-rot false-positive — GitHub/LinkedIn 등 bot-blocker 429/999, Obsidian [[wikilink]] 는 표준 URL 아님 → 세 도구 모두 미검출. lychee accept/.lycheeignore 로 제어, wikilink 는 별도 처리 필요.
    • tool-version 불일치 — .tool-versions 핀과 CI runner/로컬 JDK 가 다르면 reproducible build 깨짐(D6; ci-quality-gates 와 공유).
    • smoke test green ≠ 정상 — 5단계 중 어디서 실패했는지 step 별 exit code 분리 필요(wiki/projects DevOps 문서 "과장 금지" 항목).
  • 다른 계약 의존:
    • [[raw/branch-notes/feature-env-driven-runtime-configuration]] D7 — .env/env-keys SSOT(verifyEnvKeys). 이 계약이 .env.example 부재를 확정하므로 본 branch 의 env template 관심사는 그쪽 결과를 consume. 그 계약이 바뀌면 본 branch bootstrap step 0(env 준비) 영향.
    • [[raw/branch-notes/feature-sample-removal-adoption-contract]]APP_SAMPLE_ENABLED 런타임 토글. bootstrap (4) sample seed 가 이 flag 를 consume.
    • [[raw/branch-notes/feature-sample-domain-contract-fixture]] D5 — sample-portfolio 격리(ArchUnit). bootstrap 이 sample 을 켜도 prod 경로 침범 없음의 근거.
    • [[raw/branch-notes/feature-ci-quality-gates-contract]]fresh-clone-smoke job + Testcontainers reuse CI off 정책. 본 branch 의 테스트 계약(fresh-clone-smoke)이 그 CI gate 에서 실행됨.
    • [[raw/branch-notes/feature-test-taxonomy-fixture-contract]] — integration test taxonomy 가 Testcontainers 를 default backend 로 둠(D10 과 공유). @ServiceConnection 전환 결정의 공동 영역.
    • [[raw/branch-notes/feature-container-runtime-contract]] — container JVM/healthcheck/graceful-shutdown 기준. bootstrap 이 띄우는 런타임의 health probe(/api/healthcheck)는 그 계약과 정합.

테스트 계약

  • .env.example 자급자족 검사: clean clone 직후 cp .env.example .env && ./gradlew bootstrap만으로 5단계 sub-task(compileTestJava → docker compose up -d → Flyway migrate → sample profile seed → smoke test)가 모두 통과해야 함. 측정 방법: CI에 fresh-clone-smoke job 추가 — clean container에서 위 명령 시퀀스 실행 후 exit code 0 + smoke test green. 추가 prompt/수동 입력이 필요하면 fail. ⚠️ 2026-06-15 정합: sibling feature-env-driven-runtime-configuration B 결정으로 .env.example 대신 src/.env(git-tracked) 사용 → 본 검사의 cp .env.example .env 전제는 src/.env 기준으로 갱신 필요(§Audit ENV_EXAMPLE_SUPERSEDED).
  • sample profile이 prod profile에서 켜지면 실패.
  • README ↔ 실 command drift 검사: README.md의 code block에 등장하는 모든 ./gradlew, docker compose, make command가 실제 build script에 존재해야 함. 측정 방법: markdown-link-check + 자체 Gradle task verifyReadmeCommands. README parsing: ```bash 블록에서 command 추출 → 각 command의 첫 token이 build script에 정의된 task이거나 system 표준 도구(docker, git 등)여야 함. 미정의 command 1건이라도 있으면 fail.
  • bootstrap command가 하나로 고정되지 않으면 실패.

검증해야 할 주장

공식 문서 / 사례는 근거지만 ca-tmpl 프로젝트에서의 동작을 자동 보장하지 않음. 구현 전/중/후 실제 검증 대상.

Claim Why uncertain How to verify Status
./gradlew bootstrap 5단계가 clean clone 직후 추가 prompt 없이 모두 통과한다 bootstrap task 자체가 ca-tmpl 고유 합성 — 외부 raw 가 5단계 합성을 보장하지 않음 (Testcontainers core claim 은 test backend 적합성만 증명) CI fresh-clone-smoke job: clean container 에서 cp .env.example .env && ./gradlew bootstrap 실행 → exit code 0 + smoke test green planned
README ↔ build script command drift 가 0 건 README 의 code block 과 실제 task 정의 일치는 자동 보장되지 않음 Gradle task verifyReadmeCommands — README 의 ```bash 블록에서 command 추출 → 첫 token 이 build script task 또는 system 표준 도구인지 검사. 미정의 1건이라도 fail planned
@ServiceConnection 이 ca-tmpl 의 모든 dependency (PostgreSQL / Redis / Kafka 등) 에 대해 boilerplate 제거를 보장 TC-SPRING-C1needs-confirmation — 지원 module 범위 미확정 Spring Boot reference docs 재fetch 로 supported module list 확보 → ca-tmpl dependency 목록과 교차 needs-confirmation
Testcontainers reuse 가 로컬에서 의도된 startup 단축 효과를 내고 CI 에서는 비활성화된다 TC-REUSE-C1 property 명과 "must not be enabled in CI" 표현이 needs-confirmation Testcontainers reuse docs (https://java.testcontainers.org/features/reuse/) 재fetch + 로컬 측정 (cold start vs reused) + CI yaml 에서 reuse 플래그 부재 확인 needs-confirmation
Temurin 21 LTS 의 EOL 일자가 ca-tmpl 채택 주기 (≥ 36 개월) 와 호환 DX-TV-C8needs-confirmation — Adoptium support 페이지 인용 미확보 https://adoptium.net/support/ 재fetch 로 정확한 EOL 일자 확정 후 branch note 갱신 needs-confirmation
mise 와 asdf 가 동일한 .tool-versions 파일을 100% 호환 해석 DX-TV-C5 "Does not prove" 컬럼에서 명시적으로 보장 안 됨 mise 공식 페이지 (.tool-versions 호환성 섹션) 재fetch + 두 도구로 동일 파일 read/install 시연 needs-confirmation
.sdkmanrc.tool-versions 가 동시 존재할 때 drift 가 발생하지 않는다 (또는 단일 source 정책 채택) DX-TV-C7needs-confirmation — SDKMAN .sdkmanrc 포맷 verbatim 미확보 SDKMAN docs (https://sdkman.io/usage#env) fetch → ca-tmpl 정책을 "또는" 에서 단일 source 로 좁힐지 결정 planned
Apple Silicon (arm64) 에서 Testcontainers image 의 emulation 비용이 bootstrap 시간 (목표 1단계 분 이내) 을 초과하지 않는다 Testcontainers raw 메모에서 경고만 됨 — 정량 근거 없음 M1/M2 환경에서 bootstrap 측정 + arm64 native image 가용 여부 module 별 점검 planned
devcontainer 채택 시 ca-tmpl ./gradlew bootstrap 5단계가 devcontainer 안에서 동등 동작 DX-DC-C5 가 tool/runtime stack 만 보장 — Flyway 순서 / smoke test 자동 보장 안 함 .devcontainer/devcontainer.json 작성 → Codespaces + 로컬 VS Code 양쪽에서 bootstrap 실행 → exit code 비교 planned
markdown-link-check 가 README + docs/ 의 모든 wikilink + URL 을 false-positive 없이 검출 도구 선택 자체에 외부 spec 미수집 (D8 — 2026-06-15 lychee 권고로 재검토) npm 패키지 reference 확인 + CI 에서 dry-run → false-positive 목록 수집 후 ignore pattern 확정 planned
lychee 가 ca-tmpl 의 README + docs/ relative file link + external URL 을 false-positive 없이 검출하고 CI 에서 broken link 시 exit ≠ 0 2026-06-15 조사로 권고됐으나 ca-tmpl 실 파일 dry-run 미실시 + lychee-action CVE pin 필요 lychee --root-dir . './docs/**/*.md' './README.md' dry-run → .lycheeignore 수렴 → .github/workflows/link-check.yml(lycheeverse/lychee-action@v2.0.2+, fail: true) 에 broken link 인위 삽입 → exit ≠ 0 확인 planned

관심사 커버리지

/coverage 가 채우는 생성물 — 손유지 금지. governing 문서(wiki/projects/ca-tmpl/devops-ci-supply-chain-dx §DX + parent §18 Developer Experience)가 요구하는 DX 관심사를 본 branch 가 빠짐없이 덮는지. 기준: rules/coverage-gate.md. 아래는 /branch-spec 가 staged 한 seed — coverage-auditor 가 코드/선례 대조로 확정.

관심사 (governing) 상태 owner 심각도 근거
local bootstrap command (단일 진입점) covered-here D3 (plannedbootstrap task 미존재)
.env.example / env template self-sufficiency delegated raw/branch-notes/feature-env-driven-runtime-configuration OK D7(sibling) — src/.env+verifyEnvKeys, §Audit ENV_EXAMPLE_SUPERSEDED
Testcontainers 또는 local dependency 대체 covered-here D10 (manual @Container 실존, @ServiceConnection planned)
smoke test command covered-here D3 step5 + 테스트 계약 (planned)
sample profile 실행/비활성화 delegated raw/branch-notes/feature-sample-removal-adoption-contract OK D2/D7 위임, §Audit SAMPLE_ENABLE_MECHANISM_DRIFT
README/runbook link 기준 covered-here D4 + D8 link-rot (planned)
tool version pinning (Temurin 21 LTS) covered-here D6 (toolchain 21 실존, .tool-versions planned)
link-rot / dead-link check covered-here D8 lychee 권고 (raw archiving deferred)
fresh-clone smoke CI job delegated raw/branch-notes/feature-ci-quality-gates-contract OK §엣지·실패·의존 다른 계약 의존

Audit & Findings

2026-06-15 /branch-spec ca-tmpl src/ 코드 대조 결과. 사용자 작성 결정 영역이므로 자동 rewrite 하지 않고 정합 권고만 기록(추측 단정 금지).

Finding ID 유형 내용 권고
BOOTSTRAP_TASK_ABSENT planned (drift 아님) ./gradlew bootstrap task 미등록(app-bootstrap 은 모듈명). 현 first-run = cd src && ./gradlew bootRun. docker-compose 3종 0-byte. D3 를 planned 로 명시(완료). Phase C2 구현 시 custom task + step exit code 분리.
GRADLE_VERSION_DRIFT drift 노트 D6 "gradle-wrapper 8.x" vs 실제 gradle-wrapper.properties 9.0.0. 결정 사항 2026-06-15 정합 라인 + D6 Open Risk 반영(완료). 버전 숫자만 9.0.0 으로 정정, 핀 전략 유효.
ENV_EXAMPLE_SUPERSEDED drift (superseded by sibling) 노트의 .env.example 결정(Decisionized Work Items + 테스트 계약)이 raw/branch-notes/feature-env-driven-runtime-configuration D7(2026-06-08 B: .env.example 미사용, src/.env+verifyEnvKeys+env-keys.yaml SSOT)과 충돌. env template self-sufficiency 관심사를 그 sibling 에 위임(§Coverage). 테스트 계약의 cp .env.example .envsrc/.env 기준으로 갱신 권고(완료).
SAMPLE_ENABLE_MECHANISM_DRIFT drift 노트 D2/D7 이 Spring @Profile enablement 가정. 실제 = ArchUnit production_code_does_not_depend_on_sample_portfolio + test-scope(격리, raw/branch-notes/feature-sample-domain-contract-fixture D5) + env APP_SAMPLE_ENABLED(런타임 토글, feature-sample-removal-adoption-contract owner; registry-backed, 토글 코드 메커니즘 미구현). enable/disable·격리 모두 sibling 위임으로 표기(완료). @Profile 표현은 sibling 결정으로 대체. 토글 코드 메커니즘은 owner 가 확정(본 branch 단정 안 함).
SERVICECONNECTION_NOT_USED planned (drift) 노트 D10 "@ServiceConnection default" vs 실제 수동 @Container PostgreSQLContainer(Outbox/DistributedLock contract test). D10 reality grade planned(완료). @ServiceConnection 전환은 TC-SPRING-C1 재fetch 후 별도(Claims To Verify).

2026-06-24 구현 판정

Finding ID 결과 증거 등급 남은 경계
BOOTSTRAP_TASK_ABSENT bootstrapCompilebootstrapDependenciesbootstrapMigrateAndStartbootstrapSampleContractbootstrapSmoke 구현 locally-verified macOS/WSL2 clean clone 미검증
GRADLE_VERSION_DRIFT wrapper 9.0.0 유지, .tool-versions Temurin 21.0.11+10 소비 actually-implemented tool manager별 해석은 미검증
ENV_EXAMPLE_SUPERSEDED src/.env를 Compose env_file로 소비, 새 .env.example 미생성 locally-verified sibling owner 유지
SERVICECONNECTION_NOT_USED Spring context/slice 2개는 @ServiceConnection; direct JDBC/SQLState tests는 명시적 container factory 유지 locally-verified 공식 지원 범위 source refetch 미완료
LINK_ROT_GATE_ABSENT lycheeverse/lychee-action@v2.0.2, fail: true workflow 추가 actually-implemented remote workflow 실행은 needs-confirmation

완료 후 wiki 추출 대상

  • wiki/projects/ca-skeleton-operational-contract.md의 developer experience canonical section.
  • wiki/projects/ca-tmpl/devops-ci-supply-chain-dx.md 의 DX 슬라이스 (governing doc).

마주친 문제

묶음

Sub-branches

본 feature branch 는 개발 하네스 정합 child를 소유한다. 추가 child/derived 자료는 이 섹션에서 그룹화한다.

오류 기록 (본 feature 작업 중 발생)

면접 준비 (이 작업에서 나올 수 있는 면접 질문)

블로그·채용공고 연계 글감

관련 일일 노트

이 브랜치를 작업한 날짜들. 양방향 nav 유지.

  • raw/daily-notes/2026-06-30
  • (생성 2026-05-22 / branch-spec 2026-06-15 — 해당 daily-note 미연결. 작업 재개 시 [[raw/daily-notes/YYYY-MM-DD]] 추가)

완료 후 정리

머지/종료 시점에 채움.

  • PR 링크:
  • 리뷰 메모:
  • 머지 결과 / 배포 환경:
  • wiki 추출 대상 (verified만, wiki/projects/로만 추출):
    • actually-implemented 항목:
    • locally-verified 항목:
    • prod-verified 항목:
  • 추출하지 않을 항목 (planned / documented-only / abandoned):