27 KiB
title, source_type, status, confidence, tags, related_projects, last_reviewed
| title | source_type | status | confidence | tags | related_projects | last_reviewed | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ca-tmpl - Data Layer Baseline 결정 (Persistence + Cache + Outbound) | project | verified | high |
|
|
2026-07-02 |
UPDATE 2026-06-15 (스코프: Outbound HTTP 한정): 본 문서가 2026-05-22 에 기록한 "Phase C2 미진입 / 코드 없음" 전제는 Outbound HTTP 영역에 한해 더 이상 사실이 아니다.
src/adapter-outbound/.../httpclient/에 outbound HTTP 클라이언트가 구현 + 로컬 테스트로 검증되어 있다(아래 "Outbound HTTP Client" 절). 이 절은 wiki/explainer/adapter-outbound 가 코드 사실의 근거로 인용한다.UPDATE 2026-07-02:
/home/donghyeon/workspace/ca-tmpl/src대조 및./gradlew check통과로 이 문서를verified로 승격했다. Outbound HTTP, lower-layer cache SPI/router/fail-open, idempotency/outbox persistence, OSIV/Hikari startup guard는 구현·로컬 검증됐다. 단 본 문서의 원래 "Cache 결정"(cache-aside + Caffeine + Redisson 분산 lock + after-commit invalidation)은 일부가 lower-layer cache SPI와 다른 층이므로 구현 범위를 분리해서 읽어야 한다.
ca-tmpl - Data Layer Baseline 결정 (Persistence + Cache + Outbound)
Layer:
wiki/projects/— ca-tmpl skeleton의 data layer baseline 결정 사실 기록. 일반 개념·근거는 wiki/concepts/data-layer-persistence-cache-outbound에 둠. 본 문서는 "내 프로젝트에서 무엇을 결정했고, 어디까지 진행되었는가"만 다룬다.
프로젝트 컨텍스트
ca-tmpl은 Clean Architecture 기반 백엔드 skeleton 템플릿이다. 본 문서는 그 안에서 data layer baseline 3축(Persistence / Cache / Outbound HTTP)을 어떻게 결정했는지를 기록한다.
- 결정한 baseline:
- Persistence: SQLState 9-row classifier matrix + Hibernate OSIV off + HikariCP pool wait/exhaustion alert + read replica lag threshold (raw/project-notes/ca-skeleton-operational-contract §6).
- Cache: cache-aside default + Caffeine local lock(single-instance) + Redisson
RLockdistributed mutex(multi-instance HPA) + after-commit invalidation + eventual consistency window 5s (canonical §11). - Outbound HTTP: Spring RestClient baseline + Resilience4j CircuitBreaker · TimeLimiter · Retry + timeout connect 2s / read 5s / global 10s + retry default disabled (canonical §11, §29 G-C).
- 진행 단계: C2 부분 구현 + 로컬 검증 완료. 본 문서의 범위는 구현된 outbound/cache/persistence slice와 아직 planned로 남은 cache-aside/replica-lag/운영 tuning 경계를 분리한다.
실제 구현 내용 (actually-implemented)
Persistence / Cache / Outbound 일부 구현됨. SQLState classifier와 read-replica lag metric은 별도 확인이 필요하지만, idempotency/outbox persistence adapter와 Flyway migration, OSIV/Hikari startup guard, lower-layer cache SPI/router/fail-open/Redis adapter, outbound HTTP baseline은 코드에 존재한다.
Outbound HTTP Client (actually-implemented)
모듈 src/adapter-outbound/.../httpclient/, 단일 업스트림 의존성 1개당 인스턴스 1개. 진입 클래스 OutboundHttpClient.
입출력 (공개 API)
| 메서드 | 입력 | 반환 | 비고 |
|---|---|---|---|
static OutboundHttpClient baseline(name, baseUrl, settings, guard, resilience, retryPolicy, errorMapper, logger) |
협력자 8개 | OutboundHttpClient |
정적 팩토리 — 빈으로 등록하지 않음. fork 프로젝트가 의존성마다 named 인스턴스 생성. static 인 이유: ArchUnit B7(어댑터 타입 반환 public 비static 메서드 금지) seam |
<T> T get(String uri, Class<T> type) |
URI, 응답 타입 | T |
exchange(GET, uri, null, type) 위임 |
<T> T exchange(HttpMethod m, String uri, Object body, Class<T> type) |
메서드/URI/요청바디/응답타입 | T |
전체 파이프라인(아래) |
<T> T stream(HttpMethod m, String uri, Function<InputStream,T> reader) |
메서드/URI/스트림 리더 | T |
리트라이 없음 · size 인터셉터 없음. 대용량 응답 전용 |
호출 파이프라인 (exchange 정상 경로)
- 셧다운 fast-fail —
guard.isShuttingDown()이면 네트워크를 맺지 않고 즉시DependencyFailureException(DEPENDENCY_CIRCUIT_OPEN, name, "shutdown in progress — outbound call rejected fail-fast (D8)")throw, 로그 outcome=REJECTED. deadline = Instant.now().plus(globalCallTimeout)산정 →retryPolicy.beginCall(method, deadline)(ThreadLocal 에 적재).- 데코레이션 합성:
CircuitBreaker.decorateSupplier(cb, Retry.decorateSupplier(retry, countingSupplier))→ 합성 순서 = CB(바깥) → Retry(안) → 실제 호출. 리트라이가 CB 안쪽이라 각 재시도가 독립적으로 CB 윈도우에 카운트됨. - 예외 분기:
OutboundResponseSizeExceededException는 분류하지 않고 그대로 재throw(업스트림 장애가 아니라 "버퍼 API 오용" 계약 위반); 그 외 모든Throwable→errorMapper.classify(name, t)로 매핑 → 로그 → throw. 호출자는 항상DependencyFailureException(또는 size 예외)만 본다. finally에서retryPolicy.endCall()항상 실행(ThreadLocal 누수 방지).
예외 / 오류코드 매핑 (OutboundHttpErrorMapper.classify, cause chain 순회 → 첫 매치 채택)
진단 메시지는 server-log-only — status code + 예외 클래스명만 담고 업스트림 raw 응답 body 는 절대 미포함(D12 PII 안전, DependencyFailureException javadoc 계약). 오류코드는 OperationalError(SSOT docs/registries/error-codes.yaml):
| 매치 (cause chain) | 코드 | HTTP / retryable |
|---|---|---|
CallNotPermittedException(R4j) |
DEPENDENCY_CIRCUIT_OPEN |
503 / true |
UnknownHostException·UnresolvedAddressException |
DEPENDENCY_DNS_FAILED |
503 / true |
HttpConnectTimeoutException |
DEPENDENCY_CONNECT_FAILED |
503 / true |
ConnectException(DNS cause 포함) |
DEPENDENCY_DNS_FAILED |
503 / true |
ConnectException(그 외) |
DEPENDENCY_CONNECT_FAILED |
503 / true |
HttpTimeout·SocketTimeout·TimeoutException |
DEPENDENCY_TIMEOUT |
504 / true |
RestClientResponseException 4xx |
DEPENDENCY_4XX_CLIENT |
502 / false (401→"check credential", 403→"check scope" 힌트) |
RestClientResponseException 5xx |
DEPENDENCY_5XX_SERVER |
502 / true |
| 매치 없음(fallback) | DEPENDENCY_CONNECT_FAILED |
503 / true |
순서 주의:
HttpConnectTimeoutException extends HttpTimeoutException이라 connect 를 read timeout 보다 먼저 검사. Open Risk(D12): 408/429 는 의미상 재시도 가능하지만 현재 모든 4xx 가 non-retryable.
자료구조 + 선택 이유
| 구조 | 위치 | 이유 |
|---|---|---|
AtomicBoolean running/shuttingDown |
OutboundHttpShutdownGuard |
셧다운 스레드 write ↔ 요청 스레드 read 간 가시성 |
ThreadLocal<CallContext> + record CallContext(HttpMethod, Instant deadline) |
OutboundRetryPolicy |
동기 클라이언트라 호출이 한 스레드를 타고 가므로 deadline·method 를 스레드별 격리 |
Set.of(GET,HEAD,PUT,DELETE) |
OutboundRetryPolicy.IDEMPOTENT_METHODS |
불변 + O(1) 멱등 판정. POST/PATCH 의도적 제외 |
int[] attemptCount = {0} |
OutboundHttpClient.exchange |
람다가 캡처 지역변수를 못 바꾸므로 1칸 배열을 가변 closure cell 로 사용 |
record OutboundHttpSettings + 중첩 record Retry/CircuitBreaker(박싱 Integer/Double/Float) |
OutboundHttpSettings |
불변 값 + null = "기본값 적용" 신호 |
Optional<Retry>/Optional<CircuitBreaker> |
OutboundHttpResilience |
"데코레이션 없음"(기능 off)을 호출자가 강제로 다루게 |
Spring / Resilience4j / Micrometer 메커니즘
@ConfigurationProperties(prefix="app.outbound.http")+@ConstructorBinding→ env/yaml → record 바인딩, compact 생성자 검증 실패 시 startup 실패.SmartLifecycle(OutboundHttpShutdownGuard):getPhase()=Integer.MAX_VALUE→ 컨텍스트 종료 시 phase 내림차순 stop → 이 빈의stop()이 가장 먼저 호출(다른 아웃바운드 빈보다 먼저 플래그 set).ContextClosedEvent는 너무 늦고 순서 미보장이라 부적합.BeanPostProcessor(OutboundHttpTimeoutEnforcer, static @Bean): rawRestClient/RestClient.Builder빈 발견 시BeanCreationException→ timeout 미설정 클라이언트 등록을 startup 차단. static 이라 다른 빈보다 일찍 생성돼 가로챔.- Resilience4j:
decorateSupplier합성,RetryRegistry/CircuitBreakerRegistry가 dependency 이름별 인스턴스 캐시(= per-dependency 지표),IntervalFunction.ofExponentialRandomBackoff(지수 + jitter). - Micrometer
MeterFilter(OutboundHttpResilienceConfig): 저카디널리티 정규화 —kind→outcome태그 리네임, state 값 대문자화, 그 외resilience4j.*미터 전부DENY. 활성화 가드(D3): retry/CB 중 하나라도 켜졌는데MeterRegistry없으면IllegalStateException. RestClient2개: connect timeout =HttpClient.connectTimeout, read timeout =JdkClientHttpRequestFactory.setReadTimeout. buffered(trace→size 인터셉터) / streaming(trace 만).
설정 (app.outbound.http.*)
| 키 | 기본값 | 효과 |
|---|---|---|
connect-timeout / read-timeout / global-call-timeout |
없음(필수) | TCP 연결 / 소켓 읽기 / 리트라이 포함 전체 deadline 예산. 누락 시 startup 실패 |
retry-enabled / circuit-breaker-enabled |
false / false |
R4j retry / CB 활성. 하나라도 켜면 MeterRegistry 필수 |
response-size-limit |
10MB |
buffered 본문 in-memory 상한(초과 시 OutboundResponseSizeExceededException) |
retry.max-attempts / initial-backoff / backoff-multiplier |
3 / 100ms / 2.0 |
시도 횟수 / 첫 백오프 / 지수 배수 |
circuit-breaker.failure-rate-threshold |
50(%) |
open 임계 |
…sliding-window-size / …minimum-number-of-calls |
100 / 100 |
COUNT_BASED 윈도우 / rate 계산 최소 호출 |
…wait-duration-in-open-state / …permitted-calls-in-half-open |
60s / 10 |
open→half-open 대기 / half-open 시험 호출 수 |
클래스 연관 (빈 배선)
OutboundHttpClientConfig가 공유 빈(ShutdownGuard/TimeoutEnforcer/ErrorMapper/OutboundHttpDependencyLogger/RetryPolicy)을@Bean @ConditionalOnMissingBean등록하되OutboundHttpClient빈은 일부러 안 만든다(의존성마다 named 인스턴스).OutboundHttpResilienceConfig가OutboundHttpResilience빈 생산 + MeterFilter 설치.- ⚠️
retryPolicy인스턴스는resilience(shouldRetrypredicate)와OutboundHttpClient(beginCall)가 같은 것을 공유해야 한다 — 다르면shouldRetry가 ctx=null 로 영영 재시도하지 않음. - 인터셉터:
TraceContextPropagationInterceptor(MDC→traceparent/baggage헤더, 샘플 플래그00하드코딩, allowlist=tenant_id·request_id),ResponseSizeBoundingInterceptor(Content-Length 또는BoundedInputStream누적이 limit 초과 시 throw, buffered 전용).
로컬/dev 검증 (locally-verified)
Persistence / (data-layer) Cache: 없음 (재확인 안 함).
Outbound HTTP Client: locally-verified — src/adapter-outbound/src/test/.../httpclient/ 의 단위 테스트로 다음이 검증됨(prod 배포·측정은 없음):
OutboundHttpClientTest— retry-on 500 GET 정확히 3회 / POST 정확히 1회(I4 비멱등 차단) · CB OPEN 시 0회 short-circuit +DEPENDENCY_CIRCUIT_OPEN· 셧다운 시 0회 +REJECTED· 업스트림 secret body 미유출 · buffered size 초과 시OutboundResponseSizeExceededException(stream()은 성공) ·outcome태그 존재/kind태그 부재.OutboundHttpErrorMapperTest— 위 예외 매핑 테이블 전 행 + 408/429 Open Risk + body 미유출.OutboundHttpResilienceTest/OutboundHttpResilienceConfigTest— decorate 순서 · 기본값(3/100ms/2.0, 50%/100/100/60s/10) · MeterRegistry 가드.OutboundRetryPolicyTest— 4-조건 게이트(셧다운/멱등/retryable/deadline).OutboundHttpShutdownGuardTest— phase=Integer.MAX_VALUE, start/stop 플래그.OutboundHttpSettingsTest— config 바인딩 + 잘못된 값 startupIllegalArgumentException.TraceContextPropagationInterceptorTest/OutboundHttpDependencyLoggerTest— 헤더 주입 · 로그 레벨/필드.
운영 검증 (prod-verified)
없음. 운영(prod) 환경에 배포된 적이 없다. 따라서 릴리즈 노트 / 운영 로그 / 모니터링 대시보드 / 인시던트 보고서 어느 것도 존재하지 않는다.
- 운영(prod) 환경에 배포된 적이 없다. 따라서 릴리즈 노트 / 운영 로그 / 모니터링 대시보드 / 인시던트 보고서 어느 것도 존재하지 않는다.
문서/계획만 존재 (documented-only / planned)
다음은 모두 문서/설계 단계의 결정이며, 코드로 강제되어 있지 않다. 면접에서 "구현했다"고 말하면 안 되는 부분이다.
Persistence (partially-implemented)
- SQLState 9-row classifier matrix(
08*connection,40001serialization,40P01deadlock,23xxxintegrity,57014query canceled 등) → SpringDataAccessExceptionhierarchy 위에TRANSIENT_DEPENDENCY / CONFLICT / DATA_INTEGRITY카테고리 매핑 (Category.java10-enum 정합 — 이전PERSISTENCE표기는 stale, 부모 §6 2026-06-01 정합 +error-codes.yamlauthoritative). - Hibernate OSIV off를 baseline으로 결정 (Vlad Mihalcea anti-pattern 평가 + Spring Boot startup WARN 근거).
- HikariCP pool wait p99 / pool exhaustion을 1차 alert 지표로 지정.
- Read replica lag threshold를 SLO에 포함.
- 근거: canonical raw/project-notes/ca-skeleton-operational-contract §6 + raw/branch-notes/feature-persistence-failure-baseline.
- 구현됨: idempotency/outbox RDBMS adapter, PostgreSQL migration, OSIV-off startup guard, Hikari inter-knob startup guard.
- 남음: SQLState 9-row classifier 전체, read replica lag metric/alert, 운영 pool tuning 측정.
Cache (partially-implemented)
- cache-aside default + Caffeine local lock(
@Cacheable(sync = true)/AsyncLoadingCache) + RedissonRLockdistributed mutex(multi-instance HPA 가정). - after-commit invalidation 강제 (Spring
TransactionSynchronizationManager.registerSynchronization의afterCommit()hook). - Eventual consistency window 5초로 명시.
- Strict consistency use case(잔액, 인증, idempotency 검증)는 cache bypass.
- Negative cache(존재하지 않는 row, TTL 60s)는 invalidation 채널 적용 대상에서 제외.
- 근거: canonical §11 + raw/branch-notes/feature-cache-consistency-contract.
- 구현됨:
CacheStore,FailOpenCacheStore,CacheStoreRouter,RedisCacheStore,CacheBindingSettings, 관련 단위 테스트. - 남음: 원래 문서의 cache-aside+Caffeine local lock+Redisson distributed mutex+after-commit invalidation 전체 contract와 운영 consistency window 측정.
Outbound HTTP (결정 — 현재 구현됨, 위 "Outbound HTTP Client" 절 참조)
다음은 baseline 결정 사실이며, 결정 자체는 그대로 유효하다. 2026-06-15 기준 코드로 구현되어 있다(시간/리트라이 기본값은 결정 당시 수치와 일부 다르게 구현됨 — 아래 표시):
- Spring RestClient(6.1+)를 baseline으로 결정. RestTemplate은 maintenance-only로 신규 채택 제외, WebClient는 MVC servlet baseline의 blocking risk로 extension 분리, OpenFeign은 Spring Cloud 의존으로 baseline에서 제외. → 구현:
RestClient2종(buffered/streaming). - Resilience4j로 retry / circuit breaker 일원화. Hystrix는 maintenance mode로 배제. → 구현:
OutboundHttpResilience+OutboundHttpResilienceConfig. (TimeLimiter 대신 동기 클라이언트라 deadline 예산 +OutboundRetryPolicy게이트로 대체.) - Timeout 계층: connect / read / global 3축 모두 필수 강제(하나라도 누락 시 startup 실패). → 구현됨. 단 결정 당시 예시값
2s/5s/10s는 기본값이 아니라 필수 입력으로 구현(@ConfigurationProperties, 기본값 없음). - Retry default disabled(
retry-enabled=false) → 구현됨. idempotency-key 미보장 일반 API 보수적 결정. 켜도 비멱등(POST/PATCH)은OutboundRetryPolicy가 차단. - 근거: canonical §11, §29 Group G-C + raw/branch-notes/feature-outbound-http-client-baseline + 코드
src/adapter-outbound/.../httpclient/.
대안 검토 범위 (요약 — 상세는 concept 참조)
각 sub-topic마다 5종 이상 대안을 비교했고 baseline을 선정했다. 비교의 출처/세부는 wiki/concepts/data-layer-persistence-cache-outbound에 있다.
- Persistence: SQLState classifier vs vendor-specific code, JPA blocking vs R2DBC reactive, OSIV on vs off, Hikari sizing 공식.
- Cache: cache-aside vs write-through vs write-behind vs read-through, Caffeine vs Hazelcast(local), Redisson RLock vs SETNX vs Redlock.
- Outbound HTTP: RestClient vs RestTemplate vs WebClient vs OpenFeign vs
@HttpExchange, Resilience4j vs Hystrix vs Spring Retry.
면접에서 말할 수 있는 범위
자신 있게 답할 수 있는 질문
- ca-tmpl의 SQLState 9-row classifier matrix를 왜 만들었고, Spring
DataAccessExceptionhierarchy 위에서 어떤 카테고리(TRANSIENT_DEPENDENCY / CONFLICT / DATA_INTEGRITY,Category.java10-enum)로 매핑하기로 했는가. - Hibernate OSIV를 anti-pattern으로 보는 근거(Vlad Mihalcea + Spring Boot WARN)와 OSIV off를 ca-tmpl baseline으로 둔 이유.
- cache-aside의 eventual consistency window 5초가 의미하는 바, 그리고 strict consistency가 필요한 use case(잔액, 인증, idempotency 검증)를 cache bypass로 분리한 의도.
- Resilience4j를 Hystrix 대신 선택한 이유(Hystrix maintenance mode + Resilience4j functional decorator 모델).
- Outbound HTTP timeout을 connect 2s / read 5s / global 10s로 분리한 의도와, 셋 중 어떤 게 빠지면 어떤 위험이 생기는지.
적당히 답할 수 있는 질문
- Caffeine vs Hazelcast 같은 local cache 후보 비교 (개념 수준은 가능, 실측 비교 없음).
- RestClient vs WebClient (concept-level trade-off는 답할 수 있으나 실제 throughput 측정 없음).
- after-commit invalidation을 강제하는 이유 (개념 + Spring API 위치는 설명 가능, 실 hook 코드 없음).
답하면 안 되는 질문 (모른다고 해야 함)
- HikariCP pool size 튜닝 경험, pool wait p99 실측치, pool exhaustion 대응 경험 — 측정/운영 경험 없음.
- cache hit ratio 측정 / TTL 튜닝 / negative cache stale 사례 — 계측 없음.
- Resilience4j circuit breaker open 운영 경험, half-open probe 동작 관찰, 실제 retry budget 튜닝 — 운영 경험 없음.
- read replica lag 운영 경험, replica failover 대응 — 운영 경험 없음.
- 본 baseline을 적용한 서비스의 SLO 달성 여부 — prod 배포 없음.
과장 금지 지점
이 프로젝트를 외부에 설명할 때 사실보다 부풀려지기 쉬운 표현.
- "ca-tmpl에 SQLState classifier 전체를 구현했다" → ❌. persistence failure classifier 전체는 별도 확인 필요.
- "OSIV off startup guard와 Hikari inter-knob guard를 로컬 검증했다" → 가능.
- "cache-aside + Redisson RLock으로 분산 환경에서 안전한 캐시를 구현했다" → ❌. lower-layer cache SPI/router와 원래 cache-aside+distributed mutex contract를 혼동하지 않는다.
- "Resilience4j로 circuit breaker/retry baseline을 구현했다" → 가능. 단 운영 장애 대응 경험은 없음.
- "RestClient + timeout 2s/5s/10s로 outbound baseline을 구현하고 로컬 테스트로 검증했다" → 가능. 단 운영 SLO 보장은 아님.
- "성능 측정 후 baseline을 튜닝했다" → ❌. 측정·튜닝 모두 미수행.
- "운영에서 검증된 baseline이다" → ❌. prod 배포 없음.
면접·블로그·이력서에서는 항상 "구현된 slice와 planned slice를 분리"해야 한다. outbound/cache SPI/idempotency/outbox persistence는 구현·로컬 검증, cache-aside distributed consistency와 운영 tuning은 planned로 둔다.
Blog-topic ingest: cache/webhook/outbound 묶음 (2026-07-02)
아래 raw seed들은 data-layer/cache/outbound canonical에 연결했다. 이 문서는 2026-07-02 기준 코드와 ./gradlew check로 검증되어 blogify 가능하다. 단 각 글에서는 구현된 slice와 planned slice를 분리한다.
- raw/blog-topics/cache-backend-router-fail-open-decorator-2026-07-02: cache 장애를 backend 내부
try/catch가 아니라 router/decorator 조립 계약으로 중앙화하는 글감. 말할 수 있는 범위는 ca-tmpl cache role과 검증된 backend 범위다. "모든 cache 실패를 삼켜도 된다"는 식으로 쓰지 않는다. - raw/blog-topics/cache-consistency-after-commit-stampede-contract-2026-07-02: after-commit invalidation, stampede guard, negative TTL, consistency window를 분리하는 글감. 주의: planned test와 unsupported decision을 implemented처럼 쓰지 않는다.
- raw/blog-topics/webhook-full-jitter-dlq-observability-2026-07-02: webhook retry를 Full Jitter, DLQ, metric contract로 묶는 글감. 주의: retry/metric/DLQ 중 planned 항목은 구현 완료로 쓰지 않는다.
- raw/blog-topics/webhook-signature-replay-contract-2026-07-02: raw bytes, timestamp, message id, replay window를 webhook signature 계약으로 묶는 글감. provider 문서는 universal standard가 아니라 사례/source-backed claim으로만 사용한다.
- raw/blog-topics/webhook-ssrf-egress-proxy-redirect-block-2026-07-02: webhook endpoint 등록을 URL 저장이 아니라 egress proxy, redirect block, private range 차단 계약으로 다루는 글감. OWASP/source-backed SSRF 방어와 ca-tmpl planned policy를 분리한다.
- raw/blog-topics/jdk-httpclient-dns-connectexception-classification-2026-07-02: JDK
HttpClient에서 DNS 실패를 connection failure로 분류할 때 exception wrapping과 retry category를 어떻게 다루는지 정리하는 글감. 운영 장애 사례가 아니라 local/test evidence 중심으로 제한한다. - raw/blog-topics/micrometer-meterfilter-resilience4j-functioncounter-2026-07-02: outbound HTTP observability에서 Micrometer
MeterFilter와 Resilience4jFunctionCounterregistration/tag policy가 충돌할 수 있는 지점을 정리하는 글감. Micrometer/Resilience4j 자체의 일반 결함처럼 쓰지 않는다. - raw/blog-topics/repository-capability-archunit-fitness-function-2026-07-02: repository 접근 권한을 annotation + registry + ArchUnit fitness function으로 강제하는 글감. 모든 repository misuse를 자동 검출한다고 쓰지 않고, 정적 분석 rule이 볼 수 있는 구조로 제한한다.
- raw/blog-topics/persistence-audit-metadata-clean-architecture-2026-07-02: audit column을 domain model에 섞지 않고 persistence adapter에서 채우는 boundary choice 글감. JPA Auditing이 나쁘다고 쓰지 않고 ca-tmpl skeleton의 선택으로 제한한다.
- raw/blog-topics/distributed-lock-transaction-commit-boundary-2026-07-02:
lock.close()와 DB commit 순서가 맞물릴 때 lost update 경계가 생기는 이유를 다루는 글감. local/JDBC lock 검증을 운영 분산 환경 보장으로 표현하지 않는다. - raw/blog-topics/hikaricp-inter-knob-constraints-startup-guard-2026-06-09: HikariCP knob 간 제약을 Spring Boot startup guard로 fail-fast 검증하는 글감. 기존 canonical은 persistence/cache 영역이 stale일 수 있으므로 실제 validator/test 존재 여부를 재확인하기 전까지 구현 등급을 올리지 않는다.
관련 개념
Sources
- raw/project-notes/ca-skeleton-operational-contract — §6 Operational Error Category, §11 Adapter Failure Contract, §29 Group G-C 외부 근거 인덱스
- raw/branch-notes/feature-persistence-failure-baseline — SQLState 9-row matrix, Hikari alert threshold, OSIV off 결정 기록
- raw/branch-notes/feature-cache-consistency-contract — cache-aside default, Caffeine + Redisson, after-commit invalidation, 5s window 결정 기록
- raw/branch-notes/feature-outbound-http-client-baseline — RestClient baseline, Resilience4j, timeout 2s/5s/10s, shutdown retry suppression 결정 기록
- raw/blog-topics/jdk-httpclient-dns-connectexception-classification-2026-07-02 — JDK HttpClient DNS/ConnectException classification 블로그 글감 raw seed
- raw/blog-topics/micrometer-meterfilter-resilience4j-functioncounter-2026-07-02 — Micrometer/Resilience4j metric registration 블로그 글감 raw seed
- raw/branch-notes/feature-repository-access-permission-contract — repository capability/access permission parent branch
- raw/blog-topics/repository-capability-archunit-fitness-function-2026-07-02 — repository capability ArchUnit fitness function 블로그 글감 raw seed
- raw/branch-notes/feature-persistence-auditing-contract — persistence audit metadata parent branch
- raw/blog-topics/persistence-audit-metadata-clean-architecture-2026-07-02 — persistence audit metadata 블로그 글감 raw seed
- raw/branch-notes/feature-distributed-lock-contract — distributed lock lifecycle parent branch
- raw/blog-topics/distributed-lock-transaction-commit-boundary-2026-07-02 — distributed lock transaction commit boundary 블로그 글감 raw seed
- raw/blog-topics/hikaricp-inter-knob-constraints-startup-guard-2026-06-09 — HikariCP startup guard 블로그 글감 raw seed
- raw/branch-notes/feature-cachestore-multi-backend-router — cache backend router/decorator/fail-open 글감의 parent branch
- raw/branch-notes/feature-webhook-outbound-contract — webhook retry/signature/SSRF outbound 글감의 parent branch
- raw/blog-topics/cache-backend-router-fail-open-decorator-2026-07-02 — cache router/decorator 블로그 글감 raw seed
- raw/blog-topics/cache-consistency-after-commit-stampede-contract-2026-07-02 — cache after-commit/stampede 블로그 글감 raw seed
- raw/blog-topics/webhook-full-jitter-dlq-observability-2026-07-02 — webhook retry/DLQ/observability 블로그 글감 raw seed
- raw/blog-topics/webhook-signature-replay-contract-2026-07-02 — webhook signature/replay 블로그 글감 raw seed
- raw/blog-topics/webhook-ssrf-egress-proxy-redirect-block-2026-07-02 — webhook SSRF/egress 블로그 글감 raw seed