ca-tmpl 의 production capability platform 설계(platform 1,346줄 + redis 6,771 + httpclient 7,030 + fileserver 2,467)를 llm-wiki 거버넌스로 편입하는 분해 설계. 2-tier(기술 런타임 / 능력 계약) 모델로 신규 branch 18개를 확정하고, 기존 branch 8건의 주도권을 신규 branch 로 이전하는 절차를 고정. hub 드리프트 3건 중 2건을 코드 대조로 정정: - Spring Boot 3.5.14 → 4.0.0 (src/build.gradle:8), DEC-…-STACK-FRAMEWORK-001 revision 1 → 2 + WI 9행 pin 갱신 - ca-tmpl 경로를 desktop-server-git/clean-architecture-backend-template 로 정정 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
51 KiB
CA Skeleton — Production Capability feature 분해 설계
일자 / Date: 2026-07-28
대상 문서 / Target: raw/project-notes/ca-skeleton-operational-contract.md
대상 코드 / Code: /home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template (이하 ca-tmpl, commit b3add01)
범위 / Scope: ca-tmpl 의 production capability platform 설계를 llm-wiki 거버넌스(project note + branch note)로 편입. 신규 branch 18개 + 기존 branch 8건 주도권 이전 + hub §별 델타.
요청 언어 / User language: ko
상태 / Status: 설계 승인 완료 (2026-07-28), 구현 계획 미작성
0. 이 문서의 지위
이 문서는 raw/project-notes/ca-skeleton-operational-contract.md(이하 hub)에 무엇을 어떻게 추가·정정할지 고정한 설계 스펙이다. hub 본문의 대체물이 아니며, 여기서 확정한 ID·slug·문자열·수치를 hub 와 신규 branch-note 가 그대로 옮겨 담는다.
이 문서 자체는 구현 증거가 아니다. 여기 적힌 모든 계약·기본값·경로는 CLAUDE.md §6 기준 planned 또는 documented-only 다. 예외는 §13 드리프트 정정뿐이며, 그 절의 항목은 ca-tmpl 코드를 직접 읽어 확인한 actually-implemented 사실이다(각 행에 파일 경로 명시).
1. 배경과 조사 결과
1.1 사용자 요청
요청의 핵심은 두 가지다.
- adapter 가 운영 환경에 바로 못 쓴다. redis, fileserver, httpclient, messaging, notification, objectstorage, jpa, mongo, graphql, grpc, web, websocket 이 seam 수준에 머물러 있다.
- cross-cutting 이 PostgreSQL 에 묶여 있다. outbox·idempotency·lock·rate limit·cache 가 DB 한 곳으로만 해결되게 설계되어 있다. outbox 는 CDC 가 필요할 수 있고, lock 은 Redis 가 필요할 수 있으며, rate limit 은 정책에 따라 알고리즘을 갈아끼울 수 있어야 한다.
여기에 교육적 요구가 붙는다 — "자주 쓰는 함수를 기본 제공해서, 그걸 쓰다가 부딪히는 문제를 개발자가 알 수 있게" 하는 것. 사용자가 든 예가 정확히 그 성격이다: Redis 는 싱글스레드인데 왜 동시성 문제가 생기는가, 그리고 그 답으로서의 Lua.
명시적 비요구: 대기업 기술 블로그식 "이런 상황에서 이만큼 개선했다" 는 수치 서사는 만들지 않는다.
1.2 조사에서 드러난 사실 — 설계는 이미 있다
ca-tmpl 저장소에 요청 내용을 이미 판정한 설계가 존재한다.
| 문서 | 줄 수 | 다루는 범위 |
|---|---|---|
docs/superpowers/specs/2026-07-26-production-capability-platform-design.md |
1,346 | capability platform 전체 — provider 선택, readiness R0~R3, Redis role, 실패정책, outbox/CDC, Kafka, Mongo, HTTP, notification, object storage, file server, web/GraphQL/gRPC/WebSocket, 관측, Gradle, CI, 6-phase 로드맵 |
.../2026-07-26-redis-production-capability-design.md |
6,771 | Redis 심화 — role/deployment isolation, key model, payload/codec, atomic program registry, command·transaction·pipeline 의미, cache contract, rate limit, idempotency, lease/fencing, session, 관측, topology CI |
.../2026-07-27-httpclient-production-capability-design.md |
7,030 | HTTP client 심화 — total deadline, retry/circuit breaker 순서, pool, DNS, SSRF, TLS/mTLS, 관측 |
.../2026-07-26-fileserver-production-capability-design.md |
2,467 | file server 심화 — staged publish, guarantee 모델, provider 3종, CSV safety, quota/retention |
구현도 진행 중이다. commit b3add01 "feat: redis, fileserver, httpclient 런타임 시점 구현 추가" 기준으로 adapter/outbound/cache-redis 43개 파일(Lua program executor·catalog·descriptor, atomic primitives, key builder/namespace/digest, command admission, envelope codec 포함), httpclient 38개, fileserver 16개가 존재한다.
1.3 그런데 llm-wiki 에는 이 작업이 없다
hub 를 grep 한 결과:
- §8.0 Work Item Registry 60행(
WI-…-001~059) 중 capability platform 관련 행 0개 - §6.1 안정 결정 레지스트리 25행 중 관련 결정 0개
- §35 Implementation Coverage Checklist 에 redis/httpclient/fileserver 는 각각
cache-consistency·outbound-http-client-baseline·file-resource-handling행으로[~](결정 박힘, 코드 없음) 로 표기 — 실제로는 코드가 들어갔다 - mongo / graphql / grpc / websocket / session-store / CDC / Kafka consumer 는 row 자체가 없음
즉 코드가 문서를 앞질렀고, 그 사이 간극이 hub 의 거버넌스 밖에 있다. 이 설계의 목적은 그 간극을 branch-note 단위 소유권으로 덮는 것이다.
1.4 이 확장이 없으면 발생하는 실패
계약은 아래 실패를 막기 위해 존재한다(hub §26 Universal Acceptance Gate 질문 1).
| 실패 | 설명 |
|---|---|
| 거짓 R2 표기 | seam 하나만 있는 capability 를 문서·startup 진단이 "production ready" 로 보고한다. 운영자가 없는 보증을 믿는다. |
| 보증 등급 혼동 | Redis idempotency claim + JDBC 업무 write 를 SAME_STORE_TRANSACTIONAL 로 오인해 crash 후 중복 실행이 발생한다. |
| CDC 전환 불가 | 가변 status row outbox 에 Debezium Outbox Event Router 를 붙일 수 없다는 사실을 전환 시점에 발견한다. |
| 알고리즘 고착 | rate limit 이 fixed window 하나뿐이라 burst 허용·평균율 제어가 필요한 정책을 표현할 수 없다. |
| 조용한 경합 | GET → 판단 → INCR → EXPIRE 를 원자적이라 믿고 작성해 카운터가 어긋나거나 TTL 이 유실된다. Redis 가 싱글스레드라는 사실이 이 오해를 강화한다. |
| 캐시가 결함을 삼킴 | codec·프로그래밍 결함이 blanket catch(Exception) fail-open 을 통과해 평범한 cache miss 로 위장된다. |
| 세션 부재로 다중 파드 불가 | JWT STATELESS 고정이라 세션 기반 인증이 필요한 순간 다중 파드에서 인증 상태를 공유할 수 없다. |
| 비고정 fencing | lease 기반 lock 을 correctness 보증으로 오용해 failover 후 stale holder 가 write 한다. |
| 전체 객체 메모리 적재 | object storage 가 byte[] 전용이라 대용량 업로드/다운로드가 힙을 그대로 소비한다. |
| 미검증 활성화 | bean 이름 존재만으로 multi-instance 안전을 보고해, 실제 분산 구현 없이 안전하다고 판정한다. |
2. 확정된 설계 선택 (사용자 승인 2026-07-28)
| 선택 | 결정 | 근거 |
|---|---|---|
| 활성화 자세 | optional Gradle 모듈 + capability flag default OFF, 모듈 내부는 완제품 | core 는 지금처럼 가볍고, 안 쓰는 프로젝트는 의존성·기동시간 부담 0. frontend hub 의 FE-D026(capability default OFF)과 같은 자세 |
| 분해 단위 | 2-tier(기술 런타임 / 능력 계약) + 신규 branch 18개 | Redis 심화 설계 6,771줄이 7개 관심사에 걸쳐 있어 branch 1개로 불가. 기술×능력으로 쪼개면 연결·키·코덱 결정이 5곳에 중복 |
| Stack | core stack 유지 + Optional Adapter Stack Matrix 신설 | §34 의 "단일 stack 커밋" 정신을 각 optional 축에서도 유지(축마다 구현체 1개) |
| 제공 깊이 | port + adapter + 연산 툴킷 + 함정 게이트 | 사용자 요구의 교육적 성격을 계약으로 고정. unsafe recipe 와 atomic 대체를 문서·테스트에서 쌍으로 제시 |
| 기존 branch 충돌 | 주도권을 신규 branch 로 이전하고 동기화 | 사용자 지시(2026-07-28). Single-Owner 유지, 기존 branch 는 포인터만 보존 |
3. 범위
3.1 In scope
- 신규 branch 18개의 소유 관심사 · Work Item · 결정 · 커버리지 행 확정
- 기존 branch 8건의 주도권 이전(handover) 대상 D-row 와 동기화 절차 확정
- hub §1·§2·§11·§25·§34·§35·§6.1·§8.0·§24·§31.1·§29 의 델타 확정
- Capability Readiness Model(R0~R3) 과 capability card 필수 항목의 hub 편입
- 능력별 실패정책 매트릭스(hub §11 Optional Adapters 절 대체)
- ca-tmpl 코드 대조로 확인된 드리프트 3건 정정
3.2 Out of scope
사용자 제약을 문서에 못박는 항목이다. hub §2 "하지 않는 것" 에 추가한다.
- 각 branch 의 심층 명세 — 본 문서는 소유권·경계·hub 델타까지다. 각 branch 의 D-row 와 §구현 가이드는
/branch-spec이 별도 세션에서 채운다. - 성능 수치·개선 서사 — "무엇에서 무엇으로 개선" 형태의 측정 주장은 만들지 않는다. capacity 입력(키 크기, 연산율, 보존기간, 재시도 증폭)만 계약한다.
- 도메인 로직 — 로그인 플로우, 결제 흐름, 재고 차감 같은 도메인 형태 레시피는 만들지 않는다. capability pack 은 안전한 저장·전송·조율을 제공하고 use case 는 적용 프로젝트가 쓴다.
- vendor 선택 — 관리형 Redis/Kafka/Mongo 제공자, CDN, push 서비스, KMS 제공자.
- exactly-once 주장 — DB 와 broker 를 걸친 exactly-once, Redis lock 의 strong correctness 는 코드·문서 어디에도 쓰지 않는다.
- ca-tmpl 코드 작성 — 본 문서는 llm-wiki 거버넌스 산출물이다. 코드는 ca-tmpl repo 의 plan 이 소유한다.
3.3 Non-goal
- 신규 capability 를 기본 활성화하는 것. 기본값은 전부 비활성이며
feature-capability-provider-selection-contract의 gate 가 이를 증명한다. - 기존 60개 Work Item 의 의미 변경. §5 의 8건만 주도권이 이동하고 나머지는 그대로다.
- llm-wiki 를 ca-tmpl 심층 설계의 사본으로 만드는 것. branch-note 는 결정과 근거를 소유하고, 구현 세부는 ca-tmpl 설계를 reference 한다.
4. 분해 — 2-tier 모델
4.1 왜 2-tier 인가
기술 1개 = branch 1개로 두면 Redis 가 무너진다. Redis 는 cache·rate limit·session·idempotency·lock 5개 능력과 role·key·codec·script 4개 기반이 한 기술 안에 겹쳐 있다.
반대로 능력×기술로 쪼개면(cache-redis, lock-redis, session-redis…) 연결 설정·키 스키마·직렬화·스크립트 카탈로그 결정이 5개 branch 에 중복된다. rules/consistency-contract.md 의 Single-Owner 위반이고, Redis 클라이언트 하나 바꿀 때 5곳을 고쳐야 한다.
그래서 축을 둘로 나눈다.
Tier T (기술 런타임) — 클라이언트 · 연결 · 키 · 코덱 · 스크립트 · health · 보안
↑ provider 로 사용됨
Tier C (능력 계약) — provider-neutral port · 보증(guarantee) · 실패정책 · 전략 registry
↑ 활성화 판정을 위임
Tier P (플랫폼) — capability descriptor · provider 선택 · readiness · capability card
Redis 는 Tier T 2개 branch 가 소유하고, Tier C 5개 branch 에 provider 로만 등장한다. 중복 0.
4.2 신규 branch 18개
| # | Tier | branch slug | 소유 관심사 | Work Item |
|---|---|---|---|---|
| 1 | P | feature-capability-provider-selection-contract |
capability descriptor, provider: disabled|<id> 활성화 SSOT, readiness R0~R3, capability card 필수 항목, fail-fast 토폴로지 검증, 능력별 실패정책 매트릭스 |
WI-…-060 |
| 2 | T | feature-redis-runtime-role-isolation-contract |
cache/coordination/session 3-role 분리, 연결·TLS·ACL·pool·topology refresh, key model(namespace·digest·hash tag·version·bounds), payload envelope·codec·compatibility, role 별 health | WI-…-061 |
| 3 | T | feature-redis-atomic-program-catalog-contract |
Lua/Function 카탈로그, ScriptDescriptor, EVALSHA + NOSCRIPT 재적재, bounded execution 규칙, unsafe multi-command recipe ↔ atomic 대체 쌍, golden vector |
WI-…-062 |
| 4 | T | feature-kafka-producer-runtime-contract |
acknowledgement-aware send, acks=all + idempotent producer, bounded delivery timeout, key/partition 순서, 압축·배치 한계, TLS/SASL, 스키마 serializer, graceful flush |
WI-…-063 |
| 5 | T | feature-kafka-consumer-inbox-contract |
신규 adapter:inbound:messaging-kafka leaf, manual ack, handler/schema allowlist, bounded concurrency·queue, pause/resume, rebalance·max.poll, retry topic·DLT, InboxStorePort |
WI-…-064 |
| 6 | T | feature-mongo-runtime-baseline-contract |
URI/topology/TLS/credential/timeout/pool 설정, read preference·read/write concern, replica-set 트랜잭션 요구 검증, index manifest·drift·migration, change stream resume token·oplog window | WI-…-065 |
| 7 | T | feature-objectstorage-streaming-lifecycle-contract |
streaming upload/download·range read, head/metadata, checksum 계약, 조건부 write(ETag/version), presigned, multipart start/complete/abort·orphan 회수, SSE/KMS, staged finalization | WI-…-066 |
| 8 | C | feature-cache-strategy-registry-contract |
CacheRegionPort 결과 모델(HIT/MISS/NEGATIVE_HIT/DEGRADED), positive·negative·soft·hard TTL, jitter, single-flight, 전략 registry(cache-aside 기본 / SWR·refresh-ahead·probabilistic·L1L2·분산 stampede opt-in), 메모리 정책 소유 경계(maxmemory-policy = 배포 소유), 대량 무효화(SCAN+UNLINK) |
WI-…-067 |
| 9 | C | feature-edge-rate-limit-policy-registry-contract |
transport-edge 계약(RateLimitRequest→RateLimitDecision), policyId registry, 알고리즘 6종 교체(fixed window / sliding counter / token bucket / sliding log / GCRA / leaky bucket), 키 차원(environment+policyId+tenant?+subjectHash), decision 기반 Retry-After, emergency fallback |
WI-…-068 |
| 10 | C | feature-session-auth-mode-contract |
jwt | redis-session 배타 프로파일, session 생성 정책·CSRF·쿠키 속성·session id 회전, 로그아웃 서버 세션 삭제, serializer 버전·허용 타입, 다중 파드 read/touch/expiry/logout 계약, rolling deploy 호환 |
WI-…-069 |
| 11 | C | feature-idempotency-ownership-protocol-contract |
owner token 기반 claim/renew/complete/release, 실행 lease 와 replay TTL 분리, ACQUIRED|REPLAY|IN_PROGRESS|FINGERPRINT_MISMATCH 결과, guarantee 3종(REQUEST_REPLAY/SAME_STORE_TRANSACTIONAL/EXTERNAL_IDEMPOTENCY), provider 공통 계약 suite |
WI-…-070 |
| 12 | C | feature-fenced-lock-coordination-contract |
FencedLockPort + leader election·semaphore·work claim 분리, fencing counter 와 lease 분리, 보호 자원의 high watermark 거부 규칙, lease-lost 신호, blind DEL·무한 watchdog 금지 |
WI-…-071 |
| 13 | C | feature-outbox-dispatch-mode-contract |
outbox_event(불변) + outbox_delivery(polling 전용 상태) 분리, dispatch-mode: disabled|polling|cdc 배타, aggregate sequence 순서, claim owner token, Debezium Outbox Event Router 매핑, 파티션 보존·정리 조건, 모드 전환 runbook |
WI-…-072 |
| 14 | X | feature-graphql-production-baseline-contract |
operation·field authorization, parser 문자/토큰/깊이 한계, cost/complexity 계측, persisted query allowlist, DataLoader·N+1 계약, cursor connection, error extension 정제, introspection 정책, 스키마 breaking-change gate | WI-…-073 |
| 15 | X | feature-grpc-production-baseline-contract |
proto 생성·버전 규약·호환성 검사, TLS/mTLS, service/method authorization interceptor, 필수 deadline·cancellation 전파, 메시지·메타데이터 크기 한계, retry 허용 status, keepalive, streaming backpressure, health·drain, reflection 정책 | WI-…-074 |
| 16 | X | feature-websocket-cluster-delivery-contract |
인증된 handshake·재인증, destination 별 subscribe/send authorization, origin·frame 한계, heartbeat·idle timeout, bounded executor/queue·drop 정책, sequence/resume, broker relay 또는 durable 이벤트 브리지, 도메인 이벤트 직결 금지 | WI-…-075 |
| 17 | X | feature-read-consistency-query-contract |
ReadConsistency(STRONG/READ_YOUR_WRITES/BOUNDED_STALENESS/EVENTUAL), opaque·서명·버전 cursor, page/sort/filter allowlist, primary/replica 라우팅 근거, projection checkpoint·lag, TransactionPort.inRead() 의 의미 고정 |
WI-…-076 |
| 18 | X | feature-notification-durable-delivery-contract |
best-effort ↔ durable 인터페이스 분리, 논리 template ID·버전·locale, provider quota·bounded retry, dedupe·provider idempotency key, delivery receipt·bounce·suppression, PII-safe 로그·큐 암호화·보존 | WI-…-077 |
4.3 요청 항목과의 대응
| 사용자가 든 항목 | 흡수한 branch |
|---|---|
| redis (전반) | #2 #3 기반 + #8 #9 #10 #11 #12 가 provider 로 소비 |
| rate limit 알고리즘 교체 | #9 |
| Lua 로 동시성 해결 / 기본 제공 함수 | #3 (카탈로그·descriptor·unsafe↔atomic 쌍) + #8 #9 #11 #12 (각 능력의 원자 연산) |
| 캐시 전략·메모리 관리·maxmemory-policy·스탬피드 | #8 (+ 배포 소유 경계는 #2) |
| redis session store / JWT → session 전환 | #10 |
| outbox → CDC | #13 |
| lock → Redis·Kafka 등 DB 밖 | #12 (+ 기존 feature-distributed-lock-contract 는 efficiency mutex 유지) |
| idempotency → DB 밖 | #11 |
| messaging | #4 #5 |
| objectstorage | #7 |
| mongo | #6 |
| graphql / grpc / websocket | #14 #15 #16 |
| query (PostgreSQL 종속) | #17 |
| notification | #18 |
| observability | 각 branch 의 계약에 포함(bounded metric·span·health). 신규 branch 없음 — 기존 feature-metrics-alerting-contract·feature-distributed-tracing-contract 가 owner |
| fileserver / httpclient / jpa / web | 신규 branch 없음. 기존 owner 개정 — §5.3 참조 |
4.4 신규 branch 를 만들지 않는 4개 표면
이 판단은 설계의 일부이며 hub §25 SSOT Owner Map 에 근거를 남긴다.
- fileserver — ca-tmpl 심층 설계 2,467줄이
feature-file-resource-handling-contract의 범위와 정확히 겹친다. 신규 branch 는DUPLICATE_CONTRACT_OWNER다. 기존 branch 를 개정하고 심층 설계를 근거로 pin 한다. - httpclient — 마찬가지로
feature-outbound-http-client-baseline(D=11)이 owner다. total deadline·decorator 순서 정정은 그 branch 의 D-row 개정이다. - jpa/pool — pool sizing·timeout 계층은
feature-database-connection-pool-contract, 실패 분류는feature-persistence-failure-baseline이 owner다. replica 라우팅만 #17 로 이전한다. - web — trusted proxy chain, request deadline, graceful drain, multipart 한계는 각각
feature-security-operational-baseline·feature-runtime-health-lifecycle-contract·feature-boundary-validation-mapping-contract가 이미 소유한다. edge rate limit 만 #9 로 이전한다.
5. 주도권 이전 (Ownership Handover)
사용자 지시: "기존 브랜치와 충돌하는 부분은 주도권을 새로 생성하는 브랜치로 넘기고 동기화."
5.1 이전 대상 8건
| # | 이전 관심사 | 기존 owner | 이전 대상 D-row | 신규 owner |
|---|---|---|---|---|
| H1 | idempotency 저장소·프로토콜·TTL·fingerprint | feature-rate-limit-idempotency-contract |
D2, D3, D6, D7, D8, D9 | #11 feature-idempotency-ownership-protocol-contract |
| H2 | rate limit 키·분산·알고리즘 | feature-rate-limit-idempotency-contract |
D4, D5 | #9 feature-edge-rate-limit-policy-registry-contract |
| H3 | cache 전략(stampede·TTL·직렬화·negative·cluster) | feature-cache-consistency-contract |
D3, D4, D5, D6, D7, D8, D9 | #8 feature-cache-strategy-registry-contract |
| H4 | cache 실패 등급(fail-open 경계) | feature-cachestore-multi-backend-router |
D1, D2, D3 | #8 (실패 등급) + #2 (backend 연결) |
| H5 | correctness lock · fencing | feature-distributed-lock-contract |
D6 | #12 feature-fenced-lock-coordination-contract |
| H6 | outbox 행 모델 · dispatch 전략 | feature-domain-event-outbox-contract |
행 상태 머신 관련 D-row 전량 | #13 feature-outbox-dispatch-mode-contract |
| H7 | session · 인증 모드 | feature-security-operational-baseline |
session 관련 D-row | #10 feature-session-auth-mode-contract |
| H8 | multi-instance 활성화 검증 | feature-background-job-async-contract (+ feature-runtime-health-lifecycle-contract 의 StartupSafetyValidator 언급) |
scheduler/outbox lock 활성화 판정 | #1 feature-capability-provider-selection-contract |
2026-07-28 controller 메시지에서는 충돌 6건으로 보고했으나, 코드 대조 결과 H4(cache 실패 등급)와 H8(multi-instance 검증)이 추가되어 8건이다.
5.2 이전 절차 (branch 당 6단계)
rules/consistency-contract.md 의 Single-Owner + Reference-Only 를 그대로 적용한다.
- 신규 branch 가 owner 로 D-row 를 재작성한다. 이전이 아니라 재판정이다 — 기존 결정을 그대로 복사하지 않고, ca-tmpl 심층 설계와 코드 사실을 근거로 다시 판단한다.
- 기존 branch 의 해당 D-row 는 본문을 제거하고 포인터만 남긴다. 형식:
| D<n> | **superseded → [[raw/branch-notes/<신규 slug>]] D<m>** — <1줄 요약> | … |. 재진술 금지. - 기존 branch 의 §구현 가이드에서 이관 대상 cell 을 제거한다. CLAUDE.md §15.5 R3
OUT_OF_BRANCH_SCOPE. 이관 history 는 해당 branch 의 §Audit & Findings 에 보존한다. - hub §25 SSOT Owner Map 의 owner 열을 교체하고, consumers/producers 열에 기존 branch 를 남긴다.
- 기존 branch 의
status_label을 갱신한다. 관심사를 전량 이전한 branch(H1+H2 로 사실상 전부 이전되는feature-rate-limit-idempotency-contract)는delegated로 표기하고, 부분 이전 branch 는in-progress를 유지한다. /sync를 실행해 역참조를 전파한다.wiki_consistency_check.py가 owner 교체 후 남은 재진술을 잡는다.
5.3 이전이 아니라 개정만 하는 3건
주도권은 그대로 두고 내용만 코드 사실에 맞춘다.
| branch | 개정 사유 | 근거 |
|---|---|---|
feature-outbound-http-client-baseline |
globalCallTimeout 이 실행 중 호출을 취소하지 않고 다음 재시도 진입 여부만 판정한다. decorator 순서가 문서와 다르다 |
ca-tmpl httpclient 심층 설계 §3, §15, §20 |
feature-file-resource-handling-contract |
전체 파일 버퍼링 + 비원자 덮어쓰기. staged publish·fsync·rename 프로토콜로 개정 | ca-tmpl fileserver 심층 설계 §11, §12 |
feature-integration-adapter-templates |
"optional adapter 는 disabled env 가 기본" 을 capability descriptor 모델로 대체 | #1 이 활성화 SSOT 를 소유하게 되므로 template branch 는 소비자로 강등 |
6. Capability Readiness Model (hub 신설 §)
#1 branch 가 owner다. hub 에 §36 Capability Readiness Model 로 신설한다(§35 뒤에 붙인다 — 중간 삽입은 기존 절 참조를 어긋나게 한다).
6.1 Readiness 4단계
| 등급 | 의미 | 필요한 증거 | CLAUDE.md §6 등급 대응 |
|---|---|---|---|
R0 Contract |
타입·seam·placeholder 만 존재 | unit test + architecture boundary test | planned 또는 documented-only |
R1 Local |
단일 로컬 프로세스/로컬 서비스에서 동작 | 집중 통합 test + 한계 문서화 | actually-implemented (partial) |
R2 Production baseline |
실제 provider, 안전한 설정, 실패 의미, health, metric, 보안, graceful lifecycle | provider 계약 test + 실서비스 통합 + 동시성/실패 test + runbook | locally-verified |
R3 Scale/HA |
cluster·failover·rolling upgrade 검증 | topology test + 호환 매트릭스 + 복구/용량 runbook | prod-verified |
금지: R0 seam 을 R2 provider 로 표기하는 것. 문서·startup 진단·capability card 모두에 적용된다. 이 금지는 hub §2 "하지 않는 것" 에 추가한다.
6.2 Capability card 필수 항목
각 R2 이상 capability 는 아래를 갖춘 card 를 branch-note 에 보유한다.
owner 모듈 · semantic port
provider ID 와 readiness 등급
보증(guarantee)과 명시적 비보증(non-guarantee)
기본 실패 정책과 허용되는 override
필요한 토폴로지 · 영속/축출 정책
설정 키와 secret
liveness/readiness 영향
bounded cardinality metric 과 trace span
적합/부적합 지침 · 비용 모델 · 자원 한계
흔한 unsafe recipe · 그것이 만드는 경합/실패 · 안전한 대체
집중/통합/실패 test 명령
rolling upgrade · 복구 노트
runbook 링크
"흔한 unsafe recipe ↔ 안전한 대체" 항목이 사용자 요구의 교육적 부분을 계약으로 고정하는 장치다. provider 는 card 가 주장하는 동시성·실패 의미를 증명하는 경합 test 를 통과해야 그 보증을 card 에 쓸 수 있다.
6.3 활성화 SSOT
provider: disabled | <provider-id> # 선택 가능한 capability
dispatch-mode: disabled | polling | cdc # 모드 축이 있는 capability (outbox)
enabled: true | false # provider·mode 축이 없는 leaf 에만
규범:
matchIfMissing=true, classpath 존재, local provider 기본값이 production capability 를 활성화해서는 안 된다.- 미사용 capability 는 연결·백그라운드 워커·스키마·암묵적 런타임 동작을 만들지 않는다.
- legacy flag 를 한시적으로 유지하는 경우, SSOT 와의 불일치는 startup 오류다.
- 활성화 판정은 bean 이름이 아니라 typed descriptor 로 한다(§13 드리프트 D3 참조).
7. 결정 레지스트리 신규 15행
hub 는 §25(legacy default)와 §6.1(DEC-…, 현행 SSOT)을 쌍으로 유지한다. 신규 결정은 §6.1 에 넣고, §25 Blocking Defaults 에는 활성화 자세 1행만 추가한다.
Summary 셀은 소비 branch 의 상속 표와 문자열이 정확히 일치해야 한다. 아래가 SSOT 다. 조사 앞 공백 없음(기존 §6.1 행 표기와 동일).
| Decision ID | Rev | Domain | Decision Summary | Status | Evidence |
|---|---|---|---|---|---|
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CAPABILITY-SELECTION-001 |
1 | capability-selection |
production capability는 available by default이고 inactive by default이며 활성화는 typed descriptor 기반 provider 선택으로만 이뤄진다 | active |
ca-tmpl platform 설계 §7 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CAPABILITY-READINESS-001 |
1 | capability-readiness |
모든 capability는 R0~R3 readiness 등급과 capability card를 가지며 R0 seam을 R2 provider로 표기하지 않는다 | active |
ca-tmpl platform 설계 §6 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-REDIS-ROLE-001 |
1 | redis-role |
Redis는 cache·coordination·session 3개 role로 분리하고 key prefix나 DB number를 격리 수단으로 쓰지 않는다 | active |
ca-tmpl redis 설계 §10 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-REDIS-PROGRAM-001 |
1 | redis-program |
다단계 read-decide-write는 버전 있는 atomic program으로만 제공하고 각 helper는 unsafe recipe와 안전한 대체를 쌍으로 문서화하며 경합 test로 증명한다 | active |
ca-tmpl redis 설계 §13, §14 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CACHE-STRATEGY-001 |
1 | cache-strategy |
cache 기본 전략은 cache-aside와 after-commit 무효화이고 SWR·refresh-ahead·L1L2·분산 stampede 억제는 opt-in이며 maxmemory-policy는 배포 설정이 소유한다 | conditional-default |
ca-tmpl platform 설계 §8.4 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-CACHE-FAILURE-001 |
1 | cache-failure |
optional cache의 backend 실패는 fail open이고 codec·프로그래밍 결함은 해당 연산을 fail closed로 처리하며 cache miss로 위장하지 않는다 | active |
ca-tmpl platform 설계 §7 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RATE-LIMIT-ALGORITHM-001 |
1 | rate-limit-algorithm |
rate limit은 policyId 단위로 알고리즘을 선택하고 fixed window·sliding counter·token bucket을 기본 제공하며 sliding log·GCRA·leaky bucket은 opt-in이다 | conditional-default |
ca-tmpl platform 설계 §8.5 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-SESSION-MODE-001 |
1 | session-mode |
인증 모드는 jwt와 redis-session이 배타이고 jwt가 default이며 redis-session은 전용 session role과 CSRF·쿠키·회전·다중 파드 계약을 동반한다 | conditional-default |
ca-tmpl platform 설계 §8.6 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-IDEMPOTENCY-OWNERSHIP-001 |
1 | idempotency-ownership |
idempotency는 owner token 기반 claim·renew·complete·release 프로토콜을 쓰고 실행 lease와 replay TTL을 분리하며 보증 등급을 명시한다 | active |
ca-tmpl platform 설계 §9 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-LOCK-FENCING-001 |
1 | lock-fencing |
DistributedLockPort는 efficiency mutex로 고정하고 correctness가 필요한 경로는 별도 FencedLockPort와 보호 자원의 high watermark 거부로만 보장한다 | active |
ca-tmpl platform 설계 §10 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-OUTBOX-DISPATCH-001 |
1 | outbox-dispatch |
outbox는 불변 event 행과 polling 전용 delivery 행을 분리하고 dispatch 모드는 polling과 cdc가 배타이며 append는 업무 write와 같은 트랜잭션에 남는다 | active |
ca-tmpl platform 설계 §11.1, §11.2 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-DELIVERY-SEMANTICS-001 |
1 | delivery-semantics |
end-to-end 메시징 보증은 at-least-once 전달과 멱등 consumer·inbox로 표현하고 DB와 broker를 걸친 exactly-once를 주장하지 않는다 | active |
ca-tmpl platform 설계 §11.4 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MONGO-BASELINE-001 |
1 | mongo-baseline |
MongoDB는 read preference·read concern·write concern을 명시하고 트랜잭션과 change stream은 replica-set 요건 검증을 통과할 때만 활성화한다 | conditional-default |
ca-tmpl platform 설계 §12.3 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-READ-CONSISTENCY-001 |
1 | read-consistency |
조회는 ReadConsistency를 명시하고 replica 라우팅은 그 정책과 요청 문맥으로 결정하며 TransactionPort.inRead는 replica 사용을 뜻하지 않는다 | active |
ca-tmpl platform 설계 §12.1 |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-OPTIONAL-ADAPTER-001 |
1 | stack-optional-adapter |
optional adapter stack은 축마다 구현체 하나를 고정하고 core stack과 분리된 matrix로 관리한다 | active |
§34 Optional Adapter Stack Matrix |
Owner 열은 모두 [[raw/project-notes/ca-skeleton-operational-contract]] 다(§6.1 기존 모든 행과 동일).
7.1 §25 Blocking Defaults 신규 1행
| 항목 | 기본 결정 | SSOT branch | 실패 조건 |
|---|---|---|---|
| capability 활성화 | production capability 는 모듈로 제공하되 기본 비활성. 활성화는 typed descriptor 기반 provider 선택으로만 | feature-capability-provider-selection-contract |
classpath 존재·matchIfMissing·local provider 기본값으로 capability 가 활성화되거나, bean 이름 존재만으로 보증을 주장 |
8. Work Item Registry 신규 18행 (hub §8.0)
Applies Decisions 는 §7 의 신규 DEC 와 기존 DEC 를 섞어 pin 한다. Dependencies 는 Tier 순서(P → T → C → X)를 따른다.
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies |
|---|---|---|---|---|
WI-…-060 |
feature-capability-provider-selection-contract |
capability descriptor·provider 선택·readiness 등급 검증 test 가 통과하고 bean 이름 기반 판정이 제거된다 | CAPABILITY-SELECTION-001@1, CAPABILITY-READINESS-001@1 |
- |
WI-…-061 |
feature-redis-runtime-role-isolation-contract |
3-role 연결·키·코덱·health 계약 test 와 incompatible co-location 거부 test 가 통과한다 | REDIS-ROLE-001@1, STACK-OPTIONAL-ADAPTER-001@1 |
060 |
WI-…-062 |
feature-redis-atomic-program-catalog-contract |
program descriptor·golden vector·NOSCRIPT 재적재 test 와 unsafe recipe 경합 재현 test 가 통과한다 | REDIS-PROGRAM-001@1 |
061 |
WI-…-063 |
feature-kafka-producer-runtime-contract |
acknowledgement·delivery timeout·순서·보안 설정 계약 test 가 통과한다 | DELIVERY-SEMANTICS-001@1, EVENT-BROKER-001@1 |
060 |
WI-…-064 |
feature-kafka-consumer-inbox-contract |
inbound leaf 등록·manual ack·rebalance·DLT·inbox 멱등 계약 test 가 통과한다 | DELIVERY-SEMANTICS-001@1, IDEMPOTENCY-OWNERSHIP-001@1 |
063, 070 |
WI-…-065 |
feature-mongo-runtime-baseline-contract |
concern·index manifest·replica-set 트랜잭션·change stream checkpoint test 가 통과한다 | MONGO-BASELINE-001@1, STACK-OPTIONAL-ADAPTER-001@1 |
060 |
WI-…-066 |
feature-objectstorage-streaming-lifecycle-contract |
streaming·multipart·presigned·checksum·orphan 회수 계약 test 가 통과한다 | CAPABILITY-READINESS-001@1, MODULE-LAYOUT-001@1 |
060 |
WI-…-067 |
feature-cache-strategy-registry-contract |
전략 registry·TTL 4종·single-flight·실패 등급 분리 test 가 통과한다 | CACHE-STRATEGY-001@1, CACHE-FAILURE-001@1 |
061 |
WI-…-068 |
feature-edge-rate-limit-policy-registry-contract |
알고리즘 6종 교체·policy registry·decision 기반 Retry-After·fallback test 가 통과한다 | RATE-LIMIT-ALGORITHM-001@1, RATE-LIMIT-001@1 |
062 |
WI-…-069 |
feature-session-auth-mode-contract |
배타 인증 모드·CSRF·쿠키·회전·다중 파드 세션 계약 test 가 통과한다 | SESSION-MODE-001@1 |
061 |
WI-…-070 |
feature-idempotency-ownership-protocol-contract |
owner token 프로토콜·lease 분리·보증 등급별 provider 공통 계약 test 가 통과한다 | IDEMPOTENCY-OWNERSHIP-001@1, IDEMPOTENCY-001@1 |
060 |
WI-…-071 |
feature-fenced-lock-coordination-contract |
fencing token 단조성·high watermark 거부·lease-lost 신호 test 가 통과한다 | LOCK-FENCING-001@1, SCHEDULER-LOCK-001@1 |
060 |
WI-…-072 |
feature-outbox-dispatch-mode-contract |
event/delivery 분리·모드 배타·CDC 라우팅·파티션 정리 조건 test 가 통과한다 | OUTBOX-DISPATCH-001@1, DELIVERY-SEMANTICS-001@1 |
063 |
WI-…-073 |
feature-graphql-production-baseline-contract |
깊이·복잡도·persisted query·field authorization·스키마 gate test 가 통과한다 | CAPABILITY-READINESS-001@1, API-VERSIONING-001@1 |
060 |
WI-…-074 |
feature-grpc-production-baseline-contract |
proto 호환·TLS/mTLS·deadline·크기 한계·drain test 가 통과한다 | CAPABILITY-READINESS-001@1, STACK-OPTIONAL-ADAPTER-001@1 |
060 |
WI-…-075 |
feature-websocket-cluster-delivery-contract |
handshake 인증·destination authorization·bounded queue·cluster 전달 test 가 통과한다 | CAPABILITY-READINESS-001@1 |
060, 063 |
WI-…-076 |
feature-read-consistency-query-contract |
ReadConsistency·cursor·replica 라우팅·projection lag 계약 test 가 통과한다 | READ-CONSISTENCY-001@1, TRANSACTION-001@1 |
060 |
WI-…-077 |
feature-notification-durable-delivery-contract |
durable/best-effort 분리·template 버전·receipt·dedupe 계약 test 가 통과한다 | CAPABILITY-READINESS-001@1, RESILIENCE-001@1 |
072 |
ID 는 문서 가독성을 위해
WI-…-0NN으로 축약했다. hub 에 옮길 때 전체 형식WI-CA-SKELETON-OPERATIONAL-CONTRACT-0NN로 쓴다.Applies Decisions의DEC-…접두사도 마찬가지다.Status는 전 행planned.
9. 능력별 실패 정책 매트릭스 (hub §11 대체)
hub §11 의 ### Optional Adapters 절(3줄)을 아래 표로 대체한다. 전역 fail-open 이나 전역 fail-closed 는 없다.
| Capability | 기본 실패 정책 | 근거 |
|---|---|---|
| optional cache (backend 실패) | fail open — source 로 fallback, degraded 결과와 metric 노출 | 캐시 부재는 정확성 손실이 아니다 |
| cache codec·프로그래밍 결함 | fail closed (해당 연산) — 손상 항목 축출/격리, miss 로 위장 금지 | 결함을 miss 로 숨기면 영구 오염 |
| security session store | fail closed — 인증 상태를 지어내지 않는다 | 인증은 추측 대상이 아니다 |
| keyed mutation idempotency | fail closed | 중복 write 방지가 목적 |
| strict rate limit (남용·비용 경계) | fail closed 또는 의도적으로 제한된 local emergency limiter | 무제한 통과는 경계의 부재 |
| availability 지향 rate limit | 명시적 local emergency fallback, 조용한 무제한 통과 금지 | 저하는 허용, 은폐는 불가 |
| efficiency lock | 선언된 use case 정책에 따라 실패 또는 계속 | 중복 작업 방지가 목적 |
| fenced correctness lock | 획득·lease 상실 시 보호 작업 중단 | 중복 결과 차단이 목적 |
| outbox append | 업무 트랜잭션 롤백 | append 는 업무 write 와 원자 |
| outbox dispatcher 장애 | write 는 계속 축적, lag·backlog 알림 | 전달 지연은 업무 차단 사유가 아니다 |
| best-effort notification·message | 명시적 fail open | 이름과 계약에 best-effort 명시 |
| durable notification·message | outbox/inbox + 재시도 + 종단 실패 경로 | 유실 불가 경로 |
hub §7 Non-Retryable 기준의 cache unavailable when degradation is allowed 행은 이 표의 첫 두 행으로 세분화된다.
10. Optional Adapter Stack Matrix (hub §34)
§34 의 core Stack Matrix 는 그대로 유지하고(단 §13 D1 정정 적용), 그 아래에 새 표를 붙인다. "축마다 구현체 하나" 라는 §34 의 단일 stack 정신을 optional 축에도 적용한다.
| Capability 축 | 선택 | 버전 | 상태 | 비고 |
|---|---|---|---|---|
| Redis client | Lettuce (Spring Data Redis) | Spring Boot BOM transitive | 도입 예정 | LettuceRedisRuntime 이 이미 존재하나 SDK 의존성 추가는 Phase 1 |
| Session store | Spring Session Data Redis | Spring Boot BOM transitive | 도입 예정 | redis-session 모드 활성 시에만 |
| Kafka client | spring-kafka | Spring Boot BOM transitive | 도입 예정 | producer 먼저, consumer 는 신규 inbound leaf |
| CDC | Debezium + Kafka Connect | 배포 자산 | 도입 예정 | 애플리케이션 의존성 아님. connector 는 외부 배포 |
| MongoDB | Spring Data MongoDB | Spring Boot BOM transitive | opt-in 설정만 존재 | replica-set 요건 검증 필요 |
| Object storage | AWS SDK v2 | 2.30.0 (ext.awsSdkVersion) |
존재 | 모듈 scope BOM import |
| gRPC | io.grpc + protobuf | 1.68.1 (ext.grpcVersion) |
존재 | Boot BOM 미관리 → 모듈 scope BOM |
| GraphQL | Spring for GraphQL | Spring Boot BOM transitive | 최소 구현 존재 | production gate 미도입 |
| Resilience | Resilience4j | Spring Boot BOM 외 | 존재 | core 결정(RESILIENCE-001) 유지 |
| 통합 test | Testcontainers (+ Toxiproxy) | test scope | 부분 존재 | TESTCONTAINERS-001 정책 상속 |
Out of Stack (optional 축) — 명시적 거부:
- lock/coordination: Redisson 을 lock provider 기본으로 삼지 않는다. Spring Integration
LockRegistry계열(JDBC/Redis)이 기본이며 Redisson 은 대안. ZooKeeper·etcd·Consul 은 범위 밖. - messaging: RabbitMQ·Pulsar·ActiveMQ 는 범위 밖. Redis Streams 는 Kafka 대체재가 아니라 후속 소규모 provider 후보.
- cache: Ehcache·Hazelcast·Infinispan 은 범위 밖. L1 은 Caffeine.
- search: Elasticsearch·OpenSearch 는 범위 밖(hub §35 F 영역 유지).
§34 의 "Stack 변경 절차" 4단계는 optional matrix 에도 그대로 적용된다.
11. hub 델타 요약
| § | 변경 | 성격 |
|---|---|---|
| §1 목표 | 마지막 문단에 capability platform 자세 1문단 추가 | 개정 |
| §2 하지 않는 것 | (a) "Kafka/Redis/Slack/Google Email을 기본 dependency로 무겁게 탑재하지 않음" → "available by default, inactive by default. 미사용 capability 는 연결·워커·스키마·런타임 부작용 0" 로 개정 (b) "R0 seam 을 R2 로 표기" 금지 1행 추가 (c) §3.2 Out of scope 6항 추가 | 개정 |
| §7 | cache unavailable 행을 §9 매트릭스로 위임하는 포인터 추가 |
개정 |
| §11 | ### Optional Adapters 3줄 → §9 실패정책 매트릭스로 대체 |
대체 |
| §25 Blocking Defaults | 1행 추가(§7.1) | 추가 |
| §25 SSOT Owner Map | 신규 18행 + 이전 8건 owner 교체 | 추가·개정 |
| §6.1 | DEC 신규 15행(§7) | 추가 |
| §8.0 | WI 신규 18행(§8) + 주도권 이전에 따른 기존 7행 갱신(008, 016, 024, 025, 038, 049, 052) |
추가·개정 |
| §24 | branch 링크 18개 추가 | 추가 |
| §31.1 | GENERATED 목록 + 수기 목록에 18개 추가, 총계 갱신 | 추가 |
| §34 | core matrix 1행 정정(§13 D1) + Optional Adapter Stack Matrix 신설(§10) | 정정·추가 |
| §35 | 신규 18행 + 상태 승급 3건 + (없음) 해소 4건 + 분포 요약 갱신 |
추가·개정 |
| §36 신설 | Capability Readiness Model + capability card(§6) | 신설 |
| §29 | ca-tmpl 심층 설계 4건을 근거 인덱스에 등록 | 추가 |
| 헤더(25번 줄) | 경로 표기 규약 정정(§13 D2) | 정정 |
| §11 금지 목록 | "R0 seam 을 R2 로 표기" 금지 1행 추가 | 추가 |
11.1 §35 커버리지 체크리스트 델타
상태 승급 3건 (코드 대조 결과):
| 기존 행 | 현재 표기 | 정정 표기 | 근거 |
|---|---|---|---|
| Cache consistency | [~] |
[*] |
adapter/outbound/cache-redis 43개 파일(test 13) |
| Outbound HTTP resilience | [~] |
[*] |
adapter/outbound/httpclient 38개 파일(test 15) |
| File / Resource handling | [~] |
[*] |
adapter/outbound/fileserver 16개 파일(test 5) |
(없음) 해소 4건: MongoDB / GraphQL / gRPC / WebSocket 은 각각 신규 branch row 로 대체된다.
신규 18행: §4.2 의 branch 를 [ ](미시작) 로 등록하되, Redis foundation 2건은 [*](부분 구현) — Lua executor·catalog·descriptor·atomic primitives·key builder 가 이미 존재한다.
12. 신규 branch 생성 순서와 방법
18개를 한 세션에서 채우지 않는다. 각 branch 는 /branch <slug> 로 스캐폴딩한 뒤 /branch-spec 이 별도 세션에서 D-row 를 채운다.
1차 (플랫폼 — 나머지 전부가 이 결정을 상속): #1
2차 (기술 런타임 — 능력의 provider): #2 #3 → #4 #5, #6, #7
3차 (능력 계약 — 주도권 이전 대상 포함): #8 #9 #10 #11 #12 #13
4차 (전송·조회): #14 #15 #16 #17 #18
각 차수 진입 조건:
- 1차 완료 전 2차 착수 금지. capability descriptor 모양이 확정되지 않으면 모든 provider branch 가 활성화 계약을 각자 발명한다.
- #2 완료 전 #3 착수 금지. key model·codec 이 program descriptor 의 입력이다.
- 3차 각 branch 착수 시 §5.2 이전 절차 6단계를 함께 수행한다. 신규 D-row 작성과 기존 branch 포인터 치환을 같은 작업 단위로 묶지 않으면 두 owner 가 공존하는 기간이 생긴다.
각 branch-note 는 templates/branch-note-template.md 를 따르고, hub §24 의 Work Item Contract 7필드(Decision / Allowed / Forbidden / Required registry update / Required contract test / Failure condition / Canonical extraction target)를 채운다.
13. 드리프트 정정 (코드 근거)
이 절의 항목은 설계 제안이 아니라 ca-tmpl 코드를 읽어 확인한 사실이다. hub 를 코드에 맞춰 정정한다.
| # | 드리프트 | hub 현재 값 | 코드 사실 | 근거 파일 |
|---|---|---|---|---|
| D1 | Spring Boot 버전 | §34 Stack Matrix Framework = Spring Boot 3.5.14, §6.1 DEC-…-STACK-FRAMEWORK-001 동일 |
4.0.0 |
src/build.gradle:8 — id 'org.springframework.boot' version '4.0.0' apply false |
| D2 | ca-tmpl 경로 | 헤더 25번 줄 /home/donghyeon/workspace/ca-tmpl/ |
/home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template/ |
저장소 위치 확인(2026-07-28) |
| D3 | multi-instance 활성화 판정 | §6.1 SCHEDULER-LOCK-001 이 "DB advisory lock 사용" 만 규정 |
bean 이름 문자열 목록으로 판정 — distributedLockProvider, cacheStampedeProtection, outboxLeaderElection |
src/app-bootstrap/.../runtime/StartupSafetyValidator.java:23-27 |
D1 의 파급이 크다. §34 는 "모든 branch 가 상속하는 stack SSOT" 이고 §34 의 Stack 변경 절차는 sibling branch 영향 평가를 요구한다. 정정 시 grep -rn "project §34" raw/branch-notes/feature-*.md 로 영향 branch 를 식별한다. Spring Boot 4.0.0 은 3.5.x 대비 baseline 이 달라지므로, 각 branch 의 프레임워크 의존 결정(특히 security·web·data 계열)은 재평가 대상이다.
D2 는 llm-wiki 전역 35개 파일이 옛 경로를 참조한다(grep -rn "workspace/ca-tmpl" --include="*.md"). 본 설계의 실행 범위는 hub 1개 파일로 한정하고, 나머지는 별도 정리 작업으로 둔다 — 일괄 치환은 wiki/log.md 의 과거 실행 기록까지 바꾸게 되어 이력 왜곡이다.
D3 는 #1 branch 의 첫 번째 D-row 가 될 사실이다. StartupSafetyValidator 가 bean 이름만 확인하므로, 그 이름을 가진 아무 bean 이나 존재하면 multi-instance 안전으로 판정된다.
13.1 이번에 정정하지 않는 것
| 항목 | 이유 |
|---|---|
| llm-wiki ↔ llm-wiki-private 미러 분기 | ca-tmpl AGENTS.md:72,79 는 branch-note 캡처 대상을 llm-wiki-private 로 지정한다. 두 저장소의 hub 파일은 2026-07-28 기준 동일하다. 어느 쪽을 정본으로 둘지는 사용자 결정 사항이며, 본 설계는 작업이 지시된 llm-wiki 를 대상으로 한다. 두 저장소 동기화는 별도 작업으로 남긴다. |
wiki/projects/ca-tmpl/ 16개 canonical 문서 |
본 설계는 raw layer(project note + branch note)만 다룬다. canonical 승급은 branch 완료 후 /ingest 경로다. |
14. 근거 자료
14.1 프로젝트 내부 설계 (등급: internal-design-doc)
공식 문서 아님. 인용 시 "ca-tmpl 프로젝트 설계 결정" 으로 표기하고 외부 best practice 로 격상하지 않는다.
| 문서 | 본 설계에서 인용한 절 |
|---|---|
ca-tmpl 2026-07-26-production-capability-platform-design.md |
§3(현재 상태), §6(readiness), §7(provider 선택·실패정책), §8(Redis), §9(idempotency), §10(lock), §11(outbox/Kafka), §12(query/persistence), §13(outbound), §14(inbound), §15(관측) |
ca-tmpl 2026-07-26-redis-production-capability-design.md |
§10(role), §11(key), §12(payload), §13(program registry), §14(command 의미), §15(cache contract) |
ca-tmpl 2026-07-27-httpclient-production-capability-design.md |
§3, §15(total deadline), §17~§20(retry/CB/순서) |
ca-tmpl 2026-07-26-fileserver-production-capability-design.md |
§11(상태 머신), §12(staged publish), §13(guarantee) |
14.2 코드 사실 (등급: actually-implemented)
| 사실 | 파일 |
|---|---|
| 모듈 19개 고정 검증 | src/settings.gradle (expectedModuleCount = 19), src/config/architecture/modules.json |
RateLimitAlgorithm 이 FIXED_WINDOW 단일, transport edge 소재 |
src/adapter/inbound/web/.../ratelimit/RateLimitAlgorithm.java |
CacheStore 가 Optional<String> get / void put 만 노출 |
src/adapter/outbound/cache-redis/.../CacheStore.java |
CacheRegionPort 등 신규 cache 계약이 이미 도입됨 |
src/application-core/.../application/cache/ (7개 파일) |
IdempotencyStorePort 에 owner token 없음 (complete(scope, …) / discard(scope)) |
src/application-core/.../idempotency/IdempotencyStorePort.java:24-42 |
DistributedLockPort.tryAcquire(key, waitTime, leaseTtl) — fencing 없음 |
src/application-core/.../lock/DistributedLockPort.java:29 |
outbox 행이 PENDING/IN_FLIGHT/PUBLISHED/DEAD 로 가변 |
src/application-core/.../outbox/OutboxEventStatus.java |
ObjectStoragePort 가 byte[] 전용 |
src/application-core/.../storage/ObjectStoragePort.java:29,38 |
관리 엔드포인트가 STATELESS + CSRF 비활성 |
src/app-bootstrap/.../management/security/ManagementSecurityConfig.java:26-27 |
| Redis Lua 실행 기반 존재 | src/adapter/outbound/cache-redis/.../RedisLuaProgramExecutor.java, RedisProgramCatalog.java, RedisProgramDescriptor.java, RedisAtomicPrimitives.java |
14.3 외부 공식 문서 (branch-note Sources 시드)
각 branch 의 /branch-spec 단계에서 verbatim 인용과 함께 raw/official-docs/ 로 수집한다. 본 설계는 링크만 예약하며, 아직 인용 근거로 쓰지 않는다.
| branch | 1차 수집 대상 |
|---|---|
| #2 #3 | Redis 공식 — eval-intro(스크립팅·원자성), key eviction, distributed locks patterns / Spring Data Redis scripting |
| #8 | Redis eviction / AWS ElastiCache caching strategies(기존 raw/official-docs/cache-aside-vs-write-through-aws.md 재사용) |
| #9 | Redis rate-limiter use case |
| #10 | Spring Session Redis API |
| #12 | Redis distributed locks(fencing 서술) / 기존 raw/official-docs/cache-redisson-rlock-vs-setnx.md |
| #13 | Debezium Outbox Event Router |
| #4 #5 | Kafka design(delivery semantics·transactions), producer configs |
| #6 | MongoDB read concern / write concern / change streams |
| #7 | Amazon S3 object integrity |
| #14 | GraphQL Java limits |
| #15 | gRPC deadlines / retry |
| #16 | Spring WebSocket broker relay |
| #17 | (기존 feature-application-query-bypass-contract Sources 상속) |
15. 검증과 게이트
15.1 본 설계 실행의 완료 조건
hub 편집이 아래를 모두 만족해야 완료다.
/lint통과 — canonical 우회·status 미달 파생·registry 누락 검사/sync통과 — 이전 8건의 owner 교체 후 재진술 0- hub §35 의 분포 요약 숫자가 실제 row 수와 일치
- 신규 18개 slug 가 §8.0 / §24 / §31.1 / §35 / §25 다섯 곳에 모두 등장
- §7 의 Decision Summary 문자열이 §6.1 과 각 branch-note 상속 표에서 완전 일치(조사 앞 공백 포함)
- §13 드리프트 3건이 코드 근거 링크와 함께 정정됨
15.2 각 신규 branch 의 승급 게이트
hub §26 Universal Acceptance Gate 10문항 + §23 promotion contract 6필드를 그대로 적용한다. 추가로 capability branch 는 아래 2개를 더 만족해야 documented-only 를 벗어난다.
- capability card 의 보증(guarantee)과 비보증(non-guarantee)이 둘 다 작성되어 있다.
- card 가 주장하는 동시성·실패 의미를 증명하는 경합 test 이름이 명시되어 있다.
16. 미해결 질문
| # | 질문 | 영향 | 처리 |
|---|---|---|---|
| Q1 | llm-wiki 와 llm-wiki-private 중 어느 쪽이 정본인가 | 18개 branch-note 를 어디에 만들지 | 사용자 결정 필요. 본 설계는 llm-wiki 기준 |
| Q2 | Spring Boot 4.0.0 전환이 기존 46개 branch 결정에 미치는 영향 | security·web·data 계열 결정 재평가 | §13 D1 정정 후 grep -rn "project §34" 로 영향 평가 별도 수행 |
| Q3 | feature-rate-limit-idempotency-contract 를 delegated 로 두는가, 폐기하는가 |
WI-016 의 처리 | 본 설계는 delegated 유지(WI-016 은 두 신규 WI 로 대체 표기). 폐기는 branch 이력 손실 |
| Q4 | Kafka inbound leaf 추가로 모듈이 19 → 20 이 된다 | settings.gradle:23 의 expectedModuleCount = 19 하드코딩 |
#5 branch 의 D-row 로 처리. 모듈 registry migration 절차 필요 |
| Q5 | adapter:outbound:cache-redis 모듈명이 Redis 기술 전반을 담게 된다 |
이름과 책임 불일치 | ca-tmpl 설계가 rename 을 별도 승인 대상으로 유예. #2 branch 가 이름 유지 여부를 결정 |