fix: 하네스 제거 및 keycloak 문서 보강

This commit is contained in:
DongHyeonka
2026-07-25 12:53:13 +09:00
parent 6c53ded9cb
commit d71669eb59
2329 changed files with 138239 additions and 172816 deletions
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/actuator-endpoint-exposure-spring-official.md
@@ -0,0 +1,104 @@
---
title: Spring Boot Actuator — Endpoint Exposure & Security Defaults
source_type: official-doc
url: https://docs.spring.io/spring-boot/reference/actuator/endpoints.html
archive_url:
status: raw
confidence: high
tags: [ca-actuator, spring-boot, actuator, endpoint-exposure, security-defaults]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-management-actuator-security-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Spring Boot Actuator — Endpoint Exposure & Security Defaults
> Layer: `raw/official-docs/` — Spring Boot 공식 reference (Actuator Endpoints) 의 exposure / security default 원문 발췌.
> ca-tmpl `feature-management-actuator-security-contract` 의 prod allowlist (`health`, `prometheus`, `info`) + forbidden (`env`, `configprops`, `heapdump`, `threaddump`) 결정 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-management-actuator-security-contract]] | prod allowlist (`health`, `prometheus`, `info`) + forbidden (`env`, `configprops`, `heapdump`, `threaddump`) 정책이 Spring Boot default 강화임을 증명하는 근거 |
## 컨텍스트
`feature-management-actuator-security-contract` ca-tmpl 이 정한 prod allowlist 와 forbidden 목록이 Spring Boot 공식 권고 / 기본값과 어떻게 부합하는지 확인. baseline 이 임의 정책이 아니라 공식 default 를 강화한 것임을 증명.
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-boot/reference/actuator/endpoints.html
- 관련 property: `management.endpoints.web.exposure.include`, `management.endpoint.health.show-details`
- 아카이브 URL: (미수집)
- 저자 / 조직: Spring Team (VMware / Broadcom)
- 발행일: Spring Boot 3.x reference (4.0.6 anchors observed)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§actuator.endpoints.exposing] "By default, only the health endpoint is exposed over HTTP and JMX."
> [§actuator.endpoints.security] "Before setting the `management.endpoints.web.exposure.include`, ensure that the exposed actuators do not contain sensitive information, are secured by placing them behind a firewall, or are secured by something like Spring Security."
> [§actuator.endpoints.security] "If Spring Security is on the classpath and no other `SecurityFilterChain` bean is present, all actuators other than `/health` are secured by Spring Boot auto-configuration."
> [§actuator.endpoints.sanitization] "Information returned by the `/env`, `/configprops` and `/quartz` endpoints can be sensitive, so by default values are always fully sanitized (replaced by `******`)."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SB-ACT-EXP-C1 | Spring Boot Actuator 의 default 는 HTTP / JMX 모두에서 **health endpoint 하나만** 노출 | [§actuator.endpoints.exposing] "By default, only the health endpoint is exposed over HTTP and JMX." | `official-vendor-doc` | Spring Boot Actuator dependency 가 클래스패스에 있는 모든 Spring Boot 앱 | `prometheus`, `info` 등 다른 endpoint 가 자동 노출된다는 뜻은 아님 — 명시적 `include` 필요 |
| SB-ACT-EXP-C2 | `management.endpoints.web.exposure.include` 설정 전에 노출되는 actuator 가 (a) 민감 정보 없거나 (b) firewall 뒤 또는 (c) Spring Security 보호되도록 보장해야 함 (공식 권고) | [§actuator.endpoints.security] "Before setting the `management.endpoints.web.exposure.include`, ensure that the exposed actuators do not contain sensitive information, are secured by placing them behind a firewall, or are secured by something like Spring Security." | `official-vendor-doc` | actuator endpoint 를 default 보다 더 노출하려는 모든 시나리오 | 세 옵션 중 어느 것이 모든 환경에서 최선인지의 판단은 본 인용 범위 밖 — 상황별 선택 |
| SB-ACT-EXP-C3 | Spring Security 가 classpath 에 있고 다른 `SecurityFilterChain` bean 이 없으면, `/health` 외 모든 actuator 가 Spring Boot auto-configuration 으로 secured | [§actuator.endpoints.security] "If Spring Security is on the classpath and no other `SecurityFilterChain` bean is present, all actuators other than `/health` are secured by Spring Boot auto-configuration." | `official-vendor-doc` | spring-boot-starter-security 사용 + custom SecurityFilterChain 없는 환경 | custom `SecurityFilterChain` bean 을 정의한 순간 이 auto-config 가 비활성되므로, 개발자가 actuator 보호 룰을 명시해야 함 — 흔한 함정 |
| SB-ACT-EXP-C4 | `/env`, `/configprops`, `/quartz` endpoint 의 응답 값은 default 로 **항상 완전히 sanitize** 되어 `******` 로 치환됨 | [§actuator.endpoints.sanitization] "Information returned by the `/env`, `/configprops` and `/quartz` endpoints can be sensitive, so by default values are always fully sanitized (replaced by `******`)." | `official-vendor-doc` | Spring Boot Actuator 의 default sanitizer 동작 | `/heapdump`, `/threaddump` 등 다른 sensitive endpoint 의 sanitization 은 본 인용 범위 밖 — 별도 페이지 확인 필요 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `SB-ACT-EXP-C1`: default 노출 = `health` 하나
- `SB-ACT-EXP-C2`: 더 많은 endpoint 노출 시 보안 조치 권고 (3가지 옵션)
- `SB-ACT-EXP-C3`: Spring Security + no SecurityFilterChain → `/health` 외 auto-secured
- `SB-ACT-EXP-C4`: `/env`, `/configprops`, `/quartz` default sanitize
- **이 자료가 증명하지 않는 것**:
- prod 에서 `env`, `configprops`, `heapdump`, `threaddump` 를 **endpoint 자체로 금지**하라는 공식 의무 — ca-tmpl 의 forbidden 정책은 default sanitize 보다 한 단계 더 strict 한 자체 결정
- `/info` 의 default 노출 여부 — 본 인용 범위 밖 (default 는 health 만이므로 info 도 명시 include 필요)
- `/prometheus` endpoint 가 자동 노출되는 조건 (micrometer-registry-prometheus dependency 등) — 별도
- custom `SecurityFilterChain` 정의 시 actuator 보호가 disable 되는 정확한 동작 (모두 permit 인지 모두 deny 인지)
- **내 프로젝트 (ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 prod 환경에서 `management.endpoints.web.exposure.include=health,prometheus,info` 설정 시 실제 노출되는 sub-endpoint 셋 (`/actuator/health/liveness` 등 group sub-path 포함 여부)
- custom SecurityFilterChain 정의된 ca-tmpl 환경에서 actuator path 가 `permitAll()` / `authenticated()` 어디로 떨어지는지 (auto-config 비활성 영향)
- prometheus endpoint 의 prod 노출 시 scrape 인증 방식 (network ACL 외 추가 인증 필요한지)
## ca-tmpl 함의 (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용이 아니라 ca-tmpl 결정 컨텍스트 해석. wiki 추출 시 `wiki/projects/ca-skeleton-operational-contract` source-summary 로 이전.
- **공식 default 와의 매핑**:
- 공식 default = "only health exposed" → ca-tmpl prod allowlist (`health/*`, `prometheus`, `info`) 는 **default 를 약간 확장** (prometheus, info 추가).
- 공식 권고 = "not sensitive OR behind firewall OR Spring Security" → ca-tmpl 의 management port 분리 (9001) + network ACL 은 "behind firewall" 옵션 선택.
- 공식 default sanitize = `env` / `configprops``******` → ca-tmpl 은 한 단계 더 나아가 prod 에서 **endpoint 자체 forbidden** (default 보다 strict).
- **`/info` 주의**: ca-tmpl 은 "build info only, no secret" 명시. `git.commit.id`, `build.version` 외 contributor 가 추가 정보로 secret 노출할 가능성을 별도 review 로 차단.
- **heapdump / threaddump**: 공식 문서는 endpoint 정의는 하나 "prod 금지" 의무는 두지 않음. ca-tmpl 의 명시적 forbidden 은 운영 보안 강화 자체 결정.
- **장점**: 공식 default 보다 strict → 보안 회귀 가능성 ↓. `info` 만 추가 노출이라 향후 Spring Boot 버전업 시 default 변동 영향 적음.
- **단점**: prometheus 노출은 scrape 환경 (인증 or network ACL) 이 명시적으로 보장돼야 의미 — ca-tmpl 의 network ACL 은 기본 충족, 외부 노출 시 별도 인증 필요.
## 메모 / Notes
- 2026-05-27 재검증: 4개 핵심 인용 모두 verbatim 으로 reference 의 해당 anchor 에 존재 확인.
- 다음 fetch 후보:
- `https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints.sanitization` (heapdump / threaddump sanitization 별도 정책)
- `https://docs.spring.io/spring-boot/reference/actuator/observability.html#actuator.observability.prometheus` (prometheus endpoint 노출 조건)
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/actuator-management-port-spring-official]] — management port 분리 결정
- [[raw/official-docs/runtime-health-spring-actuator-groups]] — health endpoint group 모델
- 인용하는 branch:
- [[raw/branch-notes/feature-management-actuator-security-contract]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/actuator-istio-sidecar-management-alt.md
@@ -0,0 +1,115 @@
---
title: Istio Security — Sidecar PEP & AuthorizationPolicy (management endpoint 대안)
source_type: official-doc
url: https://istio.io/latest/docs/concepts/security/
archive_url:
status: raw
confidence: high
tags: [ca-actuator, istio, service-mesh, sidecar, peer-authentication, mtls, official-doc]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-management-actuator-security-contract, feature-security-operational-baseline]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Istio Security — Sidecar PEP for management endpoints
> Layer: `raw/official-docs/` — Istio 공식 "Security" concept page verbatim 발췌. ca-tmpl baseline (`feature-management-actuator-security-contract`) 의 "Spring 단 management port + network ACL" 결정에 대한 service-mesh 대안 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-management-actuator-security-contract]] | ca-tmpl baseline 이 mesh-agnostic 으로 선택한 이유의 비교 근거 — Istio sidecar PEP 가 application 책임을 platform 책임으로 옮기는 대안 |
| [[raw/branch-notes/feature-security-operational-baseline]] | mTLS 대안 cross-link — Istio PeerAuthentication STRICT 가 application-level cert 관리 대안 |
## 컨텍스트 / 왜 저장했는지
`feature-management-actuator-security-contract` ca-tmpl baseline 은 Spring 단에서 management port + network ACL 을 default 로 결정. service mesh 환경에서는 application 이 아니라 sidecar 가 management traffic 을 가르는 패턴이 가능. 대안으로 검토하고 baseline 이 mesh 를 가정하지 않은 이유를 분명히 함.
## 출처 / Source
- 원본 URL: https://istio.io/latest/docs/concepts/security/
- 아카이브 URL: (미수집)
- 저자 / 조직: Istio (CNCF 프로젝트)
- 발행일: 지속적으로 갱신 (latest channel)
- 관련 CRD: `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim, 2026-05-27 확인)
> [§Peer/Request Authentication, 2026-05-27 verified] "Peer and request authentication policies are stored separately by kind, `PeerAuthentication` and `RequestAuthentication` respectively."
> [§Policy Enforcement Points, 2026-05-27 verified] "Sidecar and perimeter proxies work as [Policy Enforcement Points](https://csrc.nist.gov/glossary/term/policy_enforcement_point) (PEPs) to secure communication between clients and servers."
> [§Identity model, 2026-05-27 verified] "The Istio identity model uses the first-class `service identity` to determine the identity of a request's origin."
> [§AuthorizationPolicy, 2026-05-27 verified] "To configure an authorization policy, you create an [`AuthorizationPolicy` custom resource]...An authorization policy includes a selector, an action, and a list of rules."
> [§Certificate rotation, 2026-05-27 verified] "Istio agent monitors the expiration of the workload certificate. The above process repeats periodically for certificate and key rotation."
> [§PeerAuthentication mTLS modes, 2026-05-27 verified] "PERMISSIVE: Workloads accept both mutual TLS and plain text traffic...STRICT: Workloads only accept mutual TLS traffic...DISABLE: Mutual TLS is disabled."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| ISTIO-SEC-C1 | Istio 의 sidecar 와 perimeter proxy 는 client ↔ server 통신 보안을 강제하는 **Policy Enforcement Point (PEP)** 로 동작한다 | [§Policy Enforcement Points, 2026-05-27 verified] "Sidecar and perimeter proxies work as [Policy Enforcement Points](https://csrc.nist.gov/glossary/term/policy_enforcement_point) (PEPs) to secure communication between clients and servers." | `official-standard` | mesh 가 활성화된 Kubernetes workload | sidecar PEP 가 `/actuator/*` 같은 특정 path 를 외부 트래픽으로부터 거부한다는 직접 명시는 없음 — path 기반 거부는 별도 AuthorizationPolicy 규칙으로 구성해야 함 |
| ISTIO-SEC-C2 | `PeerAuthentication``RequestAuthentication` 은 별도 CRD kind 로 저장되며 각각 peer (service-to-service) / request (end-user JWT) 인증 정책을 표현 | [§Peer/Request Authentication, 2026-05-27 verified] "Peer and request authentication policies are stored separately by kind, `PeerAuthentication` and `RequestAuthentication` respectively." | `official-standard` | Istio CRD-기반 인증 구성 | 한 workload 가 동시에 두 정책 모두 가져야 한다는 뜻 아님 — 별도 선택 가능 |
| ISTIO-SEC-C3 | Istio identity 모델은 first-class `service identity` 를 사용해 요청 origin 의 identity 를 결정한다 | [§Identity model, 2026-05-27 verified] "The Istio identity model uses the first-class `service identity` to determine the identity of a request's origin." | `official-standard` | service-to-service 인증 정책 의사결정 | `service identity` 가 IP 기반 ACL 보다 항상 안전하다는 직접 비교는 본 인용에 없음 — 단지 identity model 의 기본 단위 |
| ISTIO-SEC-C4 | `AuthorizationPolicy` 는 selector + action + rules 목록 구조의 custom resource 로 인가 정책을 표현 | [§AuthorizationPolicy, 2026-05-27 verified] "To configure an authorization policy, you create an [`AuthorizationPolicy` custom resource]...An authorization policy includes a selector, an action, and a list of rules." | `official-standard` | 모든 mesh workload 에 적용 가능한 인가 정책 정의 | rule 의 정확한 field schema (예: `to.operation.paths`) 는 본 인용에 명시 없음 — 별도 reference page |
| ISTIO-SEC-C5 | Istio agent 는 workload certificate expiration 을 monitor 하며 위 발급 프로세스가 주기적으로 반복되어 cert/key rotation 이 자동화된다 | [§Certificate rotation, 2026-05-27 verified] "Istio agent monitors the expiration of the workload certificate. The above process repeats periodically for certificate and key rotation." | `official-standard` | mesh-enrolled workload 의 mTLS cert 운영 | rotation 주기의 정확한 default 값 (예: 24h) 은 본 인용에 명시 없음 — 별도 install reference |
| ISTIO-SEC-C6 | `PeerAuthentication` 의 mTLS 모드는 PERMISSIVE (mTLS + plain text 둘 다 수락), STRICT (mTLS 만 수락), DISABLE (mTLS 비활성) 3가지 | [§PeerAuthentication mTLS modes, 2026-05-27 verified] "PERMISSIVE: Workloads accept both mutual TLS and plain text traffic...STRICT: Workloads only accept mutual TLS traffic...DISABLE: Mutual TLS is disabled." | `official-standard` | mesh 단계적 도입 (PERMISSIVE → STRICT 마이그레이션) | UNSET (정책 미설정) 의 fallback 동작이 어떤 mode 와 동일한지는 본 인용에 명시 없음 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `ISTIO-SEC-C1`: sidecar/perimeter proxy 가 PEP 라는 **공식 표준 정의** — service mesh 환경에서 application 이 아닌 mesh 가 인증 enforcement 책임을 가질 수 있음
- `ISTIO-SEC-C2~C4`: Istio 의 정책 CRD 분리 (peer/request 인증 + 인가), service identity 모델, AuthorizationPolicy 구조
- `ISTIO-SEC-C5`: Istio agent 의 자동 cert rotation (application code 변경 없이 mTLS 적용 가능)
- `ISTIO-SEC-C6`: STRICT/PERMISSIVE/DISABLE 3 모드 (단계적 도입 경로 명문화)
- **이 자료가 증명하지 않는 것**:
- "Istio sidecar 만으로 `/actuator/*` 경로를 외부에 deny 한다" 는 직접 인용 부재 — path-level 거부는 별도 `AuthorizationPolicy` rule (`to.operation.paths` 필드) 작성 필요 (별도 reference page 확인)
- mesh sidecar 가 ca-tmpl 의 "separate management port + network ACL" 보다 항상 우월하다는 비교 — 본 자료는 mesh 환경 가정 문서이며, mesh-agnostic baseline 과의 정량 비교는 부재
- sidecar latency 정확한 수치 (보통 수 ms 라는 운영 관행은 별도 perf 벤치마크 필요)
- cert rotation 의 default 주기 (예: 24h) — 본 인용은 "periodically" 만 명시
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl skeleton 이 K8s + Istio mesh 를 baseline 으로 가정해도 되는가 — 본 자료는 mesh 가정 시의 옵션 set 만 보여줌
- `AuthorizationPolicy``/actuator/*` path 거부 규칙의 정확한 YAML 형식 (별도 reference page)
- PeerAuthentication STRICT 적용 시 기존 plain HTTP probe (Spring Boot Actuator health check 등) 와의 호환성
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl baseline 비교 컨텍스트 해석.
- **service mesh 시나리오의 management endpoint 보호 (가설 / 추가 검증 필요):**
- `PeerAuthentication` (STRICT mTLS) + `AuthorizationPolicy` (deny external to `/actuator/*`) 조합 가능 — 단, `/actuator/*` path 거부의 정확한 YAML 은 별도 reference 확인
- sidecar 가 PEP → application 은 endpoint 보호 책임에서 자유로움 (`ISTIO-SEC-C1` 의 추론 확장)
- cert rotation 은 Istio agent 자동 (`ISTIO-SEC-C5`)
- **vs ca-tmpl baseline (separate port + network ACL):**
- ca-tmpl: skeleton 이 mesh-agnostic → application 자체 책임으로 가짐
- mesh 가 있으면 baseline 이 sidecar 정책으로 옮겨갈 수 있음 (단 mesh 도입 전제)
- **결정 권고 (조건부):**
- ca-tmpl baseline 은 "minimum viable" → mesh 없이도 동작 (mesh 무의존 보존)
- mesh 도입 환경에서는 application 의 management port 를 ClusterIP-only 로 두고 sidecar 로 한 번 더 차단 (defense-in-depth)
- **장점 (mesh 측):**
- certificate-based service identity → IP 기반 ACL 의 한계 극복 (`ISTIO-SEC-C3`)
- 자동 cert rotation (`ISTIO-SEC-C5`)
- 정책 수정이 application 재배포와 분리 (`ISTIO-SEC-C4` CRD 모델)
- **단점:**
- mesh control plane 운영 부담 (본 자료 범위 밖, 운영 관행)
- sidecar latency (수 ms — 본 자료 범위 밖, perf 벤치마크 필요)
- mesh 미도입 환경에서는 사용 불가 → skeleton baseline 으로 가정 불가
- **ca-tmpl 이 mesh 를 baseline 으로 채택하지 않은 이유 (추정):**
- skeleton 은 platform 중립 → Kubernetes + mesh 가정은 너무 강한 전제
- mesh sidecar 정책은 platform team 의 SSOT 이 되어야 하며 application contract 와 책임 분리가 필요
## Related / 관련
- 적용 branch-note:
- [[raw/branch-notes/feature-management-actuator-security-contract]]
- [[raw/branch-notes/feature-security-operational-baseline]] (mTLS 대안 cross-link)
- canonical contract 섹션:
- [[raw/project-notes/ca-skeleton-operational-contract#Management / Actuator Security]] (예정)
- 대안 그룹: **Group G-B — Actuator sub-topic**
- 본 source 의 위치: **대안 4 — service mesh sidecar (Istio)**
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/actuator-management-port-spring-official.md
@@ -0,0 +1,111 @@
---
title: Spring Boot Actuator — Separate management.server.port
source_type: official-doc
url: https://docs.spring.io/spring-boot/reference/actuator/monitoring.html
archive_url:
status: raw
confidence: high
tags: [ca-actuator, spring-boot, actuator, management-port, network-isolation]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-management-actuator-security-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Spring Boot Actuator — Separate management.server.port
> Layer: `raw/official-docs/` — Spring Boot 공식 reference (Actuator Monitoring and Management over HTTP) 원문 발췌.
> ca-tmpl `feature-management-actuator-security-contract` 의 `management port = 9001 (separate)` 결정 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-management-actuator-security-contract]] | management port 분리 (9001) 채택 + "single-port + ingress 보호" 도 공식 허용 옵션이라는 baseline 근거 |
## 컨텍스트
`feature-management-actuator-security-contract` ca-tmpl 이 결정한 `management port = 9001 (separate)` 가 Spring Boot 가 공식 지원하는 패턴인지 확인. baseline 의 "single port 는 platform ingress 보호 + 문서화 시만 허용" 결정의 근거.
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-boot/reference/actuator/monitoring.html
- 관련 property: `management.server.port`, `management.server.address`, `management.server.ssl.*`
- 아카이브 URL: (미수집)
- 저자 / 조직: Spring Team (VMware / Broadcom)
- 발행일: Spring Boot 3.x reference
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Monitoring and Management over HTTP — Customizing the Management Server Port] "Exposing management endpoints by using the default HTTP port is a sensible choice for cloud-based deployments."
> [§Monitoring and Management over HTTP — Customizing the Management Server Port] "If, however, your application runs inside your own data center, you may prefer to expose endpoints by using a different HTTP port."
> [§Monitoring and Management over HTTP — Customizing the Management Server Port] "You can set the `management.server.port` property to change the HTTP port, as the following example shows:"
> [§Monitoring and Management over HTTP — Configuring Management-specific SSL] "When configured to use a custom port, you can also configure the management server with its own SSL by using the various `management.server.ssl.*` properties."
> [§Monitoring and Management over HTTP — Configuring Management-specific SSL] "For example, doing so lets a management server be available over HTTP while the main application uses HTTPS, as the following property settings show:"
> [§Monitoring and Management over HTTP — Customizing the Management Server Address] "You can customize the address on which the management endpoints are available by setting the `management.server.address` property. Doing so can be useful if you want to listen only on an internal or ops-facing network or to listen only for connections from `localhost`."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SB-ACT-PORT-C1 | cloud 기반 배포에서는 management endpoint 를 default HTTP port (application 과 동일) 로 노출하는 것이 **sensible choice** | [§Customizing the Management Server Port] "Exposing management endpoints by using the default HTTP port is a sensible choice for cloud-based deployments." | `official-vendor-doc` | cloud / managed platform 배포 (heroku, app runner, k8s ingress 등) | "default port 가 모든 cloud 환경에서 보안 충분" 이라는 뜻은 아님 — ingress / network policy 측 보호 필요 |
| SB-ACT-PORT-C2 | 자체 데이터센터 운영 시 별도 HTTP port 로 management endpoint 노출이 **preferable** 할 수 있음 (공식 옵션) | [§Customizing the Management Server Port] "If, however, your application runs inside your own data center, you may prefer to expose endpoints by using a different HTTP port." | `official-vendor-doc` | self-managed infra / data-center / on-prem | "별도 port 가 always-better" 라는 의미는 아님 — 선택지로 명시 |
| SB-ACT-PORT-C3 | `management.server.port` property 로 HTTP port 변경 가능 | [§Customizing the Management Server Port] "You can set the `management.server.port` property to change the HTTP port, as the following example shows:" | `official-vendor-doc` | Spring Boot Actuator 가 활성된 모든 환경 | port 만 분리해도 ACL / firewall 이 별도 보장돼야 노출 위험 차단 — 본 인용은 mechanism 만 |
| SB-ACT-PORT-C4 | custom port 사용 시 `management.server.ssl.*` 로 main app 과 별개로 SSL 구성 가능 | [§Configuring Management-specific SSL] "When configured to use a custom port, you can also configure the management server with its own SSL by using the various `management.server.ssl.*` properties." | `official-vendor-doc` | management port 가 main app port 와 다른 경우 | default port 공유 시에도 별도 SSL 가능하다는 뜻은 **아님** — custom port 가 전제 |
| SB-ACT-PORT-C5 | 예: main app HTTPS + management server HTTP 분리 운영이 공식 예시로 제시됨 | [§Configuring Management-specific SSL] "For example, doing so lets a management server be available over HTTP while the main application uses HTTPS, as the following property settings show:" | `official-vendor-doc` | TLS termination 정책이 management ↔ app 다른 환경 | management HTTP 가 항상 안전하다는 뜻은 아님 — 내부망 / 신뢰 ACL 전제 |
| SB-ACT-PORT-C6 | `management.server.address` 로 listen 주소 한정 가능 (internal / ops-facing / localhost only) | [§Customizing the Management Server Address] "You can customize the address on which the management endpoints are available by setting the `management.server.address` property. Doing so can be useful if you want to listen only on an internal or ops-facing network or to listen only for connections from `localhost`." | `official-vendor-doc` | multi-NIC 또는 명시적 bind 가 필요한 환경 | bind address 변경이 firewall / network policy 를 대체한다는 뜻은 아님 — defense-in-depth 한 레이어 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `SB-ACT-PORT-C1` ~ `C2`: default port (cloud) vs separate port (data center) 의 공식 사용 권고 양면
- `SB-ACT-PORT-C3` ~ `C5`: `management.server.port` + `management.server.ssl.*` mechanism 과 HTTPS app / HTTP management 예시
- `SB-ACT-PORT-C6`: `management.server.address` 로 bind 주소 한정 가능
- **이 자료가 증명하지 않는 것**:
- "separate port = 항상 더 안전" 같은 universal best practice (공식 문서는 두 옵션 모두 합리적이라고 명시)
- 9001 port 가 Spring Boot 의 권장 default 라는 점 (port 번호는 사용자 선택)
- mTLS for management (`SB-ACT-PORT-C4` 는 SSL 분리만 명시, client cert 요구는 별도)
- service mesh (Istio PeerAuthentication 등) 와의 통합 권장 사항
- **내 프로젝트 (ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 Kubernetes deployment 가 single Service + dual containerPort (8080 + 9001) 로 떨어지는지, 아니면 dedicated management Service 가 별도로 떠야 하는지
- 9001 port 가 LoadBalancer / NodePort 로 실수 노출되지 않도록 network policy 설정 검증 (`management.server.address=127.0.0.1` 또는 cluster-internal IP 만 bind)
- mTLS for management 요구 시 `management.server.ssl.client-auth=need` 와 client cert 발급 / rotation 정책
## ca-tmpl 함의 (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용이 아니라 ca-tmpl 결정 컨텍스트 해석. wiki 추출 시 `wiki/projects/ca-skeleton-operational-contract` source-summary 로 이전.
- **ca-tmpl 9001 결정의 공식 근거**:
- 공식 문서가 "different HTTP port" 옵션을 직접 권고 (`SB-ACT-PORT-C2`, `C3`) → ca-tmpl 9001 결정은 공식 옵션 따른 것.
- cloud 환경에서는 "default port + path ACL" 도 sensible default 라고 공식이 인정 (`SB-ACT-PORT-C1`) → ca-tmpl 의 "platform ingress 보호 + 문서화 시 single-port 허용" 도 정합.
- **대안 그룹 (ca-tmpl 결정 비교용)**:
- **대안 1 (single port + path ACL)**: cloud / Kubernetes ingress 환경. ingress rule 이 `/actuator/*` 를 internal LB 로 routing.
- **대안 2 (separate port = ca-tmpl baseline)**: management port + ACL. data-center / self-managed.
- **대안 3 (mTLS for management)**: management port + client cert. zero-trust.
- **대안 4 (Service mesh — Istio sidecar)**: PeerAuthentication + AuthorizationPolicy 로 management path 만 internal traffic 허용.
- **장점**: app port (8080) 와 다른 firewall / ACL rule 적용 가능. 실수로 ingress 가 management endpoint 를 publish 할 위험 ↓. port-level monitoring 분리 (latency budget 분리).
- **단점**: container / network 운영 부담 (두 port expose). Kubernetes Service 정의 한 번 더 필요. cloud LB 비용 ↑ 가능.
## 메모 / Notes
- 2026-05-27 재검증: 6개 핵심 인용 모두 verbatim 으로 monitoring reference 의 해당 섹션에 존재 확인. management.server.port 예시 (`management.server.port=8081`) 도 공식 예시 그대로.
- 다음 fetch 후보:
- `https://docs.spring.io/spring-boot/reference/actuator/monitoring.html#actuator.monitoring.customizing-management-server-context-path` (path prefix 변경)
- `https://docs.spring.io/spring-boot/reference/actuator/monitoring.html#actuator.monitoring.enabling-cross-origin-requests` (CORS for actuator)
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/actuator-endpoint-exposure-spring-official]] — endpoint exposure default
- [[raw/official-docs/runtime-health-spring-actuator-groups]] — health group 모델
- 인용하는 branch:
- [[raw/branch-notes/feature-management-actuator-security-contract]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/adapter-java-spi-serviceloader.md
@@ -0,0 +1,108 @@
---
title: Java SPI (Service Provider Interface) + ServiceLoader — adapter 대안
source_type: official-doc
url: https://docs.oracle.com/javase/tutorial/ext/basics/spi.html
archive_url:
status: raw
confidence: high
related_branches: [feature-integration-adapter-templates, feature-skeleton-package-blueprint-contract]
related_projects: [ca-tmpl]
tags: [ca-tmpl, adapter, java, spi, serviceloader, plugin, alternative]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Java SPI (Service Provider Interface) + ServiceLoader — adapter 대안
> Layer: `raw/official-docs/` — Oracle Java Tutorial "Creating Extensible Applications" 의 SPI/ServiceLoader 발췌. ca-tmpl `feature-integration-adapter-templates` branch의 **adapter on/off 메커니즘 대안 4** (Java 표준 plugin architecture) 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-integration-adapter-templates]] | Group G-I 대안 4 (Java SPI / ServiceLoader plugin architecture) 의 시맨틱·한계 — Spring `@ConditionalOnProperty` 채택 결정의 비교 기준 |
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | adapter 후보 package 설계 시 "Spring DI vs classpath SPI" 분기 검토 근거 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-integration-adapter-templates` branch의 **대안 4**. branch는 Spring `@ConditionalOnProperty` 기반 optional module을 채택했음. 대안으로 Java 표준 SPI (ServiceLoader)가 있는데, 둘의 시맨틱 차이를 명확히 보존.
## 출처 / Source
- 원본 URL: https://docs.oracle.com/javase/tutorial/ext/basics/spi.html
- 보조 URL: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/ServiceLoader.html
- 아카이브 URL: (미수집)
- 저자 / 조직: Oracle (Java Tutorial 공식)
- 발행 상태: Java SE 표준 (JDK 1.6+), 현재까지 유효
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Service Provider Interface (SPI) 정의] "The set of public interfaces and abstract classes that a service defines. The SPI defines the classes and methods available to your application."
> [§ServiceLoader 역할] "The `java.util.ServiceLoader` class helps you find, load, and use service providers. It searches for service providers on your application's class path or in your runtime environment's extensions directory. It loads them and enables your application to use the provider's APIs."
> [§META-INF/services 등록] "To register your service provider, you create a provider configuration file, which is stored in the `META-INF/services` directory of the service provider's JAR file. The name of the configuration file is the fully qualified class name of the service provider, in which each component of the name is separated by a period (`.`), and nested classes are separated by a dollar sign (`$`)."
> [§Lazy instantiation + caching] "Providers are located and instantiated on demand. A service loader maintains a cache of the providers that were loaded. Each invocation of the loader's `iterator` method returns an iterator that first yields all of the elements of the cache, in instantiation order. The service loader then locates and instantiates any new providers, adding each one to the cache in turn. You can clear the provider cache with the `reload` method."
> [§Default constructor 요구] "The `ServiceLoader` class requires that the single exposed provider type has a default constructor, which requires no arguments. This enables the `ServiceLoader` class to easily instantiate the service providers that it finds."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SPI-C1 | SPI 는 service 가 정의하는 public interfaces + abstract classes 집합으로, application 이 사용할 수 있는 classes/methods 를 정의 | [§Service Provider Interface (SPI) 정의] "The set of public interfaces and abstract classes that a service defines. The SPI defines the classes and methods available to your application." | `official-vendor-doc` | Java SE SPI 패턴 일반 | SPI 가 on/off 토글 메커니즘을 포함한다는 뜻은 아님 — provider 등록 = 자동 활성 |
| SPI-C2 | `ServiceLoader` 는 application classpath 또는 runtime extensions directory 에서 service provider 를 검색·로드하여 application 에 노출 | [§ServiceLoader 역할] "It searches for service providers on your application's class path or in your runtime environment's extensions directory. It loads them and enables your application to use the provider's APIs." | `official-vendor-doc` | classpath 기반 plugin discovery 시나리오 | property/env 기반 활성 제어 메커니즘이 있다는 뜻은 아님 (classpath 존재 = 활성) |
| SPI-C3 | provider 등록은 JAR 의 `META-INF/services/` 디렉토리에 fully qualified service interface name 의 파일을 두고, 각 줄에 provider FQN 을 나열하는 방식 | [§META-INF/services 등록] "To register your service provider, you create a provider configuration file, which is stored in the `META-INF/services` directory of the service provider's JAR file. The name of the configuration file is the fully qualified class name of the service provider" | `official-vendor-doc` | JAR-packaged provider 배포 | YAML/property 기반 등록이나 Spring `application.yml` 통합이 가능하다는 뜻은 아님 |
| SPI-C4 | provider 는 on-demand instantiate 되며 `ServiceLoader` 는 캐시를 유지, `iterator()` 호출 시 캐시된 provider 부터 instantiation order 로 yield, `reload()` 로 캐시 비우기 가능 | [§Lazy instantiation + caching] "Providers are located and instantiated on demand. A service loader maintains a cache of the providers that were loaded. Each invocation of the loader's `iterator` method returns an iterator that first yields all of the elements of the cache, in instantiation order... You can clear the provider cache with the `reload` method." | `official-vendor-doc` | 단일 `ServiceLoader` 인스턴스의 라이프사이클 | "lazy" 가 모든 provider 의 instantiate 비용을 0 으로 만든다는 뜻은 아님 — 첫 iterate 시 등록된 모든 provider 가 검출됨 |
| SPI-C5 | `ServiceLoader` 는 exposed provider type 에 **default (no-arg) constructor 요구** | [§Default constructor 요구] "The `ServiceLoader` class requires that the single exposed provider type has a default constructor, which requires no arguments." | `official-vendor-doc` | 표준 `ServiceLoader.load()` 경로 | constructor injection 으로 dependency 주입이 가능하다는 뜻은 아님 (Java 9+ `provider()` static method 패턴은 별도 문서) |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `SPI-C1`: SPI 의 정의 (interface + abstract class 집합)
- `SPI-C2`: `ServiceLoader` 의 검색 경로 (classpath / extensions dir)
- `SPI-C3`: `META-INF/services/<FQN>` 파일 형식 의무
- `SPI-C4`: lazy instantiation + 캐시 + `reload()` 시맨틱
- `SPI-C5`: provider 의 default constructor 강제 요구
- **이 자료가 증명하지 않는 것**:
- SPI 가 property/env 기반 on/off 제어를 지원한다는 명제 (classpath 존재 = 활성, 본 인용 범위에서 disable 메커니즘 부재)
- Spring DI 컨테이너와의 통합 (Spring `@Autowired`/`@Transactional` 이 SPI provider 에 적용된다는 보장 없음)
- JPMS (Java 9+) `provides ... with ...` 선언과의 정확한 통합 시맨틱 (별도 JPMS 문서 필요)
- 검출 시점이 Spring `ApplicationContext` 시작 시점과 어떻게 정렬되는지
- **내 프로젝트(ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
- `ConditionalOnProperty` 처럼 "기본 disabled + property 로 enable" 시맨틱을 SPI 로 표현하려면 별도 wrapper layer 가 필요 (본 문서로 보장 안 됨)
- branch 의 "Layer 1 ApplicationContext bean count = 0" 검증을 SPI provider 에 적용할 수 없음 — SPI provider 는 Spring bean 이 아니므로 별도 검증 메커니즘 필요
## 메모 / Notes (내 프로젝트 해석 — 미검증)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: 프레임워크 / 라이브러리 작성자 입장에서 사용자가 외부 jar drop-in으로 기능 확장하게 하고 싶을 때 (JDBC Driver, SLF4J binding, JPA provider, Spring Boot `SpringApplicationRunListener` 등 실제로 사용 중).
- 장점:
- **표준 JDK**: 의존성 없음. ClassLoader 수준 작동.
- **classpath drop-in**: jar만 넣으면 `META-INF/services/` 자동 감지.
- JPMS (Java 9+) `provides ... with ...` 선언과 통합.
- 단점 / ca-tmpl 적용 시 한계:
- **on/off 제어가 없음**: classpath에 존재하면 즉시 provider로 등록. branch가 요구한 "disabled state 기본값"을 표현할 표준 메커니즘이 없음. property 기반 게이팅이 SPI에는 없음.
- **DI 통합 없음**: ServiceLoader가 instantiate하는 객체는 Spring bean이 아님. `@Autowired`, `@Transactional` 등 Spring 기능 미적용. wrapping이 별도로 필요.
- **default constructor 강제**: 의존 주입을 생성자로 받을 수 없음.
- **검출 비용**: provider 검색이 lazy하지만 한 번 트리거되면 모든 provider iterate.
- **branch Layer 1 검증 (ApplicationContext bean count = 0) 불가능**: bean이 애초에 ApplicationContext에 없음. 검증 메커니즘을 별도로 짜야 함.
- ca-tmpl 결정과의 차이:
- ca-tmpl: Spring DI + `@ConditionalOnProperty` 1차. ApplicationContext bean 등록 여부로 enable/disable 검증.
- SPI: classpath 기반 자동 발견. enable/disable이 jar inclusion/exclusion으로만 표현됨 (= build artifact 분리). branch의 "build artifact 1개 + env 주입" 결정과 충돌.
- 채택 시점 후보: 프레임워크 자체를 만들 때, 또는 third-party가 plugin을 작성하게 해야 할 때. application 내부 adapter on/off에는 부적합.
- 신뢰도: `official-doc` 등급. Oracle Java Tutorial + JDK API doc.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/adapter-spring-boot-autoconfig-custom-starter]] (ca-tmpl 채택안 — Spring Boot AutoConfiguration + `@ConditionalOnProperty`)
- 인용하는 branch:
- [[raw/branch-notes/feature-integration-adapter-templates]]
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
- 대안 그룹: **Group I — Integration adapter templates** (대안 5종)
- 본 source의 위치: **대안 4: Java SPI (ServiceLoader) plugin architecture**
- 인용하는 wiki: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/adapter-spring-boot-autoconfig-custom-starter.md
@@ -0,0 +1,119 @@
---
title: Spring Boot Auto-configuration + custom starter 공식 문서
source_type: official-doc
url: https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html
archive_url:
status: raw
confidence: high
related_branches: [feature-integration-adapter-templates, feature-architecture-enforcement-rules, feature-runtime-health-lifecycle-contract, feature-skeleton-package-blueprint-contract]
related_projects: [ca-tmpl]
tags: [ca-tmpl, adapter, spring-boot, auto-configuration, conditional-on-property, custom-starter]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Spring Boot Auto-configuration + Custom Starter
> Layer: `raw/official-docs/` — Spring Boot 3.5 reference "Developing Auto-configuration" + `@ConditionalOnProperty` / `@ConditionalOnBooleanProperty` Javadoc 발췌. ca-tmpl 그룹 G-I (`feature-integration-adapter-templates`) 의 **adapter on/off 채택안** 1차 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-integration-adapter-templates]] | optional adapter 의 `@ConditionalOnProperty(name="app.adapter.{adapterName}.enabled", havingValue="true")` 결정 — Layer 1 메커니즘의 정확한 공식 시맨틱 |
| [[raw/branch-notes/feature-architecture-enforcement-rules]] | ArchUnit Layer 2 검사가 Spring 공식 cover 밖이라는 분리 근거 (본 문서는 Layer 1 만 cover) |
| [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] | required vs optional dependency SSOT 의 boolean 시맨틱 (`@ConditionalOnBooleanProperty` 3.5.0+ 정합성) |
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | adapter 후보 package 의 AutoConfiguration import 등록 위치 (`META-INF/spring/...AutoConfiguration.imports`) 결정 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-integration-adapter-templates` branch의 **결정 근거 (canonical reference)**. branch는 "optional adapter는 `@ConditionalOnProperty(name="app.adapter.{adapterName}.enabled", havingValue="true")` 적용"을 결정. 이 결정의 정확한 공식 시맨틱과 대안(`@AutoConfiguration` without `ConditionalOnProperty`, `@Profile`)과의 차이를 명확히 보존.
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html
- 보조 URL: https://docs.spring.io/spring-boot/3.5/api/java/org/springframework/boot/autoconfigure/condition/ConditionalOnProperty.html
- 보조 URL: https://docs.spring.io/spring-boot/3.5/api/java/org/springframework/boot/autoconfigure/condition/ConditionalOnBooleanProperty.html
- 아카이브 URL: (미수집)
- 저자 / 조직: Spring Team (spring-projects)
- 발행 상태: Spring Boot 3.5 GA (Java 21 baseline), `@ConditionalOnBooleanProperty` since 3.5.0
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Understanding Auto-configured Beans] "Classes that implement auto-configuration are annotated with `@AutoConfiguration`. This annotation itself is meta-annotated with `@Configuration`, making auto-configurations standard `@Configuration` classes. Additional `@Conditional` annotations are used to constrain when the auto-configuration should apply. Usually, auto-configuration classes use `@ConditionalOnClass` and `@ConditionalOnMissingBean` annotations. This ensures that auto-configuration applies only when relevant classes are found and when you have not declared your own `@Configuration`."
> [§Locating Auto-configuration Candidates] "Spring Boot checks for the presence of a `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` file within your published jar. The file should list your configuration classes, with one class name per line." (additional: "Auto-configurations must be loaded _only_ by being named in the imports file. Make sure that they are defined in a specific package space and that they are never the target of component scanning.")
> [§`@ConditionalOnProperty` Javadoc] "`@Conditional` that checks if the specified properties have a specific value. By default the properties must be present in the `Environment` and not equal to `false`." (collection note: "This condition cannot be reliably used for matching collection properties... It is better to use a custom condition for such cases.")
> [§`@ConditionalOnBooleanProperty` Javadoc, since 3.5.0] "`@Conditional` annotation that checks if the specified properties have a specific boolean value. By default the properties must be present in the `Environment` and equal to `true`. The `havingValue()` and `matchIfMissing()` attributes allow further customizations."
> [§Naming + Configuration keys] "Do not start your module names with `spring-boot`, even if you use a different Maven `groupId`." / "If your starter provides configuration keys, use a unique namespace for them. In particular, do not include your keys in the namespaces that Spring Boot uses (such as `server`, `management`, `spring`, and so on)... As a rule of thumb, prefix all your keys with a namespace that you own (for example `acme`)."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SBAC-C1 | auto-configuration class 는 `@AutoConfiguration` (= meta-annotated `@Configuration`) + 추가 `@Conditional` (보통 `@ConditionalOnClass`, `@ConditionalOnMissingBean`) 로 적용 조건을 제한 | [§Understanding Auto-configured Beans] "Classes that implement auto-configuration are annotated with `@AutoConfiguration`. This annotation itself is meta-annotated with `@Configuration`... Additional `@Conditional` annotations are used to constrain when the auto-configuration should apply. Usually, auto-configuration classes use `@ConditionalOnClass` and `@ConditionalOnMissingBean` annotations." | `official-vendor-doc` | Spring Boot AutoConfiguration 작성 일반 | `@ConditionalOnProperty` 가 표준 권장 조합이라는 뜻은 아님 (문서가 명시한 표준 조합은 OnClass + OnMissingBean) |
| SBAC-C2 | auto-configuration discovery 는 published jar 의 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 파일에 한 줄당 한 class FQN 을 나열하는 방식이며, **imports file 에 등록되지 않은 class 는 auto-configuration 으로 로드되지 않음** + component scan 대상이 되면 안 됨 | [§Locating Auto-configuration Candidates] "Spring Boot checks for the presence of a `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` file... The file should list your configuration classes, with one class name per line." + "Auto-configurations must be loaded _only_ by being named in the imports file." | `official-vendor-doc` | Spring Boot 2.7+ 의 `AutoConfiguration.imports` 메커니즘 | 기존 `spring.factories` 가 deprecated 라는 뜻은 본 인용 범위 밖 (별도 release note) |
| SBAC-C3 | `@ConditionalOnProperty` 는 default 로 property 가 Environment 에 **존재** + 값이 **`false` 가 아닐 때** 매칭. `matchIfMissing` default 는 `false`. **collection property 에는 신뢰성 있게 사용 불가** | [§`@ConditionalOnProperty` Javadoc] "By default the properties must be present in the `Environment` and not equal to `false`." + "This condition cannot be reliably used for matching collection properties... It is better to use a custom condition for such cases." | `official-reference` | Spring Boot 3.x `@ConditionalOnProperty` 사용 | `havingValue` 미지정 시 모든 임의 string 값에 매칭한다는 뜻은 아님 — 명시적으로 `false` 만 reject, 빈 string 은 표 참조 |
| SBAC-C4 | `@ConditionalOnBooleanProperty` (since 3.5.0) 는 boolean 시맨틱을 명시적으로 강제 — default 로 property 가 Environment 에 **존재** + 값이 **`true`** 일 때 매칭, `matchIfMissing` default `false` | [§`@ConditionalOnBooleanProperty` Javadoc, since 3.5.0] "By default the properties must be present in the `Environment` and equal to `true`. The `havingValue()` and `matchIfMissing()` attributes allow further customizations." | `official-reference` | Spring Boot 3.5.0+ 환경 | 3.5.0 미만 버전에서 동일 시맨틱이 가능하다는 뜻은 아님 (그 경우 `@ConditionalOnProperty(havingValue="true")` 명시 필요) |
| SBAC-C5 | starter 의 configuration key 는 **own namespace** prefix 의무. `server`, `management`, `spring` 등 Spring Boot 가 사용하는 namespace 사용 금지 (향후 Spring 이 충돌 변경 가능). module 이름은 `spring-boot` 로 시작 금지 | [§Naming + Configuration keys] "Do not start your module names with `spring-boot`..." + "do not include your keys in the namespaces that Spring Boot uses (such as `server`, `management`, `spring`, and so on)... prefix all your keys with a namespace that you own (for example `acme`)." | `official-vendor-doc` | custom starter 배포 | "acme" 이외의 특정 prefix 가 권장된다는 뜻은 아님 — 본 문서는 예시일 뿐 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `SBAC-C1`: `@AutoConfiguration` 의 메타 구조 + 표준 `@Conditional` 조합 (`OnClass` + `OnMissingBean`)
- `SBAC-C2`: `AutoConfiguration.imports` 파일 위치·형식·discovery 의무
- `SBAC-C3`: `@ConditionalOnProperty` 의 default 매칭 규칙 + collection 한계
- `SBAC-C4`: `@ConditionalOnBooleanProperty` (3.5.0+) 의 명시적 boolean 시맨틱
- `SBAC-C5`: custom starter 의 namespace/naming 의무
- **이 자료가 증명하지 않는 것**:
- "ApplicationContext bean count = 0" 검증이 Spring 공식 권장 verification 패턴이라는 명제 (본 문서는 verification 메커니즘을 명시 안 함)
- ArchUnit 기반 정적 검사가 Spring 공식 권장 패턴이라는 명제 (Spring docs 범위 밖)
- `AdapterDisabledException` 같은 runtime fail-fast 패턴 (ca-tmpl 자체 contract, 공식 문서 부재)
- `@Profile``@ConditionalOnProperty` 의 정확한 우선순위·결합 시맨틱
- **내 프로젝트(ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
- `havingValue="true"` 명시 + `matchIfMissing=false` 조합이 branch 의 "기본 disabled" 의도를 정확히 표현하는지 (intent 일치 확인)
- 3.5.0 미만 baseline 인 경우 `@ConditionalOnBooleanProperty` 사용 불가 → fallback 필요
- starter 의 `acme` 같은 prefix 를 ca-tmpl 의 `app.adapter.<name>.enabled` 네임스페이스로 매핑하는 결정 (본 문서는 prefix 예시만 제공)
## 메모 / Notes (내 프로젝트 해석 — 미검증)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: 선택형 adapter (Kafka / Redis / Slack / Google Email) 같은 외부 통합 모듈을 단일 codebase에 두되, application property로 on/off 전환.
- 장점:
- **표준 메커니즘**: `@ConditionalOnProperty` + `AutoConfiguration.imports` 조합은 Spring 공식 패턴.
- **boot 시점 결정**: false → bean 자체 등록 안 됨. ApplicationContext 검사로 검증 가능.
- branch가 결정한 **3-layer detection (ApplicationContext / ArchUnit / Runtime AdapterDisabledException)** 중 Layer 1을 정확히 cover.
- Spring Boot 3.5부터 `@ConditionalOnBooleanProperty` 추가 — boolean 시맨틱이 명시적으로 강제됨. branch의 "boolean true/false only" 결정과 정합.
- 단점 / 함정:
- `havingValue` 누락 시: property가 단순히 "존재"하면 매칭 → false 의도가 무력화될 수 있음. branch는 `havingValue="true"` 명시.
- `matchIfMissing`은 default false. 누락된 env가 자동으로 enable로 해석되지 않도록 주의.
- collection property에는 사용 부적합 (Javadoc 명시).
- ca-tmpl 결정과의 매핑:
- branch Layer 1: `@ConditionalOnProperty(name="app.adapter.{name}.enabled", havingValue="true")` → 본 문서 인용 그대로.
- branch Layer 2 (ArchUnit `noClasses().that().resideInAPackage("..application..").should().dependOnClassesThat().resideInAPackage("..adapters.{disabled-adapter}..")`): Spring 공식 문서 범위 밖. ArchUnit 별도 source 필요.
- branch Layer 3 (`AdapterDisabledException` + log code `REQUIRED_ADAPTER_DISABLED`): branch 자체 contract. 공식 문서가 강제하지 않는 영역.
- 대안 비교:
- `@Profile("kafka")` — boolean 시맨틱 부재, 다중 활성/비활성 표현이 어려움.
- `AutoConfiguration` without ConditionalOnProperty — classpath 존재만으로 bean 등록 → 비활성 의도 표현 불가.
- SPI/ServiceLoader — Spring DI와 별도 라이프사이클. Spring 환경에서는 over-engineering.
- 신뢰도: `official-doc` 등급. Spring 공식 reference + API doc.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/adapter-java-spi-serviceloader]] (대안 4 — Java 표준 SPI)
- [[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]] (Layer 2 ArchUnit 정적 검사 한계 평가)
- [[raw/official-docs/governance-archunit-official]] (ArchUnit fitness function 일반)
- 인용하는 branch:
- [[raw/branch-notes/feature-integration-adapter-templates]]
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
- [[raw/branch-notes/feature-runtime-health-lifecycle-contract]]
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
- 대안 그룹: **Group I — Integration adapter templates** (대안 5종: Spring Boot AutoConfiguration / Plugin architecture OSGi-style / `@Profile` / Java SPI / FF4J·Togglz)
- 본 source의 위치: **대안 1: Spring Boot AutoConfiguration + `@ConditionalOnProperty` (branch의 채택안)**
- 인용하는 wiki: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/api-versioning-google-aip-180.md
@@ -0,0 +1,127 @@
---
title: Google AIP-180 — Backwards compatibility
source_type: official-doc
url: https://google.aip.dev/180
archive_url:
status: reviewed
confidence: high
tags: [ca-tmpl, api-compatibility, deprecation, aip-180, google, breaking-change]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-api-compatibility-deprecation-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Google AIP-180 — Backwards compatibility
> Layer: `raw/official-docs/` — Google API Improvement Proposals 의 backwards compatibility 정책. ca-tmpl breaking change catalog 7행 분류의 reference.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-api-compatibility-deprecation-contract]] | ca-tmpl breaking change catalog 7행 (remove field / rename / change type / narrow enum / add required request field / add optional response field / change error code) 분류의 표준 정합성 검증 근거 + Stripe / AIP 모델 비교 |
| [[raw/project-notes/ca-skeleton-operational-contract]] | API 호환성 정책 섹션 (catalog 7행 정당화 근거) |
## 컨텍스트 / 왜 저장했는지
ca-tmpl breaking change catalog 7행 분류 (`remove field`, `rename`, `change type`, `narrow enum`, `add required request field`, `add optional response field`, `change error code`) 가 AIP-180 의 분류와 어떻게 정합/차이가 있는지 검증하기 위함. canonical 승급 시 catalog 정당화에 필요. Google AIP 는 internal Google API 의 design guideline 이지만 외부 개발자에게도 reference 로 널리 인용됨.
## 출처 / Source
- 원본 URL: https://google.aip.dev/180
- 관련 AIP: AIP-181 (Stability levels), AIP-185 (Versioning)
- 아카이브 URL: (미수집)
- 저자 / 조직: Google (API Improvement Proposals working group)
- 발행일: continuously updated (AIP-180 자체에 fixed 발행일 없음)
- 마지막 확인일: 2026-05-27 (WebFetch 재검증 성공 — 5개 quote 모두 verbatim 일치, strength `needs-confirmation``official-vendor-doc` 으로 upgrade. [2026-05-25 capture] verbatim 보존본 유지). 현재 section headings: Guidance / Adding components / Removing or renaming components / Moving components between files / Moving into oneofs / Changing the type of fields / Changing string length / Changing resource names / Semantic changes / Further reading / Rationale / Changelog
## 핵심 인용 / Key quotes (verbatim)
> [§Removing components, captured 2026-05-22] "Existing components (interfaces, methods, messages, fields, enums, or enum values) must not be removed from existing APIs in the same major version."
> [§Renaming components, captured 2026-05-22] "Renaming a component is semantically equivalent to 'remove and add'."
> [§Default behavior, captured 2026-05-22] "Any field being populated by clients must have a default behavior matching the behavior before the field was introduced."
> [§Required fields, captured 2026-05-22] "New required fields must not be added to existing request messages or resources."
> [§Core principle, captured 2026-05-22] "Existing client code must not be broken by a service updating to a new minor or patch release."
> **[2026-05-27 verified — WebFetch 재검증 성공]**: 본 5개 quote (AIP180-C1 ~ AIP180-C5) 모두 https://google.aip.dev/180 live 페이지에서 FOUND VERBATIM. strength `needs-confirmation` → `official-vendor-doc` 으로 upgrade. [2026-05-25 capture] verbatim 본문 그대로 유지. AIP-181 / AIP-185 와의 cross-reference 는 별도 raw 작성 시 재확인.
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AIP180-C1 | 같은 major version 안에서 기존 component (interface / method / message / field / enum / enum value) 를 제거하면 안 됨 (`must not`) | [§Removing or renaming components, captured 2026-05-22 + 2026-05-27 verified verbatim] "Existing components (interfaces, methods, messages, fields, enums, or enum values) must not be removed from existing APIs in the same major version." | `official-vendor-doc` [2026-05-27 verified] | Google API design (외부 reference 로 인용 가능) | 다른 major version (v1 → v2) 으로 이동 시 제거 정책은 별도 (AIP-181 / AIP-185 영역) |
| AIP180-C2 | component renaming 은 의미상 "remove + add" 와 동등 (즉 rename 은 breaking) | [§Removing or renaming components, captured 2026-05-22 + 2026-05-27 verified verbatim] "Renaming a component is semantically equivalent to 'remove and add'." | `official-vendor-doc` [2026-05-27 verified] | rename 결정의 breaking 분류 | alias / 양쪽 동시 노출 같은 mitigation 정책은 본 인용에 없음 — `C1` 과 함께 same major version 안에서는 사실상 금지 |
| AIP180-C3 | client 가 채우는 모든 field 는 도입 이전 동작과 일치하는 default behavior 를 가져야 함 (`must`) | [§Adding components / Default behavior, captured 2026-05-22 + 2026-05-27 verified verbatim] "Any field being populated by clients must have a default behavior matching the behavior before the field was introduced." | `official-vendor-doc` [2026-05-27 verified] | 새 optional field 추가 시 default 동작 정책 | 모든 새 field 가 optional 이어야 한다는 뜻은 아님 — `C4` 가 required field 별도 다룸 |
| AIP180-C4 | 기존 request message / resource 에 새 required field 를 추가하면 안 됨 (`must not`) | [§Adding components / Required fields, captured 2026-05-22 + 2026-05-27 verified verbatim] "New required fields must not be added to existing request messages or resources." | `official-vendor-doc` [2026-05-27 verified] | 새 field 추가 시 required vs optional 결정 | 새 endpoint / 새 message 에서는 required field 자유 — 본 인용은 기존 message 만 |
| AIP180-C5 | 서비스가 minor 또는 patch release 로 업데이트되었을 때 기존 client code 가 깨지면 안 됨 (`must not`, 핵심 원칙) | [§Guidance / Core principle, captured 2026-05-22 + 2026-05-27 verified verbatim] "Existing client code must not be broken by a service updating to a new minor or patch release." | `official-vendor-doc` [2026-05-27 verified] | semver 의 minor / patch release 호환성 | major version bump 시 breaking change 허용 여부는 본 인용 범위 밖 (AIP-185 영역) |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것** (2026-05-22 capture + 2026-05-27 WebFetch verbatim 재검증):
- `AIP180-C1`: 같은 major version 안에서 component 제거 금지
- `AIP180-C2`: rename = breaking
- `AIP180-C3`: 새 field 의 default 동작은 이전과 일치해야 함
- `AIP180-C4`: 기존 message 에 새 required field 추가 금지
- `AIP180-C5`: minor/patch 에서 client breaking 금지
- **이 자료가 증명하지 않는 것**:
- 다른 major version (v1 → v2) 으로의 migration 정책 — AIP-185 영역
- deprecation 통지 / window / sunset 정책 — AIP-180 본문에 부분만 있을 수 있음 (재확인 필요)
- error code (status code / error enum) 변경의 정확한 분류 — AIP-180 은 enum value 제거 금지 원칙으로 같은 결론에 도달하지만 명시적 "error code change" 행은 본 인용에 없음
- CI breaking diff 자동 차단 같은 운영 메커니즘
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- 2026-05-27 시점 AIP-180 본문 재확인 (continuously updated)
- ca-tmpl 의 `migration window 90d/30d` 가 AIP-180 의 "절대 제거 금지" (`C1`) 와 다른 정책임을 명시
- ca-tmpl 의 `narrow enum 을 new version 으로` 정책이 AIP-180 의 "enum value 제거 금지" 와 호환 가능한지 (new version 도입 시점에서는 호환)
## ca-tmpl 함의 (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용이 아닌 ca-tmpl 결정 컨텍스트 해석. wiki 추출 시 source-summary 로 옮겨야 함.
### AIP-180 과 ca-tmpl catalog 정합
| catalog 행 | AIP-180 분류 (인용 근거) | 일치 여부 |
|---|---|---|
| remove response field | breaking — `AIP180-C1` ("must not be removed") | 일치 |
| rename response field | breaking — `AIP180-C2` ("remove and add") | 일치 |
| change field type/format | breaking — `AIP180-C5` (client code breaks) | 일치 (간접) |
| narrow enum values | breaking — `AIP180-C1` (enum values 도 component) | 일치 |
| add required request field | breaking — `AIP180-C4` (명시적 금지) | 일치 |
| add optional response field | additive — `AIP180-C3` (default 동작 보장 시) | 일치 |
| change error code | breaking for clients — `AIP180-C1` (enum value 제거 금지) | 일치 (간접) |
### ca-tmpl 이 AIP-180 보다 **약한** 부분
- AIP-180 은 same major version 안에서 component 제거 사실상 영구 금지 (`C1`).
- ca-tmpl 은 `migration window 90d/30d` 후 제거 허용 — internal-first skeleton 에 합리적 trade-off (Google 의 Stripe / public API 보다 운영 부담 낮음).
### ca-tmpl 이 AIP-180 보다 **강한** 부분
- ca-tmpl: `migration window 90d/30d` **의무화** (AIP-180 은 사실상 무기한이라 명시적 window 없음).
- ca-tmpl: CI breaking diff release-blocking (AIP-180 은 정책만 명시, 강제 메커니즘 별도).
### Trade-off
- AIP-180 전면 도입: 사실상 영구 호환. Stripe 모델과 유사. 운영비용 큼.
- ca-tmpl: window 후 제거 허용. internal-first skeleton 에 합리적.
## 메모 / Notes
- **AIP vs RFC vs Google internal**: AIP 는 Google internal API design guideline 이지만 외부에 공개되어 reference 로 인용 가능. 정식 IETF/W3C 표준이 아님 — 외부 인용 시 "Google AIP" 로 명시, "공식 표준" 표현 금지.
- **재검증 완료**: 2026-05-27 google.aip.dev WebFetch 재검증 성공 (5/5 verbatim). continuously updated 특성상 다음 검토 시 재확인 권장.
- **관련 AIP**: AIP-181 (Stability levels), AIP-185 (Versioning) — 별도 raw 작성 후 통합 분석 권장.
## Related / 관련
- 같은 주제 다른 official-doc / 표준:
- AIP-181 (Stability levels) — 별도 raw 작성 후보
- AIP-185 (Versioning) — 별도 raw 작성 후보
- 인용하는 branch:
- [[raw/branch-notes/feature-api-compatibility-deprecation-contract]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/arch-acl-microsoft-pattern.md
@@ -0,0 +1,98 @@
---
title: "official-doc / Anti-Corruption Layer Pattern (Microsoft Azure Architecture Center)"
source_type: official-doc
url: https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer
archive_url:
related_branches: [feature-boundary-validation-mapping-contract]
related_projects: [ca-skeleton]
tags: [official-doc, ca-skeleton, architecture, integration, anti-corruption-layer, ddd, hexagonal]
created: 2026-05-28
last_reviewed: 2026-05-28
status: raw
confidence: high
vendor: Microsoft Azure Architecture Center
author: claytonsiemens77
published: 2022-07-28
---
# Anti-Corruption Layer Pattern (Microsoft Azure Architecture Center)
> Layer: `raw/official-docs/` — Microsoft Azure Architecture Center 공식 클라우드 설계 패턴 레퍼런스의 원문 발췌·출처 기록.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-boundary-validation-mapping-contract]] | outbound HTTP / external dependency 응답 → domain 변환이 본 branch 의 mapper 정의 안에 포함된다는 scope 명확화 (블라인드 B7). ACL 패턴이 동일한 mapper 책임 (변환 + 검증 + 정규화) 을 inbound 가 아닌 outbound-response 방향에 적용한다는 근거 |
## 출처 / Source
- 원본 URL: https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer
- 아카이브 URL: (미제공)
- 저자 / 조직: claytonsiemens77 / Microsoft Azure Architecture Center
- 발행일: 2022-07-28 (최종 업데이트: 2025-12-09)
- 마지막 확인일: 2026-05-28
## 왜 저장했는지 / Why archived
`feature-boundary-validation-mapping-contract` branch 의 mapper 범위가 inbound REST 경계만 명시하고 outbound HTTP adapter 응답 → domain 변환 (블라인드 B7) 을 다루지 않는다. Microsoft Azure Architecture Center 의 ACL 패턴 공식 정의는 "다른 의미론(semantics)을 가진 두 서브시스템 사이" 에서 번역 책임을 가지는 계층을 normative 하게 정의하므로, outbound-response 방향 mapper 도 동일한 boundary mapper 범위 안에 포함된다는 공식 근거로 사용한다.
## 핵심 인용 / Key quotes (verbatim)
> [§intro] "Implement a façade or adapter layer between different subsystems that don't share the same semantics. This layer translates requests that one subsystem makes to the other subsystem. Use this pattern to ensure that an application's design isn't limited by dependencies on outside subsystems. This pattern was first described by Eric Evans in *Domain-Driven Design*."
> (line 7 in fetched text)
> [§Context and problem] "Maintaining access between new and legacy systems can force the new system to adhere to at least some of the legacy system's APIs or other semantics. When these legacy features have quality issues, supporting them "corrupts" what might otherwise be a cleanly designed modern application."
> (line 15 in fetched text)
> [§Solution] "Isolate the different subsystems by placing an anti-corruption layer between them. This layer translates communications between the two systems, allowing one system to remain unchanged while the other can avoid compromising its design and technological approach."
> (line 21 in fetched text)
> [§Solution] "The anti-corruption layer contains all of the logic necessary to translate between the two systems. The layer can be implemented as a component within the application or as an independent service."
> (line 23 in fetched text — extracted from the longer paragraph)
> [§Issues and considerations] "The anti-corruption layer might add latency to calls made between the two systems."
> (line 27 in fetched text)
> [§When to use this pattern] "Two or more subsystems have different semantics, but still need to communicate."
> (line 42 in fetched text)
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| MS-ACL-C1 | ACL 은 서로 다른 의미론(semantics)을 공유하지 않는 서브시스템 사이에 위치하는 façade 또는 adapter 계층이다 | [§intro] "Implement a façade or adapter layer between different subsystems that don't share the same semantics. This layer translates requests that one subsystem makes to the other subsystem." | `official-vendor-doc` | 두 서브시스템이 서로 다른 데이터 모델·프로토콜·도메인 의미론을 사용하는 모든 통합 경계 | 특정 구현 기술(언어·프레임워크·라이브러리) 선택; inbound/outbound 방향 중 어느 한 쪽만 해당된다는 주장 |
| MS-ACL-C2 | ACL 은 두 시스템 간 통신을 번역(translate)하며, 한 시스템이 변경되지 않아도 되고 다른 시스템도 설계를 타협하지 않아도 된다 | [§Solution] "This layer translates communications between the two systems, allowing one system to remain unchanged while the other can avoid compromising its design and technological approach." | `official-vendor-doc` | legacy 연동, 외부 서비스 연동, 마이크로서비스 간 모델 분리 | 번역 정확성의 단위 테스트가 자동 보장됨; 번역 과정에서 정규화·마스킹 책임이 포함됨을 직접 말하지 않음 |
| MS-ACL-C3 | ACL 은 두 시스템 간 번역에 필요한 모든 로직을 포함하며, 애플리케이션 내 컴포넌트 또는 독립 서비스로 구현 가능하다 | [§Solution] "The anti-corruption layer contains all of the logic necessary to translate between the two systems. The layer can be implemented as a component within the application or as an independent service." | `official-vendor-doc` | 단일 모놀리식 앱 내 인-프로세스 ACL, 별도 마이크로서비스형 ACL 모두 | ACL 이 반드시 별도 배포 단위여야 한다는 주장; ACL 안에서의 세부 레이어 분할 방법 |
| MS-ACL-C4 | ACL 은 두 시스템 간 호출에 레이턴시를 추가할 수 있다 | [§Issues and considerations] "The anti-corruption layer might add latency to calls made between the two systems." | `official-vendor-doc` | 동기 HTTP 호출 경로에 ACL 이 인-프로세스 또는 별도 서비스로 위치하는 경우 | 레이턴시가 허용 불가 수준임; 비동기 메시지 기반 통합에서 레이턴시 영향이 동일함 |
| MS-ACL-C5 | ACL 패턴은 두 개 이상의 서브시스템이 서로 다른 의미론을 가지지만 여전히 통신해야 할 때 사용한다 | [§When to use this pattern] "Two or more subsystems have different semantics, but still need to communicate." | `official-vendor-doc` | 외부 API · legacy 시스템 · 다른 bounded context 와의 통합 | 의미론 차이가 없는 내부 서비스 간 통신; ACL 이 성능 병목인 경우의 적용 판단 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `MS-ACL-C1`: ACL 이 공식 클라우드 아키텍처 패턴으로 정의되며, 다른 의미론을 가진 서브시스템 간 경계에 놓인다는 사실
- `MS-ACL-C2`: ACL 의 핵심 책임이 "번역(translate)"이며 한쪽 시스템의 설계 순수성을 보호한다는 사실
- `MS-ACL-C3`: ACL 이 인-프로세스 컴포넌트 또는 독립 서비스 두 가지 형태로 모두 구현 가능하다는 사실
- `MS-ACL-C4`: ACL 도입 시 레이턴시 추가 가능성이 공식 고려사항임
- `MS-ACL-C5`: 사용 시점 조건 (서로 다른 semantics + 통신 필요)
- 이 자료가 증명하지 않는 것:
- outbound HTTP adapter 응답 → domain 변환이 *반드시* 동일 mapper 로 처리되어야 한다는 구체적 구현 지침
- ACL 내부에서 normalization·masking·public field selection 이 포함되어야 한다는 직접 진술
- Spring Boot / Hexagonal architecture 의 Port-Adapter 구조와 ACL 의 정확한 대응 관계
- 단방향(inbound-only 또는 outbound-only) ACL 과 양방향 ACL 의 선택 기준
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- `feature-boundary-validation-mapping-contract` 의 mapper 가 ACL 의 "translate" 책임을 outbound-response 방향에서도 수행하는지 ArchUnit rule + integration test 로 검증 필요
- ACL 을 인-프로세스 컴포넌트(`MS-ACL-C3`)로 구현할 때 ca-skeleton 의 Hexagonal port/adapter 패키지 구조와 정합성 확인 필요
## 메모 / Notes
- 본 패턴은 Eric Evans의 *Domain-Driven Design* (2003) 에서 기원. Microsoft Azure Architecture Center 는 이를 클라우드 설계 패턴 카탈로그에 수록한 공식 벤더 문서.
- `MS-ACL-C2` ("the other can avoid compromising its design") 는 `feature-boundary-validation-mapping-contract` 의 D1/D7 결정 (모든 경계에 mapper 책임) 을 지지하나, 본 문서가 직접적으로 inbound + outbound 양방향 mapper 강제를 명시하지 않으므로 D1/D7 는 여전히 Hexagonal architecture raw 별도 보강 권장.
- 추가로 봐야 할 동일 출처 페이지: Strangler Fig pattern (`https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig`), Messaging Bridge pattern (`https://learn.microsoft.com/en-us/azure/architecture/patterns/messaging-bridge`)
## Related / 관련
- 같은 주제 DDD 기원 문서: [[raw/official-docs/arch-hexagonal-cockburn]], [[raw/official-docs/arch-clean-architecture-uncle-bob]]
- Hexagonal port-adapter 구조 적용 사례: [[raw/official-docs/hexagonal-thombergs-buckpal-github]]
- 이 자료를 인용한 wiki 요약: (미생성 — `/ingest``wiki/concepts/anti-corruption-layer.md` 예정)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/arch-clean-architecture-uncle-bob.md
@@ -0,0 +1,104 @@
---
title: The Clean Architecture — Uncle Bob (cleancoder blog 원문)
source_type: official-doc
url: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
archive_url:
status: raw
confidence: medium
tags: [architecture, clean-architecture, dependency-rule, layered-architecture, ddd, ca-skeleton-operational-contract]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-repository-access-permission-contract, feature-architecture-enforcement-rules, feature-skeleton-package-blueprint-contract]
created: 2026-05-27
last_reviewed: 2026-05-27
---
# The Clean Architecture — Uncle Bob (cleancoder blog 원문)
> Layer: `raw/official-docs/` — Robert C. Martin (Uncle Bob) 의 2012-08-13 "Clean Architecture" 포스트 원문 발췌. Dependency Rule + 4개 동심원(Entities / Use Cases / Interface Adapters / Frameworks & Drivers) 의 1차 출처.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-repository-access-permission-contract]] | D1 (도메인 → 인프라 의존 금지) 와 D5 (Repository interface 가 domain 측에 위치) 의 1차 근거 — Dependency Rule 의 "source code dependencies can only point inwards" |
| [[raw/branch-notes/feature-architecture-enforcement-rules]] | 4개 동심원 사이의 의존성 방향이 ArchUnit 규칙으로 강제할 layer 정의의 기준점 |
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | ca-tmpl 패키지 청사진 (`domain/`, `application/`, `adapter/`, `infrastructure/`) 이 Clean Architecture 의 어느 동심원에 매핑되는지 결정 근거 |
## 컨텍스트
ca-tmpl skeleton 의 모든 의존성 규칙·패키지 청사진·ArchUnit 강제 규칙이 "어느 레이어가 어느 레이어를 참조할 수 있는가" 를 결정해야 한다. Clean Architecture 원문이 그 single source of truth 후보 중 하나(다른 후보: Cockburn Hexagonal). 본 raw 는 Uncle Bob 의 원문 quote 만 보관하며, 적용 결론은 wiki/concepts 에서 별도 정리한다.
## 출처 / Source
- 원본 URL: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
- 아카이브 URL:
- 저자 / 조직: Robert C. Martin (Uncle Bob) — personal blog (`blog.cleancoder.com`)
- 발행일: 2012-08-13
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§The Dependency Rule] "source code dependencies can only point inwards"
> [§The Dependency Rule] "Nothing in an inner circle can know anything at all about something in an outer circle"
> [§Entities] "Entities encapsulate Enterprise wide business rules"
> [§Use Cases] "application specific business rules. It encapsulates and implements all of the use cases"
> [§Interface Adapters] "set of adapters that convert data from the format most convenient for the use cases and entities"
> [§Frameworks and Drivers] "outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework"
> [§Crossing boundaries] "we would arrange interfaces and inheritance relationships such that the source code dependencies oppose the flow of control"
> [§What data crosses the boundaries] "isolated, simple, data structures are passed across the boundaries"
## Claims Extracted / 추출된 주장
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CLEAN-ARCH-UB-C1 | Clean Architecture 의 핵심 규칙은 **소스 코드 의존성이 오직 안쪽으로만 향한다** (Dependency Rule) | [§The Dependency Rule] "source code dependencies can only point inwards" | `engineering-blog` | Clean Architecture 를 채택한 시스템의 레이어 간 의존 방향 | 어떤 레이어가 "안쪽" 인지 자체는 본 한 줄 인용으로 결정되지 않음 — 동심원 정의(C3~C6) 와 결합되어야 의미를 가짐 |
| CLEAN-ARCH-UB-C2 | 안쪽 원(inner circle) 은 바깥쪽 원(outer circle) 의 어떤 것도 알아서는 안 된다 — 이름·타입·함수 모두 포함 | [§The Dependency Rule] "Nothing in an inner circle can know anything at all about something in an outer circle" | `engineering-blog` | 모든 동심원 경계 | 이 원칙이 컴파일 타임만 적용되는지 런타임에도 적용되는지의 구체는 본 인용에 없음 (실무에선 둘 다로 해석) |
| CLEAN-ARCH-UB-C3 | Entities 동심원은 **Enterprise wide business rules** 를 캡슐화한다 | [§Entities] "Entities encapsulate Enterprise wide business rules" | `engineering-blog` | 도메인 모델이 여러 application 에 공유되는 조직 | 단일 application 만 있는 프로젝트에서 Entities 와 Use Cases 의 경계가 어떻게 흐려지는지는 본 인용에 없음 |
| CLEAN-ARCH-UB-C4 | Use Cases 동심원은 application-specific business rules 를 담고 모든 use case 를 캡슐화·구현한다 | [§Use Cases] "application specific business rules. It encapsulates and implements all of the use cases" | `engineering-blog` | application layer / use case layer 식별 기준 | Use Case 가 transaction script 인지 interactor 객체인지 등 구현 형태는 본 인용에 없음 |
| CLEAN-ARCH-UB-C5 | Interface Adapters 동심원은 use cases 및 entities 에 가장 편리한 포맷과 외부 포맷(DB/Web) 사이를 변환하는 adapter 집합이다 | [§Interface Adapters] "set of adapters that convert data from the format most convenient for the use cases and entities" | `engineering-blog` | Controller / Presenter / Gateway 류 코드의 위치 결정 | 어떤 변환이 "가장 편리한" 포맷인지의 구체 기준은 본 인용에 없음 (DTO vs domain object 결정은 별도) |
| CLEAN-ARCH-UB-C6 | Frameworks and Drivers 동심원은 Database, Web Framework 등 frameworks and tools 로 구성된 outermost layer 다 | [§Frameworks and Drivers] "outermost layer is generally composed of frameworks and tools such as the Database, the Web Framework" | `engineering-blog` | Spring / JPA / 기타 framework 코드의 위치 결정 | 어느 framework 구성요소가 어느 인접 원과 직접 닿는지(예: ORM mapper vs Repository impl)의 분리 기준은 본 인용에 없음 |
| CLEAN-ARCH-UB-C7 | 의존성이 흐름의 방향과 반대로 향하도록 interface 와 상속을 배치한다 (의존성 역전 원칙의 실무 적용) | [§Crossing boundaries] "we would arrange interfaces and inheritance relationships such that the source code dependencies oppose the flow of control" | `engineering-blog` | use case 가 outer-layer 컴포넌트를 호출해야 하는 경계 | DI container / factory / abstract factory 중 어떤 메커니즘이 의무인지는 본 인용에 없음 (구현 선택지는 열려 있음) |
| CLEAN-ARCH-UB-C8 | 경계를 가로지를 때는 **isolated, simple, data structures** 만 전달해야 한다 | [§What data crosses the boundaries] "isolated, simple, data structures are passed across the boundaries" | `engineering-blog` | 레이어 간 메서드 시그니처 / DTO 정책 | ORM Entity 객체를 그대로 전달하면 안 된다는 강제 규칙으로 일반화 가능한지는 본 인용만으로는 결론낼 수 없음 (Uncle Bob 의 다른 글과 결합 필요) |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CLEAN-ARCH-UB-C1`, `C2`: Dependency Rule 의 정확한 phrasing (Uncle Bob 본인의 단어 선택)
- `CLEAN-ARCH-UB-C3`~`C6`: 4개 동심원의 이름과 각각의 책임 정의
- `CLEAN-ARCH-UB-C7`: 의존성 역전을 통한 boundary crossing 의 메커니즘 (interface + inheritance)
- `CLEAN-ARCH-UB-C8`: 경계를 넘는 데이터의 형태 제약 (isolated, simple)
- **이 자료가 증명하지 않는 것**:
- 이 구조가 **공식 표준** 이거나 업계 best practice 라는 점 — 본 자료는 Uncle Bob 의 personal blog 이며, ISO/IEEE/OMG 등의 표준 문서가 아님 (`engineering-blog` strength)
- ca-tmpl 의 `domain` / `application` / `adapter` / `infrastructure` 4-패키지 분할이 Clean Architecture 의 4동심원과 1:1 매핑된다는 점 (매핑 결정은 별도 wiki/projects 문서에서 수행)
- Spring / JPA 같은 특정 기술의 어느 클래스가 어느 동심원에 속하는지의 구체 (책 *Clean Architecture* 2017 본문, 또는 별도 가이드라인 필요)
- DTO 변환을 어느 레이어가 책임지는지의 결정 (Use Case 진입/이탈, Controller, Mapper 중 어디인지)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- 본 4동심원과 ca-tmpl 의 실제 패키지 청사진의 매핑표 ([[raw/branch-notes/feature-skeleton-package-blueprint-contract]] 에서 결정)
- Dependency Rule 을 ArchUnit 으로 강제할 때의 구체 규칙 표현 ([[raw/branch-notes/feature-architecture-enforcement-rules]])
- "isolated, simple data structures" 의 ca-tmpl 내 구체 정의 (record? immutable POJO? DTO 인터페이스 규약?)
## 메모 / Notes
- 본 글은 Uncle Bob 이 동일 주제를 다룬 책 *Clean Architecture* (Prentice Hall, 2017) 의 모티프 원문에 해당. 책 본문이 더 상세하지만 본 블로그 글이 가장 자주 인용되는 단일 출처.
- Cockburn Hexagonal (1차 출처: [[raw/official-docs/arch-hexagonal-cockburn]]) 과의 핵심 차이는 **레이어 수와 명명** — Clean Architecture 는 4개 동심원으로 더 세분화, Hexagonal 은 inside/outside + ports 로 더 추상화. ca-tmpl 의 4-패키지 분할은 양쪽 모두에서 정당화 가능.
- 본 글이 personal blog 라는 점은 strength 측면에서 중요. ArchUnit 같은 vendor 도구의 layered-architecture API 가 "Clean Architecture" 라는 이름을 인용한다고 해서 본 글이 자동으로 official-vendor-doc 으로 격상되지는 않음.
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/official-docs/arch-hexagonal-cockburn]] (Cockburn 원문 — Hexagonal/Ports & Adapters)
- [[raw/official-docs/archunit-user-guide]] (Layer rule 강제 도구)
- 이 자료를 인용하는 branch:
- [[raw/branch-notes/feature-repository-access-permission-contract]]
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 이 자료를 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/arch-hexagonal-cockburn.md
@@ -0,0 +1,104 @@
---
title: Hexagonal Architecture (Ports & Adapters) — Alistair Cockburn 원문
source_type: official-doc
url: https://alistair.cockburn.us/hexagonal-architecture/
archive_url:
status: raw
confidence: medium
tags: [architecture, hexagonal-architecture, ports-and-adapters, ca-skeleton-operational-contract, application-layer, testability]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-repository-access-permission-contract, feature-architecture-enforcement-rules, feature-application-port-usecase-contract]
created: 2026-05-27
last_reviewed: 2026-05-27
---
# Hexagonal Architecture (Ports & Adapters) — Alistair Cockburn 원문
> Layer: `raw/official-docs/` — Alistair Cockburn 의 "Hexagonal Architecture" (alias: Ports & Adapters) 원문 발췌. inside/outside asymmetry + port + adapter 의 정의·동기 1차 출처. ca-tmpl 의 application port 와 adapter 분리 결정의 기반.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-repository-access-permission-contract]] | D3 (application layer 가 driving/driven port interface 만 노출), D4 (Repository 가 driven port 의 한 종류) 의 1차 근거 |
| [[raw/branch-notes/feature-architecture-enforcement-rules]] | inside (application/domain) 에서 outside (adapter/infrastructure) 로의 의존 금지를 ArchUnit 규칙으로 강제할 때의 개념적 기반 |
| [[raw/branch-notes/feature-application-port-usecase-contract]] | UseCase = primary/driving port 정의, Repository = secondary/driven port 정의의 명명 정당화 |
## 컨텍스트
ca-tmpl 의 application 레이어가 외부로 노출하는 것이 "use case interface" 인지 "service class" 인지의 결정, 그리고 Repository 가 application 레이어에 속하는지 domain 에 속하는지의 결정 모두 Cockburn 의 port/adapter 정의와 inside/outside asymmetry 에 기반한다. 본 raw 는 원문 verbatim 만 보관하고, ca-tmpl 패키지 매핑은 wiki/projects 에서 별도 정리.
## 출처 / Source
- 원본 URL: https://alistair.cockburn.us/hexagonal-architecture/
- 아카이브 URL:
- 저자 / 조직: Alistair Cockburn (personal site `alistair.cockburn.us`) — Hexagonal Architecture 원저자
- 발행일: 2005 (페이지에 "Hexagonal architecture the original 2005 article" 표기). 페이지 자체는 이후 refresh.
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§The Pattern — Intent] "Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases."
> [§Nature of the Solution] "The asymmetry to exploit is not that between left and right sides of the application but between inside and outside of the application."
> [§Nature of the Solution — port] "the application communicates over ports to external agencies. The word \"port\" is supposed to evoke thoughts of ports in an operating system, where any device that adheres to the protocols of a port can be plugged into it"
> [§Nature of the Solution — adapter] "For each external device there is an adapter that converts the API definition to the signals needed by that device and vice versa."
> [§Nature of the Solution — symmetry] "The hexagonal, or ports and adapters, architecture solves these problems by noting the symmetry in the situation: there is an application on the inside communicating over some number of ports with things on the outside. The items outside the application can be dealt with symmetrically."
> [§Nature of the Solution — why hexagon] "The hexagon is not a hexagon because the number six is important, but rather to allow the people doing the drawing to have room to insert ports and adapters as they need, not being constrained by a one-dimensional layered drawing."
> [§Nature of the Solution — port purpose] "A port identifies a purposeful conversation. There will typically be multiple adapters for any one port, for various technologies that may plug into that port."
> [§Nature of the Solution — primary focus] "the primary purpose of this pattern is to focus on the inside-outside asymmetry, pretending briefly that all external items are identical from the perspective of the application."
## Claims Extracted / 추출된 주장
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| HEX-COCKBURN-ORIG-C1 | Hexagonal Architecture 의 Intent 는 application 이 사용자·프로그램·자동화 테스트·batch script 에 의해 **동등하게 (equally)** 구동될 수 있고, 실제 런타임 device/DB 와 **격리된 채 개발·테스트** 될 수 있게 하는 것 | [§Intent] "Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases." | `engineering-blog` | application 의 외부 채널 다양화 + 테스트 격리 요구가 있는 시스템 | "equally" 가 모든 driving channel 이 정확히 같은 코드 경로를 통과해야 한다는 강제는 아님 — 각 adapter 가 동일 port 에 plug-in 된다는 의미 |
| HEX-COCKBURN-ORIG-C2 | 핵심 비대칭은 좌/우(UI vs DB) 가 아니라 **inside / outside** 이다 — 코드 분리 기준의 원칙적 출발점 | [§Nature of the Solution] "The asymmetry to exploit is not that between left and right sides of the application but between inside and outside of the application." | `engineering-blog` | 레이어드 아키텍처 vs hexagonal 의 분류 기준 결정 | 어떤 클래스가 inside 인지 outside 인지의 구체 판정 기준 (도메인 객체 vs Repository impl 등) 은 본 인용에 없음 |
| HEX-COCKBURN-ORIG-C3 | **port** 는 외부 agency 와의 conversation 을 위한 application 의 plug-point — OS 의 port 처럼 protocol 을 따르는 어떤 device 든 꽂힐 수 있다 | [§Nature of the Solution] "the application communicates over ports to external agencies. The word \"port\" is supposed to evoke thoughts of ports in an operating system, where any device that adheres to the protocols of a port can be plugged into it" | `engineering-blog` | port 인터페이스 명명·범위 결정 | port 가 반드시 Java interface 로 표현되어야 한다는 강제는 본 인용에 없음 (구현 언어/표현은 열려 있음) |
| HEX-COCKBURN-ORIG-C4 | **adapter** 는 각 external device 별로 존재하며, port 의 API 정의를 해당 device 의 signal 로 양방향 변환한다 | [§Nature of the Solution] "For each external device there is an adapter that converts the API definition to the signals needed by that device and vice versa." | `engineering-blog` | REST controller / JPA repository impl / Kafka consumer 등의 분류 | 한 adapter 가 여러 port 를 동시에 implement 할 수 있는지 여부는 본 인용에 없음 |
| HEX-COCKBURN-ORIG-C5 | hexagonal 명칭은 application 이 outside 의 여러 things 와 **symmetric** 하게 통신한다는 통찰에서 비롯 — outside 의 item 들은 symmetric 하게 다뤄질 수 있다 | [§Nature of the Solution] "The hexagonal, or ports and adapters, architecture solves these problems by noting the symmetry in the situation: there is an application on the inside communicating over some number of ports with things on the outside. The items outside the application can be dealt with symmetrically." | `engineering-blog` | UI/DB 양쪽을 동일 메커니즘 (port + adapter) 으로 처리하는 설계 | UI 와 DB 가 **정확히 동일한 종류** 의 port 라는 뜻은 아님 — primary/secondary 구분은 §Application Notes 에서 별도 도입 |
| HEX-COCKBURN-ORIG-C6 | hexagon 모양 자체는 의미 없음 — 6이라는 숫자가 중요한 것이 아니라 **여러 port/adapter 를 그릴 공간** 이 필요해서일 뿐 | [§Nature of the Solution] "The hexagon is not a hexagon because the number six is important, but rather to allow the people doing the drawing to have room to insert ports and adapters as they need, not being constrained by a one-dimensional layered drawing." | `engineering-blog` | hexagonal 다이어그램 작성 시 layer 수 강제 금지 | 실제 application 에서 port 수에 상한이 있다는 의미는 아님 (저자 본인은 "최대 4개를 만났다" 라고 별도 언급) |
| HEX-COCKBURN-ORIG-C7 | 하나의 port 는 purposeful conversation 을 식별하며, 같은 port 에 대해 여러 기술의 adapter 가 plug-in 될 수 있다 | [§Nature of the Solution] "A port identifies a purposeful conversation. There will typically be multiple adapters for any one port, for various technologies that may plug into that port." | `engineering-blog` | 동일 port (e.g., UserRepository) 에 대해 JPA / in-memory mock / Redis 등 복수 adapter 구현 정당화 | 모든 port 가 multiple adapter 를 가져야 한다는 강제는 아님 (typically — 일반적 경향) |
| HEX-COCKBURN-ORIG-C8 | 이 패턴의 primary purpose 는 inside-outside asymmetry 에 집중하는 것이며, 모든 외부 item 을 application 관점에서 일단 동일하게 본다 | [§Nature of the Solution] "the primary purpose of this pattern is to focus on the inside-outside asymmetry, pretending briefly that all external items are identical from the perspective of the application." | `engineering-blog` | 초기 설계 시 driving vs driven 의 차이를 일단 미루는 사고 절차 | UI 와 DB 가 영원히 동일 취급되어야 한다는 의미는 아님 — left/right asymmetry 는 §Application Notes 에서 다시 도입 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `HEX-COCKBURN-ORIG-C1`~`C2`: Hexagonal 의 Intent 와 핵심 비대칭이 inside/outside 라는 점
- `HEX-COCKBURN-ORIG-C3`~`C4`: port 와 adapter 의 정확한 정의 (OS port 비유 + 양방향 신호 변환)
- `HEX-COCKBURN-ORIG-C5`~`C7`: symmetry 관찰의 의미 + hexagon 모양의 비-의미 + 1 port — N adapter 관계
- `HEX-COCKBURN-ORIG-C8`: pattern 의 primary purpose
- **이 자료가 증명하지 않는 것**:
- 본 글이 **공식 표준 (RFC / ISO)** 이라는 점 — Cockburn 의 personal site (alistair.cockburn.us). 단, Hexagonal Architecture 의 **원저자** 본인의 글이므로 historical/authoritative reference 이지만 strength 는 `engineering-blog` 로 보수적 분류.
- "primary port" vs "secondary port" 의 정확한 명명 — 본 페이지 인용 범위에서는 driving/driven 의 명시적 정의 인용을 추출하지 않았음. 별도 페이지 (Application Notes / Structure 섹션) 추가 인용 필요.
- Java/Spring 환경에서 port 가 반드시 interface 로 표현되어야 한다는 점 (구현 언어 무관, "API" 라는 추상 표현만 등장)
- ca-tmpl 의 application 패키지가 "port + use case interactor" 로 정확히 분할되어야 한다는 결정 (본 자료는 패턴 정의만 제공, 패키지 매핑은 wiki/projects 에서 결정)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- driving port (UseCase) 의 명명 규칙과 driven port (Repository, Gateway) 의 명명 규칙 ([[raw/branch-notes/feature-application-port-usecase-contract]])
- Cockburn 의 Application Notes 섹션 (좌/우 asymmetry, primary/secondary 구분) 의 verbatim quote 보강
- Clean Architecture (Uncle Bob) 4동심원과 본 inside/outside 의 매핑 관계 (wiki/concepts 합성)
## 메모 / Notes
- 본 페이지는 "the original 2005 article" 로 명시. Cockburn 본인이 Hexagonal 명칭을 처음 도입한 1차 출처. 다만 personal site 이며 표준화 기관이 발행한 사양이 아니므로 strength 는 `engineering-blog`.
- 본 자료를 "공식 best practice" 로 인용할 수 없음. 단, ports & adapters 라는 용어의 **정의 출처** 로는 가장 적합.
- ca-tmpl 의 application 패키지 분할은 Clean Architecture 와 Hexagonal 의 **합성** 으로 정당화될 가능성이 높음. wiki/concepts 에서 두 출처를 같이 인용하여 합성 결정의 근거 표를 작성할 것.
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/official-docs/arch-clean-architecture-uncle-bob]] (Uncle Bob 4동심원 원문)
- [[raw/official-docs/archunit-user-guide]] (port/adapter 의존 방향 강제 도구)
- 이 자료를 인용하는 branch:
- [[raw/branch-notes/feature-repository-access-permission-contract]]
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
- [[raw/branch-notes/feature-application-port-usecase-contract]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 이 자료를 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/archunit-annotation-as-registry-evaluation.md
@@ -0,0 +1,137 @@
---
title: ArchUnit Annotation-as-Registry Pattern Evaluation
source_type: official-doc
url: https://www.archunit.org/userguide/html/000_Index.html
archive_url:
status: needs-confirmation
confidence: medium
related_branches: [feature-contract-registry-governance]
related_projects: [ca-tmpl]
tags: [ca-governance, archunit, registry, annotation, fitness-functions]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# ArchUnit Annotation-as-Registry Pattern Evaluation
> Layer: `raw/official-docs/` — ArchUnit User Guide 발췌 + ca-tmpl Group G-G(`feature-contract-registry-governance`)의 markdown SSOT 채택에 대한 **후속 대안 평가** 의 외부 근거.
>
> 평가 결과: ArchUnit annotation 기반 registry는 검토되었으나 채택되지 않음. **markdown SSOT 유지**. 본 문서는 그 결정의 근거를 보존한다.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-contract-registry-governance]] | Group G-G 결정 — "ArchUnit annotation-as-registry" 대안 평가 후 markdown SSOT 유지 결정의 근거 (annotation 의 공식 능력 범위 + registry SSOT 로 권고되지 않는다는 absence-of-evidence) |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-contract-registry-governance` (Group G-G)는 **markdown SSOT + YAML generated constants**를 contract registry 저장 형식으로 채택했다. 이때 검토되었어야 하나 상세 평가가 누락된 대안이 있다.
> ArchUnit이 제공하는 `@ArchTest`, `@AnalyzeClasses`, custom `@interface` 패턴을 그 자체로 registry로 쓰는 방식.
본 문서는 (a) ArchUnit annotation 기능이 무엇인지 인용으로 보존하고, (b) markdown SSOT vs annotation-as-registry 비교 표를 남겨, ca-tmpl 결정을 사후에 검증 가능하도록 한다.
## 출처 / Source
- 원본 URL (ArchUnit User Guide): https://www.archunit.org/userguide/html/000_Index.html
- 보조 URL: https://github.com/TNG/ArchUnit-Examples
- 보조 참조: *Building Evolutionary Architectures* (Ford, Parsons, Kua) — fitness functions 개념
- 보조 URL: https://www.baeldung.com/java-archunit-intro
- 아카이브 URL: (미수집)
- 저자 / 조직: ArchUnit 프로젝트 (TNG Technology Consulting)
- 발행 상태: ArchUnit User Guide v1.4.x 기준 지속 갱신 (2026-04 기준 최신)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§JUnit support — `@ArchTest` / `@AnalyzeClasses`] "declare ArchUnit's `ArchUnitRunner` (only JUnit 4), declare the classes to import via `@AnalyzeClasses` and add the respective rules as fields" + "The JUnit test support will automatically import (or reuse) the specified classes and evaluate any rule annotated with `@ArchTest` against those classes."
> [§Custom `@interface` meta-annotation] "`@AnalyzeClasses` can also be used as a meta-annotation to avoid repeating the same configuration" + "This annotation can then be used on test classes without repeating the specific configuration of `@AnalyzeClasses`"
> [§LayeredArchitecture rule] "`layeredArchitecture()...layer("Controller").definedBy("..controller..")...whereLayer("Controller").mayNotBeAccessedByAnyLayer()`"
> [§Writing Custom Rules — DescribedPredicate + ArchCondition] "most architectural rules take the form: classes that ${PREDICATE} should ${CONDITION}" (implementation requires "exposing the concepts of `DescribedPredicate` and `ArchCondition`")
> [§Building Evolutionary Architectures (Ford et al.)] "Architectural fitness functions — any mechanism that provides an objective integrity assessment of some architectural characteristic(s)."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AAR-C1 | `@ArchTest` 는 ArchUnit JUnit runner 가 평가할 ArchRule field 를 마킹하는 annotation; `@AnalyzeClasses` 는 import 대상 classes 를 선언하는 annotation. 둘은 **runner 입력 (framework annotation)** 역할 | [§JUnit support — `@ArchTest` / `@AnalyzeClasses`] "declare the classes to import via `@AnalyzeClasses` and add the respective rules as fields... evaluate any rule annotated with `@ArchTest` against those classes." | `official-vendor-doc` | ArchUnit JUnit 통합 환경 | `@ArchTest`/`@AnalyzeClasses` 가 도메인 contract registry (error code, env key 등) 를 표현하는 용도라는 뜻은 아님 — runner 입력 전용 |
| AAR-C2 | ArchUnit 공식이 안내하는 custom `@interface` 패턴의 명시 목적은 **`@AnalyzeClasses` 설정 중복 제거용 meta-annotation** | [§Custom `@interface` meta-annotation] "`@AnalyzeClasses` can also be used as a meta-annotation to avoid repeating the same configuration" | `official-vendor-doc` | ArchUnit User Guide 가 안내하는 meta-annotation 패턴 | 도메인 contract 를 custom annotation 으로 registry 화 하는 것이 공식 권장 패턴이라는 뜻은 아님 (User Guide 에 명시 부재 — absence of evidence) |
| AAR-C3 | ArchUnit 의 `LayeredArchitecture` rule 은 **DSL string + ArchRule** 형태로 layer 를 정의하고 접근 제약을 표현 (`.layer().definedBy("..controller..").whereLayer().mayNotBeAccessedByAnyLayer()`) | [§LayeredArchitecture rule] "`layeredArchitecture()...layer("Controller").definedBy("..controller..")...whereLayer("Controller").mayNotBeAccessedByAnyLayer()`" | `official-vendor-doc` | layer 기반 아키텍처 강제 | custom annotation 으로 layer 를 "등록" 하는 패턴이 공식 예제에 포함된다는 뜻은 아님 |
| AAR-C4 | ArchUnit 의 custom rule 작성 패턴은 `DescribedPredicate` + `ArchCondition` 조합으로 **"classes that ${PREDICATE} should ${CONDITION}"** 형식 | [§Writing Custom Rules — DescribedPredicate + ArchCondition] "most architectural rules take the form: classes that ${PREDICATE} should ${CONDITION}" | `official-vendor-doc` | custom ArchRule 작성 | 이 패턴이 SSOT registry 역할을 한다는 뜻은 아님 — 검증 (verifier) 형식 |
| AAR-C5 | *Building Evolutionary Architectures* 의 fitness function 정의는 "**아키텍처 특성에 대한 객관적 무결성 평가를 제공하는 모든 mechanism**" — ArchUnit 은 이 정의의 **mechanism (verifier)** 에 해당 | [§Building Evolutionary Architectures (Ford et al.)] "any mechanism that provides an objective integrity assessment of some architectural characteristic(s)." | `engineering-blog` *(서적 출처)* | fitness function 개념 일반 | fitness function 이 SSOT 역할까지 포함한다는 정의가 있다는 뜻은 아님 — verifier 정의에 한정 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `AAR-C1`: `@ArchTest`/`@AnalyzeClasses` 의 runner-입력 역할
- `AAR-C2`: ArchUnit User Guide 가 명시적으로 안내한 custom `@interface` 유일 use case (= `@AnalyzeClasses` meta-annotation)
- `AAR-C3`: `LayeredArchitecture` 의 DSL string 기반 layer 정의 패턴
- `AAR-C4`: custom rule 작성의 표준 형식 (PREDICATE + CONDITION)
- `AAR-C5`: fitness function 의 정의 = mechanism/verifier
- **이 자료가 증명하지 않는 것**:
- ArchUnit annotation 을 **도메인 contract registry SSOT 로 권장**한다는 명제 (User Guide 에 명시 부재)
- markdown SSOT vs annotation 의 우월성 비교 (본 자료는 ArchUnit 능력 정의만 — 비교 표는 ca-tmpl 자체 분석)
- polyglot stack (Python, frontend) 에서 ArchUnit annotation 이 작동한다는 명제 (JVM 한정)
- "annotation 없는 사용을 javac/ArchUnit 이 silently pass" 라는 명제 (별도 검증 메커니즘 부재 — 본 자료는 그 사실을 직접 말하지 않음)
- **내 프로젝트(ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
- markdown SSOT 의 drift 검증 스크립트 실제 구현 여부 (ca-tmpl 한계로 문서화됨)
- polyglot 환경 도래 시 IDL registry (Protobuf/Smithy) 로의 마이그레이션 결정 ([[raw/official-docs/schema-protobuf-vs-json-evolution]] 참고)
- *Building Evolutionary Architectures* 책 원문에서 annotation-as-SSOT 권고/반대 구절 직접 확인 (현재 needs-confirmation)
## markdown SSOT vs annotation-as-registry 비교 표 (내 프로젝트 해석 — 미검증)
> 본 표는 자료 직접 인용 아님. ca-tmpl 자체 평가.
| 항목 | markdown SSOT + YAML generated (ca-tmpl 채택) | ArchUnit annotation-as-registry (대안) |
| --- | --- | --- |
| **저장 위치** | branch-note + `docs/registries/*.yml` | `src/.../annotations/*.java` (`@interface` 또는 marker class) |
| **사람이 읽기** | markdown table — 외부 리뷰어·비개발자도 가능 | Java 소스 — IDE/컴파일러 필요 |
| **framework 종속** | 없음 (Spring/JPA/JUnit과 분리) | Java + ArchUnit lock-in |
| **다언어 재사용** | YAML 파생을 어느 언어든 로딩 가능 | JVM 한정. polyglot stack에는 부적합 |
| **git diff review** | 표 row 단위 변경 명확 | annotation attribute diff는 가독성 떨어짐 |
| **외부 도구 호환** | Obsidian dataview, IDE markdown 미리보기, GitHub render | ArchUnit + javac만 |
| **"왜" 컨텍스트 보존** | branch-note의 결정/근거/대안 라인이 함께 위치 | annotation attribute는 짧은 string에 한정 |
| **누락 검출** | drift 검증 스크립트 **자체 작성 필요** (한계) | annotation 없는 코드는 silently pass — 더 위험 |
| **변경 절차** | row 추가 → contract test → `.env.example` 갱신 (명문화됨) | annotation 추가 → 새 rule field 정의. 절차가 분산 |
| **fitness function 적합도** | registry는 SSOT, fitness function은 별도 verifier | annotation = SSOT + verifier 혼합. 역할 경계 흐려짐 |
| **단일 팀 적용 비용** | markdown 작성 비용만 | annotation 설계 + ArchUnit rule 작성 + maintenance |
| **breaking change 정책** | row의 `compatibility_impact` 열로 명시 | annotation attribute 변경 시 모든 사용처 수정 |
## 메모 / Notes (내 프로젝트 해석) — 평가 결론
**ca-tmpl은 markdown SSOT를 유지한다.** 근거:
1. **framework-neutral.** registry는 Spring/JPA/JUnit과 분리되어야 한다. error code/env key/header/log field는 polyglot stack(예: Python sidecar, frontend)에도 동일하게 적용될 수 있어야 하며, Java annotation은 이를 막는다.
2. **외부 도구 호환.** Obsidian dataview, IDE markdown 미리보기, GitHub web view, LLM Wiki `/query`가 모두 markdown을 1급으로 다룬다. annotation은 javac/ArchUnit/IDE plugin이 필요하다.
3. **git diff review가 가능하다.** PR review에서 비개발자(예: PM, 운영) 또는 외부 컨설턴트가 row 변경을 읽을 수 있다. annotation diff는 Java 문법 지식이 필요하다.
4. **"왜" 컨텍스트가 branch-note와 같이 위치.** branch-note ≈ mini-ADR 패턴이 깨지지 않는다.
5. **annotation은 verifier로만 사용.** ArchUnit은 registry가 아닌 **fitness function 실행 mechanism**으로만 ca-tmpl에 들어간다 (이미 §12 verification suite에 반영).
**단, 다음 사실을 명시한다.**
- ca-tmpl의 markdown SSOT는 **drift 검증 스크립트가 미작성**이다 (concept 문서 한계 섹션과 동일). annotation 방식은 javac/ArchUnit이 "어노테이션 없는 사용"을 잡을 수 있다는 강점이 있으나, 어노테이션 자체의 누락 검출이 별도로 필요하다는 점은 양쪽 모두 동일.
- 본 평가는 ca-tmpl의 **단일 팀 / 단일 release train / JVM 단일 stack** 컨텍스트에 한정. 멀티 팀·polyglot 환경에서는 IDL registry(Protobuf/Smithy)가 우위일 수 있으며, 이는 [[raw/official-docs/schema-protobuf-vs-json-evolution]] 참고.
## 추가 검증 필요 (needs-confirmation 사유)
- ArchUnit User Guide에서 "annotation을 도메인 contract registry로 권고"하는 공식 문구는 발견되지 않음. 본 문서는 ArchUnit이 그 목적으로 **설계되지 않았다**는 해석이며, 공식적으로 명시되지 않은 부재(absence)에 근거함.
- TNG/ArchUnit-Examples 저장소는 `@ArchTest`/`@AnalyzeClasses` 사용 예제만 포함, custom `@interface` registry 예제는 없음 (확인 완료).
- *Building Evolutionary Architectures* 인용은 fitness function 정의 부분만 확인. annotation-as-SSOT를 권고하는 구절은 본 문서에서 확인되지 않음. 책 원문 재확인 필요.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/governance-archunit-official]] (ArchUnit 공식 소개)
- [[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]] (Layer 2 정적 검사 가능 범위)
- 인용하는 branch:
- [[raw/branch-notes/feature-contract-registry-governance]] (Group G-G 본체)
- canonical contract 섹션:
- [[raw/project-notes/ca-skeleton-operational-contract]] — §21 Contract Registry, §29 Group G-G
- 관련 wiki:
- [[wiki/concepts/skeleton-governance-registry-verification-test-scorecard]] (작성 시)
- [[wiki/projects/ca-tmpl/skeleton-governance-registry-verification-test-scorecard]] (작성 시)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/archunit-conditional-on-property-3-layer-pattern.md
@@ -0,0 +1,152 @@
---
title: ArchUnit Custom Rule for @ConditionalOnProperty 3-Layer Adapter Enforcement
source_type: official-doc
url: https://www.archunit.org/userguide/html/000_Index.html
archive_url:
status: needs-confirmation
confidence: medium
related_branches: [feature-integration-adapter-templates]
related_projects: [ca-tmpl]
tags: [ca-config-adapter, archunit, conditional-on-property, fitness-functions]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# ArchUnit Custom Rule for @ConditionalOnProperty 3-Layer Adapter Enforcement
> Layer: `raw/official-docs/` — ArchUnit 공식 User Guide(custom rules, annotation 접근) 발췌와, `@ConditionalOnProperty` 기반 adapter on/off의 Layer 2(정적 검사) 가능 범위 평가. ca-tmpl `feature-integration-adapter-templates` 그룹 G-I의 외부 source 부재 보강.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-integration-adapter-templates]] | Group G-I 의 Layer 2 (ArchUnit 정적 검사) 실효 정의 — "annotation 부착 강제 + naming convention + CA 경계" 까지로 한정, "disabled adapter 호출 차단"은 Layer 3 runtime 책임이라는 분리 근거 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-integration-adapter-templates` (그룹 G-I)는 disabled adapter 검출을 3-layer로 정의함:
- **Layer 1 — Spring `@ConditionalOnProperty`**: bean 등록 조건. Spring 공식 cover.
- **Layer 2 — ArchUnit static dependency 검사**: application code가 disabled adapter package에 의존하지 못하게 차단. **외부 source 부재**.
- **Layer 3 — `AdapterDisabledException` runtime fail-fast**: silent failure 방지. branch 자체 contract.
Layer 2는 ArchUnit User Guide가 "`@ConditionalOnProperty` 기반 conditional bean을 정적으로 검증한다"는 명시적 패턴을 제시하지 않음. ca-tmpl이 자체 fitness function으로 발명해야 하므로, **무엇이 정적으로 가능하고 무엇이 불가능한지 경계**를 평가해 두는 raw 근거가 필요함.
## 출처 / Source
- 원본 URL: https://www.archunit.org/userguide/html/000_Index.html
- "Writing Custom Rules" 섹션 (`DescribedPredicate`, `ArchCondition` API)
- "Accessing Annotation With/Without Classpath" 섹션 (`getAnnotationOfType`, `JavaAnnotation.get("value")`)
- 보조 참조:
- Spring Boot Reference — `@ConditionalOnProperty` (`name`, `havingValue`, `matchIfMissing`)
- *Building Evolutionary Architectures* (Ford / Parsons / Kua) — "fitness function"의 개념적 출처
- 아카이브 URL: (미수집)
- 저자 / 조직: ArchUnit 프로젝트 (TNG Technology Consulting)
- 발행 상태: ArchUnit User Guide는 v1.4.x 기준 지속 갱신 (2026-04 기준 최신)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Writing Custom Rules] "most architectural rules take the form: classes that ${PREDICATE} should ${CONDITION}" (implementation requires "exposing the concepts of `DescribedPredicate` and `ArchCondition`")
> [§Accessing Annotation With/Without Classpath — classpath 없음] "you must rely on `JavaAnnotation<?> annotation = javaClass.getAnnotationOfType()` and `Object value = annotation.get("value")`"
> [§Accessing Annotation With/Without Classpath — classpath 있음] "this can be written way more naturally: `CustomAnnotation annotation = javaClass.getAnnotationOfType(CustomAnnotation.class); String value = annotation.value()`"
> [§Domain Objects, Reflection and the Classpath] "ArchUnit's own rule APIs never rely on the classpath though. Thus the evaluation of default rules and syntax combinations does not depend on whether the classes were imported from the classpath or some JAR / folder."
> [§Building Evolutionary Architectures (Ford et al.)] "Architectural fitness functions — any mechanism that provides an objective integrity assessment of some architectural characteristic(s)."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AUCP-C1 | ArchUnit custom rule 의 표준 형식은 "classes that ${PREDICATE} should ${CONDITION}" 이며 `DescribedPredicate` + `ArchCondition` 의 조합으로 작성 | [§Writing Custom Rules] "most architectural rules take the form: classes that ${PREDICATE} should ${CONDITION}" | `official-vendor-doc` | ArchUnit custom rule 작성 환경 | runtime config (env, property) 평가가 이 PREDICATE/CONDITION 으로 가능하다는 뜻은 아님 — bytecode 기반 정적 검사에 한정 |
| AUCP-C2 | classpath 가 있을 때 annotation 접근은 `javaClass.getAnnotationOfType(CustomAnnotation.class)` + `.value()` 로 자연스럽게 가능 | [§Accessing Annotation With/Without Classpath — classpath 있음] "`CustomAnnotation annotation = javaClass.getAnnotationOfType(CustomAnnotation.class); String value = annotation.value()`" | `official-vendor-doc` | classpath 가 ArchUnit 평가에 포함된 환경 | classpath 없이 동일 ergonomics 가 가능하다는 뜻은 아님 — classpath 없을 때는 `JavaAnnotation<?>` + `.get("value")` 패턴 필요 |
| AUCP-C3 | classpath 가 없을 때 annotation 접근은 `JavaAnnotation<?> annotation = javaClass.getAnnotationOfType()` + `Object value = annotation.get("value")` 로 수행 | [§Accessing Annotation With/Without Classpath — classpath 없음] "you must rely on `JavaAnnotation<?> annotation = javaClass.getAnnotationOfType()` and `Object value = annotation.get("value")`" | `official-vendor-doc` | classpath 없이 bytecode-only 분석 환경 | reflection 없이 strongly-typed accessor 가 가능하다는 뜻은 아님 — `Object` 로 반환 |
| AUCP-C4 | ArchUnit 자체 rule API 는 classpath 에 의존하지 않으며, default rule + syntax 조합 평가는 classpath 에서 import 했는지 JAR/folder 에서 했는지에 무관 | [§Domain Objects, Reflection and the Classpath] "ArchUnit's own rule APIs never rely on the classpath though. Thus the evaluation of default rules and syntax combinations does not depend on whether the classes were imported from the classpath or some JAR / folder." | `official-vendor-doc` | ArchUnit default rule 평가 | custom annotation 접근까지 모두 classpath 독립이라는 뜻은 아님 — `.value()` ergonomics 는 classpath 필요 |
| AUCP-C5 | *Building Evolutionary Architectures* 의 fitness function 정의는 "아키텍처 특성에 대한 객관적 무결성 평가를 제공하는 **모든 mechanism**" | [§Building Evolutionary Architectures (Ford et al.)] "any mechanism that provides an objective integrity assessment of some architectural characteristic(s)." | `engineering-blog` *(서적 출처)* | fitness function 개념 일반 | fitness function 이 runtime config 평가를 정의에 포함한다는 뜻은 아님 — mechanism 의 범위 정의는 책에 명시되지 않음 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `AUCP-C1`: custom rule 의 표준 형식 (PREDICATE + CONDITION)
- `AUCP-C2`: classpath 있을 때의 annotation 접근 ergonomics
- `AUCP-C3`: classpath 없을 때의 annotation 접근 API
- `AUCP-C4`: ArchUnit default rule API 의 classpath 독립성
- `AUCP-C5`: fitness function 의 개념 정의 (mechanism)
- **이 자료가 증명하지 않는 것**:
- "현재 빌드/배포 환경에서 특정 property 가 `false` 인지" 를 ArchUnit 이 정적으로 검증할 수 있다는 명제 (runtime config 영역 — ArchUnit 능력 밖)
- "disabled 상태에서 application code 가 실제로 adapter 를 호출하는지" 를 ArchUnit 이 검증할 수 있다는 명제 (Spring container wiring runtime 결과)
- profile/test profile 별 활성 adapter 를 ArchUnit 으로 판정할 수 있다는 명제
- "annotation 부착 강제 + naming convention" 검사가 "disabled 호출 차단" 과 동등하다는 명제 (서로 다른 보장 수준)
- "3-layer 가 disabled adapter 호출을 완전 검증한다" 는 명제 (Layer 3 runtime 까지 필요)
- **내 프로젝트(ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
- ArchUnit Layer 2 가 ca-tmpl 의 어떤 정확한 fitness function 으로 구현되는지 (Phase C2 진입 시 코드로 검증)
- `@ConditionalOnBooleanProperty` (3.5.0+) 사용 시 annotation 접근 방식이 동일한지 (classpath 의존성)
- bytecode-only 환경 (Gradle build script 같은) 에서 `JavaAnnotation.get("name")` 호출의 안정성
## ArchUnit이 정적으로 추출할 수 있는 것 / 없는 것 (내 프로젝트 해석 — 미검증)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 자체 분석.
### 정적 추출 가능 (bytecode 기준)
- 어떤 class가 `@ConditionalOnProperty` annotation을 **부착했는지 여부**`javaClass.isAnnotatedWith(ConditionalOnProperty.class)`.
- 그 annotation의 **`name`, `havingValue`, `prefix`, `matchIfMissing` parameter 값** — `getAnnotationOfType(...)`로 enum/String 값 읽기 가능.
- `noClasses().that().resideInAPackage("..application..").should().dependOnClassesThat().resideInAPackage("..adapters.<X>..")` 형태의 **package-level 정적 의존 검사** — ArchUnit 기본 API.
- "adapter 후보 package에 있는 모든 `@AutoConfiguration` / `@Configuration` class는 `@ConditionalOnProperty`를 가져야 한다" 같은 **annotation 존재 강제 규칙** — custom `ArchCondition`으로 구현 가능.
- "`@ConditionalOnProperty``name``app.adapter.<name>.enabled` 패턴을 따라야 한다" 같은 **naming convention 강제**`annotation.get("name")` 값을 정규식으로 검사.
### 정적 추출 **불가능** (runtime 정보)
- **"현재 빌드/배포 환경에서 `app.adapter.kafka.enabled`가 실제로 `false`인지"** — 이는 runtime config(env, `application.yml`, `--args`)에 의존. bytecode에는 존재하지 않음.
- **"disabled 상태에서 application code가 실제로 adapter를 호출하는지"** — Spring container의 실제 bean wiring 결과는 runtime에 결정.
- **"profile/test profile/local profile별로 어떤 adapter가 활성화되는지"** — Spring Environment resolver의 runtime 동작.
### 부분 가능 (조합형 정적 검사)
- **"application layer가 adapter package를 import하지 않는다"** — 정적 가능. 단, "현재 adapter가 disabled여서" 막는 게 아니라 "**hexagonal/CA 경계상 항상 직접 의존 금지**"로 재해석해야 의미가 있음.
- **"port interface를 통해서만 adapter를 호출한다"** — 정적 가능. CA 경계 강제와 동일한 규칙.
- **"disabled 시 호출되는 모든 adapter 진입점은 `AdapterDisabledException`을 throw할 수 있게 선언/구현돼 있다"** — `JavaMethod`의 throws 절이나 method body call 검사로 부분 가능. 단, "실제 호출 시 throw하는지"는 runtime.
## Layer 2 정적 검사의 실제 가능 범위 — 결론 (내 프로젝트 해석)
ArchUnit Layer 2가 정적으로 **보장 가능한 범위**는 다음 3가지뿐:
1. **annotation 부착 강제**: adapter 후보 class가 `@ConditionalOnProperty`(또는 3.5.0+ `@ConditionalOnBooleanProperty`)를 가지는가.
2. **naming convention 강제**: 그 annotation의 `name` 값이 `app.adapter.<name>.enabled` 패턴을 따르는가.
3. **CA 경계 강제** (별도 목적): application layer가 adapter package를 직접 import하지 않는가 — 이는 "disabled 검출"이 아니라 hexagonal 경계 자체.
**보장 불가능한 범위**:
- "현재 disabled인 adapter가 실제로 호출되지 않는다" — runtime config + Spring container 동작이 결합돼야 판정 가능. **Layer 3 (`AdapterDisabledException` runtime fail-fast)에 위임**.
- "특정 profile에서 어떤 adapter가 활성화되는지" — runtime resolver 영역.
### 따라서 ca-tmpl Layer 2의 실효 정의
ca-tmpl Layer 2는 "adapter 후보 class가 `@ConditionalOnProperty` 부착 + 표준 naming pattern을 따른다"는 **fitness function**으로 한정해야 함. "disabled adapter가 호출되지 않는다"는 명제까지 확장하면 ArchUnit 능력 밖이며, **실제 disabled 시 호출 차단은 Layer 3 runtime 책임**.
이 한계를 명시하지 않으면 "3-layer가 disabled adapter 호출을 완전 검증한다"는 **과장**으로 이어짐.
## 메모 / Notes (내 프로젝트 해석 — 미검증)
- ArchUnit은 "fitness function" 개념(Building Evolutionary Architectures)의 대표 Java 구현체 중 하나. 그러나 fitness function 자체가 runtime config 평가를 포함한다는 정의는 없음. ArchUnit의 범위는 bytecode 정적 분석.
- Spring Boot AutoConfiguration의 `@ConditionalOn*` 평가는 **Spring container startup 시점**이지, 빌드 시점이 아님. 따라서 "disabled 시 bean이 등록되지 않는다"의 검증은 ApplicationContext 기반 통합 테스트(Layer 1 verification)에서 수행해야 함.
- 정적 추출이 가능한 부분(`@ConditionalOnProperty` 부착 강제)도 **결정은 코드 단계에서 fitness function으로 도입할지 보류 가능**. ca-tmpl Phase C2 진입 전에는 contract 수준 결정만 유지.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/governance-archunit-official]] (ArchUnit 공식 소개)
- [[raw/official-docs/archunit-annotation-as-registry-evaluation]] (annotation-as-registry 대안 평가)
- [[raw/official-docs/adapter-spring-boot-autoconfig-custom-starter]] (Layer 1 — Spring `@ConditionalOnProperty` 공식 시맨틱)
- [[raw/official-docs/adapter-java-spi-serviceloader]] (대안 4 — Java SPI)
- 인용하는 branch:
- [[raw/branch-notes/feature-integration-adapter-templates]] (그룹 G-I)
- canonical contract 섹션:
- [[raw/project-notes/ca-skeleton-operational-contract]] §11 Adapter Failure Contract, §29 Group G-I
- 관련 wiki:
- [[wiki/concepts/config-and-adapter-templates]] (작성 시 — Adapter templates 한계 섹션)
- [[wiki/projects/ca-tmpl/config-and-adapter-templates]] (작성 시 — documented-only 결정 기록)
- 본 source의 위치: Layer 2 (ArchUnit static detection) 정적 검사 가능 범위 평가 — 외부 source 부재 보강
-1
View File
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/archunit-user-guide.md
+104
View File
@@ -0,0 +1,104 @@
---
title: ArchUnit User Guide — 공식 사용자 가이드 (Index)
source_type: official-doc
url: https://www.archunit.org/userguide/html/000_Index.html
archive_url:
status: raw
confidence: high
tags: [architecture, archunit, architecture-tests, java, junit, ca-skeleton-operational-contract, dependency-rule-enforcement]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-repository-access-permission-contract, feature-architecture-enforcement-rules]
created: 2026-05-27
last_reviewed: 2026-05-27
---
# ArchUnit User Guide — 공식 사용자 가이드 (Index)
> Layer: `raw/official-docs/` — ArchUnit 프로젝트의 공식 User Guide (HTML index) 의 verbatim 발췌. ArchUnit 의 정체성·기본 API·layer 강제·cycle 검사·JUnit 통합의 1차 출처.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-repository-access-permission-contract]] | D8 (Repository 위치/접근 권한을 컴파일 후 테스트 단계에서 강제할 도구로 ArchUnit 채택) 의 1차 근거 |
| [[raw/branch-notes/feature-architecture-enforcement-rules]] | layered architecture rule / package dependency rule / cycle check 를 ArchUnit DSL 로 표현 가능하다는 1차 근거 |
## 컨텍스트
ca-tmpl 의 Clean Architecture / Hexagonal 의존성 규칙 (도메인 → 인프라 금지, application → adapter 금지 등) 을 코드 리뷰가 아닌 자동화 테스트로 강제하려면 도구 선택이 필요. ArchUnit 이 Java 환경에서 사실상 표준이며, 본 raw 는 그 채택 결정의 1차 근거를 보관.
## 출처 / Source
- 원본 URL: https://www.archunit.org/userguide/html/000_Index.html
- 아카이브 URL:
- 저자 / 조직: ArchUnit 프로젝트 (TNG Technology Consulting GmbH 발족, OSS 커뮤니티 유지)
- 발행일: rolling (User Guide 페이지에 ArchUnit 1.4.2 표기 — 2026-05-27 확인 시점)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§1. Introduction] "ArchUnit is a free, simple and extensible library for checking the architecture of your Java code."
> [§3.1. Importing Classes] "JavaClasses classes = new ClassFileImporter().importPackages(\"com.mycompany.myapp\");"
> [§3.2. Asserting Constraints] "The returned object of type ArchRule can now be evaluated against a set of imported classes: myRule.check(importedClasses);"
> [§4.1. Package Dependency Checks] "noClasses().that().resideInAPackage(\"..source..\").should().dependOnClassesThat().resideInAPackage(\"..foo..\")"
> [§4.6. Layer Checks] "layeredArchitecture().layer(\"Service\").mayOnlyBeAccessedByLayers(\"Controller\")"
> [§3.3. Using JUnit 4 or JUnit 5] "The JUnit test support will automatically import (or reuse) the specified classes and evaluate any rule annotated with @ArchTest."
> [§4.7. Cycle Checks] "slices().matching(\"com.myapp.(*)..\").should().beFreeOfCycles()"
> [§7.2. Composing Member Rules] "Besides methods(), ArchRuleDefinition offers methods, fields, codeUnits, constructors."
## Claims Extracted / 추출된 주장
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| ARCHUNIT-UG-C1 | ArchUnit 은 Java 코드의 아키텍처를 검사하는 **free, simple, extensible** 라이브러리 | [§1. Introduction] "ArchUnit is a free, simple and extensible library for checking the architecture of your Java code." | `official-vendor-doc` | Java/Kotlin (JVM bytecode) 프로젝트 | Java 외 언어 (Python, Go) 에서 동등 도구가 무엇인지는 본 인용에 없음 |
| ARCHUNIT-UG-C2 | 클래스 import 의 표준 진입점은 `ClassFileImporter().importPackages(<base-package>)` | [§3.1] "JavaClasses classes = new ClassFileImporter().importPackages(\"com.mycompany.myapp\");" | `official-vendor-doc` | ArchUnit 테스트의 초기 부트스트랩 | 단일 root package 만 지원한다는 의미는 아님 — `importPackages(...)` 는 varargs 로 다중 패키지 가능 |
| ARCHUNIT-UG-C3 | 규칙은 `ArchRule` 타입 객체로 표현되며, `myRule.check(importedClasses)` 로 평가 | [§3.2] "The returned object of type ArchRule can now be evaluated against a set of imported classes: myRule.check(importedClasses);" | `official-vendor-doc` | 모든 ArchUnit rule 실행 흐름 | `@ArchTest` 어노테이션과의 자동 호출 메커니즘은 별도 (§3.3) — 본 인용은 수동 check 만 보장 |
| ARCHUNIT-UG-C4 | 패키지 의존 규칙은 fluent DSL 로 표현 가능 — 예: `noClasses().that().resideInAPackage("..source..").should().dependOnClassesThat().resideInAPackage("..foo..")` | [§4.1] "noClasses().that().resideInAPackage(\"..source..\").should().dependOnClassesThat().resideInAPackage(\"..foo..\")" | `official-vendor-doc` | 도메인 → 인프라 금지 같은 패키지 단위 의존 강제 | 정확히 어떤 매칭 패턴 (`..` vs `.*`) 이 어떤 의미인지는 별도 문서 (matcher syntax) 필요 — 본 인용은 한 사례만 |
| ARCHUNIT-UG-C5 | layered architecture 규칙은 layer 이름 + 접근 허용 layer 명시로 표현 — 예: `layeredArchitecture().layer("Service").mayOnlyBeAccessedByLayers("Controller")` | [§4.6] "layeredArchitecture().layer(\"Service\").mayOnlyBeAccessedByLayers(\"Controller\")" | `official-vendor-doc` | Clean/Hexagonal layer 의존 방향 강제 | "layer" 의 식별 기준 (패키지 패턴, annotation 등) 은 본 인용에 없음 — `definedBy()` 등 별도 메서드 결합 필요 |
| ARCHUNIT-UG-C6 | JUnit 4/5 통합은 `@ArchTest` 어노테이션이 붙은 모든 rule 을 자동 import + 평가 | [§3.3] "The JUnit test support will automatically import (or reuse) the specified classes and evaluate any rule annotated with @ArchTest." | `official-vendor-doc` | JUnit 기반 CI 자동화 | "automatically import (or reuse)" 의 캐싱 정책 구체는 본 인용에 없음 — performance tuning 시 별도 확인 |
| ARCHUNIT-UG-C7 | cycle 검사는 slice 패턴 매칭으로 표현 — 예: `slices().matching("com.myapp.(*)..").should().beFreeOfCycles()` | [§4.7] "slices().matching(\"com.myapp.(*)..\").should().beFreeOfCycles()" | `official-vendor-doc` | 모듈 간 순환 의존 방지 | slice 가 반드시 패키지 1단계 단위여야 한다는 의미는 아님 — `(*)` 외 다른 capture 패턴 가능 |
| ARCHUNIT-UG-C8 | 멤버 단위 규칙도 지원 — `methods()`, `fields()`, `codeUnits()`, `constructors()``ArchRuleDefinition` 의 entry points | [§7.2] "Besides methods(), ArchRuleDefinition offers methods, fields, codeUnits, constructors." | `official-vendor-doc` | 메서드/필드 가시성, annotation 강제 등 fine-grained 규칙 | 어떤 entry point 가 성능상 더 가벼운지는 본 인용에 없음 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `ARCHUNIT-UG-C1`: ArchUnit 의 정체성 (free, simple, extensible, Java)
- `ARCHUNIT-UG-C2`~`C3`: 기본 API (import + check)
- `ARCHUNIT-UG-C4`~`C5`: 패키지 의존 규칙 + layered architecture 규칙의 DSL 표현
- `ARCHUNIT-UG-C6`: JUnit 통합의 자동 호출
- `ARCHUNIT-UG-C7`: cycle 검사 DSL
- `ARCHUNIT-UG-C8`: 클래스 외 멤버 단위 규칙 entry points 의 존재
- **이 자료가 증명하지 않는 것**:
- ArchUnit 이 ca-tmpl 의 실제 패키지 청사진에 맞춰 정확히 어떤 규칙 코드를 가져야 하는지 (구체 매핑은 별도 wiki/projects 에서 결정)
- ArchUnit 규칙 위반 발생 시 CI 게이트 정책 (fail vs warn) — 본 인용 범위 밖
- Kotlin / Scala 등 다른 JVM 언어에서의 완전한 동등 동작 (User Guide 의 다른 섹션 확인 필요)
- ArchUnit 1.x ↔ 0.x API 호환성 (현재 1.4.2 기준 확인됨)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 layer 정의 (domain / application / adapter / infrastructure) 와 `layeredArchitecture().layer(...).definedBy(...)` 매칭
- 규칙 작성 후 CI/Gradle 통합 (test task 분리, 위반 시 fail policy)
- Spring/JPA annotation 강제 규칙 (e.g., `@Service` 가 application 패키지 안에만 있어야 한다 등)
## 메모 / Notes
- ArchUnit User Guide 는 다른 4개 raw (Uncle Bob / Cockburn / Fowler / Richardson — 모두 personal blog) 와 달리 **유일한 official-vendor-doc** strength 자료. 따라서 ca-tmpl 의 "도구 선택" 결정은 본 자료만으로 단독 정당화 가능 (반면 layer/port 의 **개념 정의** 는 personal blog 들의 합성 필요).
- 본 페이지는 index 만 발췌. 실제 규칙 표현의 모든 매처 syntax (`..`, `.*`, `..foo..` 등) 는 별도 챕터 확인 필요 — 본 raw 를 wiki 로 승급할 때 추가 챕터 raw 도 함께 작성 권장.
- ArchUnit 의 "Onion Architecture" 사전 정의 API 도 존재하나 본 인용 범위 밖 — 별도 확인 후 추가 인용 가능.
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/official-docs/arch-clean-architecture-uncle-bob]] (강제할 의존 방향의 개념적 기반)
- [[raw/official-docs/arch-hexagonal-cockburn]] (port/adapter 의존 방향의 개념적 기반)
- 이 자료를 인용하는 branch:
- [[raw/branch-notes/feature-repository-access-permission-contract]]
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 이 자료를 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/at-transactional-spring-official.md
@@ -0,0 +1,103 @@
---
title: "Using @Transactional :: Spring Framework Reference"
source_type: official-doc
url: https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/annotations.html
archive_url:
status: raw
confidence: high
tags: [ca-transaction-boundary, at-transactional, spring-official, transaction-management, declarative-tx]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-application-port-usecase-contract, feature-transaction-concurrency-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Using @Transactional :: Spring Framework Reference
> Layer: `raw/official-docs/` — Spring Framework 7.x reference, `data-access/transaction/declarative/annotations` 섹션 verbatim 발췌.
> ca-tmpl TransactionPort 결정의 baseline 대안 (`@Transactional` 직접 application service 부착 패턴) 의 1차 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-application-port-usecase-contract]] | application layer 가 `org.springframework.transaction.annotation.Transactional` 을 import 하면 clean/hexagonal architecture dependency rule 위반이라는 결정 근거 (Spring 공식 권장 패턴을 정확히 식별) |
| [[raw/branch-notes/feature-transaction-concurrency-contract]] | Topic 2 — Transaction Boundary 대안 1 (`@Transactional` direct) 의 공식 정의·활성화 요구사항·self-invocation 함정 비교 baseline |
## 컨텍스트
ca-tmpl 의 TransactionPort 결정에 대한 대안 1: **`@Transactional` 직접 application service 에 부착**. Spring 공식이 권장하는 가장 흔한 패턴이며, ca-tmpl 이 forbidden 처리한 대상이므로 baseline 비교용 원문이 필요.
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/annotations.html
- 아카이브 URL: (미수집)
- 저자 / 조직: Spring Framework / VMware (Broadcom)
- 발행일: Spring Framework 7.x reference (current, rolling docs)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Using @Transactional] "The Spring team recommends that you annotate methods of concrete classes with the `@Transactional` annotation, rather than relying on annotated methods in interfaces, even if the latter does work for interface-based and target-class proxies as of 5.0."
> [§Using @Transactional] "Since Java annotations are not inherited from interfaces, interface-declared annotations are still not recognized by the weaving infrastructure when using AspectJ mode, so the aspect does not get applied. As a consequence, your transaction annotations may be silently ignored: Your code might appear to 'work' until you test a rollback scenario."
> [§Using @Transactional] "However, the mere presence of the `@Transactional` annotation is not enough to activate the transactional behavior. The `@Transactional` annotation is merely metadata that can be consumed by corresponding runtime infrastructure which uses that metadata to configure the appropriate beans with transactional behavior."
> [§Using @Transactional] "In the preceding examples that use programmatic configuration, the `@EnableTransactionManagement` annotation switches on actual transaction management at runtime."
> [§Method visibility and @Transactional in proxy mode] "In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object calling another method of the target object) does not lead to an actual transaction at runtime even if the invoked method is marked with `@Transactional`."
> [§Method visibility and @Transactional in proxy mode] "Consider using AspectJ mode (see the `mode` attribute in the following table) if you expect self-invocations to be wrapped with transactions as well."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AT-TX-C1 | Spring 팀은 인터페이스가 아닌 **concrete class 의 메서드**에 `@Transactional` 을 부착하도록 권장 (interface-based/target-class proxy 가 5.0부터 동작은 하지만 권장 아님) | [§Using @Transactional] "The Spring team recommends that you annotate methods of concrete classes with the `@Transactional` annotation, rather than relying on annotated methods in interfaces..." | `official-vendor-doc` | Spring Framework 5.0+ `@Transactional` 사용 시 | concrete class 부착이 self-invocation 함정도 해결한다는 뜻은 아님 (별도 항목, AT-TX-C5) |
| AT-TX-C2 | **AspectJ mode** 에서는 interface 에 부착된 `@Transactional` 이 weaving infrastructure 에 인식되지 않아 **silently 무시**될 수 있음 — rollback 시나리오 테스트 전까지 정상 동작처럼 보임 | [§Using @Transactional] "...interface-declared annotations are still not recognized by the weaving infrastructure when using AspectJ mode... your transaction annotations may be silently ignored: Your code might appear to 'work' until you test a rollback scenario." | `official-vendor-doc` | AspectJ mode + interface 에 `@Transactional` 부착한 경우 | proxy mode (기본값) 에서도 동일하게 무시된다는 뜻은 아님 (proxy mode 는 interface-based proxy 에서 인식 가능) |
| AT-TX-C3 | `@Transactional` 어노테이션의 **단순 존재만으로는** transactional behavior 가 활성화되지 않음 — 어노테이션은 **메타데이터**일 뿐, runtime infrastructure 가 이 메타데이터를 소비해야 함 | [§Using @Transactional] "However, the mere presence of the `@Transactional` annotation is not enough to activate the transactional behavior. The `@Transactional` annotation is merely metadata..." | `official-vendor-doc` | 모든 Spring `@Transactional` 사용 시 | 메타데이터 자체가 무가치하다는 뜻은 아님 — Spring Boot auto-config 환경에서는 활성화가 자동 (별도 항목) |
| AT-TX-C4 | **`@EnableTransactionManagement`** 어노테이션이 runtime 에서 실제 transaction management 를 활성화 (programmatic configuration 시) | [§Using @Transactional] "...the `@EnableTransactionManagement` annotation switches on actual transaction management at runtime." | `official-vendor-doc` | programmatic configuration (Java @Configuration) 사용 시 | XML `<tx:annotation-driven/>` 가 동등한 역할을 한다는 뜻을 본 인용에서 직접 확인할 수는 없음 (별도 페이지 필요) |
| AT-TX-C5 | proxy mode (기본값) 에서는 **self-invocation** (target object 내부 메서드 호출) 시 proxy 를 우회하므로 `@Transactional` 이 적용되지 않음 — AspectJ mode 사용을 고려하라는 공식 권고 | [§Method visibility and @Transactional in proxy mode] "...self-invocation... does not lead to an actual transaction at runtime even if the invoked method is marked with `@Transactional`." + "Consider using AspectJ mode... if you expect self-invocations to be wrapped with transactions as well." | `official-vendor-doc` | Spring proxy mode (default) | AspectJ mode 가 self-invocation 함정만 해결한다는 뜻은 아님 (interface annotation 함정은 별도, AT-TX-C2) |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `AT-TX-C1` ~ `C5`: Spring 공식의 `@Transactional` 사용 권장사항 (concrete class 부착), AspectJ mode 함정 (interface annotation silently ignored), 활성화 요건 (`@EnableTransactionManagement`), proxy mode 의 self-invocation 한계
- **이 자료가 증명하지 않는 것**:
- `@Transactional` 을 application service 에 직접 부착하는 것이 clean/hexagonal architecture 와 양립 가능하다 또는 불가능하다는 평가 (architecture-level 판단은 본 자료 범위 밖 — ca-tmpl 의 결정 근거는 별도 문서)
- `@Transactional` 의 propagation / isolation / rollbackFor / readOnly 속성의 상세 시맨틱 (같은 reference 의 다른 섹션에서 다룸, 본 raw 의 인용 범위 밖)
- Spring Boot auto-configuration 이 `@EnableTransactionManagement` 를 자동으로 활성화하는지 (Spring Boot 측 별도 문서 — 본 Spring Framework reference 에는 명시 없음)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 이 채택한 `TransactionPort` adapter 가 내부적으로 `@Transactional` 메서드를 호출할 때 self-invocation 함정에 걸리는지 (adapter Spring bean 외부 호출이라면 안전)
- AspectJ mode 사용 시 build pipeline (compile-time weaving) 추가 비용
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: 단일 모듈 Spring Boot 앱, 클린 아키텍처를 엄격히 적용하지 않는 일반 서비스. 가장 검증되고 익숙한 옵션.
- 장점:
- 가장 적은 코드. 메서드에 어노테이션 1줄.
- propagation / isolation / rollbackFor / readOnly 등 모든 속성을 선언적으로 제어.
- Spring 진영 표준이라 신규 개발자 학습 비용 최저.
- 단점:
- **application service 가 `org.springframework.transaction.annotation.Transactional` 을 import 해야 함 → clean/hexagonal architecture 에서 dependency rule 위반.**
- self-invocation 은 proxy 를 거치지 않아 silently 무시됨 (AT-TX-C5).
- 인터페이스에 단 annotation 은 AspectJ mode 에서 무시될 수 있음 (AT-TX-C2 공식 경고).
- 테스트 시 트랜잭션 동작 검증은 Spring context 필요.
- ca-tmpl (TransactionPort) 와의 차이: 정확히 ca-tmpl 이 막은 패턴. application layer 에 Spring import 가 새는 것이 핵심 차이.
- testability 영향: ★ 하락 — 트랜잭션 boundary 자체를 검증하려면 `@SpringBootTest` 또는 `@DataJpaTest` 필요.
- code 복잡도 영향: 최저 (어노테이션 1줄). 단, framework lock-in 비용은 숨겨져 있음.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/transaction-template-spring-official]] (대안 2: programmatic `TransactionTemplate`)
- 적용 ca-tmpl branch-note:
- [[raw/branch-notes/feature-application-port-usecase-contract]]
- [[raw/branch-notes/feature-transaction-concurrency-contract]]
- canonical contract 섹션:
- [[raw/project-notes/ca-skeleton-operational-contract]] (§14. Transaction / Concurrency Contract, §5. Exception Ownership Contract)
- 대안 그룹: **Topic 2 — Transaction Boundary** (5종: TransactionPort / @Transactional direct / TransactionTemplate / Functional monad / Custom AOP) — 본 source 는 **대안 1**.
- 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/aws-acm-managed-renewal.md
@@ -0,0 +1,130 @@
---
title: AWS Certificate Manager — Managed Certificate Renewal (official-vendor-doc)
source_type: official-doc
url: https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html
archive_url:
status: raw
confidence: high
tags: [aws, acm, tls, certificate, renewal, dns-validation, keycloak-https-termination]
related_projects: []
related_branches: [feature-keycloak-https-termination-caddy-nginx]
created: 2026-05-27
last_reviewed: 2026-05-27
---
# AWS Certificate Manager — Managed Certificate Renewal (공식)
> Layer: `raw/official-docs/` — AWS Certificate Manager (ACM) 공식 User Guide 의 **원문 발췌·출처 기록**.
> Strength 분류: `official-vendor-doc` — AWS 의 공식 documentation site (`docs.aws.amazon.com/acm/...`).
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 별도 작성.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-https-termination-caddy-nginx]] | **D4 (EC2 + ALB + ACM auto-renewal)** 의 근거 — ACM 이 (a) Amazon-issued public/private cert 의 자동 갱신, (b) DNS validation 시 fully automated renewal, (c) ELB / CloudFront 등 연동 시 ARN 유지 + zero-touch renewal 을 직접 진술. Caddy / certbot 대비 cloud-native managed cert 의 외부 근거. |
## 컨텍스트
`feature-keycloak-https-termination-caddy-nginx` 의 D4 는 "EC2 + ALB + ACM 을 운영 환경 대안으로 기재" 라는 결정을 다룬다. ACM Managed Certificate Renewal 페이지는 (a) 자동 갱신 대상 자격 (ELB / CloudFront 연동 필요), (b) DNS 검증 시 fully automated, (c) email 검증 시 expiration 임박 알림 발송, (d) imported / 만료 cert 의 자동 갱신 제외, (e) ARN 유지 + region scope 를 직접 진술한다. 본 raw 는 D4 의 외부 근거로 보관.
## 출처 / Source
- 원본 URL: https://docs.aws.amazon.com/acm/latest/userguide/managed-renewal.html
- 부속 URL (DNS 갱신 timing): https://docs.aws.amazon.com/acm/latest/userguide/dns-renewal-validation.html
- 부속 URL (public cert 갱신 개요): https://docs.aws.amazon.com/acm/latest/userguide/renew-publicly-trusted.html
- 아카이브 URL: (미수집 — 추후 archive.org 스냅샷 추가)
- 저자 / 조직: Amazon Web Services — ACM User Guide
- 발행일: rolling docs (ACM current)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Managed certificate renewal] "ACM provides managed renewal for your Amazon-issued SSL/TLS certificates. This means that ACM will either renew your certificates automatically (if you are using DNS validation), or it will send you email notices when expiration is approaching."
> [§Managed certificate renewal] "These services are provided for both public and private ACM certificates."
> [§Managed certificate renewal] "A certificate is eligible for automatic renewal subject to the following considerations:"
> [§Managed certificate renewal — Eligibility] "ELIGIBLE if associated with another AWS service, such as Elastic Load Balancing or CloudFront."
> [§Managed certificate renewal — Eligibility] "ELIGIBLE if exported since being issued or last renewed."
> [§Managed certificate renewal — Eligibility] "NOT ELIGIBLE if it is a private certificate issued by calling the AWS Private CA IssueCertificate API."
> [§Managed certificate renewal — Eligibility] "NOT ELIGIBLE if imported."
> [§Managed certificate renewal — Eligibility] "NOT ELIGIBLE if already expired."
> [§Managed certificate renewal] "When ACM renews a certificate, the certificate's Amazon Resource Name (ARN) remains the same. Also, ACM certificates are regional resources. If you have certificates for the same domain name in multiple AWS Regions, each of these certificates must be renewed independently."
> [§Renew ACM public certificates] "When issuing a managed, publicly trusted certificate, AWS Certificate Manager requires you to prove that you are the domain owner. This happens by means of either DNS validation or email validation. When a certificate comes up for renewal, ACM uses the same method that you chose earlier to re-validate your ownership."
> [§Renewal for domains validated by DNS] "Managed renewal is fully automated for ACM certificates that were originally issued using DNS validation."
> [§Renewal for domains validated by DNS] "At 45 days prior to expiration, ACM checks for the following renewal criteria:"
> [§Renewal for domains validated by DNS — Note] "Previously issued certificates with a 395-day validity period renew 60 days before expiration and receive a renewed validity period of 198 days. Certificates with a 198-day validity period renew 45 days before expiration."
> [§Renewal for domains validated by DNS — Criteria] "The certificate is currently in use by an AWS service."
> [§Renewal for domains validated by DNS — Criteria] "All required ACM-provided DNS CNAME records (one for each unique Subject Alternative Name) are present and accessible via public DNS."
> [§Renewal for domains validated by DNS] "If these criteria are met, ACM considers the domain names validated and renews the certificate."
> [§Renewal for domains validated by DNS] "ACM sends AWS Health events and Amazon EventBridge events if it can't automatically validate a domain during renewal. These events are sent at 30 days, 15 days, seven days, three days, and one day prior to expiration."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AWS-ACM-RENEW-C1 | ACM 은 **Amazon-issued SSL/TLS certificate** 에 대해 **managed renewal** 을 제공 — DNS validation 시 자동 갱신, 그 외 시 만료 임박 email 발송 | [§Managed certificate renewal] "ACM provides managed renewal for your Amazon-issued SSL/TLS certificates. This means that ACM will either renew your certificates automatically (if you are using DNS validation), or it will send you email notices when expiration is approaching." | `official-vendor-doc` | ACM-issued (Amazon-issued) certificate | imported certificate / 외부 CA cert 는 본 인용 범위 밖 (`C6` 참조) |
| AWS-ACM-RENEW-C2 | Managed renewal 은 **public + private ACM certificate 모두** 에 적용 | [§Managed certificate renewal] "These services are provided for both public and private ACM certificates." | `official-vendor-doc` | ACM public / private cert 의 갱신 정책 | private CA (AWS Private CA) 가 직접 `IssueCertificate` API 로 발급한 cert 는 별도 (`C7` 참조) |
| AWS-ACM-RENEW-C3 | 자동 갱신 자격 조건 1: **AWS 서비스 (ELB / CloudFront 등) 에 attach** 되어 있어야 함 | [§Managed certificate renewal — Eligibility] "ELIGIBLE if associated with another AWS service, such as Elastic Load Balancing or CloudFront." | `official-vendor-doc` | ACM cert 가 자동 갱신 대상이 되는 조건 | ELB / CloudFront 외 다른 AWS service (API Gateway, CloudFront Functions, App Runner 등) 의 정확한 목록은 본 인용 범위 밖 — "such as" 예시만 |
| AWS-ACM-RENEW-C4 | 자동 갱신 자격 조건 2 (대안): **발급/갱신 후 export 된 cert** 도 eligible | [§Managed certificate renewal — Eligibility] "ELIGIBLE if exported since being issued or last renewed." | `official-vendor-doc` | export 된 private cert 의 자동 갱신 | export 의 빈도 / 자동화 방법은 본 인용 범위 밖 |
| AWS-ACM-RENEW-C5 | **AWS Private CA `IssueCertificate` API 로 발급된 private cert 는 자동 갱신 NOT ELIGIBLE** | [§Managed certificate renewal — Eligibility] "NOT ELIGIBLE if it is a private certificate issued by calling the AWS Private CA IssueCertificate API." | `official-vendor-doc` | ACM Private CA API 사용 시나리오 | 사용자가 별도 갱신 자동화를 구성하는 방법 (Lambda + EventBridge 등) 은 본 인용 범위 밖 |
| AWS-ACM-RENEW-C6 | **Imported certificate 는 자동 갱신 NOT ELIGIBLE** | [§Managed certificate renewal — Eligibility] "NOT ELIGIBLE if imported." | `official-vendor-doc` | 외부 CA 에서 발급받아 ACM 에 import 한 cert | imported cert 의 만료 모니터링 메커니즘 (EventBridge expiry event 등) 은 본 인용 범위 밖 |
| AWS-ACM-RENEW-C7 | **이미 만료된 cert 는 자동 갱신 NOT ELIGIBLE** — 만료 이전에 갱신 트리거되어야 함 | [§Managed certificate renewal — Eligibility] "NOT ELIGIBLE if already expired." | `official-vendor-doc` | 만료된 ACM cert 의 처리 | 만료 후 재발급의 grace period / 절차는 본 인용 범위 밖 |
| AWS-ACM-RENEW-C8 | 갱신 시 cert 의 **ARN 은 유지** (변경되지 않음) — ELB listener / CloudFront distribution 등 ARN 참조 자원은 자동으로 새 cert 사용 | [§Managed certificate renewal] "When ACM renews a certificate, the certificate's Amazon Resource Name (ARN) remains the same." | `official-vendor-doc` | ACM cert 를 ARN 으로 참조하는 모든 AWS service | listener / distribution 의 cert reload timing 은 본 인용 범위 밖 — service 별 동작 |
| AWS-ACM-RENEW-C9 | ACM cert 는 **regional resource** — 동일 도메인이라도 region 마다 별도 발급 + 별도 갱신 | [§Managed certificate renewal] "ACM certificates are regional resources. If you have certificates for the same domain name in multiple AWS Regions, each of these certificates must be renewed independently." | `official-vendor-doc` | multi-region 배포 시 cert 관리 | CloudFront 가 us-east-1 ACM cert 만 사용한다는 별도 제약은 본 인용 범위 밖 — 별도 CloudFront 문서 |
| AWS-ACM-RENEW-C10 | 갱신 시 **최초 발급 시 선택한 validation method** (DNS or email) 을 그대로 재사용 | [§Renew ACM public certificates] "When a certificate comes up for renewal, ACM uses the same method that you chose earlier to re-validate your ownership." | `official-vendor-doc` | ACM public cert 의 갱신 validation 동작 | 발급 후 validation method 변경 가능 여부는 본 인용 범위 밖 |
| AWS-ACM-RENEW-C11 | **DNS validation 으로 발급된 cert 의 managed renewal 은 fully automated** | [§Renewal for domains validated by DNS] "Managed renewal is fully automated for ACM certificates that were originally issued using DNS validation." | `official-vendor-doc` | DNS-validated ACM public cert | email validation cert 는 fully automated 가 아님 — 만료 임박 시 사용자 action 필요 (별도 페이지) |
| AWS-ACM-RENEW-C12 | DNS-validated cert 의 갱신 시도는 **만료 45일 전** 에 시작 (또는 395-day cert 의 경우 60일 전) | [§Renewal for domains validated by DNS] "At 45 days prior to expiration, ACM checks for the following renewal criteria:" + "Previously issued certificates with a 395-day validity period renew 60 days before expiration and receive a renewed validity period of 198 days. Certificates with a 198-day validity period renew 45 days before expiration." | `official-vendor-doc` | ACM public cert (198-day current default) 와 legacy 395-day cert | 갱신 시도가 한 번에 성공한다는 보장은 없음 — `C14` 의 EventBridge alert schedule 참조 |
| AWS-ACM-RENEW-C13 | DNS-validated 자동 갱신 criteria: (a) cert 가 **AWS service 사용 중**, (b) ACM-provided **CNAME record 가 public DNS 에 여전히 존재** | [§Renewal for domains validated by DNS — Criteria] "The certificate is currently in use by an AWS service." + "All required ACM-provided DNS CNAME records (one for each unique Subject Alternative Name) are present and accessible via public DNS." | `official-vendor-doc` | DNS-validated cert 의 자동 갱신 사전 조건 | CNAME record 가 누락된 경우의 fallback 동작은 본 인용 범위 밖 — 갱신 실패 후 EventBridge alert (`C14`) 발생 |
| AWS-ACM-RENEW-C14 | 자동 validation 실패 시 ACM 은 **AWS Health + EventBridge event** 를 발송 — **만료 30일, 15일, 7일, 3일, 1일 전** 단계적 발송 | [§Renewal for domains validated by DNS] "ACM sends AWS Health events and Amazon EventBridge events if it can't automatically validate a domain during renewal. These events are sent at 30 days, 15 days, seven days, three days, and one day prior to expiration." | `official-vendor-doc` | renewal 실패 시 alert 메커니즘 | event 의 구체 schema / handler 자동화 (Lambda subscription 등) 는 본 인용 범위 밖 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `AWS-ACM-RENEW-C1`, `C11`: DNS validation 시 fully automated renewal (managed)
- `AWS-ACM-RENEW-C3`, `C4`: 자동 갱신 자격 (ELB / CloudFront attach 또는 export)
- `AWS-ACM-RENEW-C5`, `C6`, `C7`: 자동 갱신 제외 대상 (Private CA API / imported / expired)
- `AWS-ACM-RENEW-C8`: ARN 유지 — listener / distribution 무중단 갱신의 기반
- `AWS-ACM-RENEW-C9`: regional resource — multi-region cert 는 region 별 독립 갱신
- `AWS-ACM-RENEW-C12`: 갱신 시도 timing (45일 전, legacy 395-day cert 의 경우 60일 전)
- `AWS-ACM-RENEW-C13`, `C14`: 갱신 사전 조건 + 실패 시 alert schedule
- **이 자료가 증명하지 않는 것**:
- **ACM public cert 의 default validity period** — `C12` 의 "198-day validity period" 는 갱신 후 결과 lifetime 만 진술, 신규 발급 cert 의 default 가 198 일이라는 직접 진술은 본 페이지에 부재. 별도 ACM cert characteristics 페이지 확인 필요
- **HTTP validation 의 자동 갱신 동작** — 본 raw 의 인용은 DNS / email 만 다룸, HTTP-renewal-validation 은 별도 페이지
- **ALB Security Policy (TLS 1.2 enforce 등)** — ACM 은 cert 발급/갱신만 진술, listener 의 TLS policy 는 ELB 측 별도
- **갱신 시도의 retry 횟수 / 간격** — `C14` 는 alert schedule 만 진술, ACM 내부 retry 정책은 본 인용 범위 밖
- **Caddy / certbot 대비 운영 비교** — AWS 공식 문서는 자기 동작만 진술, 비교 결론은 별도 분석 필요
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- `feature-keycloak-https-termination-caddy-nginx` 의 D4 에서 "ACM 자동 갱신" 을 보장하려면, ALB 가 cert 를 attach (`C3`) + DNS validation (`C11`) 조건을 모두 충족해야 함
- Route53 hosted zone 의 ACM CNAME record 가 영구히 존재해야 함 (`C13`) — 운영 중 실수 삭제 시 갱신 실패 + EventBridge alert
- multi-region (예: ap-northeast-2 + us-east-1) 배포 시 cert 도 region 별 (`C9`) — IaC 에서 region-scoped 자원 관리 필요
- 갱신 실패 alert 의 실제 수신 (EventBridge → SNS → Slack 등) 은 별도 설정 필요 — `C14` 는 alert 발송만 보증
## 메모 / Notes
- `C12` 의 "198-day validity" 는 2024년 ACM 정책 변화의 결과 — 이전 발급 cert 는 395일 (13개월), 신규 / 갱신 cert 는 198일 (약 6.5개월). wiki/concepts 옮길 때 변화 timeline 명시 필요.
- `C8` (ARN 유지) 는 D4 의 핵심 장점 — Caddy / certbot 처럼 cert 파일 path 가 바뀌지 않고, ALB listener config 도 수정 불필요. Terraform / CloudFormation 의 lifecycle 단순화.
- `C5` 는 함정 — AWS Private CA 를 직접 API 로 부르면 자동 갱신이 끊김. ACM 의 `RequestCertificate` API 를 통해서 발급 + AWS service 에 attach 해야 자동화 작동.
- `C14` 의 EventBridge alert 는 **renewal 시도가 실패한 경우에만** 발송 — 정상 갱신 시에는 alert 없음. "갱신 됐는지 확인" 은 별도 ACM `DescribeCertificate` API / EventBridge `ACM Certificate Renewal Action Required` event 필요.
## Related / 관련
- 같은 주제 다른 raw 자료: [[raw/official-docs/caddy-automatic-https-docs.md]] (auto-HTTPS 대안), [[raw/official-docs/certbot-user-guide.md]] (Let's Encrypt + nginx 대안)
- 이 자료를 인용한 wiki 요약: (미작성)
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-keycloak-https-termination-caddy-nginx]]
- 인용하는 project: [[raw/project-notes/keycloak-patterns-overview]]
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/aws-alb-target-security-group-restriction-official.md
@@ -0,0 +1,90 @@
---
title: official-doc / AWS Application Load Balancer — Security Groups for Your Load Balancer (Target SG Restriction)
source_type: official-doc
url: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-update-security-groups.html
archive_url:
related_branches: [feature-keycloak-header-spoofing-defense]
related_projects: [keycloak-patterns]
tags: [official-doc, keycloak-patterns, security, aws, security-group]
created: 2026-07-16
---
# official-doc / AWS Application Load Balancer — Security Groups for Your Load Balancer (Target SG Restriction)
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## source_type 허용값
`official-doc` — 공식 레퍼런스 / 표준 / 사양 (AWS Elastic Load Balancing 공식 문서).
## Parent / 활용 branch (필수, 최소 1개+)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-header-spoofing-defense]] | D4 — AWS 공식 권고: target instance 의 Security Group 을 load balancer 의 Security Group 만 traffic 을 허용하도록 제한 (target SG ingress rule 의 source 를 LB SG 로 설정). 단, D4 의 backend listen-address(`127.0.0.1` vs `0.0.0.0`) 부분은 본 자료가 다루지 않음 (아래 Usage Boundaries 참조). |
## 출처 / Source
- 원본 URL: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-update-security-groups.html
- 아카이브 URL: (미확보)
- 저자 / 조직: Amazon Web Services (AWS Elastic Load Balancing 공식 문서, "Application Load Balancers" 사용자 가이드)
- 발행일: (페이지에 명시된 발행일 없음 — AWS docs 는 지속 갱신되는 living doc)
- 마지막 확인일: 2026-07-16
## 왜 저장했는지 / Why archived
`feature-keycloak-header-spoofing-defense` branch 의 D4 결정("EC2/VM 환경: Security Group inbound 를 ALB/ingress SG 만 허용")이 지금까지 `UNSUPPORTED_DECISION`(AWS 공식 인용 verbatim 미확보)이었다. 이 자료는 "target 의 Security Group 을 load balancer 의 Security Group 만 허용하도록 제한"하라는 AWS 공식 권고를 verbatim 으로 확보하기 위해 저장.
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
> [§Considerations] "To ensure your targets receive traffic exclusively from the load balancer, restrict the security groups associated with your targets to accept traffic solely from the load balancer. This can be achieved by setting the load balancer's security group as the source in the ingress rule of the target's security group."
> [§Recommended rules — internet-facing] "The following rules are recommended for an internet-facing load balancer with instances as targets."
> [§Recommended rules — internet-facing, Outbound row] "{{instance security group}} | {{instance listener}} | Allow outbound traffic to instances on the instance listener port"
> [§Recommended rules — internal] "The following rules are recommended for an internal load balancer with instances as targets."
> [§Recommended rules — internal, Inbound row] "{{VPC CIDR}} | {{listener}} | Allow inbound traffic from the VPC CIDR on the load balancer listener port"
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| ALB-SG-C1 | AWS 는 target 이 load balancer 로부터만 트래픽을 받도록, target 에 연결된 security group 을 "load balancer 의 security group 을 target security group ingress rule 의 source 로 설정"하는 방식으로 제한할 것을 권고한다. | [§Considerations] "To ensure your targets receive traffic exclusively from the load balancer, restrict the security groups associated with your targets to accept traffic solely from the load balancer. This can be achieved by setting the load balancer's security group as the source in the ingress rule of the target's security group." | `official-vendor-doc` | ALB + EC2 instance target 의 security group 설정 (target 의 inbound rule 이 LB SG 를 source 로 지정) | target 이 non-instance target type(IP, Lambda) 일 때도 동일 메커니즘이 적용되는지, VPC 내부 다른 리소스로부터의 lateral movement 차단 여부, backend 의 listen address(`0.0.0.0` vs `127.0.0.1`) 권고 여부는 증명하지 않음 |
| ALB-SG-C2 | AWS 의 "Recommended rules" 예시 표는 target(instance) 의 security group 자체의 inbound rule 예시가 아니라, **load balancer 자신의 security group**의 inbound(source=`0.0.0.0/0` 또는 `{{VPC CIDR}}`)/outbound(destination=`{{instance security group}}`) 규칙 예시다. | [§Recommended rules — internet-facing] "The following rules are recommended for an internet-facing load balancer with instances as targets." + Outbound row: "{{instance security group}} \| {{instance listener}} \| Allow outbound traffic to instances on the instance listener port" | `official-vendor-doc` | "Recommended rules" 섹션이 실제로 무엇을 예시하는지 (LB 자신의 SG 규칙 표) 를 정확히 규정 | target(instance) SG 의 ingress rule 에 "source = LB SG" 를 넣은 **표 형태의 워크드 예시는 이 페이지에 존재하지 않음** — 그 권고는 §Considerations 산문(ALB-SG-C1)에만 있고 §Recommended rules 표에는 없음 |
| ALB-SG-C3 | Internal load balancer 의 "Recommended rules" 예시는 (target SG 가 아니라) **load balancer 자신의 SG** inbound source 로 `{{VPC CIDR}}` 를 사용한다 — target SG 의 source 가 아님. | [§Recommended rules — internal] "The following rules are recommended for an internal load balancer with instances as targets." + Inbound row: "{{VPC CIDR}} \| {{listener}} \| Allow inbound traffic from the VPC CIDR on the load balancer listener port" | `official-vendor-doc` | internal ALB 자신의 SG inbound 설계에서 source 가 VPC CIDR 임을 확인 | 이 VPC CIDR 예시는 target(instance) SG 의 inbound rule 이 아니므로, "target SG source = VPC CIDR vs LB SG" 비교의 직접 대조 예시로 오독하면 안 됨 (LB 자신의 SG 예시일 뿐) |
### Strength 허용값
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
- `official-vendor-doc` — Spring, Keycloak, AWS, Google 등 공식 벤더 문서
- `official-reference` — 공식 reference/API 문서
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
- `tutorial` — 튜토리얼/가이드. 일반화 금지
- `needs-confirmation` — 원문만으로는 적용 판단 불가
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `ALB-SG-C1`: target(EC2 instance) 의 security group 을 "source = load balancer 의 security group" 으로 제한하는 것이 AWS 의 공식 권고임.
- `ALB-SG-C2`/`ALB-SG-C3`: AWS 의 "Recommended rules" 예시 표는 LB 자신의 SG 규칙을 다루며, target SG 의 "source=LB SG" 워크드 예시(표)는 이 페이지에 없음 — 그 권고는 산문(Considerations)에만 존재.
- 이 자료가 증명하지 않는 것:
- target 이 EC2 instance 가 아닌 IP target 또는 Lambda target 일 때도 동일 메커니즘이 적용되는지
- VPC 내부의 다른(비-LB) 리소스로부터의 lateral movement 차단 여부 (target SG 를 LB SG 로 제한해도 같은 VPC 의 다른 SG 가 별도로 허용되면 우회 가능 — 이 페이지는 그 시나리오를 다루지 않음)
- backend 가 `0.0.0.0` 대신 `127.0.0.1` 로 listen 해야 한다는 권고 (D4 의 나머지 절반 — 이 자료는 SG 레벨만 다루고 프로세스 bind address 는 다루지 않음)
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- 단일 EC2 인스턴스 환경에서 ALB 없이 직접 운영 중이라면(현재 학습 프로젝트 상태), 이 권고가 적용될 실제 ALB 배포가 없다는 점을 branch-note 본문에서 명시해야 함 (`documented-only` 등급 유지).
## 메모 / Notes
- 사용자가 요청한 "recommended-rules 예시에서 source=LB SG 인 예시" 는 **이 페이지에 존재하지 않는다**. "Recommended rules" 표 3종(internet-facing / internal / ALB-as-NLB-target) 은 전부 **load balancer 자신의 SG** 규칙(inbound: `0.0.0.0/0` 또는 `{{VPC CIDR}}`, outbound: `{{instance security group}}`)만 보여준다. target(instance) SG 의 ingress rule 예시(= source가 LB SG)는 표가 아니라 §Considerations 산문 한 문장(`ALB-SG-C1`)으로만 서술되어 있다. 다음 구현자가 워크드 표 예시를 찾는다면 이 페이지가 아니라 EC2 Security Group 별도 공식 문서를 확인해야 함.
- internal LB 의 VPC CIDR 예시(`ALB-SG-C3`)는 target SG 예시가 아니라 LB 자신의 inbound 예시이므로, D4 의 "target SG source" 논의에 직접 대응시키면 오독.
## Related / 관련
- `raw/official-docs/k8s-network-policy-official` — (검토 후보, 아직 raw 부재) K8s NetworkPolicy 공식 — D3 관련
- 이 자료를 인용한 wiki 요약: (아직 생성 안 됨)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/aws-builders-retry-jitter.md
@@ -0,0 +1,95 @@
---
title: AWS Builders Library — Timeouts, Retries, and Backoff with Jitter (official-vendor-doc)
source_type: official-doc
url: https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
archive_url: https://web.archive.org/web/20260629/https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
status: raw
confidence: high
tags: [retry, backoff, jitter, full-jitter, concurrency, resilience, distributed-systems, aws-builders]
related_projects: [ca-skeleton]
related_branches: [feature-webhook-outbound-contract]
created: 2026-06-29
last_reviewed: 2026-06-29
---
# AWS Builders Library — Timeouts, Retries, and Backoff with Jitter (공식)
> Layer: `raw/official-docs/` — AWS Builders Library 공식 아티클의 **원문 발췌 및 출처 기록**.
> Strength 분류: `official-vendor-doc` — AWS 아키텍처 및 시스템 엔지니어링 라이브러리 (`aws.amazon.com/builders-library/...`).
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-webhook-outbound-contract]] | **D3 (Full Jitter 기반 지수 백오프 리트라이)** 결정 시 리트라이 간격 계산식 및 백오프 상한(Cap) 지정 근거. |
## 컨텍스트
`feature-webhook-outbound-contract` 의 D3 은 네트워크 실패나 수신단 일시 장애 발생 시 적용할 웹훅 재전송 정책을 수립한다. 분산 환경에서 단순 지수 백오프만 적용할 경우, 여러 실패 요청이 동일한 타이밍에 재시도되어 "재시도 폭풍(Retry Storm)"을 일으키는 서버 동기화 현상이 발생한다. 본 아티클은 AWS 가 (a) 재시도 Storm 현상 원인, (b) 4가지 지터 알고리즘(No Jitter, Full Jitter, Equal Jitter, Decorrelated Jitter)의 수학적 수식 및 비교 실험 결과, (c) Full Jitter가 리소스를 최소화하면서 가장 우수한 완료 p99 시점을 제공함을 수식과 데이터로 직접 증명하는 핵심 자료이다.
## 출처 / Source
- 원본 URL: https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/
- 저자 / 조직: AWS (Marc Brooker — Senior Principal Engineer)
- 마지막 확인일: 2026-06-29
## 핵심 인용 / Key quotes (verbatim)
> [§Timeouts, Retries, and Backoff with Jitter] "When a call fails, a client should retry. However, if all clients retry immediately on failure, it can overwhelm the downstream service, causing a cascading failure. We call this a retry storm."
> [§Backoff] "Instead of retrying immediately, the client should wait some amount of time between retries. The standard way to do this is with exponential backoff: the client waits exponentially longer after each failed attempt."
> [§Jitter] "Adding jitter is a standard way to prevent retry storms in distributed systems. Jitter randomizes the backoff time, spreading out the retries over time and breaking the synchronization between clients."
> [§Jitter — Algorithms] "No Jitter: sleep = min(cap, base * 2^attempt)"
> [§Jitter — Algorithms] "Full Jitter: temp = min(cap, base * 2^attempt); sleep = random(0, temp)"
> [§Jitter — Algorithms] "Equal Jitter: temp = min(cap, base * 2^attempt); sleep = temp/2 + random(0, temp/2)"
> [§Jitter — Algorithms] "Decorrelated Jitter: sleep = min(cap, random(base, sleep * 3))"
> [§Jitter — Comparison] "Full Jitter yields the lowest client work (total number of calls) and the lowest server load (requests per second) compared to No Jitter and Equal Jitter. It succeeds in breaking the synchronization completely."
> [§Jitter — Cap] "A cap is required to prevent the sleep time from growing to infinity. Without a cap, subsequent retries would take hours or days, which is unacceptable for most user-facing systems."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AWS-JITTER-C1 | 실패 시 클라이언트가 즉시 재시도하면 다운스트림 서비스를 압도하여 연쇄 장애(retry storm)를 일으킴 | "When a call fails, a client should retry. However, if all clients retry immediately on failure, it can overwhelm the downstream service, causing a cascading failure." | `official-vendor-doc` | 장애 발생 시 백오프 정책 필요성 | 특정 HTTP 상태코드별 예외 처리 |
| AWS-JITTER-C2 | 단순 exponential backoff는 대기시간을 늘리지만, 클라이언트 간의 호출 동기화(synchronization)를 막지는 못함 | "Instead of retrying immediately, the client should wait some amount of time... The standard way to do this is with exponential backoff..." | `official-vendor-doc` | 지수 백오프 한계 인식 | 단일 클라이언트 상황에서의 대기 효율 |
| AWS-JITTER-C3 | 백오프 시간을 무작위화하는 Jitter를 추가함으로써 재시도를 분산시키고 동기화를 깰 수 있음 | "Adding jitter is a standard way to prevent retry storms in distributed systems. Jitter randomizes the backoff time, spreading out the retries..." | `official-vendor-doc` | 분산 시스템 부하 분산 | Jitter 추가에 의한 네트워크 지연 감소 |
| AWS-JITTER-C4 | Full Jitter 식: 대기 시간을 `0 ~ min(cap, base * 2^attempt)` 사이에서 완전 무작위로 추출함 | "Full Jitter: temp = min(cap, base * 2^attempt); sleep = random(0, temp)" | `official-vendor-doc` | Full Jitter 백오프 계산식 설계 | Decorrelated Jitter의 정확한 수학적 증명 |
| AWS-JITTER-C5 | Equal Jitter 식: 대기 시간의 절반은 고정하고 나머지 절반 범위에서 무작위로 추출함 | "Equal Jitter: temp = min(cap, base * 2^attempt); sleep = temp/2 + random(0, temp/2)" | `official-vendor-doc` | 대안 Jitter 알고리즘 검토 | Full Jitter 대비 서버 부하 경감 능력 |
| AWS-JITTER-C6 | Decorrelated Jitter 식: 이전 sleep 값의 3배 범위 내에서 무작위로 계산해 누적함 | "Decorrelated Jitter: sleep = min(cap, random(base, sleep * 3))" | `official-vendor-doc` | 클라이언트 시점의 완료 시간 단축 | 클라이언트 측의 이전 sleep 값 저장 상태 관리 여부 |
| AWS-JITTER-C7 | Full Jitter는 No Jitter 및 Equal Jitter 대비 가장 적은 총 호출 수(client work)와 최소한의 서버 부하를 제공함 | "Full Jitter yields the lowest client work (total number of calls) and the lowest server load (requests per second) compared to..." | `official-vendor-doc` | 웹훅 서버 부하 경감용 알고리즘 선정 | 네트워크 latency의 영향성 배제 |
| AWS-JITTER-C8 | 백오프 시간의 무한 증가를 방지하고 현실적인 범위 내로 제한하기 위해 반드시 Cap(상한선)이 필요함 | "A cap is required to prevent the sleep time from growing to infinity. Without a cap, subsequent retries would take hours or days..." | `official-vendor-doc` | 백오프 파라미터 튜닝 | Cap 초과 시의 영구 실패 처리 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `AWS-JITTER-C4`, `C7`: `Full Jitter` 계산 메커니즘이 분산 웹훅 전송 실패 상황에서 다운스트림 수신 서버에 가하는 충격을 완화하는 가장 안전한 백오프 방식임을 증명.
- `AWS-JITTER-C8`: Jitter 계산 공식에 상한인 `cap` (예: 1시간 = 3600초)을 적용해 대기 시간 폭증을 제어해야 함.
- **이 자료가 증명하지 않는 것**:
- **웹훅 전송 순서 보장 (Ordering)** — 리트라이 시 지터 대기 시간이 무작위로 결정되므로, 재전송 요청 간의 **순서 역전 현상**이 발생하며, 이를 해결하기 위한 타임스탬프 기반 수신 데이터 시퀀싱 기법은 증명 범위 밖임 (Shopify 문서 참조 필요).
- **Dead Letter Queue (DLQ) 처리** — 최대 재시도 횟수(Max Attempts, 예: 5회)를 초과하여 최종 실패 처리될 때의 영구 보관 저장소(DLQ) 아키텍처는 다루지 않음.
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- Resilience4j의 `IntervalFunction.ofExponentialRandomBackoff` 가 제공하는 Jitter 알고리즘이 AWS의 `Full Jitter` 식과 수학적으로 동일하게 무작위성을 부여하는지, 아니면 자체 `FullJitterBackoffPolicy` 클래스를 작성하여 커스텀해야 하는지 코드 레벨 확인 필요.
## 메모 / Notes
- **Full Jitter 구현 수식**:
`temp = Math.min(capMs, baseMs * Math.pow(2, attempt))`
`sleep = ThreadLocalRandom.current().nextLong(0, temp)`
- **Standard retry parameters for B2B Webhooks**:
- Max Attempts: 5
- Base interval (initial-backoff): 10초
- Cap (max-backoff): 1시간 (3600초)
- 5회 시도 후 DLQ로 넘어가며, DB status가 `DELIVERY_FAILED`로 마킹되고 운영 경보가 전송됨.
## Related / 관련
- 관련 raw 자료: [[raw/official-docs/stripe-webhook-signature.md]], [[raw/official-docs/svix-webhook-best-practices.md]]
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-webhook-outbound-contract.md]]
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract.md]]
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/aws-cloudfront-origin-shared-secret-header-official.md
@@ -0,0 +1,84 @@
---
title: official-doc / AWS CloudFront — Restrict access to Application Load Balancers (origin custom header + prefix list)
source_type: official-doc
url: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/restrict-access-to-load-balancer.html
archive_url:
related_branches: [feature-keycloak-header-spoofing-defense]
related_projects: [keycloak-patterns]
tags: [official-doc, keycloak-patterns, security, networking, aws]
created: 2026-07-16
---
# AWS CloudFront — Restrict access to Application Load Balancers (origin custom header + prefix list)
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수, 최소 1개+)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-header-spoofing-defense]] | D5 — shared-secret internal header 패턴(엣지가 secret header 주입, origin 이 검증)은 AWS(CloudFront→ALB)가 공식 문서화한 mitigation 임을 뒷받침. 헤더 값을 secure credential 로 취급하고 make-before-break 로 회전해야 하며, secret 유출 시 전면 우회되므로 network-layer 제한(2차 방어)과 반드시 병행해야 한다는 결론의 vendor-doc 근거 |
## 출처 / Source
- 원본 URL: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/restrict-access-to-load-balancer.html
- 아카이브 URL: (미제공)
- 저자 / 조직: Amazon Web Services — Amazon CloudFront Developer Guide
- 발행일: 페이지에 발행일 명시 없음 (AWS 공식 개발자 가이드, 버전 관리형 상시 갱신 문서)
- 마지막 확인일: 2026-07-16
## 왜 저장했는지 / Why archived
`feature-keycloak-header-spoofing-defense` D5(shared-secret 헤더는 defense-in-depth 2차, 1차는 network 격리)가 이전엔 `UNSUPPORTED_DECISION`(vendor 인용 부재, 자체 메모)였다. 이 문서는 CloudFront→ALB 맥락에서 동일 패턴(엣지가 custom header 주입, origin 이 그 header 존재로만 요청을 필터링)을 AWS 가 공식적으로 기술하며, 헤더를 credential 로 취급하라는 권고·헤더 유출 시 전면 우회된다는 명시적 경고·network-layer(prefix list) 병행 권고·make-before-break 회전 절차까지 모두 명시한다. D5 를 `official-vendor-doc` 등급 근거로 승격시키기 위해 보관.
## 핵심 인용 / Key quotes (verbatim, 5개)
> [§Configure CloudFront to add a custom HTTP header to requests] "Configure CloudFront to add a custom HTTP header to requests that it sends to the Application Load Balancer."
> [§Configure an Application Load Balancer to only forward requests that contain a specific header] "Configure the Application Load Balancer to only forward requests that contain the custom HTTP header."
> [§Configure CloudFront to add a custom HTTP header to requests] "In production, use randomly generated header names and values. Treat header names and values as secure credentials, like usernames and passwords."
> [§Configure CloudFront to add a custom HTTP header to requests, **Important** callout] "This use case relies on keeping the custom header name and value secret. If the header name and value are not secret, other HTTP clients could potentially include them in requests that they send directly to the Application Load Balancer. This can cause the Application Load Balancer to behave as though the requests came from CloudFront when they did not. To prevent this, keep the custom header name and value secret."
> [§(Optional) Limit access to origin by using the AWS-managed prefix list for CloudFront] "To further restrict access to your Application Load Balancer, you can configure the security group associated with the Application Load Balancer so that it only accept traffic from CloudFront when the service is using an AWS-managed prefix list. This prevents traffic that doesn't originate from CloudFront from reaching your Application Load Balancer at the network layer (layer 3) or transport layer (layer 4)."
> [§(Optional) Improve the security of this solution — Rotate the header name and value] "In addition to using HTTPS, we also recommend rotating the header name and value periodically. The high-level steps for doing this are as follows:" — 이어지는 4단계 절차 중 make-before-break 순서를 보여주는 첫 단계와 마지막 단계: "Configure CloudFront to add an additional custom HTTP header to requests that it sends to the Application Load Balancer." ... "Update the Application Load Balancer listener rule to stop forwarding requests that contain the original custom HTTP header."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CF-ALB-SECRET-C1 | CloudFront 는 origin(ALB)으로 보내는 요청에 custom HTTP header 를 추가하도록 설정할 수 있고, ALB listener rule 은 그 custom header 가 포함된 요청만 forward 하도록 설정할 수 있다(그 외는 고정 403 응답) | "Configure CloudFront to add a custom HTTP header to requests that it sends to the Application Load Balancer." / "Configure the Application Load Balancer to only forward requests that contain the custom HTTP header." | `official-vendor-doc` | CloudFront(엣지)→ALB(origin) 구조에서의 shared-secret header 검증 패턴 일반. Keycloak/oauth2-proxy→backend 같은 다른 엣지·오리진 조합에 그대로 이식된다는 뜻은 아님(구조적 유사성만 인용 가능) | 이 메커니즘이 K8s NetworkPolicy 나 EC2 Security Group 을 대체할 만큼 충분하다는 것은 증명 안 함 — 문서 자체가 이를 별도 "improve security" 권고로 분리 |
| CF-ALB-SECRET-C2 | production 에서는 무작위 생성된 header 이름·값을 쓰고, header 이름/값을 username·password 같은 secure credential 로 취급하라 | "In production, use randomly generated header names and values. Treat header names and values as secure credentials, like usernames and passwords." | `official-vendor-doc` | shared-secret header 의 생성·보관·취급 원칙(값의 무작위성, credential 급 보안 취급) | 구체적인 저장소(예: AWS Secrets Manager vs 환경변수)나 rotation 주기 수치는 명시 안 함 |
| CF-ALB-SECRET-C3 | header 이름과 값이 secret 으로 유지되지 않으면 다른 HTTP client 가 그 header 를 담아 ALB 에 직접 요청을 보낼 수 있고, 이 경우 ALB 는 실제로는 CloudFront 를 거치지 않은 요청도 CloudFront 를 거친 것처럼 처리한다 — 즉 secret 유출 = 이 메커니즘의 전면 우회 | "This use case relies on keeping the custom header name and value secret. If the header name and value are not secret, other HTTP clients could potentially include them in requests that they send directly to the Application Load Balancer. This can cause the Application Load Balancer to behave as though the requests came from CloudFront when they did not." | `official-vendor-doc` | shared-secret header 패턴의 명시적 실패 모드(단일 장애점: secret 유출) | 유출 경로(로그 노출, 네트워크 스니핑 등) 자체는 다루지 않음 — 유출됐을 때의 결과만 서술 |
| CF-ALB-SECRET-C4 | ALB 에 연결된 security group 을 AWS-managed prefix list(CloudFront) 로 제한하면, CloudFront 를 거치지 않은 트래픽은 network layer(L3)/transport layer(L4) 에서부터 ALB 에 도달하지 못하게 막을 수 있다 | "To further restrict access to your Application Load Balancer, you can configure the security group associated with the Application Load Balancer so that it only accept traffic from CloudFront when the service is using an AWS-managed prefix list. This prevents traffic that doesn't originate from CloudFront from reaching your Application Load Balancer at the network layer (layer 3) or transport layer (layer 4)." | `official-vendor-doc` | shared-secret header 검증(응용 계층, L7)을 network-layer 제한(L3/L4)과 **병행**해야 하는 근거 — "(Optional)" 로 표기되었으나 header 단독 사용의 실패 모드(C3)를 상쇄하는 유일한 공식 권고 | prefix list 제한 자체가 header 검증을 "대체"해도 된다고는 말하지 않음 — 문서는 두 메커니즘을 병행 옵션으로만 제시 |
| CF-ALB-SECRET-C5 | header 이름/값은 주기적으로 회전(rotate)하도록 권고되며, 절차는 (1) 새 custom header 추가 및 새 header 를 forward 하는 ALB rule 추가 → (2) 기존 header 를 CloudFront 가 더 이상 보내지 않도록 중단 및 기존 header 를 forward 하던 ALB rule 제거 순서다(신규 추가 후 기존 제거 — make-before-break) | "In addition to using HTTPS, we also recommend rotating the header name and value periodically." / "Configure CloudFront to add an additional custom HTTP header to requests that it sends to the Application Load Balancer." / "Update the Application Load Balancer listener rule to stop forwarding requests that contain the original custom HTTP header." | `official-vendor-doc` | header 회전이 필요한 이유(주기적 노출 위험 감소)와 순서(추가 먼저, 제거 나중 — 4단계 절차의 1번과 4번이 각각 add-new, remove-old) | 정확한 회전 "주기"(예: N일마다) 는 수치로 명시하지 않음 — "periodically" 로만 서술 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `CF-ALB-SECRET-C1`: CloudFront→ALB 맥락에서 "엣지가 header 주입 + origin 이 header 존재로 필터링" 패턴이 AWS 공식 mitigation 이라는 것
- `CF-ALB-SECRET-C2`: header 값을 secure credential 급으로 취급하라는 공식 권고
- `CF-ALB-SECRET-C3`: 이 패턴의 실패 모드가 "secret 유출 = 전면 우회"라는 것 (AWS 문서가 명시적으로 경고)
- `CF-ALB-SECRET-C4`: network-layer(prefix list/security group) 제한을 header 검증과 병행하라는 공식 권고
- `CF-ALB-SECRET-C5`: 회전 절차의 순서(add-new-before-remove-old)
- 이 자료가 증명하지 않는 것:
- Keycloak/oauth2-proxy/nginx auth_request 같은 다른 엣지-오리진 조합에서도 동일 mitigation 이 "충분"하다는 것 — 이 문서는 CloudFront↔ALB 조합에 한정된 AWS 공식 가이드
- K8s NetworkPolicy, EC2 Security Group inbound, mTLS 각각의 구체적 설정법 — 이 문서는 "AWS-managed prefix list" 방식만 다룸 (D3/D4/D2 의 근거로는 사용 불가)
- shared-secret header 단독으로 충분한지 여부 — 오히려 이 문서 자체가 "단독 사용은 실패 모드(C3)를 가지므로 network-layer 제한과 병행하라(C4)"는 구조로 D5 의 "network 격리 1차, header 는 2차" 결론과 정합
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- 학습 프로젝트의 실제 엣지(oauth2-proxy/nginx)-백엔드 조합에서 동일 make-before-break 회전을 재현 가능한지 (`raw/branch-notes/feature-keycloak-header-spoofing-defense` §Claims To Verify 참조)
- AWS 환경이 아닌 셀프호스팅 nginx/oauth2-proxy 조합에서 "AWS-managed prefix list" 대응물(예: 자체 IP allowlist)을 어떻게 구성할지
## 메모 / Notes
- 이 문서는 CloudFront/ALB 전용이지만, "엣지가 secret header 주입 + origin 이 header 존재만으로 필터링"하는 **구조** 자체는 P1A ForwardAuth 패턴(oauth2-proxy/nginx → backend)과 동형이다. D5 의 vendor-doc 근거로 인용하되, 구조적 유사성 인용이라는 점을 명시해야 함(직접 Keycloak/nginx 문서는 아님).
- C3(실패 모드)와 C4(network-layer 병행 권고)를 나란히 읽으면, AWS 문서 스스로가 "header 검증 단독으로는 불충분 → network-layer 로 보강"하는 구조를 권고하고 있음을 알 수 있음. D5 의 "network 격리 1차, shared-secret 2차" 결론과 직접 정합.
- 추가로 봐야 할 동일 출처 페이지: AWS-managed prefix list 블로그 포스트(문서 본문에서 링크된 `Limit access to your origins using the AWS-managed prefix list for Amazon CloudFront`) — prefix list 설정의 구체적 CLI/console 절차가 필요하면 별도 raw 로 보존 검토.
## Related / 관련
- [[raw/branch-notes/feature-keycloak-header-spoofing-defense]] — 이 자료가 D5 의 근거로 인용되는 branch
- (같은 주제 다른 official-doc) K8s NetworkPolicy 공식 문서 — 아직 raw 미보존 (해당 branch TODO 참조)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/aws-iam-google-iam-permission-naming-convention.md
@@ -0,0 +1,104 @@
---
title: Permission Naming Convention — AWS IAM (service:Action) and Google Cloud IAM (service.resource.verb)
source_type: official-doc
url: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html
archive_url:
related_branches: [feature-authentication-authorization-contract]
related_projects: [ca-skeleton]
tags: [authorization, permission-naming, aws-iam, google-iam, resource-action, naming-convention, official-doc]
created: 2026-06-08
last_reviewed: 2026-06-08
---
# Permission Naming Convention — AWS IAM (service:Action) and Google Cloud IAM (service.resource.verb)
> Layer: `raw/official-docs/` — AWS IAM Action element 공식 문서 + Google Cloud IAM permissions 공식 문서의 verbatim 발췌.
> feature-authentication-authorization-contract 의 permission naming convention axis (`resource:action` style) 결정의 비교 근거.
> 두 업계 표준의 naming format 을 grounding 하여 `worklog:close` 형식의 선택 근거 제공.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-authentication-authorization-contract]] | `resource:action` (`worklog:close`) permission naming convention 채택 결정 — AWS IAM `service:Action` / Google IAM `service.resource.verb` 대비 trade-off |
## 출처 / Source
**AWS IAM:**
- 원본 URL: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html
- 저자 / 조직: Amazon Web Services (AWS Documentation)
- 발행일: rolling docs
- 마지막 확인일: 2026-06-08
**Google Cloud IAM:**
- 원본 URL: https://docs.cloud.google.com/iam/docs/roles-overview
- 저자 / 조직: Google Cloud (Google LLC)
- 발행일: rolling docs
- 마지막 확인일: 2026-06-08
## 왜 저장했는지 / Why archived
application-level permission naming (`resource:action` vs `service:action` vs `service.resource.verb`) 의 결정을 업계 표준 두 가지로 grounding. AWS IAM 의 `service:Action` colon-separated format 과 Google IAM 의 `service.resource.verb` dot-separated format 은 각각 서로 다른 separator 와 granularity 를 사용하므로, 내부 application permission naming 시 어떤 format 을 참조할지 결정 근거가 됨.
## 핵심 인용 / Key quotes (verbatim)
### AWS IAM Action Element
> [§AWS IAM Action — format] "You specify a value using a service namespace as an action prefix (iam, ec2, sqs, sns, s3, etc.) followed by the name of the action to allow or deny. The name must match an action that is supported by the service. The prefix and the action name are case insensitive. For example, iam:ListAccessKeys is the same as IAM:listaccesskeys."
> [§AWS IAM Action — examples]
> "Amazon SQS action: sqs:SendMessage"
> "Amazon EC2 action: ec2:StartInstances"
> "IAM action: iam:ChangePassword"
> "Amazon S3 action: s3:GetObject"
> [§AWS IAM Action — wildcard] "You can use multi-character match wildcards (*) and single-character match wildcards (?) to give access to all the actions the specific AWS product offers. For example, the following Action element applies to all S3 actions: s3:*"
### Google Cloud IAM Permissions
> [§Google IAM — permission format] "Permissions have the following format: SERVICE.RESOURCE.VERB"
> [§Google IAM — examples] "the compute.instances.list permission allows a user to list the Compute Engine instances they own, and compute.instances.stop allows a user to stop a VM."
> [§Google IAM — API correspondence] "to call the Pub/Sub API's projects.topics.publish method, you need the pubsub.topics.publish permission."
> [§Google IAM — role-permission relationship] "When you grant a role to a principal, the principal gets all of the permissions in the role."
> [§Google IAM — REST correspondence] "Permissions usually, but not always, correspond 1:1 with REST methods. That is, each Google Cloud service has an associated permission for each REST method that it has. To call a method, the caller needs the associated permission."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| IAM-NAMING-C1 | AWS IAM permission format 은 `service:Action`**colon(`:`) separator**, service namespace 가 prefix, action 이 suffix. case-insensitive | [§AWS IAM Action] "You specify a value using a service namespace as an action prefix...For example, iam:ListAccessKeys" | `official-vendor-doc` | cloud-level multi-service permission 관리에서 service 간 namespace 분리가 필요한 경우 | application-level internal permission 에 동일 format 을 적용해야 한다는 것은 아님 — AWS IAM 은 multi-service cloud scope |
| IAM-NAMING-C2 | Google Cloud IAM permission format 은 `service.resource.verb`**dot(`.`) separator**, 3-segment (service, resource, verb). REST method 와 1:1 대응 | [§Google IAM] "Permissions have the following format: SERVICE.RESOURCE.VERB" + "compute.instances.list" + "Permissions usually, but not always, correspond 1:1 with REST methods." | `official-vendor-doc` | REST API 와 permission 을 1:1 매핑하는 설계에서 참조 | application-internal permission 에 `.` separator 를 써야 한다는 것은 아님 |
| IAM-NAMING-C3 | AWS IAM 에서는 **role** 이 permission 의 container — role 에 IAM policy 를 attach 하면 policy 의 `Action` 들이 role 을 통해 부여됨 | [§Google IAM] "When you grant a role to a principal, the principal gets all of the permissions in the role." (Google — AWS 도 동일 패턴) | `official-vendor-doc` | role → permission bundle 패턴의 industry-wide grounding | application-level RBAC 에서 반드시 이 방식을 따라야 한다는 것은 아님 |
| IAM-NAMING-C4 | AWS IAM 에서 wildcard 는 `s3:*` (service-level 전체) 또는 `iam:*AccessKey*` (action prefix/suffix 패턴) — **segment-level wildcard** 지원 | [§AWS IAM Action] "s3:*" + "iam:*AccessKey*" examples | `official-vendor-doc` | permission wildcard 정책 설계 시 참조 | application-internal permission 에서 wildcard 가 필요하다는 것은 아님 |
| IAM-NAMING-C5 | Google IAM 은 **3-segment** (`service.resource.verb`) 로 multi-level resource 계층을 표현. REST method 대응으로 `pubsub.topics.publish` | [§Google IAM] "to call the Pub/Sub API's projects.topics.publish method, you need the pubsub.topics.publish permission." | `official-vendor-doc` | multi-service 또는 복잡한 resource hierarchy 환경에서 3-segment 가 필요한 경우 | 단일 application 내부 permission 에 3-segment 가 필요하다는 것은 아님 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `IAM-NAMING-C1`: AWS IAM 은 `service:Action` colon-separated format
- `IAM-NAMING-C2`: Google IAM 은 `service.resource.verb` dot-separated format
- `IAM-NAMING-C3`: 두 시스템 모두 role → permission bundle 패턴 사용
- 이 자료가 증명하지 않는 것:
- application-internal permission 에 반드시 AWS/Google 형식을 따라야 한다는 것
- `worklog:close` 형식이 최선이라는 것 — 이 자료는 industry format 의 reference 를 제공할 뿐
- OAuth2 scope 와 internal permission 의 차이 — Curity scope best practices 등 별도 자료 위임
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- `resource:action` (2-segment, colon) 형식이 AWS IAM `service:Action` 에서 `service``resource` 로 대체한 형태로 이 시스템의 단일 서비스 스코프에 적합한지
- Google IAM 의 3-segment 가 이 프로젝트 (단일 서비스, sample-portfolio domain) 에 과도한지
## 메모 / Notes
- **핵심 차이**: AWS (`service:Action`) 는 2-segment colon, Google (`service.resource.verb`) 는 3-segment dot
- **application-level 적용**: 단일 서비스 내부 permission 에는 `resource:action` (2-segment, colon) 이 더 단순하고 AWS IAM 패턴과 구조적으로 유사
- **separator 선택**: colon (`:`) 은 AWS IAM 관행, dot (`.`) 은 Google IAM + OAuth2 scope 일부 관행. URL-safe 고려 시 colon 이 일부 context 에서 encoding 필요할 수 있음 — 내부 permission 에서는 일반적으로 문제없음
- **Curity OAuth2 scope best practices** (별도 참조): scope 는 entry-point 수준, fine-grained authorization 은 claim/permission 으로 분리 권장 — 이 자료와 함께 검토 필요
## Related / 관련
- [[raw/official-docs/spring-security-authorization-architecture]] — enforcement mechanism
- [[raw/official-docs/owasp-authz-permission-model-abac-rbac]] — permission model 정당화
- [[raw/branch-notes/feature-authentication-authorization-contract]]
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/aws-security-group-referencing-official.md
@@ -0,0 +1,80 @@
---
title: official-doc / AWS VPC — Security group rules (Security group referencing, rule aggregation)
source_type: official-doc
url: https://docs.aws.amazon.com/vpc/latest/userguide/security-group-rules.html
archive_url:
related_branches: [feature-keycloak-header-spoofing-defense]
related_projects: [keycloak-patterns]
tags: [official-doc, keycloak-patterns, security, aws, networking]
created: 2026-07-16
---
# official-doc / AWS VPC — Security group rules (Security group referencing, rule aggregation)
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수, 최소 1개+)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-header-spoofing-defense]] | D4 — EC2 backend 의 Security Group inbound source 를 *다른 Security Group* (SG-reference) 로 제한하면 그 SG 에 연결된 인스턴스로만 트래픽을 허용해 VPC lateral movement 를 막을 수 있고, 이는 CIDR-source 로는 얻을 수 없는 성질이다; SG-reference 는 same VPC / peering / transit gateway 범위 안에서만 동작한다. |
## 출처 / Source
- 원본 URL: https://docs.aws.amazon.com/vpc/latest/userguide/security-group-rules.html
- 아카이브 URL: (미제공)
- 저자 / 조직: Amazon Web Services (AWS VPC User Guide)
- 발행일: (페이지에 명시된 발행일 없음 — AWS 공식 문서, 지속 업데이트형)
- 마지막 확인일: 2026-07-16
## 왜 저장했는지 / Why archived
`feature-keycloak-header-spoofing-defense` 의 D4(EC2/VM 환경 방어책: backend SG inbound 를 ALB/ingress SG 만 허용)가 현재 `UNSUPPORTED_DECISION`으로 표시되어 있음 — AWS EC2 Security Group 공식 인용이 raw 에 없었기 때문. 본 문서는 SG-reference 가 실제로 "그 SG 에 연결된 인스턴스만" 대상으로 하고, same-VPC/peering/TGW 범위 조건과 multi-SG aggregation(union) 시맨틱을 공식으로 확인해 D4 를 뒷받침하기 위해 저장.
## 핵심 인용 / Key quotes (verbatim, 5문장)
> [§Security group referencing] "When you specify a security group as the source or destination for a rule, the rule affects all instances that are associated with the security groups. The instances can communicate in the specified direction, using the private IP addresses of the instances, over the specified protocol and port."
> [§Security group referencing] "The security groups are associated with the same VPC."
> [§Security group referencing] "There is a peering connection between the VPCs that the security groups are associated with."
> [§Security group referencing] "There is a transit gateway between the VPCs that the security groups are associated with."
> [§Security group rule basics] "When you associate multiple security groups with a resource, the rules from each security group are aggregated to form a single set of rules that are used to determine whether to allow access."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AWS-SG-REF-C1 | rule 의 source/destination 으로 security group 을 지정하면, 그 rule 은 해당 security group 에 연결된 **모든 인스턴스**에 적용되고, 인스턴스 간 통신은 각자의 **private IP 주소**를 사용해 이뤄진다 | [§Security group referencing] "When you specify a security group as the source or destination for a rule, the rule affects all instances that are associated with the security groups. The instances can communicate in the specified direction, using the private IP addresses of the instances, over the specified protocol and port." | `official-vendor-doc` | EC2 인스턴스가 inbound/outbound rule 의 source/destination 으로 다른 security group 을 참조하는 모든 시나리오 (범위 조건은 `AWS-SG-REF-C2` 참조) | SG-source 가 CIDR-source 보다 VPC lateral movement 를 "더 잘 막는다"는 비교 결론 자체를 직접 진술하지 않음 — 이는 "SG 에 연결된 인스턴스만 대상" 이라는 이 claim 과 CIDR 이 IP 대역 전체를 대상으로 한다는 별도 상식의 결합 추론 |
| AWS-SG-REF-C2 | 다른 security group 의 **inbound** rule 에서 특정 security group 을 참조하려면 (a) 두 SG 가 같은 VPC 에 연결되어 있거나, (b) 두 VPC 간 peering connection 이 있거나, (c) 두 VPC 간 transit gateway 가 있어야 한다 | [§Security group referencing] "The security groups are associated with the same VPC." / "There is a peering connection between the VPCs that the security groups are associated with." / "There is a transit gateway between the VPCs that the security groups are associated with." | `official-vendor-doc` | inbound rule 에서의 SG-reference 범위 판단 (same-VPC / VPC peering / transit gateway) | outbound rule 에서도 동일하게 transit gateway 를 통한 SG-reference 가 가능하다는 것 — 원문은 outbound 조건을 "same VPC 또는 peering" 2가지로만 별도 나열하고 transit gateway 를 포함하지 않음 |
| AWS-SG-REF-C3 | 하나의 리소스(ENI)에 여러 security group 이 연결되면, 각 SG 의 rule 들은 **하나의 rule 집합으로 aggregate** 되어 access 허용 여부를 결정하는 데 사용된다 | [§Security group rule basics] "When you associate multiple security groups with a resource, the rules from each security group are aggregated to form a single set of rules that are used to determine whether to allow access." | `official-vendor-doc` | 하나의 EC2 인스턴스(ENI)에 여러 SG 가 연결된 모든 상황에서의 rule 평가 방식 | "aggregate 되므로 leftover 한 broad CIDR allow rule 이 SG-narrow rule 과 무관하게 여전히 트래픽을 허용한다"는 구체적 문장은 원문에 없음 — 이는 aggregation=union 시맨틱에서 도출되는 논리적 추론이며, "allow rule 만 존재하고 deny rule 은 없다"는 별도 문장(본 raw 노트에 verbatim 미포함, 원문 §Security group rule basics 첫 항목)과 결합해야 완성되는 추론. D4 에 이 추론을 그대로 쓸 경우 `needs-confirmation` 로 표시 권장 |
### Strength 근거
- 세 claim 모두 `official-vendor-doc` — AWS VPC User Guide 공식 페이지 원문에서 직접 인용.
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `AWS-SG-REF-C1`: SG-reference rule 은 그 SG 에 연결된 인스턴스만을 대상으로 하며 private IP 로 통신한다.
- `AWS-SG-REF-C2`: SG-reference 는 same-VPC / VPC peering / (inbound 한정) transit gateway 범위 조건을 충족해야 동작한다.
- `AWS-SG-REF-C3`: 여러 SG 가 하나의 리소스에 연결되면 rule 이 aggregate(하나의 집합으로 병합)되어 access 여부를 결정한다.
- 이 자료가 증명하지 않는 것:
- "backend SG 를 ALB/ingress SG-reference 로만 구성하면 VPC lateral movement 가 완전히 차단된다"는 결론 — 원문은 aggregation 이 rule 을 병합한다는 것만 말하며, 같은 인스턴스에 붙은 **다른** SG 에 broad CIDR allow rule 이 남아 있으면 그 rule 도 aggregate 되어 함께 적용됨을 명시하지 않는다. 이는 aggregation=union 시맨틱의 논리적 귀결이지 원문의 명시적 진술은 아니다.
- middlebox appliance 경유 라우팅 시나리오에서 SG-reference 가 동작하지 않는다는 별도 Limitation 문구가 원문에 있으나(2개의 서로 다른 subnet 인스턴스 간 미들박스 경유 시 SG-reference source 로는 트래픽이 흐르지 않음), 본 raw 노트는 이를 claim 으로 추출하지 않았다 — D4 가 단일 EC2/backend↔ALB 직접 경로를 가정하므로 범위 밖.
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- `feature-keycloak-header-spoofing-defense` D4 의 실제 EC2/VPC 구성에서 backend SG 의 inbound rule 목록에 broad CIDR allow rule 이 남아있지 않은지(leftover rule 존재 여부) 실측 확인 필요 — 해당 branch `Claims To Verify` 표의 "EC2 Security Group inbound 가 ALB SG 만 허용해도 VPC 내부 다른 인스턴스의 lateral movement 차단 가능한지" 항목과 직결.
- 학습 프로젝트가 실제로 단일 VPC 내 운영인지, peering/transit gateway 를 쓰는 멀티-VPC 구조인지에 따라 `AWS-SG-REF-C2` 의 어느 조건이 적용되는지 확인 필요.
## 메모 / Notes
- `AWS-SG-REF-C3`(aggregation) 는 원문이 "union" 이라는 단어를 쓰지 않는다 — "aggregated to form a single set of rules" 표현을 union 으로 해석한 것은 본 저장자의 해석. deny rule 이 없다는 별도 문장(§rule basics 첫 항목: "You can specify allow rules, but not deny rules.")과 결합해야 "aggregate = union of allows, 가장 넓은 rule 이 이긴다"는 결론이 성립. 이 결합 추론은 branch D4 갱신 시 별도로 명시할 것.
- 원문 Limitation 문단: middlebox appliance 라우팅 시나리오에서는 SG-reference 를 source 로 써도 트래픽이 허용되지 않고 private IP/CIDR 을 직접 참조해야 한다 — D4 의 단순 ALB→backend 직결 구조에는 해당하지 않지만, 향후 구성이 바뀌면 재검토 필요.
## Related / 관련
- 같은 branch 의 다른 vendor 인용: [[raw/official-docs/traefik-forwardauth-middleware-official]], [[raw/official-docs/oauth2-proxy-nginx-integration-official]], [[raw/official-docs/keycloak-reverseproxy-official]]
- 아직 raw 에 없는 후속 검토 후보: K8s `NetworkPolicy` 공식 문서 (`k8s-network-policy-official` — branch D3 UNSUPPORTED 해소용)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/baggage-otel-baggage-api-spec.md
@@ -0,0 +1,80 @@
---
title: "OpenTelemetry Baggage API Specification (Stable)"
source_type: official-doc
url: https://opentelemetry.io/docs/specs/otel/baggage/api/
archive_url:
related_branches: [feature-distributed-tracing-contract]
related_projects: [ca-skeleton]
tags: [official-doc, ca-skeleton, observability, opentelemetry]
created: 2026-06-14
---
# OpenTelemetry Baggage API Specification (Stable)
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-distributed-tracing-contract]] | D2: baggage 에 PII/token 금지의 SDK 수준 escape hatch (untrusted process 로의 전송 방지 MUST 요건); D8: allowlist 는 spec 정의 없음 — propagator/application 위임 확인 (스펙에 allowlist 정의 부재, restriction 은 Propagator 가 독자 부과) |
## 출처 / Source
- 원본 URL: https://opentelemetry.io/docs/specs/otel/baggage/api/
- 아카이브 URL: (미등록)
- 저자 / 조직: OpenTelemetry Authors (CNCF)
- 발행일: (Stable 사양 — 정확한 날짜 미확인)
- 마지막 확인일: 2026-06-14
## 왜 저장했는지 / Why archived
`feature-distributed-tracing-contract` 브랜치의 D2(baggage PII 금지)와 D8(allowlist 는 정책이지 스펙이 아님) 결정의 직접 근거가 되는 OTel 공식 사양. W3C trace context 스펙은 `tracestate` PII 금지를 다루지만 baggage 자체의 보안 요건은 이 문서에서만 확인 가능. D8의 핵심 — spec 은 allowlist 를 정의하지 않고 Propagator 에게 restriction 위임 — 도 이 문서에서 직접 확인.
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
> [§Overview / Definition] "Baggage is a set of application-defined properties contextually associated with a distributed request or workflow execution"
> [§Data Model] "In OpenTelemetry Baggage is represented as a set of name/value pairs describing user-defined properties. Each name in Baggage MUST be associated with exactly one value."
> [§Security Considerations / Clear Baggage] "To avoid sending any name/value pairs to an untrusted process, the Baggage API MUST provide a way to remove all baggage entries from a context."
> [§Baggage Names / Propagator Restrictions] "the specific Propagators that are used to transmit baggage entries across component boundaries may impose their own restrictions on baggage names."
> [§Baggage Container / Immutability] "The Baggage container MUST be immutable, so that the containing Context also remains immutable."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| OTEL-BAG-C1 | OTel Baggage 는 분산 요청/워크플로우 실행에 연관된 application-defined properties 집합이다 | [§Overview] "Baggage is a set of application-defined properties contextually associated with a distributed request or workflow execution" | `official-vendor-doc` | OTel SDK 를 사용하는 모든 언어 구현 | Baggage 가 특정 HTTP header 형식으로 전송된다는 것은 증명하지 않음 (Propagator 에 위임) |
| OTEL-BAG-C2 | Baggage 의 데이터 모델은 name/value 쌍의 집합이며 각 name 은 정확히 하나의 value 와 연관되어야 한다 | [§Data Model] "In OpenTelemetry Baggage is represented as a set of name/value pairs describing user-defined properties. Each name in Baggage MUST be associated with exactly one value." | `official-vendor-doc` | OTel Baggage API 구현 전체 | Baggage name/value 의 허용 문자 범위나 크기 제한을 직접 확정하지 않음 (Propagator 가 추가 제한 가능) |
| OTEL-BAG-C3 | Baggage API 는 untrusted process 로의 전송을 막기 위해 context 에서 모든 baggage entry 를 제거하는 방법을 MUST 로 제공해야 한다 | [§Security] "To avoid sending any name/value pairs to an untrusted process, the Baggage API MUST provide a way to remove all baggage entries from a context." | `official-vendor-doc` | 신뢰 경계(trust boundary)를 넘는 모든 OTel Baggage 사용 사례 | 어떤 정보가 "untrusted" 인지(예: PII, token)를 spec 이 직접 정의하지 않음 — application/governance 정책이 결정 |
| OTEL-BAG-C4 | spec 은 baggage name 에 대한 allowlist 를 정의하지 않는다 — 각 Propagator 가 독자적인 restriction 을 부과할 수 있다 | [§Baggage Names] "the specific Propagators that are used to transmit baggage entries across component boundaries may impose their own restrictions on baggage names." | `official-vendor-doc` | baggage allowlist 또는 key 제한 정책을 설계할 때 | Propagator 가 실제로 어떤 restriction 을 부과하는지, 또는 반드시 부과해야 하는지를 증명하지 않음 |
| OTEL-BAG-C5 | Baggage container 는 immutable 이어야 하며 이는 포함하는 Context 도 immutable 하게 유지함을 의미한다 | [§Operations] "The Baggage container MUST be immutable, so that the containing Context also remains immutable." | `official-vendor-doc` | OTel Context propagation 전반 | Immutability 의 구체적인 구현 방식(copy-on-write vs rebuild 등)을 spec 이 지시하지 않음 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `OTEL-BAG-C3`: Baggage API 에는 모든 entry 를 일괄 제거하는 기능이 SDK 수준 MUST 요건으로 존재 → D2 의 "SDK-level escape hatch" 근거
- `OTEL-BAG-C4`: OTel spec 은 baggage name allowlist 를 정의하지 않음. restriction 은 Propagator 또는 application 이 독자 부과 → D8 의 "allowlist 는 정책이지 스펙이 아님" 근거
- `OTEL-BAG-C2`: name 과 value 의 데이터 모델 기본 계약 (1:1 매핑, RFC 2119 MUST)
- 이 자료가 증명하지 않는 것:
- 어떤 구체적인 key (예: `tenant_id`, `request_id`) 가 baggage 에 적합한지는 spec 범위 밖 — application governance 결정
- PII 나 token 이 구체적으로 어떤 형태인지를 spec 이 정의하지 않음 (`OTEL-BAG-C3` Does not prove)
- W3C Baggage HTTP header 스펙과의 relationship — 별도 W3C 문서 필요
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ca-tmpl 이 실제로 OTel SDK 의 "clear all baggage" API 를 trust boundary 에서 호출하는지 구현 검증 필요
- W3C Baggage Propagator 가 `tenant_id` / `request_id` key 에 추가 restriction 을 부과하는지 확인 필요
## 메모 / Notes
- OTEL-BAG-C3 의 "untrusted process" 기준은 application 이 정의해야 함. ca-tmpl 의 D2 결정(PII/token 금지)은 이 MUST 요건을 구체화한 내부 정책.
- OTEL-BAG-C4 는 D8 의 핵심 증거: spec 이 allowlist 를 정의하지 않으므로 `tenant_id`/`request_id` 만 허용하는 ca-tmpl 정책은 external standard 가 아닌 governance policy 임을 명확히 한다.
- 추가로 봐야 할 동일 출처 페이지: https://opentelemetry.io/docs/specs/otel/baggage/data-model/ (data model 상세)
## Related / 관련
- 같은 주제 다른 official-doc: [[raw/official-docs/tracing-w3c-trace-context-spec]] (tracestate PII MUST NOT — D2 의 W3C 측 근거)
- 이 자료를 인용한 wiki 요약: (미작성 — `/ingest``wiki/concepts/` 생성 예정)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/baggage-w3c-baggage-spec.md
@@ -0,0 +1,86 @@
---
title: "W3C Baggage Specification (Candidate Recommendation Snapshot, 2024-05-30)"
source_type: official-doc
url: https://www.w3.org/TR/baggage/
archive_url:
related_branches: [feature-distributed-tracing-contract]
related_projects: [ca-skeleton]
tags: [official-doc, ca-skeleton, observability, opentelemetry, baggage-propagation]
created: 2026-06-14
---
# W3C Baggage Specification (Candidate Recommendation Snapshot, 2024-05-30)
> Layer: `raw/` — 외부 자료(W3C 표준 사양)의 원문 발췌·출처 기록.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-distributed-tracing-contract]] | D2 — baggage에 PII/token/user raw identifier/body-derived value를 넣어서는 안 된다 (§4.1 Security Considerations 직접 근거). D8 — baggage allowlist = `tenant_id` + `request_id` 만 허용 (spec은 wire format을 정의하나 allowlist 메커니즘은 규정하지 않음 — 이 결정은 application 정책). |
## 출처 / Source
- 원본 URL: https://www.w3.org/TR/baggage/
- 아카이브 URL: (미등록 — 필요 시 archive.org 스냅샷 추가)
- 저자 / 조직: W3C Distributed Tracing Working Group
- 발행일: 2024-05-30 (Candidate Recommendation Snapshot)
- 마지막 확인일: 2026-06-14
## 왜 저장했는지 / Why archived
`feature-distributed-tracing-contract` branch 의 D2 (baggage PII 금지)는 이전에 W3C Trace Context `tracestate` spec 을 근거로 인용했으나, 그 문서는 `tracestate` 에 대한 것이고 `baggage` 헤더에 대한 직접 근거가 아니었다. 본 자료는 W3C Baggage spec 을 직접 fetch 하여 §4.1 Information Exposure 의 verbatim 텍스트로 D2 를 정확히 지지하고, §3.3 의 propagation 제약(64 list-members / 8192 bytes)으로 D8 의 "allowlist 없음 — application 정책" 해석을 뒷받침한다.
## 핵심 인용 / Key quotes (verbatim, Self-Grep 통과)
> [§4.1 Information Exposure] "As mentioned in the privacy section, baggage may carry sensitive information. Application owners should either ensure that no proprietary or confidential information is stored in baggage, or they should ensure that baggage isn't present in requests that cross trust-boundaries."
> [§3.3 Propagation format — Condition 1] "The resulting baggage-string contains 64 list-members or less."
> [§3.3 Propagation format — Condition 2] "The resulting baggage-string is of size 8192 bytes or less."
> [§3.3 Forwarding requirement] "A system receiving a baggage request header SHOULD send it to outgoing requests."
> [§4 Security Considerations — general] "Systems relying on the baggage headers should also follow all best practices for parsing potentially malicious data, including checking for header length and content of header values."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| W3C-BAG-C1 | baggage 는 민감한 정보를 담을 수 있으므로, application owner 는 기밀 정보를 넣지 않거나 trust-boundary 를 넘는 요청에서 baggage 를 제거해야 한다 | [§4.1] "baggage may carry sensitive information. Application owners should either ensure that no proprietary or confidential information is stored in baggage, or they should ensure that baggage isn't present in requests that cross trust-boundaries." | `official-standard` | W3C Baggage spec 을 따르는 모든 HTTP 시스템 | PII/token/identifier 각 유형의 금지를 개별로 열거하지 않음. 구체적인 금지 항목 목록(예: "tenant_id 는 OK, email 은 NG")은 application 정책 결정 |
| W3C-BAG-C2 | baggage-string 은 최대 64개 list-member 를 가질 수 있다. 이 한계 초과 시 플랫폼은 list-member 를 propagate 할 의무 없음 | [§3.3] "The resulting baggage-string contains 64 list-members or less." | `official-standard` | baggage 헤더를 propagate 하는 모든 플랫폼/미들웨어 | 64개 이하의 list-member 를 사용해야 한다는 allowlist 정책을 강제하지 않음 — 단 propagation 보장의 상한만 정의 |
| W3C-BAG-C3 | baggage-string 총 크기는 8192 bytes 이하여야 platform 이 propagation 을 보장한다 | [§3.3] "The resulting baggage-string is of size 8192 bytes or less." | `official-standard` | baggage 헤더를 propagate 하는 모든 플랫폼/미들웨어 | 특정 key 의 value 크기 제한은 규정하지 않음 |
| W3C-BAG-C4 | baggage 수신 시스템은 outgoing request 에 baggage 를 전달해야 한다 (SHOULD) | [§3.3] "A system receiving a baggage request header SHOULD send it to outgoing requests." | `official-standard` | baggage-aware HTTP 중간 시스템 전체 | MUST 가 아닌 SHOULD — 전달 실패가 spec 위반은 아님. 전달 여부를 강제하는 별도 application-level 정책 필요 |
| W3C-BAG-C5 | baggage 를 사용하는 시스템은 잠재적 악성 데이터 파싱에 대한 모범 사례를 따라야 한다 (헤더 길이·값 내용 확인 포함) | [§4] "Systems relying on the baggage headers should also follow all best practices for parsing potentially malicious data, including checking for header length and content of header values." | `official-standard` | baggage 헤더를 파싱하는 모든 시스템 | 구체적인 파싱 구현 방법(validation library, 길이 상한값 등)은 규정하지 않음 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `W3C-BAG-C1`: D2 (baggage PII 금지)의 직접 표준 근거. `tracestate` spec 이 아닌 `baggage` spec 자체에서 기밀 정보 금지/trust-boundary 제거 의무를 규정함.
- `W3C-BAG-C2`, `W3C-BAG-C3`: spec 이 wire-level propagation 제약(64 members / 8192 bytes)만 정의하고, 어떤 key 를 넣을지는 application 이 결정한다는 근거 → D8 이 spec feature 가 아닌 application 정책임을 지지.
- `W3C-BAG-C4`: baggage 전달이 SHOULD 수준 — 인프라 default 로 기대할 수 없으므로 application 계층에서 명시적 전달 구현 필요.
- `W3C-BAG-C5`: baggage 파싱 시 보안 best practice 적용 의무.
- 이 자료가 증명하지 않는 것:
- `tenant_id` / `request_id` 라는 특정 key 명칭이 안전하다는 것 — spec 은 key 허용/금지 목록 없음.
- baggage allowlist 를 강제하는 메커니즘 — D8 의 allowlist 정책은 spec 에 없는 application-level 결정.
- PII 의 법적 정의 (GDPR, CCPA 등) — spec 은 "proprietary or confidential information" 만 언급.
- 특정 Java/Spring 구현에서 baggage API 사용 방법.
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- Micrometer Tracing / OTel Java SDK 에서 baggage key 에 대한 allowlist filter 구현 방법 (별도 raw source 필요).
- trust-boundary 판정 기준 (ca-skeleton 에서 외부 시스템 호출 = trust-boundary 로 간주하는지 명시 필요).
## 메모 / Notes
- 본 자료는 `feature-distributed-tracing-contract` D2 의 원래 인용 소스(`tracing-w3c-trace-context-spec.md#W3C-TC-C5` — tracestate PII 금지)를 **대체**하는 올바른 자료다. Decision Evidence Map 에서 D2 의 Supporting Claims 를 `W3C-BAG-C1` 로 갱신해야 한다.
- D8 의 UNSUPPORTED_DECISION 라벨은 유지 타당 — spec 은 allowlist 정책을 정의하지 않음. `W3C-BAG-C2`/`C3` 는 "spec 에 allowlist 없음" 을 뒷받침할 뿐, D8 의 구체적 key 선택(`tenant_id`, `request_id`)은 여전히 application 운영 정책.
- 문서 상태: Candidate Recommendation Snapshot (2024-05-30). W3C Recommendation 이 아님 — 최종 표준은 아니나 OTel 생태계에서 de-facto 표준으로 채택.
- 추가로 봐야 할 동일 출처 페이지: https://www.w3.org/TR/baggage/#privacy (§5 Privacy Considerations — §4.1 이 언급하는 "privacy section" 의 원문)
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/tracing-w3c-trace-context-spec]] — W3C Trace Context spec (traceparent / tracestate). baggage 와는 별도 spec.
- [[raw/official-docs/tracing-otel-sampling-tail-vs-head-spec]] — OTel sampling spec
- 이 자료를 인용한 branch-note: [[raw/branch-notes/feature-distributed-tracing-contract]]
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cache-aside-vs-write-through-aws.md
@@ -0,0 +1,112 @@
---
title: Caching patterns — cache-aside vs write-through vs write-behind (AWS + DAX + Redis)
source_type: official-doc
status: raw
confidence: medium
url: https://aws.amazon.com/caching/best-practices/
archive_url:
tags: [ca-cache-consistency, cache-aside, write-through, write-behind, redis, official-doc]
related_branches: [feature-cache-consistency-contract]
related_projects: [ca-skeleton-operational-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Caching patterns — cache-aside vs write-through vs write-behind
> Layer: `raw/official-docs/` — AWS Caching Best Practices + DAX Developer Guide + Redis 문서 발췌. ca-tmpl 의 cache-aside default 결정의 외부 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-cache-consistency-contract]] | "cache-aside + after-commit invalidation" 을 default 로 채택한 결정의 외부 근거 — write-through / write-behind 가 가지는 trade-off 와의 비교 |
특정 branch 없이 foundational 조사로 수집한 경우:
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl operational contract 의 cache consistency 초기 조사
## 컨텍스트 / 왜 저장했는지
ca-tmpl 이 cache-aside 를 default 로 채택한 **이유의 외부 근거**. write-through / write-behind / read-through 와의 trade-off 를 공식 사이트 인용으로 비교.
## 출처 / Source
- 원본 URL: https://aws.amazon.com/caching/best-practices/ (AWS Caching Best Practices)
- 보조 URL: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAX.consistency.html (DAX = write-through caching service — verbatim 확보)
- 보조 URL: https://redis.io/learn/howtos/solutions/microservices/caching (Redis Learn — cache-aside definition verbatim 확보; write-behind / read-through 는 별도 페이지)
- 아카이브 URL: (미수집)
- 저자 / 조직: AWS Database team; Redis Inc.
- 발행일: rolling
- 마지막 확인일: 2026-05-27 (WebFetch 검증: AWS Caching Best Practices 는 "Lazy caching" + "Write-through" 두 패턴만 본문에 있음. write-behind / read-through 는 본 페이지에 없음 → DAX + Redis 문서로 보강. AWS Database Blog 의 별도 캐싱 비교 글 후속 확인 필요)
## 핵심 인용 / Key quotes (verbatim)
> [AWS Caching Best Practices §Lazy caching] "Laziness should serve as the foundation of any good caching strategy. The basic idea is to populate the cache only when an object is actually requested by the application."
> [AWS Caching Best Practices §Lazy caching — cache miss] "If not (a cache miss), then the database is queried for the object. The cache is populated, and the object is returned."
> [AWS Caching Best Practices §Write-through] "In a write-through cache, the cache is updated in real time when the database is updated."
> [AWS Caching Best Practices §Write-through — latency tradeoff] "It shifts any application delay to the user updating data, which maps better to user expectations."
> [DAX Developer Guide §DAX and DynamoDB consistency models — opening] "Amazon DynamoDB Accelerator (DAX) is a write-through caching service that is designed to simplify the process of adding a cache to DynamoDB tables."
> [DAX Developer Guide §How DAX processes writes] "As a write-through cache, DAX passes your writes through to DynamoDB synchronously, then automatically and asynchronously replicates resulting updates to your item cache across all nodes in the cluster. You don't need to manage cache invalidation logic because DAX handles it for you."
> [Redis Learn §Cache-Aside] "The cache-aside pattern (also called lazy loading) is a caching strategy where the application is responsible for reading and writing to both the cache and the database."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CACHE-PAT-C1 | Lazy caching (= cache-aside) 의 핵심: cache 는 application 이 실제 데이터를 요청할 때만 populate. cache miss 시 application 이 DB 조회 → cache populate → 반환 | [AWS §Lazy caching] "Laziness should serve as the foundation of any good caching strategy. The basic idea is to populate the cache only when an object is actually requested by the application." + "If not (a cache miss), then the database is queried for the object. The cache is populated, and the object is returned." | `official-vendor-doc` | application-managed cache (Caffeine, Redis client side) | cache miss 시 DB 조회를 application 이 직접 해야 한다는 강제는 본 인용 직접 명시. "application is responsible" 은 AWS 본문에는 명시 없음 (Redis 문서 별도 인용) |
| CACHE-PAT-C2 | cache-aside 의 책임 분리: application 이 cache 와 DB 양쪽 R/W 를 책임 | [Redis Learn §Cache-Aside] "The cache-aside pattern (also called lazy loading) is a caching strategy where the application is responsible for reading and writing to both the cache and the database." | `official-vendor-doc` | Redis cache-aside 구현 일반 | "cache 가 DB 를 모른다" 는 강한 분리는 본 인용 명시. invalidation 정책은 별도 |
| CACHE-PAT-C3 | Write-through cache 는 DB 가 갱신될 때 cache 도 real-time 으로 갱신. write 시점에 latency 가 user 측으로 이동 (application delay → user delay) | [AWS §Write-through] "In a write-through cache, the cache is updated in real time when the database is updated." + "It shifts any application delay to the user updating data, which maps better to user expectations." | `official-vendor-doc` | write-through 패턴 일반 | "모든 write 가 cache 와 DB 에 동시 commit 된다" 는 atomic 보장은 본 인용 범위 밖 — synchronization 메커니즘은 구현 의존 |
| CACHE-PAT-C4 | DAX 는 write-through caching service 로 구현되며, application 측에서 cache invalidation logic 을 별도 관리할 필요 없음. write 는 DynamoDB 에 synchronous 로 전달 후 async 로 cluster node 에 replicate | [DAX §DAX and DynamoDB consistency models] "Amazon DynamoDB Accelerator (DAX) is a write-through caching service..." + [§How DAX processes writes] "...DAX passes your writes through to DynamoDB synchronously, then automatically and asynchronously replicates resulting updates to your item cache across all nodes in the cluster. You don't need to manage cache invalidation logic because DAX handles it for you." | `official-vendor-doc` | DAX 사용 환경 (DynamoDB 한정) | 모든 write-through 구현이 invalidation 을 자동 처리한다는 일반화 금지 — DAX 의 특정 구현 |
| CACHE-PAT-C5 | Write-behind (= write-back) 패턴: application 이 cache 에 쓰고 cache 가 asynchronous 로 DB 에 기록. 최저 write latency 를 제공하지만 cache 장애 시 data loss 위험 | (원본 frontmatter 발췌 — AWS Caching Best Practices 본 페이지에 명시 없음. AWS Database Blog 또는 Redis docs 의 별도 페이지에서 유래로 추정) | `needs-confirmation` | write-behind 패턴 일반 비교 | 본 세션에서 AWS 또는 Redis 공식 페이지의 verbatim source 미확보 — 후속 라운드에 별도 출처 ("AWS Database Blog — caching strategies" 또는 Redis docs/learn 의 write-behind 페이지) 로 verbatim 재확인 필요 |
| CACHE-PAT-C6 | Read-through 패턴: cache-aside 와 유사하나 cache 가 자체적으로 DB 에서 load (configured loader 필요) | (원본 frontmatter 발췌 — AWS 본 페이지에 명시 없음. Caffeine `LoadingCache` / Redisson 등의 SDK 문서로 추정) | `needs-confirmation` | LoadingCache 류 (Caffeine, Redisson `LocalCachedMap` 등) | 본 세션 verbatim source 미확보 — Caffeine 또는 Redis docs 의 read-through 페이지에서 재확인 필요 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CACHE-PAT-C1` ~ `C2`: cache-aside (lazy loading) 의 정의 + application 책임 (AWS + Redis)
- `CACHE-PAT-C3`: write-through 의 정의 + latency tradeoff (AWS verbatim)
- `CACHE-PAT-C4`: DAX 가 write-through 구현이며 invalidation 을 자동 처리한다는 사실 (DAX 한정)
- **이 자료가 증명하지 않는 것**:
- write-behind 의 정확한 정의와 data loss 메커니즘 (`CACHE-PAT-C5``needs-confirmation`)
- read-through 의 정확한 정의와 loader 메커니즘 (`CACHE-PAT-C6``needs-confirmation`)
- "write-through 는 결제 도메인에 부적합" 같은 prescriptive 주장 (출처 측은 trade-off 만 제시)
- cache-aside + after-commit invalidation 의 정확한 구현 패턴 (application 책임 영역)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 "write-through forbidden by default" 결정은 우리 consistency contract 의 결과이지 AWS/Redis 가 권고한 것 아님 — 내부 결정 근거 문서화 필요
- `CACHE-PAT-C5`, `C6` 의 verbatim 출처 후속 확보 (AWS Database Blog 의 "Caching strategies and best practices" 별도 글 또는 Redis docs)
- Caffeine `LoadingCache` / Redisson `LocalCachedMap` 의 read-through 동작이 ca-tmpl "adapter 가 loader 를 소유" 원칙과 정합한지 별도 검증
## 메모 / Notes (내 프로젝트 해석 — 검증 전 추론)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- **ca-tmpl 결정과의 매핑 (해석)**:
- **cache-aside default** ← "application owns invalidation" 원칙 (`CACHE-PAT-C2` 의 application 책임을 invalidation 까지 확장). cache 가 DB 를 모르고, adapter layer 가 명시적으로 invalidate
- **read-through 허용** ← adapter 가 loader 를 소유하는 경우만 (Caffeine `LoadingCache`, Redisson `LocalCachedMap` 등). 책임 경계가 망가지지 않음 — `CACHE-PAT-C6` verbatim 후속 필요
- **write-through forbidden by default** ← consistency contract 없이 도입하면 cache update 와 DB commit 사이의 race 가 생김. cache-aside + after-commit invalidation 이 더 안전 (내부 결정)
- **write-behind forbidden** ← prod 에서 cache 노드 장애 시 silent data loss (`CACHE-PAT-C5` 의 verbatim 후속 필요). 결제 / 주문 도메인에는 부적합 (내부 결정)
- **trade-off 요약 표 (해석)**:
| pattern | read latency | write latency | consistency | failure mode | 본 자료 직접 증명? |
|---|---|---|---|---|---|
| cache-aside | fast (hit), slow (miss) | DB만 (cache는 invalidate) | application owns | stale on bug | `CACHE-PAT-C1`, `C2` 부분 |
| read-through | fast (hit), slow (miss) | DB만 | cache owns loader | cache misconfig = read failure | `CACHE-PAT-C6` `needs-confirmation` |
| write-through | fast | slow (cache+DB sync) | strong if same tx | cache outage = write failure | `CACHE-PAT-C3`, `C4` |
| write-behind | fast | very fast | weak (async) | cache crash = data loss | `CACHE-PAT-C5` `needs-confirmation` |
- **시사점**: ca-tmpl 이 채택한 cache-aside + after-commit invalidation 은 "약한 보장 + 실패 가시성 높음" 의 조합. 결제 같은 strong consistency 에는 별도 채널이 필요.
## Related / 관련
- 같은 주제 다른 official-doc / company-tech-blog: (write-behind, read-through 의 verbatim 출처 후속 수집 필요)
- 인용하는 branch:
- [[raw/branch-notes/feature-cache-consistency-contract]]
- 인용하는 wiki: (미작성)
- 대안 그룹 (ca-tmpl 결정 컨텍스트): **Group G-C — Cache consistency** (대안 1: cache-aside [ca-tmpl 채택] / 대안 2: write-through / 대안 3: write-behind / 대안 4: read-through with loader)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cache-caffeine-asyncloadingcache-readme.md
@@ -0,0 +1,116 @@
---
title: Caffeine — AsyncLoadingCache & cache stampede prevention (GitHub Wiki — Population)
source_type: official-doc
url: https://github.com/ben-manes/caffeine/wiki/Population
archive_url:
status: raw
confidence: high
tags: [ca-cache-consistency, caffeine, local-cache, stampede, single-instance, official-doc]
related_branches: [feature-cache-consistency-contract]
related_projects: [ca-skeleton]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Caffeine — AsyncLoadingCache & cache stampede prevention
> Layer: `raw/official-docs/` — Caffeine GitHub Wiki "Population" 페이지 (verbatim 발췌) + 관련 보조 인용 (`Refresh`, Spring `@Cacheable` Javadoc).
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-cache-consistency-contract]] | ca-tmpl single-instance stampede 방지에 Caffeine `LoadingCache` / `AsyncLoadingCache` 또는 Spring `@Cacheable(sync=true)` 채택 근거 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl single-instance stampede 방지 결정 **"Caffeine local lock"** 의 근거. `LoadingCache` / `AsyncLoadingCache` 의 stampede 방지 메커니즘과 Spring `@Cacheable sync=true` 와의 관계를 명시.
## 출처 / Source
- 원본 URL (Wiki "Population" 페이지): https://github.com/ben-manes/caffeine/wiki/Population
- 보조 페이지: Caffeine Wiki — "Refresh", "Specification"
- 보조 자료: Spring Framework `@Cacheable` Javadoc (`sync` attribute)
- 저자 / 조직: Ben Manes (Caffeine 저자) / Caffeine project
- 발행일: 지속 갱신 (GitHub Wiki)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
**Caffeine Wiki "Population" — 2026-05-27 fetch 로 확인된 인용**:
> [§LoadingCache] "A `LoadingCache` is a `Cache` built with an attached `CacheLoader`."
> [§AsyncLoadingCache] "A `AsyncLoadingCache` is a `AsyncCache` built with an attached `AsyncCacheLoader`."
> [§Asynchronous Computation] "A `AsyncCache` is a `Cache` variant that computes entries on an `Executor` and returns a `CompletableFuture`."
> [§CacheLoader Options] "A `CacheLoader` should be supplied when the computation is best expressed in a synchronous fashion." / "Alternatively, a `AsyncCacheLoader` should be supplied when the computation is expressed asynchronously and returns a `CompletableFuture`."
> [§Bulk Operations] "By default, `getAll` will issue a separate call to `CacheLoader.load` for each key which is absent from the cache."
**보조 인용 (별도 페이지 — 본 fetch 로는 verbatim 미확인, 원래 raw 작성 시점 수집본 보존)**:
> [Caffeine Wiki "Refresh" — needs-confirmation] "`refreshAfterWrite` reloads asynchronously, returning the old value during reload. Combined with `AsyncLoadingCache`, refresh does not block readers." *(2026-05-27 Wiki Population fetch 에서는 verbatim 미확인 — Refresh 별도 페이지 재확인 필요)*
> [Spring Framework `@Cacheable` Javadoc — `sync` attribute] "`sync=true` instructs the cache abstraction to synchronize the invocation of the underlying method, if several threads are attempting to load a value for the same key. Only one invocation is performed; others wait." *(별도 출처 — Caffeine wiki 가 아님)*
## Claims Extracted / 추출된 주장
> 본 raw 의 1차 출처는 Caffeine Wiki "Population". `CAFFEINE-POP-C*` prefix.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CAFFEINE-POP-C1 | `LoadingCache` = `CacheLoader` 가 attach 된 `Cache` 변형 | [§LoadingCache] "A `LoadingCache` is a `Cache` built with an attached `CacheLoader`." | `official-vendor-doc` | Caffeine 2.x/3.x `LoadingCache` API 사용 | "동일 key 동시 miss 시 single load 직렬화" 메커니즘 자체는 본 인용으로 직접 증명 안 됨 — 별도 Caffeine 동작 명세 또는 `CacheLoader.load` 계약 확인 필요 |
| CAFFEINE-POP-C2 | `AsyncLoadingCache` = `AsyncCacheLoader` 가 attach 된 `AsyncCache` 변형 | [§AsyncLoadingCache] "A `AsyncLoadingCache` is a `AsyncCache` built with an attached `AsyncCacheLoader`." | `official-vendor-doc` | Caffeine async API 사용 | in-flight `CompletableFuture` 가 같은 key 동시 요청에 공유되는지 / 실패 future 의 자동 제거 여부는 본 인용 범위 밖 |
| CAFFEINE-POP-C3 | `AsyncCache``Executor` 위에서 entry 를 계산하고 `CompletableFuture` 를 반환 | [§Asynchronous Computation] "A `AsyncCache` is a `Cache` variant that computes entries on an `Executor` and returns a `CompletableFuture`." | `official-vendor-doc` | Caffeine `AsyncCache.get(key, loader)` 호출 | Executor 의 기본 구현 (ForkJoinPool 등) 은 본 인용으로 명시 안 됨 — Caffeine `Specification` 페이지 별도 확인 |
| CAFFEINE-POP-C4 | 계산이 동기적이면 `CacheLoader`, 비동기적이고 `CompletableFuture` 반환이면 `AsyncCacheLoader` 사용 | [§CacheLoader Options] "A `CacheLoader` should be supplied when the computation is best expressed in a synchronous fashion." / "Alternatively, a `AsyncCacheLoader` should be supplied when the computation is expressed asynchronously and returns a `CompletableFuture`." | `official-vendor-doc` | loader 선택 결정 | "어느 쪽이 stampede 방지 측면에서 더 강력한지" 는 본 인용 범위 밖 |
| CAFFEINE-POP-C5 | `getAll` 의 기본 동작은 cache 에 없는 각 key 에 대해 `CacheLoader.load` 를 개별 호출 | [§Bulk Operations] "By default, `getAll` will issue a separate call to `CacheLoader.load` for each key which is absent from the cache." | `official-vendor-doc` | Caffeine `Cache.getAll(keys)` 호출 | bulk load 최적화 (예: `loadAll` override) 의 효과는 본 인용으로 직접 증명 안 됨 |
| CAFFEINE-POP-C6 | `refreshAfterWrite` 는 비동기 reload, 진행 중 old value 반환. `AsyncLoadingCache` 와 결합 시 reader 를 block 하지 않음 | [Wiki "Refresh"] "`refreshAfterWrite` reloads asynchronously, returning the old value during reload. Combined with `AsyncLoadingCache`, refresh does not block readers." | `needs-confirmation` *(원 raw 수집본; 2026-05-27 Population 페이지 fetch 에서는 verbatim 미발견 — Refresh 별도 페이지 재fetch 필요)* | Caffeine refresh 모드 | reload 실패 시 old value 의 유효 기간은 본 인용 범위 밖 |
| SPRING-CACHEABLE-C1 | Spring `@Cacheable(sync=true)` 는 같은 key 에 대해 여러 thread 가 동시에 load 시도할 때 underlying method 호출을 1회로 동기화. 나머지는 대기 | [Spring `@Cacheable` Javadoc — `sync` attribute] "`sync=true` instructs the cache abstraction to synchronize the invocation of the underlying method, if several threads are attempting to load a value for the same key. Only one invocation is performed; others wait." | `official-vendor-doc` *(Spring Framework reference Javadoc; Caffeine wiki 아님)* | Spring Cache abstraction 사용 + Caffeine backend | 이 동기화가 Caffeine 내부 lock 으로 위임되는지 vs Spring 자체 lock 인지는 본 인용으로 직접 증명 안 됨 — Spring `CaffeineCache` 구현 확인 필요 |
### Strength 적용 메모
- `official-vendor-doc`: Caffeine GitHub Wiki 는 저자 (Ben Manes) 가 직접 유지하는 공식 문서. official-standard (RFC) 가 아니므로 한 단계 아래.
- `needs-confirmation`: 본 fetch 에서 verbatim 으로 확인 불가능한 인용. 원 raw 작성 시점 수집본을 보존하되 별도 fetch 로 재확인 필요.
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CAFFEINE-POP-C1~C5`: Caffeine `LoadingCache`/`AsyncLoadingCache` 의 API 형태 + `getAll` 기본 동작
- `SPRING-CACHEABLE-C1`: Spring `@Cacheable(sync=true)` 의 동기화 의미 (Spring Javadoc 기준)
- **이 자료가 증명하지 않는 것**:
- **Caffeine `LoadingCache` 가 동일 key 동시 miss 시 backend 호출을 정확히 1회로 직렬화한다는 보장**: 본 Population 페이지 fetch 에서 verbatim 인용 미확보. ca-tmpl `Required test` 검증 시 별도 동작 테스트 + Caffeine source 코드 (`BoundedLocalCache#doComputeIfAbsent`) 확인 필요
- in-flight `CompletableFuture` 공유 / 실패 future 자동 제거 메커니즘: 본 fetch 범위 밖
- Spring `@Cacheable(sync=true)` 가 Caffeine backend 와 결합 시 어느 layer 에서 lock 이 걸리는지 (Spring 자체 lock vs Caffeine 내부): 별도 검증 필요
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl `Required test` ("동일 key 동시 cache miss → backend 호출 1회") 의 actual 검증
- multi-instance 환경에서 본 메커니즘이 적용 안 되는 점 (instance 별 별도 load) — Redisson RLock 등 분산 잠금으로 승격 필요한 분기
## 메모 / Notes (내 프로젝트 해석 — PRESERVED)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석. 원 raw 의 해석을 보존하되 verifiability gap 을 명시.
- single-instance stampede 방지 mechanism:
- **Caffeine `LoadingCache` 자체가 같은 key 에 대한 동시 load 를 1회로 직렬화 (널리 알려진 동작이나, 본 Population 페이지 fetch 만으로는 verbatim 증거 없음 — `needs-confirmation`)**. 외부 lock 불필요.
- Spring abstraction 을 쓸 때는 `@Cacheable(sync=true)` 로 동등 효과. 내부적으로 Caffeine `get(key, loader)` 가 호출됨 (Spring `CaffeineCache` 구현 가정 — 본 raw 자료로 직접 증명 안 됨).
- ca-tmpl `Required test` 와의 정합성:
- "동일 key 에 대해 동시 cache miss 시 backend 호출 1회로 제한" 검증 → Caffeine `LoadingCache` 또는 `@Cacheable(sync=true)` 둘 다 통과 가정.
- test 에서 명시한 (a) `sync=true` 또는 (b) `AsyncLoadingCache` 또는 (c) Redisson RLock wrap 분기 중 **(a)(b) 가 Caffeine 분기**, (c) 가 multi-instance 분기.
- 장점:
- in-process, network round-trip 없음 → 1µs급 hit latency.
- Window TinyLFU eviction policy 로 LRU 보다 hit-rate 우수 (Caffeine 논문 인용 영역 — 본 wiki 페이지 직접 증명 아님).
- 단점 (ca-tmpl 입장):
- **multi-instance** 에서는 의미 없음 — instance 별로 별도 load 가 일어남. HPA 환경에서는 RLock 으로 승격 필요.
- JVM restart 시 cache cold start. 안 가져갈 hot key 가 cold path 를 거치면 backend burst.
- 시사점: ca-tmpl 의 "single-instance Caffeine, multi-instance Redisson" 분기는 **scope 에 맞춘 도구 차등화**. write-back/distributed mode 를 Caffeine 에 요구하지 않음 (out of scope).
## Related / 관련
- 같은 주제 다른 raw:
- (Caffeine `Refresh` / `Specification` 별도 페이지 — 미작성)
- 인용하는 branch:
- [[raw/branch-notes/feature-cache-consistency-contract]]
- 적용 contract:
- [[raw/project-notes/ca-skeleton-operational-contract]] (Cache consistency 그룹)
- 인용하는 wiki: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cache-redisson-rlock-vs-setnx.md
@@ -0,0 +1,121 @@
---
title: Redisson RLock vs Redis SETNX — distributed lock for cache stampede
source_type: official-doc
url: https://redisson.org/glossary/distributed-lock-and-synchronizer.html
archive_url:
status: raw
confidence: high
tags: [ca-cache-consistency, redisson, redis, distributed-lock, stampede, rlock, setnx]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-cache-consistency-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Redisson RLock vs Redis SETNX — distributed lock for cache stampede
> Layer: `raw/official-docs/` — Redisson 공식 문서 + Redis 공식 + Kleppmann 비판의 verbatim 발췌.
> ca-tmpl 의 "multi-instance HPA 시 Redisson RLock" 채택 + SETNX/Redlock 배제 결정의 1차 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-cache-consistency-contract]] | Group G-C 의 stampede 방지 도구 선택 — multi-instance HPA 환경에서 SETNX 직접 구현 / Redlock 을 배제하고 Redisson RLock 을 채택한 근거 (watchdog 자동 갱신, reentrancy, `j.u.c.locks.Lock` 호환) + Kleppmann 비판으로 efficiency vs correctness lock 분리 |
## 컨텍스트
ca-tmpl 의 cache stampede 방지 결정 **"multi-instance HPA 시 Redisson RLock"** 의 근거. SETNX 직접 구현 / Redlock / RLock 의 trade-off 를 비교.
## 출처 / Source
- 원본 URL (주): https://redisson.org/glossary/distributed-lock-and-synchronizer.html (Redisson 공식 — Distributed Locks and Synchronizers)
- 보조 URL (Redis 공식 — Distributed Locks with Redis / Redlock):
- 이전 URL (2026-05-22 capture 시점): https://redis.io/docs/latest/develop/use-cases/distributed-locks/ — [2026-05-27 verified attempt] HTTP 404 (페이지 이전됨)
- 현재 URL: https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/ — [2026-05-27 verified attempt] WebFetch 성공, LOCK-C1 verbatim 일치 확인
- 참고: Martin Kleppmann, "How to do distributed locking" (Redlock 비판, https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html)
- 아카이브 URL: (미수집)
- 저자 / 조직: Redisson (open source project) / Redis Ltd. / Martin Kleppmann (개인)
- 발행일: rolling docs (Redisson / Redis)
- 마지막 확인일 (capture): 2026-05-22
- 마지막 재검증 시도: 2026-05-27
- **[2026-05-25 capture]**: user 가 2026-05-22 수집한 인용 원형 유지 (needs-confirmation 마커는 2026-05-25 부여된 상태).
- **재검증 결과 [2026-05-27 verified attempt]**:
- LOCK-C1, LOCK-C2 (Redis 공식): 1차 URL `https://redis.io/docs/latest/develop/use-cases/distributed-locks/` HTTP 404. 현재 공식 URL 은 `https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/` (경로가 `use-cases``clients/patterns` 로 이전). 신 URL WebFetch 성공.
- LOCK-C1: 신 URL 본문에서 `SET resource_name my_random_value NX PX 30000` 명령 + "`NX` option" + "expire of 30000 milliseconds (`PX` option)" + "value 'my_random_value'" 의 verbatim 일치 확인됨. user 수집본 wording 과 의미 동일하나 user 수집본은 다소 paraphrase ("The simplest way to use Redis to lock a resource is to create a key in an instance with ..." 는 실제 페이지의 "To acquire the lock, the way to go is the following:" 와 다름) — verbatim wrapper 는 다르되 핵심 명령 + 옵션 의미는 **공식 출처에서 verbatim 일치 확인**.
- LOCK-C2: 신 URL 본문에서 "The key is usually created with a limited time to live, using the Redis expires feature, so that eventually it will get released (property 2 in our list)." 발견 — user 수집본 wording ("A lock with a fixed time-to-live is required to avoid deadlocks ...") 과 의미 동일하나 verbatim 불일치. user 수집본은 paraphrase.
- → LOCK-C1 의 핵심 명령 (`SET ... NX PX 30000`) 은 공식 vendor doc 에서 verbatim 확인 → **Strength 상향 `needs-confirmation` → `official-vendor-doc`** (단 user wrapping 문장은 paraphrase 잔존).
- → LOCK-C2 는 공식 vendor doc 에 동등 의미 명시 존재 → **Strength 상향 `needs-confirmation` → `official-vendor-doc-paraphrase`** (verbatim wording 은 user 수집본 ≠ 공식, 의미는 일치).
- LOCK-C3 (Redisson RLock): 1차 URL `https://redisson.org/glossary/distributed-lock-and-synchronizer.html``redisson.pro` 도메인으로 301 redirect. WebFetch permission denied (redirect 호스트 호출 차단) → verbatim 재확인 **불가**. Strength **유지** `needs-confirmation`.
- LOCK-C4 (Kleppmann): 1차 URL `https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html` WebFetch permission denied → verbatim 재확인 **불가**. Strength **유지** (기존 `engineering-blog` 유지, 상향 없음).
- **재검증 한계**: Redisson Javadoc / Kleppmann 본문 재검증 보류. ca-tmpl `verified` 승급 전 별도 채널 (Redisson Javadoc 직접 다운로드 / archive.org Kleppmann 스냅샷) 확인 필요.
## 핵심 인용 / Key quotes (verbatim, user 수집본 [2026-05-25 capture] — [2026-05-27 verified attempt] 결과 인라인)
> [§Redis 공식 — Distributed Locks with Redis] [2026-05-27 verified attempt] `official-vendor-doc` (핵심 명령 verbatim 확인): "The simplest way to use Redis to lock a resource is to create a key in an instance with `SET resource_name my_random_value NX PX 30000`. This sets the key only if it does not already exist (NX option) with an expire of 30000 milliseconds (PX option)."
> — 공식 페이지 (신 URL `https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/`) 의 실제 wording 은 "To acquire the lock, the way to go is the following: `SET resource_name my_random_value NX PX 30000`. The command will set the key only if it does not already exist (`NX` option), with an expire of 30000 milliseconds (`PX` option). The key is set to a value 'my_random_value'." → 명령 / 옵션 / 의미 verbatim 일치, user wrapping 문장은 paraphrase.
> [§Redis 공식 — Distributed Locks with Redis] [2026-05-27 verified attempt] `official-vendor-doc-paraphrase` (의미 일치, wording 불일치): "A lock with a fixed time-to-live is required to avoid deadlocks when the client crashes after acquiring the lock but before releasing it."
> — 공식 페이지 실제 wording: "The key is usually created with a limited time to live, using the Redis expires feature, so that eventually it will get released (property 2 in our list)." → 의미 동일, verbatim 불일치.
> [§Redisson — Distributed Locks and Synchronizers] [2026-05-27 verified attempt] `needs-confirmation` 유지 (1차 URL 301 → redisson.pro, redirect 호스트 호출 차단으로 재확인 불가): "RLock implements `java.util.concurrent.locks.Lock` and adds `tryLock(waitTime, leaseTime, unit)` semantics. It uses a watchdog (default 30s) that automatically extends the lock TTL while the holding thread is alive, preventing premature expiry on long operations."
> [§Kleppmann — How to do distributed locking] [2026-05-27 verified attempt] `engineering-blog` 유지 (WebFetch permission denied 으로 재확인 불가): "Any algorithm that relies on lease timers for correctness is unsafe in the presence of GC pauses or network delays. For correctness use fencing tokens; for efficiency lock the Redis way is fine."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| LOCK-C1 | Redis 의 가장 단순한 단일-인스턴스 분산 락 패턴: `SET resource_name my_random_value NX PX 30000` (NX = 없을 때만, PX = ms TTL) | [§Redis 공식 — 신 URL `https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/`] [2026-05-27 verified] verbatim 핵심 명령 확인: "To acquire the lock, the way to go is the following: `SET resource_name my_random_value NX PX 30000`. The command will set the key only if it does not already exist (`NX` option), with an expire of 30000 milliseconds (`PX` option)." | `official-vendor-doc` (Strength 상향 [2026-05-27]: `needs-confirmation``official-vendor-doc` — Redis 공식 verbatim 일치 확인. user wrapping 문장은 paraphrase 잔존) | 단일 Redis 인스턴스 환경, efficiency lock | 멀티 노드 환경 (Redlock) 에서도 동일한 단순함이 유지된다는 뜻은 아님 |
| LOCK-C2 | client crash 시 deadlock 회피를 위해 **fixed TTL** 가 필수 (lock 획득 후 release 전 crash 대비) | [§Redis 공식 — 신 URL] [2026-05-27 verified, paraphrase] 공식 wording: "The key is usually created with a limited time to live, using the Redis expires feature, so that eventually it will get released (property 2 in our list)." — 의미 동일, verbatim 불일치 | `official-vendor-doc-paraphrase` (Strength 상향 [2026-05-27]: `needs-confirmation``official-vendor-doc-paraphrase` — 공식 vendor doc 에 동등 의미 명시, verbatim wording 은 user 수집본과 불일치) | Redis 기반 분산 락 일반 | TTL 만 있으면 correctness 가 보장된다는 뜻은 아님 (Kleppmann 비판 참고, LOCK-C4) |
| LOCK-C3 | Redisson `RLock``j.u.c.locks.Lock` 인터페이스를 구현 + `tryLock(waitTime, leaseTime, unit)` 시맨틱 + **watchdog (기본 30s) 으로 holding thread 가 살아있는 동안 lock TTL 자동 연장** → 긴 작업 시 premature expiry 방지 | [§Redisson] [2026-05-27 verified attempt] 1차 URL 301 → redisson.pro, redirect 호스트 호출 차단으로 verbatim 재확인 불가: "RLock implements `java.util.concurrent.locks.Lock` and adds `tryLock(waitTime, leaseTime, unit)` semantics. It uses a watchdog (default 30s)..." | `needs-confirmation` (유지 — Strength 상향 없음) | Redisson client 사용 시 | watchdog 이 모든 GC pause / network partition 시나리오를 흡수한다는 뜻은 아님 (Kleppmann 의 fencing token 비판 별도, LOCK-C4) |
| LOCK-C4 | **Kleppmann 비판**: lease timer 에 correctness 를 의존하는 알고리즘은 GC pause / network delay 상황에서 unsafe — correctness 가 필요하면 **fencing token**, efficiency 목적이라면 Redis 방식도 충분 | [§Kleppmann] [2026-05-27 verified attempt] WebFetch permission denied → verbatim 재확인 불가: "Any algorithm that relies on lease timers for correctness is unsafe in the presence of GC pauses or network delays. For correctness use fencing tokens; for efficiency lock the Redis way is fine." | `engineering-blog` (유지 — Strength 상향 없음) | distributed lock 의 efficiency vs correctness 구분 | Redlock 이 모든 시나리오에서 부적합하다는 뜻은 아님 — efficiency lock 용도는 여전히 유효 (Kleppmann 본인 명시) |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것** ([2026-05-27 verified attempt] 결과 반영):
- `LOCK-C1`: Redis SET NX PX 분산 락 패턴의 기본 동작 — Redis 공식 (신 URL `https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/`) 에서 핵심 명령 verbatim 일치 확인 → `official-vendor-doc`
- `LOCK-C2`: TTL 의 deadlock 회피 역할 — Redis 공식에 동등 의미 명시 (wording 은 paraphrase) → `official-vendor-doc-paraphrase`
- `LOCK-C3`: Redisson RLock 의 `j.u.c.locks.Lock` 호환 + watchdog 자동 갱신 — 1차 URL 301 redirect (redisson.org → redisson.pro) + redirect 호스트 호출 차단으로 재확인 불가 → `needs-confirmation` 유지
- `LOCK-C4`: Kleppmann 의 efficiency vs correctness lock 분리 권고 — WebFetch permission denied 으로 재확인 불가 → `engineering-blog` 유지 (본 자료에서 가장 강한 출처는 LOCK-C1 의 Redis 공식으로 변경됨)
- **이 자료가 증명하지 않는 것**:
- Redisson RLock 이 모든 use case 에서 SETNX 보다 우월하다는 일반 claim (도구 선택은 운영 복잡도 / 의존성 vs 자동화 trade-off)
- Redlock 이 항상 over-engineering 이라는 평가 (Kleppmann 본인이 efficiency lock 으로는 OK 명시)
- SETNX 직접 구현이 모든 watchdog 시나리오에서 fail 한다는 보장 (운영자가 별도 갱신 스레드 구현 가능)
- watchdog 의 기본 30s 가 모든 워크로드에서 적정하다는 보장 (긴 batch / heavy GC 환경은 별도 튜닝 필요)
- Redisson 의존성 추가가 Lettuce/Jedis 와 충돌 없이 공존 가능하다는 보장 (운영 검증 필요)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- **재검증 한계로 인한 SSOT 확인 의무 (잔여)**: LOCK-C1/C2 는 [2026-05-27 verified attempt] 로 Redis 공식 신 URL verbatim 확인 완료. LOCK-C3 (Redisson RLock watchdog / leaseTime 시맨틱) 와 LOCK-C4 (Kleppmann) 는 여전히 redirect / permission 차단으로 재확인 불가 → ca-tmpl 의 `verified` / `published-ready` 승급 전 Redisson Javadoc + Kleppmann archive.org 스냅샷으로 직접 verbatim 격상 필요.
- ca-tmpl 의 "stampede 방지 = efficiency lock" 분류가 모든 cache 시나리오 (예: token bucket, rate limit) 에 적용되는지 (correctness lock 으로 격상해야 하는 endpoint 식별)
- spring-boot-starter-redisson 과 spring-boot-starter-data-redis (Lettuce) 의 connection pool 공존 운영 비용
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- SETNX 단독:
- 장점: 매우 단순. Lua script 로 atomic release (check-and-del) 가능.
- 단점:
- 직접 구현 시 **자동 갱신 (watchdog) 없음** → 처리 시간이 lease 를 넘으면 lock 해제 후 다른 thread 도 진입 (이중 stampede).
- reentrancy 없음 — 같은 thread 가 재진입 시 별도 코드.
- lock 해제 시 owner 검증 직접 구현해야 함 (key 의 random value 비교 Lua script).
- Redisson RLock:
- 장점: `java.util.concurrent.locks.Lock` 인터페이스 호환, reentrancy, **watchdog 자동 갱신**, fair lock / multi-lock / read-write lock 지원.
- 단점: Redisson client 추가 의존성. spring-boot-starter-redisson 이 별도. Lettuce/Jedis 와 connection pool 이 별도라 운영 복잡.
- Redlock (multi-node):
- 장점: 단일 Redis 장애에 강함.
- 단점: Kleppmann 비판 (LOCK-C4) — clock drift, GC pause 로 correctness 보장 안 됨. cache stampede 같은 efficiency lock 에는 over-engineering.
- ca-tmpl 결정 정당성:
- **stampede 방지는 efficiency lock** 이지 correctness lock 이 아님. RLock 단일 Redis 로 충분. 결제처럼 correctness 가 필요하면 RLock 도 부적합 — DB unique constraint 나 fencing token 사용.
- 시사점: ca-tmpl 의 "single-instance Caffeine local + multi-instance Redisson RLock" 분기는 **lock 책임 범위에 맞춘 도구 선택**. Redlock 은 의도적으로 배제.
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/company-tech-blogs/cache-woowahan-after-commit-invalidation]] (cache invalidation timing 사례)
- 적용 ca-tmpl branch-note:
- [[raw/branch-notes/feature-cache-consistency-contract]]
- canonical contract 섹션:
- [[raw/project-notes/ca-skeleton-operational-contract]] (cache consistency 관련 섹션)
- 대안 그룹: **Group G-C — Cache consistency** (stampede 도구 비교: a) Caffeine local / b) Redisson RLock [ca-tmpl multi-instance] / c) SETNX 직접 구현 / d) Redlock multi-node) — 본 source 는 **b 채택 + c/d 배제 근거**.
- 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/caddy-automatic-https-docs.md
@@ -0,0 +1,108 @@
---
title: Caddy — Automatic HTTPS (official-vendor-doc)
source_type: official-doc
url: https://caddyserver.com/docs/automatic-https
archive_url:
status: raw
confidence: high
tags: [caddy, https, tls, acme, lets-encrypt, on-demand-tls, keycloak-https-termination]
related_projects: []
related_branches: [feature-keycloak-https-termination-caddy-nginx]
created: 2026-05-27
last_reviewed: 2026-05-27
---
# Caddy — Automatic HTTPS (공식)
> Layer: `raw/official-docs/` — Caddy 공식 문서의 **원문 발췌·출처 기록**.
> Strength 분류: `official-vendor-doc` — Caddy 의 공식 documentation site (`caddyserver.com/docs/...`).
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 별도 작성.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-https-termination-caddy-nginx]] | **D2 (Caddy auto-HTTPS + Let's Encrypt)** 의 근거 — Caddy 가 ACME (Let's Encrypt / ZeroSSL) 로 자동 certificate 발급·갱신 + HTTP→HTTPS redirect 를 default 동작으로 보장. nginx + certbot 대비 운영 단순성의 외부 근거. |
## 컨텍스트
`feature-keycloak-https-termination-caddy-nginx` 의 D2 는 "Keycloak 앞단 TLS 종단을 Caddy 로 처리한다" 는 결정을 다룬다. Caddy 의 Automatic HTTPS 페이지는 (a) 도메인 인식 시 ACME 자동 발급, (b) HTTP→HTTPS redirect, (c) renewal in background 를 직접 진술한다. 본 raw 는 D2 의 외부 근거로 보관.
## 출처 / Source
- 원본 URL: https://caddyserver.com/docs/automatic-https
- 아카이브 URL: (미수집 — 추후 archive.org 스냅샷 추가)
- 저자 / 조직: Caddy / Stack Holdings — Caddy official documentation
- 발행일: rolling docs (Caddy 2.x current)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Overview] "Automatic HTTPS provisions TLS certificates for all your sites and keeps them renewed."
> [§Overview] "By default, Caddy serves all sites over HTTPS."
> [§Overview] "Caddy was the first web server to use HTTPS automatically and by default."
> [§Overview] "Caddy serves public DNS names over HTTPS using certificates from a public ACME CA such as Let's Encrypt or ZeroSSL."
> [§Overview] "Caddy keeps all managed certificates renewed and redirects HTTP (default port 80) to HTTPS (default port 443) automatically."
> [§Overview] "It will not get certificates for individual subdomains unless explicitly configured to do so; and renewals happen in the background."
> [§Activation] "Caddy implicitly activates automatic HTTPS when it knows a domain name (i.e. hostname) or IP address it is serving."
> [§Activation] "Explicitly disabling it via JSON or via Caddyfile will prevent automatic HTTPS from being activated, either in whole or in part."
> [§Storage] "Caddy will store public certificates, private keys, and other assets in its configured storage facility"
> [§Storage] "The main thing you need to know using the default config is that the `$HOME` folder must be writeable and persistent."
> [§On-Demand TLS] "On-demand TLS is useful if: you do not know all the domain names when you start or reload your server"
> [§On-Demand TLS] "when a TLS handshake is received for a server name (SNI) that Caddy does not yet have a certificate for, the handshake is held while Caddy obtains a certificate"
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CADDY-AHTTPS-C1 | Caddy 의 Automatic HTTPS 는 **TLS certificate 자동 발급 + 자동 갱신** 을 모든 site 에 대해 수행 | [§Overview] "Automatic HTTPS provisions TLS certificates for all your sites and keeps them renewed." | `official-vendor-doc` | Caddy 2.x 에서 도메인 명시 설정 시 | "모든 종류의 CA 와 호환" 이라는 뜻은 아님 — 본 인용 다음 줄에서 ACME CA 한정 (`C3` 참조) |
| CADDY-AHTTPS-C2 | Caddy 는 **default 로 모든 site 를 HTTPS** 로 serve 하며, HTTPS by default 를 채택한 최초의 web server 라고 주장 | [§Overview] "By default, Caddy serves all sites over HTTPS." + "Caddy was the first web server to use HTTPS automatically and by default." | `official-vendor-doc` | Caddy 의 default 동작 | "다른 web server 가 HTTPS by default 가 아니다" 라는 비교 진술은 본 인용으로 일반화 금지 (nginx 1.25+ 등은 별도 확인) |
| CADDY-AHTTPS-C3 | Caddy 는 **public DNS name 의 HTTPS****public ACME CA** (Let's Encrypt 또는 ZeroSSL) 의 certificate 로 처리 | [§Overview] "Caddy serves public DNS names over HTTPS using certificates from a public ACME CA such as Let's Encrypt or ZeroSSL." | `official-vendor-doc` | public DNS 도메인을 가진 site | internal domain / private CA 사용 시에는 별도 설정 필요 (본 인용 범위 밖) |
| CADDY-AHTTPS-C4 | Caddy 는 **HTTP (port 80) → HTTPS (port 443) redirect 를 자동** 수행 + managed cert 자동 갱신 | [§Overview] "Caddy keeps all managed certificates renewed and redirects HTTP (default port 80) to HTTPS (default port 443) automatically." | `official-vendor-doc` | default Caddyfile 설정 사용 시 | redirect 의 HTTP status code (301 vs 308) 는 본 인용 범위 밖 |
| CADDY-AHTTPS-C5 | Caddy 는 **개별 subdomain 에 대해서는 자동 발급하지 않으며** (명시 설정 필요), renewal 은 background 에서 수행 | [§Overview] "It will not get certificates for individual subdomains unless explicitly configured to do so; and renewals happen in the background." | `official-vendor-doc` | wildcard / subdomain 인증서 정책 | renewal 의 정확한 주기 (예: "30일 전") 는 본 인용 범위 밖 — 별도 ACME issuer 정책 의존 |
| CADDY-AHTTPS-C6 | Automatic HTTPS 는 Caddy 가 serve 하는 hostname 또는 IP 를 인식하면 **암묵적으로 활성화** | [§Activation] "Caddy implicitly activates automatic HTTPS when it knows a domain name (i.e. hostname) or IP address it is serving." | `official-vendor-doc` | Caddyfile / JSON 에 명시된 hostname/IP | "IP address 인 경우 ACME 가 발급한다" 는 뜻은 아님 — IP 에 대한 public CA 발급은 제한적 |
| CADDY-AHTTPS-C7 | JSON 또는 Caddyfile 에서 명시적으로 disable 가능 (전체 또는 부분) | [§Activation] "Explicitly disabling it via JSON or via Caddyfile will prevent automatic HTTPS from being activated, either in whole or in part." | `official-vendor-doc` | Automatic HTTPS opt-out 시나리오 | opt-out 의 구체적 directive 명 (`auto_https off` 등) 은 본 인용 범위 밖 — 별도 Caddyfile reference 참조 |
| CADDY-AHTTPS-C8 | 인증서/키 등 자산은 **configured storage facility** 에 저장, default config 에서는 `$HOME` 이 writable + persistent 여야 함 | [§Storage] "Caddy will store public certificates, private keys, and other assets in its configured storage facility" + "the `$HOME` folder must be writeable and persistent." | `official-vendor-doc` | container / systemd 환경에서 Caddy 운영 | container 환경에서 `$HOME` 의 default 가 어디인지는 본 인용 범위 밖 — Docker image 별 확인 |
| CADDY-AHTTPS-C9 | **On-Demand TLS** 는 시작/reload 시점에 모든 domain 을 알 수 없는 경우 유용 | [§On-Demand TLS] "On-demand TLS is useful if: you do not know all the domain names when you start or reload your server" | `official-vendor-doc` | multi-tenant / wildcard SaaS 시나리오 | on-demand TLS 가 production default 라는 뜻은 아님 — opt-in 기능 |
| CADDY-AHTTPS-C10 | On-Demand TLS 는 알려지지 않은 SNI 의 handshake 도래 시 **handshake 를 보류** 하고 cert 를 obtain | [§On-Demand TLS] "when a TLS handshake is received for a server name (SNI) that Caddy does not yet have a certificate for, the handshake is held while Caddy obtains a certificate" | `official-vendor-doc` | on-demand TLS 활성화 시 | handshake 보류의 timeout / DoS 방지 메커니즘은 본 인용 범위 밖 — 별도 on-demand TLS 페이지 참조 |
| CADDY-AHTTPS-C11 | **HSTS (Strict-Transport-Security) 관련 직접 진술은 본 페이지에 부재** — 부재 사실 자체가 claim | (인용 없음 — 본 페이지에서 HSTS 미언급) | `needs-confirmation` | HSTS default behavior 주장 시 | Caddy 가 HSTS 를 default 로 보내지 않는다는 뜻이 아님. 단지 본 페이지가 보증하지 않는다는 사실. **D2 의 "HSTS defaults" 주장은 본 raw 로 입증 불가 — `tls` directive 또는 별도 페이지 확인 필요** |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CADDY-AHTTPS-C1`, `C3`, `C4`, `C5`: ACME 자동 발급 + HTTP→HTTPS redirect + background renewal 가 Caddy 의 default 동작
- `CADDY-AHTTPS-C6`, `C7`: 활성화/비활성화 트리거 (hostname 인식 / 명시 disable)
- `CADDY-AHTTPS-C8`: storage 요구사항 (`$HOME` writable + persistent)
- **이 자료가 증명하지 않는 것**:
- **HSTS 자동 적용** — 본 페이지에 HSTS 직접 언급 없음 (`C11`). D2 에서 "HSTS defaults" 를 주장하려면 별도 출처 필요 (예: Caddy `tls` directive 문서, `header` 전역 directive 문서)
- **certificate renewal 의 정확한 timing** (예: "만료 30일 전") — `C5` 는 "background 에서 renewal" 만 진술
- **ZeroSSL fallback 의 발생 조건** — `C3` 은 "such as Let's Encrypt or ZeroSSL" 만 진술, 선택 로직은 본 인용 범위 밖
- **nginx + certbot 대비 운영 우위** — Caddy 문서는 자기 동작만 진술, 비교 결론은 별도 분석 필요
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- `feature-keycloak-https-termination-caddy-nginx` 의 D2 에서 **HSTS 보장** 을 명시하려면 본 raw 외 추가 출처 필요 (Caddy `tls` 또는 `header` directive 문서 + 실제 response header 검증)
- Keycloak `KC_PROXY_HEADERS=xforwarded` 와 Caddy 의 default proxy header 동작 호환성 — 본 raw 는 reverse proxy header 명세까지 다루지 않음 (별도 Caddy `reverse_proxy` directive 문서 필요)
- Let's Encrypt 의 rate limit (per-domain 주당 50건 등) — 본 raw 범위 밖, Let's Encrypt 공식 문서 참조
## 메모 / Notes
- `C11`**중요한 부재 사실**. D2 의 "HSTS defaults" 주장을 본 raw 로 정당화하면 **UNSUPPORTED_DECISION** 으로 분류되어야 함. 별도 출처 보강 필수.
- `C2` 의 "first web server to use HTTPS automatically and by default" 는 historical 주장 — 다른 server (nginx 1.25, Apache 2.4 등) 와의 비교는 본 인용으로 일반화 금지.
- container 환경에서 Caddy 운영 시 `$HOME` (`C8`) 가 read-only FS 면 동작 실패 — Dockerfile / k8s volume 설정 검증 필요.
## Related / 관련
- 같은 주제 다른 raw 자료: [[raw/official-docs/certbot-user-guide.md]] (nginx + certbot 대안)
- 이 자료를 인용한 wiki 요약: (미작성)
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-keycloak-https-termination-caddy-nginx]]
- 인용하는 project: [[raw/project-notes/keycloak-patterns-overview]]
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/calver-spec-calver-official.md
@@ -0,0 +1,83 @@
---
title: CalVer — Calendar Versioning Specification (calver.org)
source_type: official-doc
url: https://calver.org/
archive_url:
related_branches: [feature-build-release-supply-chain-contract]
related_projects: [ca-skeleton]
vendor: calver.org
tags: [official-doc, ca-skeleton, ci-cd, calver, semver, version-scheme, calendar-versioning]
created: 2026-06-15
---
# CalVer — Calendar Versioning Specification (calver.org)
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-build-release-supply-chain-contract]] | Decision D9 — "CalVer forbidden"의 negative-evidence: CalVer when-to-use 기준(대규모/시간민감 scope)이 library/skeleton에는 해당하지 않음. |
## 출처 / Source
- 원본 URL: https://calver.org/
- 아카이브 URL: (미등록)
- 저자 / 조직: calver.org (Mahmoud Hashemi 외 기여자)
- 발행일: (연도 미표기, ongoing)
- 마지막 확인일: 2026-06-15
## 왜 저장했는지 / Why archived
`feature-build-release-supply-chain-contract` 브랜치의 D9 결정 — "artifact version = SemVer + git sha suffix, CalVer forbidden" — 의 negative-evidence 근거.
CalVer 공식 사이트가 명시하는 적합 조건(대규모/상시변동 scope, 시간민감 프로젝트)이 library skeleton에 해당하지 않음을 원문으로 뒷받침하며, library/API compatibility-contract 사용 사례에 대한 권고가 원문에 **아예 없음**을 기록한다.
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
> [§Overview / Definition] "CalVer is a versioning convention based on your project's release calendar, instead of arbitrary numbers."
> [§Overview / When to use — Question 1] "Does your project feature a large or constantly-changing scope?"
> [§Overview / When to use — Question 2] "Is your project time-sensitive in any way? Do other external changes drive new project releases?"
> [§Overview / When to use — Conclusion] "If you answered yes to any of these questions, CalVer's semantics make it a strong choice for your project."
> [§Absence — explicit] calver.org 는 library 개발, API compatibility contract, skeleton project 용도에 대한 권고를 **전혀 포함하지 않는다**. "when NOT to use CalVer" 섹션도 존재하지 않는다. 위 두 질문에 "no"를 답하는 프로젝트(scope 고정, 시간민감 아님)에 대한 지침은 원문에 없다. (absence-of-guidance notation — verbatim 발췌 아님)
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CALVER-C1 | CalVer는 "임의 숫자" 대신 프로젝트의 릴리즈 캘린더를 기반으로 하는 버전 규약이다 | [§Definition] "CalVer is a versioning convention based on your project's release calendar, instead of arbitrary numbers." | `official-reference` | CalVer를 도입/비교하는 모든 프로젝트 | SemVer가 더 적합한 경우에 대한 직접적 언급 없음 |
| CALVER-C2 | CalVer의 첫 번째 적합 조건: 프로젝트가 대규모이거나 상시 변동하는 scope를 가지는가 | [§When to use] "Does your project feature a large or constantly-changing scope?" | `official-reference` | Ubuntu, Twisted, Boltons 같은 대형 시스템/유틸리티 모음 | 소규모·고정 scope를 가진 library/skeleton에 CalVer를 쓰지 말라는 명시적 금지 아님 — 질문에 "no"를 답하는 경우는 원문이 침묵 |
| CALVER-C3 | CalVer의 두 번째 적합 조건: 시간 민감하거나 외부 변화(보안 업데이트, 비즈니스 변경, timezone 변경 등)가 릴리즈를 구동하는가 | [§When to use] "Is your project time-sensitive in any way? Do other external changes drive new project releases?" | `official-reference` | certifi(인증서), pytz(timezone), security patch 중심 프로젝트 | compatibility contract가 주 설계 축인 library에는 이 조건이 미적용임을 명시하지 않음 |
| CALVER-C4 | 위 두 질문 중 하나라도 "yes"이면 CalVer가 강력한 선택이 된다 | [§When to use] "If you answered yes to any of these questions, CalVer's semantics make it a strong choice for your project." | `official-reference` | 적합 조건을 만족하는 프로젝트 | "no"인 경우 CalVer가 부적합하다는 명시적 진술 없음 — 부재(absence)를 negative-evidence로 사용해야 함 |
| CALVER-C5 | calver.org는 library 개발, API compatibility contract, skeleton project 에 대한 CalVer 사용 권고나 금지를 포함하지 않는다 | [§Absence] 원문 어디에도 "library", "API compatibility", "skeleton" 사용 사례에 대한 섹션이 없음 | `needs-confirmation` | D9 negative-evidence 논증(library skeleton에 CalVer가 금지되어야 하는 이유를 원문 부재로 뒷받침) | 이 부재만으로 CalVer가 library에 "잘못"이라는 것을 직접 증명하지 않음 — SemVer 공식 문서(semver.org) 보강 필요 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `CALVER-C1`: CalVer는 릴리즈 날짜를 버전에 인코딩하는 규약임
- `CALVER-C2` + `CALVER-C3`: CalVer의 공식 적합 기준은 "대규모/상시변동 scope" + "시간민감/외부구동 릴리즈"
- `CALVER-C4`: 두 조건 중 하나라도 맞으면 CalVer가 "강력한 선택"이라고 원문이 직접 말함
- `CALVER-C5`: library/API/skeleton 사용 사례에 대한 guidance가 **원문에 전혀 없음** (absence-of-guidance)
- 이 자료가 증명하지 않는 것:
- "CalVer는 library에 쓰면 안 된다"는 명시적 금지 — 이것은 `CALVER-C5`의 absence + SemVer 설계 철학을 결합한 추론임
- SemVer가 library에 더 적합하다는 주장 — 이는 semver.org 원문으로 별도 뒷받침 필요
- CalVer를 사용하는 library가 실패했다는 사례 증거
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- D9의 완전한 정당화를 위해 semver.org의 "API compatibility" 철학 원문 등록 권고 (D9의 Decision Evidence Map은 현재 UNSUPPORTED_DECISION 상태)
- `CALVER-C5``needs-confirmation` — calver.org가 explicit exclusion list를 게시하지 않는 것이 "library에 부적합"의 충분 근거인지 별도 검토
## 메모 / Notes
- calver.org Notable Users: Ubuntu (`YY.0M`), NixOS (`YY.0M`), Twisted (`YY.MM.MICRO`), youtube-dl (`YYYY.0M.0D`), certifi (`YYYY.MM.DD`), pip (`YY.MINOR.MICRO`), Spring Cloud (`YYYY.MINOR.MICRO`), Home Assistant (`YYYY.MM.MICRO`) — 공통점: OS 배포판, 인증서, timezone, CLI 유틸리티, 대형 프레임워크. Library skeleton과는 scope·driver 모두 다름.
- D9 negative-evidence 논증 구조: (1) CalVer 적합 조건 = 대규모/상시변동 scope + 시간민감 (CALVER-C2, C3) → (2) ca-skeleton은 scope 고정·버전 호환성이 주 설계축 → (3) 조건 불일치 → (4) calver.org가 library/skeleton 사용 사례에 대한 guidance를 제공하지 않음 (CALVER-C5) → D9 결정 지지. 이 논증을 완결하려면 semver.org raw 추가 등록 권고.
- `CALVER-C4`의 논리적 역 ("no이면 부적합")은 원문이 명시하지 않음. 이를 D9 지지 논거로 쓸 때 추론임을 명시해야 함.
## Related / 관련
- 보강 권고 (아직 미등록): `[[raw/official-docs/semver-spec-semver-official]]` — SemVer 공식 사이트 (semver.org), D9의 positive-evidence ("SemVer + git sha = library API compatibility contract 표준")
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
-1
View File
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/certbot-user-guide.md
+92
View File
@@ -0,0 +1,92 @@
---
title: Certbot — User Guide (official-vendor-doc)
source_type: official-doc
url: https://eff-certbot.readthedocs.io/en/stable/using.html
archive_url:
status: raw
confidence: high
tags: [certbot, lets-encrypt, acme, nginx, tls, renewal, keycloak-https-termination]
related_projects: []
related_branches: [feature-keycloak-https-termination-caddy-nginx]
created: 2026-05-27
last_reviewed: 2026-05-27
---
# Certbot — User Guide (공식)
> Layer: `raw/official-docs/` — EFF Certbot 공식 사용자 가이드의 **원문 발췌·출처 기록**.
> Strength 분류: `official-vendor-doc` — EFF (Electronic Frontier Foundation) 가 maintain 하는 Certbot 의 공식 문서.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 별도 작성.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-https-termination-caddy-nginx]] | **D3 (certbot CLI + renewal model)** 의 근거 — certbot 의 subcommand 체계 + automated renewal (preconfigured scheduled task) + nginx plugin 의 공식 명세. Caddy auto-HTTPS 대안으로 nginx + certbot 채택 시의 운영 모델 외부 근거. |
## 컨텍스트
`feature-keycloak-https-termination-caddy-nginx` 의 D3 은 "Caddy 대안으로 nginx + certbot 을 채택할 경우의 운영 모델" 을 다룬다. Certbot 의 user guide 는 (a) subcommand 체계 (`certonly`, `renew`, `run`), (b) automated renewal (scheduled task / `certbot renew`), (c) nginx plugin (`--nginx`), (d) 갱신 임계 (lifetime 의 1/3 미만), (e) hooks 를 직접 진술한다. 본 raw 는 D3 의 외부 근거로 보관.
## 출처 / Source
- 원본 URL: https://eff-certbot.readthedocs.io/en/stable/using.html
- 아카이브 URL: (미수집 — 추후 archive.org 스냅샷 추가)
- 저자 / 조직: EFF (Electronic Frontier Foundation) — Certbot project
- 발행일: rolling docs (Certbot 4.x stable)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Certbot Commands] "Certbot uses a number of different commands (also referred to as \"subcommands\") to request specific actions such as obtaining, renewing, or revoking certificates."
> [§Automated Renewals] "Most Certbot installations come with automatic renewals preconfigured. This is done by means of a scheduled task which runs `certbot renew` periodically."
> [§Automated Renewals] "If you are unsure whether you need to configure automated renewal: Review the instructions for your system and installation method at https://certbot.eff.org/instructions. They will describe how to set up a scheduled task, if necessary."
> [§Nginx] "The Nginx plugin should work for most configurations. We recommend backing up Nginx configurations before using it (though you can also revert changes to configurations with `certbot --nginx rollback`)."
> [§Renewing certificates] "This command attempts to renew any previously-obtained certificates which are ready for renewal. As of Certbot 4.0.0, a certificate is considered ready for renewal when less than 1/3rd of its lifetime remains."
> [§Renewing certificates] "The `renew` command includes hooks for running commands or scripts before or after a certificate is renewed."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CERTBOT-UG-C1 | Certbot 은 certificate 의 obtain / renew / revoke 등 특정 동작을 **subcommand** 체계로 노출 | [§Certbot Commands] "Certbot uses a number of different commands (also referred to as \"subcommands\") to request specific actions such as obtaining, renewing, or revoking certificates." | `official-vendor-doc` | Certbot CLI 사용 시나리오 | subcommand 의 전체 목록 (`certonly`, `run`, `delete`, `revoke` 등) 은 본 인용에 명시되어 있지 않음 — 별도 reference 페이지 참조 |
| CERTBOT-UG-C2 | **대부분의 Certbot installation 은 automated renewal 이 preconfigured** 되어 있으며, 이는 `certbot renew` 를 주기적으로 실행하는 **scheduled task** 로 구현됨 | [§Automated Renewals] "Most Certbot installations come with automatic renewals preconfigured. This is done by means of a scheduled task which runs `certbot renew` periodically." | `official-vendor-doc` | OS 패키지 매니저 / snap 등 표준 installation 경로 | scheduled task 의 구체 구현 (systemd timer vs cron) 은 본 인용 범위 밖 — installation 방식 의존 (`C3` 참조) |
| CERTBOT-UG-C3 | scheduled task 의 구체 설정 방식은 system / installation method 별로 다르며, certbot.eff.org/instructions 에서 안내 | [§Automated Renewals] "Review the instructions for your system and installation method at https://certbot.eff.org/instructions. They will describe how to set up a scheduled task, if necessary." | `official-vendor-doc` | OS / installer 별 renewal 구성 차이 | "모든 OS 에서 systemd timer 가 default" 라는 뜻은 아님 — installation method 의존 |
| CERTBOT-UG-C4 | **Nginx plugin** (`--nginx`) 은 대부분의 구성에서 동작하며, 사용 전 nginx 설정 backup 권장. `certbot --nginx rollback` 으로 변경 되돌리기 가능 | [§Nginx] "The Nginx plugin should work for most configurations. We recommend backing up Nginx configurations before using it (though you can also revert changes to configurations with `certbot --nginx rollback`)." | `official-vendor-doc` | nginx + certbot 통합 시나리오 | "모든 nginx 설정에서 동작 보장" 이라는 뜻은 아님 ("should work for most") — edge case (복잡 server block 등) 는 manual config 필요 |
| CERTBOT-UG-C5 | `certbot renew` 는 이전에 발급된 cert 중 **갱신 준비된 것** 만 갱신 시도. **Certbot 4.0.0 부터** "갱신 준비됨" 의 기준은 **lifetime 의 1/3 미만 남음** | [§Renewing certificates] "This command attempts to renew any previously-obtained certificates which are ready for renewal. As of Certbot 4.0.0, a certificate is considered ready for renewal when less than 1/3rd of its lifetime remains." | `official-vendor-doc` | Certbot 4.0.0 이상 의 `certbot renew` 동작 | Certbot 4.0.0 이전 버전의 동일 임계 (90일 cert 의 30일 전 등) 가 본 정의와 동일하다는 뜻은 아님 — 이전 버전은 별도 changelog 확인 |
| CERTBOT-UG-C6 | `renew` 명령은 **갱신 전/후 명령 실행을 위한 hooks** 를 포함 (`--pre-hook`, `--post-hook`, `--deploy-hook`) | [§Renewing certificates] "The `renew` command includes hooks for running commands or scripts before or after a certificate is renewed." | `official-vendor-doc` | 갱신 시 nginx reload / 서비스 재시작 자동화 | hook flag 명 (`--pre-hook` 등) 의 구체 사용법은 본 인용 범위 밖 — 별도 reference 참조 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CERTBOT-UG-C2`, `C3`: automated renewal 은 standard installation 의 default — scheduled task 가 미리 구성됨
- `CERTBOT-UG-C4`: nginx plugin (`--nginx`) 의 공식 지원 + rollback 메커니즘
- `CERTBOT-UG-C5`: Certbot 4.0.0 부터 renewal 임계는 lifetime 의 1/3 (e.g. 90일 cert 의 30일 전, 6일 short-lived cert 의 2일 전)
- `CERTBOT-UG-C6`: renewal hooks 가 공식 지원됨 (nginx reload 자동화 가능)
- **이 자료가 증명하지 않는 것**:
- **scheduled task 의 구체 구현이 systemd timer 인지 cron 인지** — `C3` 명시적으로 "installation method 의존" 이라 진술. Ubuntu 22.04 의 snap certbot 은 systemd timer (`snap.certbot.renew.timer`), apt-installed certbot 은 cron (`/etc/cron.d/certbot`) — 본 raw 가 직접 보증하지 않음
- **`--nginx` plugin 이 nginx 설정을 어떻게 수정하는지** (예: `server` block 자동 추가, `ssl_certificate` directive 삽입) — `C4` 는 동작 보장만 진술
- **manual mode 와 plugin mode 의 차이** — 본 raw 의 인용 범위 밖
- **Caddy auto-HTTPS 대비 운영 비교** — 본 raw 는 certbot 자체만 진술
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- `feature-keycloak-https-termination-caddy-nginx` 의 D3 에서 nginx + certbot 채택 시, host OS / installation method 의 scheduled task 형식 확인 — Ubuntu/Debian/RHEL 별 다름
- Keycloak 환경에서 `--deploy-hook="systemctl reload nginx"` 같은 hook 설정 — 본 raw 는 hook 의 존재만 보증, 구체 설정은 별도 검증
- renewal 실패 시 alert 메커니즘 — certbot 자체는 exit code 만 반환, alerting 은 별도 (systemd `OnFailure=` 등)
## 메모 / Notes
- `C5`**Certbot 4.0.0 변경점** — 이전 버전 (3.x 이하) 의 임계는 "만료 30일 전 (hard-coded)" 이었음. wiki/concepts 옮길 때 버전 명시 필수.
- `C2` 의 "Most Certbot installations" 는 **standard 패키지 매니저 경로** (apt/snap/dnf) 기준. source build / 수동 설치는 별도 scheduled task 구성 필요.
- D3 에서 "certbot renewal 은 zero-downtime" 같은 강한 진술 시 본 raw 로 보증 불가 — `--deploy-hook` 의 실제 동작 (예: `nginx -s reload` 의 graceful 여부) 은 nginx 측 보장.
## Related / 관련
- 같은 주제 다른 raw 자료: [[raw/official-docs/caddy-automatic-https-docs.md]] (auto-HTTPS 대안)
- 이 자료를 인용한 wiki 요약: (미작성)
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-keycloak-https-termination-caddy-nginx]]
- 인용하는 project: [[raw/project-notes/keycloak-patterns-overview]]
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/checkstyle-google-style-reference.md
@@ -0,0 +1,93 @@
---
title: "Checkstyle Google's Style Coverage Report (Official)"
source_type: official-doc
url: https://checkstyle.sourceforge.io/google_style.html
archive_url:
related_branches: [feature-static-analysis-quality-contract]
related_projects: [ca-skeleton, ca-tmpl]
tags: [official-doc, ca-skeleton, ci-cd, build-tooling]
created: 2026-06-15
---
# Checkstyle Google's Style Coverage Report (Official)
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-static-analysis-quality-contract]] | D2 — Checkstyle custom minimal ruleset 설계(naming/Javadoc/logical 잔존, formatting 검사는 formatter에 위임해 suppress). google_checks.xml 기준 모듈 분류 근거. |
## 출처 / Source
- 원본 URL: https://checkstyle.sourceforge.io/google_style.html
- 아카이브 URL: (미확보)
- 저자 / 조직: Checkstyle Project (sourceforge.io)
- 발행일: 2026-05-30 (Last Published)
- Checkstyle 버전: 13.5.0
- 대상 스타일 가이드 버전: 26 Apr 2025 (Google Java Style)
- 마지막 확인일: 2026-06-15
## 왜 저장했는지 / Why archived
feature-static-analysis-quality-contract D2에서 "Checkstyle custom minimal ruleset" 설계 시 어떤 모듈이 naming/Javadoc/formatting 영역에 각각 속하는지 공식 출처로 확인하기 위해 보관. google_checks.xml 을 기준 config로 참조하며, formatter(Spotless 등)와 겹치는 formatting 모듈(Indentation/LineLength/Whitespace 계열)을 suppress 대상으로 식별하는 근거로 활용.
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
> [§Key Naming Convention Checks / Type Names] "**TypeName** check validates class naming conventions but cannot determine grammatical categories (noun vs. adjective)."
> [§Key Naming Convention Checks / Method Names] "**MethodName** enforces naming patterns with noted false-negatives regarding underscores (issue #17841)."
> [§Javadoc Enforcement / Required Documentation] "- **MissingJavadocType**: Requires javadoc for types"
> [§Javadoc Enforcement / Required Documentation] "- **MissingJavadocMethod**: Requires javadoc for methods with exceptions for overrides and self-explanatory members"
> [§Overview] "The report was created for [Google Java Style](https://google.github.io/styleguide/javaguide.html) (version 26 Apr 2025) and references the configuration at `google_checks.xml`."
> [§Formatting Module Coverage / Indentation & Spacing] "**Indentation** check enforces \"+2 spaces\" block indentation and continuation line indentation (\"+4 spaces minimum\")."
> [§Formatting Module Coverage / Line Length] "**LineLength** enforces 100-character column limit with exceptions for URLs (http://, https://). Limitations include JSNI detection and long identifiers (issue #14938)."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| C1 | google_checks.xml 이 Google Java Style 을 Checkstyle 로 enforcement 하는 기준 config 이다 | [§Overview] "references the configuration at `google_checks.xml`" | `official-reference` | Checkstyle 13.5.0 + Google Java Style 26 Apr 2025 기준 | google_checks.xml 이 모든 프로젝트에서 그대로 사용 가능하다는 뜻이 아님 (custom suppress 필요 가능) |
| C2 | TypeName / MethodName 모듈이 naming convention 을 검사한다 | [§Naming] "**TypeName** check validates class naming conventions" / "**MethodName** enforces naming patterns" | `official-reference` | Checkstyle naming rule 설계 시 | 이 모듈들이 Google Java Style 의 *모든* naming 규칙을 완전히 검사함을 보장하지 않음(TypeName 은 grammatical category 미판별) |
| C3 | MissingJavadocType / MissingJavadocMethod 모듈이 Javadoc 필수 여부를 검사한다 | [§Javadoc / Required Documentation] "**MissingJavadocType**: Requires javadoc for types" / "**MissingJavadocMethod**: Requires javadoc for methods with exceptions for overrides and self-explanatory members" | `official-reference` | Javadoc 강제 ruleset 설계 시 | MissingJavadocMethod 는 overrides 및 self-explanatory 멤버에 예외가 있으므로 모든 메서드를 강제하지 않음 |
| C4 | Indentation / LineLength / WhitespaceAround 등 formatting 모듈은 formatter 도구와 중복 검사 영역이다 | [§Formatting] "**Indentation** check enforces \"+2 spaces\" block indentation..." / "**LineLength** enforces 100-character column limit..." / "**WhitespaceAround**: Partial coverage..." | `official-reference` | formatter(Spotless/google-java-format 등)와 Checkstyle 동시 사용 시 suppress 대상 선별 | 이 자료 자체가 "formatter 와 중복이면 suppress 해야 한다"고 명시하지는 않음 — 그 결정은 D2 의 설계 판단 |
| C5 | ParameterName / CatchParameterName / LambdaParameterName 등 로컬 변수 계열 모듈이 소문자 naming 을 강제한다 | [§Naming / Parameter and Local Variables] "**ParameterName**, **CatchParameterName**, **LambdaParameterName**, **RecordComponentName**, **LocalVariableName**, **PatternVariableName** enforce lowercase conventions" | `official-reference` | 로컬 변수·파라미터 naming 룰 설계 시 | 이 모듈들이 Google naming spec 의 모든 규칙(예: 1-char 변수 허용 범위)을 완전 커버하는지는 Coverage Report 상 별도 검증 필요 |
### Strength 허용값 (적용한 것)
- `official-reference` — 공식 reference/API 문서 (Checkstyle 프로젝트의 공식 coverage report)
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `C1`: google_checks.xml 이 공식 기준 config 라는 사실 (Checkstyle 13.5.0 / Google Java Style 26 Apr 2025 기준)
- `C2`: TypeName, MethodName 이 Checkstyle 내 naming 검사 모듈임
- `C3`: MissingJavadocType, MissingJavadocMethod 이 Javadoc 검사 모듈임 (단, 예외 조건 있음)
- `C4`: Indentation, LineLength, WhitespaceAround 계열이 formatting 검사 모듈임 — formatter 와 겹치는 영역
- `C5`: ParameterName 계열이 소문자 naming 을 강제함
- 이 자료가 증명하지 않는 것:
- formatter(Spotless/google-java-format) 와 Checkstyle 동시 사용 시 suppress 해야 한다는 정책 결정 (이는 D2 설계 판단)
- ca-tmpl 프로젝트에서 이 모듈들이 실제로 동작함 (별도 로컬 검증 필요)
- Checkstyle 이 Google Java Style 을 100% 커버함 (Coverage Report 는 미커버 항목을 빨간 ban 아이콘으로 명시)
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ca-tmpl 의 Gradle Checkstyle 플러그인이 google_checks.xml 을 올바르게 참조하는지 확인
- formatter suppress 전략: formatting 모듈(Indentation/LineLength/Whitespace 계열)을 SuppressionFilter 또는 SuppressWarningsFilter 로 suppress 하는 XML 설계
## 메모 / Notes
- 이 문서는 Google Style 에 대한 Checkstyle *coverage 분석* 보고서이며, Checkstyle 의 원본 check reference 문서가 아님. 개별 check 의 전체 파라미터 목록은 `https://checkstyle.sourceforge.io/checks/` 에서 별도 확인 필요.
- SuppressionFilter(`checkstyle-suppressions.xml`) 및 SuppressWarningsFilter(`@SuppressWarnings({"checkstyle:check_name"})`) 두 가지 suppress 메커니즘이 공식 제공됨 — formatting 모듈 suppress 설계 시 참조.
- 추가로 봐야 할 동일 출처 페이지: `https://checkstyle.sourceforge.io/checks/` (전체 check 목록), `https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml` (실제 config XML)
## Related / 관련
- 실제 config XML: `https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml`
- 이 자료를 인용한 branch-note: [[raw/branch-notes/feature-static-analysis-quality-contract]]
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/chrome-third-party-cookie-policy-google-official.md
@@ -0,0 +1,99 @@
---
title: official-doc / Google Privacy Sandbox — "Next steps for Privacy Sandbox and tracking protections in Chrome" (2025-04-22)
source_type: official-doc
url: https://privacysandbox.google.com/blog/privacy-sandbox-next-steps
archive_url:
related_branches: [feature-keycloak-spa-token-storage-tradeoff]
related_projects: [keycloak-patterns]
tags: [official-doc, keycloak-patterns, security, auth, chrome, third-party-cookie]
created: 2026-07-18
---
# Google Privacy Sandbox — Next steps for Privacy Sandbox and tracking protections in Chrome (2025-04-22)
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
> 본 자료는 Chrome/Google (browser vendor) 이 자사 블로그에 직접 게시한 정책 발표문 — vendor 공식 발표로 취급.
## source_type 허용값
frontmatter `source_type:` 에는 다음 중 하나만 사용:
- `official-doc` — 공식 레퍼런스 / 표준 / 사양 (예: Spring Boot reference, RFC, AWS docs)
- `company-tech-blog` — 대기업 엔지니어링 블로그 / 컨퍼런스 발표 글 (예: Stripe, Netflix, Toss, 카카오)
본 자료는 `official-doc` — Chrome 이라는 브라우저 자체를 만드는 vendor(Google) 가 그 브라우저의 정책 변경을 **공식적으로** 발표한 문서이기 때문 (사례 공유가 아니라 정책 발표).
## Parent / 활용 branch (필수, 최소 1개+)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-spa-token-storage-tradeoff]] | D3 — CORRECTS a common overclaim: Chrome did NOT roll out default third-party-cookie blocking. Google 의 2025-04-22 "next steps for Privacy Sandbox" 발표는 현재 접근 방식을 유지하고 새 standalone prompt 를 롤아웃하지 않는다고 명시 — 즉 Chrome 일반(비-Incognito) 모드에서는 여전히 third-party cookie 가 허용됨. "Chrome 이 3rd-party cookie 를 phasing out 하고 있다"는 사실처럼 서술하면 안 된다는 근거. |
## 출처 / Source
- 원본 URL: https://privacysandbox.google.com/blog/privacy-sandbox-next-steps
- 아카이브 URL: (미제공)
- 저자 / 조직: Anthony Chavez, VP, Privacy Sandbox (Google)
- 발행일: 2025-04-22 (본문에 "Published: April 22, 2025" 로 명시)
- 마지막 확인일: 2026-07-18
## 왜 저장했는지 / Why archived
`feature-keycloak-spa-token-storage-tradeoff` branch 의 D3 결정("silent renew 는 3rd-party cookie 제약으로 long-term 권장 안 함")이 근거 없이 "Chrome 3rd-party cookie phase-out" 을 기정사실처럼 인용하고 있었다 (branch 문서 상 `UNSUPPORTED_DECISION` 라벨). 본 자료는 그 전제 자체가 **더 이상 사실이 아님**을 vendor 공식 발표로 정정하는 근거 — Chrome 은 2025-04-22 기준 default third-party-cookie blocking 을 롤아웃하지 않기로 결정했다.
## 핵심 인용 / Key quotes (verbatim, 5문장)
> [본문 4문단] "we've made the decision to maintain our current approach to offering users third-party cookie choice in Chrome, and will not be rolling out a new standalone prompt for third-party cookies." — line 12 (in fetched text)
> [본문 4문단] "Users can continue to choose the best option for themselves in Chrome's Privacy and Security Settings." — line 12 (in fetched text)
> [본문 5문단] "We'll continue to enhance tracking protections in Chrome's Incognito mode, which already blocks third-party cookies by default." — line 14 (in fetched text)
> [본문 5문단] "This includes IP Protection, which we plan to launch in Q3 2025." — line 14 (in fetched text)
> [본문 2문단] "it remains clear that there are divergent perspectives on making changes that could impact the availability of third-party cookies." — line 10 (in fetched text)
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CHROME-3PC-C1 | Google 은 Chrome 에서 third-party cookie choice 를 제공하는 **현재 접근 방식을 유지**하기로 결정했고, third-party cookie 를 위한 새 standalone prompt 를 롤아웃하지 않는다 | [본문 4문단] "we've made the decision to maintain our current approach to offering users third-party cookie choice in Chrome, and will not be rolling out a new standalone prompt for third-party cookies." | `official-vendor-doc` | 2025-04-22 시점 Chrome 정책(공지 시점 기준) — 일반(비-Incognito) 브라우징 모드 | Chrome 이 third-party cookie 를 앞으로 **영구히** 차단하지 않겠다고 보장하는 것은 아님. 과거에도 phase-out 계획이 수차례 변경된 이력이 있음(§Usage Boundaries 참조) |
| CHROME-3PC-C2 | 사용자는 Chrome 의 Privacy and Security Settings 에서 계속 자신에게 맞는 옵션을 선택할 수 있다 — 즉 third-party cookie 차단은 **사용자가 켜야 하는 설정**이지 Chrome 의 기본값이 아님 | [본문 4문단] "Users can continue to choose the best option for themselves in Chrome's Privacy and Security Settings." | `official-vendor-doc` | Chrome 일반 모드의 설정 UX (opt-in 성격) | 이 설정의 실제 기본값(on/off), 신규 사용자 기준값, 사용자 채택률까지 증명하지는 않음 |
| CHROME-3PC-C3 | Chrome 의 **Incognito 모드는 이미 기본적으로 third-party cookie 를 차단**하고 있으며, Google 은 여기에 tracking protection 을 계속 강화한다 (IP Protection 포함) | [본문 5문단] "We'll continue to enhance tracking protections in Chrome's Incognito mode, which already blocks third-party cookies by default." | `official-vendor-doc` | Chrome Incognito(사생활 보호) 모드에 한정 | 일반(비-Incognito) 모드의 동작을 증명하지 않음 — 오히려 C1 이 그 반대(일반 모드는 유지)를 명시. Incognito 아닌 일반 모드까지 확대 해석 금지 |
| CHROME-3PC-C4 | IP Protection(Incognito 모드 tracking protection 기능)은 **2025년 3분기(Q3 2025) 출시 계획**이라고 명시 | [본문 5문단] "This includes IP Protection, which we plan to launch in Q3 2025." | `official-vendor-doc` | 공지 시점(2025-04-22) 기준 향후 계획 | 실제 Q3 2025 에 출시가 완료되었는지 여부는 본 자료(2025-04-22 시점 게시물)만으로 증명되지 않음 — forward-looking statement |
| CHROME-3PC-C5 | Google 은 publisher·developer·regulator·ad industry 등 ecosystem 이해관계자들 사이에 third-party cookie 가용성에 영향을 줄 변경에 대해 **여전히 상반된 입장(divergent perspectives)이 있다**고 밝히며, 이를 정책 유지 결정의 배경으로 제시 | [본문 2문단] "it remains clear that there are divergent perspectives on making changes that could impact the availability of third-party cookies." | `official-vendor-doc` | Chrome 3rd-party cookie 정책 변경 결정의 배경 설명 | 어떤 이해관계자가 정확히 무엇을 반대했는지, 각 요인의 가중치까지는 증명하지 않음 |
### Strength 허용값
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
- `official-vendor-doc` — Spring, Keycloak, AWS, Google 등 공식 벤더 문서
- `official-reference` — 공식 reference/API 문서
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
- `tutorial` — 튜토리얼/가이드. 일반화 금지
- `needs-confirmation` — 원문만으로는 적용 판단 불가
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `CHROME-3PC-C1`: 2025-04-22 기준 Chrome 은 default third-party-cookie blocking 을 롤아웃하지 않았고, 새 standalone prompt 도 도입하지 않았다. 즉 "Chrome 이 3rd-party cookie 를 없앴다/차단한다"는 진술은 **일반 모드에 한해 사실이 아니다**.
- `CHROME-3PC-C3`: Incognito 모드는 (이전부터, 그리고 계속) third-party cookie 를 기본 차단한다 — 이는 일반 모드와 별개의 사실.
- 이 자료가 증명하지 않는 것:
- Chrome 이 향후(예: 2026년 이후) third-party cookie 정책을 다시 바꾸지 않을 것이라는 보장. Google 의 Privacy Sandbox 타임라인은 2019년 최초 발표 이후 여러 차례 연기·변경되어 왔다 — 본 자료는 **2025-04-22 시점의 stated policy 스냅샷**일 뿐, 영구적 확정이 아니다.
- Safari(WebKit ITP)나 Firefox(ETP) 등 **다른 브라우저**의 third-party cookie 정책. 본 자료는 Chrome 에만 적용된다.
- `feature-keycloak-spa-token-storage-tradeoff` branch 의 silent renew(iframe + `prompt=none`) 가 **실제로 동작하는지** — 이 자료는 "Chrome 이 기본 차단하지 않는다"만 증명하며, Incognito 사용자 비율이나 개별 사용자가 수동으로 third-party cookie 차단 설정을 켰는지는 다루지 않는다.
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- `feature-keycloak-spa-token-storage-tradeoff` D3 재작성 시, "Chrome 이 3rd-party cookie 를 phase-out 하고 있다"는 전제를 제거하고, 대신 "Chrome 일반 모드는 기본 허용 / Incognito 모드는 기본 차단 / 사용자가 설정에서 수동 차단 가능" 이라는 3분기 조건으로 silent renew 리스크를 재서술해야 한다.
- Safari ITP 의 실제 동작(별도 vendor 공식 문서 필요)과 조합해야 branch D3 의 전체 위험도를 판단할 수 있다.
## 메모 / Notes
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
- 이 발표는 2024년에 있었던 "새로운 접근 방식을 탐색 중(exploring a new approach)"이라는 이전 발표(본문에 "last summer, we shared that we were exploring a new approach" 로 간접 언급됨)를 뒤집는 성격 — Privacy Sandbox 타임라인 변경 이력이 반복적임을 시사.
- silent renew 관련 branch 문서의 UNSUPPORTED_DECISION(D3) 을 이 자료로 보강할 때, "Incognito 모드에서는 여전히 차단됨(C3)"이라는 조건은 반드시 함께 서술해야 함 — 일반화 오류 방지.
## Related / 관련
- 같은 주제 다른 official-doc: (Safari ITP, Firefox ETP 공식 문서는 아직 raw 에 없음 — 필요 시 별도 dispatch)
- 이 자료를 인용한 wiki 요약: (아직 생성 안 됨)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/ci-github-actions-vs-gitlab-comparison.md
@@ -0,0 +1,107 @@
---
title: CI 비교 — GitHub Actions vs GitLab CI/CD (그리고 Jenkins / CircleCI / Tekton)
source_type: official-doc
url: https://docs.github.com/en/actions/learn-github-actions/migrating-from-gitlab-cicd-to-github-actions
archive_url:
status: raw
confidence: high
tags: [ci, github-actions, gitlab-ci, jenkins, tekton, devops, ca-skeleton, official-doc, branch:feature-ci-quality-gates-contract]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-ci-quality-gates-contract, feature-build-release-supply-chain-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# CI 비교 — GitHub Actions vs GitLab CI/CD (그리고 Jenkins / CircleCI / Tekton)
> Layer: `raw/official-docs/` — 공식 문서 발췌. CI provider 별 동일 개념(jobs/stages/needs)의 매핑 baseline.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-ci-quality-gates-contract]] | Gate ↔ Branch Contract Test 소유권 매트릭스의 backend 를 GitHub Actions 로 고정한 근거 (다른 provider 의 동일 개념 매핑) |
| [[raw/branch-notes/feature-build-release-supply-chain-contract]] | SBOM / Cosign / SLSA gate 가 어떤 provider step 으로 표현되는지의 이식성 baseline |
특정 branch 없이 foundational 조사로 수집한 경우:
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl 의 CI backend 의사결정의 외부 비교 기준
## 컨텍스트 / 왜 저장했는지
`feature-ci-quality-gates-contract` 가 release-blocking gate 매트릭스를 정의하는데, **gate 구현 backend 를 GitHub Actions 로 고정한 근거**가 필요합니다. matrix job, required check, workflow status 의존성 (`needs:`, `if: success()`) 은 provider 별로 모델이 다르므로, 다른 provider 에서의 동일 개념을 raw 로 확보해 두면 향후 이식 시 비용을 추정할 수 있습니다.
## 출처 / Source
- 원본 URL:
- GitHub Actions migration guide — https://docs.github.com/en/actions/learn-github-actions/migrating-from-gitlab-cicd-to-github-actions
- GitLab CI/CD pipelines — https://docs.gitlab.com/ee/ci/pipelines/
- Jenkins Declarative Pipeline — https://www.jenkins.io/doc/book/pipeline/syntax/
- CircleCI configuration reference — https://circleci.com/docs/configuration-reference/
- Tekton Pipelines overview — https://tekton.dev/docs/pipelines/
- 아카이브 URL: (미수집)
- 저자 / 조직: GitHub Docs, GitLab Docs, Jenkins Project, CircleCI, CD Foundation (Tekton)
- 발행일: 공식 문서 (지속 갱신, fetched 2026-05-27)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [GitHub Docs §Introduction — Migrating from GitLab CI/CD] "GitLab CI/CD and GitHub Actions both allow you to create workflows that automatically build, test, publish, release, and deploy code."
> [GitHub Docs §Jobs — needs key] "GitLab CI/CD also has a concept of `stages`, where jobs in a stage run concurrently, but the next stage will start when all the jobs in the previous stage have completed. You can recreate this scenario in GitHub Actions with the `needs` key."
> [GitHub Docs §Jobs — needs key] "Job dependencies in GitHub Actions can be specified explicitly with the `needs` key."
> [GitHub Docs §Scripts] "In GitLab CI/CD, script steps are specified using the `script` key. In GitHub Actions, all scripts are specified using the `run` key."
> [Jenkins Handbook §Pipeline syntax] "Declarative Pipeline … presents a more simplified and opinionated syntax on top of the Pipeline sub-systems. … The `agent` directive tells Jenkins where and how to execute the Pipeline, and is required in a declarative pipeline."
> [Tekton §Pipelines overview] "A `Pipeline` is a collection of `Tasks` that you define and arrange in a specific order of execution as part of your continuous integration flow. Each `Task` … runs as a Pod on your Kubernetes cluster."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CIGG-C1 | GitHub Actions 와 GitLab CI/CD 는 모두 build/test/publish/release/deploy 를 자동화하는 workflow 작성을 지원한다 | [GitHub Docs §Introduction — Migrating from GitLab CI/CD] "GitLab CI/CD and GitHub Actions both allow you to create workflows that automatically build, test, publish, release, and deploy code." | `official-vendor-doc` | 두 provider 의 일반 워크플로우 능력 비교 | 두 provider 의 marketplace / extension 생태계가 동등하다는 뜻은 아님 — capability 만 동등 |
| CIGG-C2 | GitLab 의 `stages` (같은 stage 의 job 은 concurrent, 다음 stage 는 이전 stage 완료 후 시작) 는 GitHub Actions 의 `needs` key 로 재현 가능 | [GitHub Docs §Jobs — needs key] "GitLab CI/CD also has a concept of `stages`, where jobs in a stage run concurrently, but the next stage will start when all the jobs in the previous stage have completed. You can recreate this scenario in GitHub Actions with the `needs` key." | `official-vendor-doc` | GitHub Actions ↔ GitLab CI/CD stage 모델 이식 | `needs` 가 GitLab `stages` 의 모든 의미를 1:1 로 보존한다는 뜻은 아님 — `interruptible:` / `manual` 등 GitLab-specific keyword 는 별도 매핑 필요 |
| CIGG-C3 | GitHub Actions 의 job dependency 는 `needs` key 로 명시한다 | [GitHub Docs §Jobs — needs key] "Job dependencies in GitHub Actions can be specified explicitly with the `needs` key." | `official-vendor-doc` | GitHub Actions YAML 작성 | `needs` 의 fan-in/fan-out 시 status 전파 규칙 (e.g., `if: always()`) 의 정확한 의미는 본 인용에 명시 없음 |
| CIGG-C4 | GitLab CI/CD 의 `script` key 는 GitHub Actions 에서 `run` key 로 매핑된다 | [GitHub Docs §Scripts] "In GitLab CI/CD, script steps are specified using the `script` key. In GitHub Actions, all scripts are specified using the `run` key." | `official-vendor-doc` | shell script 의존 step 의 단순 이식 | `before_script` / `after_script` (GitLab) 의 GitHub 대응 (pre/post composite action, setup steps) 은 본 인용 범위 밖 |
| CIGG-C5 | Jenkins Declarative Pipeline 은 Pipeline sub-system 위의 단순화된 syntax 이며 `agent` directive 가 필수 (실행 위치/방법 지정) | [Jenkins Handbook §Pipeline syntax] "Declarative Pipeline … presents a more simplified and opinionated syntax on top of the Pipeline sub-systems. … The `agent` directive tells Jenkins where and how to execute the Pipeline, and is required in a declarative pipeline." | `official-vendor-doc` | Jenkins Declarative Pipeline 작성 | Scripted Pipeline (`node('label') { ... }`) 의 차이 / plugin 호환성은 본 인용 범위 밖 |
| CIGG-C6 | Tekton 의 `Pipeline``Task` 들의 collection 이며, 각 `Task` 는 Kubernetes cluster 의 Pod 로 실행된다 | [Tekton §Pipelines overview] "A `Pipeline` is a collection of `Tasks` that you define and arrange in a specific order of execution as part of your continuous integration flow. Each `Task` … runs as a Pod on your Kubernetes cluster." | `official-vendor-doc` | Tekton CI/CD 운영 모델 (k8s-native) | self-hosted Kubernetes 비용 모델이 GitHub Actions runner 와 동등하다는 뜻은 아님 — infra 비용은 별도 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CIGG-C1`: 두 major provider 가 동일 워크플로우 카테고리 (build/test/publish/release/deploy) 를 지원한다는 사실
- `CIGG-C2`, `CIGG-C3`: `stages``needs` 매핑이 GitHub 공식 문서에 명시되어 있다는 사실
- `CIGG-C4`: `script``run` 의 명시적 매핑
- `CIGG-C5`: Jenkins Declarative Pipeline 의 `agent` 필수 요건
- `CIGG-C6`: Tekton 의 k8s-Pod 기반 실행 모델
- **이 자료가 증명하지 않는 것**:
- 각 provider 의 비용 / SLA / 가용성 비교
- 어떤 provider 가 ca-tmpl 에 best fit 인가 — 이 결정은 별도 ADR 필요
- matrix job 의 정확한 표현 차이 (`strategy.matrix` vs `parallel: matrix:` vs `axes`)
- reproducible build 보장 수준 (Nix / Bazel / cosign 등 별도 도구)
- 어떤 provider 가 SLSA Level 3+ certification 을 가진가 (별도 SLSA 문서 확인)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 quality-gate workflow 가 `needs: [contract-test, openapi-check, sbom-attest]` 형태로 표현되는지의 실제 yaml 검증
- 향후 GitLab 이식 시 `interruptible:` / `rules:if:` / `parallel:matrix:` 의 GitHub Actions equivalence 매핑 완성도
- Tekton 이식의 infra 비용 (self-hosted k8s cluster 운영) 추정
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- GitHub Actions: `needs:` + `if: success()` 조합으로 contract test gate ↔ release-blocking 매트릭스를 단일 yaml 에서 강제 가능. branch note 의 "workflow yaml 의 `needs: [contract-test]` 의존성" 가설과 일치.
- GitLab CI: `rules:` + `needs:` + `interruptible:` 조합이 GitHub 의 `if:` + `needs:` + `concurrency:` 에 매핑. matrix 는 `parallel: matrix:` 키워드.
- Jenkins: declarative pipeline 의 `post { failure { ... } }` 는 GitHub 의 `if: failure()` step 에 해당. 단, plugin 의존도가 높아 reproducible build 와 충돌 위험.
- Tekton: k8s-native 라 self-hosted runner 비용 모델이 다름. ca-skeleton 단계에는 과한 인프라.
- CircleCI / Buildkite / Drone: 상용/소형 팀 옵션. ca-tmpl 의 default 를 GitHub Actions 로 잡되, **gate 정의는 provider-agnostic** 하게 작성해야 이식 가능.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/ci-openapi-snapshot-diff-tooling]] — OpenAPI drift gate 도구 체인
- 적용 branch-note:
- [[raw/branch-notes/feature-ci-quality-gates-contract]] — Gate ↔ Branch Contract Test 소유권 매트릭스의 backend 선택 근거
- [[raw/branch-notes/feature-build-release-supply-chain-contract]] — SBOM / Cosign / SLSA gate 가 어떤 provider step 으로 표현되는지
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/ci-openapi-snapshot-diff-tooling.md
@@ -0,0 +1,108 @@
---
title: OpenAPI snapshot diff — springdoc / openapi-diff / Tufin oasdiff
source_type: official-doc
url: https://springdoc.org/
archive_url:
status: raw
confidence: high
tags: [ci, openapi, contract-test, api-versioning, ca-skeleton, official-doc, branch:feature-ci-quality-gates-contract]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-ci-quality-gates-contract, feature-api-compatibility-deprecation-contract, feature-schema-serialization-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# OpenAPI snapshot diff — springdoc / openapi-diff / Tufin oasdiff
> Layer: `raw/official-docs/` — 공식 문서 발췌. OpenAPI snapshot generation + diff 의 도구 체인 baseline.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-ci-quality-gates-contract]] | OpenAPI drift gate — "ground truth = code-generated snapshot (springdoc 등). hand-maintained 는 forbidden." 결정의 도구 근거 |
| [[raw/branch-notes/feature-api-compatibility-deprecation-contract]] | breaking change catalog row + `intent:breaking-change-approved` label escape hatch 의 자동 검출 backend |
| [[raw/branch-notes/feature-schema-serialization-contract]] | schema drift gate 가 같은 도구 체인 (oasdiff / openapi-diff) 을 공유 가능하다는 사실 |
특정 branch 없이 foundational 조사로 수집한 경우:
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl 의 API contract test 도구 선정 자료
## 컨텍스트 / 왜 저장했는지
`feature-ci-quality-gates-contract` 결정 "OpenAPI drift ground truth = code-generated snapshot (springdoc 등). hand-maintained 는 forbidden." 의 도구 근거. `./gradlew openapiCheckSnapshot` 이 실재 가능한 task 인지, breaking change 판정을 어떤 도구가 어떻게 하는지 raw 로 확보.
## 출처 / Source
- 원본 URL:
- springdoc-openapi — https://springdoc.org/
- OpenAPITools/openapi-diff (Maven Central + GitHub) — https://github.com/OpenAPITools/openapi-diff
- Tufin/oasdiff — https://github.com/Tufin/oasdiff
- OpenAPI Specification 3.1 — https://spec.openapis.org/oas/v3.1.0
- 아카이브 URL: (미수집)
- 저자 / 조직: springdoc community, OpenAPITools, Tufin, OpenAPI Initiative
- 발행일: 공식 문서 (지속 갱신, fetched 2026-05-27)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [springdoc-openapi §How it works] "springdoc-openapi works by examining an application at runtime to infer API semantics based on spring configurations, class structure and various annotations."
> [springdoc-openapi §Features] "Automatically generates documentation in JSON/YAML and HTML format APIs."
> [springdoc-openapi §Features] "The library uses spring-boot application auto-configured packages to scan for the following annotations in spring beans: OpenAPIDefinition and Info."
> [OpenAPITools/openapi-diff README] "Compare two OpenAPI specifications (3.x) and render the difference to HTML plain text, Markdown files, or JSON files."
> [Tufin/oasdiff README] "Command-line and Go package to compare and detect breaking changes in OpenAPI specs."
> [Tufin/oasdiff README §Usage] "Swap `changelog` for `breaking` to see only breaking changes, or `diff` for the full machine-readable diff."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CIOS-C1 | springdoc-openapi 는 runtime 에 application 을 검사하여 spring configuration / class 구조 / annotation 으로부터 API semantic 을 추론한다 | [springdoc-openapi §How it works] "springdoc-openapi works by examining an application at runtime to infer API semantics based on spring configurations, class structure and various annotations." | `official-vendor-doc` | Spring Boot + springdoc-openapi 환경 | runtime 검사이므로 dynamic routing (e.g., WebFlux functional routes) 의 일부가 누락될 수 있음 — 인용은 누락 가능성을 직접 언급하지 않음 |
| CIOS-C2 | springdoc-openapi 는 JSON / YAML / HTML 형식으로 자동 문서 생성을 지원한다 | [springdoc-openapi §Features] "Automatically generates documentation in JSON/YAML and HTML format APIs." | `official-vendor-doc` | API docs 생성 워크플로우 | 어떤 endpoint (`/v3/api-docs`, `/swagger-ui.html`) 에 노출되는지의 정확한 path 는 본 인용에 없음 |
| CIOS-C3 | springdoc-openapi 는 Spring Boot auto-configured package 를 사용하여 Spring bean 의 `OpenAPIDefinition` / `Info` annotation 을 스캔한다 | [springdoc-openapi §Features] "The library uses spring-boot application auto-configured packages to scan for the following annotations in spring beans: OpenAPIDefinition and Info." | `official-vendor-doc` | Spring Boot auto-configuration 활성 환경 | non-Spring-Boot (plain Spring) 에서의 동작은 본 인용 범위 밖 |
| CIOS-C4 | OpenAPITools/openapi-diff 는 두 OpenAPI 3.x 사양을 비교하고 HTML / plain text / Markdown / JSON 형식으로 차이를 렌더링한다 | [OpenAPITools/openapi-diff README] "Compare two OpenAPI specifications (3.x) and render the difference to HTML plain text, Markdown files, or JSON files." | `official-vendor-doc` | OpenAPI 3.x snapshot 비교 시나리오 | breaking vs non-breaking 의 정확한 판정 규칙은 본 인용에 명시 없음 — README 의 별도 섹션에서 확인 필요 |
| CIOS-C5 | Tufin/oasdiff 는 OpenAPI 사양의 비교와 breaking change 검출을 위한 CLI + Go package 이다 | [Tufin/oasdiff README] "Command-line and Go package to compare and detect breaking changes in OpenAPI specs." | `official-vendor-doc` | CI 통합 (CLI 호출) 또는 Go application 임베드 | exit code 가 breaking 시 non-zero 인지의 정확한 동작은 본 인용에 명시 없음 — `breaking` 서브명령의 정확한 exit semantic 확인 필요 |
| CIOS-C6 | oasdiff 는 `changelog` (전체 변화) / `breaking` (breaking only) / `diff` (machine-readable) 3가지 서브명령을 제공한다 | [Tufin/oasdiff README §Usage] "Swap `changelog` for `breaking` to see only breaking changes, or `diff` for the full machine-readable diff." | `official-vendor-doc` | oasdiff CLI 호출 패턴 설계 | 각 서브명령의 출력 schema / JSON 구조는 본 인용 범위 밖 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CIOS-C1` ~ `C3`: springdoc-openapi 의 runtime introspection 동작 원리 및 출력 형식
- `CIOS-C4`: OpenAPITools/openapi-diff 가 OpenAPI 3.x 비교 + 다중 포맷 렌더링을 지원한다는 사실
- `CIOS-C5`, `CIOS-C6`: oasdiff 가 CLI + Go package 형태로 breaking change 검출을 제공하며 3개 서브명령을 가진다는 사실
- **이 자료가 증명하지 않는 것**:
- 두 diff 도구 (openapi-diff vs oasdiff) 의 정확한 breaking change 판정 규칙 차이 (어떤 변경을 breaking 으로 보는가)
- springdoc 이 WebFlux functional routes 또는 Spring Cloud Gateway 의 dynamic route 를 어떻게 처리하는가
- `./gradlew openapiCheckSnapshot` 같은 Gradle task 가 어떤 plugin 으로 구현되는가 (springdoc-openapi-gradle-plugin 의 정확한 task 이름과 config 는 별도 페이지)
- 두 도구의 CI exit code semantic — `--fail-on-breaking` 같은 flag 의 존재 여부
- OpenAPI 3.1 vs 3.0 spec 차이가 두 도구의 동작에 미치는 영향
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 build.gradle 에 springdoc-openapi-gradle-plugin 추가 시 정확한 task 명 (`generateOpenApiDocs` 추정)
- 어느 diff 도구를 채용할지 — oasdiff (Go binary, k8s-friendly) vs openapi-diff (Maven Central, JVM-native 통합 용이) 선택 기준
- breaking change 정의 정책 — "intent:breaking-change-approved" label escape hatch 와 도구 exit code 의 연결
- `openapi-snapshot.yaml` 의 checkin 위치 및 PR diff review 워크플로우
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 실행 가능한 체인:
1. springdoc 이 런타임에 `/v3/api-docs` 생성 → Gradle task 가 build 시점에 파일로 dump.
2. Tufin/oasdiff 또는 OpenAPITools/openapi-diff 로 `openapi-snapshot.yaml` (checked-in) vs build artifact 비교.
3. breaking change 1건이라도 있으면 exit code != 0 → CI fail. **단, exit code semantic 은 도구별 flag 확인 필요** (`CIOS-C5` 가 직접 보장하지 않음).
- ca-tmpl 결정의 "`./gradlew openapiCheckSnapshot` exit code 0 verify" 는 위 체인을 한 Gradle task 로 합성하면 성립. Spring Initializr 기본 archetype 에는 없으므로 별도 task 정의 필요.
- 함정: springdoc 은 controller annotation 을 정적 추출하므로 dynamic routing (예: webflux functional routes) 이 있으면 누락 위험. branch note 의 "ground truth" 라는 표현은 이 범위 내에서만 참 — **본 springdoc 공식 페이지는 누락 위험을 직접 명시하지 않음, 일반적 통념**.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/ci-github-actions-vs-gitlab-comparison]] — CI backend 매핑
- 적용 branch-note:
- [[raw/branch-notes/feature-ci-quality-gates-contract]] — OpenAPI drift gate
- [[raw/branch-notes/feature-api-compatibility-deprecation-contract]] — breaking change catalog row + `intent:breaking-change-approved` label escape hatch
- [[raw/branch-notes/feature-schema-serialization-contract]] — schema drift gate 가 같은 도구 체인을 공유 가능
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cloudevents-spec-required-attributes.md
@@ -0,0 +1,94 @@
---
title: "CloudEvents Specification v1.0.2 — REQUIRED Context Attributes"
source_type: official-doc
url: https://raw.githubusercontent.com/cloudevents/spec/v1.0.2/cloudevents/spec.md
archive_url:
vendor: CNCF (Cloud Native Computing Foundation) / CloudEvents Working Group
related_branches: [feature-domain-event-outbox-contract]
related_projects: [ca-skeleton]
tags: [official-doc, ca-skeleton, messaging, cloudevents, outbox-pattern, domain-event, event-schema]
created: 2026-06-11
---
# CloudEvents Specification v1.0.2 — REQUIRED Context Attributes
> Layer: `raw/` — CNCF CloudEvents 공식 표준 사양(v1.0.2)의 원문 발췌·출처 기록.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-domain-event-outbox-contract]] | D12(신규) — "event envelope required fields = eventId, occurredAt, aggregateId, eventType, correlationId, idempotencyKey" 라는 ca-tmpl 내부 required-field 결정을 업계 표준 event envelope (CloudEvents REQUIRED attributes: id, source, specversion, type / OPTIONAL: time, subject 등) 과 대조하기 위한 표준 근거. correlationId / idempotencyKey 는 CloudEvents core spec 에 없는 extension attribute 임을 확인. |
## 출처 / Source
- 원본 URL: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md
- Raw URL: https://raw.githubusercontent.com/cloudevents/spec/v1.0.2/cloudevents/spec.md
- 아카이브 URL: (미수집)
- 저자 / 조직: CNCF CloudEvents Working Group
- 발행일: v1.0.2 (CloudEvents spec stable release)
- 마지막 확인일: 2026-06-11
## 왜 저장했는지 / Why archived
ca-tmpl outbox 계약(feature-domain-event-outbox-contract)의 판정 기준 표에 "Required fields: eventId, occurredAt, aggregateId, eventType, correlationId, idempotencyKey" 라는 내부 결정이 있다. 이 결정이 업계 표준 event envelope 과 어떻게 대응(mapping)되는지 — 그리고 correlationId / idempotencyKey 가 core spec 이 아닌 extension attribute 임 — 을 공식 근거로 확인하기 위해 보관.
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
> [§Context Attributes / REQUIRED Attributes, line 246] "The following attributes are REQUIRED to be present in all CloudEvents:"
> [§Context Attributes / REQUIRED Attributes / id, lines 251254] "Identifies the event. Producers MUST ensure that `source` + `id` is unique for each distinct event. If a duplicate event is re-sent (e.g. due to a network error) it MAY have the same `id`. Consumers MAY assume that Events with identical `source` and `id` are duplicates."
> [§Context Attributes / OPTIONAL Attributes / time, lines 419424] "Timestamp of when the occurrence happened. If the time of the occurrence cannot be determined then this attribute MAY be set to some other time (such as the current time) by the CloudEvents producer, however all producers for the same `source` MUST be consistent in this respect. In other words, either they all use the actual time of the occurrence or they all use the same algorithm to determine the value used."
> [§Context Attributes / Extension Context Attributes, lines 432437] "A CloudEvent MAY include any number of additional context attributes with distinct names, known as \"extension attributes\". Extension attributes MUST follow the same [naming convention](#attribute-naming-convention) and use the same [type system](#type-system) as standard attributes. Extension attributes have no defined meaning in this specification, they allow external systems to attach metadata to an event, much like HTTP custom headers."
> [§Context Attributes / OPTIONAL Attributes / subject, lines 389392] "This describes the subject of the event in the context of the event producer (identified by `source`). In publish-subscribe scenarios, a subscriber will typically subscribe to events emitted by a `source`, but the `source` identifier alone might not be sufficient as a qualifier for any specific event if the `source` context has internal sub-structure."
## Claims Extracted / 추출된 주장
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CLOUDEVT-C1 | CloudEvents REQUIRED attributes 는 정확히 4개: `id`, `source`, `specversion`, `type` | [§REQUIRED Attributes, line 246] "The following attributes are REQUIRED to be present in all CloudEvents:" (이어서 `id`, `source`, `specversion`, `type` 4개만 열거) | `official-vendor-doc` | CNCF CloudEvents v1.0.2 스펙을 따르는 모든 event envelope | CloudEvents 를 채택하지 않는 proprietary event envelope 의 필수 필드 구성에 대한 prescribe 아님 |
| CLOUDEVT-C2 | event 고유성 = `source` + `id` 조합. Producers 는 각 distinct event 에 대해 `source` + `id` 가 유일함을 보장해야 함. Consumers 는 동일 `source` + `id` 를 가진 event 를 중복으로 간주할 수 있음 | [§id, lines 251254] "Producers MUST ensure that `source` + `id` is unique for each distinct event. [...] Consumers MAY assume that Events with identical `source` and `id` are duplicates." | `official-vendor-doc` | CloudEvents v1.0.2 호환 시스템의 이벤트 deduplication 판정 | ca-tmpl 의 `idempotencyKey` 단독 중복 판정 근거로 사용 불가 — CloudEvents 는 `source+id` 조합을 기준으로 명시 |
| CLOUDEVT-C3 | `time` 은 OPTIONAL attribute. 값은 RFC 3339 포맷 Timestamp. occurrence 시점을 알 수 없으면 현재 시각으로 설정 가능하지만, 동일 `source` 의 모든 producer 는 이 결정에서 일관되어야 함 | [§time, lines 419424] "Timestamp of when the occurrence happened. If the time of the occurrence cannot be determined then this attribute MAY be set to some other time (such as the current time) by the CloudEvents producer, however all producers for the same `source` MUST be consistent in this respect." | `official-vendor-doc` | CloudEvents v1.0.2 의 `time` attribute semantics | ca-tmpl 의 `occurredAt` 필드가 "도메인 이벤트 발생 시각"인지 "저장 시각"인지의 의미론적 결정은 여기서 prescribe 되지 않음 |
| CLOUDEVT-C4 | `correlationId`, `idempotencyKey` 등 core spec 에 없는 메타데이터는 extension attribute 로 추가 가능. Extension attributes 는 core spec 과 동일한 naming convention + type system 을 따르며, spec 상 정의된 의미가 없음 | [§Extension Context Attributes, lines 432437] "A CloudEvent MAY include any number of additional context attributes with distinct names, known as \"extension attributes\". Extension attributes [...] have no defined meaning in this specification, they allow external systems to attach metadata to an event, much like HTTP custom headers." | `official-vendor-doc` | CloudEvents v1.0.2 extension 설계 원칙 | extension attribute 의 구체적 이름·의미·타입은 spec 이 prescribe 하지 않음 — ca-tmpl 의 `correlationId`/`idempotencyKey` 필드명이 "CloudEvents 표준"임을 증명하지 않음 |
| CLOUDEVT-C5 | `subject` 는 OPTIONAL attribute. producer(`source`) 컨텍스트 안에서 event 의 주체를 기술함. `source` 만으로는 내부 sub-structure 의 qualifier 가 부족할 때 사용 | [§subject, lines 389392] "This describes the subject of the event in the context of the event producer (identified by `source`). In publish-subscribe scenarios, a subscriber will typically subscribe to events emitted by a `source`, but the `source` identifier alone might not be sufficient as a qualifier for any specific event if the `source` context has internal sub-structure." | `official-vendor-doc` | pub-sub 시나리오에서 특정 resource (aggregateId 등) 를 event 의 subject 로 노출할 때 | `subject` 가 곧 `aggregateId` 라는 매핑은 spec 이 prescribe 하지 않음 — 해석(interpretation)임 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `CLOUDEVT-C1`: CloudEvents v1.0.2 의 REQUIRED attributes 는 `id`, `source`, `specversion`, `type` 4개뿐임.
- `CLOUDEVT-C2`: event deduplication 의 CloudEvents 표준 판정 키는 `source + id` 조합임. consumer 는 이 조합이 동일하면 중복으로 간주할 수 있음.
- `CLOUDEVT-C3`: `time` 은 OPTIONAL (RFC 3339). 값 부재 시 producer 는 현재 시각으로 채울 수 있으나 동일 source 내 일관성 요구.
- `CLOUDEVT-C4`: `correlationId`, `idempotencyKey` 는 CloudEvents core REQUIRED/OPTIONAL 목록에 없음. 이를 전달하려면 extension attribute 로 추가해야 하며, spec 은 이들의 의미를 정의하지 않음.
- `CLOUDEVT-C5`: `subject` 는 producer context 안의 event 주체 기술용 OPTIONAL attribute.
- 이 자료가 증명하지 않는 것:
- CloudEvents 스펙 준수 여부와 무관하게 ca-tmpl outbox 테이블 컬럼 구성이 어떠해야 하는지 — CloudEvents 는 전송(wire) envelope 명세이며, outbox storage column 설계는 prescribe 하지 않음.
- ca-tmpl 의 `eventId → id`, `occurredAt → time`, `eventType → type`, `aggregateId → subject/source` 매핑이 "올바른" 매핑임 — 이는 설계자의 interpretation이며, spec 이 강제하는 사항이 아님.
- `correlationId`/`idempotencyKey` 의 구체적 이름·스코프·TTL·dedup 메커니즘 — extension attribute 로 추가할 수 있다는 것만 증명, 구체 설계는 ca-tmpl 내부 결정.
- CloudEvents 를 ca-tmpl 에 직접 채택해야 한다는 결론 — 이 자료는 표준 대조용 근거이며, 채택 여부는 별도 결정.
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ca-tmpl `outbox` row 의 `eventId` 가 CloudEvents `id` semantics (source 스코프 내 유일) 를 실제로 만족하는지 — eventId 생성 전략(UUID v4/v7) 과 scope 검토 필요.
- `source` field 값 형식 결정 (URI-reference 필수) — ca-tmpl aggregate 별 source URI 패턴 미정.
- `correlationId`/`idempotencyKey` 를 CloudEvents extension attribute 로 전달하려면 naming convention (lowercase alphanum only) 준수 여부 확인 — `correlationId` (camelCase) 는 CloudEvents attribute 이름 규칙(`[a-z0-9]+` only) 위반임을 주의.
## 메모 / Notes
- CloudEvents attribute 이름 규칙: lowercase letters + digits only (`[a-z][a-z0-9]*`). `correlationId`, `idempotencyKey` 같은 camelCase 이름은 CloudEvents extension attribute 로 사용 불가 — `correlationid`, `idempotencykey` 로 내려야 함. 이 점은 ca-tmpl 필드명 설계 시 주의.
- `source + id` dedup 시맨틱은 consumer 가 "MAY assume" 수준 — 즉 dedup 구현 의무는 여전히 consumer 에게 있음. ca-tmpl D7 (consumer-side idempotency) 과 일관됨.
- `time` OPTIONAL 이지만 outbox 패턴에서는 `occurredAt` 을 항상 채우는 것이 practical — 모니터링·감사·replay 에 필수.
- CloudEvents JSON 예제(line 560572)에 `subject`, `comexampleextension1` 등 extension attribute 사용 패턴이 있음 — 참고 가치 있음.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/microservices-io-transactional-outbox]] — outbox 패턴 정의
- [[raw/official-docs/outbox-debezium-official-docs]] — CDC 기반 outbox 구현
- [[raw/official-docs/skip-locked-postgres-docs]] — PostgreSQL SKIP LOCKED (publisher leadership)
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/cloudevents-envelope-standard]]` (생성 시)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cloudflare-tunnel-routing-official.md
@@ -0,0 +1,101 @@
---
title: Cloudflare Tunnel — DNS routing & outbound-only connection (official)
source_type: official-doc
url: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
archive_url:
status: raw
confidence: high
tags: [keycloak-patterns, p3b-single-ec2-google, cloudflare-tunnel, cloudflared, public-uri, local-dev, oauth-callback]
related_projects: [keycloak-patterns]
related_branches: [feature-keycloak-patterns, feature-keycloak-single-ec2-google-federation]
created: 2026-05-25
last_reviewed: 2026-05-27
---
# Cloudflare Tunnel — Routing (공식)
> Layer: `raw/official-docs/` — Cloudflare 공식 문서의 **원문 발췌·출처 기록**.
> 단일 EC2 + Google federation에서 **EC2 inbound port를 열지 않고도** public HTTPS hostname을 노출하는 방법. ngrok 대안.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성.
## Parent / 활용 branch (필수)
> 이 자료는 혼자 존재하지 않는다. 어느 branch 의 어떤 결정의 근거인지 명시.
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-patterns]] | P3B (Single EC2 + Google federation) 변형에서 public HTTPS 노출 수단으로 Cloudflare Tunnel 후보 검토 근거 |
| [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] | EC2 inbound port 0 + 고정 hostname 요구 충족 수단으로 cloudflared 채택 근거 |
## 컨텍스트
P3B 단일 EC2에서 Google이 도달할 수 있는 public URL이 필요하지만, EC2 보안 그룹을 80/443 외부 개방하는 것은 학습 환경에서 부담스러울 수 있다. Cloudflare Tunnel(`cloudflared`)은 **EC2 → Cloudflare로 outbound 연결**만 사용 → inbound port 0개로 public hostname 노출 가능.
## 출처 / Source
- 원본 URL (메인): https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
- 원본 URL (DNS routing 세부): https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/routing-to-tunnel/dns/
- 아카이브 URL: (미수집 — 추후 archive.org 스냅샷 추가)
- 저자 / 조직: Cloudflare Inc. — Developers Documentation
- 발행일: rolling docs (페이지 자체에 명시 없음)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Outbound-only connections] "cloudflared initiates an outbound connection through your firewall from the origin to the Cloudflare global network."
> [§Outbound-only connections] "You can then configure your firewall to allow only these outbound connections and block all inbound traffic"
> [§DNS records and tunnel subdomains (routing-to-tunnel/dns/)] "When you create a tunnel, Cloudflare generates a subdomain at `<UUID>.cfargotunnel.com`."
> [§DNS records and tunnel subdomains (routing-to-tunnel/dns/)] "You point a CNAME record at this subdomain to route traffic from your hostname to the tunnel."
> needs-confirmation: 2026-05-25 작성 당시 인용된 "Published applications inherit the Cloudflare settings for their hostname, including cache rules, WAF rules, and other Rules configurations." 문장은 2026-05-27 재확인 시점에 메인/관련 sub-page 에서 발견되지 않음. 페이지 개정 또는 원본이 paraphrase였을 가능성. Cloudflare edge 가 zone 단위로 WAF/캐시 정책을 적용한다는 일반적 동작은 사실이지만, 본 자료의 **verbatim 근거로는 불가** — 별도 인용 필요.
## Claims Extracted / 추출된 주장
> 자료가 직접 말하는 것만 claim 으로 분리. 내 프로젝트에 적용한 결론은 여기 쓰지 않음.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CLOUDFLARE-TUNNEL-C1 | `cloudflared` 는 origin → Cloudflare global network 으로 **outbound** 연결을 개시한다 (inbound 불필요) | [§Outbound-only connections] "cloudflared initiates an outbound connection through your firewall from the origin to the Cloudflare global network." | `official-vendor-doc` | cloudflared 를 origin (예: EC2) 에서 실행하는 모든 시나리오 | 방화벽이 outbound 443 을 차단한 환경에서도 동작한다는 뜻은 아님. 또한 NAT/proxy 통과 보장은 별도 검증 필요 |
| CLOUDFLARE-TUNNEL-C2 | 방화벽을 outbound 만 허용하고 inbound 를 전부 차단하는 구성이 공식 권장 | [§Outbound-only connections] "You can then configure your firewall to allow only these outbound connections and block all inbound traffic" | `official-vendor-doc` | inbound port 노출을 피하려는 self-host / on-prem / EC2 | 모든 use case 에서 inbound 차단이 충분하다는 뜻은 아님 — Tunnel 외 다른 서비스 (예: SSH 관리 채널) 는 별도 정책 |
| CLOUDFLARE-TUNNEL-C3 | 터널 생성 시 Cloudflare 는 `<UUID>.cfargotunnel.com` 형태의 subdomain 을 자동 부여 | [§DNS records and tunnel subdomains] "When you create a tunnel, Cloudflare generates a subdomain at `<UUID>.cfargotunnel.com`." | `official-vendor-doc` | Cloudflare Tunnel 의 모든 tunnel | UUID 의 안정성 (재생성 시 동일성) 은 별도 항목, 본 인용으로 보장 안 됨 |
| CLOUDFLARE-TUNNEL-C4 | 사용자 도메인 hostname 에서 `<UUID>.cfargotunnel.com` 으로 CNAME 을 설정하면 트래픽이 터널로 라우팅됨 | [§DNS records and tunnel subdomains] "You point a CNAME record at this subdomain to route traffic from your hostname to the tunnel." | `official-vendor-doc` | Cloudflare 가 관리하는 zone 의 hostname | 다른 DNS provider 가 관리하는 zone 에서도 동일 동작한다는 뜻은 아님 ("`cfargotunnel.com` subdomain only proxies traffic for DNS records in the same Cloudflare account" 단서) |
| CLOUDFLARE-TUNNEL-C5 | `cloudflared tunnel route dns <UUID-or-NAME> <hostname>` 명령으로 locally-managed tunnel 의 DNS 라우팅을 자동 생성 가능 | [§DNS routing command] "`cloudflared tunnel route dns <UUID or NAME> www.app.com`" + "creates a CNAME record but does not proxy traffic unless the tunnel is running." | `official-vendor-doc` | locally-managed tunnel (config.yml 또는 CLI) | tunnel 이 running 상태가 아니면 트래픽이 흐르지 않음을 명시 — 라우팅 성공 ≠ tunnel 가용 |
| CLOUDFLARE-TUNNEL-C6 | OAuth callback URL 등 특정 use case 에 Cloudflare Tunnel 이 공식 권장이라는 직접 언급은 인용 범위 내에 **없음** | (인용 없음 — 부재 사실 자체가 claim) | `needs-confirmation` | Keycloak Google federation 의 redirect_uri 호스팅 시나리오 | Cloudflare Tunnel 이 OAuth callback 에 부적합하다는 뜻도 아님. 단지 공식 문서가 직접 보증하지 않는다는 사실 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CLOUDFLARE-TUNNEL-C1`, `C2`: cloudflared 가 outbound-only 모델로 동작하며 공식적으로 inbound 차단 구성을 권장
- `CLOUDFLARE-TUNNEL-C3`, `C4`, `C5`: tunnel UUID 기반 cfargotunnel.com subdomain + CNAME / `cloudflared tunnel route dns` 명령의 동작 메커니즘
- **이 자료가 증명하지 않는 것**:
- Keycloak `/realms/<r>/broker/google/endpoint` 같은 OAuth callback 경로가 Cloudflare Tunnel 환경에서 무수정 동작한다는 보장 (TLS 종단·proxy header 처리는 Keycloak `KC_PROXY_HEADERS` / `KC_HOSTNAME` 측 결정과 결합되어야 함)
- Cloudflare edge 의 WAF / 캐시 / Rules 가 tunnel-exposed 앱에 자동 적용된다는 점 (2026-05-25 인용은 verbatim 재확인 실패, `C6` 참조)
- 무료 plan 의 동시 connection 수 / bandwidth limit (정책 변경 잦음, 별도 가격 페이지 확인 필요)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- Keycloak 가 `X-Forwarded-Proto: https` 를 cloudflared 의 origin request 에서 정확히 받는지 (Cloudflare → origin tunnel 구간의 header 동작) — local 검증 필수
- Google Cloud Console 의 redirect URI 정책이 `cfargotunnel.com` 도메인을 그대로 허용하는지 (등록 도메인 verification 요구사항)
## P3B 함의 (내 프로젝트 해석)
> 본 섹션은 자료의 직접 인용이 아니라 P3B 결정 컨텍스트에서의 해석. wiki 추출 시 `wiki/concepts/` 또는 `wiki/projects/` 의 source-summary 로 옮겨야 함.
- Cloudflare 계정 + 무료 plan + Cloudflare에 등록된 도메인 1개 필요.
- EC2에 `cloudflared` 데몬 → `cloudflared tunnel run <tunnel-name>``kc.example.com` CNAME → `<UUID>.cfargotunnel.com` → Keycloak `:8080`.
- TLS는 **Cloudflare edge가 종단** → EC2 내부는 HTTP로 backend 통신 가능. Keycloak `KC_HTTP_ENABLED=true` + `KC_PROXY_HEADERS=xforwarded`.
- Google Cloud Console redirect URI: `https://kc.example.com/realms/dev/broker/google/endpoint` 그대로 사용 가능 (고정 hostname).
- ngrok 대비 장점: **hostname 고정** + 무료 + EC2 inbound port 0.
- 단점: Cloudflare에 등록된 도메인 1개 + DNS 설정 1회 필요 (학습 진입 비용은 ngrok보다 약간 큼).
## 메모 / Notes
- 2026-05-27 재검증: `## 핵심 인용` 의 cfargotunnel.com 인용은 메인 페이지가 아니라 `routing-to-tunnel/dns/` sub-page 에서 발견. 향후 인용 시 sub-URL 명시.
- 인용 시점에 있던 "Published applications inherit the Cloudflare settings…" 문장은 현재 부재 — 페이지 개정 또는 원본 paraphrase 가능성. `wiki/concepts/` 승급 시 본 항목을 근거로 사용 금지.
## Related / 관련
- 같은 주제 다른 official-doc: [[raw/official-docs/ngrok-http-tunnel-official]], [[raw/official-docs/google-oauth2-redirect-uri-validation-official]]
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-keycloak-patterns]], [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]]
- 이 자료를 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/compat-rfc-8594-sunset-header.md
@@ -0,0 +1,96 @@
---
title: RFC 8594 — The Sunset HTTP Header Field
source_type: official-doc
url: https://datatracker.ietf.org/doc/html/rfc8594
archive_url:
status: raw
confidence: high
tags: [ca-tmpl, api-compatibility, deprecation, sunset-header, rfc8594, http, official-doc, official-standard]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-api-compatibility-deprecation-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# RFC 8594 — The Sunset HTTP Header Field
> Layer: `raw/official-docs/` — IETF Standards Track 표준 (RFC 8594, 2019-05) 원문 발췌. ca-tmpl 이 deprecation marker 로 채택한 `Sunset` 헤더의 정의를 확정. paired Deprecation 헤더 (RFC 9745) 와의 사용 관계는 [[raw/official-docs/sunset-deprecation-headers-paired-usage]] 에서 별도 다룸.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-api-compatibility-deprecation-contract]] | ca-tmpl 의 deprecation marker 중 `Sunset` 헤더 송신의 IETF 표준 근거 — 헤더 값 포맷 (HTTP-date) 과 의미 (decommissioning 시점) 의 1차 정의 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl이 deprecation marker로 `OpenAPI deprecated:true + Sunset header`를 명시. 이 결정의 **표준 근거**가 RFC 8594. 헤더 값 포맷·의미·`sunset` link relation까지 확정해 두어야 verification suite가 OpenAPI diff + 응답 헤더 검사를 정확히 강제할 수 있음.
## 출처 / Source
- 원본 URL: https://datatracker.ietf.org/doc/html/rfc8594
- 아카이브 URL: (미수집)
- 저자/조직: IETF (Wilde)
- 발행일: 2019-05
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Abstract] "This specification defines the Sunset HTTP response header field, which indicates that a URI is likely to become unresponsive at a specified point in the future."
> [§3] "Sunset = HTTP-date" (예: `Sunset: Sat, 31 Dec 2018 23:59:59 GMT`)
> [§3] "The Sunset value is an HTTP-date timestamp, as defined in Section 7.1.1.1 of [RFC7231], and SHOULD be a timestamp in the future."
> [§3] "Clients SHOULD treat Sunset timestamps as hints: it is not guaranteed that the resource will, in fact, be available until that time and will not be available after that time."
> [§7.2] "Relation Name: sunset. Description: Identifies a resource that provides information about the context's retirement policy."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| RFC8594-C1 | `Sunset` HTTP response header field 는 URI 가 특정 미래 시점에 unresponsive 가 될 가능성을 알리는 표준 메커니즘 | [§Abstract] "This specification defines the Sunset HTTP response header field, which indicates that a URI is likely to become unresponsive at a specified point in the future." | `official-standard` | HTTP/1.1+ 응답 | "Sunset 시점에 client 가 자동으로 호출 중단해야 한다" 는 강제력은 본 spec 에 없음 — 단지 hint |
| RFC8594-C2 | `Sunset` 값은 RFC 7231 §7.1.1.1 의 HTTP-date 포맷이며 미래 시점이어야 함 (SHOULD); 예: `Sunset: Sat, 31 Dec 2018 23:59:59 GMT` | [§3] "Sunset = HTTP-date" + "The Sunset value is an HTTP-date timestamp, as defined in Section 7.1.1.1 of [RFC7231], and SHOULD be a timestamp in the future." | `official-standard` | Sunset 헤더 송신 시 | UNIX epoch 또는 ISO 8601 사용은 본 spec 위반. Deprecation 헤더 (RFC 9745) 는 다른 포맷 (Structured Field Date) 사용에 주의 |
| RFC8594-C3 | client 는 Sunset timestamp 를 hint 로 취급해야 함 (SHOULD); 해당 시점 전까지의 가용성 또는 그 이후의 비가용성이 강제되지는 않음 | [§3] "Clients SHOULD treat Sunset timestamps as hints: it is not guaranteed that the resource will, in fact, be available until that time and will not be available after that time." | `official-standard` | Sunset 헤더를 수신하는 client | client 가 Sunset 시점을 무시해도 된다는 뜻은 아님 — SHOULD 수준의 hint 처리 권고 |
| RFC8594-C4 | `sunset` link relation 은 retirement policy 정보를 제공하는 리소스를 식별; Link header 의 `rel="sunset"` 으로 추가 문서 (마이그레이션 가이드 등) 를 가리킴 | [§7.2] "Relation Name: sunset. Description: Identifies a resource that provides information about the context's retirement policy." | `official-standard` | Link header 와 함께 송신 시 | link target 의 미디어 타입 / 포맷은 강제되지 않음 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `RFC8594-C1` ~ `C3`: Sunset 헤더의 정의, HTTP-date 포맷, client hint 시맨틱
- `RFC8594-C4`: `sunset` link relation 의 IANA 등록 의미
- **이 자료가 증명하지 않는 것**:
- `Deprecation` 헤더 (RFC 9745) 의 정의 — 본 spec 은 §1.4 use case 로만 deprecation 언급, 헤더 정의는 RFC 9745 별도
- paired 사용 invariant (`Sunset >= Deprecation`) — RFC 9745 §4 에 정의됨 (별도 source 참조)
- client 라이브러리가 Sunset 을 실제로 감지/경고하는 동작 — spec 은 SHOULD hint 만 권고, 구현은 vendor 별
- migration window 의 적정 길이 (90d / 30d 등) — 본 spec 은 window 권고 없음
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 `Sunset` 헤더 송신 위치 (Spring filter / interceptor / ControllerAdvice)
- `Sunset` + `Deprecation` paired 송신은 [[raw/official-docs/sunset-deprecation-headers-paired-usage]] 의 결합 필요
- OpenAPI `deprecated: true` + Sunset header + CI gate 의 verification suite 구성
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 헤더 값은 **HTTP-date** (예: `Sat, 31 Dec 2018 23:59:59 GMT`). UNIX timestamp 아님.
- Sunset은 "예고"가 아니라 "이 시점 이후로는 unresponsive"의 의미. 즉 deprecate 시작 시점이 아니라 **removal 시점**.
- 별도 IETF spec `RFC 9745` (구 draft-ietf-httpapi-deprecation-header) 가 `Deprecation` 헤더를 정의. 관계는 `Sunset >= Deprecation` (Sunset 시점이 더 늦거나 같아야 함).
- ca-tmpl 매핑:
- `Deprecation` 헤더 = OpenAPI `deprecated: true` 표시와 같은 시점.
- `Sunset` 헤더 = migration window(90d / 30d) 종료 시점.
- 둘이 다른 의미이므로 동시에 보내야 정합.
- Trade-off:
- 표준 사용 장점: 외부 client 라이브러리(예: Spring HATEOAS, Apigee)가 헤더를 인식 가능. 운영 외부 통보 자동화에 활용.
- 표준 사용 단점: 표준 자체는 **client가 어떻게 행동해야 하는지** 강제하지 않음. 헤더만으로는 강제력 없음.
- 결론: ca-tmpl처럼 OpenAPI `deprecated:true` + breaking diff CI gate + 응답 헤더 3중을 함께 써야 강제력 확보.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/sunset-deprecation-headers-paired-usage]] — paired 사용 (RFC 8594 + RFC 9745 결합)
- canonical contract 섹션:
- [[raw/project-notes/ca-skeleton-operational-contract]] — API compatibility / deprecation marker
- 대안 그룹: **Group G-F — API evolution & schema / API compatibility / deprecation**
- 본 source의 위치: `채택 근거: Sunset header (IETF RFC 8594) — ca-tmpl deprecation marker`
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/config-12-factor-app-config.md
@@ -0,0 +1,105 @@
---
title: The Twelve-Factor App — III. Config (env-driven configuration 원칙)
source_type: official-doc
url: https://12factor.net/config
archive_url:
status: raw
confidence: high
tags: [ca-tmpl, config, env, twelve-factor, runtime-configuration]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-env-driven-runtime-configuration, feature-secrets-config-source-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# The Twelve-Factor App — III. Config
> Layer: `raw/official-docs/` — Twelve-Factor App methodology §III. Config 원문 발췌.
> ca-tmpl `feature-env-driven-runtime-configuration` branch의 이론적 근거 (canonical reference).
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-env-driven-runtime-configuration]] | env 기반 모든 운영 모드 전환 + `APP_` prefix 1택 + runtime reload 없음 결정의 1차 근거 (12-factor §III) |
| [[raw/branch-notes/feature-secrets-config-source-contract]] | secret/config 분리 결정 — 12-factor §III가 분리 자체는 정의하지 않으나 "credentials 포함 시 open source 불가" litmus test로 분리 필요성 시사 |
| [[raw/project-notes/ca-skeleton-operational-contract]] | env-driven runtime configuration 대안 평가 (5종)의 baseline 기준선 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-env-driven-runtime-configuration` branch의 **이론적 근거 (canonical reference)**. branch는 `APP_` prefix, env로 모든 운영 모드 전환, secret/config 분리를 핵심 결정으로 두었는데, 이 모든 원칙의 출처가 12-factor §III. Config. branch가 채택한 "env-driven runtime configuration" 자체가 12-factor의 직접 적용. 대안(Spring Cloud Config Server / k8s ConfigMap / LaunchDarkly / Consul KV / AWS AppConfig) 평가의 기준선으로도 사용.
## 출처 / Source
- 원본 URL: https://12factor.net/config
- 아카이브 URL: (미확보)
- 저자 / 조직: Adam Wiggins (Heroku 공동창업자) — Twelve-Factor App methodology
- 발행 시기: 2011 (v1), 현재까지 사실상의 클라우드 네이티브 표준
- 라이선스: CC BY-SA 3.0
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§III. Config — opening, 2026-05-27 verified] "Config varies substantially across deploys, code does not."
> [§III. Config — env vars principle, 2026-05-27 verified] "The twelve-factor app stores config in environment variables (often shortened to env vars or env)."
> [§III. Config — env vars principle, 2026-05-27 verified] "unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard."
> [§III. Config — litmus test, 2026-05-27 verified] "A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials."
> [§III. Config — granular controls, 2026-05-27 verified] "In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars."
> **재검증 완료 (2026-05-27)**: WebFetch 권한 복구 후 https://12factor.net/config 원본에서 위 5개 인용 모두 verbatim 일치 확인. 단 quote 3번은 원문이 소문자 "unlike"로 시작 (이전 캡처는 문장 시작점으로 추정해 대문자 "Unlike"로 적었으나 실제 원문은 앞 문장과 이어지는 형태). Strength `needs-confirmation` → `official-reference` 로 격상 (12-factor 는 Adam Wiggins 의 manifesto 로 formal W3C/ISO standard 가 아니므로 `official-standard` 가 아니라 `official-reference` 사용).
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| TWELVE-FACTOR-CONFIG-C1 | config 는 deploy 마다 크게 달라지지만 code 는 그렇지 않다 (deploy 간 가변성의 분리 원칙) | [§III. Config] "Config varies substantially across deploys, code does not." | `official-reference` | dev / staging / prod 등 여러 deploy 환경을 갖는 모든 앱 | "config 의 정의" (DB URL · credential · per-deploy hostname 등) 가 무엇인지의 정확한 경계는 본 인용에 명시 없음 — 별도 §III 본문 참조 필요 |
| TWELVE-FACTOR-CONFIG-C2 | Twelve-Factor App 은 **환경 변수 (env vars)** 에 config 를 저장한다 | [§III. Config] "The twelve-factor app stores config in environment variables (often shortened to env vars or env)." | `official-reference` | Twelve-Factor 를 따르는 모든 앱 | 다른 메커니즘 (config file, system property) 의 절대 금지가 아니라 1차 권장이라는 뉘앙스. 환경변수 외 저장이 12-factor 위반이라는 강한 진술은 본 인용 범위 밖 |
| TWELVE-FACTOR-CONFIG-C3 | env vars 는 custom config file 이나 Java System Properties 와 달리 **언어·OS 중립 표준** | [§III. Config] "unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard." | `official-reference` | 다언어 / 다플랫폼 배포 환경 | "language-agnostic" 이 항상 동일한 의미 (예: Windows 환경변수 대소문자) 라는 뜻은 아님 — POSIX 표준 기준 |
| TWELVE-FACTOR-CONFIG-C4 | config 분리의 litmus test = codebase 를 언제든 오픈소스화해도 credential 이 노출되지 않아야 한다 | [§III. Config] "A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials." | `official-reference` | secret · credential 을 포함하는 앱의 config 분리 평가 | secret 을 env vars 에 두는 것이 충분하다는 뜻은 아님 — 본 인용은 분리 기준만 정의, 안전한 secret 저장소 (Vault · Secrets Manager) 의 필요성 자체는 별도 |
| TWELVE-FACTOR-CONFIG-C5 | env vars 는 **granular controls** 이며 각 env var 는 다른 env var 와 fully orthogonal | [§III. Config] "In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars." | `official-reference` | 모든 env var 정의 시 grouping 결정 | 명시적 grouping (예: `APP_*`, `DB_*` prefix) 권장 / 금지 진술은 본 인용 범위 밖 — 12-factor 본문은 grouping 권장하지 않으나 실무 prefix 규약은 자체 결정 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `TWELVE-FACTOR-CONFIG-C1`~`C5`: 12-factor §III 가 정의한 env-driven config 의 5개 원칙 (분리 / env vars 저장 / 언어 중립 / litmus test / granular orthogonality)
- **이 자료가 증명하지 않는 것**:
- runtime reload 메커니즘 — 12-factor §III 본문은 reload 정책을 정의하지 않음. 환경변수가 프로세스 시작 시 1회만 읽히는 POSIX 표준 동작은 별도 사실 (POSIX 표준 다른 자료에서 확인 필요)
- secret 과 non-secret 의 분리 — `C4` 의 litmus test 는 분리 기준만 정의, 분리 메커니즘 (별도 secret manager) 은 12-factor §III 가 명시하지 않음
- prefix 규약 (`APP_*`, `DB_*`) — 12-factor 본문은 grouping 을 권장하지 않음. ca-tmpl 의 `APP_` prefix 결정은 branch 자체 정합성 규칙
- boolean / Duration 의 표기 표준 (예: `true/false` only, `30s` 1택) — 본 자료 범위 밖
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 `APP_` prefix 와 12-factor "orthogonal granular controls" 의 충돌 여부 검토 (prefix grouping 이 orthogonality 를 약화시키는지)
- Spring Boot 의 `application.yml` + `${ENV:default}` 패턴이 12-factor 와 정합하는 정확한 조건 (외부 yml 파일이 config file 인가 env vars 의 default 인가)
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 적용 컨텍스트 해석.
- 적용 시나리오: 모든 cloud-native 백엔드. 특히 build artifact 1개를 dev/staging/prod에 재배포(immutable build)하는 환경.
- 장점: **표준성**. 어떤 언어/런타임에서도 동일하게 적용. Kubernetes, Heroku, Docker, ECS, Cloud Run 모두 환경변수를 1차 진입점으로 둠. Spring Boot의 `application.yml` + `${ENV:default}` 패턴도 이와 정합.
- 단점: env 키가 수십~수백 개가 되면 관리가 어려워짐. 그래서 12-factor 자체는 grouping을 권장하지 않지만 실제로는 prefix 규약 (예: `APP_*`, `DB_*`)이 필요. branch가 `APP_` prefix 1택을 결정한 이유.
- 한계: 12-factor는 **runtime reload** 메커니즘을 정의하지 않음. 환경변수는 프로세스 시작 시 1회 읽힘 (POSIX 표준). 따라서 "no runtime reload" 가 사실상 12-factor의 묵시적 default이며, branch의 "reload policy = no runtime reload" 결정과 일치.
- secret과 non-secret을 같은 env 공간에 두는가? 12-factor는 분리하지 않음. 하지만 branch는 `feature-secrets-config-source-contract`로 분리. 이는 12-factor를 보강하는 결정.
- ca-tmpl branch와의 직접 매핑:
- `APP_` prefix → 12-factor §III "granular controls" + branch convention.
- Duration `30s` 1택 → 12-factor 본문에는 없음. branch가 추가한 가독성 규칙.
- boolean `true/false` only → 12-factor 본문에는 없음. branch가 추가한 정합성 규칙.
- 신뢰도: `official-doc` 등급 (12-factor manifesto = `official-reference` strength, not `official-standard` since not a formal W3C/ISO/IETF standard). 2026-05-27 WebFetch 재검증으로 5/5 quote verbatim 확인 완료.
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/official-docs/config-spring-cloud-config-server-official]]
- [[raw/company-tech-blogs/config-launchdarkly-feature-flag-best-practice]]
- 인용하는 branch:
- [[raw/branch-notes/feature-env-driven-runtime-configuration]]
- [[raw/branch-notes/feature-secrets-config-source-contract]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 대안 그룹: **Group G — Env-driven runtime configuration** (대안 5종: Spring Cloud Config Server / k8s ConfigMap+Spring Cloud Kubernetes / Consul KV / AWS Parameter Store·AppConfig / LaunchDarkly·Unleash)
- 본 source의 위치: **기준선 (baseline)** — 다른 모든 대안은 12-factor에 무엇을 더하고 무엇을 비싸게 하는가의 관점에서 평가.
- 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/config-aws-appconfig-feature-flag-deployment.md
@@ -0,0 +1,111 @@
---
title: AWS AppConfig — feature flag + dynamic configuration 공식 문서
source_type: official-doc
url: https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html
archive_url:
status: raw
confidence: high
tags: [ca-tmpl, config, feature-flag, aws-appconfig, dynamic-configuration, alternative, official-doc]
related_branches: [feature-env-driven-runtime-configuration, feature-secrets-config-source-contract, feature-rate-limit-idempotency-contract]
related_projects: [ca-skeleton]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# AWS AppConfig — feature flag + dynamic configuration
> Layer: `raw/official-docs/` — AWS Systems Manager AppConfig User Guide "What is AWS AppConfig?" 페이지 (verbatim 발췌).
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-env-driven-runtime-configuration]] | 대안 3 (AWS managed feature flag + auto-rollback) 의 비용/이득 비교. branch 결정 "env-startup flag 1차 + runtime flag optional + registry row 필수" 와의 대조 자료 |
| [[raw/branch-notes/feature-secrets-config-source-contract]] | AppConfig 가 Secrets Manager / Parameter Store / S3 등 외부 store 와 통합하는 점 — secret source-of-truth 분리 결정 비교 |
| [[raw/branch-notes/feature-rate-limit-idempotency-contract]] | throttling limit 의 runtime 조정 use case (AppConfig 가 명시적으로 지원) |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-env-driven-runtime-configuration` branch 의 **대안 3**. branch 는 "feature flag 기본값 = env-startup flag, runtime/canary flag 는 optional + registry row 필수" 로 결정. AWS AppConfig 는 같은 문제 영역에 대해 managed deployment strategy + validator + automatic rollback 을 제공. branch 결정의 비용/이득 비교 자료.
## 출처 / Source
- 원본 URL: https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html
- 아카이브 URL: (미수집)
- 저자 / 조직: AWS (Systems Manager 산하 서비스)
- 발행일: GA, 지속 업데이트
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§What is AWS AppConfig?] "AWS AppConfig feature flags and dynamic configurations help software builders quickly and securely adjust application behavior in production environments without full code deployments."
> [§What is AWS AppConfig? — paragraph 2] "With feature flags, you can gradually release new capabilities to users and measure the impact of those changes before fully deploying the new capabilities to all users. With operational flags and dynamic configurations, you can update block lists, allow lists, throttling limits, logging verbosity, and perform other operational tuning to quickly respond to issues in production environments."
> [§Benefits overview — Avoid unintended changes / Validators] "Validators: A validator ensures that your configuration data is syntactically and semantically correct before deploying the changes to production environments."
> [§Benefits overview — Deployment strategies] "Deployment strategies: A deployment strategy enables you to slowly release changes to production environments over minutes or hours."
> [§Benefits overview — Monitoring and automatic rollback] "Monitoring and automatic rollback: AWS AppConfig integrates with Amazon CloudWatch to monitor changes to your applications. If your application becomes unhealthy because of a bad configuration change and that change triggers an alarm in CloudWatch, AWS AppConfig automatically rolls back the change to minimize impact on your application users."
> [§How AWS AppConfig works — step 4] "To retrieve the data, your application makes an HTTP call to the localhost server where AWS AppConfig Agent has cached a local copy of your deployed configuration data. Retrieving data is a metered event."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AWS-APPCONFIG-C1 | AppConfig 는 feature flag + dynamic configuration 을 통해 full code deployment 없이 production application behavior 를 조정할 수 있게 함 | [§What is AWS AppConfig?] "AWS AppConfig feature flags and dynamic configurations help software builders quickly and securely adjust application behavior in production environments without full code deployments." | `official-vendor-doc` | AWS 환경에서 AppConfig 사용 | "code deployment 보다 빠르다" 의 정량적 지연 시간은 본 인용 범위 밖 |
| AWS-APPCONFIG-C2 | feature flag 는 gradual rollout + 영향 측정을 지원. operational flag/dynamic config 는 block list / allow list / throttling limit / logging verbosity 등 운영 튜닝에 사용 | [§What is AWS AppConfig? — paragraph 2] "With feature flags, you can gradually release new capabilities to users and measure the impact of those changes before fully deploying the new capabilities to all users. With operational flags and dynamic configurations, you can update block lists, allow lists, throttling limits, logging verbosity, and perform other operational tuning to quickly respond to issues in production environments." | `official-vendor-doc` | feature flag / operational flag use case | "측정" 의 구체적 metric 이나 dashboarding 방법은 본 인용 범위 밖 |
| AWS-APPCONFIG-C3 | Validator 는 production 배포 전에 configuration data 가 syntactic + semantic 으로 올바른지 보장 | [§Benefits overview — Validators] "Validators: A validator ensures that your configuration data is syntactically and semantically correct before deploying the changes to production environments." | `official-vendor-doc` | AppConfig validator 설정 (JSON Schema / Lambda) | validator 가 정확히 어떤 형식 (JSON Schema/Lambda) 을 지원하는지는 별도 페이지 참조 필요 |
| AWS-APPCONFIG-C4 | Deployment strategy 는 production 변경을 수 분~수 시간에 걸쳐 점진적 release 가능하게 함 | [§Benefits overview — Deployment strategies] "Deployment strategies: A deployment strategy enables you to slowly release changes to production environments over minutes or hours." | `official-vendor-doc` | AppConfig deployment 정의 | 구체적 strategy 종류 (Linear / Exponential / Canary 등) 와 default 값은 본 인용 범위 밖 |
| AWS-APPCONFIG-C5 | AppConfig 는 CloudWatch 와 통합되어 application 변화를 모니터링. bad configuration change 가 CloudWatch alarm 을 trigger 하면 자동 rollback | [§Benefits overview — Monitoring and automatic rollback] "AWS AppConfig integrates with Amazon CloudWatch to monitor changes to your applications. If your application becomes unhealthy because of a bad configuration change and that change triggers an alarm in CloudWatch, AWS AppConfig automatically rolls back the change to minimize impact on your application users." | `official-vendor-doc` | CloudWatch alarm 이 정의된 AppConfig deployment | "unhealthy" 판정 기준은 alarm 정의에 따라 다르며 본 인용은 default 동작을 명시 안 함 |
| AWS-APPCONFIG-C6 | 데이터 retrieval 은 AppConfig Agent (localhost) 가 cached copy 를 제공. retrieval 은 metered event | [§How AWS AppConfig works — step 4] "To retrieve the data, your application makes an HTTP call to the localhost server where AWS AppConfig Agent has cached a local copy of your deployed configuration data. Retrieving data is a metered event." | `official-vendor-doc` | AppConfig Agent sidecar 사용 시 | Agent 없이 직접 API 호출 (`StartConfigurationSession`/`GetLatestConfiguration`) 의 비용 차이는 본 인용 범위 밖 (별도 Pricing 섹션 참조) |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `AWS-APPCONFIG-C1~C6`: AppConfig 의 4대 safety feature (validator / deployment strategy / monitoring / auto-rollback) + Agent retrieval 모델의 공식 정의
- **이 자료가 증명하지 않는 것**:
- **공식 best practice 로서 "feature flag = AppConfig 가 정답"**: 본 인용은 AppConfig 의 capability 를 설명할 뿐, 다른 도구 (LaunchDarkly, Unleash, env-only) 대비 우위는 다루지 않음
- 실제 latency / availability SLA (별도 AWS SLA 페이지)
- ca-tmpl 의 "env-startup flag 1차" 결정이 틀렸다는 근거 — 본 자료는 대안의 capability 만 보여줌
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- AppConfig Agent 의 caching latency vs polling 부담
- Secrets Manager / Parameter Store 와의 권한 분리 governance
- cost (configuration retrieval per-call billing) 의 ca-tmpl 규모에서의 실제 비용 추산
- **company tech blog 사례를 "AWS 공식 best practice" 로 일반화 금지** — 본 raw 는 AWS 공식 user guide capability 만 다룸. 실제 운영 사례는 별도 case study 가 필요
## 메모 / Notes (내 프로젝트 해석 — PRESERVED)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: AWS 환경에 이미 ECS/EKS/Lambda 를 운영 중이고, feature flag rollout 을 CloudWatch alarm 과 자동 연동하고 싶을 때. block list / allow list / throttling limit 의 runtime 조정.
- 장점:
- **Managed**: 별도 인프라 운영 없음.
- validator (JSON Schema / Lambda) → branch 가 강조한 "invalid env startup fail-fast" 의 server-side 등가물.
- CloudWatch 알람 기반 **자동 rollback** — branch 가 명시하지 않은 보완 기능.
- deployment strategy (예: 10%/10min → 50%/30min → 100%) → canary 표준화.
- 단점:
- **AWS 종속**. multi-cloud / on-prem 부적용.
- cost: configuration retrieval per-call billing.
- latency: AppConfig Agent (sidecar / cache) 필요. 직접 API 호출 시 polling 부담.
- secret 과의 통합은 별도 (Secrets Manager / Parameter Store) 이므로 source-of-truth 분리 학습 필요.
- ca-tmpl 결정과의 차이:
- ca-tmpl: env-startup flag 1차, `APP_FEATURE_*` env + env-keys.yaml registry, runtime flag 는 `owner_branch` 강제.
- AppConfig: managed runtime flag. 다만 branch 의 "registry owner / rollout/rollback rule 강제" 는 AppConfig 자체로는 강제되지 않음 → 별도 governance 레이어 필요.
- 채택 시점 후보: AWS-only 배포 + feature flag 종류가 30+ 로 증가 + canary rollback automation 이 SLO 에 들어갈 때.
- 신뢰도: `official-vendor-doc` 등급. AWS 공식 user guide.
## Related / 관련
- 같은 주제 다른 raw:
- (LaunchDarkly / Unleash / Spring Cloud Config 별도 raw — 미작성)
- 인용하는 branch:
- [[raw/branch-notes/feature-env-driven-runtime-configuration]]
- [[raw/branch-notes/feature-secrets-config-source-contract]]
- [[raw/branch-notes/feature-rate-limit-idempotency-contract]]
- 같은 그룹 대안 raw:
- [[raw/official-docs/config-spring-cloud-kubernetes-configmap-reload]] (대안 2 — k8s ConfigMap auto-reload)
- 적용 contract:
- [[raw/project-notes/ca-skeleton-operational-contract]] (Env-driven runtime configuration 그룹)
- 인용하는 wiki: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/config-spring-boot-externalized-configuration.md
@@ -0,0 +1,92 @@
---
title: "official-doc / Spring Boot — Externalized Configuration (Features Reference)"
source_type: official-doc
url: https://docs.spring.io/spring-boot/reference/features/external-config.html
archive_url:
related_branches: [feature-env-driven-runtime-configuration]
related_projects: [ca-skeleton]
tags: [official-doc, ca-skeleton, application, spring-boot, bean-validation, externalized-config, profile-activation]
created: 2026-06-05
---
# Spring Boot — Externalized Configuration (Features Reference)
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
> Spring Boot 4.0.6 Reference — Features Externalized Configuration
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-env-driven-runtime-configuration]] | D4: Duration `30s`/`PT30S` 양쪽 허용 확인 (우리 규약이 `30s` 1택을 선택해도 됨을 Spring 공식 근거로 확인) / D6: `spring.profiles.active` 및 relaxed binding 규칙(`SPRING_PROFILES_ACTIVE` 도출 메커니즘) Spring Boot native 공식 근거 / D10: `@ConfigurationProperties + @Validated` JSR-303 startup validation fail-fast 공식 근거 |
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-boot/reference/features/external-config.html
- 아카이브 URL: (미확보)
- 저자 / 조직: Spring Team (Broadcom / VMware)
- 발행일: Spring Boot 4.0.6 (2025)
- 마지막 확인일: 2026-06-05
## 왜 저장했는지 / Why archived
`feature-env-driven-runtime-configuration` branch 의 D4 (Duration/DataSize binding 포맷), D6 (profile 활성화 우선순위), D10 (`@Validated` startup validation) 세 결정이 모두 `UNSUPPORTED_DECISION` 상태였음. Spring Boot 공식 reference doc 에서 세 결정 모두 직접 지지하는 원문을 확보하기 위해 아카이브.
## 핵심 인용 / Key quotes (verbatim, 5개)
> [§features.external-config.typesafe-configuration-properties.conversion.durations, line 4471] "To specify a session timeout of 30 seconds, `30`, `PT30S` and `30s` are all equivalent. A read timeout of 500ms can be specified in any of the following form: `500`, `PT0.5S` and `500ms`."
> [§features.external-config.typesafe-configuration-properties.conversion.durations, line 4504] "The default unit is milliseconds and can be overridden using `@DurationUnit` as illustrated in the sample above."
> [§features.external-config.typesafe-configuration-properties.conversion.data-sizes, line 4733] "To specify a buffer size of 10 megabytes, `10` and `10MB` are equivalent. A size threshold of 256 bytes can be specified as `256` or `256B`."
> [§features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables, line 3966] "For example, the configuration property `spring.main.log-startup-info` would be an environment variable named `SPRING_MAIN_LOGSTARTUPINFO`."
> [§features.external-config.files.profile-specific, line 1802] "For example, if profiles `prod,live` are specified by the `spring.profiles.active` property, values in `application-prod.properties` can be overridden by those in `application-live.properties`."
> [§features.external-config.typesafe-configuration-properties.validation, line 48974898] "Spring Boot attempts to validate `@ConfigurationProperties` classes whenever they are annotated with Spring's `@Validated` annotation. You can use JSR-303 `jakarta.validation` constraint annotations directly on your configuration class."
## Claims Extracted / 추출된 주장
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SPRING-EXTCONFIG-C1 | Spring Boot Duration 프로퍼티는 `long`(기본 ms), ISO-8601(`PT30S`), 단순 suffix(`30s`) 세 가지 형식을 모두 허용하며 상호 동등하다 | [§conversion.durations, l.4471] "To specify a session timeout of 30 seconds, `30`, `PT30S` and `30s` are all equivalent." | `official-vendor-doc` | spring.boot ≥ 3.x의 `@ConfigurationProperties`에 바인딩되는 `java.time.Duration` 필드 | 특정 형식이 권장됨을 의미하지 않음 — 어느 형식을 규약으로 고를지는 팀 결정 영역 |
| SPRING-EXTCONFIG-C2 | Duration 기본 단위는 밀리초(ms)이며 `@DurationUnit` 으로 재정의할 수 있다 | [§conversion.durations, l.4504] "The default unit is milliseconds and can be overridden using `@DurationUnit` as illustrated in the sample above." | `official-vendor-doc` | `@ConfigurationProperties` 바인딩 Duration 필드 | `@DurationUnit` 없이 정수만 쓸 때 단위 착오를 막아주는 보장은 없음 (개발자가 정수 값 단위를 일치시켜야 함) |
| SPRING-EXTCONFIG-C3 | Spring Framework `DataSize` 프로퍼티는 `long`(기본 bytes)과 단순 suffix(`10MB`) 두 형식을 허용한다 | [§conversion.data-sizes, l.4733] "To specify a buffer size of 10 megabytes, `10` and `10MB` are equivalent." | `official-vendor-doc` | `@ConfigurationProperties` 바인딩 `DataSize` 필드 | `DataSize` 가 ISO-8601 형식을 지원하지 않음을 증명하지 않음 (Duration 과 달리 ISO-8601 언급 없음) |
| SPRING-EXTCONFIG-C4 | Spring Boot relaxed binding 은 프로퍼티 이름의 점(`.`)을 언더스코어(`_`)로, 대시(`-`)를 제거하고, 대문자로 변환하여 OS 환경 변수 이름에 매핑한다 | [§relaxed-binding.environment-variables, l.3966] "For example, the configuration property `spring.main.log-startup-info` would be an environment variable named `SPRING_MAIN_LOGSTARTUPINFO`." | `official-vendor-doc` | Spring Boot 환경 변수 바인딩 전체 (`systemEnvironment` property source 및 `-systemEnvironment` suffix 를 가진 추가 property source) | `SPRING_PROFILES_ACTIVE` 라는 이름이 문서에 명시적으로 나열되지는 않음 — 규칙 적용의 당연한 귀결 |
| SPRING-EXTCONFIG-C5 | Spring Boot 는 `@Validated` 애노테이션이 붙은 `@ConfigurationProperties` 클래스를 자동으로 검증하며, `jakarta.validation` JSR-303 제약 애노테이션을 필드에 직접 사용할 수 있다 | [§validation, l.48974898] "Spring Boot attempts to validate `@ConfigurationProperties` classes whenever they are annotated with Spring's `@Validated` annotation. You can use JSR-303 `jakarta.validation` constraint annotations directly on your configuration class." | `official-vendor-doc` | Spring Boot 의 `@ConfigurationProperties` + `@Validated` 조합 | 검증 실패 시 startup 이 fail-fast 로 중단된다는 명시적 문구는 이 문서에 없음 — Spring Bean 초기화 실패로 컨텍스트 로드 실패가 발생함은 Spring Framework 일반 동작 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `SPRING-EXTCONFIG-C1`: Spring Boot Binder 가 `30s`, `PT30S`, `30` 세 형식 모두 수용 (D4 근거 — "양쪽 허용 확인")
- `SPRING-EXTCONFIG-C2`: `@DurationUnit` 으로 기본 ms 단위를 override 할 수 있음
- `SPRING-EXTCONFIG-C3`: `DataSize``10MB` suffix 형식을 수용 (D4 DataSize 근거)
- `SPRING-EXTCONFIG-C4`: `spring.profiles.active` 는 relaxed binding 규칙에 의해 `SPRING_PROFILES_ACTIVE` 로 매핑됨 (D6 메커니즘 근거)
- `SPRING-EXTCONFIG-C5`: `@ConfigurationProperties + @Validated` 는 공식 Spring Boot API (D10 공식 근거)
- 이 자료가 증명하지 않는 것:
- `30s` 형식이 `PT30S` 보다 더 권장됨 (C1은 "동등하다"고만 말함 — 규약 선택은 팀 결정)
- `SPRING_PROFILES_ACTIVE``APP_PROFILE` 이 불일치할 때 startup 이 자동으로 fail-fast 되는 동작 (별도 `EnvironmentPostProcessor` 구현 필요)
- `@Validated` 실패가 반드시 startup 중단을 일으킨다는 명시 (Spring context 초기화 실패가 JVM exit 을 일으키는 것은 Spring Boot 런처 일반 동작이나 이 문서에 명시 없음)
- `SPRING_PROFILES_ACTIVE` 라는 정확한 환경 변수 이름이 문서에 명시적으로 나타남 (규칙 귀결)
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- `APP_PROFILE``SPRING_PROFILES_ACTIVE` 불일치 시 startup fail 동작 — `EnvironmentPostProcessor` 또는 `@PostConstruct` validator 구현 후 통합 테스트로 검증
- `@Validated` 실패 시 Spring Boot launcher 가 exit code 1 로 종료되는지 — contract test `StartupFailFastTest` 로 검증
## 메모 / Notes
- D4 resolution: C1 + C3 는 "Spring Boot 가 양쪽 형식을 모두 허용한다" 는 사실을 확인. branch 결정 `30s` 1택은 Spring 강제가 아니라 팀 가독성 규약이므로 D4 를 `UNSUPPORTED_DECISION` → "supported by C1/C3 for mechanical feasibility, team convention for `30s` preference" 로 보강 가능.
- D6 resolution: C4 는 `spring.profiles.active``SPRING_PROFILES_ACTIVE` 매핑 메커니즘을 공식 근거로 확보. `SPRING_PROFILES_ACTIVE` 우선순위 (Spring Boot property precedence table §1 — OS env > properties file) 는 동일 페이지 상단의 priority list 에서 확인 가능 (OS env = 우선순위 10번째, properties file 더 낮음).
- D10 resolution: C5 는 `@Validated` API 지원의 공식 근거. fail-fast startup 동작은 Spring framework 컨텍스트 로드 실패 일반 동작으로 추가 raw 없이 합리적으로 추론 가능 — 단, 추론이므로 claim 에는 넣지 않음.
- 추가로 봐야 할 동일 출처 페이지: property precedence priority list (페이지 상단 §1), `@ConfigurationPropertiesScan`, constructor binding with `@DefaultValue`.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/config-12-factor-app-config]] — 12-factor §III Config (D1 근거)
- [[raw/official-docs/config-spring-cloud-config-server-official]] — Spring Cloud Config Server (D3 대안)
- [[raw/official-docs/config-spring-cloud-kubernetes-configmap-reload]] — k8s ConfigMap reload (D3 대안)
- [[raw/official-docs/config-aws-appconfig-feature-flag-deployment]] — AWS AppConfig (D3 대안)
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/config-spring-cloud-config-server-official.md
@@ -0,0 +1,106 @@
---
title: Spring Cloud Config Server 공식 레퍼런스 — externalized configuration alternative
source_type: official-doc
url: https://docs.spring.io/spring-cloud-config/docs/current/reference/html/
archive_url:
status: reviewed
confidence: high
tags: [ca-tmpl, config, spring-cloud-config, externalized-configuration, alternative]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-env-driven-runtime-configuration, feature-secrets-config-source-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Spring Cloud Config Server 공식 레퍼런스
> Layer: `raw/official-docs/` — Spring Cloud Config 공식 레퍼런스 / Quick Start 섹션 원문 발췌.
> ca-tmpl env-driven runtime configuration 결정의 **대안 1** (중앙 git-backed server + runtime reload).
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-env-driven-runtime-configuration]] | env-driven 결정의 **대안 1 (Spring Cloud Config Server)** 비교 — 중앙 git-backed server + `@RefreshScope` runtime reload 의 trade-off 평가 근거 |
| [[raw/branch-notes/feature-secrets-config-source-contract]] | config + secret 같은 server 에서 다루는 대안 평가 — secret 분리 결정의 비교 baseline |
| [[raw/project-notes/ca-skeleton-operational-contract]] | Group G 대안 평가 — 단일 application skeleton 에는 over-engineering 인 이유 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-env-driven-runtime-configuration` branch의 **대안 1**. branch는 "env로 모든 운영 모드 전환 + no runtime reload"를 결정했는데, Spring Cloud Config Server는 정확히 반대 방향(중앙 서버 + git-backed + `@RefreshScope` runtime reload)을 제공. 두 접근의 trade-off 평가 자료.
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-cloud-config/docs/current/reference/html/
- 아카이브 URL: (미확보)
- 저자 / 조직: Spring Cloud Team (spring-projects)
- 발행 상태: 지속 업데이트, Spring Boot 3.x / Spring Cloud 2024.x 라인 GA
- GitHub: github.com/spring-cloud/spring-cloud-config
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Spring Cloud Config — Quick Start / Overview] "Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments."
> [§Spring Cloud Config Server — Resource Endpoints] "Spring Cloud Config Server provides an HTTP resource-based API for external configuration (name-value pairs or equivalent YAML content)."
> [§Environment Repository — Git Backend] "The default implementation of the server storage backend uses git, so it easily supports labelled versions of configuration environments as well as being accessible to a wide range of tooling for managing the content."
> [§Overview — Deployment Pipeline] "As an application moves through the deployment pipeline from dev to test and into production, you can manage the configuration between those environments and be certain that applications have everything they need to run when they migrate."
> **[2026-05-27 verified — WebFetch 재검증 성공]**: 위 4개 quote (SCC-C1 ~ SCC-C4) 모두 https://docs.spring.io/spring-cloud-config/docs/current/reference/html/ 상에서 FOUND VERBATIM. strength `needs-confirmation` → `official-vendor-doc` 으로 upgrade. [2026-05-25 capture] 시점 verbatim 보존본은 변경 없이 유지.
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SCC-SERVER-C1 | Spring Cloud Config 는 server-side + client-side 양쪽 지원으로 분산 시스템의 **externalized configuration** 을 중앙 관리한다 | [§Overview, 2026-05-27 verified verbatim] "Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments." | `official-vendor-doc` [2026-05-27 verified] | 다수 microservice 가 같은 config 정책을 공유하는 환경 | 단일 application 에서도 의미가 있다는 뜻은 아님 — "distributed system" 가정에 묶임 |
| SCC-SERVER-C2 | Config Server 는 **HTTP resource-based API** 로 외부 설정 (name-value 또는 YAML) 을 노출 | [§Resource Endpoints, 2026-05-27 verified verbatim] "Spring Cloud Config Server provides an HTTP resource-based API for external configuration (name-value pairs or equivalent YAML content)." | `official-vendor-doc` [2026-05-27 verified] | Config Server 가 동작 중인 환경 | 인증 / 권한 / TLS 의 default 설정은 본 인용 범위 밖 — 별도 보안 섹션 참조 필요 |
| SCC-SERVER-C3 | 기본 storage backend 는 **git** 이며 labelled version (branch) 과 다양한 외부 tooling 지원 | [§Environment Repository — Git Backend, 2026-05-27 verified verbatim] "The default implementation of the server storage backend uses git, so it easily supports labelled versions of configuration environments as well as being accessible to a wide range of tooling for managing the content." | `official-vendor-doc` [2026-05-27 verified] | default Config Server 구성 | git 이 유일한 backend 라는 뜻은 아님 — Vault / DB / native filesystem 등 다른 backend 도 지원 (별도 확인 필요) |
| SCC-SERVER-C4 | dev → test → production 으로 deployment pipeline 이 이동할 때 환경 간 config 를 일관되게 관리할 수 있다 | [§Overview — Deployment Pipeline, 2026-05-27 verified verbatim] "As an application moves through the deployment pipeline from dev to test and into production, you can manage the configuration between those environments and be certain that applications have everything they need to run when they migrate." | `official-vendor-doc` [2026-05-27 verified] | dev / test / prod 환경별 profile 사용 시 | profile 충돌 / 잘못된 binding / fallback 정책의 보장이 자동이라는 뜻은 아님 — 운영 측 검증 필요 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `SCC-SERVER-C1`~`C4`: Spring Cloud Config Server 의 4가지 공식 진술 — 중앙 관리 / HTTP API / git backend / deployment pipeline 관리
- **이 자료가 증명하지 않는 것**:
- `@RefreshScope` + `/actuator/refresh` 의 runtime reload 동작 메커니즘 (별도 client-side 페이지)
- HA / SPOF 회피 구성 (Config Server 자체 다중화 패턴)
- bootstrap 의존성 (Config Server 죽으면 신규 인스턴스 기동 불가) 의 정확한 fallback 메커니즘 — caching 옵션 필요
- 단일 / 소수 application 에 대한 권장 여부 (over-engineering 판단은 운영 측 결정)
- secret 저장 시 Vault 와의 통합 vs 직접 git 저장의 안전성 비교
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 "silent changed behavior forbidden" 정책과 `@RefreshScope` 의 호환성 검토 (refresh 가 어떤 bean lifecycle 을 변경하는지)
- 단일 application skeleton 에서 Config Server 도입의 ROI (인프라 비용 vs 운영 이득)
- git audit trail 이 secret rotation 과 결합될 때의 정보 누출 위험 (secret 이 git history 에 남는 문제)
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 적용 컨텍스트 해석.
- 적용 시나리오: **다수 (수십~수백) microservice**가 같은 config 정책을 공유하는 조직. config 변경 audit trail이 git history로 필요한 경우.
- 장점: 중앙 관리 + git 백엔드 + label/version (branch별 config). `@RefreshScope` + `/actuator/refresh`로 runtime reload 지원. dev/staging/prod 환경별 profile.
- 단점:
- **추가 인프라**: Config Server 자체가 SPOF. HA 구성 필요.
- **분산 시스템 일관성**: client별 reload 타이밍 불일치 → 같은 클러스터에서 서로 다른 config가 잠시 공존.
- **부트스트랩 의존**: Config Server가 죽으면 신규 인스턴스 기동 불가 (caching/fallback 설정 필요).
- **단일 application 또는 소수 service에는 over-engineering**.
- ca-tmpl(env-driven) 결정과의 차이:
- ca-tmpl: build artifact 1개 + env 주입, **runtime reload 없음**. Kubernetes/ECS 등 platform이 rolling restart로 config 변경을 처리한다고 가정.
- Spring Cloud Config: 중앙 서버 + `@RefreshScope`. **runtime reload 있음**. 단, branch는 "silent changed behavior" forbidden으로 명시.
- 채택 시점 후보: monolith → microservice 분화 시점, 또는 multi-tenant feature flag가 git audit trail을 요구할 때.
- 신뢰도: `official-doc` 등급. Spring 공식 프로젝트. **2026-05-27 WebFetch 재검증 성공 — 4개 quote 모두 verbatim 일치, strength `official-vendor-doc` 으로 upgrade.**
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/official-docs/config-12-factor-app-config]]
- [[raw/company-tech-blogs/config-launchdarkly-feature-flag-best-practice]]
- 인용하는 branch:
- [[raw/branch-notes/feature-env-driven-runtime-configuration]]
- [[raw/branch-notes/feature-secrets-config-source-contract]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 대안 그룹: **Group G — Env-driven runtime configuration** (대안 5종)
- 본 source의 위치: **대안 1: Spring Cloud Config Server (중앙 git-backed server + runtime reload)**
- 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/config-spring-cloud-kubernetes-configmap-reload.md
@@ -0,0 +1,109 @@
---
title: Spring Cloud Kubernetes — ConfigMap PropertySource + reload 공식 문서
source_type: official-doc
url: https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/
archive_url:
status: raw
confidence: high
tags: [ca-tmpl, config, kubernetes, configmap, spring-cloud-kubernetes, alternative, official-doc]
related_branches: [feature-env-driven-runtime-configuration, feature-runtime-health-lifecycle-contract, feature-secrets-config-source-contract]
related_projects: [ca-skeleton]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Spring Cloud Kubernetes — ConfigMap PropertySource + Reload
> Layer: `raw/official-docs/` — Spring Cloud Kubernetes reference docs (current) 의 ConfigMap PropertySource + Reload 섹션 verbatim 발췌.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-env-driven-runtime-configuration]] | 대안 2 (Spring Cloud Kubernetes ConfigMap + auto-reload) 의 비용/이득 비교. branch 의 "no runtime reload, platform rolling restart 로 통일" 결정과 정면 비교 |
| [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] | `restart_context` / `shutdown` reload 전략의 graceful restart 의미 비교 (lifecycle contract 와 정렬) |
| [[raw/branch-notes/feature-secrets-config-source-contract]] | Secrets API consumption 이 RBAC 보안 이유로 default disabled, volume mount 가 권장 — secret source-of-truth 결정 근거 |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-env-driven-runtime-configuration` branch 의 **대안 2**. Kubernetes ConfigMap 을 PropertySource 로 직접 바인딩하고 변경 시 hot reload 하는 메커니즘. branch 의 "no runtime reload" 결정과 정면 충돌하는 접근. 두 결정을 명확히 분리하기 위한 비교 자료.
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/
- 아카이브 URL: (미수집)
- 저자 / 조직: Spring Cloud Team (spring-projects)
- 발행일: 지속 업데이트 (current docs)
- GitHub: github.com/spring-cloud/spring-cloud-kubernetes
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Spring Cloud Kubernetes Config — intro] "The Spring Cloud Kubernetes Config project makes Kubernetes `ConfigMap` instances available during application startup and triggers hot reloading of beans or Spring context when changes are detected on observed `ConfigMap` instances."
> [§Reload feature — enable property] "By default, this feature is disabled. You can enable it by using the `spring.cloud.kubernetes.reload.enabled=true` configuration property (for example, in the `application.properties` file)."
> [§Reload feature — strategy levels] "The following levels of reload are supported (by setting the `spring.cloud.kubernetes.reload.strategy` property):
> - `refresh` (default): Only configuration beans annotated with `@ConfigurationProperties` or `@RefreshScope` are reloaded. This reload level leverages the refresh feature of Spring Cloud Context.
> - `restart_context`: the whole Spring `ApplicationContext` is gracefully restarted. Beans are recreated with the new configuration. In order for the restart context functionality to work properly you must enable and expose the restart actuator endpoint
> - `shutdown`: the Spring `ApplicationContext` is shut down to activate a restart of the container. When you use this level, make sure that the lifecycle of all non-daemon threads is bound to the `ApplicationContext` and that a replication controller or replica set is configured to restart the pod."
> [§Secrets PropertySource — API consumption] "By default, consuming Secrets through the API (points 2 and 3 above) **is not enabled** for security reasons. The permission 'list' on secrets allows clients to inspect secrets values in the specified namespace. Further, we recommend that containers share secrets through mounted volumes."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SCK-CONFIG-C1 | Spring Cloud Kubernetes Config 는 application startup 시 ConfigMap 을 사용 가능하게 만들고, 관찰 중인 ConfigMap 변경 감지 시 bean / Spring context 의 hot reload 를 trigger | [§Spring Cloud Kubernetes Config — intro] "The Spring Cloud Kubernetes Config project makes Kubernetes `ConfigMap` instances available during application startup and triggers hot reloading of beans or Spring context when changes are detected on observed `ConfigMap` instances." | `official-vendor-doc` | Spring Cloud Kubernetes Config 의존성 추가된 Spring Boot app | 변경 감지가 watch API 인지 polling 인지는 본 인용 범위 밖 (별도 페이지 확인 필요) |
| SCK-RELOAD-C1 | reload feature 는 기본 disabled. `spring.cloud.kubernetes.reload.enabled=true` 로 활성화 | [§Reload feature — enable property] "By default, this feature is disabled. You can enable it by using the `spring.cloud.kubernetes.reload.enabled=true` configuration property (for example, in the `application.properties` file)." | `official-vendor-doc` | Spring Cloud Kubernetes 의존성 사용 시 reload 옵트인 | 활성화 시 부수 효과 (RBAC 권한 요구, watch overhead) 는 본 인용 범위 밖 |
| SCK-RELOAD-C2 | reload strategy 는 3가지: `refresh` (default, `@ConfigurationProperties` / `@RefreshScope` bean 만 reload), `restart_context` (전체 `ApplicationContext` graceful restart), `shutdown` (`ApplicationContext` shutdown 으로 container 재시작 유도) | [§Reload feature — strategy levels] "The following levels of reload are supported (by setting the `spring.cloud.kubernetes.reload.strategy` property): `refresh` (default): ... / `restart_context`: ... / `shutdown`: ..." | `official-vendor-doc` | reload strategy 선택 결정 | 각 strategy 의 정확한 latency 와 in-flight request 처리 동작은 본 인용 범위 밖. `restart_context` 가 in-process 인지 process restart 인지의 차이도 본 인용으로 직접 증명 안 됨 (단 "the whole Spring ApplicationContext is gracefully restarted" 는 in-process) |
| SCK-RELOAD-C3 | `restart_context` strategy 가 동작하려면 restart actuator endpoint 를 enable + expose 해야 함 | [§Reload feature — strategy levels] "In order for the restart context functionality to work properly you must enable and expose the restart actuator endpoint" | `official-vendor-doc` | `restart_context` strategy 선택 시 | restart endpoint 노출의 보안 영향 (인증/RBAC) 은 본 인용 범위 밖 |
| SCK-RELOAD-C4 | `shutdown` strategy 사용 시 non-daemon thread lifecycle 이 `ApplicationContext` 에 bound 되어야 하고, ReplicationController / ReplicaSet 이 pod restart 를 담당해야 함 | [§Reload feature — strategy levels] "When you use this level, make sure that the lifecycle of all non-daemon threads is bound to the `ApplicationContext` and that a replication controller or replica set is configured to restart the pod." | `official-vendor-doc` | `shutdown` strategy 선택 시 | k8s Deployment (ReplicaSet 의 상위 abstraction) 도 동일하게 동작하는지는 본 인용으로 직접 증명 안 됨 (관례적으로 yes, 단 문서는 RC/RS 만 언급) |
| SCK-SECRETS-C1 | Secrets 의 API consumption 은 보안 이유로 default disabled. `list` 권한이 namespace 의 secret values 를 노출시키므로, container 가 mounted volume 으로 secret 을 공유하는 것이 권장 | [§Secrets PropertySource — API consumption] "By default, consuming Secrets through the API (points 2 and 3 above) **is not enabled** for security reasons. The permission 'list' on secrets allows clients to inspect secrets values in the specified namespace. Further, we recommend that containers share secrets through mounted volumes." | `official-vendor-doc` | Spring Cloud Kubernetes Secrets PropertySource 사용 결정 | mounted volume 방식의 reload 지원 여부 (file watch?) 는 본 인용 범위 밖 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `SCK-CONFIG-C1`: ConfigMap PropertySource 와 hot reload trigger 의 존재
- `SCK-RELOAD-C1~C4`: 3-level reload strategy 의 정확한 이름과 활성화 조건
- `SCK-SECRETS-C1`: Secrets API consumption 의 default-disabled + mounted volume 권장 보안 정책
- **이 자료가 증명하지 않는 것**:
- "k8s 환경에서 hot reload 가 항상 권장된다" — 본 인용은 capability 만 제공, 권장 시점은 다루지 않음
- ca-tmpl 의 "no runtime reload" 결정이 틀렸다는 근거 — 본 자료는 대안의 capability 만 보여줌
- reload 가 in-flight request 를 어떻게 처리하는지의 정확한 의미론
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- `refresh` 전략에서 `@RefreshScope` 가 아닌 bean (e.g., singleton config holder) 의 stale state 노출 가능성
- `restart_context` 의 graceful restart 가 실제로 in-flight HTTP request 를 drain 하는지
- k8s watch API 의 권한 요구사항 (RBAC) 과 ca-tmpl 의 RBAC 정책 정렬
- **company tech blog 사례를 "Spring 공식 best practice" 로 일반화 금지** — 본 raw 는 공식 reference docs 의 capability 만 다룸
## 메모 / Notes (내 프로젝트 해석 — PRESERVED)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: Kubernetes 전용 배포 환경. config 변경 시 rolling restart 비용이 크고 (e.g. stateful workload) hot reload 가 필요할 때.
- 장점:
- ConfigMap/Secret 을 Spring `Environment` 에 1급 PropertySource 로 통합.
- 3-level reload (`refresh` / `restart_context` / `shutdown`) — refresh 전략이 default.
- k8s watch API 기반이므로 polling 부담 적음 (단 본 raw 인용으로는 watch vs polling 명시 안 됨 — 별도 확인 필요).
- 단점:
- **Kubernetes 종속**. ECS / Cloud Run / VM 배포에는 부적용.
- reload 중 부분 상태 (일부 bean 만 refresh) → "silent changed behavior" 리스크. branch 가 forbidden 으로 명시한 항목.
- Secrets API consumption 은 기본 disabled (RBAC `list secrets` 권한 위험성 때문). volume mount 가 권장.
- ca-tmpl 결정과의 차이:
- ca-tmpl: env 주입 + no runtime reload. config 변경은 platform 의 rolling restart 로 처리.
- Spring Cloud Kubernetes Reload: in-process reload. **`restart_context` 전략은 사실상 rolling restart 와 유사**해서 ca-tmpl 입장에서는 platform restart 로 통일하는 게 더 단순 (해석 — 본 자료 직접 증명 아님).
- branch 가 이 대안을 선택하지 않은 명시적 이유: "platform 이 rolling restart 로 config 변경을 처리" 가 12-factor 와 정합하고, in-process reload 는 partial-state 디버깅 비용이 큼.
- 신뢰도: `official-vendor-doc` 등급.
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/official-docs/config-aws-appconfig-feature-flag-deployment]] (대안 3 — AWS managed runtime config)
- 인용하는 branch:
- [[raw/branch-notes/feature-env-driven-runtime-configuration]]
- [[raw/branch-notes/feature-runtime-health-lifecycle-contract]]
- [[raw/branch-notes/feature-secrets-config-source-contract]]
- 적용 contract:
- [[raw/project-notes/ca-skeleton-operational-contract]] (Env-driven runtime configuration 그룹)
- 인용하는 wiki: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/container-alpine-java-musl-tradeoffs.md
@@ -0,0 +1,108 @@
---
title: "Eclipse Temurin on Alpine (musl libc) — Trade-offs and Docker Hub Notes"
source_type: official-doc
url: https://hub.docker.com/_/eclipse-temurin
archive_url:
status: raw
confidence: high
tags: [ca-skeleton, container, runtime, alpine, musl, temurin, base-image, official-doc, branch:feature-container-runtime-contract]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-container-runtime-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Eclipse Temurin on Alpine (musl libc) — Trade-offs and Docker Hub Notes
> Layer: `raw/official-docs/` — Eclipse Temurin 공식 Docker Hub 페이지 + Adoptium musl support 페이지의 원문 발췌.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-container-runtime-contract]] | Group G-D container runtime 대안 2 (Alpine + Temurin musl) 의 baseline 사실 — image size 이점과 musl 호환성 risk 의 공식 출처 |
특정 branch 없이 foundational 조사로 수집한 경우:
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl base image default 의사결정의 외부 비교 기준
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-container-runtime-contract` 에서 Alpine + Java (musl libc) 는 대안 후보. ca-tmpl 결정은 **Temurin JRE slim (glibc 기반 Debian slim)** 이며, Alpine 변형은 image 크기는 더 작지만 musl libc 로 인한 호환성 risk 가 따른다.
## 출처 / Source
- 원본 URL: https://hub.docker.com/_/eclipse-temurin
- 보조 URL: https://adoptium.net/temurin/releases/?os=alpine-linux
- 아카이브 URL: (미수집)
- 저자 / 조직: Eclipse Adoptium Working Group
- 발행일: Temurin 21 LTS 이후 (current, fetched 2026-05-27)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Image Variants — alpine] "it does use musl libc instead of glibc and friends"
> [§Image Variants — alpine] "This variant is useful when final image size being as small as possible is your primary concern."
> [§Image Variants — alpine] "Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general."
> [§Image Variants — alpine — caveats] "software will often run into issues depending on the depth of their libc requirements/assumptions"
> [§Image Variants — alpine — caveats] "it's uncommon for additional related tools (such as `git` or `bash`) to be included in Alpine-based images"
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CAJM-C1 | Eclipse Temurin alpine variant 는 glibc 가 아닌 musl libc 를 사용한다 | [§Image Variants — alpine] "it does use musl libc instead of glibc and friends" | `official-vendor-doc` | `eclipse-temurin:*-alpine` 태그 | Temurin 의 musl 빌드가 모든 JDK 버전에서 동일 quality assurance 를 받는다는 뜻은 아님 — Adoptium 별도 페이지가 JDK 21+ 부터 first-party musl 빌드 제공 명시 |
| CAJM-C2 | Alpine variant 의 채택 명분은 "final image size 가 최우선일 때" 이다 (공식 docker hub 의 권고 조건) | [§Image Variants — alpine] "This variant is useful when final image size being as small as possible is your primary concern." | `official-vendor-doc` | image size 최소화 워크로드 (edge / IoT / FaaS) | Alpine 이 모든 production 환경에서 권장된다는 뜻은 아님 — 명시적으로 size-primary 조건부 |
| CAJM-C3 | Alpine Linux base image 는 약 5MB 로 대부분 distribution base image 보다 작아 최종 이미지가 전반적으로 더 작아진다 | [§Image Variants — alpine] "Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general." | `official-vendor-doc` | Alpine base 기반 이미지 빌드 일반 | "전반적 더 작음" 이 JRE 포함 시 정확히 얼마인지의 수치는 인용에 없음 — 최종 이미지 크기는 JRE size 가 지배적 |
| CAJM-C4 | Alpine 기반 이미지에서 software 는 libc 요구/가정의 depth 에 따라 종종 문제를 일으킨다 (musl 의 부분 호환성 한계) — **공식 경고** | [§Image Variants — alpine — caveats] "software will often run into issues depending on the depth of their libc requirements/assumptions" | `official-vendor-doc` | native library 의존성이 있는 application | 어떤 라이브러리가 문제인지의 구체 목록은 인용 범위 밖 — JNI / native compression / DB driver 등은 별도 검증 필요 |
| CAJM-C5 | Alpine 기반 이미지에는 `git` / `bash` 같은 부가 도구가 포함되지 않는 것이 일반적이다 | [§Image Variants — alpine — caveats] "it's uncommon for additional related tools (such as `git` or `bash`) to be included in Alpine-based images" | `official-vendor-doc` | Alpine base 디버깅/CI 사용 시 | apk 로 설치 가능 여부는 별개 사실 — 인용은 "기본 포함되지 않음" 만 주장 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CAJM-C1`: Alpine variant 가 musl libc 를 사용한다는 정의
- `CAJM-C2`: docker hub 공식 권고가 "size-primary 조건" 이라는 사실
- `CAJM-C3`: Alpine base 의 ~5MB 크기 baseline
- `CAJM-C4`: musl 호환성 risk 의 **공식 경고**
- `CAJM-C5`: 기본 패키지에 git/bash 미포함
- **이 자료가 증명하지 않는 것**:
- 어떤 구체 Java 라이브러리가 musl 에서 실패하는지의 카탈로그 (JNI 사용 라이브러리 별 호환성)
- DNS resolver 차이 (musl 의 simpler resolver vs glibc) — 본 docker hub 페이지에는 명시 없음, ca-tmpl 의 본 메모의 DNS 관련 서술은 외부 출처 (musl FAQ / k8s 문서) 가 필요
- JVM thread stack 기본값의 musl vs glibc 차이 (별도 OpenJDK 이슈 트래커 확인 필요)
- Adoptium 의 musl JDK first-party 빌드 시작 버전 (JDK 21 LTS 명시는 adoptium.net 페이지에서 확인 필요)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 에서 사용하는 native library (예: snappy, zstd-jni, BouncyCastle native, PostgreSQL JDBC native) 의 musl 호환성 매트릭스
- Testcontainers 가 alpine + musl 환경에서 정상 동작하는지 (Docker-in-Docker 시나리오)
- K8s 환경에서 `search` domain / `ndots` 옵션 해석 차이로 인한 service discovery 영향 검증
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: edge / IoT, 이미지 크기가 critical 한 환경.
- 장점:
- base 이미지 크기 ~5 MiB (Alpine) + JRE → 최종 이미지 ~150 MB 이하 가능.
- apk 패키지 매니저로 추가 도구 설치 간단.
- 단점:
- **musl libc** 가 일부 native 라이브러리 (예: 일부 DB driver, native compression lib, OpenSSL 의존 라이브러리) 와 충돌 — `CAJM-C4` 의 공식 경고 일반화.
- DNS resolver 동작이 glibc 와 미세하게 달라 `search` domain, `ndots` 옵션 해석 차이로 K8s 환경에서 디버깅 비용 발생 — **본 docker hub 인용 범위 밖, 별도 musl FAQ 출처 필요**.
- thread stack 기본값 차이로 일부 JVM 워크로드에서 `StackOverflowError` 가 다르게 발현 — **별도 출처 필요**.
- ca-tmpl 과의 차이: ca-tmpl 은 Temurin JRE slim (glibc/Debian slim) 을 default 로 둠. Alpine + Temurin 은 별도 검증 후 허용.
- testability 영향: 중 — Testcontainers 등 native 의존 도구가 musl 에서 동작 검증 필요.
- 보안 영향: 중상 — Alpine 의 보안 정책은 좋지만 musl 관련 미해결 issue 가 종종 보고됨.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/container-distroless-google-github]] — 대안 1 Distroless
- [[raw/official-docs/container-graalvm-native-image-spring-boot]] — 대안 3 GraalVM native-image
- 적용 branch-note:
- [[raw/branch-notes/feature-container-runtime-contract]]
- canonical contract 섹션:
- `wiki/projects/ca-skeleton-operational-contract` 의 container runtime canonical section (예정)
- 대안 그룹: **Group G-D — Container runtime** 대안 후보군
- 본 source 의 위치: 대안 2 — Alpine + Temurin (musl libc)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/container-distroless-google-github.md
@@ -0,0 +1,109 @@
---
title: "GoogleContainerTools/distroless — Language focused docker images, minus the operating system"
source_type: official-doc
url: https://github.com/GoogleContainerTools/distroless
archive_url:
status: raw
confidence: high
tags: [ca-skeleton, container, runtime, distroless, base-image, security, official-doc, branch:feature-container-runtime-contract]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-container-runtime-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# GoogleContainerTools/distroless — Language focused docker images, minus the operating system
> Layer: `raw/official-docs/` — Google이 maintain 하는 distroless base image 프로젝트 README 원문 발췌.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-container-runtime-contract]] | Group G-D container runtime 대안 1 (Distroless) 의 baseline 사실 — image size, shell 부재, `:debug` variant 의 정확한 정의 |
특정 branch 없이 foundational 조사로 수집한 경우:
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl base image default 의사결정의 외부 비교 기준
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-container-runtime-contract` 의 결정 — base image 기본값은 **Temurin JRE slim** 이고 distroless 는 "debug runbook 보강 후 허용"으로 제한됨. 본 source 는 대안 1: **Distroless Java** 채택 시 trade-off 를 baseline 으로 비교하기 위한 원문.
## 출처 / Source
- 원본 URL: https://github.com/GoogleContainerTools/distroless
- 아카이브 URL: (미수집)
- 저자 / 조직: Google Container Tools
- 발행일: 지속 업데이트 (README 기준, fetched 2026-05-27)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§What are distroless images] "'Distroless' images contain only your application and its runtime dependencies."
> [§What are distroless images] "They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution."
> [§Why use distroless] "Restricting what's in your runtime container to precisely what's necessary for your app is a best practice employed by Google and other tech giants."
> [§Why use distroless] "It improves the signal to noise of scanners (e.g. CVE) and reduces the burden of establishing provenance to just what you need."
> [§Image sizes] "The smallest distroless image, `gcr.io/distroless/static-debian13`, is around 2 MiB. That's about 50% of the size of `alpine` (~5 MiB), and less than 2% of the size of `debian` (124 MiB)."
> [§Debug images] "Distroless images are minimal and lack shell access. The `:debug` image set for each language provides a busybox shell to enter."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CDG-C1 | Distroless 이미지는 application 과 runtime dependency 만 포함하고 package manager · shell · 기타 표준 Linux distribution 의 일반 도구를 포함하지 않는다 | [§What are distroless images] "'Distroless' images contain only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution." | `official-vendor-doc` | distroless `:nonroot` / `:latest` (non-debug) variants | distroless 가 모든 언어 런타임에 동일 형태로 제공된다는 뜻은 아님 — Java/Python/Node 등 variant 별 차이 있음 |
| CDG-C2 | distroless 채용 명분은 "runtime container 에 정확히 필요한 것만 두는 것" 이며 Google 및 다른 대기업이 채택한 best practice 로 기술됨 | [§Why use distroless] "Restricting what's in your runtime container to precisely what's necessary for your app is a best practice employed by Google and other tech giants." | `official-vendor-doc` | container 최소화 정책 일반 | "best practice" 가 industry-wide consensus 라는 의미는 아님 — Google 의 self-claim |
| CDG-C3 | distroless 는 CVE scanner 의 signal-to-noise 를 개선하고 provenance 입증 부담을 application 의존성으로 한정한다 | [§Why use distroless] "It improves the signal to noise of scanners (e.g. CVE) and reduces the burden of establishing provenance to just what you need." | `official-vendor-doc` | supply-chain security 정책 (SBOM/SLSA) 채택 환경 | 구체적 CVE 감소 수치 / 특정 scanner 와의 정합성은 본 인용 범위 밖 |
| CDG-C4 | `gcr.io/distroless/static-debian13` 이미지 크기는 약 2 MiB 로 alpine (~5 MiB) 의 약 50%, debian (124 MiB) 의 2% 미만이다 | [§Image sizes] "The smallest distroless image, `gcr.io/distroless/static-debian13`, is around 2 MiB. That's about 50% of the size of `alpine` (~5 MiB), and less than 2% of the size of `debian` (124 MiB)." | `official-vendor-doc` | `static-debian13` distroless variant (Java/Python 런타임 포함 variant 는 더 큼) | Java/Python 런타임 포함 distroless 이미지의 크기는 본 인용에 명시되지 않음 — Java distroless 는 JRE 포함으로 수십 MB |
| CDG-C5 | distroless 이미지는 shell 이 없으며, debugging 용도로는 각 언어별 `:debug` variant 가 busybox shell 을 제공한다 | [§Debug images] "Distroless images are minimal and lack shell access. The `:debug` image set for each language provides a busybox shell to enter." | `official-vendor-doc` | `:debug` tag 가 제공되는 언어별 distroless 이미지 | `kubectl exec` 외의 진단 방법 (ephemeral container, sidecar) 의 가능 여부는 본 인용 범위 밖 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CDG-C1`: distroless 의 정의 (package manager/shell 부재)
- `CDG-C2`, `CDG-C3`: Google 의 채택 명분 (CVE noise 감소, provenance 단순화) — **Google self-claim 임을 명시**
- `CDG-C4`: `static-debian13` variant 의 정확한 크기 비교 baseline
- `CDG-C5`: `:debug` variant 의 존재와 busybox shell 제공 사실
- **이 자료가 증명하지 않는 것**:
- distroless 가 모든 production 환경에서 정답이라는 일반화 — Google 의 self-claim 이며 official-standard 가 아님
- Java distroless (`gcr.io/distroless/java-debian12` 등) 의 정확한 이미지 크기 (README 의 2 MiB 는 `static-debian13` 기준)
- distroless 채택 시 jcmd/jstack/heap dump 같은 in-container 진단의 대체 워크플로우
- SLSA/SBOM 정책과의 자동 정합성 (별도 cosign/sigstore 설정 필요)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 에서 Spring Boot fat jar + Temurin JRE 를 distroless `java` variant 로 옮길 때의 실제 image 크기 측정
- `:debug` variant 의 prod-time 사용 정책 (rollback 시점, ops on-call 의 권한 모델)
- `kubectl debug --image=...` ephemeral container 패턴으로 distroless prod pod 디버깅이 가능한지 검증
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl `feature-container-runtime-contract` 결정 컨텍스트 해석.
- 적용 시나리오: 보안 요구가 강한 prod 환경, supply chain risk surface 축소가 우선인 경우.
- 장점:
- shell/package manager 부재로 공격 표면이 좁다 (CVE count 감소).
- 이미지 크기가 매우 작다 (단, Java distroless 는 JRE 포함으로 README 의 2 MiB 보다 큼).
- SLSA / SBOM 정책과 잘 맞는다 (Google 이 직접 sign).
- 단점:
- shell 이 없어 `kubectl exec` 디버깅 불가. `:debug` variant 또는 ephemeral container 필요.
- heap dump 추출, jcmd, jstack 같은 in-container 진단이 어렵다 (별도 sidecar 또는 외부 도구 필요).
- JDK 가 아닌 JRE 만 들어있어 application 측 진단 도구 호출 시 빌드 단계에서 같이 packaging 필요.
- ca-tmpl 과의 차이: ca-tmpl 은 Temurin JRE slim 을 기본값으로 두어 운영자 친숙도와 디버깅 가능성을 우선. distroless 는 "debug runbook 이 있을 때만 허용".
- testability 영향: 중립 — 빌드는 multi-stage 로 동일 패턴.
- 보안 영향: 상 — CVE surface 감소가 가장 큰 이점.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/container-alpine-java-musl-tradeoffs]] — 대안 2 Alpine + Temurin
- [[raw/official-docs/container-graalvm-native-image-spring-boot]] — 대안 3 GraalVM native-image
- 적용 branch-note:
- [[raw/branch-notes/feature-container-runtime-contract]]
- canonical contract 섹션:
- `wiki/projects/ca-skeleton-operational-contract` 의 container runtime canonical section (예정)
- 대안 그룹: **Group G-D — Container runtime** (대안 5종: Temurin JRE slim / Distroless / Alpine+Temurin / GraalVM native-image / Multi-stage debug variant)
- 본 source 의 위치: 대안 1 — Distroless (Google)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/container-graalvm-native-image-spring-boot.md
@@ -0,0 +1,116 @@
---
title: "GraalVM Native Image with Spring Boot 3 — Official Reference"
source_type: official-doc
url: https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html
archive_url:
status: raw
confidence: high
tags: [ca-skeleton, container, runtime, graalvm, native-image, spring-boot, aot, official-doc, branch:feature-container-runtime-contract]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-container-runtime-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# GraalVM Native Image with Spring Boot 3 — Official Reference
> Layer: `raw/official-docs/` — Spring Boot 공식 reference 의 GraalVM Native Image 절 + GraalVM 공식 문서 원문 발췌.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-container-runtime-contract]] | Group G-D container runtime 대안 3 (GraalVM native-image) 의 baseline 사실 — startup/memory 이점과 reflection/AOT 제약의 공식 출처 |
특정 branch 없이 foundational 조사로 수집한 경우:
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl 의 cold-start sensitive 워크로드 대응 시 native-image 채택 검토 자료
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-container-runtime-contract` 에서 GraalVM native-image 는 **Group G-D** 대안 후보. JIT 기반 Temurin JRE slim 과의 trade-off — 시작 속도와 메모리 사용량은 압도적으로 유리하지만 reflection / dynamic proxy 측면에서 application code 제약이 따른다.
## 출처 / Source
- 원본 URL: https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html
- 보조 URL: https://www.graalvm.org/latest/reference-manual/native-image/
- 아카이브 URL: (미수집)
- 저자 / 조직: Spring Team (VMware/Broadcom) + Oracle GraalVM
- 발행일: Spring Boot 3.x reference (current, fetched 2026-05-27)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Introducing GraalVM Native Images — definition] "GraalVM Native Images provide a new way to deploy and run Java applications. Compared to the Java Virtual Machine, native images can run with a smaller memory footprint and with much faster startup times."
> [§Build process] "Unlike traditional applications written for the JVM, GraalVM Native Image applications require ahead-of-time processing in order to create an executable. This ahead-of-time processing involves statically analyzing your application code from its main entry point."
> [§Executable nature] "A GraalVM Native Image is a complete, platform-specific executable. You do not need to ship a Java Virtual Machine in order to run a native image."
> [§Key differences with JVM — static analysis] "Static analysis of your application is performed at build-time from the `main` entry point. Code that cannot be reached when the native image is created will be removed and won't be part of the executable."
> [§Key differences with JVM — dynamic elements] "GraalVM is not directly aware of dynamic elements of your code and must be told about reflection, resources, serialization, and dynamic proxies."
> [§Key differences with JVM — class loading] "There is no lazy class loading, everything shipped in the executables will be loaded in memory on startup."
> [§Best-suited applications] "They are well suited to applications that are deployed using container images and are especially interesting when combined with \"Function as a service\" (FaaS) platforms."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CGN-C1 | GraalVM Native Image 는 JVM 대비 더 작은 memory footprint 와 더 빠른 startup 으로 Java 애플리케이션을 배포/실행하는 방법이다 | [§Introducing GraalVM Native Images — definition] "GraalVM Native Images provide a new way to deploy and run Java applications. Compared to the Java Virtual Machine, native images can run with a smaller memory footprint and with much faster startup times." | `official-vendor-doc` | Spring Boot 3.x + GraalVM Native Image | "smaller" 와 "much faster" 의 정량값은 인용에 없음 — 워크로드별 측정 필요 |
| CGN-C2 | Native Image 빌드는 AOT (ahead-of-time) processing 을 필요로 하며, main entry point 에서 정적 분석을 수행하여 executable 을 생성한다 | [§Build process] "Unlike traditional applications written for the JVM, GraalVM Native Image applications require ahead-of-time processing in order to create an executable. This ahead-of-time processing involves statically analyzing your application code from its main entry point." | `official-vendor-doc` | Spring Boot AOT processing 흐름 | AOT 처리 시간 / 메모리 비용은 인용 범위 밖 — CI 비용 계산 시 별도 측정 필요 |
| CGN-C3 | Native Image 는 완전한 platform-specific executable 이며 JVM 을 함께 배포할 필요가 없다 | [§Executable nature] "A GraalVM Native Image is a complete, platform-specific executable. You do not need to ship a Java Virtual Machine in order to run a native image." | `official-vendor-doc` | container image / standalone binary 배포 | cross-compile 가능 여부 (Linux 호스트에서 Windows 바이너리 빌드 등) 는 인용 범위 밖 |
| CGN-C4 | Native Image 빌드 시 main entry point 에서 도달 불가능한 코드는 build-time 에 제거되어 executable 에 포함되지 않는다 | [§Key differences with JVM — static analysis] "Static analysis of your application is performed at build-time from the `main` entry point. Code that cannot be reached when the native image is created will be removed and won't be part of the executable." | `official-vendor-doc` | dead code elimination 결과 | dynamic 하게 reachable 한 코드 (reflection 통한 호출) 가 어떻게 처리되는지는 별도 claim CGN-C5 참조 |
| CGN-C5 | GraalVM 은 reflection · resources · serialization · dynamic proxies 같은 dynamic 요소를 직접 인식하지 못하며, **명시적으로 알려주어야 한다** | [§Key differences with JVM — dynamic elements] "GraalVM is not directly aware of dynamic elements of your code and must be told about reflection, resources, serialization, and dynamic proxies." | `official-vendor-doc` | reflection-heavy Spring application | "어떻게 알려주는가" 의 구체 메커니즘 (RuntimeHints / reachability-metadata JSON) 은 본 인용에 명시 없음 — 별도 페이지 |
| CGN-C6 | Native Image 는 lazy class loading 이 없으며, executable 에 포함된 모든 것이 startup 시 메모리에 로드된다 | [§Key differences with JVM — class loading] "There is no lazy class loading, everything shipped in the executables will be loaded in memory on startup." | `official-vendor-doc` | Native Image runtime model | startup 후 메모리 사용량이 실제로 더 작다는 주장과 모순처럼 보이나, 인용은 단지 "lazy 가 없음" 만 말함 — dead code elimination 으로 최종 RAM 이 작아짐 |
| CGN-C7 | Native Image 는 container image 로 배포되는 application 에 적합하며, FaaS 플랫폼과 결합 시 특히 흥미롭다 | [§Best-suited applications] "They are well suited to applications that are deployed using container images and are especially interesting when combined with \"Function as a service\" (FaaS) platforms." | `official-vendor-doc` | FaaS / scale-to-zero / cold-start sensitive workload | 모든 container 배포에서 Native Image 가 더 낫다는 일반화는 아님 — "well suited" 와 "especially interesting" 의 조건부 표현 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CGN-C1`, `CGN-C7`: Native Image 의 정성적 benefit (startup, memory, FaaS 적합성)
- `CGN-C2`, `CGN-C3`: AOT 빌드 + standalone executable 의 정의
- `CGN-C4`, `CGN-C5`, `CGN-C6`: JVM 과의 핵심 차이 3개 (static analysis, dynamic 요소 명시 필요, no lazy class loading)
- **이 자료가 증명하지 않는 것**:
- 정량 수치 (startup 단축률, RSS 메모리 절감 %, image 크기) — 인용은 모두 정성 표현
- 빌드 시간 / CI 비용 — Native Image 빌드는 분 단위로 길지만 본 인용에는 명시 없음
- peak throughput 비교 (JIT C2 의 profile-guided 최적화 부재로 인한 영향)
- 특정 Spring starter / 라이브러리의 Native Image 호환성 매트릭스
- RuntimeHints / reachability-metadata JSON 의 작성 방법 (별도 페이지)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 에서 사용하는 라이브러리들의 reachability-metadata 제공 여부 (`META-INF/native-image/...`)
- Spring Boot Gradle plugin (`org.graalvm.buildtools.native`) 의 정확한 빌드 시간 측정
- Native Image 빌드된 ca-tmpl 의 cold-start time / RSS 실측
- Testcontainers + native executable 의 통합 테스트 패턴
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 적용 시나리오: serverless / FaaS, cold start 가 critical 한 워크로드, scale-to-zero 환경.
- 장점:
- cold start 가 수십 ms 단위 (JIT 대비 10배 이상 단축) — **별도 측정 필요, 본 인용은 정성 표현만**.
- RSS 메모리 사용량 30-50% 감소 — **별도 측정 필요**.
- container image 크기 축소 (JRE 미포함 시 ~80 MB 대) — `CGN-C3` 의 결과.
- 단점:
- 빌드 시간이 길어진다 (수 분 이상). CI 비용 증가.
- reflection / dynamic proxy / serialization 사용 시 `reachability-metadata` 또는 `RuntimeHints` 등록 필수 — `CGN-C5` 의 직접 결과.
- Spring AOT processing 은 일부 starter (특히 oldschool reflection 기반 라이브러리) 와 호환성 검증이 필요.
- 런타임 profiling 기반 최적화 (JIT C2) 가 사라져 peak throughput 은 JIT 대비 낮을 수 있다.
- ca-tmpl 과의 차이: ca-tmpl 은 JIT 기반 Temurin JRE slim + `MaxRAMPercentage=75` 로 운영 친숙도를 우선. native-image 는 cold start 우선 워크로드 한정 옵션.
- testability 영향: 하 — native-image 빌드 후의 동작 검증은 Testcontainers + native test 분리 필요.
- code 복잡도 영향: 상 — `RuntimeHintsRegistrar`, `@RegisterReflectionForBinding`, `META-INF/native-image/...` 메타데이터 관리 부담.
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/container-distroless-google-github]] — 대안 1 Distroless
- [[raw/official-docs/container-alpine-java-musl-tradeoffs]] — 대안 2 Alpine + Temurin
- 적용 branch-note:
- [[raw/branch-notes/feature-container-runtime-contract]]
- canonical contract 섹션:
- `wiki/projects/ca-skeleton-operational-contract` 의 container runtime canonical section (예정)
- 대안 그룹: **Group G-D — Container runtime** 대안 후보군
- 본 source 의 위치: 대안 3 — GraalVM native-image (AOT) + Spring Boot Native
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/container-stdout-logging-12factor-official.md
@@ -0,0 +1,91 @@
---
title: "Twelve-Factor App — XI. Logs (Factor 11: Treat logs as event streams)"
source_type: official-doc
url: https://12factor.net/logs
archive_url:
vendor: Heroku / Adam Wiggins
related_branches: [feature-log-management-contract]
related_projects: []
tags: [official-doc, ca-skeleton, observability, twelve-factor, stdout-logging, log-routing]
created: 2026-06-13
---
# Twelve-Factor App — XI. Logs (Factor 11: Treat logs as event streams)
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-log-management-contract]] | D4: production logging = stdout JSON default; file logging = local/dev only — 앱은 로그 라우팅·저장을 절대 직접 관리하지 않으며, 각 프로세스는 이벤트 스트림을 unbuffered 로 stdout 에 기록하고, 수집·라우팅은 실행 환경(컨테이너 런타임/플랫폼)이 담당한다는 Twelve-Factor 표준의 직접 근거 |
## 출처 / Source
- 원본 URL: https://12factor.net/logs
- 아카이브 URL: (없음 — 사용자 archive_url 미제공)
- 저자 / 조직: Adam Wiggins / Heroku (The Twelve-Factor App)
- 발행일: 2011년경 (원문 날짜 미표기)
- 마지막 확인일: 2026-06-13
## 왜 저장했는지 / Why archived
`feature-log-management-contract` D4("production logging = stdout JSON default; file logging = local/dev only")가 `UNSUPPORTED_DECISION`으로 표기되어 있었으며, 이를 뒷받침할 canonical industry standard가 필요했다. Twelve-Factor App Factor XI("Logs")는 앱이 로그 라우팅/저장을 관리해선 안 된다는 원칙의 직접적·공식적 출처다. D4를 `UNSUPPORTED_DECISION`에서 `official-standard` 근거 기반으로 승격하는 유일한 primary source.
## 핵심 인용 / Key quotes (verbatim, 5문장)
> [§XI Logs, para 3] "A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout."
> [§XI Logs, para 3, cont.] "During local development, the developer will view this stream in the foreground of their terminal to observe the app's behavior."
> [§XI Logs, para 4] "In staging or production deploys, each process' stream will be captured by the execution environment, collated together with all other streams from the app, and routed to one or more final destinations for viewing and long-term archival."
> [§XI Logs, para 4, cont.] "These archival destinations are not visible to or configurable by the app, and instead are completely managed by the execution environment."
> [§XI Logs, para 1] "Logs are the stream of aggregated, time-ordered events collected from the output streams of all running processes and backing services."
## Claims Extracted / 추출된 주장
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| LOG-12F-C1 | Twelve-Factor 앱은 로그의 라우팅·저장을 스스로 관리해선 안 된다 — logfile 쓰기·관리 시도 금지 | [§XI para 3] "A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles." | `official-standard` | Twelve-Factor 방법론을 따르는 모든 서버사이드 앱 (언어·프레임워크 무관) | 특정 컨테이너 런타임(Docker/K8s) 또는 프레임워크(Spring Boot)의 구체적 설정값을 직접 증명하지 않음. stdout JSON 포맷(구조화 여부)에 대한 언급 없음 |
| LOG-12F-C2 | 각 실행 중인 프로세스는 이벤트 스트림을 unbuffered 로 stdout 에 기록한다 | [§XI para 3] "each running process writes its event stream, unbuffered, to stdout." | `official-standard` | 모든 Twelve-Factor 앱 프로세스 | 특정 로그 포맷(JSON vs plain text)을 강제하지 않음. `unbuffered` 구현 방법(JVM flush 설정 등)을 명시하지 않음 |
| LOG-12F-C3 | staging/production 에서는 실행 환경이 프로세스 스트림을 캡처하고 앱의 모든 스트림과 합쳐 최종 목적지(장기 보관 포함)로 라우팅한다 | [§XI para 4] "In staging or production deploys, each process' stream will be captured by the execution environment, collated together with all other streams from the app, and routed to one or more final destinations for viewing and long-term archival." | `official-standard` | Twelve-Factor 앱이 배포된 staging/production 환경 | "실행 환경"의 구체적 구현(Logplex, Fluentd, Kubernetes logging driver 등)이 어떤 것이어야 하는지 규정하지 않음. 로컬 개발 환경에는 직접 적용되지 않음 |
| LOG-12F-C4 | 로그 최종 아카이브 목적지는 앱에게 보이지 않으며 앱이 설정할 수 없고, 실행 환경이 완전히 관리한다 | [§XI para 4] "These archival destinations are not visible to or configurable by the app, and instead are completely managed by the execution environment." | `official-standard` | production/staging 배포 환경의 앱 코드 레이어 | 로그 목적지(Splunk, Elasticsearch, CloudWatch 등) 선택의 우열을 규정하지 않음. 앱이 로그 메타데이터(structured fields)를 풍부하게 제공하는 것의 금지를 의미하지 않음 |
| LOG-12F-C5 | 로그는 모든 실행 중인 프로세스와 backing service 의 출력 스트림에서 수집된 집계된·시간 순서 이벤트 스트림이다 — 고정된 시작/끝이 없으며 앱 동작 중 지속적으로 흐른다 | [§XI para 1] "Logs are the stream of aggregated, time-ordered events collected from the output streams of all running processes and backing services. [...] Logs have no fixed beginning or end, but flow continuously as long as the app is operating." | `official-standard` | 모든 Twelve-Factor 앱 | 로그가 반드시 구조화(JSON) 형식이어야 한다는 요구사항은 없음. 로그 sampling, 레벨 정책, MDC field 명세 등은 이 Factor 의 범위 밖 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `LOG-12F-C1`: 앱 코드에서 logfile 직접 쓰기·관리는 Twelve-Factor 원칙 위반임
- `LOG-12F-C2`: 각 프로세스가 stdout 으로 unbuffered 출력하는 것이 표준 구현 방식임
- `LOG-12F-C3`: staging/prod 에서 스트림 캡처·라우팅은 실행 환경의 책임임 (앱 책임 아님)
- `LOG-12F-C4`: 앱은 로그 목적지를 알 필요도, 설정할 권한도 없음
- `LOG-12F-C5`: 로그의 개념적 정의 (스트림, 시간 순서, 연속성)
- 이 자료가 증명하지 않는 것:
- stdout 로그의 **포맷** (JSON vs plain-text) — 포맷 선택은 별도 근거 필요 (ECS, OTel, Logstash 등)
- `unbuffered` 의 구체적 구현 (JVM 의 `-Djava.util.logging.manager` 설정, Spring Boot Logback flush 정책 등)
- 로그 sampling 비율 (D5 의 prod 10% / WARN·ERROR 100% 정책은 별도 근거 없음 — UNSUPPORTED_DECISION)
- Kubernetes 또는 Docker 에서의 구체적 container logging driver 설정
- Spring Boot `logback-spring.xml``<springProfile>` 분기 (D10) 구현 방법
- file appender 를 **절대** 써선 안 된다는 결론 — "local/dev 에서 파일 로그를 사용하는 것"은 Factor XI 를 위반하지 않음 (개발자가 터미널 외 파일로 보는 것은 허용 패턴). 단, production 에서 앱이 직접 logfile 을 관리하는 것은 위반
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- Spring Boot + Logback + `logstash-logback-encoder` 조합에서 stdout 출력이 실제로 unbuffered 인지 (JVM 버퍼링 여부) — locally-verified 필요
- `FILE_ENABLED=true` 가 local/dev 에만 적용되는지 환경 게이트 검증 — D4 구현 현황에서 toggle 은 actually-implemented 이나 prod 오활성화 방지 테스트 별도 필요
- K8s 배포 환경에서 stdout → container runtime → logging driver 체인의 실제 동작 확인 (Logplex/Fluentd 대안)
## 메모 / Notes
- Factor XI 는 로그 포맷을 규정하지 않는다. JSON 구조화 로그(D1)는 별도 근거(`log-logback-mask-pattern-converter-official`, `log-ecs-schema-elastic-official`)에서 뒷받침된다.
- `config-12-factor-app-config.md`(Factor III) 와 같은 출처(12factor.net)이며, 같은 방법론의 다른 Factor 다.
- 추가로 봐야 할 동일 출처 페이지: https://12factor.net (전체 12 Factors 개요) — 특히 Factor III(Config), Factor IX(Disposability), Factor XII(Admin processes)가 ca-tmpl 운영 계약과 연관됨.
## Related / 관련
- 같은 출처 다른 Factor: [[raw/official-docs/config-12-factor-app-config]] (Factor III — 환경 변수 설정)
- 같은 주제 다른 official-doc: [[raw/official-docs/log-logback-mask-pattern-converter-official]] (D1/D10 근거), [[raw/official-docs/log-ecs-schema-elastic-official]] (D6 근거), [[raw/official-docs/log-otel-log-data-model-spec]] (D7 근거)
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-log-management-contract]] (D4 Decision Evidence Map)
- 이 자료를 인용한 wiki 요약: (미생성 — `/ingest``wiki/concepts/` 에 작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cosign-keyless-identity-verification-policy.md
@@ -0,0 +1,106 @@
---
title: Cosign Keyless Identity Verification Policy
source_type: official-doc
status: raw
confidence: high
url: https://docs.sigstore.dev/cosign/verifying/verify/
archive_url:
tags: [ca-supply-chain, cosign, sigstore, keyless, identity-verification]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-build-release-supply-chain-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Cosign Keyless Identity Verification Policy
> Layer: `raw/official-docs/` — Sigstore Cosign 공식 docs (`docs.sigstore.dev`) 의 keyless verify 명령 + identity 매칭 flag verbatim 발췌. ca-tmpl 의 "Cosign keyless signing 의무" 결정 누락분 (identity 매칭 정책) 의 보강 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-build-release-supply-chain-contract]] | "Cosign keyless signing 의무, signature 없이 deploy forbidden" 의 보강 — keyless 모드는 `--certificate-identity` + `--certificate-oidc-issuer` 가 필수임을 박는다. (G-E 후속 보강) |
## 컨텍스트 / 왜 저장했는지
ca-tmpl `feature-build-release-supply-chain-contract` branch 가 "Cosign keyless signing 의무, signature 없이 deploy forbidden" 까지만 결정하고 **identity 매칭 정책** (`--certificate-identity` + `--certificate-oidc-issuer`) 을 누락한 것이 G-E 후속 보강 항목으로 식별됐다. Sigstore 공식 문서가 keyless 모드에서 두 flag 의 사용을 명시 제시하므로, signature 존재 검증만으로는 임의의 OIDC identity 가 만든 서명도 통과할 수 있다는 사고 시나리오를 외부 근거로 박아두기 위함.
## 출처 / Source
- 원본 URL: https://docs.sigstore.dev/cosign/verifying/verify/
- 보조 출처: https://github.com/sigstore/cosign/issues/3671 (cosign verify 키리스 검증 시 identity flag 강제 동작 확인 — sigstore/cosign issue tracker)
- 보조 출처: https://www.qcecuring.com/blog/sigstore-cosign-keyless-github-actions (GitHub Actions OIDC identity 포맷 — 3rd-party blog, **참고용**)
- 아카이브 URL: (미수집)
- 저자 / 조직: Sigstore project (Linux Foundation)
- 발행일: docs.sigstore.dev 현행 문서 (fetch 일자 2026-05-22, 재확인 2026-05-27)
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Verifying Signatures — identity-based verification command] "cosign verify <image URI> --certificate-identity=name@example.com --certificate-oidc-issuer=https://accounts.example.com"
> [§OIDC Issuer Endpoints] "Google: https://accounts.google.com" + "Microsoft: https://login.microsoftonline.com" + "GitHub: https://github.com/login/oauth" + "GitLab: https://gitlab.com"
> [§Identity flag 강제 — sigstore/cosign Issue #3671 (보조 출처)] "--certificate-identity or --certificate-identity-regexp is required for verification in keyless mode"
> [§Keyless 모델 — Sigstore docs paraphrase] 키리스 검증은 identity-based approach (OIDC issuer 와 결합) 를 사용하며, signing service 는 long-term key 가 아닌 short-lived credential 을 통해 identity 와 signature 를 연결한다. (docs.sigstore.dev 본문 요약 — verbatim "keyless signing" 정의 문장은 본 verify 페이지에 단독 존재하지 않으며, 본 인용은 페이지가 시사하는 모델 정리.)
> [§GitHub Actions OIDC subject 형식 — 3rd-party blog 보조 출처] GitHub Actions OIDC 로 서명된 image 의 expected `--certificate-identity` 는 워크플로 경로 + git ref 형식: `https://github.com/<ORG>/<REPO>/.github/workflows/<WORKFLOW-FILE>@refs/heads/<BRANCH>` (또는 `@refs/tags/<TAG>`).
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CSIGN-KL-C1 | identity-based 검증의 cosign verify 명령은 `--certificate-identity=<subject>``--certificate-oidc-issuer=<issuer URL>` 두 flag 를 함께 사용 | [§Verifying Signatures] "cosign verify <image URI> --certificate-identity=name@example.com --certificate-oidc-issuer=https://accounts.example.com" | `official-vendor-doc` | Sigstore Cosign keyless 검증 | 두 flag 가 모든 cosign verify 모드에서 강제라는 뜻은 아님 — key-based 검증은 `--key` 사용 (별도 모드) |
| CSIGN-KL-C2 | Sigstore docs 가 제시하는 OIDC issuer 예시 URL: Google = `https://accounts.google.com`, Microsoft = `https://login.microsoftonline.com`, GitHub (사람 사용자) = `https://github.com/login/oauth`, GitLab = `https://gitlab.com` | [§OIDC Issuer Endpoints] "Google: https://accounts.google.com" + "Microsoft: https://login.microsoftonline.com" + "GitHub: https://github.com/login/oauth" + "GitLab: https://gitlab.com" | `official-vendor-doc` | 사람 사용자 OIDC issuer 매칭 | GitHub Actions OIDC token issuer (`https://token.actions.githubusercontent.com`) 와 동일하지 않음 — CI 환경은 별도 issuer URL 사용 (docs verify 페이지에 명시 없음 — 보조 출처 / 별도 docs 확인 필요) |
| CSIGN-KL-C3 | keyless 모드에서 `--certificate-identity` (또는 `--certificate-identity-regexp`) 는 검증에 필수 | [§sigstore/cosign Issue #3671] "--certificate-identity or --certificate-identity-regexp is required for verification in keyless mode" | `needs-confirmation` | Sigstore Cosign keyless verify | 본 인용은 cosign issue tracker (보조 출처) 기반. 공식 docs 가 동일 문장으로 명시했는지는 별도 확인 필요. `--certificate-oidc-issuer` 가 동일하게 필수인지도 별도 확인 필요 |
| CSIGN-KL-C4 | Sigstore 키리스 모델 = identity-based verification + short-lived credentials (long-term key 미사용) | [§Keyless 모델] (docs paraphrase) 키리스 검증은 identity-based approach 를 사용하며 long-term key 가 아닌 short-lived credential 을 통해 identity 와 signature 를 연결 | `needs-confirmation` | Sigstore Cosign keyless 일반 모델 이해 | 본 인용은 verify 페이지 paraphrase 이며 verbatim "keyless signing" 정의 문장 출처는 별도 페이지 (Fulcio 등). 본 자료만으로 Fulcio 의 정확한 동작을 증명하지 않음 |
### Strength 근거
- `CSIGN-KL-C1`, `CSIGN-KL-C2`: `official-vendor-doc` — Sigstore docs.sigstore.dev 공식 verify 페이지 verbatim
- `CSIGN-KL-C3`: `needs-confirmation` — issue tracker 기반. 공식 docs 동일 문장 확인 필요
- `CSIGN-KL-C4`: `needs-confirmation` — verify 페이지 paraphrase, 정의 문장 출처 별도 페이지 확인 필요
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CSIGN-KL-C1`: identity-based verify 의 정확한 cosign 명령 구문 (두 flag 함께 사용)
- `CSIGN-KL-C2`: 사람 사용자 OIDC issuer URL 의 정확한 형식
- **이 자료가 증명하지 않는 것**:
- 두 flag (`--certificate-identity` + `--certificate-oidc-issuer`) 가 keyless 모드에서 모두 hard-required 라는 cosign CLI 동작 (issue tracker 기반 보조 출처. 본 docs 페이지는 권장 예시로만 제시. 1차 공식 인용 확인 필요)
- GitHub Actions OIDC 의 정확한 expected `--certificate-identity` 포맷 (보조 출처 / GitHub OIDC docs 별도 확인 필요)
- Kubernetes admission controller (Sigstore policy-controller / Kyverno) 의 정확한 verify rule 구문 (별도 admission controller docs)
- Cosign 이 사용하는 DSSE envelope signing 알고리즘 (별도 sigstore docs)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl deploy gate 가 GitHub Actions OIDC 기반인 경우, expected identity 의 정확한 워크플로 경로 + ref 매칭 규칙
- Cosign CLI 의 정확한 fail-fast 동작 (identity mismatch 시 exit code 등)
- admission controller 또는 Kyverno 정책 syntax 의 expected identity/issuer 선언 방식
## 메모 / Notes (내 프로젝트 해석)
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
- 키리스 검증의 두 강제 flag (cosign CLI 동작 + Sigstore docs 결합 권장):
- `--certificate-identity=<expected subject>` (또는 `--certificate-identity-regexp`)
- `--certificate-oidc-issuer=<expected issuer URL>` (또는 `--certificate-oidc-issuer-regexp`)
- 둘 중 하나만 검사하면 우회 가능 (사고 모델):
- issuer 만 검사 → 같은 IdP 사용자라면 누구든 통과 (예: 같은 GitHub org 의 다른 repo workflow 도 통과)
- identity 만 검사 → IdP 가 임의여도 통과 (예: 동일 subject 문자열을 발급하는 다른 OIDC IdP)
- 클러스터 단 강제: Kubernetes admission controller (Sigstore policy-controller, Kyverno `verifyImages` 룰) 에서 expected identity/issuer 를 정책으로 선언해 unsigned + identity-mismatch image 를 admission 단계에서 차단.
- ca-tmpl 약식 표현 정정 필요 지점: 단순 "Cosign signature 누락 차단" 이 아니라 "Cosign signature + identity 매칭 차단".
- 본 파일은 docs.sigstore.dev fetch 결과 + sigstore/cosign issue tracker + 정리 블로그 교차 확인으로 작성. 인용은 Sigstore 공식 docs 1차 출처를 우선으로 표기 (CSIGN-KL-C1, C2). 보조 출처 기반 claim 은 `needs-confirmation` 으로 표기 (C3, C4).
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/supply-chain-slsa-provenance-framework]] (SLSA build level + provenance)
- [[raw/official-docs/slsa-v1-provenance-schema]] (in-toto Statement / DSSE envelope subject)
- 인용하는 branch:
- [[raw/branch-notes/feature-build-release-supply-chain-contract]] — Cosign keyless 의무 결정의 원천 branch-note. identity 매칭 정책 누락이 본 문서 작성 trigger
- 인용하는 project-note:
- [[raw/project-notes/ca-skeleton-operational-contract]] — Contract Registry (canonical SSOT). 본 보강은 supply chain contract 항목에 반영되어야 함
- 인용하는 wiki:
- [[wiki/concepts/devops-ci-supply-chain-dx]] — 한계/주의점 섹션 Cosign 항목과 직접 연결
- [[wiki/projects/ca-tmpl/devops-ci-supply-chain-dx]] — documented-only/planned 섹션 보강 대상
-1
View File
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cqrs-fowler-bliki.md
+99
View File
@@ -0,0 +1,99 @@
---
title: CQRS — Martin Fowler bliki 원문
source_type: official-doc
url: https://martinfowler.com/bliki/CQRS.html
archive_url:
status: raw
confidence: medium
tags: [architecture, cqrs, read-model, write-model, ddd, event-sourcing, ca-skeleton-operational-contract]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-repository-access-permission-contract, feature-domain-modeling-guardrails]
created: 2026-05-27
last_reviewed: 2026-05-27
---
# CQRS — Martin Fowler bliki 원문
> Layer: `raw/official-docs/` — Martin Fowler 의 bliki "CQRS" (2011-07-14) 원문 발췌. read model 과 write model 의 분리, CQRS 적용 시점/위험에 대한 1차 인용 출처.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-repository-access-permission-contract]] | D10 (Repository 가 command path 와 query path 에서 동일 interface 를 강제할지, 또는 query 전용 read model 을 별도 도입할지) 결정의 근거 |
| [[raw/branch-notes/feature-domain-modeling-guardrails]] | 도메인 모델을 update/display 두 모델로 분리할지 단일 모델로 유지할지의 가드레일 근거 — Fowler 의 "be very cautious about using CQRS" 경고 포함 |
## 컨텍스트
ca-tmpl 의 Repository 가 단일 인터페이스로 read/write 를 모두 책임지는 단순 모델을 권장할지, 아니면 처음부터 read model 분리를 청사진에 넣을지의 결정. Fowler 의 bliki 가 "CQRS 를 무차별 적용하지 말라" 는 보수적 입장을 명시하므로, ca-tmpl skeleton 의 default 결정 (단일 모델 + 필요 시 분리) 의 1차 근거가 됨.
## 출처 / Source
- 원본 URL: https://martinfowler.com/bliki/CQRS.html
- 아카이브 URL:
- 저자 / 조직: Martin Fowler — bliki (`martinfowler.com/bliki/`), personal blog
- 발행일: 2011-07-14
- 마지막 확인일: 2026-05-27
## 핵심 인용 / Key quotes (verbatim)
> [§Opening] "you can use a different model to update information than the model you use to read information"
> [§Main content — CRUD baseline] "The mainstream approach people use for interacting with an information system is to treat it as a CRUD datastore"
> [§Main content — separate models] "The change that CQRS introduces is to split that conceptual model into separate models for update and display"
> [§When to use it — scaling benefit] "CQRS allows you to separate the load from reads and writes allowing you to scale each independently"
> [§When to use it — caution] "you should be very cautious about using CQRS. Many information systems fit well with the notion of an information base"
> [§When to use it — complexity] "adding CQRS to such a system can add significant complexity"
> [§Architectural patterns — event sourcing combination] "It's common to see CQRS system split into separate services communicating with Event Collaboration"
## Claims Extracted / 추출된 주장
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CQRS-FOWLER-C1 | CQRS 의 기본 정의는 **읽을 때 사용하는 모델과 갱신할 때 사용하는 모델을 다르게** 쓰는 것 | [§Opening] "you can use a different model to update information than the model you use to read information" | `engineering-blog` | read model 과 write model 의 분리를 검토하는 시스템 | 두 모델이 반드시 별도 저장소·별도 서비스여야 한다는 강제는 아님 — 같은 DB 안의 다른 view/projection 도 CQRS 정의에 부합 |
| CQRS-FOWLER-C2 | mainstream 접근은 정보 시스템을 **CRUD datastore** 처럼 다루는 것 — CQRS 는 이 대안 | [§Main content] "The mainstream approach people use for interacting with an information system is to treat it as a CRUD datastore" | `engineering-blog` | 일반 CRUD 위주 시스템과의 비교 | CRUD 자체가 잘못된 접근이라는 의미는 아님 — Fowler 는 후반에 "many systems fit well with information base" 라고 CRUD 를 변호 |
| CQRS-FOWLER-C3 | CQRS 가 도입하는 변화의 핵심은 **개념 모델을 update 용과 display 용 두 모델로 분리** | [§Main content] "The change that CQRS introduces is to split that conceptual model into separate models for update and display" | `engineering-blog` | application 의 domain/read model 설계 | 분리가 반드시 데이터 저장 레벨까지 가야 한다는 강제는 아님 (개념 모델 분리만으로도 CQRS 정의 충족) |
| CQRS-FOWLER-C4 | CQRS 의 잠재 이득 중 하나는 read/write 부하를 분리하여 **각각 독립적으로 scale** 할 수 있다는 점 | [§When to use it] "CQRS allows you to separate the load from reads and writes allowing you to scale each independently" | `engineering-blog` | read-heavy + write-heavy 가 비대칭인 시스템 | 모든 시스템이 이 분리 scaling 으로 이득을 본다는 의미는 아님 — read/write 비율이 비대칭일 때만 의미 |
| CQRS-FOWLER-C5 | Fowler 는 CQRS 사용에 **매우 신중할 것 (very cautious)** 을 권고 — 많은 정보 시스템은 information base 개념에 잘 맞기 때문 | [§When to use it] "you should be very cautious about using CQRS. Many information systems fit well with the notion of an information base" | `engineering-blog` | CQRS 채택 의사결정 단계 | 모든 시스템에서 CQRS 가 부적합하다는 강제는 아님 — collaborative domain / 비대칭 부하 등 특정 조건에서 적합 |
| CQRS-FOWLER-C6 | 부적합한 시스템에 CQRS 를 추가하면 **significant complexity** 가 더해질 수 있음 | [§When to use it] "adding CQRS to such a system can add significant complexity" | `engineering-blog` | CRUD 와 잘 맞는 시스템에 CQRS 추가 시 | "significant" 의 정량적 측정은 없음 (코드 라인 수 / 운영 비용 등 구체 수치는 본 인용 밖) |
| CQRS-FOWLER-C7 | CQRS 시스템은 **Event Collaboration 으로 통신하는 분리된 서비스** 로 split 되는 경우가 흔함 (event sourcing/event-driven 연계) | [§Architectural patterns] "It's common to see CQRS system split into separate services communicating with Event Collaboration" | `engineering-blog` | CQRS + event sourcing + microservices 결합 시나리오 | CQRS 가 반드시 event sourcing 과 결합되어야 한다는 강제는 아님 — "common" 일 뿐, 본 인용으로 의존성 입증은 못 함 |
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CQRS-FOWLER-C1`~`C3`: CQRS 의 정확한 정의 (read/write 모델 분리) + CRUD 와의 대비
- `CQRS-FOWLER-C4`: scaling 이득의 메커니즘 (read/write 부하 독립 scaling)
- `CQRS-FOWLER-C5`~`C6`: Fowler 의 명시적 보수적 권고 ("be very cautious", "significant complexity")
- `CQRS-FOWLER-C7`: CQRS 와 event collaboration 의 흔한 결합 (common, not mandatory)
- **이 자료가 증명하지 않는 것**:
- 본 글이 **공식 표준 또는 vendor doc** 이라는 점 — Fowler bliki 는 personal blog. ThoughtWorks 의 공식 입장이 아님. strength `engineering-blog`.
- CQRS 가 반드시 event sourcing / 별도 read DB / eventual consistency 를 요구한다는 점 (Fowler 본문은 "common" 이라고만 표현)
- ca-tmpl 의 default 가 단일 모델이어야 한다는 결정 — Fowler 의 caution 은 일반 가이드이며, 특정 프로젝트의 default 결정과 자동 1:1 매칭되지 않음
- 구체적인 read model 구현 형태 (materialized view / projection / cache / 별도 service) 의 선택 기준
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 Repository 가 query / command 분리 인터페이스를 강제할지 ([[raw/branch-notes/feature-repository-access-permission-contract]] D10)
- 분리 도입 시 read model 의 저장 위치 (동일 RDB view / 별도 search index / cache layer)
- eventual consistency 가 도입될 경우 사용자 경험·UI 보정 정책
## 메모 / Notes
- Fowler 의 핵심 메시지는 "CQRS 는 strong tool 이지만, 무차별 사용은 해롭다" — `wiki/concepts/cqrs.md` 작성 시 이 caution 을 본문 상단에 명시할 것.
- 본 글의 후반부 ("information base", "task-based UI" 등) 는 별도 추가 인용 필요 — 본 raw 는 정의 + scaling + 경고 + event collaboration 4개 축만 보장.
- DDD 의 Aggregate 와 CQRS 의 관계 (read model 이 aggregate boundary 를 우회하는 패턴) 는 본 글에 직접 없음 — [[raw/branch-notes/feature-domain-modeling-guardrails]] 에서 별도 출처 필요.
## Related / 관련
- 같은 주제 다른 raw:
- [[raw/official-docs/microservices-io-transactional-outbox]] (CQRS 와 자주 결합되는 outbox 패턴)
- [[raw/official-docs/arch-hexagonal-cockburn]] (port 분리와 read/write 분리의 개념적 연결)
- 이 자료를 인용하는 branch:
- [[raw/branch-notes/feature-repository-access-permission-contract]]
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
- 인용하는 project:
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 이 자료를 인용한 wiki 요약: (미작성)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cqrs-pattern-azure-architecture-center.md
@@ -0,0 +1,97 @@
---
title: official-doc / CQRS Pattern — Azure Architecture Center (Microsoft)
source_type: official-doc
url: https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs
archive_url:
status: raw
confidence: high
tags: [architecture, cqrs, read-model, write-model, materialized-view, event-sourcing, ca-skeleton]
related_branches: [feature-application-query-bypass-contract]
related_projects: [ca-skeleton]
created: 2026-06-04
last_reviewed: 2026-06-04
---
# CQRS Pattern — Azure Architecture Center (Microsoft)
> Layer: `raw/official-docs/` — Microsoft Azure Architecture Center 의 CQRS Pattern 공식 가이드 (2025-02-20 갱신). "single data store CQRS" 와 "separate data stores CQRS" 의 공식 two-tier 분류, 복잡성 경고, 적용 조건을 포함. ca-tmpl 의 CQRS-lite (Alt 2) 와 Full CQRS (Alt 3) 의 결정 근거.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-application-query-bypass-contract]] | D2 (CQRS-lite — same store 에서 read/write model 분리) 와 D3 (Full CQRS — separate data stores) 의 공식 근거. "simple CRUD" 에는 부적합하다는 Azure 경고가 skeleton default 결정의 보수적 기준을 뒷받침 |
## 출처 / Source
- 원본 URL: https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs
- 아카이브 URL:
- 저자 / 조직: Microsoft — Azure Architecture Center (CAF/WAF 팀)
- 발행일: 2025-02-20 (last updated)
- 마지막 확인일: 2026-06-04
## 왜 저장했는지 / Why archived
Microsoft 의 공식 클라우드 아키텍처 패턴 가이드 (Azure Architecture Center) 가 CQRS 를 single data store 와 separate data stores 두 tier 로 공식 분류한다. 이 두-tier 분류가 ca-tmpl CQRS-lite (Alt 2) vs Full CQRS (Alt 3) 결정의 공식적 프레임. 복잡성 경고 ("this pattern might not be suitable when domain is simple") 는 skeleton default 선택의 근거가 됨.
## 핵심 인용 / Key quotes (verbatim)
> [§Solution — queries definition] "Queries never alter data. Instead, they return data transfer objects (DTOs) that present the required data in a convenient format, without any domain logic."
> [§Separate models — single data store] "This approach represents the foundational level of CQRS, where both the read and write models share a single underlying database but maintain distinct logic for their operations."
> [§Separate models — single data store, read model] "A read model is designed to serve queries for retrieving data. It focuses on generating DTOs or projections that are optimized for the presentation layer. It enhances query performance and responsiveness by avoiding domain logic."
> [§Separate models — different data stores] "A more advanced CQRS implementation uses distinct data stores for the read and write models. Separation of the read and write data stores allows you to scale each model to match the load."
> [§Separate models — sync] "When you use separate data stores, you must ensure that both remain synchronized. A common pattern is to have the write model publish events when it updates the database, which the read model uses to refresh its data."
> [§Problems — eventual consistency] "When the read databases and write databases are separated, the read data might not show the most recent changes immediately. This delay results in stale data."
> [§Problems — complexity] "The core concept of CQRS is straightforward, but it can introduce significant complexity into the application design, specifically when combined with the Event Sourcing pattern."
> [§When to use — performance tuning] "Systems where the performance of data reads must be fine-tuned separately from performance of data writes benefit from CQRS. This pattern is especially beneficial when the number of reads is greater than the number of writes."
> [§When NOT to use — simple domain] "This pattern might not be suitable when: The domain or the business rules are simple. A simple CRUD-style user interface and data access operations are sufficient."
> [§Benefits — independent scaling] "CQRS enables the read models and write models to scale independently. This approach can help minimize lock contention and improve system performance under load."
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| AZURE-CQRS-C1 | CQRS query 측은 데이터를 변경하지 않으며 domain logic 없이 DTO 를 반환 | [§Solution] "Queries never alter data. Instead, they return data transfer objects (DTOs) that present the required data in a convenient format, without any domain logic." | `official-vendor-doc` | CQRS 에서 query model 의 역할 정의 — ca-tmpl QueryUseCase 반환 타입 설계에 적용 | DTO 가 aggregate 를 통해 생성되어야 하는지 직접 projection 이어야 하는지는 본 인용이 명시 안 함 |
| AZURE-CQRS-C2 | CQRS 의 "foundational level" 은 single database 를 공유하되 read/write logic 을 분리하는 것 (CQRS-lite) | [§Single data store] "This approach represents the foundational level of CQRS, where both the read and write models share a single underlying database but maintain distinct logic for their operations." | `official-vendor-doc` | 단일 관계형 DB 위에서 read/write model 을 분리하는 패턴 — ca-tmpl Alt 2 의 정의 | "foundational" 이 "기본값이어야 한다" 는 권고는 아님 — Microsoft 는 use-case 별 선택을 권고 |
| AZURE-CQRS-C3 | read model 은 domain logic 없이 presentation 에 최적화된 DTO/projection 생성에 집중 | [§Single data store, read model] "A read model is designed to serve queries for retrieving data. It focuses on generating DTOs or projections that are optimized for the presentation layer. It enhances query performance and responsiveness by avoiding domain logic." | `official-vendor-doc` | CQRS read model 의 역할과 구현 방향 | "presentation layer" 에 최적화된다는 뜻이 web adapter 에 직접 의존해야 한다는 의미는 아님 — hexagonal 에서 port 를 통해 projection DTO 반환 가능 |
| AZURE-CQRS-C4 | "more advanced" CQRS 는 read/write 각각 다른 data store 를 사용하며 독립 scaling 이 가능 | [§Separate stores] "A more advanced CQRS implementation uses distinct data stores for the read and write models." | `official-vendor-doc` | separate data store 가 필요한 CQRS (Alt 3) | "more advanced" = "더 나은" 이 아님 — 더 복잡한 패턴이라는 의미 |
| AZURE-CQRS-C5 | separate data stores CQRS 는 두 store 간 동기화가 필요하며 write model 이 event 를 publish 해 read model 을 갱신하는 것이 common pattern | [§Separate stores — sync] "A common pattern is to have the write model publish events when it updates the database, which the read model uses to refresh its data." | `official-vendor-doc` | separate store CQRS 의 동기화 메커니즘 | "이 방식이 유일한 동기화 방법" 은 아님 — CDC (Debezium 등) 도 valid 대안 |
| AZURE-CQRS-C6 | separate store CQRS 는 eventual consistency 를 유발 — read data 가 최신 변경을 즉시 반영 못할 수 있음 | [§Problems] "When the read databases and write databases are separated, the read data might not show the most recent changes immediately. This delay results in stale data." | `official-vendor-doc` | separate store CQRS 를 채택한 시스템 | single store CQRS-lite 는 이 eventual consistency 문제가 없음 — 같은 DB 에서 일관된 read 가능 |
| AZURE-CQRS-C7 | CQRS 는 단순 도메인 또는 simple CRUD UI 에는 적합하지 않음 | [§When not to use] "This pattern might not be suitable when: The domain or the business rules are simple. A simple CRUD-style user interface and data access operations are sufficient." | `official-vendor-doc` | CQRS 채택 결정의 "not suitable" 조건 — ca-tmpl skeleton default 로 full CQRS 를 채택하지 않는 근거 | "CQRS-lite (single store) 도 불필요하다" 는 뜻은 아님 — 본 인용은 separate store CQRS 와 event sourcing 결합의 복잡성 맥락 |
| AZURE-CQRS-C8 | CQRS 는 read > write 인 비대칭 부하 또는 read/write 각각 독립 성능 튜닝이 필요한 시스템에 이득 | [§When to use — performance] "Systems where the performance of data reads must be fine-tuned separately from performance of data writes benefit from CQRS. This pattern is especially beneficial when the number of reads is greater than the number of writes." | `official-vendor-doc` | read/write 부하가 비대칭인 시스템에서 CQRS 채택 조건 | "reads > writes" 가 항상 CQRS 를 정당화하지는 않음 — single store projection 으로도 해결 가능한 경우 있음 |
| AZURE-CQRS-C9 | CQRS 는 도메인 로직이 복잡하고 event sourcing 과 결합 시 significant complexity 를 유발 | [§Problems] "The core concept of CQRS is straightforward, but it can introduce significant complexity into the application design, specifically when combined with the Event Sourcing pattern." | `official-vendor-doc` | event sourcing + CQRS 결합 시 | CQRS 단독 (event sourcing 없이) 의 complexity 는 본 인용에서 별도 언급 없음 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `AZURE-CQRS-C1`~`C3`: CQRS query 측의 "no domain logic + DTO" 원칙 + single store 의 공식 "foundational" 레벨 분류
- `AZURE-CQRS-C4`~`C6`: separate store CQRS 의 정의 + sync mechanism + eventual consistency 문제
- `AZURE-CQRS-C7`~`C9`: CQRS 의 "when not to use" 조건 + 적합 조건 + complexity 경고
- 이 자료가 증명하지 않는 것:
- Java/Spring Boot 환경에서의 구체 구현 방식
- ArchUnit 으로 CQRS pattern 을 강제하는 방법
- hexagonal architecture 와 CQRS 의 통합 패턴 (application port / adapter 배치)
- "foundational level (single store)" 이 ca-tmpl skeleton 의 default 여야 한다는 결정 — Azure 가 권고한 것이 아니라 본 research 의 inference
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- Alt 2 (CQRS-lite) 에서 read model 이 application layer 의 port 를 통해 반환될 때 hexagonal purity 유지 방법 (web DTO / JPA entity leak 방지)
- Alt 3 (Full CQRS) 를 escalation 조건으로만 채택할 경우 opt-in 계약의 범위
## 메모 / Notes
- Azure Well-Architected Framework 의 "Performance Efficiency" pillar 근거로 CQRS 채택을 권고 — 이는 platform-agnostic guidance 이며 Java/Spring 특화 내용 아님
- "foundational level = single store" + "more advanced = separate stores" 두-tier 분류는 ca-tmpl Alt 2 와 Alt 3 의 official framing 으로 직접 활용 가능
## Related / 관련
- [[raw/official-docs/cqrs-fowler-bliki]] — CQRS 개념 원작자 Martin Fowler 의 caution 경고
- [[raw/company-tech-blogs/domain-event-sourcing-vs-cqrs-greg-young]] — CQRS 원작자 Greg Young 의 정의
- [[raw/branch-notes/feature-application-query-bypass-contract]] — 본 자료를 소비하는 branch
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/crockford-base32-spec.md
@@ -0,0 +1,94 @@
---
title: "official-doc / Crockford's Base32 — Human-Friendly 32-Symbol Encoding Specification"
source_type: official-doc
url: https://www.crockford.com/base32.html
archive_url:
related_branches: [feature-resource-identifier-contract]
related_projects: [ca-skeleton]
tags: [official-doc, ca-skeleton, api-design, ulid, base32-encoding, resource-identifier]
created: 2026-05-31
---
# Crockford's Base32 — Human-Friendly 32-Symbol Encoding Specification
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
## Parent / 활용 branch (필수, 최소 1개+)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-resource-identifier-contract]] | D2 (charset / encoding) — Crockford base32 32자 심볼 셋 정의 + human-friendly 설계 근거 (I/L/O/U 제거 이유); D3 (case sensitivity) — 디코딩 시 대소문자 무관 + I/L/i/l/1 → 1, O/o → 0 정규화 규칙 근거 |
## 출처 / Source
- 원본 URL: https://www.crockford.com/base32.html
- 아카이브 URL: (미등록 — 향후 archive.org 스냅샷 추가 권장)
- 저자 / 조직: Douglas Crockford (개인 사양 — 개인이 관리하는 비공식 표준. IETF 표준 아님)
- 발행일: 2002-11-02 (페이지 하단 날짜 기준)
- 마지막 확인일: 2026-05-31
## 왜 저장했는지 / Why archived
ULID 는 내부적으로 Crockford base32 를 채택하여 26자 문자열을 생성한다. ca-skeleton 의 resource ID charset / encoding 결정(D2)과 case-sensitivity 정책(D3)의 근거로서, Crockford 가 직접 기술한 심볼 셋 정의·제외 이유·디코딩 정규화 규칙을 원문 그대로 보존한다. RFC 4648 base32 와의 차이(I/L/O/U 제거, 대소문자 정규화)를 증명하는 1차 출처.
## 핵심 인용 / Key quotes (verbatim, 5문장)
> [§Symbols] "We chose a symbol set of 10 digits and 22 letters. We exclude 4 of the 26 letters: I, L, O, U."
> — (line 25 in fetched text)
> [§Symbols — Excluded Letters] "I — Can be confused with 1" / "L — Can be confused with 1" / "O — Can be confused with 0" / "U — Accidental obscenity"
> — (lines 2831 in fetched text)
> [§Symbols] "When decoding, upper and lower case letters are accepted, and i and l will be treated as 1 and o will be treated as 0. When encoding, only upper case letters are used."
> — (line 33 in fetched text)
> [§Symbols] "Hyphens (-) can be inserted into symbol strings. This can partition a string into manageable pieces, improving readability by helping to prevent confusion. Hyphens are ignored during decoding. An application may look for hyphens to assure symbol string correctness."
> — (line 37 in fetched text)
> [§Base] "Base 32 seems the best balance between compactness and error resistance. Each symbol carries 5 bits."
> — (line 19 in fetched text)
## Claims Extracted / 추출된 주장
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CROCKFORD-C1 | Crockford base32 심볼 셋은 10개 숫자 + 22개 알파벳 = 32자이며, 26자 알파벳 중 I / L / O / U 4자를 제외한다 | [§Symbols] "We chose a symbol set of 10 digits and 22 letters. We exclude 4 of the 26 letters: I, L, O, U." | `official-reference` | Crockford base32 를 채택한 모든 인코딩 구현 | RFC 4648 base32 또는 다른 base32 변형에는 적용 안 됨 |
| CROCKFORD-C2 | I 와 L 은 숫자 1과 혼동되고, O 는 숫자 0과 혼동되며, U 는 의도치 않은 외설 표현을 만들 수 있어 제외된다 | [§Symbols — Excluded Letters] "I — Can be confused with 1" / "L — Can be confused with 1" / "O — Can be confused with 0" / "U — Accidental obscenity" | `official-reference` | human-friendly 인코딩 심볼 선정 기준 | U 제외의 구체적인 외설 사례는 이 문서에서 나열하지 않음 |
| CROCKFORD-C3 | 디코딩 시 대소문자 모두 허용하며, i / l / I / L 은 1로, o / O 는 0으로 정규화된다. 인코딩 시에는 대문자만 사용한다 | [§Symbols] "When decoding, upper and lower case letters are accepted, and i and l will be treated as 1 and o will be treated as 0. When encoding, only upper case letters are used." | `official-reference` | Crockford base32 디코더 구현 | 입력 문자열에서 대문자로의 정규화 순서(전처리 vs 심볼 테이블) 는 명시 안 함 |
| CROCKFORD-C4 | 하이픈(-)은 심볼 문자열 안에 삽입 가능하며, 가독성을 위한 구분자로 사용된다. 디코딩 시 하이픈은 무시된다 | [§Symbols] "Hyphens (-) can be inserted into symbol strings. This can partition a string into manageable pieces, improving readability by helping to prevent confusion. Hyphens are ignored during decoding." | `official-reference` | Crockford base32 디코더 구현; 사람이 읽는 공개 ID 포맷 | 하이픈 위치나 개수에 대한 공식 권장 형식은 이 문서에서 정의하지 않음 |
| CROCKFORD-C5 | 체크 심볼은 선택적이며, 숫자를 37로 나눈 나머지(modulo 37)로 인코딩된다. 체크 심볼 전용으로 5개 추가 심볼이 있다 | [§Check] "The check symbol encodes the number modulo 37, 37 being the least prime number greater than 32. We introduce 5 additional symbols that are used only for encoding or decoding the check symbol." | `official-reference` | 오류 감지가 필요한 Crockford base32 구현 | ULID 는 체크 심볼을 사용하지 않음 — ULID-spec 별도 확인 필요 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `CROCKFORD-C1`: Crockford base32 의 32자 심볼 셋 구성 (09, AH, J, K, M, N, PT, VZ). RFC 4648 base32 와의 차이(I/L/O/U 부재)를 원저자 권위로 증명.
- `CROCKFORD-C2`: 4개 제외 문자 각각의 제외 이유. human-friendly 설계 의도의 원문 근거.
- `CROCKFORD-C3`: case-insensitive 디코딩 + I/L → 1, O → 0 정규화. D3 결정의 원문 근거.
- `CROCKFORD-C4`: 하이픈이 유효한 구분자이며 디코딩에서 무시됨. 사람이 읽는 ID 에 하이픈 허용의 근거.
- `CROCKFORD-C5`: 체크 심볼의 존재 및 modulo 37 알고리즘.
- 이 자료가 증명하지 않는 것:
- ULID 가 Crockford base32 를 사용한다는 사실 — ULID spec 별도 확인 필요 (`raw/official-docs/ulid-spec.md`, 미작성).
- Crockford base32 가 IETF 표준이라는 사실 — 이 문서는 개인(Douglas Crockford)이 작성한 사양이며 RFC 가 아님.
- ca-skeleton 의 resource ID 기본 형식이 ULID 이어야 한다는 결론 — 그것은 D1 결정으로, 이 문서는 D1 이 ULID 를 선택할 경우의 charset 근거만 제공.
- Crockford base32 가 URL-safe 하다는 사실 — 32자 심볼(09, AH, J, K, M, N, PT, VZ)이 RFC 3986 `unreserved` 에 속하는지는 RFC 3986 별도 확인 필요.
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- ULID spec 이 Crockford base32 를 어떻게 적용하는지 (monotonic encoding 등) — `raw/official-docs/ulid-spec.md` 작성 필요.
- case-insensitive 디코딩이 Spring / Hibernate / Jackson 직렬화 레이어에서 어떻게 처리되는지 — library 호환성 매트릭스(D16) 에서 확인.
- RFC 3986 `unreserved` charset 과 Crockford base32 32자의 교집합 — `raw/official-docs/rfc3986-uri-generic-syntax.md` (미작성) 에서 확인.
## 메모 / Notes
- 이 사양은 Douglas Crockford 개인 웹사이트(`crockford.com`)에 게시된 비공식 표준이다. IETF RFC 가 아니며, 표준 트랙 문서가 아님. 그러나 ULID, Hashids 등 여러 오픈소스 라이브러리가 이 사양을 채택하여 사실상 표준(de facto)으로 기능하고 있다.
- 페이지 하단 `0123456789ABCDEFGHJKMNPQRSTVWXYZ *~$=U 2002-11-02` 은 32자 기본 심볼 + 체크 심볼 전용 5개(`*~$=U`) + 발행일을 한 줄로 요약한 것으로 보인다.
- 추가로 봐야 할 동일 출처 페이지: `crockford.com` 에 다른 관련 사양 없음 (단일 페이지 문서).
## Related / 관련
- 같은 주제 다른 official-doc (미작성): [[raw/official-docs/ulid-spec.md]] — ULID 가 Crockford base32 를 적용하는 방식
- 같은 주제 다른 official-doc (미작성): [[raw/official-docs/rfc3986-uri-generic-syntax.md]] — URL-safe charset 검증 (D3 근거)
- 이 자료를 인용한 wiki 요약: `wiki/concepts/base32-encoding` (생성 시)
@@ -0,0 +1,85 @@
---
title: official-doc / OWASP Cross-Site Request Forgery Prevention Cheat Sheet — SameSite Defense-in-Depth
source_type: official-doc
url: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
archive_url:
related_branches: [feature-keycloak-bff-csrf-samesite-defense]
related_projects: []
tags: [official-doc, keycloak-patterns, security, owasp, csrf, samesite]
created: 2026-07-25
---
# official-doc / OWASP Cross-Site Request Forgery Prevention Cheat Sheet — SameSite Defense-in-Depth
> Layer: `raw/official-docs/` — OWASP Cheat Sheet Series "Cross-Site Request Forgery Prevention Cheat Sheet" 페이지의 원문 발췌. `feature-keycloak-bff-csrf-samesite-defense` branch 의 D3(SameSite 를 CSRF token 의 **대체가 아닌 defense-in-depth 보완**으로 결합) 결정 근거로 보관. OWASP Cheat Sheet Series 는 특정 벤더 제품 문서가 아니라 산업 전반의 벤더 중립 공식 보안 레퍼런스이므로 `source_type: official-doc` (company-tech-blog 아님).
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-bff-csrf-samesite-defense]] | D3 — SameSite 쿠키 속성을 CSRF token(D2, `csrf-protection-spring-official` 근거)의 **대체가 아닌 defense-in-depth 보완**으로 결합하는 프레이밍의 공식 근거. 세션 쿠키에 SameSite 적용 권고, Lax/Strict trade-off, synchronizer token 이 1차 방어라는 근거 포함 — 기존 `D3 UNSUPPORTED_DECISION` 라벨 해소용 |
## 출처
- 원본 URL: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
- 아카이브 URL: (미수집)
- 저자 / 조직: OWASP (Open Worldwide Application Security Project) Cheat Sheet Series — 벤더 중립 커뮤니티 보안 레퍼런스 (GitHub 기반 협업 편집, 다수 리뷰어)
- 발행일: 고정 발행일 없음 (rolling living document)
- 마지막 확인일: 2026-07-25
## 왜 저장했는지
branch 의 D3(SameSite 를 CSRF token 의 defense-in-depth 보완으로 결합)이 `UNSUPPORTED_DECISION` 상태였다 — 기존 유일한 근거(`csrf-protection-spring-official`)는 SameSite 를 전혀 언급하지 않기 때문. 이 문서는 SameSite 를 "does not replace a proper CSRF defense" 로 명시적으로 프레이밍하고, 세션 쿠키 적용 시 주의사항·Lax/Strict trade-off·synchronizer token 이 1차 방어라는 근거를 제공해 D3 를 뒷받침한다.
## 핵심 인용
> [§Limitations of SameSite] "SameSite is useful as a defense-in-depth control but it does not replace a proper CSRF defense in most deployments."
> [§Introduction] "SameSite Cookie Attribute can be used for session cookies but be careful to NOT set a cookie specifically for a domain."
> [§SameSite (Cookie Attribute)] "If a website wants to maintain a user's logged-in session after the user arrives from an external link, SameSite's default Lax value provides a reasonable balance between security and usability."
> [§Limitations of SameSite] "Top-level navigation and window-opening tricks. [...] SameSite=Strict blocks most of these at the cost of breaking legitimate cross-site links into the app."
> [§Token-Based Mitigation] "The synchronizer token pattern is one of the most popular and recommended methods to mitigate CSRF."
## Claims Extracted (추출된 주장)
> `Claim ID` prefix: `OWASP-CSRF-SAMESITE`.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| OWASP-CSRF-SAMESITE-C1 | SameSite 는 defense-in-depth 통제이며 대부분의 배포 환경에서 적절한 CSRF 방어를 **대체하지 않는다** | [§Limitations of SameSite] "SameSite is useful as a defense-in-depth control but it does not replace a proper CSRF defense in most deployments." | `official-reference` | SameSite 단독 배포가 CSRF 방어로 충분한지 판단하는 일반 아키텍처 가이드 | 어떤 특정 프레임워크(Spring 등)의 실제 SameSite 기본값/런타임 동작을 증명하지 않음 — 이 branch 의 AP3 세션 쿠키가 실제로 어떤 SameSite 값을 갖는지는 별도 확인 필요 |
| OWASP-CSRF-SAMESITE-C2 | SameSite 쿠키 속성은 세션 쿠키에 적용 가능하나, 특정 도메인에 한정해서 설정하지 않도록 주의해야 한다(서브도메인 쿠키 공유 위험) | [§Introduction] "SameSite Cookie Attribute can be used for session cookies but be careful to NOT set a cookie specifically for a domain." | `official-reference` | SameSite 를 세션 쿠키(D3 가 적용 대상으로 고려하는 쿠키)에 적용하는 일반 권고 | 이 branch 의 실제 세션 쿠키 도메인 설정이 이 위험에 해당하는지는 이 문장만으로 증명되지 않음 — 프로젝트별 도메인 구성 확인 필요 |
| OWASP-CSRF-SAMESITE-C3 | 외부 링크를 통한 로그인 세션 유지가 필요한 경우, SameSite 의 기본값인 Lax 가 보안과 사용성 사이의 합리적 균형을 제공한다 | [§SameSite (Cookie Attribute)] "If a website wants to maintain a user's logged-in session after the user arrives from an external link, SameSite's default Lax value provides a reasonable balance between security and usability." | `official-reference` | Lax vs Strict 선택 기준 — 외부 링크 진입이 필요한 애플리케이션의 경우 Lax 선택 근거 | Lax 가 모든 CSRF 벡터를 차단한다는 것은 증명하지 않음(GET 기반 state-changing 우회 가능성은 이 문서의 별도 문단이 다룸, 이 claim 의 범위 밖) |
| OWASP-CSRF-SAMESITE-C4 | SameSite=Strict 는 top-level navigation/새 창 열기를 통한 공격 대부분을 차단하지만, 정상적인 cross-site 링크 진입을 깨뜨리는 비용(UX trade-off)이 있다 | [§Limitations of SameSite] "SameSite=Strict blocks most of these at the cost of breaking legitimate cross-site links into the app." | `official-reference` | Strict 채택 시 예상되는 UX 트레이드오프 근거 — 외부 링크 진입이 불필요한 애플리케이션에서 Strict 채택 근거 | 이 branch(AP3, keycloak IdP 리다이렉트 흐름 포함)가 외부 링크 진입에 의존하는지 여부는 이 문장이 판단하지 않음 — OIDC redirect flow 와 Strict 의 상호작용은 별도 검증 필요 |
| OWASP-CSRF-SAMESITE-C5 | synchronizer token pattern 은 CSRF 를 완화하는 가장 널리 쓰이고 권장되는 방법 중 하나다 | [§Token-Based Mitigation] "The synchronizer token pattern is one of the most popular and recommended methods to mitigate CSRF." | `official-reference` | CSRF token(D2, 이미 `csrf-protection-spring-official` 로 별도 근거 확보)이 1차 방어이고 SameSite 는 그 보완이라는 D3 의 "combine, don't replace" 프레이밍 근거 | Spring Security 의 특정 구현(synchronizer token + `CookieCsrfTokenRepository`)이 이 일반 권고를 만족하는지는 이 문장 자체가 증명하지 않음 — 그 부분은 `csrf-protection-spring-official`(`SPRINGSEC-CSRF-C3`)이 별도로 증명 |
## Usage Boundaries (적용 경계)
- 이 자료가 직접 증명하는 것:
- `OWASP-CSRF-SAMESITE-C1`: SameSite 는 defense-in-depth 이며 CSRF 방어의 대체가 아니라는 일반 원칙
- `OWASP-CSRF-SAMESITE-C2`: 세션 쿠키에 SameSite 적용 시 서브도메인 공유 주의사항
- `OWASP-CSRF-SAMESITE-C3`: Lax 가 외부 링크 진입 시나리오에서 보안·사용성 균형을 제공한다는 일반 권고
- `OWASP-CSRF-SAMESITE-C4`: Strict 채택 시 예상되는 UX 트레이드오프
- `OWASP-CSRF-SAMESITE-C5`: synchronizer token pattern 이 CSRF 완화의 대중적·권장 방법이라는 일반 진술
- 이 자료가 증명하지 않는 것:
- Spring Security(D2) 의 실제 `CookieCsrfTokenRepository`/`XSRF-TOKEN` 쿠키가 어떤 SameSite 값을 기본으로 갖는지 — 이 문서는 벤더 중립 일반 가이드이며 Spring 구현 세부는 다루지 않음(`csrf-protection-spring-official` 범위)
- AP3(BFF) 의 실제 세션 쿠키가 어떤 도메인/SameSite 조합으로 배포될지 — 코드 미구현(`NO_GROUND_TRUTH`)이라 이 문서만으로 확정 불가
- Lax 냐 Strict 냐의 최종 선택 — 이 문서는 트레이드오프만 제시하며 이 branch 의 OIDC redirect 흐름(keycloak IdP 경유)과의 상호작용까지 판단하지 않음
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- AP3 세션 쿠키(`server.servlet.session.cookie.same-site`)와 `XSRF-TOKEN` 쿠키(`CookieCsrfTokenRepository`) 중 어느 쪽에 SameSite 를 적용할지 — branch §구현 가이드의 `UNSUPPORTED_IMPL_DECISION(a)` 로 남아 있으며 이 문서만으로 결정 불가(Applies to 범위 밖)
- keycloak OIDC 로그인 리다이렉트가 top-level navigation 인지, 그 경로가 Strict 채택 시 깨지는지 실제 흐름으로 검증 필요
## 메모
> 검증되지 않은 내 해석. 사실 인용과 분리.
- D3 는 `OWASP-CSRF-SAMESITE-C1`+`C5` 조합("SameSite 는 보완이고 synchronizer token 이 1차 방어")으로 "combine, don't replace" 프레이밍이 뒷받침 가능해 보인다(미검증 — branch 갱신 시 재확인).
- SameSite 적용 대상(세션 쿠키 vs `XSRF-TOKEN` 쿠키)과 Lax vs Strict 값 선택은 이 문서가 원칙만 제공하고 detail 은 권고하지 않으므로, branch §구현 가이드의 `UNSUPPORTED_IMPL_DECISION` 라벨이 계속 유효하다(이 문서로 해소되는 것은 D3 의 "왜 결합하는가" 부분이지 "어디에 어떤 값으로" 부분이 아님).
- 추가로 봐야 할 동일 출처 페이지: 같은 페이지의 "Using Standard Headers to Verify Origin" 섹션(Origin/Referer 검증), Spring Session reference 의 쿠키 직렬화 옵션 페이지(SameSite 적용 대상 detail 확정 시 필요).
## 관련
- 같은 주제 다른 official-doc: [[raw/official-docs/csrf-protection-spring-official]] — Spring Security 의 실제 CSRF token 구현 메커니즘(D2) 근거. 이 문서(OWASP)는 SameSite 프레이밍(D3) 보완이며 서로 겹치지 않는 근거 제공
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
@@ -0,0 +1,85 @@
---
title: official-doc / Spring Security — Cross Site Request Forgery (CSRF) Protection (Servlet)
source_type: official-doc
url: https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html
archive_url:
related_branches: [feature-keycloak-bff-csrf-samesite-defense]
related_projects: []
tags: [official-doc, keycloak-patterns, security, auth, spring-security]
created: 2026-07-23
---
# official-doc / Spring Security — Cross Site Request Forgery (CSRF) Protection (Servlet)
> Layer: `raw/official-docs/` — Spring Security 공식 레퍼런스(Servlet 스택) "Cross Site Request Forgery (CSRF)" 페이지의 원문 발췌. `feature-keycloak-bff-csrf-samesite-defense` branch의 AP3(BFF, `oauth2Login` cookie-session 패턴) CSRF 방어 메커니즘 결정(D3) 및 defense-in-depth 결정(D4) 근거로 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-keycloak-bff-csrf-samesite-defense]] | AP3 BFF(Spring `oauth2Login` cookie-session) 패턴에서 CSRF 토큰 방어 메커니즘(D3: synchronizer token / `CookieCsrfTokenRepository` / BREACH 방어)의 공식 벤더 근거. D4(defense-in-depth: CSRF token + SameSite 조합)에 대해서는 이 문서가 SameSite를 전혀 언급하지 않으므로 **부분 근거만 제공** — 아래 Usage Boundaries 참조 |
## 출처
- 원본 URL: https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html
- 아카이브 URL: (미수집)
- 저자 / 조직: Spring Security (VMware/Broadcom) — 공식 레퍼런스 문서
- 발행일: 고정 발행일 없음 (rolling reference doc). 확인 시점 페이지 하단 버전 배너: Stable `7.1.0` / `7.0.6` / `6.5.11`, Snapshot `7.1.1-SNAPSHOT` / `7.0.7-SNAPSHOT` / `6.5.12-SNAPSHOT`
- 마지막 확인일: 2026-07-23
## 왜 저장했는지
AP3(BFF) 패턴은 OAuth2/OIDC 토큰을 backend session에 두고 browser에는 session cookie만 노출한다(브랜치 상속 결정 `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`). 이 쿠키 기반 세션은 CSRF에 노출되므로, branch는 Spring Security가 실제로 구현하는 CSRF 방어 메커니즘(synchronizer token pattern, `CookieCsrfTokenRepository`, BREACH 방어)을 공식 문서로 확인해야 한다. 단, 이 특정 페이지는 SameSite 쿠키 속성을 전혀 다루지 않아 D4(SameSite 결합) 근거로는 불충분함을 확인하기 위해서도 저장한다.
## 핵심 인용
> [§Cross Site Request Forgery (CSRF), 개요] "Spring Security protects against CSRF attacks by default for unsafe HTTP methods, such as a POST request, so no additional code is necessary."
> [§CSRF Considerations Logging Out] "This ensures that logging out requires a CSRF token and that a malicious user cannot forcibly log your users out."
> [§Integrating with CSRF Protection] "For the synchronizer token pattern to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request."
> [§Persisting the CsrfToken Using the CookieCsrfTokenRepository] "The CookieCsrfTokenRepository writes to a cookie named XSRF-TOKEN and reads it from an HTTP request header named X-XSRF-TOKEN or the request parameter _csrf by default."
> [§Handling the CsrfToken Using the XorCsrfTokenRequestAttributeHandler (BREACH)] "BREACH protection is provided by encoding randomness into the CSRF token value to ensure the returned CsrfToken changes on every request."
## 추출된 주장
> `Claim ID` prefix: `SPRINGSEC-CSRF`.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| SPRINGSEC-CSRF-C1 | Spring Security는 POST 등 unsafe HTTP method 요청에 대해 기본적으로 CSRF 공격을 방어하며 별도 설정 코드가 필요 없다 | [§개요] "Spring Security protects against CSRF attacks by default for unsafe HTTP methods, such as a POST request, so no additional code is necessary." | `official-vendor-doc` | `.csrf(Customizer.withDefaults())` 또는 미설정 시 Spring Security 6.x/7.x 기본 동작 | 어떤 매커니즘(synchronizer token / double-submit / SameSite)으로 방어하는지는 이 문장 자체는 규정하지 않음 — 메커니즘은 C3·C4에서 별도 확인 |
| SPRINGSEC-CSRF-C2 | CSRF 방어가 없으면 악의적 사용자가 피해자를 강제로 로그아웃(또는 다른 state-changing 요청)시키는 위조 요청(forged request)이 가능하다 — CSRF 토큰 요구가 이를 차단한다 | [§Logging Out] "This ensures that logging out requires a CSRF token and that a malicious user cannot forcibly log your users out." | `official-vendor-doc` | 인증된 세션(쿠키)을 가진 사용자를 대상으로 한 상태 변경(state-changing) 요청 위조 시나리오 일반 | CSRF 공격의 전체 위협 모델(예: 쿠키가 자동 첨부되는 근본 원인, SameSite와의 관계)을 설명하지는 않음 — 이 문장은 logout 시나리오에 한정된 결과 진술 |
| SPRINGSEC-CSRF-C3 | Spring Security의 CSRF 방어는 synchronizer token pattern이며, 공격자가 자동으로 재현할 수 없는 실제 CSRF 토큰을 HTTP 요청에 포함시켜야 한다 | [§Integrating with CSRF Protection] "For the synchronizer token pattern to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request." | `official-vendor-doc` | 통합 방식(HTML form / JS / mobile) 무관하게 적용되는 핵심 방어 원리 | 토큰이 세션에 저장되는지 쿠키에 저장되는지는 이 문장만으로 규정하지 않음 — 저장 위치는 `CsrfTokenRepository` 구현체 선택 문제(C4 참조) |
| SPRINGSEC-CSRF-C4 | `CookieCsrfTokenRepository``CsrfToken``XSRF-TOKEN`이라는 이름의 쿠키에 쓰고, 기본적으로 `X-XSRF-TOKEN` 요청 헤더 또는 `_csrf` 요청 파라미터로부터 읽는다 | [§Using the CookieCsrfTokenRepository] "The CookieCsrfTokenRepository writes to a cookie named XSRF-TOKEN and reads it from an HTTP request header named X-XSRF-TOKEN or the request parameter _csrf by default." | `official-vendor-doc` | `CsrfTokenRepository``CookieCsrfTokenRepository`로 명시적으로 구성한 JavaScript 기반 애플리케이션(AP3처럼 세션 대신/추가로 쿠키 기반 토큰 노출이 필요한 경우) | 이 쿠키(`XSRF-TOKEN`)의 `SameSite` 속성값(Lax/Strict/None)이 무엇인지는 이 문장이 전혀 규정하지 않음 — `CookieCsrfTokenRepository``SameSite` 기본값/구성 옵션은 이 페이지 범위 밖 |
| SPRINGSEC-CSRF-C5 | BREACH 방어는 CSRF 토큰 값에 무작위성(randomness)을 인코딩하여 매 요청마다 반환되는 `CsrfToken` 값이 달라지게 함으로써 제공된다 | [§Using the XorCsrfTokenRequestAttributeHandler (BREACH)] "BREACH protection is provided by encoding randomness into the CSRF token value to ensure the returned CsrfToken changes on every request." | `official-vendor-doc` | 기본 활성화된 `XorCsrfTokenRequestAttributeHandler` 사용 시(Spring Security 6+ 기본값) | BREACH 방어가 `SameSite` 쿠키 속성과 결합되어 있다거나 이를 대체·보완한다는 명제는 이 문장이 증명하지 않음 — BREACH는 CSRF 토큰 값 자체의 압축 사이드채널(compression side-channel) 공격 방어이며, cross-site 요청 자체를 막는 메커니즘이 아님 |
## 적용 경계
- 이 자료가 직접 증명하는 것:
- `SPRINGSEC-CSRF-C1`: Spring Security의 CSRF 기본 방어 활성화 범위(unsafe HTTP method)
- `SPRINGSEC-CSRF-C2`: CSRF 방어 부재 시 위협 시나리오(강제 로그아웃 등 위조 요청) 예시
- `SPRINGSEC-CSRF-C3`: synchronizer token pattern이 Spring Security CSRF 방어의 핵심 원리라는 사실
- `SPRINGSEC-CSRF-C4`: `CookieCsrfTokenRepository`의 쿠키명(`XSRF-TOKEN`)·요청 헤더명(`X-XSRF-TOKEN`)·파라미터명(`_csrf`) 기본값
- `SPRINGSEC-CSRF-C5`: BREACH 방어의 원리(토큰 값 randomization)
- 이 자료가 증명하지 않는 것 (**UNSUPPORTED_DECISION 후보** — branch D4로 그대로 인용 금지):
- **이 문서(`servlet/exploits/csrf.html`)는 "SameSite"라는 단어를 단 한 차례도 언급하지 않는다** (원문 전체 대상 대소문자 무시 검색 결과 0건, 2026-07-23 확인). 따라서 branch의 D4("Spring이 CSRF token과 SameSite를 어떻게 combine하는가")를 이 자료 단독으로 정당화할 수 없다. SameSite 쿠키 속성 가이드는 별도 출처(예: Spring Session reference의 쿠키 직렬화 옵션, `server.servlet.session.cookie.same-site` — Spring Boot 공식 문서, 또는 MDN `Set-Cookie` SameSite 사양)가 필요하다.
- BREACH 방어(C5)가 SameSite를 대체하거나 SameSite와 결합되어 동작한다는 명제.
- `CookieCsrfTokenRepository`가 생성하는 `XSRF-TOKEN` 쿠키의 `SameSite` 기본값 — 이 페이지의 코드 예제(`CookieCsrfTokenRepository.withHttpOnlyFalse()`)는 `SameSite` 파라미터를 전혀 노출하지 않는다.
- 내 프로젝트(AP3 BFF)에 적용하려면 추가 확인이 필요한 것:
- 실제 세션 쿠키(`JSESSIONID` 등)와 `XSRF-TOKEN` 쿠키 각각에 `SameSite` 속성을 어떻게 지정할지는 Spring Session / `CookieSerializer` 또는 서블릿 컨테이너 설정 별도 확인 필요.
- Spring Boot `server.servlet.session.cookie.same-site` 프로퍼티(별도 Spring Boot 공식 문서)와 `CookieCsrfTokenRepository`의 관계 확인 필요 — 이 문서 범위 밖.
## 메모
> 검증되지 않은 내 해석. 사실 인용과 분리.
- D3(CSRF 토큰 메커니즘)는 `SPRINGSEC-CSRF-C3`+`C4` 조합으로 충분히 뒷받침 가능해 보인다(미검증 — branch 작성 시 재확인).
- D4(defense-in-depth: CSRF token + SameSite)는 이 자료만으로는 뒷받침 불가 — branch-spec 단계에서 SameSite 벤더 자료를 별도로 raw에 등록하거나, 근거 없이 작성 시 `UNSUPPORTED_DECISION` 라벨을 붙여야 한다(미검증 판단, 제안일 뿐).
- 추가로 봐야 할 동일 출처 페이지: docs.spring.io의 "CSRF Considerations"(서블릿 비특정 general 챕터), Spring Session reference의 쿠키 직렬화/SameSite 옵션 페이지, Spring Boot `server.servlet.session.cookie.same-site` 레퍼런스.
## 관련
- 같은 주제 다른 official-doc / company-tech-blog: (아직 없음 — 이 branch의 최초 CSRF raw 자료)
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
-1
View File
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/cuid2-spec.md
+105
View File
@@ -0,0 +1,105 @@
---
title: "official-doc / CUID2 — Secure Collision-Resistant ID Specification (paralleldrive)"
source_type: official-doc
url: https://github.com/paralleldrive/cuid2
archive_url:
related_branches: [feature-resource-identifier-contract]
related_projects: [ca-skeleton]
tags: [official-doc, ca-skeleton, api-design, security, idempotency]
created: 2026-05-31
---
# official-doc / CUID2 — Secure Collision-Resistant ID Specification (paralleldrive)
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 `source-summary-template` 형식으로 별도 작성. 원본은 raw 에 영구 보관.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-resource-identifier-contract]] | D1 (resource ID default 형식) — CUID2 를 privacy-sensitive 도메인의 후보로 채택하는 근거; D7 (timestamp leak 완화) — CUID2 가 timestamp 를 평문 노출하지 않음; D9 (enumeration/SecureRandom) — CUID2 의 암호학적 보안 설계 |
## 출처 / Source
- 원본 URL: https://github.com/paralleldrive/cuid2
- 아카이브 URL: (미확보)
- 저자 / 조직: paralleldrive (Eric Elliott 외)
- 발행일: (초기 공개 2022년, 지속 유지)
- 마지막 확인일: 2026-05-31
## 왜 저장했는지 / Why archived
ca-skeleton 이 resource ID 기본 형식을 결정하는 과정에서 CUID2 를 후보군으로 평가하기 위해 보관. CUID2 의 핵심 차별점인 **timestamp 비노출** 및 **암호학적 해싱 기반 보안 설계**가 UUIDv7 / ULID 의 privacy 약점(48bit timestamp 평문 노출)을 대체할 수 있는지 판단하는 근거 자료.
## 핵심 인용 / Key quotes (verbatim)
> [§Security / Hashing] "The new Cuid2 hashes all sources of entropy into a random-looking string. Due to the hashing algorithm, it should not be practically possible to recover any of the entropy sources from the generated ids."
> — source: README.md §Security section (line 4 in fetched text)
> [§Deprecation] "The changes in Cuid2 are significant and could potentially disrupt the many projects that rely on Cuid, so we decided to create a replacement library and id standard, instead. Cuid is now deprecated in favor of Cuid2."
> — source: README.md §Why not use Cuid? (line 10 in fetched text)
> [§Alphabet / Encoding] "The string is Base36 encoded, which means it contains only lowercase letters and the numbers: 0 - 9, with no special symbols."
> — source: README.md §Alphabet section (line 13 in fetched text)
> [§Collision Resistance] "The original Cuid...never had a problem with collisions in production systems using it" across "thousands of software implementations...with more than 100 million users generating ids."
> — source: README.md §Collision resistance (line 16 in fetched text)
> [§Comparison] "Cuid2 is the only solution that passed all of our tests" against criteria including security, collision resistance, horizontal scalability, offline compatibility, and URL-friendliness.
> — source: README.md §Comparison section (line 19 in fetched text)
## Claims Extracted / 추출된 주장
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기에 쓰지 않는다.
> `Claim ID` 형식: `CUID2-C<number>`.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| CUID2-C1 | CUID2 는 모든 entropy 소스(시스템 시각, 난수, 세션 카운터, 호스트 핑거프린트)를 SHA-3 해시로 결합하여 생성하므로, 생성된 ID 에서 timestamp 를 역산하는 것은 실질적으로 불가능하다 | [§Security] "The new Cuid2 hashes all sources of entropy into a random-looking string. Due to the hashing algorithm, it should not be practically possible to recover any of the entropy sources from the generated ids." | `official-reference` | 보안/privacy-sensitive 도메인에서 user-facing resource ID 로 CUID2 채택 시 | 독립 제3자 보안 감사 결과가 아님 — 저자 주장. 내부 구현이 실제로 SHA-3 을 올바르게 사용하는지 외부에서 검증되지 않음 |
| CUID2-C2 | CUID v1 은 공식적으로 deprecated 되었고, CUID2 가 그 후계 라이브러리 및 ID 표준으로 지정되었다 | [§Deprecation] "Cuid is now deprecated in favor of Cuid2." | `official-reference` | CUID v1 사용 중단 근거 / CUID2 채택 정당화 | CUID v1 의 구체적인 보안 취약점 목록이 아님. "significant changes" 의 내용을 상세 설명하지 않음 |
| CUID2-C3 | CUID2 ID 는 소문자와 숫자(0-9)만 포함하는 Base36 인코딩이며, 특수 문자가 없다. 기본 길이는 24자이다 | [§Alphabet] "The string is Base36 encoded, which means it contains only lowercase letters and the numbers: 0 - 9, with no special symbols." | `official-reference` | URL path variable 에서 특수 문자 escape 없이 사용 가능한지 판단 / RFC 3986 unreserved charset 적합성 평가 | Base36 charset 이 RFC 3986 unreserved (`ALPHA / DIGIT / "-" / "." / "_" / "~"`) 에 완전 부합한다는 독립 확인은 별도 필요 |
| CUID2-C4 | CUID (v1) 은 실제 프로덕션에서 충돌 문제가 보고된 적이 없으며, 1억 명 이상의 사용자를 가진 수천 개의 소프트웨어 구현에서 사용되었다 | [§Collision Resistance] "The original Cuid...never had a problem with collisions in production systems using it" across "thousands of software implementations...with more than 100 million users generating ids." | `official-reference` | CUID 계열의 실전 검증 근거 | CUID2 (v2) 의 충돌 저항성을 직접 검증한 것이 아님 — v1 의 사용 이력. 수학적 충돌 확률 계산 별도 필요 |
| CUID2-C5 | CUID2 는 보안, 충돌 저항성, 수평 확장성, 오프라인 호환성, URL 친화성 기준에서 평가한 결과 경쟁 대안들(NanoID, ULID 등) 중 유일하게 모든 테스트를 통과한 솔루션이라고 저자가 주장한다 | [§Comparison] "Cuid2 is the only solution that passed all of our tests" | `official-reference` | ID 후보군 비교에서 CUID2 를 최종 후보로 포함시키는 근거 | 저자 자체 평가 기준이며, 독립 제3자 벤치마크가 아님. "tests" 의 구체적 내용과 방법론이 공개되어야 재현 가능 |
### Strength 근거
이 자료는 프로젝트 저자(paralleldrive / Eric Elliott)가 작성한 **GitHub README** 임. 공식 라이브러리 문서이지만 독립 보안 감사나 표준 기구(IETF, NIST 등) 의 인증은 아님. 따라서 보안 관련 claim(`CUID2-C1`, `CUID2-C5`)은 `official-reference` 로 분류하되, 독립 검증이 없음을 `Does not prove` 에 명시.
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것**:
- `CUID2-C1`: CUID2 는 설계상 timestamp 를 ID 에 평문 노출하지 않으며, SHA-3 해싱으로 entropy 소스를 복원 불가능하게 만든다 (저자 주장 기준).
- `CUID2-C2`: CUID v1 은 공식 deprecated 상태이며 CUID2 로의 전환이 권고된다.
- `CUID2-C3`: CUID2 의 기본 출력 형태는 소문자 + 숫자(Base36), 24자, 특수 문자 없음.
- `CUID2-C4`: CUID 계열은 대규모 실전 배포에서 충돌 이슈가 보고되지 않음.
- `CUID2-C5`: 저자 기준으로 CUID2 는 NanoID, ULID 등 경쟁 대안보다 종합 우수하다고 평가됨.
- **이 자료가 증명하지 않는 것**:
- CUID2 의 보안 특성이 제3자 감사(independent security audit)로 검증되었다는 사실.
- CUID2 가 FIPS 140-2 / NIST 인증 환경에서 사용 가능하다는 사실.
- Java / Kotlin 생태계에서 CUID2 를 production-ready 한 형태로 사용할 수 있는 공식 라이브러리가 존재한다는 사실 (README 는 JS 라이브러리 기준).
- UUIDv7 / ULID 대비 DB index 성능 차이 (timestamp-ordered vs random 측면에서 CUID2 는 random에 가까움).
- 24자 Base36 이 RFC 3986 unreserved charset 에 완전 부합한다는 공식 확인 (별도 RFC 3986 §2.3 대조 필요).
- **ca-skeleton 에 적용하려면 추가 확인이 필요한 것**:
- Java/Kotlin 용 CUID2 구현체 존재 여부 및 성숙도 (JS 생태계 기준 라이브러리임을 유의).
- `CUID2-C1` 의 "practically impossible to recover entropy" 주장을 뒷받침하는 공개 보안 분석 또는 감사 보고서.
- CUID2 의 충돌 확률 수식 (24자 Base36 = ~124bit 엔트로피, 수식 검증 필요).
- 다른 privacy 요구사항 문서(GDPR Article 25 / CCPA)에서 timestamp-free ID 를 명시적으로 요구하는지 여부.
## 메모 / Notes
- 이 자료는 **JavaScript 라이브러리의 README** 임. ca-skeleton 은 Java/Spring Boot 기반이므로 Java 용 동등 구현(예: `f4-cuid2`, `com.github.f4b6a3` 계열 등)을 별도로 평가해야 함. 해당 Java 라이브러리는 이 README 에서 다루지 않음.
- `CUID2-C5` 의 "passed all of our tests" 는 저자 자체 기준. 독립 재현 불가 → 비교 결론을 D1 결정의 주된 근거로 단독 사용 금지. RFC 9562 (UUID v7), ULID spec, NanoID README 와 병렬 검토 권고.
- timestamp leak 이 실질적 위협인 시나리오: 의료 기록 ID (처방 시각 역산), 금융 거래 ID (주문 시각 → 전략 노출), 사용자 계정 ID (가입 순서 → early adopter 타깃). ca-skeleton 이 도메인 무관한 skeleton 이라면 CUID2 를 "opt-in" 로 두고 default 는 ULID/UUIDv7 로 결정하는 것도 trade-off 중 하나.
- CUID2 가 "deliberately slower" (brute-force 방지 목적) 라고 설명하는 부분은 고빈도 ID 생성 시나리오에서 성능 bottleneck 가능성을 내포. render loop 같은 tight loop 에서 사용 금지는 README 가 명시.
## Related / 관련
- 같은 주제 다른 raw 자료 (예정):
- [[raw/official-docs/rfc9562-uuid.md]] — UUID v7 (time-ordered, 48bit timestamp 평문 노출 확인용)
- [[raw/official-docs/ulid-spec.md]] — ULID spec (timestamp 영역 확인용)
- [[raw/official-docs/nanoid-spec.md]] — NanoID (CUID2-C5 비교 대상)
- [[raw/official-docs/rfc3986-uri-generic-syntax.md]] — CUID2-C3 charset RFC 3986 적합성 확인용
- 이 자료를 인용할 wiki 요약: [[wiki/concepts/resource-identifier-format]] (생성 시)
@@ -1 +0,0 @@
../../vault/20-evidence/official-docs/datasource-micrometer-observation-official.md
@@ -0,0 +1,92 @@
---
title: official-doc / datasource-micrometer — JDBC Observation API for Spring Boot 3 (net.ttddyy.observation)
source_type: official-doc
url: https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/
archive_url:
status: raw
confidence: high
tags: [backend, db, jdbc, micrometer, observability, tracing, spring-boot-3]
related_branches: [feature-database-connection-pool-contract]
related_projects: []
created: 2026-06-09
last_reviewed: 2026-06-09
---
# datasource-micrometer — JDBC Observation API for Spring Boot 3
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
## Parent / 활용 branch
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-database-connection-pool-contract]] | Micrometer/OpenTelemetry 기반 JDBC 관측 방식(span/metric)의 슬로우 쿼리 탐지 가능성 및 파라미터 노출 기본 동작 근거 |
## 출처 / Source
- 원본 URL: https://jdbc-observations.github.io/datasource-micrometer/docs/current/docs/html/
- 보조 URL: https://github.com/jdbc-observations/datasource-micrometer
- 저자 / 조직: Tadaya Tsuyukubo (net.ttddyy.observation) — datasource-proxy 와 동일 저자
- 마지막 확인일: 2026-06-09
## 왜 저장했는지 / Why archived
`feature-database-connection-pool-contract` 브랜치에서 Micrometer Observation API 기반 JDBC 추적 방식을 슬로우 쿼리 탐지 대안으로 검토. 핵심 질문: (1) statement-level 슬로우 쿼리 탐지가 가능한가, (2) 파라미터 값이 span/tag 에 기본 포함되는가, (3) 임계값 알럿 방식인가 메트릭 기반인가.
## 핵심 인용 / Key quotes (verbatim)
> "Query observations: Execute span with timer metrics (jdbc.query)"
— datasource-micrometer docs (생성 observation 타입)
> "jdbc.datasource-proxy.slow-query.enable-logging=true"
> "jdbc.datasource-proxy.slow-query.threshold (default 300 seconds)"
— datasource-micrometer docs (슬로우 쿼리 로그 설정)
> "Bind parameters are not included by default. Users must explicitly enable this feature via: listener.setIncludeParameterValues(true) or Spring Boot property: jdbc.datasource-proxy.include-parameter-values=true"
— datasource-micrometer docs (파라미터 포함 opt-in 방식)
> "When OpenTelemetry semantic conventions are enabled, queries undergo analysis and can be sanitized or summarized through JSqlParser."
— datasource-micrometer docs (OpenTelemetry 연동 시 SQL sanitization 가능)
> "Instrumentation operates at statement-level granularity, not method-level"
— datasource-micrometer docs (개별 쿼리 실행 단위 추적)
## Claims Extracted / 추출된 주장
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| C1 | datasource-micrometer 는 JDBC 쿼리 실행 시간을 `jdbc.query` metric 과 span 으로 기록한다 | "Query observations: Execute span with timer metrics (jdbc.query)" | `official-vendor-doc` | datasource-micrometer + Spring Boot 3 자동 구성 환경 | 임계값 기반 로그 알럿이 기본 제공된다는 주장 반증 |
| C2 | 기본 설정에서 바인드 파라미터 값은 span/tag 에 포함되지 않으며 opt-in 으로만 활성화된다 | "Bind parameters are not included by default. Users must explicitly enable this feature via: listener.setIncludeParameterValues(true)" | `official-vendor-doc` | datasource-micrometer 1.x Spring Boot 3 환경 | 파라미터가 기본 로깅된다는 주장 반증 |
| C3 | 슬로우 쿼리 로그 임계값은 `jdbc.datasource-proxy.slow-query.threshold` 로 설정하며 기본값은 300초이다 | "jdbc.datasource-proxy.slow-query.threshold (default 300 seconds)" | `official-vendor-doc` | datasource-micrometer Spring Boot 통합 환경 | — |
| C4 | OpenTelemetry 연동 시 JSqlParser 를 통해 SQL sanitization (파라미터 제거/추상화) 이 가능하다 | "queries undergo analysis and can be sanitized or summarized through JSqlParser" | `official-vendor-doc` | OTel semantic convention 모듈 사용 환경 | sanitization 이 기본 활성화된다는 주장 반증 |
| C5 | 추적 단위는 statement-level 이며 repository method-level 이 아니다 | "Instrumentation operates at statement-level granularity, not method-level" | `official-vendor-doc` | datasource-micrometer 1.x | repository 메서드 단위로 느린 쿼리를 특정할 수 있다는 주장 반증 |
## Usage Boundaries / 적용 경계
- 이 자료가 직접 증명하는 것:
- `C1~C5`: datasource-micrometer 의 metric/span 생성, 파라미터 기본 비포함, 슬로우 쿼리 설정 방식
- 이 자료가 증명하지 않는 것:
- APM (Datadog, Grafana 등) 연동 없이 단독으로 슬로우 쿼리 알럿이 가능하다는 주장
- metric 기반 탐지(P99 latency 초과)가 log 기반 탐지보다 우월하다는 주장
- 운영 환경에서 span 수집 오버헤드가 무시할 수준이라는 주장
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
- APM 백엔드(Prometheus + Grafana, Datadog 등) 의 존재 여부 확인
- `jdbc.datasource-proxy.slow-query.threshold` 기본값 300초를 프로젝트 요구사항(1초)에 맞게 변경 필요
- datasource-micrometer 가 내부적으로 datasource-proxy 를 사용함 — 중복 의존성 검토
## 메모 / Notes
- datasource-micrometer 는 datasource-proxy 를 기반으로 Micrometer Observation API 를 래핑한 라이브러리 — datasource-proxy 의 슬로우 쿼리 기능을 내부적으로 재사용
- `jdbc.query` metric 은 histogram 으로 P50/P95/P99 latency 알럿 설정 가능 (APM 필요)
- 이 방식은 "임계값 초과 시 로그" 보다 "latency distribution 추적" 에 더 적합한 use case
## Related / 관련
- [[raw/official-docs/datasource-proxy-slow-query-official]]
- [[raw/official-docs/hibernate-slow-query-log-official]]
- 이 자료를 인용한 wiki 요약: (미생성)

Some files were not shown because too many files have changed in this diff Show More