init: llm-wiki-haness 하네스 설계
This commit is contained in:
@@ -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 요약: (미작성)
|
||||
@@ -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)**
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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: (미작성)
|
||||
@@ -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: (미작성)
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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` 예정)
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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]] (작성 시)
|
||||
@@ -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 부재 보강
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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]]
|
||||
@@ -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 요약: (아직 생성 안 됨)
|
||||
@@ -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]]
|
||||
+84
@@ -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 참조)
|
||||
@@ -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]]
|
||||
@@ -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 해소용)
|
||||
@@ -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/` 생성 예정)
|
||||
@@ -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 요약: (생성 시 추가)
|
||||
@@ -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)
|
||||
@@ -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: (미작성)
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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]]
|
||||
@@ -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 요약: (생성 시 링크)
|
||||
@@ -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]]
|
||||
@@ -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 요약: (생성 시 추가)
|
||||
@@ -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 요약: (아직 생성 안 됨)
|
||||
@@ -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 으로 표현되는지
|
||||
@@ -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 가 같은 도구 체인을 공유 가능
|
||||
@@ -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 251–254] "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 419–424] "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 432–437] "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 389–392] "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 251–254] "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 419–424] "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 432–437] "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 389–392] "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 560–572)에 `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]]` (생성 시)
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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`
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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: (미작성)
|
||||
@@ -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 4897–4898] "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.4897–4898] "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 요약: (생성 시 추가)
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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: (미작성)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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/` 에 작성)
|
||||
@@ -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 섹션 보강 대상
|
||||
@@ -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 요약: (미작성)
|
||||
@@ -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
|
||||
@@ -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 28–31 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자 심볼 셋 구성 (0–9, A–H, J, K, M, N, P–T, V–Z). 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자 심볼(0–9, A–H, J, K, M, N, P–T, V–Z)이 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,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]] (생성 시)
|
||||
@@ -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 요약: (미생성)
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: official-doc / datasource-proxy — Slow Query Listener & ParameterTransformer (net.ttddyy)
|
||||
source_type: official-doc
|
||||
url: https://jdbc-observations.github.io/datasource-proxy/docs/snapshot/user-guide/index.html
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [backend, db, jdbc, proxy, slow-query, observability, datasource-proxy]
|
||||
related_branches: [feature-database-connection-pool-contract]
|
||||
related_projects: []
|
||||
created: 2026-06-09
|
||||
last_reviewed: 2026-06-09
|
||||
---
|
||||
|
||||
# datasource-proxy — Slow Query Listener & ParameterTransformer
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-database-connection-pool-contract]] | JDBC 프록시 계층에서 파라미터를 노출하지 않고 슬로우 쿼리를 탐지하는 datasource-proxy 방식의 가능성과 설정 방법 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://jdbc-observations.github.io/datasource-proxy/docs/snapshot/user-guide/index.html
|
||||
- 보조 URL: https://github.com/gavlyukovskiy/spring-boot-data-source-decorator/blob/master/README.md
|
||||
- 저자 / 조직: Tadaya Tsuyukubo (net.ttddyy), gavlyukovskiy (Spring Boot 통합)
|
||||
- 마지막 확인일: 2026-06-09
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-database-connection-pool-contract` 브랜치에서 JDBC 프록시 계층 기반 슬로우 쿼리 탐지 방식으로 datasource-proxy를 검토. 핵심 질문: ParameterTransformer 를 통해 파라미터 값을 로그에서 제거할 수 있는가? 프로젝트 "SQL/파라미터 로그 금지" 하드 룰과의 호환성 검토.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> "logSlowQueryByCommons(threshold, TimeUnit), logSlowQueryBySlf4j(threshold, TimeUnit), logSlowQueryByJUL(threshold, TimeUnit), logSlowQueryToSysOut(threshold, TimeUnit)"
|
||||
|
||||
— datasource-proxy user guide (slow query listener API)
|
||||
|
||||
> "ProxyDataSourceBuilder.create(actualDataSource).logSlowQueryBySlf4j(1, TimeUnit.SECONDS).multiline().build();"
|
||||
|
||||
— user guide, ProxyDataSourceBuilder example
|
||||
|
||||
> "QueryTransformer and ParameterTransformer allows you to modify executing query and parameters right before calling the database."
|
||||
|
||||
— spring-boot-data-source-decorator README
|
||||
|
||||
> "decorator.datasource.datasource-proxy.slow-query.enable-logging=true"
|
||||
> "decorator.datasource.datasource-proxy.slow-query.threshold=300"
|
||||
|
||||
— spring-boot-data-source-decorator README (Spring Boot application.properties 설정)
|
||||
|
||||
> "@Bean public ParameterTransformer parameterTransformer() { return new MyParameterTransformer(); }"
|
||||
|
||||
— spring-boot-data-source-decorator README (custom bean 등록 패턴)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | datasource-proxy 는 `logSlowQueryBySlf4j(threshold, TimeUnit)` 으로 임계값 기반 슬로우 쿼리 로깅을 제공한다 | "logSlowQueryBySlf4j(threshold, TimeUnit)" | `official-vendor-doc` | datasource-proxy 모든 버전 | 자동으로 파라미터가 마스킹된다는 주장 반증 |
|
||||
| C2 | `ParameterTransformer` 인터페이스를 Bean 으로 등록하면 datasource-proxy 가 파라미터 처리 전에 이를 호출한다 | "@Bean public ParameterTransformer parameterTransformer() { return new MyParameterTransformer(); }" | `official-vendor-doc` | spring-boot-data-source-decorator 사용 환경 | 빌트인 마스킹 기능이 존재한다는 주장 반증 — 커스텀 구현 필요 |
|
||||
| C3 | Spring Boot application.properties 로 슬로우 쿼리 임계값을 초 단위로 설정할 수 있다 (기본값 300초) | "decorator.datasource.datasource-proxy.slow-query.threshold=300" | `official-vendor-doc` | spring-boot-data-source-decorator 1.12.1 (Spring Boot 3.x) | 밀리초 단위 설정이 기본 지원된다는 주장 반증 (초 단위) |
|
||||
| C4 | 슬로우 쿼리 기본 로그 출력에 파라미터 값이 포함되는지 여부는 공식 문서에 명시되지 않았다 — ParameterTransformer 없이는 포함될 가능성 있음 | "QueryTransformer and ParameterTransformer allows you to modify executing query and parameters" | `official-vendor-doc` | datasource-proxy 사용 환경 | 자동으로 파라미터가 마스킹된다는 주장 반증 |
|
||||
| C5 | Spring Boot 통합 라이브러리는 Spring Boot 3.x 를 지원한다 (버전 1.12.1) | "Spring Boot 3.x — Version 1.12.1" | `official-vendor-doc` | Spring Boot 3.x 환경 | — |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`: datasource-proxy 에 슬로우 쿼리 탐지 기능이 있음
|
||||
- `C2`: ParameterTransformer 를 통해 파라미터를 변환(마스킹 포함)할 수 있음 — 단 커스텀 구현 필요
|
||||
- `C3`: Spring Boot application.properties 로 설정 가능
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- 파라미터 마스킹을 위한 빌트인 기능이 존재한다는 주장
|
||||
- 슬로우 쿼리 로그 기본 출력에 파라미터가 포함/미포함된다는 확정적 주장
|
||||
- production 환경에서의 성능 오버헤드 (모든 JDBC 호출 인터셉트)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `ParameterTransformer` 구현으로 모든 파라미터를 `[REDACTED]` 로 치환하는 것이 슬로우 쿼리 로그 출력에도 반영되는지 테스트 필요
|
||||
- `logSlowQueryBySlf4j` 의 기본 메시지 형식 확인 — 파라미터 포함 여부
|
||||
- 임계값이 초 단위인지 밀리초 단위인지 재확인 (공식 문서는 "seconds")
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- ParameterTransformer 는 쿼리 실행 전 파라미터를 변환하는 Hook이므로, 마스킹 전 실제 값이 DB 로 전달됨 — 이는 로그 보안을 위한 변환이지 DB 쿼리 자체를 변경하는 것이 아님
|
||||
- `multiline()` 옵션은 쿼리 로그를 여러 줄로 출력하는 포맷 설정
|
||||
- HikariCP 와 함께 사용 시 HikariCP 가 내부적으로 사용하는 DataSource 를 ProxyDataSource 로 감싸야 함
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/hibernate-slow-query-log-official]]
|
||||
- 이 자료를 인용한 wiki 요약: (미생성)
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "About Dependabot Security Updates — GitHub Official Docs"
|
||||
source_type: official-doc
|
||||
url: https://docs.github.com/en/code-security/concepts/supply-chain-security/about-dependabot-security-updates
|
||||
archive_url:
|
||||
related_branches: [feature-dependency-vulnerability-management-contract]
|
||||
related_projects: []
|
||||
tags: [official-doc, ca-tmpl, security, ci-cd]
|
||||
created: 2026-06-15
|
||||
---
|
||||
|
||||
# About Dependabot Security Updates — GitHub Official Docs
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-dependency-vulnerability-management-contract]] | Dependabot은 조건부(조직 표준이거나 단순 Gradle 구조) 허용. Dependabot security updates의 정의와 grouping 동작(생태계 단위 묶음, 버전 업데이트와 혼합 불가)이 근거. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.github.com/en/code-security/concepts/supply-chain-security/about-dependabot-security-updates
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: GitHub, Inc.
|
||||
- 발행일: (GitHub Docs — 지속 갱신 문서)
|
||||
- 마지막 확인일: 2026-06-15
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
Dependabot security updates의 공식 정의, security vs version updates 구분, grouped security updates 동작 제약(생태계 간 묶음 불가 / 버전 업데이트와 묶음 불가)을 verbatim 으로 확보하기 위해 보관. `feature-dependency-vulnerability-management-contract` 브랜치의 "Dependabot 조건부 허용" 결정의 기반 근거.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
|
||||
|
||||
> [§About Dependabot security updates — bullet list] "*Dependabot security updates* are automated pull requests that help you update dependencies with known vulnerabilities."
|
||||
> (fetched text line 31)
|
||||
|
||||
> [§About Dependabot security updates — bullet list] "*Dependabot version updates* are automated pull requests that keep your dependencies updated, even when they don't have any vulnerabilities. To check the status of version updates, navigate to the **Insights** tab of your repository, then select **Dependency Graph**, and Dependabot."
|
||||
> (fetched text line 32)
|
||||
|
||||
> [§About grouped security updates — paragraph 1] "To further reduce the number of pull requests you may be seeing, you can enable grouped security updates to group sets of dependencies together (per package ecosystem). Dependabot then raises a single pull request to update as many vulnerable dependencies as possible in the group to secure versions at the same time."
|
||||
> (fetched text line 42)
|
||||
|
||||
> [§About grouped security updates — paragraph 2] "For security updates, Dependabot will only group dependencies from different directories per ecosystem under certain conditions and configurations. Dependabot **will not** group dependencies from different package ecosystems together, and it **will not** group security updates with version updates."
|
||||
> (fetched text line 44)
|
||||
|
||||
> [§About Dependabot security updates — paragraph 5] "However, security updates are triggered only for dependencies that are specified in a manifest or lock file."
|
||||
> (fetched text line 23, within longer sentence)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | Dependabot security updates는 알려진 취약점이 있는 의존성을 업데이트하는 자동 PR이다 | [§About Dependabot security updates] "*Dependabot security updates* are automated pull requests that help you update dependencies with known vulnerabilities." | `official-vendor-doc` | GitHub Dependabot이 활성화된 모든 저장소 | 특정 언어/빌드툴(Gradle 등)에서 실제로 동작함을 보장하지 않음. 지원 생태계 목록(별도 페이지) 확인 필요 |
|
||||
| C2 | Dependabot version updates는 취약점 없이도 의존성을 최신으로 유지하는 별도 기능이다 | [§About Dependabot security updates] "*Dependabot version updates* are automated pull requests that keep your dependencies updated, even when they don't have any vulnerabilities." | `official-vendor-doc` | Dependabot version updates를 활성화한 저장소 | security updates와 version updates가 동시에 활성화될 때의 상호작용 세부 동작은 별도 확인 필요 |
|
||||
| C3 | Grouped security updates는 생태계(package ecosystem) 단위로 묶어 단일 PR을 발행한다 | [§About grouped security updates] "you can enable grouped security updates to group sets of dependencies together (per package ecosystem). Dependabot then raises a single pull request to update as many vulnerable dependencies as possible in the group to secure versions at the same time." | `official-vendor-doc` | grouped security updates를 활성화한 저장소 | 어떤 저장소/생태계가 grouping을 지원하는지 — 지원 생태계 별도 페이지 확인 필요 |
|
||||
| C4 | Dependabot은 서로 다른 package ecosystem의 의존성을 하나의 그룹으로 묶지 않으며, security updates와 version updates를 함께 묶지 않는다 | [§About grouped security updates] "Dependabot **will not** group dependencies from different package ecosystems together, and it **will not** group security updates with version updates." | `official-vendor-doc` | grouped security updates 사용 시 항상 적용되는 불변 제약 | 이 제약이 미래 GitHub 정책 변경으로 바뀔 수 없다는 보장은 아님 |
|
||||
| C5 | Security updates는 manifest 또는 lock file에 명시된 의존성에 대해서만 트리거된다 | [§About Dependabot security updates] "security updates are triggered only for dependencies that are specified in a manifest or lock file." | `official-vendor-doc` | Dependabot security updates를 사용하는 모든 저장소 | transitive/indirect 의존성에 대한 PR 생성 여부 (ecosystem별로 다름 — npm은 예외적으로 parent까지 업데이트 가능, 별도 note box 참조) |
|
||||
|
||||
### NOT supported by this page
|
||||
|
||||
- **native auto-merge in dependabot.yml**: 이 페이지에는 `auto-merge` 키워드가 전혀 등장하지 않는다. auto-merge 동작 여부는 별도 페이지(`Configuring Dependabot security updates` 또는 GitHub branch protection / merge queue 문서)에서 확인해야 한다. 이 자료만으로는 "dependabot.yml에 native auto-merge 설정이 없다"고도, "있다"고도 증명 불가 — `NEEDS_CONFIRMATION`.
|
||||
- **Gradle 생태계의 구체적 지원 여부**: 이 페이지는 지원 생태계를 별도 링크(`Dependabot supported ecosystems and repositories`)로 위임. Gradle이 지원됨을 이 페이지에서 직접 확인할 수 없다.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`: GitHub Dependabot security updates의 공식 정의
|
||||
- `C2`: security updates vs version updates의 공식 구분
|
||||
- `C3`: grouped security updates의 동작 방식 (생태계 단위, 단일 PR)
|
||||
- `C4`: grouped security updates의 불변 제약 (cross-ecosystem 묶음 불가, version updates와 혼합 불가)
|
||||
- `C5`: security updates 트리거 조건 (manifest/lock file 명시 의존성 한정)
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Gradle 생태계에서의 실제 지원 여부 (별도 페이지 확인 필요)
|
||||
- native auto-merge 설정의 존재 여부 (이 페이지에서 언급 없음)
|
||||
- transitive dependency 처리의 일반 규칙 (npm은 예외, 다른 생태계는 제한적)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-tmpl Gradle 프로젝트가 Dependabot 지원 생태계 목록에 포함되는지
|
||||
- grouped security updates 활성화 시 실제 PR 생성 패턴 (단순 Gradle 구조 가정 검증)
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- auto-merge 관련: 이 페이지에 없으므로 날조 금지. "GitHub Actions workflow + `gh pr merge --auto`" 또는 별도 branch protection auto-merge 설정으로 구현하는 패턴이 일반적이나, 그 근거는 별도 문서에서 확보 필요.
|
||||
- Gradle grouping 실제 동작: `dependabot.yml`에 `groups:` 키를 추가하면 per-ecosystem 묶음 가능 — 단 상세 설정 방법은 `Configuring Dependabot security updates` 페이지 참조 필요.
|
||||
- 추가로 봐야 할 동일 출처 페이지:
|
||||
- `https://docs.github.com/en/code-security/dependabot/dependabot-security-updates/configuring-dependabot-security-updates` (설정 세부)
|
||||
- `https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates` (version updates 비교)
|
||||
- `https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-supported-ecosystems-and-repositories` (Gradle 지원 여부)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog: (미등록 — 추가 시 여기 링크)
|
||||
- 이 자료를 인용한 wiki 요약: (미생성 — `/ingest` 후 `wiki/concepts/dependabot-security-updates.md` 후보)
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: "Dependabot Supported Ecosystems and Repositories — GitHub Official"
|
||||
source_type: official-doc
|
||||
url: https://docs.github.com/en/code-security/reference/supply-chain-security/supported-ecosystems-and-repositories
|
||||
archive_url:
|
||||
related_branches: [feature-build-release-supply-chain-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, ci-cd, gradle, supply-chain]
|
||||
created: 2026-06-15
|
||||
vendor: GitHub (Dependabot)
|
||||
---
|
||||
|
||||
# Dependabot Supported Ecosystems and Repositories — GitHub Official
|
||||
|
||||
> Layer: `raw/official-docs/` — GitHub 공식 Dependabot 지원 생태계 레퍼런스 발췌.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 별도 작성. 원본은 raw 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-build-release-supply-chain-contract]] | Decision D3 — "Dependabot은 조직 표준일 때 허용"의 근거: Dependabot의 Gradle ecosystem 공식 지원 범위(버전 업데이트 ✓, 보안 업데이트 ✓, 단 파일 파싱 방식 + 보안 업데이트는 dependency submission API 수동 업로드 한정)와 그 한계 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.github.com/en/code-security/reference/supply-chain-security/supported-ecosystems-and-repositories
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: GitHub (Dependabot 공식 문서)
|
||||
- 발행일: (지속 갱신 — last confirmed 2026-06-15)
|
||||
- 마지막 확인일: 2026-06-15
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
Dependabot의 Gradle ecosystem 지원 범위(지원하는 manifest 파일 목록, 버전 업데이트 vs 보안 업데이트의 차이, 파일 파싱 방식 vs Gradle 실행 방식 구분)를 공식 문서로 확보하기 위해 보관. `feature-build-release-supply-chain-contract` D3 — "Dependabot은 조직 표준일 때 허용" 결정이 현재 `UNSUPPORTED_DECISION` 상태이며, 이 문서가 Dependabot의 Gradle 지원 공식 근거가 됨.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5문장)
|
||||
|
||||
> [§ Supported ecosystems maintained by GitHub — 소개] "You can configure updates for repositories that contain a dependency manifest or lock file for one of the supported package managers. For some package managers, you can also configure vendoring for dependencies. For more information, see vendor."
|
||||
|
||||
> [§ Supported ecosystems maintained by GitHub — 지원 표, Gradle 행] "| Gradle | gradle | Not applicable |"
|
||||
> (표 칼럼 순서: Package manager | YAML value | Supported versions | Version updates | Security updates | Private repositories | Private registries | Vendoring. Gradle 행의 aria-label 기준: Version updates=Supported, Security updates=Supported, Private repositories=Supported, Private registries=Supported, Vendoring=Not supported)
|
||||
|
||||
> [§ Gradle — 파일 파싱 방식] "Dependabot supports updates to the following files without needing to run Gradle:"
|
||||
|
||||
> [§ Gradle — 지원 manifest 파일 목록] "- build.gradle, build.gradle.kts (for Kotlin projects)"
|
||||
> "- gradle/libs.versions.toml (for projects using a standard Gradle version catalog)"
|
||||
> "- gradle.lockfile (for projects using Gradle dependency locking)"
|
||||
|
||||
> [§ Gradle — 보안 업데이트 한계] "For Dependabot security updates, Gradle support is limited to manual uploads of the dependency graph data using the dependency submission API. For more information about the dependency submission API, see Using the dependency submission API."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DBOT-ECO-C1 | Dependabot의 `package-ecosystem: gradle` YAML 값으로 Gradle 의존성 업데이트를 설정할 수 있으며, Supported versions는 "Not applicable"(버전 지정 불필요)이다 | [§ 지원 표] "| Gradle | gradle | Not applicable |" | `official-vendor-doc` | Gradle 의존성을 Dependabot으로 관리하는 모든 저장소 | Dependabot이 Gradle을 실제로 실행한다는 것을 증명하지 않음; YAML 설정 방법 자체를 증명하지 않음 |
|
||||
| DBOT-ECO-C2 | Dependabot은 Gradle 업데이트 시 Gradle을 실행하지 않고 파일을 파싱하는 방식으로 동작하며, build.gradle / build.gradle.kts / gradle/libs.versions.toml / gradle.lockfile을 지원한다 | [§ Gradle] "Dependabot supports updates to the following files without needing to run Gradle:" | `official-vendor-doc` | Gradle 프로젝트에서 Dependabot version updates를 사용하는 경우 | Gradle Wrapper 업데이트에는 Gradle을 실행하므로 "파싱만" 진술이 모든 작업에 해당하지는 않음 |
|
||||
| DBOT-ECO-C3 | Gradle Wrapper 업데이트 시에만 Dependabot이 Gradle을 실행하며, gradle/wrapper/gradle-wrapper.properties, gradlew, gradlew.bat, gradle/wrapper/gradle-wrapper.jar를 갱신한다 | [§ Gradle] "To update the Gradle Wrapper, Dependabot runs Gradle and updates:" | `official-vendor-doc` | Gradle Wrapper 버전 추적이 필요한 프로젝트 | 의존성 업데이트(dependency version update)에 Gradle 실행 여부를 증명하지 않음(오히려 DBOT-ECO-C2가 반증) |
|
||||
| DBOT-ECO-C4 | Gradle 보안 업데이트(security updates)는 dependency submission API를 통한 수동 의존성 그래프 업로드로 제한된다 — 자동 감지 방식이 아님 | [§ Gradle] "For Dependabot security updates, Gradle support is limited to manual uploads of the dependency graph data using the dependency submission API." | `official-vendor-doc` | Dependabot 보안 경보(security alerts) + Gradle 프로젝트의 조합 | 버전 업데이트(version updates)의 자동 동작에는 해당 없음; dependency submission API 사용 방법을 증명하지 않음 |
|
||||
| DBOT-ECO-C5 | Gradle은 transitive dependency에 취약점이 감지되더라도 Dependabot이 저장소에서 해당 의존성을 찾을 수 없어 보안 업데이트 PR을 생성하지 않는다 | [§ Gradle Note] "When an alert is detected in a transitive dependency, Dependabot isn't able to find the vulnerable dependency in the repository, and therefore won't create a security update for that alert." | `official-vendor-doc` | Gradle 프로젝트에서 transitive dependency 취약점 관리가 필요한 경우 | 직접 의존성(direct dependency)의 취약점 처리 방식을 증명하지 않음; Renovate 등 대안 도구의 동작을 증명하지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `DBOT-ECO-C1`: Gradle이 Dependabot이 공식 지원하는 생태계임 (YAML value: `gradle`)
|
||||
- `DBOT-ECO-C2`: Dependabot version updates가 build.gradle, build.gradle.kts, gradle/libs.versions.toml, gradle.lockfile을 Gradle 실행 없이 파싱함
|
||||
- `DBOT-ECO-C3`: Gradle Wrapper 업데이트 시에는 Gradle 실행 발생
|
||||
- `DBOT-ECO-C4`: Gradle 보안 업데이트는 dependency submission API 수동 업로드로만 동작 — 자동 스캔이 아님
|
||||
- `DBOT-ECO-C5`: Transitive dependency 취약점에 대해서는 보안 업데이트 PR 미생성
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Renovate 대비 Dependabot의 우위 또는 열위 (이 문서는 Dependabot 단독 범위)
|
||||
- Gradle `implementation` vs `api` 의존성의 처리 차이
|
||||
- Dependabot이 Gradle의 모든 dependency resolution을 완전히 이해한다는 것
|
||||
- Private registry 설정 방법의 상세 (별도 문서: "Configuring access to private registries for Dependabot")
|
||||
- 내 프로젝트(ca-skeleton)에 적용하려면 추가 확인이 필요한 것:
|
||||
- Gradle dependency-locking (`gradle.lockfile`) 사용 시 Dependabot이 lockfile 업데이트를 생성하는지 (DBOT-ECO-C2는 파일 지원을 명시하나 lockfile 업데이트 동작 세부는 별도 확인 필요)
|
||||
- ca-skeleton의 `gradle/libs.versions.toml` (version catalog) 사용 여부 — 사용 중이면 DBOT-ECO-C2 직접 적용
|
||||
- Dependabot security updates 활성화 시 dependency submission API 연동 구성 필요 여부
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- Gradle 표 행의 Private registries 칼럼: aria-label 기준 `Supported` — WebFetch 1차 결과에서 `Not supported` 로 잘못 요약됨. Self-Grep + HTML aria-label 직접 확인으로 `Supported` 확정.
|
||||
- "without needing to run Gradle" 구문은 Dependabot version updates의 핵심 동작 방식. Maven과 대비: `## Maven` 섹션에 "Dependabot doesn't run Maven but supports updates to pom.xml files."라는 유사 패턴 존재 (line 980, 동일 파일).
|
||||
- Gradle Wrapper 업데이트는 예외적으로 Gradle 실행이 필요하므로 hermetic build 환경에서 Gradle Wrapper 업데이트 PR에 주의 필요.
|
||||
- D3 결정에 대해: 이 문서는 Dependabot의 Gradle 지원 공식 범위를 증명하지만, Renovate 대비 Dependabot 선택 근거(우열 비교)는 이 문서 단독으로는 증명되지 않음 — D3는 "조직 표준일 때 허용"이라는 조건부 채택이므로 이 자료는 "허용 조건 하의 능력 범위" 증명에 해당.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/dependabot-security-updates-gradle-official]] — Gradle 보안 업데이트 상세
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/supply-chain-gradle-vs-maven-dependency-locking]] — Gradle dependency-locking vs Maven Enforcer (D8 근거)
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/renovate-vulnerability-alerts-gradle-official]] — Renovate Gradle 지원 (D3 Renovate 측 근거)
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/github-dependency-review-action]] — Dependency Review Action
|
||||
- 이 자료를 인용한 branch-note: [[raw/branch-notes/feature-build-release-supply-chain-contract]]
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: official-doc / Docker Compose — `depends_on` long syntax `condition: service_healthy`
|
||||
source_type: official-doc
|
||||
url: https://docs.docker.com/reference/compose-file/services/
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-docker-compose-stack]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, infra, docker]
|
||||
created: 2026-07-16
|
||||
---
|
||||
|
||||
# official-doc / Docker Compose — `depends_on` long syntax `condition: service_healthy`
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-docker-compose-stack]] | D3: `depends_on: condition: service_healthy` 로 keycloak → app 기동 순서를 강제하는 결정의 Compose 사양 근거 — 이 자료가 long-form `depends_on` + `condition` 키의 존재와 `service_healthy` 의 의미(healthcheck 통과 후에만 dependent 기동)를 확인시켜, 기존 branch-note 의 `UNSUPPORTED_DECISION` 라벨을 해소할 근거를 제공한다. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.docker.com/reference/compose-file/services/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Docker, Inc. (Compose Specification 공식 레퍼런스)
|
||||
- 발행일: (페이지에 명시 없음 — 지속 갱신되는 living reference 문서)
|
||||
- 마지막 확인일: 2026-07-16
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-keycloak-docker-compose-stack` branch 의 D3 결정("healthcheck 로 의존성 강제, `depends_on: condition: service_healthy`")이 기존에는 Docker Compose spec 자체가 raw 에 미등록이라 `UNSUPPORTED_DECISION` 이었다. 본 자료는 Docker 공식 Compose file reference 의 `depends_on`/`healthcheck` 섹션 원문을 발췌해 그 결정의 1차 근거로 삼는다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§depends_on / Long syntax] "- `condition`: Sets the condition under which dependency is considered satisfied
|
||||
> - `service_healthy`: Specifies that a dependency is expected to be "healthy"
|
||||
> (as indicated by [`healthcheck`](#healthcheck)) before starting a dependent
|
||||
> service."
|
||||
(line 462, 464-466 in fetched markdown)
|
||||
|
||||
> [§depends_on / Long syntax] "- `service_completed_successfully`: Specifies that a dependency is expected to run
|
||||
> to successful completion before starting a dependent service."
|
||||
(line 467-468)
|
||||
|
||||
> [§depends_on / Short syntax] "With short syntax, Compose does not wait for dependency services to be "healthy" before
|
||||
starting a dependent service."
|
||||
(line 450-451)
|
||||
|
||||
> [§depends_on / Long syntax, 결과 보증 문단] "Compose guarantees dependency services marked with
|
||||
`service_healthy` are "healthy" before starting a dependent service."
|
||||
(line 502-503)
|
||||
|
||||
> [§healthcheck] "The `healthcheck` attribute declares a check that's run to determine whether or not the service containers are "healthy". It works in the same way, and has the same default values, as the HEALTHCHECK Dockerfile instruction"
|
||||
(line 1095)
|
||||
|
||||
> [§healthcheck, 예시 코드블록]
|
||||
> ```yml
|
||||
> healthcheck:
|
||||
> test: ["CMD", "curl", "-f", "http://localhost"]
|
||||
> interval: 1m30s
|
||||
> timeout: 10s
|
||||
> retries: 3
|
||||
> start_period: 40s
|
||||
> start_interval: 5s
|
||||
> ```
|
||||
(line 1103-1109)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| COMPOSE-DEP-C1 | `depends_on` 의 long-form syntax 는 `condition` 키를 지원하며, 그 값 중 하나가 `service_healthy` 다. | [line 462] "`condition`: Sets the condition under which dependency is considered satisfied" | `official-standard` | Compose 파일 작성 시 서비스 간 시작 순서를 `depends_on.<service>.condition` 형태로 세밀 제어하고자 할 때 | 이 claim 만으로는 특정 Docker Compose 버전에서 이 문법이 최초 지원된 시점(버전)까지는 증명하지 않음 (`restart`, `required` 키는 각각 버전 도입 각주가 있으나 `condition` 자체엔 버전 각주 없음) |
|
||||
| COMPOSE-DEP-C2 | `service_healthy` 조건은 "dependency 가 `healthcheck` 로 표시된 대로 'healthy' 상태가 된 뒤에야 dependent 서비스를 시작한다"는 것을 의미한다. | [line 464-466] "`service_healthy`: Specifies that a dependency is expected to be "healthy" (as indicated by [`healthcheck`](#healthcheck)) before starting a dependent service." | `official-standard` | keycloak(dependency) 에 `healthcheck` 가 정의되어 있고, app(dependent) 이 `depends_on: keycloak: condition: service_healthy` 를 선언하는 구성 | keycloak 서비스 자체에 `healthcheck` 가 없거나 잘못 정의된 경우 이 조건이 영원히 unhealthy 로 남아 app 이 기동하지 않을 수 있다는 실패 모드까지는 이 인용이 직접 말하지 않음 (별도 확인 필요) |
|
||||
| COMPOSE-DEP-C3 | `service_completed_successfully` 조건은 dependency 가 "성공적으로 완료 실행된 뒤에야" dependent 서비스를 시작한다는 의미다 (`service_healthy`, `service_started` 와 대비되는 별도 조건). | [line 467-468] "`service_completed_successfully`: Specifies that a dependency is expected to run to successful completion before starting a dependent service." | `official-standard` | init-container 성격의 1회성 job 서비스에 의존하는 구성 (본 branch 의 keycloak/app 상시 실행 서비스에는 미해당) | keycloak/postgres/app 모두 상시 실행 서비스이므로 이 조건이 D3 결정에 직접 쓰이지는 않음 — 대조용 claim |
|
||||
| COMPOSE-DEP-C4 | short syntax (`depends_on: [db]`) 는 healthcheck 를 기다리지 않고 시작 순서만 보장한다 — long syntax `condition: service_healthy` 와 대조되는 기본 동작. | [line 450-451] "With short syntax, Compose does not wait for dependency services to be "healthy" before starting a dependent service." | `official-standard` | short-form 을 쓸지 long-form 을 쓸지 결정하는 근거 — D3 가 명시적으로 long-form 을 선택해야 하는 이유 | short syntax 를 쓸 때 실제로 발생하는 실패 사례(예: keycloak JWKS 미준비 시 앱 기동 실패)의 재현 로그까지 증명하지는 않음 — 그건 실 구현 후 `raw/errors/`에서 별도 검증 |
|
||||
| COMPOSE-DEP-C5 | Compose 는 `service_healthy` 로 표시된 dependency 들이 "healthy" 상태가 된 뒤에만 dependent 서비스를 생성한다는 것을 보증(guarantee)한다. | [line 502-503] "Compose guarantees dependency services marked with `service_healthy` are "healthy" before starting a dependent service." | `official-standard` | D3 결정의 핵심 정당화 문장 — "app 이 keycloak ready 이전에 기동해 JWKS 호출 실패" 문제를 `depends_on: condition: service_healthy` 로 해결할 수 있다는 근거 | 이 guarantee 는 "시작 순서"에 대한 것이며, keycloak 컨테이너 내부의 애플리케이션(realm import, admin bootstrap 등)이 완전히 초기화됐다는 것까지 보증하지 않음 — healthcheck 자체가 무엇을 검사하는지에 따라 다름 (keycloak `/health/ready` 엔드포인트 정의는 별도 raw 필요, branch-note Claims To Verify 참조) |
|
||||
| COMPOSE-DEP-C6 | `healthcheck` 속성은 서비스 컨테이너가 "healthy" 한지 판정하는 체크를 선언하며, `test`(문자열 또는 리스트), `interval`, `timeout`, `retries`, `start_period`, `start_interval` 필드를 가진다 (예시: `interval: 1m30s`, `timeout: 10s`, `retries: 3`, `start_period: 40s`). | [line 1095] "The `healthcheck` attribute declares a check that's run to determine whether or not the service containers are "healthy"." + [line 1103-1109] 코드블록 | `official-standard` | keycloak/postgres 서비스에 실제 `healthcheck:` 블록을 작성할 때 필드명·형식의 근거 | 이 자료는 healthcheck 필드의 문법만 정의할 뿐, keycloak 이미지에 적합한 `test` 커맨드 값(예: `curl` 이 이미지에 존재하는지, `/health/ready` 경로가 맞는지)까지는 증명하지 않음 — Keycloak 벤더 문서에서 별도 확인 필요 (branch-note 의 `KC-CONTAINER-C5` needs-confirmation claim 참조) |
|
||||
|
||||
### Strength 허용값 참고
|
||||
|
||||
본 문서 전 claim 은 Docker 공식 Compose file reference (docs.docker.com) 원문에서 직접 발췌했으므로 모두 `official-standard` — Compose Specification 은 Docker 가 관리하는 오픈 사양(Compose Spec)의 공식 레퍼런스 구현체 문서다.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `COMPOSE-DEP-C1`~`C2`, `C5`: Docker Compose long-form `depends_on` 문법에 `condition: service_healthy` 가 존재하며, 이는 dependency 의 `healthcheck` 가 healthy 를 보고한 뒤에만 dependent 서비스가 시작됨을 보증한다.
|
||||
- `COMPOSE-DEP-C3`: `service_completed_successfully` 조건의 존재 (대조용, 본 branch 미사용).
|
||||
- `COMPOSE-DEP-C4`: short syntax 와 long syntax 의 동작 차이.
|
||||
- `COMPOSE-DEP-C6`: `healthcheck` 속성 자체의 필드 문법(`test`/`interval`/`timeout`/`retries`/`start_period`/`start_interval`).
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- keycloak 컨테이너에 실제로 어떤 `healthcheck.test` 커맨드가 적합한지 (예: `curl` 바이너리 존재 여부, `/health/ready` 엔드포인트 활성화 조건) — 이는 Keycloak 벤더 문서 영역.
|
||||
- `condition: service_healthy` 가 정확히 어느 Docker Compose 버전부터 지원되는지의 버전 각주 (`restart`/`required` 키는 버전 각주가 있으나 `condition` 자체엔 없음).
|
||||
- `service_healthy` guarantee 가 애플리케이션 수준의 완전한 준비 상태(예: realm import 완료)까지 보증한다는 것 — 이건 컨테이너 healthcheck 정의 범위에 달려 있음.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- keycloak 서비스에 실제 `healthcheck:` 블록을 작성할 때 쓸 `test` 커맨드 (Keycloak 26.x 이미지에 `curl`/`wget` 존재 여부, management port 9000 분리 여부) — branch-note 의 `needs-confirmation` claim.
|
||||
- postgres `healthcheck` (`pg_isready`) 는 이 자료 범위 밖 (postgres 공식 이미지 문서에서 확인).
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 이 발췌는 `docs.docker.com/reference/compose-file/services.md` (사이트가 제공하는 plaintext/markdown 미러 — 페이지 하단 "View Markdown" 버튼이 가리키는 URL) 에서 가져온 원문이다. 렌더링된 HTML 페이지가 아니라 이 markdown 소스를 사용한 이유: HTML 은 Tailwind 클래스와 pagefind 마크업이 뒤섞여 있어 verbatim self-grep 이 어렵고, WebFetch 도구는 내부적으로 소형 모델을 거쳐 paraphrase 된 요약을 반환해 self-grep 검증이 불가능했다. `curl` 로 두 URL 모두 raw 상태로 저장해 대조했다.
|
||||
- (미검증 추론 금지 — 추가 해석 없음)
|
||||
- 추가로 봐야 할 동일 출처 페이지: Keycloak 공식 `/health/ready` 엔드포인트 정의 페이지 (management port 분리 여부), postgres 공식 이미지의 `pg_isready` healthcheck 예시.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/keycloak-server-containers-docker]] — Keycloak Docker 공식 (KC_* 환경 변수, `feature-keycloak-docker-compose-stack` D1/D5 근거)
|
||||
- [[raw/official-docs/keycloak-getting-started-docker]] — Docker quickstart (D6 근거)
|
||||
- 이 자료를 인용한 wiki 요약: (미생성)
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: official-doc / Docker Compose — Networking (Default Service-Name DNS Discovery & extra_hosts / host-gateway)
|
||||
source_type: official-doc
|
||||
url: https://docs.docker.com/compose/how-tos/networking/
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-iss-claim-hostname-mismatch]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, networking, docker]
|
||||
created: 2026-07-17
|
||||
---
|
||||
|
||||
# Docker Compose — Networking (Default Service-Name DNS Discovery & extra_hosts / host-gateway)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
|
||||
> Docker Compose 공식 문서의 (1) 기본 네트워크에서 서비스명이 별도 설정 없이 DNS 로 발견되는 동작과 (2) `extra_hosts`/`host-gateway` 를 이용한 custom hostname→IP 매핑 메커니즘을 다룬다.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
frontmatter `source_type:` 은 `official-doc` — Docker Compose 공식 레퍼런스 문서.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-iss-claim-hostname-mismatch]] | 해결 방안 (A)/(C) 의 `extra_hosts` + `host-gateway` 메커니즘 공식 명세, **그리고** 해결 방안 (F)(Spring `jwk-set-uri` 를 `keycloak:8080` 로 지정)의 도달성 근거 — Compose 기본 서비스명 DNS 가 별도 설정 없이 동작한다는 공식 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.docker.com/compose/how-tos/networking/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Docker, Inc. (공식 Docker Compose 문서)
|
||||
- 발행일: (페이지에 명시된 발행일 확인 불가 — 공식 docs.docker.com 상시 갱신 페이지)
|
||||
- 마지막 확인일: 2026-07-17
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
단일 EC2/로컬 docker-compose 환경에서 backend 가 Keycloak 의 JWKS 를 별도 hostname 설정 없이 `http://keycloak:8080` 로 fetch 할 수 있다는 것(해결 방안 F)과, `extra_hosts`/`host-gateway` 로 custom hostname 을 컨테이너에 주입하는 메커니즘(해결 방안 A/C)의 공식 근거를 보관하기 위해.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Default network and service discovery, fetched line 24] "Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by its service name."
|
||||
|
||||
> [§Default network and service discovery, fetched line 25] "Each service registers its name with an internal DNS server, so containers can reach each other using the service name directly. No IP addresses or manual configuration is needed."
|
||||
|
||||
> [§Custom DNS with extra_hosts, fetched line 83] "You can add custom hostname-to-IP mappings to a container's /etc/hosts file using extra_hosts. This is useful when a service needs to resolve a hostname that isn't registered in Docker's internal DNS. For example, a fixed-IP dependency or a staging endpoint:"
|
||||
|
||||
> [§Custom DNS with extra_hosts, fetched line 85] "To map a hostname dynamically to the host machine's IP, use the special host-gateway value:"
|
||||
|
||||
> [§Custom DNS with extra_hosts, fetched line 87] "On Linux, host-gateway resolves to the host's IP on the default bridge network. On Mac and Windows, Docker automatically provides this, host-gateway resolves to the same internal IP address as host.docker.internal."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DOCKER-COMPOSE-NET-C1 | 기본 Compose 네트워크에 join 한 컨테이너는 다른 컨테이너로부터 도달 가능(reachable)하고, 자신의 서비스명으로 발견 가능(discoverable)하다 | "Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by its service name." | `official-vendor-doc` | `docker compose up` 이 생성하는 기본 `<project-name>_default` bridge 네트워크에 join 한 모든 서비스 | `network_mode: host`/`none`/커스텀 external 네트워크 미가입 상태 등 기본 네트워크를 벗어난 구성에서의 동작은 증명하지 않음 |
|
||||
| DOCKER-COMPOSE-NET-C2 | 각 서비스는 자신의 이름을 internal DNS server 에 등록하며, 컨테이너는 IP 주소나 별도 수동 설정 없이 서비스명으로 직접 서로 도달할 수 있다 | "Each service registers its name with an internal DNS server, so containers can reach each other using the service name directly. No IP addresses or manual configuration is needed." | `official-vendor-doc` | backend 컨테이너가 `http://keycloak:8080` 처럼 서비스명을 hostname 으로 사용해 같은 기본 네트워크의 Keycloak 컨테이너에 도달하는 것(해결 방안 F 의 핵심 근거) — 두 서비스가 같은 Compose 프로젝트의 동일 기본 네트워크에 있다는 전제 | 서비스가 다른 custom network 로 분리되어 있거나 `network_mode: host` 를 쓰는 경우까지 이 동작이 성립한다는 것은 증명하지 않음. JWT `iss` claim 값 자체(토큰에 박히는 issuer URL)와는 별개 문제 — 이 claim 은 "backend 가 JWKS 를 fetch 할 수 있는지"만 증명하며, `KC_HOSTNAME` 이 결정하는 `iss` claim 값 일치 여부는 증명하지 않음 |
|
||||
| DOCKER-COMPOSE-NET-C3 | `extra_hosts` 는 컨테이너의 `/etc/hosts` 파일에 custom hostname-to-IP 매핑을 추가하는 옵션이며, Docker 내부 DNS 에 등록되지 않은 hostname(예: 고정 IP 의존성, staging endpoint)을 해석해야 할 때 유용하다 | "You can add custom hostname-to-IP mappings to a container's /etc/hosts file using extra_hosts. This is useful when a service needs to resolve a hostname that isn't registered in Docker's internal DNS." | `official-vendor-doc` | `extra_hosts` 로 `api.staging`, `cache.internal`, `host.docker.internal` 같은 **Docker 내부 DNS 에 없는 신규 hostname** 을 매핑하는 시나리오 (해결 방안 A/C 의 메커니즘 근거) | `extra_hosts` 가 base 이미지의 **기존** `/etc/hosts` entry(예: `127.0.0.1 localhost`)를 재매핑(override)할 때 어느 쪽이 우선하는지는 이 페이지가 다루지 않음 — 본문 예시는 전부 신규 hostname 추가 사례뿐, `localhost` 자체를 재매핑하는 사례는 없음 |
|
||||
| DOCKER-COMPOSE-NET-C4 | host 머신의 IP 를 동적으로 매핑하려면 `extra_hosts` 에 특수 값 `host-gateway` 를 사용한다 | "To map a hostname dynamically to the host machine's IP, use the special host-gateway value:" | `official-vendor-doc` | `extra_hosts: ["<hostname>:host-gateway"]` 형태로 host IP 를 몰라도 동적으로 매핑해야 하는 모든 시나리오 | `host-gateway` 를 지원하는 최소 Docker Engine/Compose 버전은 이 페이지에 명시되어 있지 않음 — 버전 요구사항은 별도 release notes 확인 필요 |
|
||||
| DOCKER-COMPOSE-NET-C5 | Linux 에서 `host-gateway` 는 기본 bridge 네트워크에서의 host IP 로 해석되고, Mac/Windows 에서는 Docker 가 자동으로 이를 제공하며 `host.docker.internal` 과 동일한 internal IP 로 해석된다 | "On Linux, host-gateway resolves to the host's IP on the default bridge network. On Mac and Windows, Docker automatically provides this, host-gateway resolves to the same internal IP address as host.docker.internal." | `official-vendor-doc` | Linux 단일 EC2 환경(본 branch 의 실 배포 대상) vs macOS/Windows Docker Desktop 학습 환경 간 `host-gateway` 해석 차이 비교 | 이 차이가 발생하는 정확한 내부 구현(예: Docker Desktop 의 VM 네트워크 계층)은 다루지 않으며, `default bridge network` 가 아닌 custom bridge/overlay 네트워크에서의 `host-gateway` 해석은 이 페이지가 직접 증명하지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `DOCKER-COMPOSE-NET-C1`, `DOCKER-COMPOSE-NET-C2`: Compose 기본 네트워크에 join 한 서비스는 별도 설정 없이 서비스명으로 서로 발견·도달 가능 (해결 방안 F 의 도달성 근거)
|
||||
- `DOCKER-COMPOSE-NET-C3`, `DOCKER-COMPOSE-NET-C4`, `DOCKER-COMPOSE-NET-C5`: `extra_hosts` 로 custom hostname 을 `/etc/hosts` 에 추가하는 메커니즘과 `host-gateway` 특수 값의 Linux vs Mac/Windows 해석 차이 (해결 방안 A/C 의 메커니즘 근거)
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `extra_hosts` 로 `localhost` 자체를 재매핑했을 때 base 이미지의 기존 `127.0.0.1 localhost` entry 와의 우선순위 — 이 페이지는 신규 hostname 추가 예시(`api.staging`, `host.docker.internal` 등)만 다루며 기존 entry 재매핑 사례를 다루지 않음. **본 branch 해결 방안 (C) 의 핵심 리스크이므로 별도 실측 검증 필요**
|
||||
- `host-gateway` 지원 최소 Docker 버전 — 버전 정보는 이 페이지 소관이 아니라 release notes 소관
|
||||
- JWT `iss` claim 값 자체의 일치 여부(=`KC_HOSTNAME` 이 결정하는 issuer URL 문제) — 이 자료는 "backend 가 Keycloak 에 네트워크적으로 도달 가능한지"만 증명하며, 토큰에 박히는 `iss` 문자열이 backend 의 `issuer-uri` 기대값과 일치하는지는 별개 문제(본 branch의 D1/D5, Keycloak hostname guide 소관)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 실제 docker-compose 환경에서 backend 컨테이너가 `extra_hosts: ["localhost:host-gateway"]` 설정 후 `/etc/hosts` 를 열어 기존 `127.0.0.1 localhost` entry 가 override 되는지, 아니면 두 entry 가 공존해 첫 번째 것이 우선하는지 실측 (해결 방안 C 채택 전 필수 검증 — Claims To Verify 표에 추가 권장)
|
||||
- `extra_hosts: host-gateway` 의 최소 Docker 버전을 별도 Docker Engine release notes 로 확인
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- 이 자료는 두 가지 서로 다른 branch 결정을 동시에 뒷받침한다: (1) 해결 방안 F — 애초에 `extra_hosts`/`host-gateway` 없이 Spring `jwk-set-uri` 를 `keycloak:8080` (Compose 서비스명) 로 지정해도 JWKS fetch 자체는 되는지의 도달성 근거, (2) 해결 방안 A/C — `localhost`/`host.docker.internal` 을 host-gateway 로 매핑해 backend 가 호스트에 도달하는 메커니즘. 둘은 상호 배타적 해법이 아니라 "JWKS 를 어디서 fetch 하느냐"의 대안 축이므로, branch-note 의 Decision Evidence Map 에서 D2(세 해결 방안 A/B/C) 옆에 F 도 별도 옵션으로 추가하는 것을 고려할 것(현재 branch 본문의 In-scope 목록에는 F 가 명시적으로 나열되어 있지 않음 — branch-note 갱신 필요 여부는 사용자 판단).
|
||||
- 추가로 봐야 할 동일 출처 페이지: Docker Engine `network_mode` 공식 문서 (D2 의 `network_mode: host` Linux-only 진술과 최소 Docker 버전 요구사항을 직접 뒷받침하려면 별도 raw 필요 — 현재 branch-note Claims To Verify 에 `needs-confirmation` 으로 남아있음).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/docker-port-publishing-loopback-bind-official]] — 같은 Docker 공식 문서군, host-boundary 포트 노출 관련 (다른 branch 근거)
|
||||
- [[raw/official-docs/keycloak-hostname-configuration]] — 본 branch 의 다른 Source. `KC_HOSTNAME` 이 결정하는 `iss` claim 값 자체의 공식 근거 (본 자료의 네트워크 도달성 근거와 상호 보완)
|
||||
- 이 자료를 인용한 wiki 요약: (아직 없음)
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: official-doc / Docker Engine 20.10 Release Notes — host.docker.internal on Linux (dockerd) + host-gateway BuildKit fix
|
||||
source_type: official-doc
|
||||
url: https://docs.docker.com/engine/release-notes/20.10/
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-iss-claim-hostname-mismatch]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, networking, docker]
|
||||
created: 2026-07-17
|
||||
---
|
||||
|
||||
# Docker Engine 20.10 Release Notes — host.docker.internal on Linux (dockerd) + host-gateway BuildKit fix
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
|
||||
> Docker Engine 20.10 시리즈 release notes 중 (1) Linux `dockerd` 에서 `host.docker.internal` 지원이 도입된 릴리즈, (2) `--add-host=host.docker.internal:host-gateway` 조합이 BuildKit 활성화 시 실패하던 버그와 그 수정 릴리즈를 다룬다.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
frontmatter `source_type:` 은 `official-doc` — Docker Engine 공식 release notes (docs.docker.com).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-iss-claim-hostname-mismatch]] | 해결 방안 (A)/(C) 가 요구하는 `host-gateway`/`host.docker.internal` 의 최소 Docker Engine 버전 확정 — branch 본문 "마주친 문제" 의 출처 없는 "최소 Docker 20.10+" 메모를 공식 release notes 로 confirm(부분) — 단, `host.docker.internal` 자체의 dockerd/Linux 지원 시작 버전은 confirm 되나, `host-gateway` 라는 리터럴 값의 도입 버전은 이 페이지가 명시적으로 진술하지 않음(아래 Usage Boundaries 참조) |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.docker.com/engine/release-notes/20.10/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Docker, Inc. (공식 Docker Engine release notes)
|
||||
- 발행일: 페이지 자체는 상시 갱신되는 aggregated release notes. 인용한 개별 항목의 발행일은 각 버전 heading 에 명시됨 — `20.10.0` → `2020-12-08`, `20.10.23` → `2023-01-19`.
|
||||
- 마지막 확인일: 2026-07-17
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
branch `feature-keycloak-iss-claim-hostname-mismatch` 의 해결 방안 (A)/(C) 가 전제하는 "`extra_hosts: host-gateway` 는 Docker 20.10+ 에서 동작한다" 는 branch 본문의 출처 없는 메모를 공식 Docker Engine release notes 로 검증하기 위해. 조사 결과 `host.docker.internal` 의 Linux dockerd 지원은 20.10.0 에서 명시적으로 확인되나, `host-gateway` 리터럴 자체의 도입 버전은 이 페이지 텍스트만으로는 확정할 수 없다(아래 C2/Usage Boundaries).
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§20.10.0 / Networking, 2020-12-08] "Support host.docker.internal in dockerd on Linux" (moby/moby#40007)
|
||||
|
||||
> [§20.10.23 / Bug fixes and enhancements, 2023-01-19] "Fix an issue where `docker build` would fail when using `--add-host=host.docker.internal:host-gateway` with BuildKit enabled" (moby/moby#44650)
|
||||
|
||||
> [§20.10.0 heading + date] "20.10.0" / "2020-12-08"
|
||||
|
||||
> [§20.10.23 heading + date] "20.10.23" / "2023-01-19"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DOCKER-2010-C1 | Docker Engine 20.10.0 (2020-12-08 릴리즈) 의 Networking 항목에서 Linux 상의 `dockerd` 에 `host.docker.internal` 지원이 추가되었다고 명시 | "Support host.docker.internal in dockerd on Linux" | `official-vendor-doc` | Docker Engine(`dockerd`) 의 **Linux** 빌드에서 `host.docker.internal` 이름 해석 기능이 20.10.0 부터 존재함을 확인 | (1) `host-gateway` 라는 리터럴 문자열/값 자체가 이 항목과 같은 릴리즈(20.10.0)에서 도입되었다는 것 — 이 페이지 20.10.0 항목 텍스트에는 "host-gateway" 문자열이 등장하지 않음. (2) Docker Compose `extra_hosts:` YAML 문법의 존재/버전 요구사항 — 그건 Compose spec 소관 ([[raw/official-docs/docker-compose-networking-extra-hosts-official]]). (3) Docker Desktop(Mac/Windows) 에서의 `host.docker.internal` 동작 — 이 항목은 Linux dockerd 한정이며 Desktop 은 별도 VM 네트워크 계층 사용 |
|
||||
| DOCKER-2010-C2 | Docker Engine 20.10.23 (2023-01-19 릴리즈) 의 Bug fixes and enhancements 항목에서, `docker build` 가 BuildKit 활성화 상태로 `--add-host=host.docker.internal:host-gateway` 를 사용할 때 실패하던 버그를 수정했다고 명시 | "Fix an issue where `docker build` would fail when using `--add-host=host.docker.internal:host-gateway` with BuildKit enabled" | `official-vendor-doc` | `docker build`(BuildKit 경로) 가 `host-gateway` 리터럴을 `--add-host` 값으로 사용하는 조합에 20.10.23 이전 결함이 있었고 그 시점엔 이미 `host-gateway` 문법 자체는 존재/사용 중이었음을 간접 확인(버그 수정 대상이려면 기능이 이미 존재해야 함) | `host-gateway` 가 정확히 몇 버전에 **처음** 도입되었는지 — 이 항목은 "이미 존재하던 기능의 BuildKit 특정 결함 수정"만 서술하며 도입 시점을 진술하지 않음. `docker run`/Compose 경로(비-BuildKit)에서 동일 결함이 있었는지도 이 항목 범위 밖 |
|
||||
|
||||
### Strength 근거
|
||||
|
||||
- 두 claim 모두 Docker Engine 공식 release notes(docs.docker.com, Docker, Inc. 발행)에서 직접 인용 — `official-vendor-doc`.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `DOCKER-2010-C1`: `host.docker.internal` 의 Linux `dockerd` 지원은 **20.10.0(2020-12-08)** 부터 공식적으로 존재.
|
||||
- `DOCKER-2010-C2`: `--add-host=host.docker.internal:host-gateway` + BuildKit 조합은 **20.10.23(2023-01-19) 이전** 에 결함이 있었고, 그 시점 이전에 이미 해당 문법이 사용되고 있었음(버그 수정 대상이므로).
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **`host-gateway` 리터럴 값 자체의 최초 도입 버전.** 이 페이지 전체(20.10.0 ~ 20.10.24)에서 문자열 `host-gateway` 가 등장하는 곳은 20.10.23 버그 수정 항목 단 한 곳뿐이며, 20.10.0 의 `host.docker.internal` 항목 텍스트에는 등장하지 않는다(self-grep 확인 완료). 따라서 branch 본문의 "최소 Docker 20.10+" 메모는 **`host.docker.internal`(Linux dockerd) 지원 자체는 confirm** 되지만, `host-gateway` 리터럴의 최소 버전을 이 문서만으로 20.10.0 이라고 확정할 수는 **없다** — 부분 confirm.
|
||||
- Docker Compose 파일의 `extra_hosts:` YAML 문법 — 이는 Compose spec/공식 문서 소관이며, 본 project 에서는 이미 [[raw/official-docs/docker-compose-networking-extra-hosts-official]] 가 그 근거를 담당한다(해당 문서도 "host-gateway 지원 최소 Docker 버전은 이 페이지 소관 아님"이라고 명시하며 본 문서를 그 후속 조사로 기대하고 있었음).
|
||||
- Docker Desktop(Mac/Windows) 의 `host.docker.internal`/`host-gateway` 동작 — 이 릴리즈 노트의 해당 항목은 명시적으로 "in dockerd on Linux" 로 범위를 한정한다. Desktop 환경은 별도 VM 네트워크 계층(예: Compose 문서의 "On Mac and Windows, Docker automatically provides this" 진술)이 적용되며 이 문서가 다루는 영역이 아니다.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `host-gateway` 리터럴의 정확한 도입 버전을 확정하려면 moby/moby PR #40007(20.10.0 의 host.docker.internal PR) 원문을 직접 확인해야 한다 — 이 release notes 페이지의 텍스트만으로는 "같은 PR에서 host-gateway 값도 함께 도입되었는지"를 증명할 수 없다(합리적 추정은 가능하나 verbatim 근거 아님).
|
||||
- branch 의 실 배포 대상(단일 EC2, Linux)에서 사용할 실제 Docker Engine 버전이 20.10.0 이상(이상적으로 20.10.23 이상, BuildKit 버그를 피하려면)인지 `docker version` 으로 확인 필요.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- moby/moby PR #40007 의 제목("Support host.docker.internal in dockerd on Linux")과 Linux 에서 `host.docker.internal` 이 구현되는 일반적 메커니즘(= `--add-host` 의 특수 값)을 고려하면 `host-gateway` 리터럴도 같은 PR/릴리즈에서 함께 도입되었을 가능성이 높다 — 그러나 이 release notes 문서 자체는 그 사실을 verbatim 으로 진술하지 않으므로 **미검증 추론**으로만 남긴다. 확정하려면 GitHub PR #40007 원문 또는 moby/moby CHANGELOG 를 별도 raw 자료로 추가 조사할 것.
|
||||
- 이 자료는 [[raw/official-docs/docker-compose-networking-extra-hosts-official]] 가 명시적으로 남긴 "host-gateway 지원 최소 Docker 버전을 별도 Docker Engine release notes 로 확인" 이라는 후속 조사 요청에 대한 응답으로 작성됨.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/docker-compose-networking-extra-hosts-official]] — 같은 branch 의 다른 Source. `extra_hosts`/`host-gateway` 의 **문법과 Linux vs Mac/Windows 해석 차이**를 다루며, "최소 버전은 이 페이지 소관 아님"이라고 명시적으로 본 문서로 위임했음.
|
||||
- [[raw/official-docs/keycloak-hostname-configuration]] — 본 branch 의 또 다른 Source. `KC_HOSTNAME` 이 결정하는 `iss` claim 값 자체의 공식 근거 (본 자료의 host-gateway 네트워크 메커니즘과 상호 보완).
|
||||
- 이 자료를 인용한 wiki 요약: (아직 없음)
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: official-doc / Docker Engine — Host network driver (platform support & port-mapping behavior)
|
||||
source_type: official-doc
|
||||
url: https://docs.docker.com/engine/network/drivers/host/
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-iss-claim-hostname-mismatch]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, networking, docker]
|
||||
created: 2026-07-17
|
||||
---
|
||||
|
||||
# official-doc / Docker Engine — Host network driver (platform support & port-mapping behavior)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
- `official-doc` — Docker 공식 Engine 레퍼런스 문서.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-iss-claim-hostname-mismatch]] | 해결 방안 (B) `network_mode: host` 의 **플랫폼 제약** — branch 노트가 출처 없이 "Linux only" 라고 적은 미검증 메모를 공식 문서로 confirm/refute 하는 1차 근거. 결과: 부분 refute — Docker Engine on Linux 는 native 지원이 맞으나, Docker Desktop 4.34+ 에서도 opt-in 으로 지원됨(무조건 "동작 안 함" 아님). 단 layer 4 한정 + Enhanced Container Isolation 비호환 등 추가 제약이 있음. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.docker.com/engine/network/drivers/host/
|
||||
- 아카이브 URL: (미확보)
|
||||
- 저자 / 조직: Docker, Inc. (공식 Engine 문서)
|
||||
- 발행일: (페이지에 명시 없음 — 최종 갱신일 비공개)
|
||||
- 마지막 확인일: 2026-07-17
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
branch `feature-keycloak-iss-claim-hostname-mismatch` 의 §범위 "해결 방안 (B) `network_mode: host` (Docker hairpin NAT — Linux only)" 및 §마주친 문제의 "macOS/Windows Docker Desktop에서 동작 안 함 — Linux only" 진술이 **출처 없는 미검증 메모**였다. 이 공식 문서로 해당 진술의 현재 정확도를 판정하고, 포트 매핑(`-p`/`--publish`/`ports:`) 비호환 사유를 근거로 확보하기 위해 저장.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5문장)
|
||||
|
||||
> [§Prerequisites] "The host networking driver only works on Linux hosts, and as an opt-in feature in Docker Desktop version 4.34 and later." (line 60)
|
||||
|
||||
> [§Platform support] "Docker Desktop version 4.34 and later (requires enabling the feature in Settings)" (line 29)
|
||||
|
||||
> [§Limitations] "Only Linux containers are supported. Host networking does not work with Windows containers." (line 55)
|
||||
|
||||
> [§Note] "Given that the container does not have its own IP-address when using host mode networking, port-mapping doesn't take effect, and the -p, --publish, -P, and --publish-all option are ignored, producing a warning instead:" (line 20)
|
||||
|
||||
> [§Limitations] "The host network feature of Docker Desktop works on layer 4. This means that unlike with Docker on Linux, network protocols that operate below TCP or UDP are not supported." (line 53)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DOCKER-HOSTNET-C1 | Host network driver 는 Linux host 에서 native 로 동작하며, Docker Desktop 4.34+ 에서는 설정에서 수동 활성화해야 하는 opt-in 기능으로 지원된다 | [§Prerequisites] "The host networking driver only works on Linux hosts, and as an opt-in feature in Docker Desktop version 4.34 and later." | `official-vendor-doc` | `network_mode: host` 를 사용하는 개발 환경이 Linux host 인지 Docker Desktop(4.34+, opt-in 활성화)인지 판별 | 이 branch 의 실제 개발 환경이 Linux host 인지 Docker Desktop 인지, 또는 Docker Desktop 버전이 4.34 이상인지는 증명하지 않음 — 로컬 `docker version` 확인 별도 필요 |
|
||||
| DOCKER-HOSTNET-C2 | Docker Desktop 에서 host networking 지원은 버전 4.34 이상이며 Settings > Resources > Network 에서 수동 활성화가 필요하다 | [§Platform support] "Docker Desktop version 4.34 and later (requires enabling the feature in Settings)" | `official-vendor-doc` | Docker Desktop 사용자가 4.34 미만이면 host networking 자체가 존재하지 않음을 확인하는 근거 | 4.34 미만 버전에서의 정확한 동작(완전 부재 vs 다른 제약)은 이 문장만으로 세부 확인 불가 |
|
||||
| DOCKER-HOSTNET-C3 | host networking 은 Windows 컨테이너에서 동작하지 않으며 Linux 컨테이너만 지원한다 | [§Limitations] "Only Linux containers are supported. Host networking does not work with Windows containers." | `official-vendor-doc` | branch 의 keycloak/backend 컨테이너가 Linux 컨테이너 이미지인 경우 이 제약은 무관함을 확인 | Windows 컨테이너를 아예 사용하지 않는 본 프로젝트에는 직접 영향 없음 — 이 claim 은 그 사실을 증명하는 게 아니라 제약의 존재만 증명 |
|
||||
| DOCKER-HOSTNET-C4 | host network mode 에서는 컨테이너가 자체 IP 를 갖지 않으므로 port-mapping 이 작동하지 않고, `-p`/`--publish`/`-P`/`--publish-all` 옵션이 무시되며 경고가 출력된다 | [§Note] "Given that the container does not have its own IP-address when using host mode networking, port-mapping doesn't take effect, and the -p, --publish, -P, and --publish-all option are ignored, producing a warning instead:" | `official-vendor-doc` | docker-compose `ports:` 매핑을 `network_mode: host` 서비스에 남겨두면 무시된다는 근거 — keycloak 서비스 compose 파일에서 `ports:` 제거 필요성의 근거 | 이 문장은 `docker run -p` / CLI 플래그 기준 진술이며, docker-compose YAML 의 `ports:` 키를 문자 그대로 언급하지 않음 — 동작은 기능적으로 동일하나 문서가 compose YAML 문법을 직접 지칭하지 않는다는 점은 명시해둘 것 |
|
||||
| DOCKER-HOSTNET-C5 | Docker Desktop 의 host network 기능은 layer 4(TCP/UDP) 에서만 동작하며, Linux 의 Docker 와 달리 TCP/UDP 하위 계층 프로토콜은 지원하지 않는다 | [§Limitations] "The host network feature of Docker Desktop works on layer 4. This means that unlike with Docker on Linux, network protocols that operate below TCP or UDP are not supported." | `official-vendor-doc` | Docker Desktop 환경에서 host networking 을 쓸 때 Linux native 구현과 기능적으로 동일하지 않음을 아는 근거 | HTTP/JWT 트래픽(TCP 기반)이 이 제약의 영향을 받는지 여부는 이 문장이 직접 말하지 않음 — TCP 기반이므로 영향 없을 것이라는 추론은 이 자료의 claim 이 아니라 별도 추론(§메모에서만 다룸) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `DOCKER-HOSTNET-C1`/`C2`: host networking 이 Linux Engine 뿐 아니라 Docker Desktop 4.34+ 에서도 (opt-in 조건부로) 지원된다는 것. 즉 branch 노트의 "Linux only" 라는 무조건적 진술은 **현재(2026-07-17 확인) 기준 부정확**하다 — 정확히는 "Linux native, Docker Desktop 은 4.34+ 부터 opt-in 지원, 단 layer 4 한정".
|
||||
- `DOCKER-HOSTNET-C3`: Windows 컨테이너는 host networking 을 지원하지 않는다는 것.
|
||||
- `DOCKER-HOSTNET-C4`: host mode 에서 포트 매핑 CLI 플래그가 무시되고 경고가 출력된다는 것.
|
||||
- `DOCKER-HOSTNET-C5`: Docker Desktop 구현이 layer 4 로 제한된다는 것.
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Keycloak 의 `iss` claim 생성 로직이나 `KC_HOSTNAME` 동작 — Docker 문서는 Keycloak 을 언급하지 않는다.
|
||||
- Spring Security Resource Server 의 `issuer-uri` 검증 방식 — 전혀 다른 스택.
|
||||
- `extra_hosts: host-gateway` 의 최소 Docker 버전(20.10+) — 이 페이지에는 해당 진술 없음 (branch 노트의 다른 미검증 메모는 이 자료로 해결되지 않음, 별도 자료 필요).
|
||||
- macOS/Windows Docker Desktop 에서 host networking 이 "완전히 동작 안 한다"는 절대 진술 — 오히려 이 자료는 정반대로 4.34+ 에서 opt-in 지원됨을 명시한다.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 실제 개발 환경이 Linux Engine 인지 Docker Desktop 인지, Docker Desktop 이라면 버전이 4.34 이상인지 (`docker version` 로 확인).
|
||||
- Docker Desktop 이라면 Settings > Resources > Network 에서 "Enable host networking" 이 실제로 켜져 있는지.
|
||||
- Enhanced Container Isolation 이 활성화된 환경인지 (활성화 시 host networking 자체와 상호 배타적).
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- branch 노트 §마주친 문제의 "network_mode: host 사용 시 macOS/Windows Docker Desktop에서 동작 안 함 — Linux only" 메모는 **부분적으로만 맞다**. 정확히는: Linux Engine 은 native 지원, Docker Desktop 은 4.34+ 부터 opt-in 지원(수동 활성화 필요) — "전혀 동작 안 함"은 아님. 단, 이 자료가 Docker Desktop 버전을 명시하지 않으므로, branch 작성 시점(2026-05-25)의 Docker Desktop 버전이 4.34 미만이었을 가능성은 배제 못함 — 그 경우 당시 관찰은 사실이었을 수 있다. 이는 **미검증 추론**이며 검증하려면 branch 작성 시점의 Docker Desktop 버전 확인이 필요하다.
|
||||
- HTTP/JWT 트래픽이 TCP 기반이라 layer-4-only 제약(`DOCKER-HOSTNET-C5`)의 영향을 받지 않을 것이라는 판단은 이 자료가 직접 말하지 않는 **미검증 추론**이다 — Claims Extracted 표에는 넣지 않았음.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Docker Compose 공식 스펙의 `network_mode: host` 항목(compose YAML 문법 기준 진술 확보), `extra_hosts` / `host-gateway` 공식 문서(별도 branch 미검증 메모 해결용).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/keycloak-hostname-configuration]] — 같은 branch 의 1차 근거 (Keycloak `KC_HOSTNAME` 및 iss claim 공식 설명)
|
||||
- [[raw/official-docs/docker-port-publishing-loopback-bind-official]] — 같은 vendor(Docker) 의 포트 publishing 관련 공식 문서
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: official-doc / Docker Engine — Container Port Publishing (default 0.0.0.0 vs loopback bind)
|
||||
source_type: official-doc
|
||||
url: https://docs.docker.com/engine/network/port-publishing/
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-header-spoofing-defense]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, networking, security, docker]
|
||||
created: 2026-07-16
|
||||
---
|
||||
|
||||
# Docker Engine — Container Port Publishing (default 0.0.0.0 vs loopback bind)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
|
||||
> Docker Engine 공식 문서의 `-p`/`--publish` 포트 퍼블리싱 기본 동작(모든 host 주소로 열림)과 loopback(`127.0.0.1`) bind 로 접근 범위를 Docker host 로 제한하는 옵션을 다룬다.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
frontmatter `source_type:` 은 `official-doc` — Docker Engine 공식 레퍼런스 문서.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-header-spoofing-defense]] | D4 — 단일 EC2 co-located 배포에서 backend 의 published port 를 `127.0.0.1`(loopback) 로 bind 하면 Docker host 에서만 접근 가능해지고, `0.0.0.0` publish 는 "insecure by default" — Security Group(ENI 경계) 이 커버하지 못하는 host-boundary 계층의 방어라는 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.docker.com/engine/network/port-publishing/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Docker, Inc. (공식 Docker Engine 문서)
|
||||
- 발행일: (페이지에 명시된 발행일 확인 불가 — 공식 docs.docker.com 상시 갱신 페이지)
|
||||
- 마지막 확인일: 2026-07-16
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
단일 EC2 에 Keycloak + backend 가 co-located 될 때, EC2 Security Group(ENI 경계)만으로는 같은 host 안에서 도달 가능한 포트를 막을 수 없다. Docker 의 `-p 127.0.0.1:HOST_PORT:CONTAINER_PORT` bind 가 SG 와 별개인 **host-boundary** 계층 방어라는 것을 공식 문서로 확인하기 위해 보관.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [docs.docker.com/engine/network/port-publishing — default bind 설명, fetched line 40] "By default, when a container's ports are mapped without any specific host address, the Docker daemon publishes ports to all host addresses (`0.0.0.0` and `[::])."
|
||||
|
||||
> [docs.docker.com/engine/network/port-publishing — `-p` 예시, fetched line 42] "For example, `docker run -p 8080:80 [...]` creates a mapping between port 8080 on any address on the Docker host, and the container's port 80."
|
||||
|
||||
> [docs.docker.com/engine/network/port-publishing — 보안 경고, fetched line 16 / 44] "Publishing container ports is insecure by default. Meaning, when you publish a container's ports it becomes available not only to the Docker host, but to the outside world as well."
|
||||
|
||||
> [docs.docker.com/engine/network/port-publishing — loopback 제한, fetched line 46] "If you include the localhost IP address (`127.0.0.1`, or `::1`) with the publish flag, only the Docker host can access the published container port."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DOCKER-PORT-PUB-C1 | 특정 host 주소를 지정하지 않고 포트를 매핑하면, Docker daemon 은 기본적으로 모든 host 주소(`0.0.0.0`, `[::]`)에 포트를 publish 한다 | "By default, when a container's ports are mapped without any specific host address, the Docker daemon publishes ports to all host addresses (`0.0.0.0` and `[::])." | `official-vendor-doc` | host IP prefix 없이 `-p HOST_PORT:CONTAINER_PORT` 또는 동등한 docker-compose `ports:` 매핑을 사용하는 모든 단일 Docker Engine host | Docker Swarm ingress mode 의 routing mesh 동작이나 Docker Desktop 의 VM 네트워크 계층에서의 차이는 다루지 않음 |
|
||||
| DOCKER-PORT-PUB-C2 | `docker run -p 8080:80` 예시는 Docker host 의 **모든 주소**에서 포트 8080 을 컨테이너 포트 80 에 매핑한다는 것을 공식 예시로 보여준다 | "For example, `docker run -p 8080:80 [...]` creates a mapping between port 8080 on any address on the Docker host, and the container's port 80." | `official-reference` | `-p` flag 문법 이해 (host IP 생략 시 동작) | 특정 애플리케이션의 보안 요구사항 충족 여부는 증명하지 않음 |
|
||||
| DOCKER-PORT-PUB-C3 | 컨테이너 포트를 publish 하는 것은 기본적으로 안전하지 않다(insecure by default) — publish 하면 Docker host 뿐 아니라 외부 세계에서도 접근 가능해진다 | "Publishing container ports is insecure by default. Meaning, when you publish a container's ports it becomes available not only to the Docker host, but to the outside world as well." | `official-vendor-doc` | host IP 제한 없이 `-p` 를 사용하는 모든 배포 시나리오에 대한 일반 경고 | 어떤 추가 완화책(SG, 방화벽, NetworkPolicy 등)이 충분한지는 증명하지 않음 — 이 경고는 Docker 자체의 기본 동작에 대한 것 |
|
||||
| DOCKER-PORT-PUB-C4 | publish flag 에 localhost IP(`127.0.0.1` 또는 `::1`)를 포함시키면, 오직 Docker host 만 publish 된 컨테이너 포트에 접근할 수 있다 | "If you include the localhost IP address (`127.0.0.1`, or `::1`) with the publish flag, only the Docker host can access the published container port." | `official-vendor-doc` | `-p 127.0.0.1:HOST_PORT:CONTAINER_PORT` 형태의 loopback bind, 단일 host Docker Engine 배포 | Docker host 자체에 접근 가능한 다른 프로세스/사용자로부터의 접근까지 막는다는 뜻은 아님(loopback 은 host-boundary 방어이지, host 내부 프로세스 간 격리는 아님). Swarm ingress mode 에서의 동일 동작은 증명하지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `DOCKER-PORT-PUB-C1`, `DOCKER-PORT-PUB-C2`: host IP 미지정 시 Docker 가 기본적으로 `0.0.0.0`/`[::]` 전체에 publish 한다는 것
|
||||
- `DOCKER-PORT-PUB-C3`: 이 기본 동작이 "insecure by default" 라는 공식 경고
|
||||
- `DOCKER-PORT-PUB-C4`: `127.0.0.1`/`::1` loopback IP 를 publish flag 에 포함하면 접근 범위가 Docker host 로 좁혀진다는 것
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- EC2 Security Group(ENI 경계)이 이 host-boundary 방어를 대체하거나 불필요하게 만든다는 것 — 오히려 이 문서는 SG 와 무관한 **별개 계층**(host 자체의 listen 주소)을 설명할 뿐이다
|
||||
- loopback bind 만으로 같은 host 안의 다른 프로세스/컨테이너로부터의 접근까지 차단된다는 것(이건 host 내부 격리 문제이며 별도 검증 필요)
|
||||
- Docker Swarm 모드의 routing mesh(ingress) 에서도 동일하게 동작한다는 것
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 실제 `docker-compose.yml` 에서 backend 서비스의 `ports:` 를 `127.0.0.1:8080:8080` 형태로 bind 했을 때, EC2 인스턴스 로컬에서만 curl 성공하고 외부 IP 로는 실패하는지 실측 검증 (branch note 의 `Claims To Verify` 표에 해당 항목 추가 필요)
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- D4 의 핵심 논리는 "SG = ENI(네트워크 인터페이스) 경계 방어, loopback bind = host 프로세스의 listen 주소 자체를 제한하는 방어" 로 계층이 다르다는 것. 이 자료는 그 두 번째 계층(host listen 주소)의 공식 근거만 제공한다. SG 와의 관계(계층이 다르다는 비교 주장)는 이 문서 자체가 말하는 바가 아니라 branch-note 저자의 조합적 추론이므로, branch-note 쪽 Decision Evidence Map 에서는 별도로 "SG 비교" 부분을 UNSUPPORTED 로 표시하거나 AWS Security Group 공식 문서를 별도 raw 로 추가해 뒷받침해야 함.
|
||||
- 추가로 봐야 할 동일 출처 페이지: AWS EC2 Security Group 공식 문서 (D4 의 SG 경계 비교 주장을 직접 뒷받침하려면 별도 raw 필요 — 현재 branch-note TODO 에 "검토 후보"로만 있음).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- (같은 주제의 다른 official-doc 없음 — 최초 등록)
|
||||
- 이 자료를 인용한 wiki 요약: (아직 없음)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: Martin Fowler — Domain Event (EAA Dev catalog)
|
||||
source_type: official-doc
|
||||
url: https://martinfowler.com/eaaDev/DomainEvent.html
|
||||
archive_url:
|
||||
related_branches: [feature-domain-event-outbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-outbox-pattern, domain-event, ddd, fowler, backend, messaging]
|
||||
created: 2026-06-11
|
||||
last_reviewed: 2026-06-11
|
||||
---
|
||||
|
||||
# Martin Fowler — Domain Event (EAA Dev catalog)
|
||||
|
||||
> Layer: `raw/official-docs/` — Martin Fowler EAA Dev catalog "DomainEvent" (2005-12-12) verbatim 발췌.
|
||||
> D1 ("domain event 는 transport detail 을 모름") 의 정의 근거: domain event 의 본질이 도메인에서 일어난 사실의 기록이며 transport/infrastructure 가 정의에 포함되지 않음을 보인다.
|
||||
> **Evidence strength: `engineering-blog`** — Fowler EAA Dev 는 개인 패턴 카탈로그 (draft 상태 명시). official-vendor-doc / official-standard 아님. D1 을 공식 best practice 로 격상하려면 Eric Evans DDD 원전 등 별도 official raw 필요.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-domain-event-outbox-contract]] | D1 — "domain event 는 transport detail (Kafka topic, HTTP endpoint, Slack channel) 을 모름" — domain event 의 정의가 도메인 사실의 기록(record of something that happened in the domain)이며 transport/infrastructure 가 정의에 포함되지 않음을 보이는 근거. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://martinfowler.com/eaaDev/DomainEvent.html
|
||||
- 아카이브 URL: (미수집 — archive.org 스냅샷 별도 확보 권장)
|
||||
- 저자 / 조직: Martin Fowler
|
||||
- 발행일: 2005-12-12
|
||||
- 마지막 확인일: 2026-06-11
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-domain-event-outbox-contract` D1 은 "domain event 가 Kafka topic / HTTP endpoint 등 transport detail 을 포함해서는 안 된다"는 금지 결정이지만, Decision Evidence Map 에서 UNSUPPORTED_DECISION 라벨이 붙어 있었다. 본 자료는 Fowler 의 Domain Event 정의("captures the memory of something interesting which affects the domain")와 "two-layer 구조에서 second layer 는 실제 input source 를 모른다"는 설명이 D1 의 정의 근거가 됨을 보이기 위해 수집한다. 단, Fowler 원문이 "transport independence" 를 직접 claim 하지는 않으므로 해석 범위는 Usage Boundaries 참조.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
|
||||
|
||||
> [§subtitle / tagline] "Captures the memory of something interesting which affects the domain"
|
||||
|
||||
> [§How it Works — opening] "The essence of a Domain Event is that you use it to capture things that can trigger a change to the state of the application you are developing. These event objects are then processed to cause changes to the system, and stored to provide an Audit Log."
|
||||
|
||||
> [§How it Works — two-layer] "In this stream the first input layer of the system takes no action to the stimulus other than to create and log an event. The second layer can then be ignorant of the actual input source, it just reacts to the event and processes it."
|
||||
|
||||
> [§How it Works — immutability] "I characterize the data on a Domain Event as immutable source data that captures what the event is about and mutable processing data that records what the system does in response to it."
|
||||
|
||||
> [§How it Works — time] "Events are about something happening at a point in time, so it's natural for events to contain time information. When doing this it's important to consider two Time Point that could be stored with the event: the time the event occurred in the world and the time the event was noticed."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리. 내 프로젝트 해석은 Usage Boundaries 에만 기술.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DOMAIN-EVT-FOWLER-C1 | Domain Event 의 목적은 도메인에 영향을 미친 흥미로운 것의 기억(memory)을 포착하는 것이다 | [§tagline] "Captures the memory of something interesting which affects the domain" | `engineering-blog` | Fowler EAA Dev 패턴 카탈로그에서의 Domain Event 정의 | "transport detail 포함 금지" 를 직접 명시하지 않음. Eric Evans DDD 원전과 같은 공식 표준은 아님 |
|
||||
| DOMAIN-EVT-FOWLER-C2 | Domain Event 의 본질은 application state 변경을 유발하는 것들을 포착하는 데 있으며, 이벤트 객체는 처리된 후 Audit Log 로 저장된다 | [§How it Works] "The essence of a Domain Event is that you use it to capture things that can trigger a change to the state of the application you are developing. These event objects are then processed to cause changes to the system, and stored to provide an Audit Log." | `engineering-blog` | Event Sourcing 이나 Outbox 패턴의 정의 레이어 논거로 사용 가능 | "Kafka topic 또는 HTTP endpoint 와 결합해야 한다/하지 말아야 한다"는 직접 진술 없음 |
|
||||
| DOMAIN-EVT-FOWLER-C3 | two-layer 구조에서 두 번째 레이어는 실제 input source 를 모른 채 이벤트에 반응한다 | [§How it Works] "The second layer can then be ignorant of the actual input source, it just reacts to the event and processes it." | `engineering-blog` | 이벤트 처리 레이어의 input-source 독립성 논거 | 이 문장은 event processor 의 input 추상화를 설명하는 것이지, domain event 객체 자체가 transport detail 을 배제해야 한다는 prescriptive claim 이 아님 |
|
||||
| DOMAIN-EVT-FOWLER-C4 | Domain Event 의 source data 는 불변(immutable)이며, 이벤트가 무엇에 관한 것인지를 포착하는 불변 source data 와 시스템 반응을 기록하는 mutable processing data 로 특성화된다 | [§How it Works] "I characterize the data on a Domain Event as immutable source data that captures what the event is about and mutable processing data that records what the system does in response to it." | `engineering-blog` | event payload 설계 시 불변성 및 데이터 분리 기준 | "불변성이 transport independence 를 보장한다"는 논리적 도약은 이 자료가 직접 지지하지 않음 |
|
||||
| DOMAIN-EVT-FOWLER-C5 | 이벤트는 특정 시점에 발생한 것이므로 두 가지 Time Point — 세계에서 이벤트가 발생한 시간(occurred)과 인지된 시간(noticed) — 를 저장할지 고려해야 한다 | [§How it Works] "Events are about something happening at a point in time, so it's natural for events to contain time information. When doing this it's important to consider two Time Point that could be stored with the event: the time the event occurred in the world and the time the event was noticed." | `engineering-blog` | Domain Event payload 의 timestamp 필드 설계 (`occurredAt` vs `notifiedAt`) | "어떤 timestamp 필드명이 표준인가"를 prescribe 하지 않음. ca-tmpl 의 `occurredAt` 필드명은 내부 결정 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `DOMAIN-EVT-FOWLER-C1`: Domain Event = "도메인에서 일어난 흥미로운 것의 기억" 이라는 Fowler 의 정의
|
||||
- `DOMAIN-EVT-FOWLER-C2`: Domain Event 는 application state 변경 유발 + Audit Log 저장 목적의 객체라는 정의
|
||||
- `DOMAIN-EVT-FOWLER-C3`: 이벤트 처리 두 번째 레이어가 실제 input source 에 무관하게 동작한다는 설명 (input abstraction)
|
||||
- `DOMAIN-EVT-FOWLER-C4`: Domain Event source data 의 불변성 원칙
|
||||
- `DOMAIN-EVT-FOWLER-C5`: `occurredAt` (occurred) / `noticedAt` (noticed) 두 가지 Time Point 고려 필요성
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **"domain event 가 transport detail 을 포함해서는 안 된다"는 prescriptive 규칙을 원문이 직접 claim 하지 않는다.** D1 의 "transport detail 을 모름"은 DOMAIN-EVT-FOWLER-C1~C3 의 정의로부터 도출된 _해석_ 이지, Fowler 원문의 verbatim 진술이 아니다. transport-independence 는 해석이지 직접 claim 이 아님을 Usage Boundary 에 명시한다.
|
||||
- Fowler EAA Dev 는 개인 패턴 카탈로그이며, 원문 자체에 "this material is very much in draft form" 이라고 명시되어 있음. Eric Evans DDD, Vaughn Vernon IDDD 같은 공식 원전과 동등한 강도로 인용할 수 없다.
|
||||
- DOMAIN-EVT-FOWLER-C3 의 "second layer ignorant of input source" 는 event processor / handler 의 아키텍처 layering 을 설명하는 것이지, domain event 클래스의 필드 구성에 대한 규칙이 아니다.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- D1 을 `UNSUPPORTED_DECISION` 에서 `engineering-blog` 강도 이상으로 격상하려면 Eric Evans "Domain-Driven Design" 또는 Vaughn Vernon "Implementing Domain-Driven Design" 의 domain event 정의 raw 별도 수집 필요.
|
||||
- `DOMAIN-EVT-FOWLER-C5` 의 Time Point 두 가지를 ca-tmpl outbox table 필드 (`occurredAt` + 별도 `relayedAt` 등)로 매핑하는 결정은 내부 결정이며 이 raw 가 직접 prescribe 하지 않음.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- Fowler 원문 첫 문단: "this material is very much in draft form and I won't be doing any corrections or updates" — 2005년 작성 이후 갱신 없음. `engineering-blog` strength 이상의 인용 금지.
|
||||
- DOMAIN-EVT-FOWLER-C3 ("second layer ignorant of input source") 는 D1 의 간접 지지 근거로 사용 가능하나, 그 자체가 "transport detail 포함 금지" prescriptive 규칙은 아님. branch-note Decision Evidence Map 에서 이 distinction 을 명시해야 함.
|
||||
- 추가로 봐야 할 동일 출처 페이지: https://martinfowler.com/eaaDev/EventSourcing.html (EventSourcing 패턴, DOMAIN-EVT-FOWLER-C2 의 "Event Sourcing" 언급과 연결)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog:
|
||||
- [[raw/official-docs/domain-fowler-anemic-vs-rich-model]] — 동일 저자, rich domain model 관련
|
||||
- [[raw/official-docs/microservices-io-transactional-outbox]] — outbox 패턴 카탈로그 (Chris Richardson)
|
||||
- [[raw/official-docs/outbox-debezium-official-docs]] — Debezium outbox SMT (transport layer 측)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: Martin Fowler — Anemic Domain Model (anti-pattern)
|
||||
source_type: official-doc
|
||||
url: https://martinfowler.com/bliki/AnemicDomainModel.html
|
||||
archive_url: https://web.archive.org/web/2024/https://martinfowler.com/bliki/AnemicDomainModel.html
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [domain, ddd, anemic-model, rich-model, fowler, ca-skeleton]
|
||||
related_branches: [feature-domain-modeling-guardrails, feature-architecture-enforcement-rules, feature-skeleton-package-blueprint-contract, feature-domain-feature-onboarding-contract]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Martin Fowler — AnemicDomainModel
|
||||
|
||||
> Layer: `raw/official-docs/` — Martin Fowler bliki "AnemicDomainModel" (2003-11-25) verbatim 발췌. ca-tmpl 의 Rich Domain Model 강제 결정 (invariant in constructor / safe reason enum / domain logger ban) 의 1차 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-domain-modeling-guardrails]] | rich model 강제 — domain class에 invariant 위치, mutation은 aggregate method 호출만, anemic getter/setter 거부 |
|
||||
| [[raw/branch-notes/feature-architecture-enforcement-rules]] | "all logic in *Service" anemic 패턴을 ArchUnit 룰로 차단 (domain method 비어있으면 lint 경고) 근거 |
|
||||
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | feature 패키지 내 `domain/` 디렉터리가 단순 DTO 가 아니라 behavior 포함 entity/VO 임을 강제 |
|
||||
| [[raw/branch-notes/feature-domain-feature-onboarding-contract]] | 신규 feature 온보딩 시 anemic 회피 체크리스트 (생성 시 invariant validation / setter 노출 금지) 근거 |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
ca-tmpl 결정: "domain logger ban + safe reason enum + invariant in constructor"는 Rich Domain Model을 강제하는 결정. 반대 방향(Anemic Model)은 ca-tmpl이 명시적으로 거부한 안티패턴. Fowler의 글이 가장 자주 인용되는 출처.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://martinfowler.com/bliki/AnemicDomainModel.html
|
||||
- 아카이브 URL: https://web.archive.org/web/2024/https://martinfowler.com/bliki/AnemicDomainModel.html
|
||||
- 보조: Eric Evans "Domain-Driven Design" Ch.5 (entity behavior)
|
||||
- 보조: "Refactoring" 2nd ed. — primitive obsession / value object 추출
|
||||
- 저자/조직: Martin Fowler
|
||||
- 발행일: 2003-11-25
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Opening] "at first blush it looks like the real thing...little more than bags of getters and setters"
|
||||
|
||||
> [§Critique] "The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together."
|
||||
|
||||
> [§Critique] "The anemic domain model is really just a procedural style design, exactly the kind of thing that object bigots like me...have been fighting"
|
||||
|
||||
> [§Cost] "they incur all of the costs of a domain model, without yielding any of the benefits"
|
||||
|
||||
> [§Cost] "The primary cost is the awkwardness of mapping to a database, which typically results in a whole layer of O/R mapping"
|
||||
|
||||
> [§Consequence] "you essentially end up with Transaction Scripts, and thus lose the advantages that the domain model can bring"
|
||||
|
||||
> [§Domain logic] "The logic that should be in a domain object is domain logic - validations, calculations, business rules"
|
||||
|
||||
> [§Eric Evans 인용] "the more common mistake is to give up too easily on fitting the behavior into an appropriate object, gradually slipping toward procedural programming."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| FOWLER-ANEMIC-C1 | Anemic Domain Model 은 OO 의 핵심 원칙 (data + process 결합) 에 반하므로 anti-pattern 으로 분류된다 | [§Critique] "it's so contrary to the basic idea of object-oriented design; which is to combine data and process together" | `engineering-blog` | OO 언어 (Java/C#/Smalltalk 류) 도메인 모델 | 모든 getter/setter heavy 클래스가 anemic 이라는 뜻은 아님 — Transaction Script 패턴 자체는 별도 trade-off 결정 |
|
||||
| FOWLER-ANEMIC-C2 | Anemic model 은 절차적 (procedural) 스타일 설계와 동등하다 | [§Critique] "The anemic domain model is really just a procedural style design" | `engineering-blog` | OO 설계 평가 | "절차적이면 항상 나쁘다" 의 증거는 아님 — Fowler 자신이 Transaction Script 도 별도 valid pattern 으로 분류 |
|
||||
| FOWLER-ANEMIC-C3 | Anemic model 은 domain model 의 비용(O/R 매핑 등)을 모두 지불하면서 이득은 못 얻는 구조 | [§Cost] "they incur all of the costs of a domain model, without yielding any of the benefits" + [§Cost] "The primary cost is the awkwardness of mapping to a database, which typically results in a whole layer of O/R mapping" | `engineering-blog` | JPA/Hibernate 등 O/R 매핑 사용하는 프로젝트 | "O/R 매핑이 무조건 비용" 이라는 일반화 아님 — 글 자체가 domain model + O/R 매핑 비교 맥락 |
|
||||
| FOWLER-ANEMIC-C4 | Anemic 구조의 귀결은 Transaction Scripts 가 되어 domain model 의 장점을 잃는다 | [§Consequence] "you essentially end up with Transaction Scripts, and thus lose the advantages that the domain model can bring" | `engineering-blog` | "domain model 을 채택했다고 표방하는" 코드베이스 | Transaction Script 자체가 부적절하다는 뜻은 아님 — Fowler 의 PoEAA 에서 별도 valid pattern |
|
||||
| FOWLER-ANEMIC-C5 | Domain object 에 위치해야 할 로직 = validations + calculations + business rules | [§Domain logic] "The logic that should be in a domain object is domain logic - validations, calculations, business rules" | `engineering-blog` | OO 도메인 모델 책임 분배 | 로깅 / 트랜잭션 경계 / 외부 IO 가 domain 에 와도 된다는 뜻은 아님 (Fowler 가 별도 application/infrastructure layer 분리 권고) |
|
||||
| FOWLER-ANEMIC-C6 | Eric Evans (DDD) 는 "behavior 를 적절한 객체에 fit 시키는 것을 너무 빨리 포기하고 절차적 프로그래밍으로 점진 회귀하는 것" 을 가장 흔한 실수로 지목 | [§Eric Evans 인용] "the more common mistake is to give up too easily on fitting the behavior into an appropriate object, gradually slipping toward procedural programming." | `engineering-blog` (Fowler 의 Evans 인용) | DDD 채택 프로젝트의 회귀 패턴 진단 | Evans 원전 (DDD 책) 의 정확한 페이지/문단을 본 자료가 명시하지 않음 — Evans 원전 직접 확인 별도 필요 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `FOWLER-ANEMIC-C1`~`C5`: Fowler 가 "Anemic" 을 anti-pattern 으로 명명하고 그 비용/귀결을 진단한 본인 글의 정확한 wording
|
||||
- `FOWLER-ANEMIC-C6`: Fowler 가 Evans 의 입장을 어떻게 요약했는지 (Evans 의 원전 자체 아님)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- Anemic 회피가 "공식 표준 best practice" 라는 정당화 — Fowler bliki 는 본인 의견 글이며 공식 spec/RFC/벤더 doc 아님 (Strength = `engineering-blog`)
|
||||
- ca-tmpl 의 구체 결정 (domain logger ban / safe reason enum / package-private constructor) 의 이름과 메커니즘 — 본 글은 anti-pattern 진단까지만, 구체 구현은 Vernon IDDD 등 별도 자료 결합 필요
|
||||
- JPA + Rich Model 의 ORM-friendly 패턴 (no-arg constructor 가시성 / mapper 위치) — Vernon [[raw/official-docs/domain-vaughn-vernon-aggregate-root]] 가 별도 근거
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ArchUnit 룰로 "domain method 가 비어있으면 경고" 같은 정량 임계 (몇 줄 이상이면 OK?) — 본 자료 범위 밖
|
||||
- "logger 금지" 가 본 글에서 직접 도출되는지 — 본 글에서는 "domain logic = validation/calculation/business rule" 만 명시 (transport/logger 언급 없음)
|
||||
- 한국 백엔드 현장에서 Spring 튜토리얼 default 가 anemic 이라는 관찰 (메모 항목) — 본 글로 증명 불가, 별도 ingest 필요
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석 — 자료 직접 인용 아님)
|
||||
|
||||
- ca-tmpl과의 매핑:
|
||||
- **rich model 측 (ca-tmpl 채택)**: invariant가 constructor/value object에 위치. mutation은 aggregate root method 호출만. domain exception이 사유를 표현.
|
||||
- **anemic model 측 (ca-tmpl 거부)**: domain class는 getter/setter만, 모든 logic이 `*Service`에 위치. ca-tmpl의 "domain logger ban" + "domain exception safe reason"이 anemic을 자연스럽게 거부함 (서비스 측 logger로 다 위임하면 reason enum이 무의미).
|
||||
- 한국 백엔드 현장 관찰 (memo, ca-tmpl과 직접 무관):
|
||||
- 우아한형제들 기술블로그 "DDD Aggregate" 시리즈(2020-2022)는 Vernon 라인의 Rich Model 권장.
|
||||
- Spring 기본 튜토리얼은 종종 anemic 예시 (`@Entity` + setter + `@Service`). ca-tmpl은 이 default를 거부.
|
||||
- 트레이드오프:
|
||||
- rich model은 ORM(JPA)와 마찰: JPA가 reflection으로 객체 생성 → no-arg constructor 필요 → ca-tmpl의 "package-private/protected" 결정으로 해결.
|
||||
- rich model은 DTO/Response 변환 layer가 반드시 필요. ca-tmpl의 "domain-to-response direct exposure forbidden" 결정과 일치.
|
||||
- 출처 신뢰도: Fowler bliki는 공식 spec이 아니지만 OO/DDD 영역에서 reference standard로 취급되는 글. Strength = `engineering-blog` (개인 블로그/bliki 형식이므로 `official-vendor-doc` 으로 격상 금지).
|
||||
|
||||
## 관련 ca-tmpl branch / contract
|
||||
|
||||
- 적용 branch-note:
|
||||
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#19. Domain Application Readiness Contract]]
|
||||
- 대안 그룹: **Group G-J — Privacy / File / Domain Modeling** (domain modeling)
|
||||
- 본 source의 위치: **ca-tmpl reference standard** — Fowler "Anemic Domain Model" anti-pattern
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/domain-vaughn-vernon-aggregate-root]] (Vernon IDDD / Effective Aggregate Design)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
|
||||
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: Vaughn Vernon — Aggregate root rules (Implementing DDD / Effective Aggregate Design)
|
||||
source_type: official-doc
|
||||
url: https://www.dddcommunity.org/library/vernon_2011/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: medium
|
||||
tags: [domain, ddd, aggregate-root, vaughn-vernon, ca-skeleton]
|
||||
related_branches: [feature-domain-modeling-guardrails, feature-architecture-enforcement-rules, feature-skeleton-package-blueprint-contract, feature-domain-feature-onboarding-contract]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Vaughn Vernon — Effective Aggregate Design (Implementing DDD)
|
||||
|
||||
> Layer: `raw/official-docs/` — Vaughn Vernon "Effective Aggregate Design" (2011 paper, 3-part PDF on dddcommunity.org) + "Implementing Domain-Driven Design" (Addison-Wesley 2013) Ch.10 발췌. ca-tmpl 의 aggregate root 가시성 / VO invariant / ORM-friendly constructor 결정의 1차 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-domain-modeling-guardrails]] | aggregate root mutator 가시성 = package-private/protected, VO private constructor + invariant in constructor 채택 |
|
||||
| [[raw/branch-notes/feature-architecture-enforcement-rules]] | "외부에서 entity 의 setter 직접 호출 금지" ArchUnit 룰의 근거 (mutation = root method only) |
|
||||
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | feature 패키지 내 aggregate 경계 = 하나의 root + 내부 entity/VO 묶음 구조 |
|
||||
| [[raw/branch-notes/feature-domain-feature-onboarding-contract]] | 신규 도메인 추가 시 "small aggregate" 가이드 — 거대 aggregate 방지 체크 |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
ca-tmpl 결정: "aggregate root mutator package-private/protected" + "VO private constructor + invariant". 이 결정의 출처. Vernon은 DDD 커뮤니티에서 Eric Evans 다음으로 인용되는 표준 reference.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL (landing 페이지): https://www.dddcommunity.org/library/vernon_2011/ — Vaughn Vernon "Effective Aggregate Design" 3-part PDF 시리즈 메타데이터 페이지
|
||||
- 원본 PDF: 위 페이지에서 Part I/II/III 링크 (직접 PDF 본문 verbatim 발췌 미수집 — 본 raw 의 4 rules 인용은 통상적으로 회자되는 요약 wording 임)
|
||||
- 보조: "Implementing Domain-Driven Design" (Addison-Wesley, 2013, Ch. 10 Aggregates) — 도서 본문, URL 없음
|
||||
- 보조: DDD-Crew aggregate patterns — https://github.com/ddd-crew
|
||||
- 저자/조직: Vaughn Vernon
|
||||
- 발행일: 2011-10-01 (paper, dddcommunity.org sponsor: Domain Language, Inc.), 2013 (IDDD book)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes
|
||||
|
||||
> [§dddcommunity 페이지 메타] "Vaughn's concrete rules spell out the current consensus view of DDD leaders" — 본 URL 페이지 본문은 PDF 링크 + 저자 소개만 노출, 4 rules 본문 자체는 PDF 안에 있음.
|
||||
|
||||
다음은 IDDD 책 Ch.10 (Aggregates) 와 Effective Aggregate Design paper 에서 통상적으로 회자되는 4 rules 의 요약 wording. **PDF 원전 직접 verbatim 발췌 아님 — `needs-confirmation` 으로 분류.**
|
||||
|
||||
> [§Rule 1, paraphrased] "Model True Invariants in Consistency Boundaries. An aggregate is a cluster of associated objects that we treat as a unit for the purpose of data changes. A properly designed aggregate is one that can be modified in any way required by the business with its invariants completely consistent within a single transaction."
|
||||
|
||||
> [§Rule 2, paraphrased] "Design Small Aggregates. Large clusters of objects in one aggregate may be expedient when first conceived, but they will not perform well and will not scale."
|
||||
|
||||
> [§Rule 3, paraphrased] "Reference Other Aggregates by Identity. Storing references to other aggregates by identity (not by direct object reference) keeps aggregates small, supports eventual consistency between aggregates, and avoids the temptation to modify multiple aggregates in a single transaction."
|
||||
|
||||
> [§Rule 4, paraphrased] "Update Other Aggregates Using Eventual Consistency. When you find yourself wanting to modify multiple aggregates in one transaction, reconsider whether they should be a single aggregate, or whether eventual consistency (via domain events) is acceptable."
|
||||
|
||||
> [§IDDD Ch.10, paraphrased] "Make aggregate roots manage internal mutation. Internal entities are mutated only through methods on the root. ORM-friendly constructors should be package-private or protected to prevent application code from bypassing invariants."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| VERNON-AGG-C1 | dddcommunity.org Vernon 2011 페이지에 "Effective Aggregate Design" 3-part PDF 시리즈가 호스팅되고, Vernon 의 규칙들이 DDD 리더들의 합의 견해 (current consensus) 로 소개됨 | [§dddcommunity 페이지 메타] "Vaughn's concrete rules spell out the current consensus view of DDD leaders" | `official-reference` | DDD aggregate 설계 reference 출처 인증 | 4 rules 의 정확한 wording 이 본 URL HTML 에 있다는 뜻은 아님 — 본문은 PDF |
|
||||
| VERNON-AGG-C2 | Aggregate 의 invariant 는 **단일 transaction 내** 에서 완전히 일관되어야 한다 (Rule 1, Consistency Boundary) | [§Rule 1, paraphrased] "modified in any way required by the business with its invariants completely consistent within a single transaction" | `needs-confirmation` (paraphrased — PDF 원전 verbatim 확인 필요) | DDD-style aggregate 채택 프로젝트 | "transaction 경계 = DB transaction" 이라는 뜻은 아님 — Vernon 자신이 동일 글에서 eventual consistency 도 정의 |
|
||||
| VERNON-AGG-C3 | Aggregate 는 작게 설계해야 한다 — 큰 aggregate 는 성능/확장에 문제를 일으킨다 (Rule 2, Small Aggregates) | [§Rule 2, paraphrased] "Large clusters...will not perform well and will not scale" | `needs-confirmation` (paraphrased) | 모든 aggregate 설계 결정 | 정확한 크기 임계 (예: entity 수 ≤ N) 의 정량 기준은 본 자료 없음 |
|
||||
| VERNON-AGG-C4 | 다른 aggregate 는 **direct reference 가 아닌 identity** 로만 참조해야 한다 (Rule 3) | [§Rule 3, paraphrased] "Reference Other Aggregates by Identity...keeps aggregates small, supports eventual consistency" | `needs-confirmation` (paraphrased) | inter-aggregate 관계 모델링 | JPA `@ManyToOne` 자체가 금지된다는 뜻은 아님 (Vernon 도 trade-off 인정) — 별도 ORM 매핑 결정 필요 |
|
||||
| VERNON-AGG-C5 | 여러 aggregate 의 동시 변경이 필요하면 단일 aggregate 로 재설계하거나 domain event 기반 **eventual consistency** 로 처리해야 한다 (Rule 4) | [§Rule 4, paraphrased] "use...eventual consistency (via domain events)" | `needs-confirmation` (paraphrased) | multi-aggregate update 시나리오 | event broker / outbox 의 구체 구현은 본 글 범위 밖 (별도 outbox contract 결정) |
|
||||
| VERNON-AGG-C6 | IDDD Ch.10 은 ORM-friendly constructor 가시성을 package-private/protected 로 두어 application layer 가 invariant 를 우회하지 못하게 하는 패턴을 제시 | [§IDDD Ch.10, paraphrased] "ORM-friendly constructors should be package-private or protected to prevent application code from bypassing invariants" | `needs-confirmation` (도서 인용 — 페이지/문단 미지정) | JPA + DDD aggregate 결합 프로젝트 | Spring/Kotlin/Scala 특유의 추가 가시성 제어 (internal, sealed 등) 는 본 글 범위 밖 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `VERNON-AGG-C1`: dddcommunity.org 가 Vernon paper 의 공식 reference host 라는 사실
|
||||
- **이 자료가 증명하지 않는 것** (현재 단계):
|
||||
- 4 rules 의 **정확한 verbatim wording** — `VERNON-AGG-C2~C5` 는 PDF 본문 직접 확인 전까지 paraphrased / `needs-confirmation`
|
||||
- IDDD 책 Ch.10 의 ORM-friendly constructor 문구 — `VERNON-AGG-C6` 도 도서 원전 페이지 확인 필요
|
||||
- ca-tmpl 의 "domain logger ban" 결정 — Vernon 자체는 logger 금지 명시 안 함, "domain knows nothing about infrastructure" 에서 *간접 도출* (별도 근거 필요)
|
||||
- "JPA annotation 을 domain class 에 두는 것" 의 옳고 그름 — Vernon IDDD 자체는 양쪽 예시 모두 제공
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- PDF Part I/II/III 본문에서 4 rules 의 정확한 chapter 제목과 verbatim 문장 추출 (현재는 paraphrased)
|
||||
- "package-private" 이 Java 외 다른 JVM 언어 (Kotlin `internal`, Scala `private[package]`) 에 어떻게 매핑되는지
|
||||
- ca-tmpl 의 "Option A: domain 외부 매핑 (MapStruct/JpaEntity 분리)" 이 Vernon Option B (JPA annotation on domain) 대비 더 안전하다는 근거 — 본 자료로 증명 불가, 별도 결정 라인 필요
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석 — 자료 직접 인용 아님)
|
||||
|
||||
- ca-tmpl 결정과의 매핑:
|
||||
- **"VO with private constructor + invariant in constructor"** = Vernon's "fail-fast invariant" 원칙. Vernon은 VO를 immutable side-effect-free로 정의.
|
||||
- **"aggregate root mutator package-private/protected"** = IDDD Ch.10의 "ORM-friendly constructor" 패턴. JPA가 reflection으로 객체 생성하려면 no-arg constructor가 필요한데, public이 되면 application layer가 invariant를 우회 가능. package-private/protected로 풀어줌.
|
||||
- **"domain logger ban"** = Vernon의 "domain은 transport-free" 원칙과 호환. Vernon이 명시적으로 "logger 금지"라고 쓰지는 않았으나 "domain knows nothing about infrastructure"에서 도출 가능.
|
||||
- ca-tmpl 결정 중 "ORM 외부 매핑"의 의미:
|
||||
- Option A (ca-tmpl 채택): domain class에 JPA annotation 없이, MapStruct 또는 별도 JpaEntity로 외부 매핑.
|
||||
- Option B (Vernon 도서 예시): domain class에 JPA annotation을 두되 mutator를 package-private 화. 더 간결하지만 domain이 JPA를 import함 → ca-tmpl의 "forbidden import" rule 위배.
|
||||
- ca-tmpl이 Option A를 택한 이유는 본 raw에 명시되지 않음 (별도 결정 라인 필요).
|
||||
- 출처 신뢰도: dddcommunity.org 호스팅 paper + 저자 도서 — DDD 영역에서 reference 표준이나, PDF 본문 verbatim 미확보 → C2~C6 은 `needs-confirmation` 유지. URL 자체는 `official-reference` (community-curated official library).
|
||||
|
||||
## 관련 ca-tmpl branch / contract
|
||||
|
||||
- 적용 branch-note:
|
||||
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#19. Domain Application Readiness Contract]]
|
||||
- 대안 그룹: **Group G-J — Privacy / File / Domain Modeling** (domain modeling)
|
||||
- 본 source의 위치: **ca-tmpl reference standard** — Vernon "Effective Aggregate Design" 4 rules + ORM-friendly constructor
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/domain-fowler-anemic-vs-rich-model]] (Fowler bliki — anemic anti-pattern, Rich Model 의 짝)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-domain-modeling-guardrails]]
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Dual-Write Anti-pattern (microservices.io / 일반 정설)
|
||||
source_type: official-doc
|
||||
url: https://microservices.io/patterns/data/application-events.html
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: medium
|
||||
tags: [ca-outbox-pattern, dual-write, anti-pattern, failure-case, microservices-io, official-doc]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
related_branches: [feature-domain-event-outbox-contract, feature-background-job-async-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Dual-Write Anti-pattern (microservices.io)
|
||||
|
||||
> Layer: `raw/official-docs/` — microservices.io "Pattern: Application events" 및 "Transactional outbox" 페이지의 problem 섹션 **원문 발췌·출처 기록**.
|
||||
> ca-tmpl 의 SKIP LOCKED outbox 결정에 대한 **대안 3: dual-write (DB 와 broker 에 직접 동시 쓰기)**. **실패 케이스**로 조사 — 왜 outbox 가 필요한가의 negative case.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-domain-event-outbox-contract]] | Topic 3 — Outbox Pattern 대안 매트릭스에서 dual-write 를 negative reference 로 채택한 결정의 1차 근거 — 분산 트랜잭션 불가 + 부분 실패 silent divergence |
|
||||
| [[raw/branch-notes/feature-background-job-async-contract]] | Background job 발행에서 `save(); publish();` 직접 호출 패턴이 금지되는 근거 |
|
||||
| [[raw/project-notes/ca-skeleton-operational-contract]] | §18 / §19 의 outbox 채택 정당화 — dual-write 의 명시적 금지 reference |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-tmpl 의 SKIP LOCKED outbox 결정에 대한 **대안 3: dual-write (DB 와 broker 에 직접 동시 쓰기)**. **실패 케이스**로 조사 — outbox 패턴 도입의 직접적 동기. dual-write 는 단일 DB + 단일 broker 환경에서도 atomic 보장이 불가능하며, 부분 실패 시 lost event / phantom event 가 발생한다.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://microservices.io/patterns/data/application-events.html
|
||||
- 보조 URL: https://microservices.io/patterns/data/transactional-outbox.html (problem 섹션)
|
||||
- 보조: Confluent / Debezium 다수 글에서 동일한 "dual-write 금지" 메시지를 반복
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Chris Richardson — microservices.io
|
||||
- 발행일: rolling docs (페이지 자체에 명시 없음)
|
||||
- 마지막 확인일 (capture): 2026-05-22
|
||||
- 마지막 재검증 시도: 2026-05-27
|
||||
- **[2026-05-25 capture]**: user 가 2026-05-22 수집한 인용 원형 유지 (재검증 보류 마커는 2026-05-25 부여된 상태).
|
||||
- **재검증 결과 [2026-05-27 verified attempt]**:
|
||||
- 1차 URL `https://microservices.io/patterns/data/application-events.html` WebFetch 결과 **REDIRECT 상태** — 페이지는 `transactional-outbox.html` 로의 redirect notice 만 남음. 즉 user 가 인용한 "Application events" 본문 자체가 이제 1차 URL 에서 직접 노출되지 않음 (microservices.io 가 페이지를 통합한 것으로 추정).
|
||||
- 보조로 redirect 대상 `transactional-outbox.html` 도 WebFetch 했으나 본 raw 의 3개 quote (Problem / Failure mode / Crash scenario) 는 모두 발췌 결과에서 NOT FOUND.
|
||||
- WebFetch 가 페이지 전체를 노출하지 않을 수 있어 NOT FOUND 가 absence 의 결정적 증거는 아니나, **출처 페이지 자체가 redirect 로 바뀌어** verbatim 위치를 더는 1차 URL 로 가리킬 수 없는 상황.
|
||||
- **재검증 한계 + Strength 정책**: 출처 URL 의 redirect 발생 + 3개 quote 모두 redirect 대상 페이지에서 verbatim NOT FOUND → 본 문서 인용은 모두 `needs-confirmation` Strength **유지** (Strength 상향 없음). wiki 승급 전 다음 중 하나 필요: (a) archive.org 스냅샷으로 원본 "Application events" 페이지 wording 복원 + 인용 위치 확정, (b) 동등한 내용을 명시한 다른 1차 source (Chris Richardson 책 / Confluent / Debezium) 로 cross-reference.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim claimed, user 수집본 [2026-05-25 capture] — [2026-05-27 verified attempt] 출처 URL redirect + verbatim NOT FOUND)
|
||||
|
||||
> [§Application events — Problem] "A service often needs to atomically update the database and publish a message/event. It is not viable to use a distributed transaction that spans the database and the message broker."
|
||||
> — [2026-05-27 verified attempt]: 출처 페이지 redirect → `transactional-outbox.html` 에서 NOT FOUND.
|
||||
|
||||
> [§Application events — Failure mode] "Without 2PC, writing to the database and then publishing to a broker — or vice versa — can result in inconsistency if either step fails."
|
||||
> — [2026-05-27 verified attempt]: redirect 대상 페이지에서 NOT FOUND.
|
||||
|
||||
> [§Application events — Crash scenario] "Even if both calls succeed individually, a process crash between them leaves the system in an inconsistent state."
|
||||
> — [2026-05-27 verified attempt]: redirect 대상 페이지에서 NOT FOUND.
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 본 raw 의 모든 quote 는 2026-05-22 user 수집본이며 2026-05-27 WebFetch 차단으로 verbatim 재확인 보류 → 전체 `needs-confirmation`.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DUAL-WRITE-C1 | 서비스는 종종 DB 갱신과 메시지 발행을 atomic 하게 해야 하지만, DB 와 message broker 에 걸친 distributed transaction 사용은 viable 하지 않다 | [§Application events — Problem] "A service often needs to atomically update the database and publish a message/event. It is not viable to use a distributed transaction that spans the database and the message broker." | `needs-confirmation` | DB + broker 를 동시에 다루는 모든 서비스 | "어떤 환경에서도 절대 불가" 는 아님 — 본 인용은 일반적 viability 부정, 일부 broker 의 XA 지원은 별도 검증 |
|
||||
| DUAL-WRITE-C2 | 2PC 없이 DB 에 쓰고 broker 에 발행 (또는 그 반대) 하는 경우, 어느 한쪽이 실패하면 inconsistency 가 발생할 수 있다 | [§Application events — Failure mode] "Without 2PC, writing to the database and then publishing to a broker — or vice versa — can result in inconsistency if either step fails." | `needs-confirmation` | 2PC 없이 DB + broker 를 순차 호출하는 모든 패턴 | inconsistency 의 정확한 형태 (lost event vs phantom event) 분류는 본 인용에 포함되지 않음 |
|
||||
| DUAL-WRITE-C3 | 두 호출이 개별적으로 성공하더라도, 그 사이에 프로세스 크래시가 발생하면 시스템은 inconsistent state 가 된다 | [§Application events — Crash scenario] "Even if both calls succeed individually, a process crash between them leaves the system in an inconsistent state." | `needs-confirmation` | DB commit 과 broker publish 사이의 임의 지점에서 프로세스 종료 가능한 모든 환경 | crash recovery 메커니즘 (retry, compensation) 으로 이를 해결 가능한지 본 인용은 침묵 — outbox 가 그 해결책임은 별도 인용 (transactional outbox 페이지) |
|
||||
|
||||
### Strength 정책
|
||||
|
||||
본 문서의 모든 claim 은 `needs-confirmation`. 이유: 2026-05-27 재검증 시점에 WebFetch 가 차단되어 user 수집본 (2026-05-22) 의 verbatim 일치 여부를 공식 페이지 대조로 확인 못함. wiki 승급 전 재확인 필수.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것** (재확인 시):
|
||||
- `DUAL-WRITE-C1`: 분산 트랜잭션 (DB + broker 2PC) 의 viability 부정 — outbox 도입의 1차 동기
|
||||
- `DUAL-WRITE-C2`: 순차 호출 시 부분 실패 = inconsistency
|
||||
- `DUAL-WRITE-C3`: 두 호출 사이의 crash 도 inconsistency 원인 (성공 호출만으로는 안전 불가)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- 모든 broker (Kafka, RabbitMQ, SQS, ...) 가 2PC 를 지원하지 않는다는 절대 명제 (Kafka 는 transaction API 가 있지만 외부 DB 와의 2PC 는 별도 논의)
|
||||
- lost event 와 phantom event 의 명시적 분류 (메모 영역에서 해석 필요)
|
||||
- dual-write 가 모든 시나리오에서 항상 잘못된 선택이라는 일반화 (low-criticality 도메인에서 monitoring 으로 운영 가능한 케이스도 존재 — 본 인용은 silent divergence 위험만 지적)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 의 도메인 이벤트가 silent loss 를 허용할 수 없는 critical 도메인인지 확인 (그렇다면 outbox 채택이 정당화)
|
||||
- dual-write 가 잘못이라는 결론 자체는 microservices.io 1차 source 외에 Confluent / Debezium / Stripe 의 동일 메시지로 corroborate 가능 (다중 source 권장)
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석 — 직접 인용 아님)
|
||||
|
||||
- 적용 시나리오: **권장하지 않음.** 단일 DB + 단일 broker 라도 atomic 보장 불가.
|
||||
- 장점:
|
||||
- 코드가 가장 단순 (`save(); publish();`)
|
||||
- 인프라 추가 없음
|
||||
- 단점 (failure modes — 본 인용의 일반 inconsistency 명제로부터 도출):
|
||||
- **DB commit 성공 + broker publish 실패** → 외부에는 이벤트 안 감, 상태만 변함 (lost event)
|
||||
- **broker publish 성공 + DB commit rollback** → 외부에는 발생하지 않은 이벤트 발행 (phantom event)
|
||||
- **DB commit 성공 + 프로세스 크래시 → publish 안 됨** (lost event, `DUAL-WRITE-C3` 의 직접 결과)
|
||||
- 분산 트랜잭션 (XA/2PC) 은 broker 측 지원 미흡/성능 문제로 사실상 불가 (`DUAL-WRITE-C1`)
|
||||
- ca-tmpl (SKIP LOCKED polling) 과의 차이:
|
||||
- outbox 는 "이벤트도 DB 에 같이 쓴다" 로 atomic 문제를 회피
|
||||
- dual-write 는 이 atomic 문제를 그대로 노출 → outbox 도입의 직접적 동기
|
||||
- 운영 복잡도: 코드는 낮음, 장애 디버깅 비용은 매우 높음 (silent data divergence).
|
||||
- exactly-once / at-least-once 보장 수준: **보장 없음**. lost / phantom 둘 다 가능.
|
||||
- 외부 의존성 추가 여부: 없음 (그러나 그 대가가 신뢰성 손실).
|
||||
- 결론: ca-tmpl 이 dual-write 를 피하고 outbox 를 택한 것은 정설. 이 문서는 "대안"이 아니라 "왜 outbox 를 골랐는가의 negative reference".
|
||||
- 대안 그룹 (Topic 3 — Outbox Pattern, 대안 6종): SKIP LOCKED polling / Debezium CDC / Kafka Connect SMT / **Dual-write [금지]** / Event sourcing / Spring @TransactionalEventListener
|
||||
- 본 source 의 위치: negative reference — Dual-write 금지
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/outbox-skip-locked-microservices-io]] (baseline: SKIP LOCKED polling — dual-write 의 해결책)
|
||||
- [[raw/official-docs/outbox-debezium-official-docs]] (대안 1: CDC)
|
||||
- [[raw/official-docs/skip-locked-postgres-docs]] (메커니즘)
|
||||
- 같은 주제 company-tech-blog:
|
||||
- [[raw/company-tech-blogs/outbox-woowahan-techblog-pattern]]
|
||||
- 인용하는 branch / project:
|
||||
- [[raw/branch-notes/feature-domain-event-outbox-contract]]
|
||||
- [[raw/branch-notes/feature-background-job-async-contract]]
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Devcontainer spec — Spring Boot / Java 적용
|
||||
source_type: official-doc
|
||||
url: https://containers.dev/implementors/spec/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [developer-experience, devcontainer, vscode, codespaces, spring-boot, ca-skeleton, official-doc]
|
||||
related_projects: [ca-skeleton]
|
||||
related_branches: [feature-developer-experience-contract, feature-build-release-supply-chain-contract, feature-skeleton-package-blueprint-contract]
|
||||
created: 2026-05-25
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Devcontainer spec — Spring Boot / Java 적용
|
||||
|
||||
> Layer: `raw/official-docs/` — Devcontainer 공식 spec + 관련 공식 문서 발췌. ca-tmpl 의 "bootstrap = `./gradlew bootstrap` 5단계 + OS 매트릭스" 결정의 대안 (devcontainer default 채택) 을 평가하기 위한 1차 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-developer-experience-contract]] | bootstrap 5단계 + OS 매트릭스 채택 — devcontainer 가 보장하는 것 (tool/runtime stack) 과 보장하지 않는 것 (단일 진입점 / smoke test / Flyway 순서) 의 분리 근거 |
|
||||
| [[raw/branch-notes/feature-build-release-supply-chain-contract]] | JDK version pin 이 devcontainer image 와 supply chain reproducibility 사이에서 공유되는 위치 명시 |
|
||||
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | Spring Initializr archetype 위에 ca-tmpl operational contract 가 얹히는 layering 위치 |
|
||||
|
||||
추가 foundational 인용:
|
||||
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] — DX 계약에서 "IDE별 개인 설정 out-of-scope" 가 devcontainer 의 보장 범위와 별도임을 명시하기 위한 근거
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
`feature-developer-experience-contract` 결정 "bootstrap = `./gradlew bootstrap` 5단계 + OS 매트릭스 (Linux/macOS/WSL2)" 의 대안 평가를 위해 devcontainer 정의가 무엇을 보장하고 무엇을 보장하지 않는지 raw 로 확보. ca-tmpl 이 devcontainer 를 default 로 두지 않은 이유 ("IDE별 개인 설정" out-of-scope) 에 대한 근거 자료.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL (primary): https://containers.dev/implementors/spec/
|
||||
- 보조 URL:
|
||||
- devcontainers/images (Java) — https://github.com/devcontainers/images/tree/main/src/java
|
||||
- VS Code Dev Containers extension — https://code.visualstudio.com/docs/devcontainers/containers
|
||||
- GitHub Codespaces overview — https://docs.github.com/en/codespaces/overview
|
||||
- Spring Initializr — https://start.spring.io / docs https://docs.spring.io/initializr/docs/current/reference/html/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Microsoft / GitHub / containers.dev WG, Spring team
|
||||
- 발행일: 공식 문서 (지속 갱신)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Development Container Specification (containers.dev)] "A development container is a container in which a user can develop an application."
|
||||
|
||||
> [§Development Container Specification (containers.dev)] "The purpose of the Development Container Specification is to provide a way to enrich containers with the content and metadata necessary to enable development inside them."
|
||||
|
||||
> [§`devcontainer.json` (containers.dev)] "Products using it should expect to find a devcontainer.json file in one or more of the following locations: `.devcontainer/devcontainer.json`, `.devcontainer.json`, or `.devcontainer/<folder>/devcontainer.json`."
|
||||
|
||||
> [§Metadata (containers.dev)] "A development container defines an environment in which you develop your application before you are ready to deploy."
|
||||
|
||||
> [§Orchestration options (containers.dev)] "This specification leaves space for further development and implementation of other orchestrator mechanisms and file formats."
|
||||
|
||||
> [§Developing inside a Container — VS Code docs] "The Visual Studio Code Dev Containers extension lets you use a container as a full-featured development environment."
|
||||
|
||||
> [§Create a devcontainer.json file — VS Code docs] "A devcontainer.json file in your project tells VS Code how to access (or create) a development container with a well-defined tool and runtime stack."
|
||||
|
||||
> [§Create a devcontainer.json file — VS Code docs] "The dev container configuration is either located under `.devcontainer/devcontainer.json` or stored as a `.devcontainer.json` file (note the dot-prefix) in the root of your project."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DX-DC-C1 | Development container 는 user 가 application 을 개발할 수 있는 container | [§Development Container Specification] "A development container is a container in which a user can develop an application." | `official-standard` | dev container 일반 정의 | application runtime container (production) 와 같다는 뜻 아님 — development 전용 |
|
||||
| DX-DC-C2 | Development Container Specification 의 목적은 development 를 가능케 하는 content / metadata 로 container 를 enrich 하는 방법 제공 | [§Development Container Specification] "The purpose of the Development Container Specification is to provide a way to enrich containers with the content and metadata necessary to enable development inside them." | `official-standard` | dev container 채택 환경 | spec 이 build/release pipeline 까지 cover 한다는 뜻 아님 — development phase 한정 |
|
||||
| DX-DC-C3 | devcontainer.json 파일은 다음 위치 중 하나에서 발견됨: `.devcontainer/devcontainer.json`, `.devcontainer.json`, `.devcontainer/<folder>/devcontainer.json` | [§`devcontainer.json`] "Products using it should expect to find a devcontainer.json file in one or more of the following locations: `.devcontainer/devcontainer.json`, `.devcontainer.json`, or `.devcontainer/<folder>/devcontainer.json`." | `official-standard` | devcontainer 채택 프로젝트 layout | 위 위치들이 동시에 존재할 때의 priority 는 본 인용 범위 밖 |
|
||||
| DX-DC-C4 | VS Code Dev Containers extension 은 container 를 full-featured development environment 로 사용 가능케 함 | [§Developing inside a Container — VS Code docs] "The Visual Studio Code Dev Containers extension lets you use a container as a full-featured development environment." | `official-vendor-doc` | VS Code 사용 환경 | IntelliJ / Eclipse 등 다른 IDE 에서도 같은 보장이 있다는 뜻 아님 — VS Code 한정 |
|
||||
| DX-DC-C5 | devcontainer.json 은 VS Code 에 well-defined tool / runtime stack 을 가진 development container 에 접근/생성하는 방법을 알려줌 | [§Create a devcontainer.json file — VS Code docs] "A devcontainer.json file in your project tells VS Code how to access (or create) a development container with a well-defined tool and runtime stack." | `official-vendor-doc` | VS Code Dev Containers 통합 | "tool / runtime stack" 이 build 시스템 / DB migration / smoke test 까지 자동 정의된다는 뜻 아님 — image 와 metadata 만 |
|
||||
| DX-DC-C6 | spec 은 추가 orchestrator mechanism / file format 의 development / implementation 여지를 남겨둠 (현재 spec 이 모든 orchestrator 를 cover 하지 않음) | [§Orchestration options] "This specification leaves space for further development and implementation of other orchestrator mechanisms and file formats." | `official-standard` | spec 의 현재 scope 한계 | 현재 spec 이 충분히 production-ready 가 아니라는 뜻 아님 — extensibility 명시 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `DX-DC-C1` ~ `C3`: devcontainer spec 의 정의, 목적, 파일 위치
|
||||
- `DX-DC-C4` ~ `C5`: VS Code 통합 방식과 "tool / runtime stack" 정의 범위
|
||||
- `DX-DC-C6`: spec 의 orchestration extensibility 의도
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- devcontainer 가 ca-tmpl 의 "bootstrap 5단계" 를 대체할 수 있다는 주장 (spec 은 image 정의 + metadata 만 — 단일 진입점 / smoke test / Flyway migrate 순서는 별도)
|
||||
- GitHub Codespaces 와 VS Code Dev Containers extension 이 같은 devcontainer.json 으로 100% 호환된다는 사실 (Codespaces 공식 페이지 별도 fetch 필요 — 본 fetch 에는 Codespaces 인용 미포함)
|
||||
- Spring Initializr 가 devcontainer 또는 CI 설정을 생성하지 않는다는 사실 (Spring Initializr 공식 fetch 가 본 차수에 없음 — `needs-confirmation`)
|
||||
- Java/JDK image (devcontainers/images Java) 가 LTS 버전을 default 로 보장한다는 사실 (별도 fetch 필요)
|
||||
- IntelliJ 사용자에게 devcontainer 가 동등한 통합 경험을 준다는 사실
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 의 `./gradlew bootstrap` 5단계가 devcontainer 내부에서도 별도로 정의되어야 하는 항목 목록
|
||||
- devcontainer features (e.g., `ghcr.io/devcontainers/features/java`) 가 ca-tmpl 의 JDK pin 정책과 충돌하지 않는지
|
||||
- Codespaces 사용 시 devcontainer.json + ca-tmpl bootstrap script 가 양립하는지의 실제 시연
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- Devcontainer 가 해결하는 것 (`DX-DC-C1`, `DX-DC-C2`, `DX-DC-C5` 기반): tool version (JDK/Gradle), OS-level deps (libxml, locale), VSCode/Codespaces 통일.
|
||||
- Devcontainer 가 *해결하지 않는* 것 (spec scope 한계, `DX-DC-C2` 의 "development phase 한정"): 첫 `./gradlew bootstrap` 단일 진입점, smoke test 정의, Flyway migrate 순서. 즉 ca-tmpl 의 5단계 bootstrap 은 devcontainer 안에서도 별도로 정의되어야 함.
|
||||
- 트레이드오프: devcontainer 를 강제하면 Codespaces/VSCode 사용자에게 마찰이 줄지만, IntelliJ + 로컬 JDK 사용자에게는 중복 환경이 됨 (`DX-DC-C4` 는 VS Code 한정). ca-tmpl 의 "IDE별 개인 설정 out-of-scope" 는 이 트레이드오프를 회피하는 명시적 선택.
|
||||
- Spring Initializr 는 archetype 시작점일 뿐, ca-tmpl 이 정의하는 operational contract (CI gate, supply chain, DX) 와는 분리됨 (단 본 fetch 에는 Spring Initializr 인용 미확보). raw 로 명시해 wiki/concepts 변환 시 "Initializr 가 충분하다" 는 오해 차단.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/dx-mise-asdf-tool-versioning]] — JDK / tool version 핀 (devcontainer image 와 분리된 layer)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-developer-experience-contract]] — bootstrap 5단계 + OS 매트릭스 + JDK 핀
|
||||
- [[raw/branch-notes/feature-build-release-supply-chain-contract]] — JDK version pin via reproducibility
|
||||
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] — Spring Initializr archetype layering
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] (DX 계약 섹션)
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Tool versioning — mise / asdf / SDKMAN / `.tool-versions`
|
||||
source_type: official-doc
|
||||
url: https://mise.jdx.dev/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [developer-experience, tool-versioning, mise, asdf, sdkman, jdk, ca-skeleton, official-doc]
|
||||
related_projects: [ca-skeleton]
|
||||
related_branches: [feature-developer-experience-contract, feature-build-release-supply-chain-contract, feature-ci-quality-gates-contract]
|
||||
created: 2026-05-25
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Tool versioning — mise / asdf / SDKMAN / `.tool-versions`
|
||||
|
||||
> Layer: `raw/official-docs/` — mise / asdf / SDKMAN / Adoptium Temurin 공식 페이지 발췌. ca-tmpl 의 "JDK = Temurin 21 LTS. gradle-wrapper 8.x. `.tool-versions` 또는 `.sdkmanrc` 로 핀." 결정의 도구 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-developer-experience-contract]] | JDK Temurin 21 LTS + `.tool-versions`/`.sdkmanrc` 핀 결정 — 도구를 강제하지 않고 파일 포맷을 강제하는 전략 근거 |
|
||||
| [[raw/branch-notes/feature-build-release-supply-chain-contract]] | JDK version pin via `.tool-versions` 또는 `gradle/wrapper/` 가 reproducibility 조건임을 근거 |
|
||||
| [[raw/branch-notes/feature-ci-quality-gates-contract]] | CI runner JDK 버전이 `.tool-versions` 와 일치해야 reproducible build 성립한다는 사실 근거 |
|
||||
|
||||
추가 foundational 인용:
|
||||
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] — DX 계약의 "tool version 단일화" 항목이 mise/asdf/SDKMAN 어느 것을 강제하지 않고 `.tool-versions` 포맷 자체를 강제하는 근거
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
`feature-developer-experience-contract` 결정 "JDK = Temurin 21 LTS. gradle-wrapper 8.x. `.tool-versions` 또는 `.sdkmanrc` 로 핀." 의 도구 근거. mise/asdf/SDKMAN 중 어떤 것을 default 로 권장할지, `.tool-versions` 포맷이 어디 spec 에 정의되어 있는지 raw 로 확보.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL (primary): https://mise.jdx.dev/
|
||||
- 보조 URL:
|
||||
- asdf-vm 공식 — https://asdf-vm.com/
|
||||
- asdf `.tool-versions` 형식 — https://asdf-vm.com/manage/configuration.html
|
||||
- SDKMAN! `.sdkmanrc` — https://sdkman.io/usage#env
|
||||
- Adoptium Temurin 21 LTS — https://adoptium.net/temurin/releases/?version=21
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: jdxcode (mise), asdf-vm community, SDKMAN! community, Eclipse Adoptium
|
||||
- 발행일: 공식 문서 (지속 갱신)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
### mise (https://mise.jdx.dev/)
|
||||
|
||||
> [§The Idea] "mise does the same for your dev env. It installs and activates the right tools, loads the right env vars, and wires up the right tasks for the commands you run."
|
||||
|
||||
> [§The Menu] "One CLI for the whole project setup."
|
||||
|
||||
> [§Dev Tools] "Install project tools, pin versions, and switch automatically as you move between directories."
|
||||
|
||||
> [§pantry · 900+ tools, 1 toml file] "900+ tools, 1 toml file"
|
||||
|
||||
### asdf-vm (https://asdf-vm.com/)
|
||||
|
||||
> [§asdfThe Multiple Runtime Version Manager] "Manage all your runtime versions with one tool!"
|
||||
|
||||
> [§One Config File] ".tool-versions to manage all your tools, runtimes and their versions in a single, sharable place."
|
||||
|
||||
> [§One Tool] "Manage each of your project runtimes with a single CLI tool and command interface."
|
||||
|
||||
> [§Plugins] "Large ecosystem of existing runtimes & tools. Simple API to add support for new tools as you need!"
|
||||
|
||||
### Adoptium Temurin 21
|
||||
|
||||
> (Adoptium releases 페이지 fetch 에서는 LTS 정책의 verbatim 정의 인용 미확보. JDK 21 의 "LTS" 표기만 navigation 에 존재. LTS 의 정확한 정의는 별도 Adoptium support 페이지 fetch 필요 — 본 차수에서는 `needs-confirmation`.)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| DX-TV-C1 | mise 는 dev env 의 tool 을 설치/활성화하고, env vars 를 로드하며, command 에 맞는 task 를 wiring 함 | [§The Idea (mise)] "mise does the same for your dev env. It installs and activates the right tools, loads the right env vars, and wires up the right tasks for the commands you run." | `official-vendor-doc` | mise 채택 환경 | mise 가 asdf 의 superset 이라는 뜻 아님 — 본 페이지에는 asdf 와의 호환성 인용 미확보 |
|
||||
| DX-TV-C2 | mise 는 프로젝트 도구를 install / pin / directory 이동 시 auto-switch 함 | [§Dev Tools (mise)] "Install project tools, pin versions, and switch automatically as you move between directories." | `official-vendor-doc` | per-project 도구 관리 | pin 방식 (TOML vs `.tool-versions`) 의 정확한 spec 은 본 인용 범위 밖 |
|
||||
| DX-TV-C3 | mise pantry 는 900+ tools 를 1개 TOML 파일로 관리 | [§pantry (mise)] "900+ tools, 1 toml file" | `official-vendor-doc` | mise TOML 사용 환경 | 모든 tool 이 LTS / stable 보장된다는 뜻 아님 |
|
||||
| DX-TV-C4 | asdf 는 multiple runtime version manager — 모든 runtime version 을 하나의 도구로 관리 | [§asdfThe Multiple Runtime Version Manager (asdf)] "Manage all your runtime versions with one tool!" | `official-vendor-doc` | asdf 채택 환경 | asdf 자체 성능 / 속도 보장 아님 |
|
||||
| DX-TV-C5 | `.tool-versions` 파일은 모든 tool, runtime, 그 버전을 단일 공유 위치에서 관리 (asdf 1차 정의) | [§One Config File (asdf)] ".tool-versions to manage all your tools, runtimes and their versions in a single, sharable place." | `official-vendor-doc` | asdf / asdf-호환 도구 사용 환경 | mise 가 `.tool-versions` 를 100% 호환한다는 사실 (본 fetch 에서는 mise 페이지에 명시 인용 미확보 — 별도 mise 문서 페이지 확인 필요) |
|
||||
| DX-TV-C6 | asdf 는 plugin model 로 작동하며, 기존 runtime/tool 생태계가 크고, 새 tool 지원을 위한 simple API 제공 | [§Plugins (asdf)] "Large ecosystem of existing runtimes & tools. Simple API to add support for new tools as you need!" | `official-vendor-doc` | asdf plugin 사용 환경 | plugin 의 보안 검증 / 신뢰성 보장 아님 — community 책임 |
|
||||
| DX-TV-C7 | (SDKMAN `.sdkmanrc` 인용은 본 차수 fetch 에서 미확보 — 별도 fetch 필요) | (해당 인용 없음 — 본 차수 fetch 누락) | `needs-confirmation` | (별도 fetch 필요) | `.sdkmanrc` 의 정확한 포맷 / 호환성 인용 불가 |
|
||||
| DX-TV-C8 | (Adoptium Temurin 21 의 LTS 지원 기간 verbatim 인용은 본 차수 fetch 에서 미확보 — releases 페이지에 navigation "JDK 21 - LTS" 만 존재. 별도 Adoptium support 페이지 fetch 필요) | (해당 인용 없음 — 본 차수 fetch 누락) | `needs-confirmation` | (별도 fetch 필요) | "2028-09 까지 지원" 같은 구체 기간은 인용으로 보장 안 됨 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `DX-TV-C1` ~ `C3`: mise 의 기능 (tool install / pin / auto-switch / TOML 관리)
|
||||
- `DX-TV-C4` ~ `C6`: asdf 의 정의, `.tool-versions` 의 1차 spec 위치 (asdf-vm), plugin model
|
||||
- **이 자료가 증명하지 않는 것** (`needs-confirmation`):
|
||||
- mise 가 asdf `.tool-versions` 를 100% 호환한다는 사실 (mise 1차 fetch 에는 명시 인용 미확보 — `DX-TV-C5` 의 "Does not prove" 컬럼 참조)
|
||||
- SDKMAN `.sdkmanrc` 의 정확한 포맷, `.tool-versions` 와의 호환성 (`DX-TV-C7` — 본 차수 fetch 미수행)
|
||||
- Adoptium Temurin 21 의 정확한 LTS 지원 기간 (`DX-TV-C8` — 본 차수 fetch 미수행)
|
||||
- mise/asdf/SDKMAN 중 어느 것이 ca-tmpl 의 default 로 적합한지 (벤더 비교는 본 인용 범위 밖)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- `.sdkmanrc` 와 `.tool-versions` 동시 존재 시 drift 위험의 실제 시연 + ca-tmpl 의 "단일 source 권장" 정책 확정
|
||||
- Adoptium Temurin 21 LTS 의 정확한 EOL 일자 (별도 페이지 fetch)
|
||||
- Gradle 8.x toolchain auto-provisioning 이 `.tool-versions` 없이도 JDK 를 받아오는지의 실제 동작 (별도 Gradle 공식 페이지 fetch 필요)
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- `.tool-versions` 는 asdf 가 도입한 사실상 표준 포맷 (`DX-TV-C5`). mise 가 호환한다는 일반 통념은 본 1차 fetch 에서는 verbatim 보장 안 됨 → 두 도구 모두 같은 파일을 읽는다는 주장은 별도 fetch 후 확정.
|
||||
- SDKMAN 의 `.sdkmanrc` 는 별도 포맷이므로 *둘 다* 두면 drift 가능. ca-tmpl 이 "또는" 으로 표현한 것은 drift 위험을 내포 → wiki 변환 시 단일 source 권장으로 좁힐 필요.
|
||||
- Temurin 21 을 default LTS 로 둔 근거 (`DX-TV-C8` 미확정): Adoptium 의 LTS 정책 (인용 미확보). 다른 vendor (Corretto, Zulu, GraalVM CE) 도 LTS 제공하지만 default 를 단일화하는 편이 reproducibility 에 유리 (해석).
|
||||
- gradle-wrapper 8.x: Gradle 8 LTS 는 toolchain auto-provisioning 을 지원한다는 일반 통념 → `.tool-versions` 없이도 Gradle 이 JDK 를 받아 올 수 있음 (별도 인용 필요). ca-tmpl 이 `.tool-versions` 핀을 강제하는 것은 *IDE / CLI / Gradle outside* 사용자까지 통일하려는 의도.
|
||||
- 트레이드오프: mise 는 빠르고 활발하지만 신규, asdf 는 안정적이지만 plugin script 기반으로 느림 (성능 인용 미확보 — 해석). ca-tmpl 이 도구를 강제하지 않고 *파일 포맷* 을 강제하는 전략은 합리적.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/dx-devcontainer-spring-boot]] — devcontainer image 와 tool 버전 핀의 layer 분리
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-developer-experience-contract]] — JDK Temurin 21 LTS + `.tool-versions`/`.sdkmanrc` 핀
|
||||
- [[raw/branch-notes/feature-build-release-supply-chain-contract]] — reproducibility 의 JDK pin 조건
|
||||
- [[raw/branch-notes/feature-ci-quality-gates-contract]] — CI runner JDK = `.tool-versions` 일치 요건
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] (DX 계약 섹션)
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Testcontainers Java — best practice와 reuse / Singleton 패턴
|
||||
source_type: official-doc
|
||||
url: https://java.testcontainers.org/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: medium
|
||||
tags: [developer-experience, testcontainers, integration-test, spring-boot, ca-skeleton, official-doc]
|
||||
related_branches: [feature-developer-experience-contract, feature-test-taxonomy-fixture-contract, feature-ci-quality-gates-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Testcontainers Java — best practice와 reuse / Singleton 패턴
|
||||
|
||||
> Layer: `raw/official-docs/` — Testcontainers for Java 공식 페이지 + reuse / Spring 통합 보조 페이지 verbatim 발췌.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-developer-experience-contract]] | bootstrap 5단계 중 "(2) `docker compose up -d` (local dependencies via Testcontainers config 또는 별도 compose file)" 의 도구 근거. Spring Boot 3.1+ `@ServiceConnection` 도입 이후 Testcontainers 가 ca-tmpl 의 default integration test backend 가 될 수 있는지 평가 |
|
||||
| [[raw/branch-notes/feature-test-taxonomy-fixture-contract]] | integration test taxonomy 가 Testcontainers 를 default backend 로 가질 수 있음 |
|
||||
| [[raw/branch-notes/feature-ci-quality-gates-contract]] | integration test (default profile) gate — reuse opt-in 의 CI 정책 (off) 결정 근거 |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
`feature-developer-experience-contract` bootstrap 5단계 중 "(2) `docker compose up -d` (local dependencies via Testcontainers config 또는 별도 compose file)" 의 도구 근거. Spring Boot 3.1+ 의 `@ServiceConnection` 도입 이후 Testcontainers 가 ca-tmpl 의 default integration test backend 가 될 수 있는지 raw 로 확보.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL:
|
||||
- Testcontainers for Java — https://java.testcontainers.org/
|
||||
- Testcontainers reuse — https://java.testcontainers.org/features/reuse/
|
||||
- Testcontainers Spring Boot 통합 — https://java.testcontainers.org/modules/spring/ (및 Spring Boot 3.1+ `@ServiceConnection`)
|
||||
- Spring Boot Testcontainers support — https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.testing.testcontainers
|
||||
- 저자 / 조직: AtomicJar (Docker 산하), Testcontainers community, Spring team
|
||||
- 발행일: 공식 문서 (지속 갱신)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
**Testcontainers for Java 메인 페이지 — 2026-05-27 fetch 로 확인된 인용**:
|
||||
|
||||
> [§Core Definition (java.testcontainers.org)] "*Testcontainers for Java* is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container."
|
||||
|
||||
> [§Use Cases] "Testcontainers make the following kinds of tests easier: Data access layer integration tests, Application integration tests, UI/Acceptance tests"
|
||||
|
||||
> [§Functionality] "use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines"
|
||||
|
||||
> [§Key Benefit] "safe in the knowledge that your tests will always start with a known DB state"
|
||||
|
||||
**보조 인용 (별도 페이지 — 본 fetch 로는 verbatim 미확인, 원래 raw 작성 시점 수집본 보존)**:
|
||||
|
||||
> [Testcontainers reuse docs — needs-confirmation] "Container reuse is an opt-in feature, controlled by the `testcontainers.reuse.enable` property. When enabled, containers with the same configuration hash are reused across test runs, significantly reducing test startup time. … Reuse must not be enabled in CI." *(2026-05-27 fetch 에서 verbatim 미확인 — https://java.testcontainers.org/features/reuse/ 재fetch 필요. Reuse 가 opt-in 인 점은 사실로 알려져 있으나 정확한 property 명 / "must not be enabled in CI" 표현 검증 필요)*
|
||||
|
||||
> [Spring Boot reference docs — needs-confirmation] "Spring Boot's `@ServiceConnection` annotation, introduced in Spring Boot 3.1, automatically configures the connection details (JDBC URL, credentials, host, port) of a Testcontainers-managed service to the Spring `ApplicationContext`. This removes most boilerplate configuration." *(`@ServiceConnection` 도입 사실은 Spring Boot 3.1 release notes 로 확인되나, 본 인용의 정확한 verbatim 은 Spring Boot reference docs 재fetch 필요)*
|
||||
|
||||
> [Testcontainers Java docs — paraphrased, needs-confirmation] "The recommended pattern for sharing a container across multiple test classes is the singleton container pattern: declare the container as a `static` field and start it manually. JUnit's `@Testcontainers` lifecycle should not be combined with the singleton pattern." *(원래 raw 자체에 "요약" 으로 표시됨 — verbatim 아님. 정확한 공식 표현 재확인 필요)*
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| TC-CORE-C1 | Testcontainers for Java 는 JUnit test 를 지원하는 Java library 로서, 공통 DB / Selenium 브라우저 / Docker container 에서 실행 가능한 무엇이든 lightweight + throwaway instance 를 제공 | [§Core Definition (java.testcontainers.org)] "*Testcontainers for Java* is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container." | `official-vendor-doc` | JUnit + Docker 사용 가능한 환경 | "JUnit 외 다른 test framework (TestNG, Spock) 도 1급 지원" 은 본 인용 범위 밖 |
|
||||
| TC-CORE-C2 | Testcontainers 가 쉽게 만드는 test 카테고리: data access layer integration tests / application integration tests / UI/Acceptance tests | [§Use Cases] "Testcontainers make the following kinds of tests easier: Data access layer integration tests, Application integration tests, UI/Acceptance tests" | `official-vendor-doc` | 위 3가지 test 카테고리 | unit test 의 mock 대체로 쓰는 것은 본 인용으로 권장되지 않음 (use case 목록에 없음) |
|
||||
| TC-CORE-C3 | containerized DB instance (MySQL / PostgreSQL / Oracle) 를 사용해 dev machine 의 복잡한 설정 없이 data access layer code 의 완전한 호환성을 test 가능 | [§Functionality] "use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines" | `official-vendor-doc` | MySQL / PostgreSQL / Oracle 사용 시 | 다른 DB (SQL Server, MongoDB 등) 의 지원 수준은 본 인용 범위 밖 (별도 module 페이지 참조) |
|
||||
| TC-CORE-C4 | test 가 항상 알려진 DB state 로 시작한다는 보장 | [§Key Benefit] "safe in the knowledge that your tests will always start with a known DB state" | `official-vendor-doc` | container per-test or per-class lifecycle 사용 시 | "container reuse 가 활성화된 상태에서도 동일 보장" 은 본 인용으로 직접 증명 안 됨 — reuse 는 state 가 누적될 수 있음 |
|
||||
| TC-REUSE-C1 | Container reuse 는 opt-in feature 로, `testcontainers.reuse.enable` property 로 제어. 활성화 시 동일 configuration hash 의 container 가 test run 간 재사용되어 startup time 을 크게 단축. CI 에서는 활성화 금지 | [Testcontainers reuse docs] "Container reuse is an opt-in feature, controlled by the `testcontainers.reuse.enable` property. When enabled, containers with the same configuration hash are reused across test runs, significantly reducing test startup time. … Reuse must not be enabled in CI." | `needs-confirmation` *(원 raw 수집본; 2026-05-27 fetch 에서 verbatim 미확인. 사실 자체는 알려진 동작이나 정확한 property 명과 "must not be enabled in CI" 표현 재검증 필요)* | reuse 활성화 결정 | 활성화 시 state 누적의 정확한 영향 (test isolation 깨짐 정도) 은 본 인용 범위 밖 |
|
||||
| TC-SPRING-C1 | Spring Boot 3.1 의 `@ServiceConnection` annotation 은 Testcontainers-managed service 의 connection detail (JDBC URL / credentials / host / port) 을 Spring `ApplicationContext` 에 자동 구성. 대부분의 boilerplate 제거 | [Spring Boot reference docs] "Spring Boot's `@ServiceConnection` annotation, introduced in Spring Boot 3.1, automatically configures the connection details (JDBC URL, credentials, host, port) of a Testcontainers-managed service to the Spring `ApplicationContext`. This removes most boilerplate configuration." | `needs-confirmation` *(Spring Boot 3.1 release notes 로 도입 사실 확인. 본 인용의 정확한 verbatim 은 Spring Boot reference 재fetch 필요)* | Spring Boot 3.1+ 사용 시 | 지원되는 container module 범위 (모든 module vs 일부 module 만) 는 본 인용 범위 밖 |
|
||||
| TC-SINGLETON-C1 | 여러 test class 간 container 공유의 권장 패턴은 singleton container pattern: container 를 `static` field 로 선언하고 수동 start. JUnit `@Testcontainers` lifecycle 과 결합 금지 | [Testcontainers Java docs — paraphrased] "The recommended pattern for sharing a container across multiple test classes is the singleton container pattern: declare the container as a `static` field and start it manually. JUnit's `@Testcontainers` lifecycle should not be combined with the singleton pattern." | `needs-confirmation` *(원 raw 에서 "요약" 표기됨 — verbatim 아님. 정확한 공식 표현 재확인 필요)* | 여러 test class 가 같은 container 를 공유해야 할 때 | 단일 container 의 state isolation 전략 (truncate vs drop/recreate vs DI 격리) 은 본 인용 범위 밖 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `TC-CORE-C1~C4`: Testcontainers 의 정의 + 권장 use case (integration test 3종) + DB compatibility 보장 + known DB state 보장
|
||||
- **이 자료가 증명하지 않는 것 (verbatim 미확인)**:
|
||||
- reuse 의 정확한 property 명 / "CI 에서 금지" 의 공식 표현 (`TC-REUSE-C1` 은 `needs-confirmation`)
|
||||
- `@ServiceConnection` 의 정확한 reference doc 인용 (`TC-SPRING-C1` 은 `needs-confirmation`)
|
||||
- singleton container pattern 의 정확한 공식 표현 (`TC-SINGLETON-C1` 은 `needs-confirmation`)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- reuse property 명 / CI 정책: https://java.testcontainers.org/features/reuse/ 재fetch
|
||||
- `@ServiceConnection` reference: Spring Boot reference docs 재fetch
|
||||
- singleton pattern: Testcontainers Java docs 의 정확한 표현 재fetch
|
||||
- Apple Silicon (arm64) 환경의 image emulation 비용 — 본 raw 인용 범위 밖, 별도 module 페이지 참조 필요
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석 — PRESERVED)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- ca-tmpl bootstrap 5단계의 (2) 를 Testcontainers 로 구현하면 *test* lifecycle 과 *bootstrap* lifecycle 이 서로 다르다는 점 주의:
|
||||
- bootstrap: 사람이 로컬에서 한 번 띄우는 dependency → `docker compose up -d` 가 더 적합.
|
||||
- integration test: JUnit 안에서 격리 → Testcontainers + `@ServiceConnection`.
|
||||
- 즉 branch note 의 "Testcontainers 또는 local dependency 대체 기준" 은 두 경로를 *둘 다* 명시해야 함. 한쪽만 두면 test 와 bootstrap 중 하나가 누락.
|
||||
- reuse 옵션은 CI 에서는 금지 (본 raw 의 `TC-REUSE-C1` 는 `needs-confirmation` — 원 표현 재검증 필요). 로컬 dev 속도 향상용. CI 에서는 매번 fresh container 를 띄워야 contract test 의 isolation 보장.
|
||||
- 함정: Apple Silicon (arm64) 환경에서 일부 image 는 emulation 필요 → bootstrap 시간 증가. OS 매트릭스 "macOS Apple Silicon 우선" 과 충돌 가능, 본 raw 에서만 메모.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-developer-experience-contract]] — bootstrap 5단계 + Testcontainers/local dep 결정
|
||||
- [[raw/branch-notes/feature-test-taxonomy-fixture-contract]] — integration test taxonomy 가 Testcontainers 를 default backend 로 가질 수 있음
|
||||
- [[raw/branch-notes/feature-ci-quality-gates-contract]] — integration test (default profile) gate
|
||||
- 적용 contract:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] (Developer Experience 그룹)
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: "Send Amazon ECS logs to CloudWatch — awslogs log driver"
|
||||
source_type: official-doc
|
||||
url: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html
|
||||
archive_url:
|
||||
related_branches: [feature-log-management-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, observability, aws, stdout-logging, log-routing]
|
||||
created: 2026-06-13
|
||||
---
|
||||
|
||||
# Send Amazon ECS logs to CloudWatch — awslogs log driver
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-log-management-contract]] | D4 — "production logging = stdout JSON default, file logging local/dev only": awslogs 드라이버가 컨테이너 stdout/stderr 를 Docker 를 거쳐 CloudWatch Logs 로 전달하므로, 앱은 로그 파일 직접 전달 책임을 갖지 않는다. 플랫폼(ECS+awslogs)이 스트림을 수집하므로 stdout 출력만으로 운영 로그 수집이 완결된다. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_awslogs.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Amazon Web Services
|
||||
- 발행일: (ongoing — AWS 공식 문서, 지속 갱신)
|
||||
- 마지막 확인일: 2026-06-13
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-log-management-contract` D4 결정("production logging = stdout JSON default")은 "앱이 로그 파일을 직접 관리·전달하지 않는다"는 런타임 가정에 근거한다. 이 자료는 그 가정의 직접 근거: AWS ECS 공식 문서가 awslogs 드라이버가 컨테이너 stdout/stderr 를 Docker 를 거쳐 CloudWatch Logs 로 단순 전달(pass-through)함을 명시하며, 앱 쪽 별도 로그 shipper 가 필요 없음을 확인한다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
|
||||
|
||||
> [§Note — Log source] "The type of information that is logged by the containers in your task depends mostly on their `ENTRYPOINT` command. By default, the logs that are captured show the command output that you typically might see in an interactive terminal if you ran the container locally, which are the `STDOUT` and `STDERR` I/O streams. The `awslogs` log driver simply passes these logs from Docker to CloudWatch Logs."
|
||||
|
||||
> [§Intro] "You can configure the containers in your tasks to send log information to CloudWatch Logs. If you're using Fargate for your tasks, you can view the logs from your containers. If you're using EC2, you can view different logs from your containers in one convenient location, and it prevents your container logs from taking up disk space on your container instances."
|
||||
|
||||
> [§Fargate] "If you're using Fargate for your tasks, you need to add the required `logConfiguration` parameters to your task definition to turn on the `awslogs` log driver."
|
||||
|
||||
> [§EC2] "If you're using EC2 for your tasks and want to turn on the `awslogs` log driver, your Amazon ECS container instances require at least version 1.9.0 of the container agent."
|
||||
|
||||
> [§EC2 — IAM] "Your Amazon ECS container instances also require `logs:CreateLogStream` and `logs:PutLogEvents` permission on the IAM role that you can launch your container instances with."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| LOG-ECS-AWSLOGS-C1 | awslogs 드라이버는 컨테이너의 stdout/stderr 스트림을 Docker 를 통해 CloudWatch Logs 로 그대로 전달(pass-through)한다 — 앱 내부에 별도 로그 shipper 가 필요하지 않다 | [§Note] "The `awslogs` log driver simply passes these logs from Docker to CloudWatch Logs." | `official-vendor-doc` | AWS ECS(Fargate 또는 EC2) + awslogs log driver 구성 | 다른 컨테이너 오케스트레이터(k8s, Nomad)나 다른 log driver(fluentd, splunk) 에서도 동일하게 동작한다는 뜻 아님. AWS-vendor 특화 동작. |
|
||||
| LOG-ECS-AWSLOGS-C2 | 컨테이너 로그 캡처 대상은 기본적으로 ENTRYPOINT 커맨드의 stdout / stderr I/O 스트림이다 | [§Note] "By default, the logs that are captured show the command output that you typically might see in an interactive terminal if you ran the container locally, which are the `STDOUT` and `STDERR` I/O streams." | `official-vendor-doc` | awslogs log driver 가 활성화된 ECS 태스크 컨테이너 | 파일에 쓴 로그나 syslog 가 자동으로 캡처된다는 뜻 아님. stdout/stderr 이외 스트림은 별도 처리 필요. |
|
||||
| LOG-ECS-AWSLOGS-C3 | ECS on EC2 환경에서 awslogs 를 활성화하면 컨테이너 로그가 컨테이너 인스턴스의 디스크 공간을 점유하지 않게 된다 | [§Intro] "it prevents your container logs from taking up disk space on your container instances" | `official-vendor-doc` | EC2 launch type + awslogs driver | Fargate 에서는 로컬 디스크 관리 모델이 다름 (Fargate는 기본적으로 로컬 디스크 노출 없음). |
|
||||
| LOG-ECS-AWSLOGS-C4 | Fargate 에서 awslogs 드라이버를 활성화하려면 태스크 정의에 `logConfiguration` 파라미터를 명시해야 한다 | [§Fargate] "you need to add the required `logConfiguration` parameters to your task definition to turn on the `awslogs` log driver" | `official-vendor-doc` | AWS Fargate launch type | EC2 launch type의 활성화 절차와 다름(EC2는 에이전트 버전 + IAM 정책 추가 필요). |
|
||||
| LOG-ECS-AWSLOGS-C5 | EC2 에서 awslogs 드라이버 사용 시 컨테이너 인스턴스의 IAM role 에 `logs:CreateLogStream` 및 `logs:PutLogEvents` 권한이 필요하다 | [§EC2 — IAM] "Your Amazon ECS container instances also require `logs:CreateLogStream` and `logs:PutLogEvents` permission on the IAM role that you can launch your container instances with." | `official-vendor-doc` | EC2 launch type + awslogs driver | Fargate 의 경우 `ecsTaskExecutionRole` 을 통한 권한 모델이 다름. IAM 권한은 최소 필요 조건이며 충분 조건이 아닐 수 있음(네트워크/VPC endpoint 설정 등 추가 조건 있음). |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `LOG-ECS-AWSLOGS-C1`: AWS ECS + awslogs log driver 조합에서 앱이 stdout 에만 쓰면 CloudWatch Logs 로 수집이 완결됨 — 별도 로그 shipper 불필요.
|
||||
- `LOG-ECS-AWSLOGS-C2`: awslogs 가 캡처하는 기본 대상은 stdout/stderr 이며, 파일 기반 로그는 별도 처리가 필요함.
|
||||
- `LOG-ECS-AWSLOGS-C3`: EC2 launch type 에서 awslogs 는 디스크 사용 방지 효과.
|
||||
- `LOG-ECS-AWSLOGS-C4`: Fargate 에서 awslogs 활성화는 태스크 정의 `logConfiguration` 필수.
|
||||
- `LOG-ECS-AWSLOGS-C5`: EC2에서 awslogs 동작에 필요한 최소 IAM 권한(`logs:CreateLogStream`, `logs:PutLogEvents`).
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- stdout JSON 이 모든 컨테이너 런타임에서 기본 권장 로그 방식이라는 크로스-플랫폼 표준 — 이것은 AWS-vendor 특화 문서이며 Kubernetes, GCP Cloud Run, Azure Container Apps 에 동일하게 적용된다는 근거 없음.
|
||||
- 12-factor app 원칙 XI (Logs를 이벤트 스트림으로 다루어라)의 직접 인용 — 12-factor 와 논리적으로 일치하지만 본 문서는 그것을 명시하지 않음.
|
||||
- awslogs 가 JSON 형식을 강제하거나 권장한다는 내용 — 형식(JSON vs plain text)은 앱 책임이며 awslogs 는 형식에 무관하게 전달함.
|
||||
- CloudWatch Logs 에서의 파싱/필터/알람 설정 방법 — 별도 CloudWatch 문서 필요.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-tmpl 가 ECS(Fargate or EC2)에 실제 배포될 경우 `logConfiguration` 태스크 정의 설정 검증 필요.
|
||||
- 로컬/dev 환경은 ECS 없이 Docker Compose 로 운영 — `FILE_ENABLED` toggle(D4)이 로컬 환경에서 올바르게 동작하는지는 별도 검증 필요.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- `LOG-ECS-AWSLOGS-C1` 은 feature-log-management-contract D4 의 `UNSUPPORTED_DECISION` 을 `official-vendor-doc` 수준으로 부분 승격시키는 직접 근거다. 다만 "D4 의 근거가 AWS ECS 전용"임을 decision evidence map 에 명시해야 함 — 향후 non-AWS 환경(Kubernetes, on-prem)으로 이관 시 재검토 필요.
|
||||
- awslogs 의 `awslogs-delivery-mode` 파라미터(blocking / non-blocking + max-buffer-size)는 비동기 버퍼 관련 — `feature-log-management-contract` 의 AsyncAppender overflow 정책(D8)과 유사 관심사이나 레이어가 다름(ECS 레벨 vs 앱 내부 레벨). 별도 raw source 추가 검토 가능.
|
||||
- 추가로 봐야 할 동일 출처 페이지: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specify-log-config.html (태스크 정의 logConfiguration 예시)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog:
|
||||
- [[raw/official-docs/log-otel-log-data-model-spec.md]] — OTel log signal 대안 (D7 근거)
|
||||
- [[raw/official-docs/log-ecs-schema-elastic-official.md]] — ECS log schema (D6 근거)
|
||||
- [[raw/official-docs/log-logback-mask-pattern-converter-official.md]] — Logback masking (D1/D2/D10 근거)
|
||||
- 이 자료를 인용한 branch: [[raw/branch-notes/feature-log-management-contract]]
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
title: gradle-errorprone-plugin README — net.ltgt.errorprone 공식 플러그인 레퍼런스
|
||||
source_type: official-doc
|
||||
url: https://github.com/tbroyer/gradle-errorprone-plugin
|
||||
archive_url: https://web.archive.org/web/2026/https://github.com/tbroyer/gradle-errorprone-plugin
|
||||
related_branches: [feature-static-analysis-quality-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, ci-cd, gradle, errorprone, static-analysis]
|
||||
created: 2026-06-15
|
||||
---
|
||||
|
||||
# gradle-errorprone-plugin README — net.ltgt.errorprone 공식 플러그인 레퍼런스
|
||||
|
||||
> Layer: `raw/official-docs/` — tbroyer/gradle-errorprone-plugin GitHub 레포지토리 README의 verbatim 발췌.
|
||||
> `net.ltgt.errorprone` 플러그인의 적용 방법, JDK 16+ forking 동작, `options.errorprone` DSL, 최소 요구 버전의 1차 출처.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-static-analysis-quality-contract]] | D5 — `net.ltgt.errorprone` 플러그인 채택, Java 21에서 javac forking + JVM args 자동 처리 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://github.com/tbroyer/gradle-errorprone-plugin
|
||||
- 아카이브 URL: https://web.archive.org/web/2026/https://github.com/tbroyer/gradle-errorprone-plugin
|
||||
- 저자 / 조직: Thomas Broyer (tbroyer), open source
|
||||
- 발행일: 지속 갱신 (README — 조회 기준 2026-06-15)
|
||||
- 마지막 확인일: 2026-06-15
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-static-analysis-quality-contract` branch 의 D5 결정(ErrorProne 채택 + Java 21 환경에서의 플러그인 동작)을 정당화하기 위해 보관.
|
||||
특히 JDK 16+ 에서 plugin 이 자동으로 forking compiler 를 사용하고 `--add-exports`/`--add-opens` JVM args 를 주입한다는 사실 — 수동 구성 없이도 Java 21 빌드가 가능함의 직접 증거.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
|
||||
|
||||
> [§Requirements] "This plugin requires using at least Gradle 6.8 and JDK 11 (for compilation; it's OK to use JDK 8 to run Gradle as long as compilations use at least JDK 11 through [Gradle Java Toolchains][gradle-toolchains])."
|
||||
|
||||
> [§Requirements — ErrorProne version table]
|
||||
> | Error Prone version | Minimum JDK version |
|
||||
> | :------------------: | :-----------------: |
|
||||
> | Up to 2.31 | 11 |
|
||||
> | From 2.32 up to 2.42 | 17 |
|
||||
> | Starting from 2.43 | 21 |
|
||||
|
||||
> [§Usage — plugin block] `id("net.ltgt.errorprone") version "<plugin version>"`
|
||||
|
||||
> [§Usage — dependency block] `errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")`
|
||||
|
||||
> [§JDK 16+ support] "The plugin will automatically [use a forking compiler][CompileOptions.fork] and pass the necessary [JVM arguments][BaseForkOptions.getJvmArgs] whenever it detects such a JDK is being used for the compilation task and ErrorProne is enabled (unless the Gradle daemon's JVM already was given the appropriate options [through `org.gradle.jvmargs`][org.gradle.jvmargs])."
|
||||
|
||||
> [§Usage — options.errorprone configuration] `options.errorprone.disableWarningsInGeneratedCode = true`
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | 플러그인 최소 요구 사항은 Gradle 6.8 이상, JDK 11 이상 (컴파일 기준) | [§Requirements] "This plugin requires using at least Gradle 6.8 and JDK 11 (for compilation; ...)" | `official-reference` | net.ltgt.errorprone 플러그인 모든 버전 | ca-tmpl 특정 Gradle 버전과의 실제 호환성 |
|
||||
| C2 | ErrorProne 2.43 이상은 JDK 21 이상을 요구한다 | [§Requirements table] "Starting from 2.43 — 21" | `official-reference` | ErrorProne 2.43+ 사용 시 | 특정 ca-tmpl 빌드에서 2.43+ 버전 선택 여부 |
|
||||
| C3 | `net.ltgt.errorprone` plugin id 로 적용하고 `errorprone` configuration 에 `error_prone_core` 의존을 추가한다 | [§Usage] `id("net.ltgt.errorprone")` + `errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")` | `official-reference` | Gradle Kotlin DSL (Groovy DSL도 동등하게 지원) | 플러그인 버전 선택 기준 |
|
||||
| C4 | JDK 16+ 환경에서 plugin 은 자동으로 forking compiler 를 사용하고 필요한 JVM arguments (`--add-opens`/`--add-exports`) 를 주입한다 — 수동 구성 불필요 | [§JDK 16+ support] "The plugin will automatically [use a forking compiler]... and pass the necessary [JVM arguments]... whenever it detects such a JDK is being used" | `official-reference` | ErrorProne 사용 + JDK 16 이상으로 컴파일하는 Gradle 프로젝트 | Gradle daemon JVM 에 이미 `org.gradle.jvmargs` 로 해당 옵션이 설정된 경우 (그 경우 auto-fork 생략) |
|
||||
| C5 | `options.errorprone { disableWarningsInGeneratedCode = true }` 로 생성 코드 경고를 억제할 수 있다 | [§Usage] `options.errorprone.disableWarningsInGeneratedCode = true` | `official-reference` | `@Generated` / `@javax.annotation.Generated` 애노테이션이 붙은 클래스 | 생성 코드 판별 기준(annotation 유무)이 프로젝트마다 동일하다는 것 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C3`: `net.ltgt.errorprone` plugin id + `errorprone` configuration 패턴이 플러그인의 공식 적용 방법임
|
||||
- `C4`: JDK 16+ 에서 수동 JVM arg 추가 없이 plugin 이 자동 처리함 — Java 21 빌드에서 별도 `forkOptions.jvmArgs` 블록 불필요
|
||||
- `C1`/`C2`: ca-tmpl 이 Gradle 6.8+ + ErrorProne 2.43+ 를 사용한다면 JDK 21 이상이 필요
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- ca-tmpl 특정 버전(예: `com.google.errorprone:error_prone_core:2.x`)과의 실제 동작 호환성
|
||||
- Android Gradle Plugin 환경에서의 동작 (README 에 명시적 불지원)
|
||||
- C4 의 예외 조건: `javaHome` 또는 `executable` 을 명시한 fork task 에는 JVM args 미주입
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-tmpl 의 실제 Gradle 버전 + ErrorProne 버전 조합 호환성 로컬 검증
|
||||
- `disableWarningsInGeneratedCode` 가 MapStruct/Lombok 생성 코드에 실제 적용되는지 확인
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- C4 의 "unless Gradle daemon JVM 에 이미 옵션 설정" 예외는 실무에서 `org.gradle.jvmargs` 로 직접 설정하는 경우가 드물어 대부분 자동 처리됨 — 그러나 CI 환경에서 gradle.properties 확인 권고.
|
||||
- 추가로 봐야 할 동일 출처 페이지: [Configuration Properties 전체 표](https://github.com/tbroyer/gradle-errorprone-plugin#properties) — `checks`, `checkOptions`, `excludedPaths` 등 추가 DSL 옵션.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog: [[raw/official-docs/archunit-user-guide]] (정적 분석 — 아키텍처 룰 강제)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/errorprone-gradle-integration]]` (생성 시)
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Event Sourcing as an Alternative to Outbox (microservices.io)
|
||||
source_type: official-doc
|
||||
url: https://microservices.io/patterns/data/event-sourcing.html
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [ca-outbox-pattern, event-sourcing, alternative, microservices-io, official-doc]
|
||||
related_branches: [feature-domain-event-outbox-contract, feature-background-job-async-contract]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Event Sourcing as an Alternative to Outbox (microservices.io)
|
||||
|
||||
> Layer: `raw/official-docs/` — microservices.io 의 "Pattern: Event sourcing" 문서. ca-tmpl outbox 대안 중 **대안 4 (event sourcing — 도메인 모델 자체 교체)** 의 1차 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-domain-event-outbox-contract]] | outbox 6대안 비교에서 "event sourcing" 위치 — outbox 자체가 불요해지는 모델로 분류하는 근거 |
|
||||
| [[raw/branch-notes/feature-background-job-async-contract]] | event 발행을 background job 으로 처리할지 vs event store 내장 subscriber 로 처리할지의 분기 근거 |
|
||||
|
||||
특정 branch 없이 foundational 조사로 수집한 경우:
|
||||
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] — Contract #19 (Domain Application Readiness Contract) 의 Domain Event / Outbox 항목에서 event sourcing 채택 안 함의 근거 자료
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-tmpl 의 SKIP LOCKED outbox 결정에 대한 **대안 4: event sourcing**. outbox 자체가 불요해지는 모델 — event store 가 source of truth 가 되므로 별도 발행 메커니즘이 필요 없거나 매우 단순해짐.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://microservices.io/patterns/data/event-sourcing.html
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Chris Richardson (microservices.io)
|
||||
- 발행일: rolling (microservices.io patterns catalog)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
- 보조 자료: Confluent blog "Event Sourcing, CQRS, and Stream Processing" (`https://www.confluent.io/blog/event-sourcing-cqrs-stream-processing-apache-kafka-whats-connection/`)
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Solution] "Event sourcing persists the state of a business entity such an Order or a Customer as a sequence of state-changing events."
|
||||
|
||||
> [§Solution] "Whenever the state of a business entity changes, a new event is appended to the list of events."
|
||||
|
||||
> [§Solution] "Since saving an event is a single operation, it is inherently atomic."
|
||||
|
||||
> [§Solution] "The application reconstructs an entity's current state by replaying the events."
|
||||
|
||||
> [§Solution] "When a service saves an event in the event store, it is delivered to all interested subscribers."
|
||||
|
||||
> [§Resulting Context — Benefits] "It solves one of the key problems in implementing an event-driven architecture and makes it possible to reliably publish events whenever state changes."
|
||||
|
||||
> [§Resulting Context — Drawbacks] "The event store is difficult to query since it requires typical queries to reconstruct the state of the business entities."
|
||||
|
||||
보조 인용 (Confluent blog, 동일 주제):
|
||||
|
||||
> [Confluent — Event Sourcing, CQRS, and Stream Processing] "Event sourcing involves modeling the state changes made by applications as an immutable sequence or 'log' of events."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| ES-OUTBOX-C1 | Event sourcing 은 business entity 의 state 를 일련의 state-changing events 의 sequence 로 영속화한다 | [§Solution] "Event sourcing persists the state of a business entity such an Order or a Customer as a sequence of state-changing events." | `official-reference` | event sourcing 채택 시스템의 영속화 모델 정의 | event sourcing 이 모든 도메인에 적합하다는 뜻은 아님 — drawback 인용 별도 |
|
||||
| ES-OUTBOX-C2 | event 저장은 단일 operation 이므로 본질적으로 atomic — 즉 dual-write 문제가 발생하지 않는 구조 | [§Solution] "Since saving an event is a single operation, it is inherently atomic." | `official-reference` | event store 가 단일 transaction 단위로 event 를 추가하는 영속화 경계 | "DB + Kafka 두 시스템에 동시 쓰기가 자동으로 atomic" 이라는 뜻은 아님 — event store 단일 시스템 내부에서만 |
|
||||
| ES-OUTBOX-C3 | 현재 상태는 events 를 replay 함으로써 재구성된다 (저장된 것은 events, 계산되는 것은 state) | [§Solution] "The application reconstructs an entity's current state by replaying the events." | `official-reference` | event sourcing 의 read path 메커니즘 | replay 비용이 항상 허용 가능하다는 뜻은 아님 — snapshot 필요성은 별도 |
|
||||
| ES-OUTBOX-C4 | event store 에 event 가 저장되면 모든 관심 있는 subscriber 에게 전달된다 → 별도 발행 메커니즘(outbox/CDC) 의 역할이 event store 자체로 흡수 | [§Solution] "When a service saves an event in the event store, it is delivered to all interested subscribers." | `official-reference` | event store 가 내장 subscription 기능을 제공하는 구현 (EventStoreDB, Axon 등) | "subscriber 전달이 exactly-once" 라는 뜻은 아님 — delivery semantics 본 인용에 미명시 |
|
||||
| ES-OUTBOX-C5 | event sourcing 은 event-driven architecture 구현의 핵심 문제(상태 변경 시 안정적 event 발행) 를 해결한다 | [§Resulting Context — Benefits] "It solves one of the key problems in implementing an event-driven architecture and makes it possible to reliably publish events whenever state changes." | `official-reference` | event-driven architecture 의 dual-write 문제 컨텍스트 | "outbox 보다 항상 우수하다" 는 뜻은 아님 — 트레이드오프 별도 |
|
||||
| ES-OUTBOX-C6 | event store 는 query 가 어렵다 — 일반 query 가 entity state 재구성을 요구하기 때문 | [§Resulting Context — Drawbacks] "The event store is difficult to query since it requires typical queries to reconstruct the state of the business entities." | `official-reference` | event sourcing 의 read model 운영 부담 | CQRS read model 분리가 의무라는 뜻은 아님 — 권장 패턴일 뿐 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `ES-OUTBOX-C1` ~ `C3`: event sourcing 의 정의·atomicity·replay 메커니즘
|
||||
- `ES-OUTBOX-C4` ~ `C5`: event sourcing 이 outbox 와 같은 별도 발행 메커니즘의 필요성을 흡수한다는 사실
|
||||
- `ES-OUTBOX-C6`: event sourcing 의 query 어려움 (CQRS / snapshot / projection 필요성의 근거)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- event sourcing 이 outbox/SKIP LOCKED 보다 "더 나은 선택" 이라는 일반적 권고
|
||||
- 기존 CRUD 시스템에서 event sourcing 으로 마이그레이션 비용 구체적 산정
|
||||
- event store 의 EOS (exactly-once) 보장 — 본 페이지는 delivery semantics 미명시
|
||||
- Spring/JPA 기반 도메인에서 event sourcing 도입의 ORM 충돌 정도
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 의 도메인이 event sourcing 에 적합한가 (금융/감사 로그 중심 vs 단순 CRUD)
|
||||
- team 의 CQRS/projection 운영 경험 수준 — event sourcing 학습 곡선 평가
|
||||
- event store 도구 선택 (EventStoreDB / Axon / Kafka-as-log) 의 운영 비용
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- 적용 시나리오: 도메인이 본질적으로 event-driven (금융 거래, 주문 상태 전이, 감사 로그가 핵심). 팀이 CQRS / projection 운영에 익숙한 경우.
|
||||
- 장점:
|
||||
- **outbox 불요** — event 자체가 저장 단위 (`ES-OUTBOX-C2` + `C4` 결합 해석)
|
||||
- 완전한 audit log (모든 상태 변화가 보존)
|
||||
- replay 로 신규 read model 구축 자유로움
|
||||
- temporal query (과거 시점 상태 재구성) 가능
|
||||
- 단점:
|
||||
- **현재 상태 조회가 비싸다** (`ES-OUTBOX-C6`) → snapshot / projection 인프라 필요
|
||||
- 학습 곡선 가파름 (CQRS, eventual consistency, projection 재구축 등)
|
||||
- schema evolution (event 버전 관리) 부담
|
||||
- 기존 CRUD 시스템에서 마이그레이션 비용 큼
|
||||
- 일반적 ORM/JPA workflow 와 충돌
|
||||
- ca-tmpl(SKIP LOCKED polling) 과의 차이:
|
||||
- outbox 는 **기존 CRUD + 이벤트 발행** 하이브리드. event sourcing 은 **저장 모델 자체를 교체**.
|
||||
- "보조 발행 메커니즘" 이 아니라 "도메인 모델 패러다임 전환" 이라 의사결정 스케일이 다름
|
||||
- 운영 복잡도: 높음. event store + projection + snapshot 운영.
|
||||
- exactly-once / at-least-once 보장 수준: 발행은 여전히 **at-least-once** 가정 안전 (본 페이지 미명시 — 별도 검증 필요).
|
||||
- 외부 의존성 추가 여부: event store (EventStoreDB, Kafka as log, Axon 등) 또는 자체 구축.
|
||||
- 결론: ca-tmpl 같은 기존 CRUD-based 도메인에 event sourcing 을 도입하는 것은 outbox 의 "대안" 이 아니라 "전혀 다른 도메인 설계 선택" 에 가까움. 트레이드오프 폭이 가장 큼.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 raw 자료:
|
||||
- [[raw/company-tech-blogs/domain-event-sourcing-vs-cqrs-greg-young]] (event sourcing vs CQRS 구분 — Greg Young 원작자)
|
||||
- [[raw/company-tech-blogs/outbox-confluent-kafka-connect-smt]] (대안 2: Kafka Connect SMT)
|
||||
- [[raw/company-tech-blogs/outbox-netflix-domain-events-cdc]] (대안 6: Netflix DBLog)
|
||||
- [[raw/company-tech-blogs/outbox-wix-engineering-debezium]] (대안 1 사례: Wix Debezium production)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-domain-event-outbox-contract]]
|
||||
- [[raw/branch-notes/feature-background-job-async-contract]]
|
||||
- 인용하는 wiki: (미작성)
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: Screaming Architecture (Uncle Bob, 2011)
|
||||
source_type: official-doc
|
||||
url: https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [ca-architecture-layout, feature-first, screaming-architecture, clean-architecture]
|
||||
related_branches: [feature-architecture-enforcement-rules, feature-skeleton-package-blueprint-contract, feature-domain-feature-onboarding-contract, feature-domain-modeling-guardrails]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Screaming Architecture (Uncle Bob)
|
||||
|
||||
> Layer: `raw/official-docs/` — Robert C. Martin (Uncle Bob) "Screaming Architecture" (cleancoder.com 블로그, 2011-09-30) verbatim 발췌. ca-tmpl 의 feature-first 결정 (대안 1, 채택 baseline) 의 이론적 출처.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-architecture-enforcement-rules]] | 패키지 최상위가 "use case / feature 이름" 으로 시작해야 한다는 ArchUnit 룰의 이론 근거 |
|
||||
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | `features/{featureName}/{presentation,application,domain,infrastructure}` blueprint 의 "feature 최상위" 분할 정당화 |
|
||||
| [[raw/branch-notes/feature-domain-feature-onboarding-contract]] | 신규 기능 추가 시 framework 가 아닌 use case 로 패키지 명명하는 가이드 |
|
||||
| [[raw/branch-notes/feature-domain-modeling-guardrails]] | "architecture should tell about the system, not frameworks" — domain 이 framework annotation 으로 오염되지 않게 하는 원칙 근거 |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
ca-tmpl의 feature-first 결정(대안 1, 채택 baseline)에 대한 이론적 근거. Uncle Bob이 제시한 "use case 중심으로 패키지를 잘라야 한다"는 주장은 Package-by-Feature의 정신적 뿌리이며, ca-tmpl이 `features/{featureName}` 단위로 자르는 이유의 1차 출처.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html
|
||||
- 아카이브 URL: (미확보)
|
||||
- 저자/조직: Robert C. Martin (Uncle Bob)
|
||||
- 발행일: 2011-09-30
|
||||
- 후속 정리: 동저자의 *Clean Architecture* (2017) 21장 "Screaming Architecture"
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Opening question] "So what does the architecture of your application scream?"
|
||||
|
||||
> [§Architecture vs framework] "Architectures are not (or should not) be about frameworks."
|
||||
|
||||
> [§Deferred decisions] "A good software architecture allows decisions about frameworks, databases, web-servers...to be deferred and delayed."
|
||||
|
||||
> [§Web as delivery] "The Web is a delivery mechanism, and your application architecture should treat it as such."
|
||||
|
||||
> [§Reader perspective] "Your architectures should tell readers about the system, not about the frameworks."
|
||||
|
||||
> [§Framework relationship] "Frameworks are tools to be used, not architectures to be conformed to."
|
||||
|
||||
> [§Caution] "View it skeptically. Yes, it might help, but at what cost."
|
||||
|
||||
> [§Building analogy] (요약) house plan 은 layout 만 봐도 "house" 임이 드러남 (foyer/living room/kitchen). library 는 grand entrance/check-out area/gallery shelves 로 "library" 임이 드러남. 소프트웨어도 동일하게 healthcare/accounting 등 시스템 목적이 드러나야 하며 Rails/Spring/Hibernate 같은 framework 가 드러나면 안 됨.
|
||||
|
||||
> [§Ivar Jacobson 인용] "software architectures are structures that support the use cases of the system."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SCREAM-C1 | 좋은 소프트웨어 아키텍처는 framework 가 아니라 시스템의 use case / 도메인을 외부로 드러내야 한다 | [§Opening] "what does the architecture of your application scream?" + [§Architecture vs framework] "Architectures are not (or should not) be about frameworks." + [§Reader perspective] "Your architectures should tell readers about the system, not about the frameworks." | `engineering-blog` | 비즈니스 도메인이 명확한 시스템 | "framework 사용 자체가 금지" 라는 뜻은 아님 — framework 는 도구로 사용 가능, 단지 architecture 의 정체성으로 두면 안 됨 |
|
||||
| SCREAM-C2 | 좋은 아키텍처는 framework/DB/web server 같은 환경 결정을 **deferred and delayed** 할 수 있어야 한다 | [§Deferred decisions] "A good software architecture allows decisions about frameworks, databases, web-servers...to be deferred and delayed." | `engineering-blog` | 장수 lifecycle 시스템 | "환경 결정을 영원히 안 한다" 가 아니라 "초기에 못 박지 않는다" 의 의미 — 본 글에서 정확한 deferment 시점 기준 미제시 |
|
||||
| SCREAM-C3 | Web 은 delivery mechanism 이며 application architecture 의 일부가 아니다 | [§Web as delivery] "The Web is a delivery mechanism, and your application architecture should treat it as such." | `engineering-blog` | web/UI 가 있는 시스템 | REST controller / HTTP 라우팅을 작성하지 말라는 뜻은 아님 — 단지 domain core 가 HTTP 에 의존하지 말아야 한다는 원칙 |
|
||||
| SCREAM-C4 | Framework 는 conform 해야 할 architecture 가 아니라 use 할 도구다 — 비용 의식적으로 채택해야 함 | [§Framework relationship] "Frameworks are tools to be used, not architectures to be conformed to." + [§Caution] "View it skeptically. Yes, it might help, but at what cost." | `engineering-blog` | Spring/Rails/Django 등 opinionated framework 채택 결정 | framework 자체를 거부해야 한다는 뜻 아님 — trade-off 평가가 의무 |
|
||||
| SCREAM-C5 | (Ivar Jacobson 인용) 소프트웨어 아키텍처는 시스템의 use case 를 지원하는 구조다 | [§Jacobson 인용] "software architectures are structures that support the use cases of the system." | `engineering-blog` (Uncle Bob 의 Jacobson 인용) | use case 중심 설계 | Jacobson 원전 출처 (책/논문) 가 본 글에 명시 없음 — 원전 직접 확인 별도 필요 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `SCREAM-C1~C4`: Uncle Bob 블로그 글의 핵심 주장 verbatim — feature-first / use case-centric 패키지 분할의 철학적 근거
|
||||
- `SCREAM-C5`: Uncle Bob 이 Jacobson 의 입장을 어떻게 인용했는지 (Jacobson 원전 아님)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- "package-by-feature 가 공식 best practice" 라는 정당화 — 본 글은 Uncle Bob 의 개인 블로그 (cleancoder.com), Strength = `engineering-blog`. `official-vendor-doc` 으로 격상 금지.
|
||||
- 구체적 패키지 분할 가이드 (예: `features/{name}/{layer}/`) — 본 글은 철학 진술까지만, 구체 구조는 *Clean Architecture* 책 21장 또는 ca-tmpl 자체 결정
|
||||
- feature-first 가 layer-first 대비 정량 우위가 있다는 증거 (응집도/결합도 메트릭) — 본 글 범위 밖, Sahibinden 사례 / 별도 측정 필요
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- "deferred decision" 의 ca-tmpl 구체 매핑 — 어느 시점까지 DB/web server 결정을 늦출 수 있는가의 기준
|
||||
- ca-tmpl 의 `features/` 디렉터리가 실제 "scream" 하는지 (외부 reviewer 가 한 번 봤을 때 도메인이 보이는지) 의 검증 절차
|
||||
- 마이크로서비스 분리 시 feature-first 가 어떻게 module boundary 로 이어지는지 — 본 글 범위 밖
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석 — 자료 직접 인용 아님)
|
||||
|
||||
- 적용 시나리오: 도메인 의미가 분명한 비즈니스 시스템. CRUD-only 토이 프로젝트에는 과함.
|
||||
- 장점: 최상위 디렉터리만 봐도 "이 시스템이 무엇인지" 드러남. Feature 단위로 잘려 있으면 향후 microservice 분리 비용이 낮음.
|
||||
- 단점: 원문은 패키지 구조보다 "프레임워크에 종속된 사고방식" 비판에 집중. 구체적 패키지 가이드는 *Clean Architecture* 책 21장에 더 자세함.
|
||||
- ca-tmpl(feature-first)와의 차이: 동일한 철학. ca-tmpl의 `features/{name}/{presentation,application,domain,infrastructure}`는 이 원칙의 직접 구현체에 해당.
|
||||
|
||||
## 관련 ca-tmpl branch / contract
|
||||
|
||||
- 적용 branch-note:
|
||||
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
|
||||
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
|
||||
- [[raw/branch-notes/feature-domain-feature-onboarding-contract]]
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#20. Skeleton Blueprint Contract]]
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#19. Domain Application Readiness Contract]]
|
||||
- 대안 그룹: **Topic 1 — Architecture Layout** (대안 5종: feature-first / layer-first / hexagonal / modulith / onion)
|
||||
- 본 source의 위치: ca-tmpl 채택안 baseline (feature-first)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog:
|
||||
- [[raw/official-docs/layer-first-baeldung-clean-architecture-spring-boot]] (대안 2: layer-first 의 대표 튜토리얼)
|
||||
- [[raw/company-tech-blogs/feature-first-sahibinden-package-by-layer-vs-feature]] (Sahibinden 의 비교 사례, feature-first 측 증거 강화)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-architecture-enforcement-rules]]
|
||||
- [[raw/branch-notes/feature-skeleton-package-blueprint-contract]]
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "official-doc / WHATWG Fetch — CORS Protocol"
|
||||
source_type: official-doc
|
||||
url: https://fetch.spec.whatwg.org/
|
||||
archive_url:
|
||||
vendor: WHATWG
|
||||
related_branches: [feature-api-contract-baseline, feature-security-operational-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, security, networking, cors, fetch-spec]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / WHATWG Fetch — CORS Protocol
|
||||
|
||||
> Layer: `raw/official-docs/` — WHATWG Fetch 표준(Living Standard)의 §3.3 CORS protocol 섹션 원문 발췌·출처 기록.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | D13 — OPTIONS preflight 는 envelope 우회, resource metadata 는 envelope 따름. preflight request 의 식별 기준(OPTIONS method + `Access-Control-Request-Method` header)이 WHATWG Fetch §3.3.2 에 normative 하게 정의됨 |
|
||||
| [[raw/branch-notes/feature-security-operational-baseline]] | D9 — CORS allowlist + credentials false default + max-age 600s + wildcard-with-credentials 금지. Fetch spec §3.3.5 가 1차 normative source. 기존 UNSUPPORTED_DECISION 라벨 해소 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://fetch.spec.whatwg.org/
|
||||
- 아카이브 URL: (미등록 — Living Standard, 항상 최신)
|
||||
- 저자 / 조직: WHATWG (Anne van Kesteren et al.)
|
||||
- 발행일: Living Standard (지속 갱신)
|
||||
- 마지막 확인일: 2026-05-31
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-security-operational-baseline` D9 (CORS allowlist + credentials + max-age + wildcard 금지) 가 `UNSUPPORTED_DECISION` 상태로 남아있었고, wildcard+credentials 조합 금지 및 `Access-Control-Max-Age` 의 의미를 normative 하게 정의하는 1차 표준 문서가 부재했음. WHATWG Fetch spec §3.3 이 browser-enforced CORS 동작의 유일한 normative 출처이며, `feature-api-contract-baseline` D13 (OPTIONS preflight 의 envelope 우회)의 preflight 식별 기준도 동일 섹션에서 정의됨.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5개)
|
||||
|
||||
> [§3.3 General] "The CORS protocol consists of a set of headers that indicates whether a response can be shared cross-origin."
|
||||
> (원문 위치: line 5446 in fetched HTML)
|
||||
|
||||
> [§3.3.2 HTTP requests] "A CORS-preflight request is a CORS request that checks to see if the CORS protocol is understood. It uses `OPTIONS` as method and includes the following header: `Access-Control-Request-Method` — Indicates which method a future CORS request to the same resource might use."
|
||||
> (원문 위치: line 5463–5472 in fetched HTML)
|
||||
|
||||
> [§3.3.3 HTTP responses — `Access-Control-Max-Age`] "Indicates the number of seconds (5 by default) the information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` headers can be cached."
|
||||
> (원문 위치: line 5543–5546 in fetched HTML)
|
||||
|
||||
> [§3.3.5 CORS protocol and credentials — table note] "If credentials mode is `include`, then `Access-Control-Allow-Origin` cannot be `*`."
|
||||
> (원문 위치: line 5736–5738 in fetched HTML)
|
||||
|
||||
> [§3.3.3 HTTP responses — `Access-Control-Allow-Credentials`] "Indicates whether the response can be shared when request's credentials mode is `include`."
|
||||
> (원문 위치: line 5506–5508 in fetched HTML)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| FETCH-CORS-C1 | CORS protocol 은 cross-origin response 공유 여부를 나타내는 HTTP header 집합이다 | [§3.3.1] "The CORS protocol consists of a set of headers that indicates whether a response can be shared cross-origin." | `official-standard` | 모든 browser cross-origin fetch | server 가 CORS 설정을 어떻게 구현해야 하는지 (server-side impl 방법은 spec 범위 밖) |
|
||||
| FETCH-CORS-C2 | CORS-preflight request 는 `OPTIONS` method 를 사용하며 `Access-Control-Request-Method` header 를 포함한다 | [§3.3.2] "A CORS-preflight request is a CORS request that checks to see if the CORS protocol is understood. It uses `OPTIONS` as method and includes the following header: `Access-Control-Request-Method` — Indicates which method a future CORS request to the same resource might use." | `official-standard` | browser UA 가 preflight 를 전송하는 모든 경우 | server 가 preflight 에 어떻게 응답해야 하는지 (응답 필드는 §3.3.3에서 별도 정의) |
|
||||
| FETCH-CORS-C3 | credentials mode 가 `include` 인 경우 `Access-Control-Allow-Origin` 은 `*` 일 수 없다 | [§3.3.5 table] "If credentials mode is `include`, then `Access-Control-Allow-Origin` cannot be `*`." | `official-standard` | browser UA 의 CORS check 알고리즘 | Spring CORS 설정이 이 조합을 startup 시 자동으로 거부하는지 (Spring-specific 동작은 별도 검증 필요) |
|
||||
| FETCH-CORS-C4 | `Access-Control-Allow-Credentials` header 는 request 의 credentials mode 가 `include` 일 때 response 를 공유할 수 있는지를 나타낸다 | [§3.3.3] "Indicates whether the response can be shared when request's credentials mode is `include`." | `official-standard` | `credentials: include` 로 전송된 CORS request 에 대한 server response | CORS preflight 자체는 credentials 를 포함하지 않음 (preflight 의 credentials mode 는 `same-origin`) |
|
||||
| FETCH-CORS-C5 | `Access-Control-Max-Age` 는 preflight 결과를 캐시할 수 있는 초(second) 수를 나타내며 기본값은 5초이다 | [§3.3.3] "Indicates the number of seconds (5 by default) the information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` headers can be cached." | `official-standard` | browser UA 의 CORS-preflight cache | server 측 max-age 600s 결정의 타당성 — 브라우저가 UA-imposed limit 을 상한으로 두기 때문에 실제 캐시 시간은 서버 설정과 다를 수 있음 (§4.8 "If max-age is greater than an imposed limit") |
|
||||
|
||||
### Strength 허용값 참조
|
||||
|
||||
- 모든 5개 claim: `official-standard` — WHATWG Living Standard (browser 구현의 normative 기준)
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
### 이 자료가 직접 증명하는 것
|
||||
|
||||
- `FETCH-CORS-C1`: browser 가 cross-origin response 공유 여부를 CORS header 로 판단함
|
||||
- `FETCH-CORS-C2`: browser UA 는 non-CORS-safelisted method 또는 non-CORS-safelisted request-header 가 포함된 요청에 대해 OPTIONS preflight 를 먼저 전송함. preflight = OPTIONS + `Access-Control-Request-Method` header 는 normative
|
||||
- `FETCH-CORS-C3`: `credentials: include` + `Access-Control-Allow-Origin: *` 조합은 WHATWG spec 이 직접 금지 (browser 가 이 조합을 실패 처리)
|
||||
- `FETCH-CORS-C4`: `Access-Control-Allow-Credentials: true` 가 없으면 `credentials: include` 요청의 응답이 공유되지 않음
|
||||
- `FETCH-CORS-C5`: `Access-Control-Max-Age` 미설정 시 browser 기본값 = 5초. UA 는 자체 imposed limit 을 상한으로 적용 가능
|
||||
|
||||
### 이 자료가 증명하지 않는 것
|
||||
|
||||
- **server-side CORS allowlist 구현 방법**: Fetch spec 은 browser UA 의 동작을 정의. Spring `CorsConfiguration`, `WebMvcConfigurer.addCorsMappings()`, Spring Security `CorsFilter` 의 구현 방법은 Spring 벤더 문서에서 별도 확인 필요
|
||||
- **Spring CorsConfiguration 이 startup 시 wildcard+credentials 조합을 자동으로 거부하는지**: `FETCH-CORS-C3` 는 browser 측 실패를 정의하며, server 측 Spring 의 startup-time validation 은 별도 source 필요 (`feature-security-operational-baseline` Claims To Verify 항목 유지)
|
||||
- **gateway-level CORS 처리**: API gateway / WAF 가 app 보다 먼저 CORS 를 처리하는 경우 동작. Fetch spec 범위 밖
|
||||
- **max-age 600s 가 production 에서 최적 값임**: `FETCH-CORS-C5` 는 기본값 5초와 UA 상한 존재를 증명하나, 600s 선택의 타당성은 별도 trade-off 결정
|
||||
|
||||
### 내 프로젝트에 적용하려면 추가 확인이 필요한 것
|
||||
|
||||
- Spring Security `CorsConfiguration.checkOriginPatterns()` 가 wildcard+credentials 조합에서 실제로 startup fail 또는 runtime reject 하는지 통합 테스트 필요 (`needs-confirmation` 상태 유지)
|
||||
- `NimbusJwtDecoder` 와 별개로 Spring MVC `CorsFilter` 또는 `@CrossOrigin` 의 실제 동작 확인
|
||||
- browser UA-imposed max-age limit (Chrome: 86400s, Firefox: 86400s) 과 서버 설정 max-age 600s 의 실효 관계 확인
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- WHATWG Fetch spec 은 Living Standard 로 날짜 고정 버전이 없음. 인용 시 항상 "as of YYYY-MM-DD" 명시 권장
|
||||
- §4.8 CORS-preflight fetch 알고리즘에 "If max-age is failure or null, then set max-age to 5" 가 명시 — browser default 5초는 spec normative
|
||||
- §4.8 "If max-age is greater than an imposed limit on max-age, then set max-age to the imposed limit" — browser 가 server 설정값을 truncate 가능. 현재 Chrome/Firefox 상한 86400s (24h)
|
||||
- `feature-security-operational-baseline` D9 의 `UNSUPPORTED_DECISION` 은 이 raw source 의 `FETCH-CORS-C3` 로 1차 normative 근거가 확보됨. D9 의 Decision Evidence Map 에 `FETCH-CORS-C3` 를 추가하고 `UNSUPPORTED_DECISION` 라벨 제거 권장 (별도 세션에서 branch-note 갱신)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 RFC: RFC 6454 (The Web Origin Concept) — `Origin` header 정의의 원본 RFC
|
||||
- Spring CORS 벤더 문서: `raw/official-docs/` 미등록 — 후속 fetch 필요
|
||||
- 본 자료 인용 예정 wiki 요약: `wiki/concepts/cors-protocol` (생성 시)
|
||||
- 관련 branch-note: [[raw/branch-notes/feature-security-operational-baseline]], [[raw/branch-notes/feature-api-contract-baseline]]
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: AWS S3 — Presigned URL upload (direct browser-to-S3)
|
||||
source_type: official-doc
|
||||
url: https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [file, s3, presigned-url, upload, ca-skeleton, official-doc]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
related_branches: [feature-file-resource-handling-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# AWS S3 — Uploading objects using presigned URLs
|
||||
|
||||
> Layer: `raw/official-docs/` — AWS S3 User Guide "Uploading objects with presigned URLs" 페이지 verbatim 발췌. ca-tmpl file handling 대안 비교 (app-via vs direct S3).
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-file-resource-handling-contract]] | direct S3 (presigned URL) 가 app-via 3-layer (gateway 20MB / Spring 10MB / request 12MB) limit 우회 대안임을 정당화 — byte 가 앱을 거치지 않으므로 size enforcement 위치가 달라짐 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-tmpl의 file handling 결정(10MB Spring / 12MB global / 20MB gateway)은 **앱 서버를 경유**하는 경우의 트리플 layer. 대안인 **direct S3 upload (presigned URL)** 은 앱 서버가 byte를 받지 않아 size limit 의미 자체가 달라짐. 운영 비교가 필요.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html
|
||||
- 보조 1: AWS Blog "Uploading to Amazon S3 directly from a web or mobile application" — https://aws.amazon.com/blogs/compute/uploading-to-amazon-s3-directly-from-a-web-or-mobile-application/
|
||||
- 보조 2: S3 POST policy ("Browser-based uploads using POST") — https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자/조직: AWS
|
||||
- 발행일: 지속 업데이트 (2024 기준 검증)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Uploading objects with presigned URLs — opening] "You may use presigned URLs to allow someone to upload an object to your Amazon S3 bucket. Using a presigned URL will allow an upload without requiring another party to have AWS security credentials or permissions. A presigned URL is limited by the permissions of the user who creates it."
|
||||
|
||||
> [§Uploading objects with presigned URLs — opening] "That is, if you receive a presigned URL to upload an object, you can upload an object only if the creator of the URL has the necessary permissions to upload that object."
|
||||
|
||||
> [§Uploading objects with presigned URLs — opening] "When someone uses the URL to upload an object, Amazon S3 creates the object in the specified bucket. If an object with the same key that is specified in the presigned URL already exists in the bucket, Amazon S3 replaces the existing object with the uploaded object. After upload, the bucket owner will own the object."
|
||||
|
||||
> [§Using the AWS SDKs — Note] "If you use the AWS CLI or AWS SDKs, the expiration time for presigned URLs can be set as high as 7 days."
|
||||
|
||||
> [§Using the AWS Toolkit for Visual Studio — step 7] "Choose **PUT** to specify that this presigned URL will be used for uploading an object."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| FS3-PRE-C1 | presigned URL 은 받는 측에 AWS 자격증명/권한을 요구하지 않고 upload 를 허용하며, URL 의 권한 범위는 발급자의 권한으로 제한된다 | [§opening] "Using a presigned URL will allow an upload without requiring another party to have AWS security credentials or permissions. A presigned URL is limited by the permissions of the user who creates it." | `official-vendor-doc` | S3 PUT 업로드용 presigned URL 발급 | 발급자 권한이 동적으로 revoke 되었을 때 이미 발급된 URL 이 즉시 무효화된다는 뜻은 아님 |
|
||||
| FS3-PRE-C2 | presigned URL 로 업로드 시 같은 key 의 객체가 이미 있으면 S3 는 기존 객체를 새 객체로 **교체** 한다 | [§opening] "If an object with the same key that is specified in the presigned URL already exists in the bucket, Amazon S3 replaces the existing object with the uploaded object." | `official-vendor-doc` | 동일 key 재업로드 시나리오 | versioning 활성화 bucket 의 동작은 본 인용 범위 밖 (별도 versioning 문서 필요) |
|
||||
| FS3-PRE-C3 | upload 완료 후 객체의 소유권은 **bucket owner** 에게 귀속된다 | [§opening] "After upload, the bucket owner will own the object." | `official-vendor-doc` | 표준 bucket (Object Ownership 기본 설정) | ACL/Object Ownership 설정 변경 시의 동작은 별도 |
|
||||
| FS3-PRE-C4 | AWS CLI/SDK 로 presigned URL 발급 시 expiration time 은 최대 **7일** 까지 설정 가능 | [§Using the AWS SDKs — Note] "If you use the AWS CLI or AWS SDKs, the expiration time for presigned URLs can be set as high as 7 days." | `official-vendor-doc` | CLI/SDK 기반 presigned URL 발급 | 모든 발급 방법 (예: console / signer credential 형식별) 의 한도가 동일하다는 뜻은 아님 |
|
||||
| FS3-PRE-C5 | upload 용 presigned URL 의 HTTP 메소드는 **PUT** 으로 지정한다 | [§Toolkit step 7] "Choose **PUT** to specify that this presigned URL will be used for uploading an object." | `official-vendor-doc` | Toolkit/SDK 기반 단일 객체 업로드 URL 발급 | POST policy 기반 browser POST 업로드 (별도 sigv4 POST 페이지) 와는 다른 메커니즘 |
|
||||
|
||||
### Strength 허용값 사용
|
||||
|
||||
- `official-vendor-doc` — AWS 공식 User Guide
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `FS3-PRE-C1`: presigned URL 의 권한 위임 메커니즘 (발급자 권한 = URL 권한)
|
||||
- `FS3-PRE-C2`: 동일 key 재업로드 시 replace 동작 (default)
|
||||
- `FS3-PRE-C3`: upload 완료 후 ownership 귀속처
|
||||
- `FS3-PRE-C4`: SDK/CLI 발급 시 최대 7일 expiration
|
||||
- `FS3-PRE-C5`: 단일 객체 업로드용 메소드 = PUT
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `content-length-range` / POST policy 기반 size limit enforcement (보조 URL `sigv4-HTTPPOSTConstructPolicy.html` 의 별도 페이지 영역)
|
||||
- antivirus / content-type 검증을 S3 가 수행한다는 사실 (별도 S3 event → Lambda 패턴 필요)
|
||||
- presigned URL 이 발급 후 발급자 자격증명 rotation 으로 즉시 무효화되는지 (별도 IAM 동작 문서)
|
||||
- direct S3 upload 가 app-via 보다 어떤 환경에서 더 비용효율적인지 (운영 비교는 별도 분석)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 의 "EXTERNAL_OUTBOUND_ALLOWED capability" 가 presigned URL 발급 시점의 signing 호출에 어떻게 매핑되는지
|
||||
- quarantine bucket → scan → main bucket 패턴의 정확한 S3 event 트리거 구성
|
||||
- SPA 의 PUT 호출 시 browser CORS preflight 요구사항 (별도 S3 CORS 문서)
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- ca-tmpl 비교:
|
||||
- **App-via upload (ca-tmpl 현재 결정)**: gateway 20MB → Spring 10MB single + 12MB request total. 앱이 byte를 받아 antivirus/content-type 검증 가능. 단 app instance memory/disk 압박.
|
||||
- **Direct S3 (대안)**: 앱이 presigned URL만 발급. byte는 client → S3 직행. app instance load 0. 단 content-type 검증과 antivirus는 S3 event(ObjectCreated) → Lambda/worker로 비동기화.
|
||||
- size limit enforcement 위치 차이:
|
||||
- app-via: Spring multipart parser가 enforce.
|
||||
- direct S3: presigned URL의 POST policy `content-length-range` 또는 PUT 시 `Content-Length` 헤더와 bucket policy로 enforce.
|
||||
- ca-tmpl 의사결정 trade-off:
|
||||
- direct S3는 path traversal 자동 해결 (opaque key 발급).
|
||||
- direct S3는 antivirus가 **post-upload** 가 되어 ca-tmpl의 "antivirus at gateway" 결정과 충돌 (gateway가 우회됨). 별도 "S3 quarantine bucket → scan → main bucket" pattern 필요.
|
||||
- ca-tmpl 결정인 "outbound = object store call이 EXTERNAL_OUTBOUND_ALLOWED capability 요구"는 presigned URL 발급 시점에서도 유효 (signing은 outbound credential 사용).
|
||||
|
||||
## 관련 ca-tmpl branch / contract
|
||||
|
||||
- 적용 branch-note:
|
||||
- [[raw/branch-notes/feature-file-resource-handling-contract]]
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#18. Control Plane Contract]]
|
||||
- 대안 그룹: **Group G-J — Privacy / File / Domain Modeling** (file resource handling)
|
||||
- 본 source의 위치: 대안 1 — Direct S3 presigned URL upload (app via 3-layer 우회)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/file-tus-resumable-upload-protocol]] (tus.io resumable — 다른 대안)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-file-resource-handling-contract]]
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: tus.io — Resumable upload protocol (v1.0.0)
|
||||
source_type: official-doc
|
||||
url: https://tus.io/protocols/resumable-upload
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [file, tus, resumable-upload, multipart, ca-skeleton, official-doc]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
related_branches: [feature-file-resource-handling-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# tus.io — Open protocol for resumable file uploads
|
||||
|
||||
> Layer: `raw/official-docs/` — tus.io 공식 protocol v1.0.0 발췌. 대용량/이어올리기 시나리오에서 ca-tmpl Spring 10MB multipart enforcement 의 한계 비교 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-file-resource-handling-contract]] | tus 가 resumable 표준이라는 사실 → ca-tmpl 의 Spring multipart 단일 stream 가정과 충돌하는 영역 식별 (Tus-Max-Size 헤더 enforcement, session vs orphan threshold 분리 필요성) |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-tmpl의 multipart 10MB limit은 short file 기준. 대용량(영상, 백업) upload는 connection drop → 처음부터 재시도라는 운영 문제 발생. tus는 byte offset 기반 resume 표준. ca-tmpl이 현재 채택하지 않은 이유와 채택 시 size limit 결정에 어떤 영향이 있는지 비교용.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://tus.io/protocols/resumable-upload
|
||||
- 보조 1: tus-java-server (reference Java implementation) — https://github.com/tomdesair/tus-java-server
|
||||
- 보조 2: Vimeo "How we built a resumable upload service" — https://medium.com/vimeo-engineering-blog/from-zero-to-100mbs-how-we-massively-improved-vimeos-upload-speed-71f72ca1ca5e
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자/조직: transloadit / tus.io community (Marius Kleidl 외)
|
||||
- 발행일: v1.0.0 — 2018-02 (지속 업데이트)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Abstract] "The protocol provides a mechanism for resumable file uploads via HTTP (RFC 9110)."
|
||||
|
||||
> [§HEAD] "The Server MUST always include the `Upload-Offset` header in the response for a `HEAD` request, even if the offset is `0`."
|
||||
|
||||
> [§PATCH] "All `PATCH` requests MUST use `Content-Type: application/offset+octet-stream`, otherwise the server SHOULD return a `415 Unsupported Media Type` status."
|
||||
|
||||
> [§PATCH] "If the offsets do not match, the Server MUST respond with the `409 Conflict` status without modifying the upload resource."
|
||||
|
||||
> [§Tus-Max-Size] "The `Tus-Max-Size` response header MUST be a non-negative integer indicating the maximum allowed size of an entire upload in bytes."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| TUS-RUP-C1 | tus 는 HTTP (RFC 9110) 위에서 동작하는 resumable file upload 프로토콜이다 | [§Abstract] "The protocol provides a mechanism for resumable file uploads via HTTP (RFC 9110)." | `official-standard` | resumable upload 표준 채택 평가 | RFC 9110 자체가 tus 를 정의/승인한다는 뜻은 아님 — tus 는 HTTP **위에** 정의된 프로토콜 |
|
||||
| TUS-RUP-C2 | Server 는 HEAD 응답에 `Upload-Offset` 헤더를 항상 포함해야 한다 (offset 이 0 이어도 포함 — MUST) | [§HEAD] "The Server MUST always include the `Upload-Offset` header in the response for a `HEAD` request, even if the offset is `0`." | `official-standard` | tus core protocol 구현 시 HEAD 핸들러 | client 측 retry 로직의 정확한 형태나 idempotency 보장은 본 조항 범위 밖 |
|
||||
| TUS-RUP-C3 | PATCH 요청은 `Content-Type: application/offset+octet-stream` 을 반드시 사용해야 하며 (MUST), 그렇지 않으면 server 는 `415 Unsupported Media Type` 응답을 권장 (SHOULD) | [§PATCH] "All `PATCH` requests MUST use `Content-Type: application/offset+octet-stream`, otherwise the server SHOULD return a `415 Unsupported Media Type` status." | `official-standard` | tus PATCH 요청/응답 처리 | Spring 의 default multipart parser 가 이 content-type 을 처리한다는 뜻이 아님 — 별도 controller 필요 |
|
||||
| TUS-RUP-C4 | 클라이언트 offset 과 서버 offset 이 일치하지 않으면 server 는 `409 Conflict` 로 응답하고 upload 리소스를 수정하지 않아야 한다 (MUST) | [§PATCH] "If the offsets do not match, the Server MUST respond with the `409 Conflict` status without modifying the upload resource." | `official-standard` | concurrent / out-of-order PATCH 처리 | conflict 후 클라이언트의 정확한 복구 절차 (재 HEAD 후 재 PATCH) 형태는 본 인용에 명시 없음 |
|
||||
| TUS-RUP-C5 | `Tus-Max-Size` 응답 헤더는 전체 upload 의 허용 최대 byte 수를 나타내는 non-negative integer 여야 한다 (MUST) | [§Tus-Max-Size] "The `Tus-Max-Size` response header MUST be a non-negative integer indicating the maximum allowed size of an entire upload in bytes." | `official-standard` | tus server 의 size limit 알림 | per-PATCH chunk size limit 이 동일 메커니즘으로 표현된다는 뜻은 아님 — chunk-level limit 은 별도 확장 |
|
||||
|
||||
### Strength 허용값 사용
|
||||
|
||||
- `official-standard` — tus.io v1.0.0 protocol specification (open standard)
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `TUS-RUP-C1`: tus 의 HTTP 기반 정의
|
||||
- `TUS-RUP-C2`, `TUS-RUP-C3`, `TUS-RUP-C4`: HEAD/PATCH 의 핵심 의무사항 (Upload-Offset / Content-Type / 409 Conflict)
|
||||
- `TUS-RUP-C5`: `Tus-Max-Size` 헤더의 단위와 의미
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- tus 가 모든 production 환경에서 multipart 대비 더 안정적이라는 일반화 (Vimeo case study 는 별도 company-tech-blog 영역)
|
||||
- tus-java-server reference impl 의 Spring Boot 통합 정확한 절차
|
||||
- tus session 의 server-side storage backend 선택 (memory / disk / object store) 의 trade-off
|
||||
- chunk-level retry 와 session-level resume 의 정확한 경계 (Tus-Max-Size 외 chunk extension)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 의 "1h orphan cleanup" 정책과 tus unfinished upload session 의 충돌 가능성
|
||||
- Spring 환경에서 PATCH + `application/offset+octet-stream` 처리 controller 의 직접 구현 패턴
|
||||
- `Tus-Max-Size` 와 nginx/gateway level body size limit 의 상호작용
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- ca-tmpl과의 trade-off:
|
||||
- **tus 채택 시 장점**: 100MB 영상 업로드도 disconnect 무관, mobile 사용자 friendly, 서버 메모리 부담 분산 (chunk 단위).
|
||||
- **tus 채택 시 비용**: PATCH 기반 protocol → Spring multipart는 동작 안 함, 별도 controller + storage layer 필요. ca-tmpl의 Spring 10MB enforcement가 직접 적용 안 됨 (`Tus-Max-Size` 헤더로 대체).
|
||||
- temp file cleanup 정책 변경 필요: tus는 unfinished upload가 hours 동안 잔존 가능 → ca-tmpl의 "1h orphan cleanup"이 tus upload session을 잘못 삭제할 수 있음. session timeout과 orphan threshold 분리 필요.
|
||||
- 대안 비교:
|
||||
- **multipart only (ca-tmpl 현재)**: 단순, 작은 파일에 최적, resume 불가.
|
||||
- **tus**: resumable, 큰 파일 적합, 서버 stateful (session storage 필요).
|
||||
- **direct S3 multipart upload (S3 SDK)**: S3 자체의 multipart API. 5MB 미만 last part 외엔 chunk 단위 retry 가능. tus와 유사한 효과지만 vendor-specific.
|
||||
- ca-tmpl 결정 영향: 현재 "streaming 100MB max + 60s timeout"이 단일 stream 가정. tus 채택 시 session-level limit과 chunk-level limit 분리 필요.
|
||||
|
||||
## 관련 ca-tmpl branch / contract
|
||||
|
||||
- 적용 branch-note:
|
||||
- [[raw/branch-notes/feature-file-resource-handling-contract]]
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#18. Control Plane Contract]]
|
||||
- 대안 그룹: **Group G-J — Privacy / File / Domain Modeling** (file resource handling)
|
||||
- 본 source의 위치: 대안 2 — tus.io resumable protocol (100MB+ video upload, session vs orphan threshold)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/file-s3-presigned-url-upload]] (direct S3 presigned URL — 다른 대안)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-file-resource-handling-contract]]
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: Find Security Bugs — Official Site & Bug Patterns Reference
|
||||
source_type: official-doc
|
||||
url: https://find-sec-bugs.github.io/
|
||||
archive_url:
|
||||
related_branches: [feature-static-analysis-quality-contract]
|
||||
related_projects: [ca-skeleton, ca-tmpl]
|
||||
tags: [official-doc, ca-skeleton, security, owasp, static-analysis]
|
||||
created: 2026-06-15
|
||||
---
|
||||
|
||||
# Find Security Bugs — Official Site & Bug Patterns Reference
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-static-analysis-quality-contract]] | D4 — FindSecBugs(SpotBugs 보안 플러그인) 채택. 코드 수준 보안 anti-pattern 탐지이며 의존성 CVE 스캔(`feature-dependency-vulnerability-management-contract`)과 구분됨. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://find-sec-bugs.github.io/
|
||||
- 버그 패턴 목록 URL: https://find-sec-bugs.github.io/bugs.htm
|
||||
- 아카이브 URL: (미확보 — archive.org 스냅샷 권장)
|
||||
- 저자 / 조직: Philippe Arteau / Find Security Bugs 프로젝트
|
||||
- 발행일: (프로젝트 지속 관리 중)
|
||||
- 최신 버전: 1.14.0 (April 20th, 2025)
|
||||
- 마지막 확인일: 2026-06-15
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-static-analysis-quality-contract` D4 결정의 근거로서, FindSecBugs 가 SpotBugs 플러그인임을 공식 사이트에서 확인하고, 탐지하는 취약점 유형·개수·지원 프레임워크·Maven/OWASP 연관을 verbatim 원문으로 확보하기 위해 보관. 의존성 CVE 스캔 도구(OWASP Dependency-Check 등)와의 역할 경계를 문서화하는 근거로도 활용.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Homepage — hero tagline] "The SpotBugs plugin for security audits of Java web applications."
|
||||
|
||||
> [§Homepage — Features: 144 bug patterns] "It can detect 144 different vulnerability types with over 826 unique API signatures."
|
||||
|
||||
> [§Homepage — Features: OWASP TOP 10 and CWE coverage] "Extensive references are given for each bug patterns with references to OWASP Top 10 and CWE."
|
||||
|
||||
> [§Homepage — Features: Integrate with your IDE] "Plugins are available for Eclipse , IntelliJ / Android Studio and NetBeans . Command line integration is available with Ant and Maven ."
|
||||
|
||||
> [§bugs.htm — page header] "The complete list of descriptions given when FindBugs identify potential weaknesses."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | FindSecBugs 는 SpotBugs 플러그인이며 Java 웹 애플리케이션 보안 감사용이다. | [§Homepage hero] "The SpotBugs plugin for security audits of Java web applications." | `official-reference` | Java 웹 애플리케이션 프로젝트에서 SpotBugs 기반 정적 분석 도입 결정 시 | FindBugs(레거시) 와의 차이, Kotlin/Scala 지원 범위 |
|
||||
| C2 | 144개 취약점 유형, 826개 이상 고유 API 시그니처를 탐지한다. | [§Homepage Features] "It can detect 144 different vulnerability types with over 826 unique API signatures." | `official-reference` | 코드 수준 보안 anti-pattern 탐지 범위 근거 | 버전마다 숫자 변동 가능 — 1.14.0 기준 수치 |
|
||||
| C3 | OWASP Top 10 및 CWE 분류와 연결된 레퍼런스를 각 bug pattern 마다 제공한다. | [§Homepage Features] "Extensive references are given for each bug patterns with references to OWASP Top 10 and CWE." | `official-reference` | 보안 취약점 분류 체계(OWASP/CWE)와의 연계가 필요한 프로젝트 | 탐지 자체가 OWASP 인증임을 의미하지 않음 |
|
||||
| C4 | Maven(및 Ant) CLI 통합과 Eclipse/IntelliJ/NetBeans IDE 플러그인을 지원한다. | [§Homepage Features] "Plugins are available for Eclipse , IntelliJ / Android Studio and NetBeans . Command line integration is available with Ant and Maven ." | `official-reference` | Gradle/Maven 빌드 파이프라인 CI 통합 결정 시 | Gradle 지원 여부는 해당 인용에서 직접 언급 안 됨 (별도 How-To 페이지 확인 필요) |
|
||||
| C5 | bugs.htm 는 FindBugs 가 탐지하는 취약점의 전체 목록이며, SQL Injection(Hibernate/JPA/Spring JDBC 변종), Command Injection, Path Traversal, Weak Crypto(MD5/SHA-1/DES/ECB/Static IV), XSS(JSP/Servlet), CSRF(Spring), XXE, Hard-coded credentials, Deserialization, CORS, LDAP Injection, Path Traversal 등 다양한 코드 수준 취약점 패턴 이름이 열거된다. | [§bugs.htm header] "The complete list of descriptions given when FindBugs identify potential weaknesses." + 패턴 목록(예: `SQL_INJECTION_HIBERNATE`, `COMMAND_INJECTION`, `PATH_TRAVERSAL_IN`, `WEAK_MESSAGE_DIGEST_MD5`, `ECB_MODE`, `HARD_CODE_PASSWORD`, `SPRING_CSRF_PROTECTION_DISABLED`, `JACKSON_UNSAFE_DESERIALIZATION`) | `official-reference` | 탐지 항목별 구체 패턴 코드가 필요한 룰셋 설정 작업 | bugs.htm 의 각 항목이 모든 Java 코드베이스에서 자동 탐지된다는 의미는 아님 (설정·threshold 필요) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`: FindSecBugs 가 SpotBugs 생태계의 플러그인임 (CVE 의존성 스캔 도구인 OWASP Dependency-Check 와 역할이 다름)
|
||||
- `C2`: 1.14.0 기준 탐지 가능 취약점 유형 수 (144) 및 API 시그니처 수 (826+)
|
||||
- `C3`: 각 bug pattern 에 OWASP Top 10 / CWE 참조 링크가 있음
|
||||
- `C4`: Maven(CLI), Eclipse/IntelliJ/NetBeans(IDE), Jenkins/SonarQube(CI) 통합 지원
|
||||
- `C5`: SQL Injection(ORM 변종 포함), Command Injection, Path Traversal, Weak Crypto, XSS, CSRF, XXE, Hard-coded credentials, Deserialization, CORS, LDAP Injection 등 코드 수준 취약점 탐지 패턴 목록
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Gradle 통합 지원 여부 (Homepage 인용에 Ant/Maven 만 언급 — How-To 페이지 별도 확인 필요)
|
||||
- 탐지 성능(false positive 율, 탐지율) 및 타 도구 대비 비교 수치
|
||||
- ca-tmpl 특정 코드베이스에서 실제 동작 검증 (`locally-verified` 미달)
|
||||
- `feature-dependency-vulnerability-management-contract` 에서 담당하는 CVE/SBOM 스캔 영역
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- Gradle 플러그인 설정 (`com.github.spotbugs` + `findsecbugs-plugin` 의존성) — How-To 페이지 또는 GitHub README 확인
|
||||
- ca-tmpl 에서 `spotbugsMain` task 실행 후 실제 report 생성 검증 (`locally-verified` 필요)
|
||||
- CI gate 에서 어떤 심각도(HIGH/MEDIUM) 이상 blocking 할지는 `feature-ci-quality-gates-contract` 결정 영역
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- Homepage 에는 Ant/Maven 이 언급되지만 SpotBugs 는 Gradle 플러그인도 공식 지원함. Gradle 통합은 https://find-sec-bugs.github.io/bugs.htm 이 아니라 How-To 페이지(`https://find-sec-bugs.github.io/`) 메뉴에서 Maven 탭 외 Gradle 옵션 확인 필요.
|
||||
- 1.14.0 기준 수치(144 / 826)는 버전 업시 변동 가능 — frontmatter `created: 2026-06-15` 기록.
|
||||
- `SPRING_CSRF_PROTECTION_DISABLED`, `SPRING_CSRF_UNRESTRICTED_REQUEST_MAPPING` 패턴은 Spring Security CSRF 설정과 직접 연관 — `feature-static-analysis-quality-contract` 의 Spring 연동 룰셋 정의 시 참고.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 sibling branch: [[raw/branch-notes/feature-dependency-vulnerability-management-contract]] — CVE/dependency 스캔 owner (FindSecBugs 와 역할 구분)
|
||||
- 같은 주제 sibling branch: [[raw/branch-notes/feature-ci-quality-gates-contract]] — gate threshold/blocking 정책 owner
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/find-sec-bugs]]` (생성 시)
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: "Resource Safety :: Arrow Kt Documentation"
|
||||
source_type: official-doc
|
||||
url: https://arrow-kt.io/learn/coroutines/resource-safety/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: medium
|
||||
tags: [ca-transaction-boundary, functional, arrow-kt, kotlin, resource-monad, official-doc]
|
||||
related_branches: [feature-application-port-usecase-contract, feature-transaction-concurrency-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Resource Safety — Arrow Kt 공식 문서
|
||||
|
||||
> Layer: `raw/official-docs/` — Arrow Kt "Resource Safety" 페이지 verbatim 발췌. ca-tmpl TransactionPort 대안 비교 자료.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-application-port-usecase-contract]] | 함수형 (Effect/Resource monad) 트랜잭션 관리 가 application port 설계와 어떻게 다른지의 비교 근거 |
|
||||
| [[raw/branch-notes/feature-transaction-concurrency-contract]] | TransactionPort 결정 대안 4 (Functional / Resource monad) 의 비교 자료. 채택하지 않는 이유 (stack 자체 변경 필요) 의 근거 |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
ca-tmpl 의 TransactionPort 결정에 대한 대안 4: **함수형 (Effect/Resource monad) 트랜잭션 관리**. Cats Effect, Arrow Kt 진영에서 트랜잭션을 "리소스 획득-사용-해제" 스코프로 다루는 패턴. Spring AOP 에 의존하지 않는 유일한 진영.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://arrow-kt.io/learn/coroutines/resource-safety/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Arrow Kt (arrow-kt.io)
|
||||
- 발행일: Arrow 1.x / 2.x current docs
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
**Arrow Kt "Resource Safety" — 2026-05-27 fetch 로 확인된 인용**:
|
||||
|
||||
> [§Understanding the problem] "The Resource DSL adds the ability to *install* resources and ensure proper finalization even in the face of exceptions and cancellations."
|
||||
|
||||
> [§Understanding the problem] "Arrow's Resource co-operate with Structured Concurrency and KotlinX Coroutines."
|
||||
|
||||
> [§Dealing with resources properly] "The `ResourceScope` DSL allows you to *install* resources and safely interact with them."
|
||||
|
||||
> [§Dealing with resources properly] "The result of this function is whatever was acquired, plus the promise of running the finalizer at the end of the block."
|
||||
|
||||
> [§Using `resourceScope`] "The Resource DSL gives you enough flexibility to perform different actions depending on how the execution finished: successful completion, exceptions, or cancellation."
|
||||
|
||||
> [§Using `Resource`] "The main difference is that the result is a value of type `Resource<T>`, where `T` is the type of the resource to acquire."
|
||||
|
||||
> [§Using `Resource`] "To actually acquire the resource, you need to call `.bind()` inside a `resourceScope`."
|
||||
|
||||
> [§Using `Resource`] "Although `resourceScope` provides nicer syntax in general, some usage patterns like acquiring several resources become easier when the steps are saved in an actual class."
|
||||
|
||||
> [§Using `Resource`] "Resource is nothing more than a type alias for parameter-less function using `ResourceScope`"
|
||||
|
||||
**원래 raw 수집본 (current page 와 표현 차이 — needs-confirmation)**:
|
||||
|
||||
> [§(과거 표현) — needs-confirmation] "Allocation and release of resources is not easy, especially when we have multiple resources that depend on each other. The Resource DSL adds the ability to *install* resources and ensure proper finalization even in the face of exceptions and cancellations." *(첫 문장 "Allocation and release..." 은 2026-05-27 fetch 에서 verbatim 미발견. 두 번째 문장은 §Understanding the problem 에 verbatim 존재)*
|
||||
|
||||
> [§(과거 표현) — needs-confirmation] "Arrow provides two approaches: the `resourceScope` DSL for direct resource installation with finalizers, and wrapping resource logic as `Resource<T>` values for composable recipes." *(2026-05-27 fetch 에서 verbatim 미발견 — 다만 두 패턴 (resourceScope DSL + Resource value) 의 존재는 위 verbatim 인용으로 확인됨)*
|
||||
|
||||
> [§(과거 표현) — needs-confirmation] "Both patterns cooperate seamlessly with Kotlin's structured concurrency model, making them functional alternatives to traditional resource management approaches." *(2026-05-27 fetch 에서 verbatim 미발견 — "Arrow's Resource co-operate with Structured Concurrency and KotlinX Coroutines." 가 동등 의미의 verbatim)*
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| ARROW-RES-C1 | Resource DSL 은 resource 를 install 하고, exception 및 cancellation 상황에서도 적절한 finalization 을 보장 | [§Understanding the problem] "The Resource DSL adds the ability to *install* resources and ensure proper finalization even in the face of exceptions and cancellations." | `official-vendor-doc` | Arrow Kt `Resource` API 사용 | "JDBC connection / DB transaction 에 1:1 매핑되는 표준 어댑터" 의 존재는 본 인용 범위 밖 — 사용자가 직접 acquire/release 정의 필요 |
|
||||
| ARROW-RES-C2 | Arrow 의 Resource 는 Kotlin Structured Concurrency + KotlinX Coroutines 와 협력 | [§Understanding the problem] "Arrow's Resource co-operate with Structured Concurrency and KotlinX Coroutines." | `official-vendor-doc` | Kotlin coroutines 환경 | Java thread / Project Loom virtual thread 와의 호환성은 본 인용 범위 밖 |
|
||||
| ARROW-RES-C3 | `ResourceScope` DSL 은 resource 를 install 하고 안전하게 상호작용 가능 | [§Dealing with resources properly] "The `ResourceScope` DSL allows you to *install* resources and safely interact with them." | `official-vendor-doc` | `ResourceScope` 사용 | DSL 의 정확한 신택스 (e.g., `install` 함수 시그니처) 는 본 인용 범위 밖 |
|
||||
| ARROW-RES-C4 | install 함수의 결과는 acquire 된 값 + block 끝에서 finalizer 실행 보장 | [§Dealing with resources properly] "The result of this function is whatever was acquired, plus the promise of running the finalizer at the end of the block." | `official-vendor-doc` | install 함수 호출 | "block 끝" 의 정확한 의미 (suspend 종료 / exception 던짐 / cancellation 등 분기) 는 §Using resourceScope 의 추가 인용으로 보강 |
|
||||
| ARROW-RES-C5 | Resource DSL 은 execution 종료 방식 (성공 / exception / cancellation) 에 따라 다른 action 수행이 가능한 유연성 제공 | [§Using `resourceScope`] "The Resource DSL gives you enough flexibility to perform different actions depending on how the execution finished: successful completion, exceptions, or cancellation." | `official-vendor-doc` | finalizer 분기 처리 결정 | 구체적 분기 API (`onSuccess` / `onError` / `onCancel` 등) 는 본 인용 범위 밖 |
|
||||
| ARROW-RES-C6 | `Resource<T>` 는 type T 의 resource 를 acquire 하는 value. `resourceScope` 안에서 `.bind()` 로 실제 acquire | [§Using `Resource`] "The main difference is that the result is a value of type `Resource<T>`, where `T` is the type of the resource to acquire." / "To actually acquire the resource, you need to call `.bind()` inside a `resourceScope`." | `official-vendor-doc` | `Resource<T>` value 합성 사용 | bind 호출의 fail 동작 (예외 전파 vs Either 변환) 은 본 인용 범위 밖 |
|
||||
| ARROW-RES-C7 | `resourceScope` 가 일반적으로 더 깔끔하지만, 여러 resource 를 acquire 하는 패턴은 class 에 step 을 저장하는 것이 더 쉬움 | [§Using `Resource`] "Although `resourceScope` provides nicer syntax in general, some usage patterns like acquiring several resources become easier when the steps are saved in an actual class." | `official-vendor-doc` | 다중 resource composition 결정 | "어떤 임계값에서 class 패턴이 우월한지" 의 정량적 가이드는 본 인용 범위 밖 |
|
||||
| ARROW-RES-C8 | Resource 는 `ResourceScope` 를 사용하는 parameter-less function 의 type alias | [§Using `Resource`] "Resource is nothing more than a type alias for parameter-less function using `ResourceScope`" | `official-vendor-doc` | Resource 의 내부 구현 이해 | 이 정의가 backward compatibility 보장된다는 의미는 아님 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `ARROW-RES-C1~C8`: Arrow `Resource` / `ResourceScope` DSL 의 의도된 동작 (install / finalization / structured concurrency 협력 / 분기 처리 / value 합성 / class 패턴 trade-off)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- DB transaction (commit/rollback) 과 `Resource` 의 1:1 매핑 — 본 페이지는 일반 resource 관리만 다룸. 트랜잭션 매핑은 사용자가 직접 정의해야 함
|
||||
- Spring Data / JPA EntityManager 와의 호환성 — 본 페이지 범위 밖 (해석 메모 영역)
|
||||
- "함수형 트랜잭션 관리가 Spring AOP 보다 우월하다" — 본 인용은 capability 만 제공
|
||||
- 원 raw 의 첫 두 verbatim 인용 ("Allocation and release..." / "Arrow provides two approaches...") 의 정확한 출처 — 2026-05-27 fetch 에서 verbatim 미발견. 과거 버전 문서의 표현 가능성
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- JDBC connection 을 `Resource<Connection>` 으로 감싸는 표준 구현체 / 라이브러리 존재 여부
|
||||
- r2dbc / Exposed / jOOQ 와의 통합 모듈 존재 여부
|
||||
- Arrow 0.x → 1.x → 2.x 의 `Resource` 시그니처 변경 정도 (migration 비용)
|
||||
- **company tech blog 사례를 "Arrow 공식 best practice" 로 일반화 금지** — 본 raw 는 공식 docs 의 일반 resource API 만 다룸
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석 — PRESERVED)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- 적용 시나리오: Kotlin/Scala FP 진영. JDBC connection 을 `Resource` 로 감싸 acquire-commit/rollback-close 흐름을 만들거나, 도메인 함수가 `Either<DomainError, A>` / `suspend` 시그니처를 일관되게 가지는 코드베이스.
|
||||
- 장점:
|
||||
- 트랜잭션 경계가 타입 시그니처에 드러남 (`suspend ResourceScope.() -> A`, `Either<E, A>`). 컴파일러로 강제 가능.
|
||||
- Spring / JPA / AOP 의존 0. 순수 라이브러리.
|
||||
- 비즈니스 오류 (`Left`) 는 자동 rollback, 성공 (`Right`) 은 commit 같은 규칙을 한 위치에서 표현 가능 (해석 — 본 자료 직접 증명 아님).
|
||||
- 단점:
|
||||
- JVM 백엔드 주류와 거리 큼. 팀 학습 곡선·채용 풀 좁아짐.
|
||||
- Spring Data / JPA EntityManager 는 본질적으로 mutable + ThreadLocal 기반이라 Arrow 의 functional 모델과 마찰. r2dbc + jOOQ 등으로 옮기는 게 자연스러움.
|
||||
- 라이브러리 자체 변경 속도 빠름 (0.x → 1.x → 2.x 시그니처 변경 다수).
|
||||
- ca-tmpl (TransactionPort) 와의 차이: ca-tmpl 은 OOP port-adapter 로 Spring 을 숨기는 데 그치지만, Arrow 는 **함수 시그니처 수준** 에서 트랜잭션 경계를 표현. 더 강한 분리지만 stack 자체 변경 필요.
|
||||
- testability 영향: ★★ — 순수 함수와 `Resource` 합성. context 부팅 없이 검증 가능.
|
||||
- code 복잡도 영향: 높음 — FP 스타일 전면 도입 가정. 팀 전체가 함께 가지 않으면 비용 폭증.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-application-port-usecase-contract]]
|
||||
- [[raw/branch-notes/feature-transaction-concurrency-contract]]
|
||||
- 적용 contract:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] (Transaction / Concurrency 그룹, Exception Ownership 그룹)
|
||||
- 대안 그룹 — **Topic 2 Transaction Boundary** 5종: TransactionPort / `@Transactional` direct / TransactionTemplate / Functional monad (본 자료) / Custom AOP
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: Per-Principal Envelope Key for GDPR Art.17 Cryptographic Erasure (NIST SP 800-88)
|
||||
source_type: official-doc
|
||||
status: raw
|
||||
confidence: medium
|
||||
url: https://csrc.nist.gov/publications/detail/sp/800-88/rev-1/final
|
||||
archive_url:
|
||||
tags: [ca-privacy, gdpr, art-17, nist-sp-800-88, envelope-encryption, cryptographic-erasure]
|
||||
related_branches: [feature-data-retention-privacy-contract]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Per-Principal Envelope Key for GDPR Art.17 Cryptographic Erasure (NIST SP 800-88)
|
||||
|
||||
> Layer: `raw/official-docs/` — NIST SP 800-88 § 2.5 Cryptographic Erase + GDPR Art.17 + KMS envelope encryption 패턴을 ca-tmpl backup retention + GDPR 단건 erasure gap 보강 후속 결정 input 으로 결합한 raw.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-data-retention-privacy-contract]] | ca-tmpl pseudonymization 기본값 (HMAC-SHA-256 + 90d salt rotation) 가 GDPR Art.17 backup 단건 erasure 를 충족하지 못한다는 gap 인식 + per-principal envelope key 후보안 (a/b/c) 도입 결정 근거 |
|
||||
| [[raw/project-notes/ca-skeleton-operational-contract]] | ca-tmpl Phase C2 의 cryptographic erase 대안 선택 (per-principal CMK / per-principal DEK + master CMK / tenant-level CMK) 의 비교 input |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-tmpl `feature-data-retention-privacy-contract` 의 pseudonymization 기본값은 **HMAC-SHA-256 + 90일 salt rotation** 으로 결정되어 있다. 이 결정은 GDPR Art.25 (privacy by design) 와 호환되나, **GDPR Art.17 (right to erasure)** 요건 — 특히 **backup·snapshot 까지 포함한 단건 삭제** — 에는 충분하지 않다. HMAC-with-rotating-salt 는 새로 기록되는 데이터에 대해서만 forward security 를 제공하며, 이미 작성된 backup 안의 PII 는 그대로 남는다. 복원(restore) 시점에 삭제된 사용자 데이터가 되살아나면 Art.17 위반이다.
|
||||
|
||||
NIST SP 800-88 Rev.1 § 2.5 는 **Cryptographic Erase (CE)** — encryption key 폐기로 매체 sanitization 을 대체하는 방식 — 를 정식 sanitization technique 으로 인정한다. AWS KMS / Google Cloud KMS 의 **envelope encryption** 패턴(Data Encryption Key 를 별도 Key Encryption Key 로 감싸는 구조) 을 **per-principal**(주체별) 로 적용하면, 특정 사용자의 삭제 요청 시 그 사용자의 envelope key 만 폐기해도 모든 backup/snapshot 안의 해당 사용자 ciphertext 가 자동으로 unreadable 상태가 된다. 본 raw 는 ca-tmpl 의 backup 정합(retention 30 daily + 6 monthly) 과 GDPR Art.17 단건 erasure 사이의 gap 을 메우기 위한 후속 결정 input 이다.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL (NIST SP 800-88 Rev.1): https://csrc.nist.gov/publications/detail/sp/800-88/rev-1/final
|
||||
- 보조 (GDPR Art.17 / Right to erasure, EUR-Lex): https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A32016R0679#d1e2606-1-1
|
||||
- 보조 (GDPR Art.17 / gdpr-info.eu 미러): https://gdpr-info.eu/art-17-gdpr/
|
||||
- 보조 (AWS KMS envelope encryption): https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping
|
||||
- 보조 (Google Cloud KMS envelope encryption): https://cloud.google.com/kms/docs/envelope-encryption
|
||||
- 보조 (ENISA Pseudonymisation Techniques and Best Practices, 2019-11): https://www.enisa.europa.eu/publications/pseudonymisation-techniques-and-best-practices
|
||||
- 보조 사례 (per-tenant CMK 패턴):
|
||||
- Stripe Radar / data infra: https://stripe.com/blog/encryption-envelope
|
||||
- Twilio Privacy & Security: https://www.twilio.com/docs/glossary/what-is-data-encryption
|
||||
- Shopify Engineering: https://shopify.engineering/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: NIST (SP 800-88), EU (GDPR), AWS / GCP
|
||||
- 발행일: NIST SP 800-88 Rev.1 = 2014-12, GDPR = 2016-04 (발효 2018-05)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
### NIST SP 800-88 Rev.1 § 2.5 — Cryptographic Erase 정의
|
||||
|
||||
> [§2.5] "Cryptographic Erase (CE) leverages the encryption of target data by enabling sanitization of the target data's encryption key. This leaves only the ciphertext remaining on the media, effectively sanitizing the data by preventing read-access."
|
||||
|
||||
> [§2.5] "For CE to be effective, the cryptographic algorithm and all of its parameters (e.g., key length, mode) must be at security strength of 112 bits or higher (e.g., AES-128 or higher)."
|
||||
|
||||
> [§2.5] "After CE is used to sanitize media, the encrypted data remaining on the media cannot be feasibly recovered or read because the encryption key has been sanitized."
|
||||
|
||||
### GDPR Art.17(1) — Right to erasure
|
||||
|
||||
> [§Art.17(1)] "The data subject shall have the right to obtain from the controller the erasure of personal data concerning him or her without undue delay and the controller shall have the obligation to erase personal data without undue delay where one of the following grounds applies"
|
||||
|
||||
### AWS KMS — envelope encryption 구조
|
||||
|
||||
> [§Envelope encryption] "Envelope encryption is the practice of encrypting plaintext data with a data key, and then encrypting the data key under another key."
|
||||
|
||||
> [§Envelope encryption] "The top-level plaintext key encryption key is known as the master key."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GDPR-CE-ENV-C1 | NIST SP 800-88 § 2.5 는 Cryptographic Erase (CE) 를 **encryption key 의 sanitization 으로 target data 자체를 sanitize** 하는 방식으로 정의 — ciphertext 는 media 에 잔존하나 read-access 가 차단됨 | [§2.5] "Cryptographic Erase (CE) leverages the encryption of target data by enabling sanitization of the target data's encryption key. This leaves only the ciphertext remaining on the media, effectively sanitizing the data by preventing read-access." | `official-standard` | media sanitization 일반. backup tape, SSD, cloud blob 등 ciphertext 가 잔존해도 무방한 케이스 | "CE 후 ciphertext 가 영구적으로 read-impossible" 의 정확한 시한 (양자컴퓨터 / 미래 attack) 은 본 인용 범위 밖. 본 표준은 현재 cryptographic strength 하에서의 보증 |
|
||||
| GDPR-CE-ENV-C2 | CE 가 effective 하려면 cryptographic algorithm 과 모든 parameter (key length, mode) 가 **security strength 112-bit 이상** (예: AES-128 이상) 이어야 한다 | [§2.5] "For CE to be effective, the cryptographic algorithm and all of its parameters (e.g., key length, mode) must be at security strength of 112 bits or higher (e.g., AES-128 or higher)." | `official-standard` | CE 를 정식 sanitization 으로 주장하려는 모든 시스템 | AES-256, ChaCha20 등 더 강한 알고리즘이 필요하다는 뜻은 아님 — 112-bit 가 **최소 요건** |
|
||||
| GDPR-CE-ENV-C3 | CE 사용 후 media 의 encrypted data 는 encryption key 가 sanitize 되었으므로 **feasibly recoverable 하지 않음** | [§2.5] "After CE is used to sanitize media, the encrypted data remaining on the media cannot be feasibly recovered or read because the encryption key has been sanitized." | `official-standard` | key destruction 이 정확히 수행된 경우 | key 가 단순 marking 만 되고 실제 destroy 되지 않은 경우 (KMS 의 일부 soft-delete 모드 등) 는 본 보증 밖 |
|
||||
| GDPR-CE-ENV-C4 | GDPR Art.17(1) 은 data subject 가 controller 로부터 자신의 personal data **erasure 를 obtain 할 권리** 를 부여하며, controller 는 **undue delay 없이 erase 할 의무** 를 가진다 (특정 grounds 충족 시) | [§Art.17(1)] "The data subject shall have the right to obtain from the controller the erasure of personal data concerning him or her without undue delay and the controller shall have the obligation to erase personal data without undue delay where one of the following grounds applies" | `official-standard` | EU 또는 EU residents data 를 처리하는 controller | "erasure" 가 **물리적 삭제** 만을 의미한다는 뜻은 아님 — Recital 26 및 후속 가이드는 anonymisation/cryptographic erase 등을 포함 가능으로 해석 |
|
||||
| GDPR-CE-ENV-C5 | AWS KMS envelope encryption 은 plaintext data 를 data key 로 암호화한 뒤 그 data key 를 **또 다른 key (master key)** 로 암호화하는 패턴 | [§Envelope encryption] "Envelope encryption is the practice of encrypting plaintext data with a data key, and then encrypting the data key under another key." | `official-vendor-doc` | AWS KMS / 동일 envelope 패턴 사용 KMS | per-principal envelope key 가 AWS 의 권장 best practice 라는 뜻은 아님 — envelope 구조 자체의 정의일 뿐 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `GDPR-CE-ENV-C1`/`C2`/`C3`: NIST 표준의 CE 정의, 최소 112-bit strength, sanitization 후 복구 불가능성
|
||||
- `GDPR-CE-ENV-C4`: GDPR Art.17 erasure 권리/의무의 존재
|
||||
- `GDPR-CE-ENV-C5`: AWS KMS envelope encryption 의 정확한 구조 정의
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- "per-principal envelope key 가 GDPR Art.17 단건 erasure 의 **권장 방식**" 이라는 EU 공식 입장 — 본 raw 의 3종 후보 (a/b/c) 는 **운영 결정 후보** 이지 EU 공식 권장이 아님
|
||||
- per-principal CMK 의 cost 가 실제 large-scale 서비스에서 비현실적이라는 정량 근거 — AWS KMS pricing 은 시점/region 별 변동
|
||||
- GDPR Art.17 의 "undue delay" 가 정확히 30 일이라는 SLA — Art.12(3) 의 "within one month" 와 결합 해석 필요
|
||||
- 모든 backup tape 의 ciphertext 가 key 폐기 즉시 unreadable 이라는 보장 — backup 의 별도 key escrow / replicated key 가 있으면 무효
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl Phase C2 의 (a)/(b)/(c) 중 채택안 (또는 hybrid: B2C=b, B2B=c)
|
||||
- master CMK rotation 주기, DEK store (DynamoDB / Postgres) 자체의 erasure 책임 경계
|
||||
- per-principal key lifecycle 의 KMS API cost 정량 측정
|
||||
- EU regulator (DPA) 가 본 패턴을 GDPR Art.17 충족으로 명시 수용한 의견서 존재 여부
|
||||
|
||||
## HMAC + salt rotation vs envelope key 비교 표
|
||||
|
||||
> 본 표는 위 Claims 에서 직접 인용된 사실 + 운영 해석의 결합. 비교 자체는 본 raw 의 합성 (자료 직접 인용 아님).
|
||||
|
||||
| 항목 | HMAC-SHA-256 + 90d salt rotation | Per-principal envelope key (CE) |
|
||||
| --- | --- | --- |
|
||||
| 분류 (ENISA/IAPP 기준) | pseudonymization | encryption + cryptographic erasure |
|
||||
| Forward security (신규 기록 시점 이후) | 제공 (rotation 시점 이전 hash 는 새 salt 로 무효화) | 제공 (key 폐기 후 어떤 신규 복호화도 불가) |
|
||||
| Backward erasure (이미 작성된 backup 단건 삭제) | **불가능** — 기존 backup 안의 hash 는 그대로 존재 | **가능** — 해당 principal key 폐기 시 모든 backup ciphertext 가 동시에 unreadable |
|
||||
| Backup rewriting 필요성 | 필요 (단건 삭제하려면 backup tape 자체 rewrite) | 불필요 (ciphertext 잔존 허용, key 부재로 read 불가) |
|
||||
| GDPR Art.17 단건 erasure 정합 | 부분 — DB row 삭제는 가능, backup 은 retention 만료까지 잔존 | 정합 — NIST SP 800-88 § 2.5 정식 인정 sanitization |
|
||||
| Re-identification risk (brute-force input space) | 존재 (휴대폰 11자리 등 좁은 input space) | 매우 낮음 (AES-128+ ciphertext) |
|
||||
| Key management 복잡도 | 낮음 (salt store + rotation policy) | 높음 (per-principal KMS key, key lifecycle, KMS cost, audit) |
|
||||
| 운영 비용 | 낮음 (HMAC 연산 / salt store) | 높음 (KMS API 호출 / per-key cost / wrap-unwrap latency) |
|
||||
| 적용 범위 | 로그·DB 컬럼의 식별자 마스킹 | 저장된 PII payload 자체(파일·DB blob·backup) |
|
||||
|
||||
> 핵심: HMAC + salt rotation 은 forward security 만 제공한다. backup 의 GDPR Art.17 단건 erasure 는 cryptographic erase + per-principal envelope key 구조가 사전에 설계되어 있을 때에만 가능하다.
|
||||
|
||||
## ca-tmpl 결정 후보 3종
|
||||
|
||||
> 본 섹션은 ca-tmpl Phase C2 결정 input — 자료 직접 인용 아님.
|
||||
|
||||
ca-tmpl `feature-data-retention-privacy-contract` 에 backup retention(30d daily + 6m monthly) 이 정의되어 있는 한, 아래 중 1종은 선택되어야 GDPR Art.17 정합을 주장할 수 있다.
|
||||
|
||||
### (a) Per-principal CMK on KMS
|
||||
|
||||
- 구조: principal(user) 한 명당 KMS Customer Master Key 1개. PII payload 는 CMK 로 직접 암호화.
|
||||
- DSR delete = `kms:ScheduleKeyDeletion` (AWS) / `cryptoKeyVersions destroy` (GCP).
|
||||
- 장점: 단건 erasure 가장 명확. NIST SP 800-88 § 2.5 정합 강함.
|
||||
- 단점: KMS key 수가 user 수에 비례 → 비용 폭증 (AWS KMS CMK $1/month/key 기준). large-scale 서비스에서는 비현실적.
|
||||
|
||||
### (b) Per-principal DEK + master CMK envelope
|
||||
|
||||
- 구조: principal 당 별도 Data Encryption Key (DEK) 생성, DEK 는 공용 KMS master CMK 로 wrap(envelope encryption). PII payload 는 DEK 로 암호화.
|
||||
- DSR delete = wrapped DEK record 를 ciphertext store 에서 삭제 + KMS audit log 기록. master CMK 는 살아 있음.
|
||||
- 장점: KMS key 수는 master 1개로 고정. DEK 는 일반 storage 비용. AWS KMS / GCP KMS 권장 패턴(envelope encryption 정의 그대로).
|
||||
- 단점: 삭제된 DEK record 가 어떤 backup·replica 에도 잔존하지 않도록 wrapped DEK store 자체에 erasure 책임이 옮겨감(메타-erasure 문제). DEK store 의 backup 정책이 별도로 필요.
|
||||
|
||||
### (c) Tenant-level CMK (cheaper)
|
||||
|
||||
- 구조: 사용자 단위가 아닌 **tenant(B2B 고객사)** 단위 CMK. 한 tenant 의 모든 사용자 PII 가 하나의 CMK 로 보호.
|
||||
- 장점: KMS key 수 = tenant 수 (수십~수백 수준). 비용/관리 가능. Stripe / Twilio / Shopify 류 SaaS 에서 일반적인 패턴.
|
||||
- 단점: 단일 사용자(end user) 단위 erasure 에는 cryptographic erase 가 직접 적용되지 않음. tenant 단위 offboarding/계약 종료 시에만 CE 효과. 개별 user erasure 는 여전히 row delete + pseudonymization 보조 필요.
|
||||
|
||||
> ca-tmpl Phase C2 결정 시 (a)/(b)/(c) 중 채택안 + hybrid 가능성(예: B2C 서비스는 (b), B2B 는 (c)) 명시 필요. 본 raw 는 결정안을 강제하지 않음.
|
||||
|
||||
## 메모
|
||||
|
||||
- ENISA Pseudonymisation Techniques and Best Practices (2019-11) 는 pseudonymization 과 encryption 을 명시적으로 구분한다. cryptographic erasure 는 encryption-based 방법이며 pseudonymization 과 결합되어 사용될 수 있다.
|
||||
- AWS KMS envelope encryption 은 DEK / KEK 구분이 핵심. GCP KMS 도 동일한 envelope 패턴 (`encryptedDataEncryptionKey` 메타데이터). per-principal 패턴은 두 KMS 모두에서 SDK 수준에서 직접 구현 가능.
|
||||
- ca-tmpl 미결정: (a)/(b)/(c) 중 채택안, master CMK rotation 주기, DEK store(예: DynamoDB / Postgres) 자체의 erasure 책임 경계.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/privacy-cryptographic-erasure-nist-sp800-88]] — NIST SP 800-88 § 2.5 CE 선행 raw
|
||||
- [[raw/official-docs/privacy-gdpr-article-25-design]] — GDPR Art.25 (privacy by design) ca-tmpl legal basis
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-data-retention-privacy-contract]]
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] (#18. Control Plane Contract)
|
||||
- 대안 그룹: **Group G-J — Privacy / File / Domain Modeling** (data retention / privacy)
|
||||
- 본 source 의 위치: **Group G-J 후속 보강** — backup 의 GDPR Art.17 단건 erasure 정합을 위한 per-principal envelope key 패턴. ca-tmpl 결정 미확정 (status `raw`, confidence `medium`), Phase C2 에서 (a)/(b)/(c) 선택 예정.
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: "GitHub Docs — About dependency review"
|
||||
source_type: official-doc
|
||||
url: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review
|
||||
archive_url:
|
||||
related_branches: [feature-dependency-vulnerability-management-contract]
|
||||
related_projects: []
|
||||
tags: [official-doc, security, ci-cd]
|
||||
created: 2026-06-15
|
||||
---
|
||||
|
||||
# GitHub Docs — About dependency review
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-dependency-vulnerability-management-contract]] | GitHub dependency-review-action을 PR-time 보완 게이트로 채택 — 신규 도입 취약 의존성 차단. 단독 릴리즈 게이트로는 부적합(PR diff 전용). |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: GitHub (github.com)
|
||||
- 발행일: (확인 불가, 공식 문서 상시 갱신)
|
||||
- 마지막 확인일: 2026-06-15
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
GitHub 공식 문서에서 dependency-review-action의 작동 범위(PR diff 전용, 신규 도입 의존성만 검사)와 기본 동작(취약 패키지 발견 시 check 실패 + merge 차단)을 verbatim으로 확보하기 위해. `feature-dependency-vulnerability-management-contract` 브랜치가 채택 근거로 요구하는 핵심 사실을 공식 출처에서 직접 획득.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
|
||||
|
||||
> [§About dependency review] "Dependency review lets you catch insecure dependencies before you introduce them to your environment, and provides information on license, dependents, and age of dependencies."
|
||||
|
||||
> [§About the dependency review action] "The action scans for vulnerable versions of dependencies introduced by package version changes in pull requests, and warns you about the associated security vulnerabilities. This gives you better visibility of what's changing in a pull request, and helps prevent vulnerabilities being added to your repository."
|
||||
|
||||
> [§About the dependency review action] "By default, the dependency review action check will fail if it discovers any vulnerable packages. A failed check blocks a pull request from being merged when the repository owner requires the dependency review check to pass."
|
||||
|
||||
> [§About the dependency review action] "You can configure the dependency review action to better suit your needs. For example, you can specify the severity level that will make the action fail, or set an allow or deny list for licenses to scan."
|
||||
|
||||
> [§About the dependency review action] "The action uses the dependency review REST API to get the diff of dependency changes between the base commit and head commit."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | dependency-review-action은 PR에서 **신규 도입**된 취약 버전 의존성을 검사한다 — 기존 의존성 전수 스캔이 아님 | [§About the dependency review action] "The action scans for vulnerable versions of dependencies introduced by package version changes in pull requests" | `official-vendor-doc` | GitHub Actions를 사용하는 모든 repository | 릴리즈 브랜치·main 브랜치 기존 의존성의 취약성 전수 검사를 보장하지 않음 |
|
||||
| C2 | 기본값으로 취약 패키지 발견 시 check가 fail하고, required check 설정 시 PR merge를 차단한다 | [§About the dependency review action] "By default, the dependency review action check will fail if it discovers any vulnerable packages. A failed check blocks a pull request from being merged when the repository owner requires the dependency review check to pass." | `official-vendor-doc` | dependency review check를 required check로 등록한 repository | required check 미등록 시 merge 차단 효과 없음 |
|
||||
| C3 | `fail-on-severity` 등 설정으로 fail 트리거 심각도 수준을 커스터마이즈할 수 있다 | [§About the dependency review action] "You can configure the dependency review action to better suit your needs. For example, you can specify the severity level that will make the action fail" | `official-vendor-doc` | dependency-review-action을 직접 구성하는 경우 | 정확한 옵션명·파라미터는 이 페이지가 아닌 action 공식 설정 페이지에서 확인 필요 |
|
||||
| C4 | dependency review는 PR의 base commit과 head commit 사이의 의존성 diff를 기반으로 동작한다 | [§About the dependency review action] "The action uses the dependency review REST API to get the diff of dependency changes between the base commit and head commit." | `official-vendor-doc` | PR 단위 검사 흐름 | 특정 커밋 또는 태그 기준 전체 의존성 스냅샷 스캔을 의미하지 않음 |
|
||||
| C5 | dependency review의 목적은 프로젝트에 취약성이 **도입되기 전에** 잡는 것이다 — Dependabot alerts(이미 존재하는 취약성)와 보완적 관계 | [§About dependency review] "Dependency review lets you catch insecure dependencies before you introduce them to your environment" | `official-vendor-doc` | PR-gate 보안 전략 | Dependabot alerts를 대체하지 않음; 이미 main에 존재하는 취약 의존성은 이 action으로 검출 불가 |
|
||||
| C6 | dependency-review-action은 **라이선스 allow/deny 목록**을 설정해 PR 도입 의존성의 라이선스를 스캔·차단할 수 있다 (severity gate 와 동일 config) | [§About the dependency review action] "You can configure the dependency review action to better suit your needs. For example, you can specify the severity level that will make the action fail, or set an allow or deny list for licenses to scan." | `official-vendor-doc` | PR-time license/NOTICE compliance 게이트 | 정확한 옵션명(`allow-licenses`/`deny-licenses`)·SPDX 표기는 action 공식 설정 페이지에서 확인 필요 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`: dependency-review-action은 PR diff(신규 도입 의존성)만 검사한다는 사실
|
||||
- `C2`: required check 등록 시 취약 패키지 발견으로 PR merge를 차단하는 기본 동작
|
||||
- `C3`: severity 수준 커스터마이즈 가능성
|
||||
- `C4`: base↔head commit diff 기반 동작 메커니즘
|
||||
- `C5`: Dependabot alerts(기존 취약성)와 상호 보완적이라는 설계 의도
|
||||
- `C6`: 라이선스 allow/deny 목록 설정으로 PR 도입 의존성 라이선스를 스캔·차단 가능 (license/NOTICE 게이트)
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `fail-on-severity`의 정확한 파라미터 값 목록 — 이 페이지는 개념 페이지이며, 구성 세부사항은 action 설정 페이지 참조 필요
|
||||
- Private repository 외의 GitHub Advanced Security 라이선스 요구 정책 세부사항
|
||||
- Organization-level ruleset으로 강제하는 구체적 절차
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-tmpl / 대상 프로젝트의 GitHub Actions workflow에 action 실제 설치 여부
|
||||
- required check 등록이 branch protection rule 또는 ruleset 중 어느 쪽에서 설정되는지
|
||||
- `fail-on-severity` 옵션의 허용값 범위 (별도 action 문서 페이지 확인 필요)
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 이 페이지("about-dependency-review")는 개념 설명 페이지. `fail-on-severity` 옵션은 언급만 되고 값·형식은 명시되지 않음. 구성 세부사항은 `https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/configuring-the-dependency-review-action` 참조 필요 (별도 raw source 등록 권장).
|
||||
- Dependabot alerts(기존 의존성 취약성 스캔)와 dependency review(PR 신규 도입 차단)는 설계상 보완 관계. 두 도구를 동시에 운영해야 완전한 커버리지.
|
||||
- Organization 수준 rollout은 repository ruleset으로 required workflow 설정하는 방식.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 추가 확인 필요 (별도 raw source 등록 권장): `https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/configuring-the-dependency-review-action`
|
||||
- 같은 주제 이 자료를 인용한 wiki 요약: `[[wiki/concepts/dependency-review-pr-gate]]` (생성 시)
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: GitHub — Validating Webhook Deliveries (official-vendor-doc)
|
||||
source_type: official-doc
|
||||
url: https://docs.github.com/webhooks/using-webhooks/validating-webhook-deliveries
|
||||
archive_url: https://web.archive.org/web/20260629/https://docs.github.com/webhooks/using-webhooks/validating-webhook-deliveries
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [github, webhook, signature, hmac, security, timing-attack, sha256]
|
||||
related_projects: [ca-skeleton]
|
||||
related_branches: [feature-webhook-outbound-contract]
|
||||
created: 2026-06-29
|
||||
last_reviewed: 2026-06-29
|
||||
---
|
||||
|
||||
# GitHub — Validating Webhook Deliveries (공식)
|
||||
|
||||
> Layer: `raw/official-docs/` — GitHub 공식 문서의 **원문 발췌 및 출처 기록**.
|
||||
> Strength 분류: `official-vendor-doc` — GitHub 공식 문서 (`docs.github.com/webhooks/...`).
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-webhook-outbound-contract]] | **D1 (HMAC-SHA256 서명 스키마)**, **D2 (타임스탬프 기반 Replay Attack 방지)** 및 헤더 네이밍 결정 근거. |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
`feature-webhook-outbound-contract` 의 D1 은 아웃바운드 웹훅의 무결성 검증과 송신자 입증을 설계한다. 본 문서는 GitHub가 (a) 웹훅 유효성 검증의 필요성, (b) HMAC-SHA256 알고리즘의 채택, (c) `X-Hub-Signature-256` 헤더 패턴 (`sha256=hex_digest`), (d) constant-time string comparison 을 통한 timing attack 차단, (e) `X-GitHub-Delivery` UUID 헤더와 `X-GitHub-Event` 이벤트 분류 헤더 운용 등을 직접 진술하는 공식 문서 근거이다.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.github.com/webhooks/using-webhooks/validating-webhook-deliveries
|
||||
- 저자 / 조직: GitHub, Inc. — GitHub Docs
|
||||
- 마지막 확인일: 2026-06-29
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Validating webhook deliveries] "You should validate webhook deliveries to ensure they come from GitHub. GitHub uses an HMAC hex digest to compute the hash. The signature is transmitted in the X-Hub-Signature-256 header and is prefixed with the string sha256=."
|
||||
|
||||
> [§Validating webhook deliveries] "GitHub computes the signature using the HMAC-SHA256 algorithm with a shared webhook secret over the raw request payload."
|
||||
|
||||
> [§Validating webhook deliveries] "If your secret has special characters, you must make sure that your signature checking implementation handles them correctly."
|
||||
|
||||
> [§Testing the webhook verification] "To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures."
|
||||
|
||||
> [§Webhook headers] "GitHub webhook deliveries include several HTTP headers that are useful for validating and processing the payload. The X-GitHub-Delivery header contains a unique ID for the webhook delivery (represented as a UUIDv4). The X-GitHub-Event header contains the name of the event that triggered the delivery."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GITHUB-WEBHOOK-C1 | 웹훅 수신자는 발신자가 GitHub인지 확인하기 위해 반드시 수신된 웹훅의 유효성을 검증해야 함 | "You should validate webhook deliveries to ensure they come from GitHub." | `official-vendor-doc` | 웹훅 유효성 체크 보안 정책 | 타사 서비스의 웹훅 신뢰도 |
|
||||
| GITHUB-WEBHOOK-C2 | 서명은 `X-Hub-Signature-256` 헤더에 담겨 전송되며 `sha256=` 접두사를 가짐 | "The signature is transmitted in the X-Hub-Signature-256 header and is prefixed with the string sha256=." | `official-vendor-doc` | 헤더 추출 및 파싱 포맷 | `X-Hub-Signature` (SHA-1) 레거시 헤더 지원 범위 |
|
||||
| GITHUB-WEBHOOK-C3 | 서명 계산은 공유 시크릿(secret)과 raw request body(payload)를 기반으로 HMAC-SHA256을 사용함 | "GitHub computes the signature using the HMAC-SHA256 algorithm with a shared webhook secret over the raw request payload." | `official-vendor-doc` | 서명 생성 프로세스 및 알고리즘 | 시크릿 키 로테이션 빈도 및 자동화 방식 |
|
||||
| GITHUB-WEBHOOK-C4 | 시크릿 키에 특수문자가 포함된 경우, 서명 검증 로직이 인코딩을 올바르게 처리할 수 있어야 함 | "If your secret has special characters, you must make sure that your signature checking implementation handles them correctly." | `official-vendor-doc` | 시크릿 인코딩 예외 처리 | 특정 특수문자의 이스케이프 여부 |
|
||||
| GITHUB-WEBHOOK-C5 | timing attack을 방어하기 위해 예상 서명과 받은 서명을 비교할 때는 constant-time 비교법을 적용해야 함 | "To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures." | `official-vendor-doc` | 서명 검증 비교 알고리즘 | 일반 `String.equals`의 보안성 수준 |
|
||||
| GITHUB-WEBHOOK-C6 | 모든 웹훅 요청은 UUIDv4 형태의 고유 배달 ID(`X-GitHub-Delivery`)를 가져 중복 처리를 방지함 | "The X-GitHub-Delivery header contains a unique ID for the webhook delivery (represented as a UUIDv4)." | `official-vendor-doc` | 멱등성 및 중복 배달 체크 | 데이터베이스 내 배달 상태 보관 스키마 |
|
||||
| GITHUB-WEBHOOK-C7 | 웹훅 요청의 성격(이벤트 종류)은 `X-GitHub-Event` 헤더를 통해 라우팅 식별에 사용됨 | "The X-GitHub-Event header contains the name of the event that triggered the delivery." | `official-vendor-doc` | 수신단 이벤트 라우터 설계 | 페이로드 내의 데이터 구조 파싱 방식 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `GITHUB-WEBHOOK-C2`, `C3`: `X-Hub-Signature-256` 헤더 패턴 (`sha256=<hex>`) 및 HMAC-SHA256 알고리즘 사용.
|
||||
- `GITHUB-WEBHOOK-C5`: constant-time 비교 강제.
|
||||
- `GITHUB-WEBHOOK-C6`, `C7`: 배달 UUID (`X-GitHub-Delivery`) 및 이벤트 타입 헤더 (`X-GitHub-Event`) 분리 구조.
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- **Replay Attack 방지 타임스탬프** — GitHub는 헤더에 리플레이 방지용 타임스탬프를 명시적으로 보내지 않으며, 이를 처리하는 오차 허용 윈도우 수치는 본 문서의 증명 범위 밖임 (Stripe 등 타사 문서 참조 필요).
|
||||
- **시크릿 관리 및 로테이션 주기** — 시크릿 키를 동적으로 교체하거나 Vault 등과 연동하는 구체적인 아키텍처는 다루지 않음.
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- `X-GitHub-Delivery` 헤더를 수신 측에서 `Idempotency Key`로 간주하여 중복 호출을 막을 수 있지만, 전송 도중 네트워크 타임아웃 등으로 인해 **동일 이벤트가 서로 다른 Delivery ID로 재전송될 가능성**이 있는지 여부는 추가 확인 필요 (일반적으로 재시도 시 Delivery ID가 유지되는지 확인 필요).
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- **Header Prefix Handling**: 서명 검증 시 `sha256=` 문자열을 헤더 값에서 파싱해 제거한 후, HMAC-SHA256 hex digest와 비교해야 함.
|
||||
- **Event Header Routing**: `X-GitHub-Event` 헤더를 활용해 `order.created`, `payment.completed` 등의 구체적인 도메인 이벤트 핸들러로 라우팅하는 Dispatcher 구현에 유용하게 모방 가능.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 관련 raw 자료: [[raw/official-docs/stripe-webhook-signature.md]], [[raw/official-docs/svix-webhook-best-practices.md]]
|
||||
- 이 자료를 인용한 wiki 요약: (미작성)
|
||||
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-webhook-outbound-contract.md]]
|
||||
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract.md]]
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-122 — Resource Names"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/122
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, google-aip]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-122 — Resource Names
|
||||
|
||||
> Layer: `raw/official-docs/` — Google API Improvement Proposals(AIP) 공식 문서 원문 발췌.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성.
|
||||
> 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
> 이 자료는 **혼자 존재하지 않는다.** 어느 branch의 구현 결정의 **근거**로서 보관됨.
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | (future B13 — 현재 branch 미결) Resource URL naming convention (plural lowercase collection segment). sample-ticket fixture 의 `/v1/tickets` 같은 collection name 명명 기준 — AIP-122 의 collection identifier 규칙이 직접 근거 후보. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/122
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Google LLC (AIP editors)
|
||||
- 발행일: (AIP — 지속 업데이트, 확인일 기준)
|
||||
- 마지막 확인일: 2026-05-31
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-api-contract-baseline` 의 §5.2 Next-Session Raw Boost Plan 에서 명시된 신설 예정 raw 자료 중 하나. resource URL naming convention (collection segment 의 plural, lowercase 규칙) 의 외부 근거로 Google AIP-122 가 1차 reference 후보로 지목됨. 본 branch 의 `/v1/tickets` URL 패턴 결정의 normative 근거를 제공할 수 있는지 검토 목적으로 보관.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5문장)
|
||||
|
||||
> [§ Resource names — intro] "Most APIs expose _resources_ (their primary nouns) which users are able to create, retrieve, and manipulate. Additionally, resources are _named_: each resource has a unique identifier that users use to reference that resource, and these names are what users should _store_ as the canonical names for the resources."
|
||||
|
||||
> [§ Collection identifiers — plural] "The collection identifier segments in a resource name **must** be the plural form of the noun used for the resource."
|
||||
|
||||
> [§ Collection identifiers — format] "Collection identifiers **must** begin with a lower-cased letter and contain only ASCII letters and numbers (`/[a-z][a-zA-Z0-9]*/`)."
|
||||
|
||||
> [§ Resource ID segments — user-specified] "If resource IDs are user-specified, the API **must** document allowed formats. User-specified resource IDs **should** conform to [RFC-1034](https://tools.ietf.org/html/rfc1034)...Additionally, user-specified resource IDs **should** restrict letters to lower-case (`^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$`)."
|
||||
|
||||
> [§ Resource name components — hierarchy] "Resource name components **should** usually alternate between collection identifiers (example: `publishers`, `books`, `users`) and resource IDs (example: `123`, `les-miserables`, `vhugo1802`)."
|
||||
|
||||
> [§ Full vs relative resource names] "**Note:** Resource names as described here are used within the scope of a single API (or else in situations where the owning API is clear from the context), and are only required to be unique within that scope. For this reason, they are sometimes called _relative resource names_ to distinguish them from _full resource names_"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP122-C1 | Resource name 은 URI path schema 를 따르는 계층적 식별자이며, 각 resource 는 고유 name 을 가지고 사용자는 이 name 을 canonical 식별자로 저장해야 한다 | [§ intro] "each resource has a unique identifier that users use to reference that resource, and these names are what users should _store_ as the canonical names for the resources." | `official-reference` | Google API 설계 — protobuf/gRPC 컨텍스트 기본, HTTP REST 매핑은 AIP-127 별도 참조 | REST URL path 가 곧 AIP resource name 과 동일하다는 것. HTTP REST URL 의 normative 기준이 AIP-122 단독이라는 것 |
|
||||
| AIP122-C2 | Collection identifier segment 는 반드시 resource 유형의 복수형 명사여야 한다 | [§ Collection identifiers] "The collection identifier segments in a resource name **must** be the plural form of the noun used for the resource." | `official-reference` | Google AIP 를 따르는 API 설계. REST API URL collection segment 의 plural 규칙 근거로 cross-cite 가능 | 모든 REST API 표준이 반드시 plural 을 사용해야 한다는 것 (AIP 는 Google 사내 community guideline 이며 IETF/W3C 표준 아님) |
|
||||
| AIP122-C3 | Collection identifier 는 소문자로 시작해야 하며 ASCII 문자와 숫자만 포함한다 (`/[a-z][a-zA-Z0-9]*/`) | [§ Collection identifiers] "Collection identifiers **must** begin with a lower-cased letter and contain only ASCII letters and numbers (`/[a-z][a-zA-Z0-9]*/`)." | `official-reference` | Google AIP collection identifier 의 문자 집합 규칙 | kebab-case (하이픈 포함) collection identifier 가 허용된다는 것 — regex 에 하이픈 없음. 본 AIP 는 lowerCamelCase 형태를 허용하나 REST path segment 에서 실제로 camelCase 를 쓰는지 여부는 AIP-127 참조 필요 |
|
||||
| AIP122-C4 | Resource ID segment 는 user-specified 인 경우 RFC-1034 준수를 권고하며 소문자 제한 regex `^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$` 를 권고 | [§ Resource IDs] "User-specified resource IDs **should** conform to [RFC-1034]...user-specified resource IDs **should** restrict letters to lower-case (`^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$`)." | `official-reference` | user-specified resource ID (slug/handle 형태). **SHOULD** 이므로 강제 아님 | system-generated ID (UUID 등) 에 적용된다는 것 — 본 문서는 server-assigned ID 포맷을 normative 하게 제한하지 않음 |
|
||||
| AIP122-C5 | Resource name 은 collection identifier 와 resource ID 가 번갈아 나타나는 계층 구조이며, 단일 API 범위 내에서 사용되는 것은 relative resource name, API service name 을 포함하면 full resource name 이다 | [§ Hierarchy] "Resource name components **should** usually alternate between collection identifiers...and resource IDs"; [§ Full/Relative] "they are sometimes called _relative resource names_ to distinguish them from _full resource names_" | `official-reference` | Google API 의 resource name 구조 전반 — parent/child resource 관계 표현 방식 | REST URL 의 versioning (`/v1`) 이 AIP resource name 구조 안에 포함된다는 것. AIP 의 full resource name 은 REST URL 과 다른 개념 (schemeless URI — `//service/path`, REST 는 `https://service/v1/path`) |
|
||||
|
||||
### Strength 허용값 참고
|
||||
|
||||
- 본 문서의 모든 Claim 은 `official-reference` 로 분류.
|
||||
- AIP 는 Google 사내 API community guideline 이며 IETF RFC / W3C 표준이 아니다.
|
||||
- `official-vendor-doc` 가 아닌 `official-reference` 로 분류한 이유: AIP 는 특정 Google 제품 (Cloud, Kubernetes 등) 의 공식 API 문서가 아니라 Google 내부 API 설계 guideline 의 공개 버전.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
### 이 자료가 직접 증명하는 것
|
||||
|
||||
- `AIP122-C2`: REST API 의 collection segment 를 복수형으로 명명해야 하는 근거 — Google AIP 기준. `/v1/tickets`, `/v1/publishers` 같은 패턴의 `tickets`, `publishers` 가 plural 이어야 함을 support.
|
||||
- `AIP122-C3`: collection segment 가 소문자로 시작하고 ASCII 문자·숫자만 써야 한다는 것.
|
||||
- `AIP122-C4`: user-specified resource ID 의 권고 포맷 (소문자 + 숫자 + 하이픈, 최대 63자).
|
||||
- `AIP122-C5`: resource name 의 collection/ID 교대 계층 구조 패턴.
|
||||
|
||||
### 이 자료가 증명하지 않는 것
|
||||
|
||||
- **AIP 는 IETF/W3C 표준이 아니다.** `official-reference` strength — Google API community guideline. 이 근거만으로 REST API 표준이라고 주장할 수 없다.
|
||||
- **AIP-122 는 주로 protobuf/gRPC 컨텍스트다.** REST URL path 로의 매핑은 별도 AIP-127 (HTTP and gRPC Transcoding) 가 다룬다. `/v1/tickets` 같은 REST URL 패턴이 AIP-122 단독으로 normative 하게 결정된다는 것은 본 인용 범위 밖.
|
||||
- **collection identifier regex (`/[a-z][a-zA-Z0-9]*/`) 에는 하이픈이 없다.** kebab-case collection segment (`/v1/ticket-comments`) 는 AIP-122 의 collection identifier 규칙에 직접 합치하지 않음 — AIP-122 는 lowerCamelCase (`ticketComments`) 형태를 허용. kebab-case 허용 여부는 AIP-127 또는 별도 REST guideline 참조 필요.
|
||||
- **`/v1` versioning prefix 가 AIP resource name 구조 안에 있다는 것.** AIP 의 full resource name 은 `//service/path` (schemeless URI, 버전 미포함) 이며 REST URL `https://service/v1/path` 와 다른 개념. versioning 근거는 AIP-185 (별도 raw 기 보관).
|
||||
- **sample-ticket fixture 의 `/v1/tickets/{id}` 결정의 normative 근거가 AIP-122 단독이라는 것.** AIP-122 는 collection name plural + lowercase 를 corroborate 하지만 URL 전체 구조의 normative 기준으로 단독 사용은 부족 — AIP-127 cross-cite 필요.
|
||||
|
||||
### 내 프로젝트에 적용하려면 추가 확인이 필요한 것
|
||||
|
||||
- **AIP-127 (HTTP and gRPC Transcoding) 정독 필요**: REST URL path 와 AIP resource name 의 매핑 규칙. kebab-case collection segment 허용 여부 확인.
|
||||
- **REST API collection segment 의 공식 표준 여부**: AIP-122 는 Google 기준. IETF 차원의 REST 리소스 명명 표준은 RFC 3986 (URI) 이나 별도 naming convention 표준 없음 — de facto 관행만 존재. AIP-122 를 cross-cite 할 때 "Google API guideline 기반" 임을 명시할 것.
|
||||
- **sample-ticket fixture 의 resource ID 포맷**: `AIP122-C4` (user-specified ID — RFC-1034 준수 권고) 가 ticket fixture 의 ID 결정에 적용되는지 — 현재 resource ID format SSOT 는 `feature-api-contract-baseline` 의 out-of-scope (미결, §Cross-branch Contract Map 참조).
|
||||
- **`/v1/tickets` 의 직접 normative 근거 교차 확인**: AIP-122 (collection plural, lowercase) + AIP-185 (URI `/v1` versioning) 를 양쪽 cross-cite 해야 URL 패턴 전체의 근거가 완성됨.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-122 의 collection identifier 는 lowerCamelCase 를 표준으로 한다 (`userEvents`, `ticketComments`). REST path 에서의 kebab-case vs camelCase 선택은 AIP-127 에서 다루는 내용으로 추정 — 다음 세션에 AIP-127 raw 신설 권고.
|
||||
- AIP 는 Google Cloud API Design Guide 의 전신 / 발전 형태. 별도 "Google Cloud API Design Guide" 도 관련 자료이나 AIP 가 더 세부 규칙을 담음.
|
||||
- `AIP122-C3` 의 regex `[a-z][a-zA-Z0-9]*` 는 camelCase 를 허용한다 (대문자 포함). 본 프로젝트가 kebab-case path segment 를 선택했다면 AIP-122 의 collection identifier 규칙을 직접 따르는 것이 아닌 "정신적으로 일치" 수준임을 명시할 것.
|
||||
- AIP-122 는 resource alias (`users/me` 같은 semantic alias) 도 허용하되 "all data returned from the API must use the canonical resource name" 원칙을 명시 — alias endpoint 설계 시 참조 가능.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- **AIP-127 (HTTP and gRPC Transcoding)**: REST URL path ↔ AIP resource name 매핑 — 반드시 cross-cite. [[raw/official-docs/google-aip-127-http-transcoding]] (미신설 — 다음 세션 신설 권고)
|
||||
- **AIP-185 (Resource Versioning)**: URI `/v1` prefix 규칙 — 기 보관. [[raw/official-docs/google-aip-185-resource-versioning]]
|
||||
- **AIP-180 (Backwards Compatibility)**: backward compatibility 의무 cross-cite. [[raw/official-docs/api-versioning-google-aip-180]]
|
||||
- **AIP-132 (List method)**: sort parameter syntax — 미신설. [[raw/official-docs/google-aip-132-list-method]] (§5.2 신설 예정)
|
||||
- **AIP-151 (Long-Running Operations)**: LRO 응답 패턴 — 미신설. [[raw/official-docs/google-aip-151-long-running-operations]] (§5.2 신설 예정)
|
||||
- **RFC 3986 (URI Syntax)**: URI 전반 문법 정의 — 별도 cross-cite 필요 시
|
||||
- 이 자료를 인용한 wiki 요약: `wiki/concepts/rest-resource-naming` (생성 전)
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-127 — HTTP and gRPC Transcoding"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/127
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, google-aip]
|
||||
status: raw
|
||||
confidence: unknown
|
||||
created: 2026-07-21
|
||||
last_reviewed:
|
||||
---
|
||||
|
||||
# official-doc: Google AIP-127 — HTTP and gRPC Transcoding
|
||||
|
||||
> Layer: `raw/official-docs/` — 외부 공식 문서의 **원문 발췌·출처 기록**.
|
||||
|
||||
## 상태
|
||||
|
||||
**원문 미발췌 스텁입니다.** `[[raw/official-docs/google-aip-122-resource-names]]` 가 "REST URL path ↔
|
||||
AIP resource name 매핑 — 반드시 cross-cite" 로 이 문서를 참조하면서 *미신설* 로 표시해 둔 자리입니다.
|
||||
|
||||
CLAUDE.md §7 원본 보존 규칙상 이 문서는 위 `url` 을 실제로 열어 **핵심 인용 3~5문장을 verbatim 으로
|
||||
발췌**한 뒤에야 근거로 쓸 수 있습니다. 발췌 전까지 이 문서를 인용해 단정적 진술을 만들지 않습니다.
|
||||
|
||||
## 관련
|
||||
|
||||
- [[raw/official-docs/google-aip-122-resource-names]] — 이 문서를 cross-cite 하는 상위 자료
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-132 — Standard Methods: List"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/132
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, google-aip, list-method, pagination, ordering]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-132 — Standard Methods: List
|
||||
|
||||
> Layer: `raw/official-docs/` — 외부 공식 문서의 원문 발췌·출처 기록.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | (future B14 — 현재 branch 미결) Sort parameter syntax 결정 — D7 의 `sort` request param 정확한 syntax (`?sort=name,desc` vs `?sort=-name` vs `?sort=name:desc`) 에 대해 AIP-132 의 `order_by` string 형식 (`"foo desc, bar"`) 이 normative reference 로 기능. pagination field naming 차이 cross-cite (AIP-132: `page_size`/`page_token` snake_case proto field vs branch D7: `page`/`size` REST query string) |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/132
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Google (API Improvement Proposals)
|
||||
- 발행일: 2019-01-21
|
||||
- 마지막 확인일: 2026-05-31
|
||||
- AIP State: Approved
|
||||
- 마지막 갱신: 2025-02-25 (ordering well-known types clarification)
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
AIP-132 는 Google 의 resource-oriented API 설계 지침 중 `List` 표준 method 의 request/response signature, pagination field 명명 (`page_size`, `page_token`, `next_page_token`), `order_by` 필드의 syntax (`"foo desc, bar"` 형식), `filter` 필드의 AIP-160 연계를 normatively 정의한다. branch `feature-api-contract-baseline` 의 D7 (`page`/`size`/`sort` request param 결정) 및 미결 B14 (sort syntax) 에 대한 `official-reference` 근거로 보관.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5개)
|
||||
|
||||
> **인용 1 — List method 표준 signature** [§Guidance, line 715–716]
|
||||
>
|
||||
> "The request and response messages **must** match the RPC name, with `Request` and `Response` suffixes."
|
||||
|
||||
> **인용 2 — Pagination fields** [§Request message, line 767–768]
|
||||
>
|
||||
> "The `page_size` and `page_token` fields, which support pagination, **must** be specified on all list request messages. For more information, see AIP-158."
|
||||
|
||||
> **인용 3 — next_page_token response field** [§Response message, line 809–812]
|
||||
>
|
||||
> "The `next_page_token` field, which supports pagination, **must** be included on all list response messages. It **must** be set if there are subsequent pages, and **must not** be set if the response represents the final page. For more information, see AIP-158."
|
||||
|
||||
> **인용 4 — order_by syntax (descending)** [§Ordering, line 828–829]
|
||||
>
|
||||
> "The default sorting order is ascending. To specify descending order for a field, users append a `\" desc\"` suffix; for example: `\"foo desc, bar\"`."
|
||||
|
||||
> **인용 5 — filter field + AIP-160 reference** [§Filtering, line 850–852]
|
||||
>
|
||||
> "List methods **may** allow clients to specify filters; if they do, the request message **should** contain a `string filter` field. Filtering is described in more detail in AIP-160."
|
||||
|
||||
> **인용 6 — HTTP verb (safe method)** [§Guidance, line 717]
|
||||
>
|
||||
> "The HTTP verb **must** be `GET`."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 프로젝트 적용 결론은 `## 메모` 또는 branch-note 에서만 작성.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP132-C1 | List method 의 RPC 는 `List` prefix 를 가지며, request/response message 는 RPC 이름과 동일한 `Request`/`Response` suffix 를 **must** 가진다 | [§Guidance] "The request and response messages **must** match the RPC name, with `Request` and `Response` suffixes." | `official-reference` | Google AIP 를 따르는 API (proto-based RPC + HTTP transcoding) | REST-only API 의 URL 또는 JSON body field 명명. 본 branch 의 REST endpoint 명명 자체는 AIP-127 (HTTP/gRPC transcoding) 별도 적용 범위 |
|
||||
| AIP132-C2 | List request message 는 `page_size` (int32) 와 `page_token` (string) 필드를 **must** 포함해야 한다 | [§Request message] "The `page_size` and `page_token` fields, which support pagination, **must** be specified on all list request messages." | `official-reference` | Google AIP 를 따르는 proto List method | REST query string 의 파라미터 명 직접 적용 불가 — proto field 명이 REST query string 으로 변환되는 매핑은 AIP-127 §6 (HTTP transcoding) 적용. 본 branch 의 `page`/`size` query param 은 이 claim 의 직접 산출이 아님 |
|
||||
| AIP132-C3 | List response message 는 `next_page_token` (string) 필드를 **must** 포함해야 하며, 후속 페이지가 있으면 set, 마지막 페이지이면 **must not** set 이다 | [§Response message] "The `next_page_token` field, which supports pagination, **must** be included on all list response messages. It **must** be set if there are subsequent pages, and **must not** be set if the response represents the final page." | `official-reference` | Google AIP proto List response | 본 branch 의 `meta.page.total` 또는 `meta.page.number` 같은 envelope 필드 — AIP-132 는 `total_size` 를 optional (`may`) 로만 정의하며 offset/page 번호를 response 에 요구하지 않음 |
|
||||
| AIP132-C4 | `order_by` 필드 syntax: 기본 ascending, descending 은 `" desc"` suffix 로 표현 (e.g., `"foo desc, bar"`), comma-separated, 공백 무시, subfield 는 dot notation | [§Ordering] "The default sorting order is ascending. To specify descending order for a field, users append a `\" desc\"` suffix; for example: `\"foo desc, bar\"`." | `official-reference` | Google AIP 를 따르는 API 의 `order_by` string field | REST query string 파라미터 명 (`?sort=` vs `?order_by=`) 자체 — AIP-132 는 proto field 명 `order_by` 를 정의하나 URL query param key 명 정규화는 AIP-127. `?sort=-name` (마이너스 prefix 방식) 또는 `?sort=name:desc` (콜론 방식) 는 AIP-132 normative syntax 와 다름 |
|
||||
| AIP132-C5 | List method 의 `filter` 필드는 선택 사항 (`may`) 이며, 포함 시 `string filter` 타입이고 세부 문법은 AIP-160 에서 정의 | [§Filtering] "List methods **may** allow clients to specify filters; if they do, the request message **should** contain a `string filter` field. Filtering is described in more detail in AIP-160." | `official-reference` | Google AIP 를 따르는 API 의 filtering 기능 | filter 문법의 구체 연산자 (예: `AND`, `OR`, 비교 연산자) — 이는 AIP-160 에서 별도 정의됨. 본 claim 은 필드 존재와 AIP-160 참조만 증명 |
|
||||
| AIP132-C6 | List method 의 HTTP verb 는 **must** `GET` 이어야 하며 이는 safe method 이다 (RFC 9110 §9.2.1 GET is safe) | [§Guidance] "The HTTP verb **must** be `GET`." | `official-reference` | Google AIP 를 따르는 List endpoint 의 HTTP method | GET 의 safe/idempotent 속성 자체 — 이는 RFC 9110 §9.2.1/9.2.2 normative. AIP-132 는 GET 을 **must** 로 요구하나 "safe" 또는 "idempotent" 라는 용어 자체는 본 문서에서 명시하지 않음 |
|
||||
|
||||
### Strength 확인
|
||||
|
||||
AIP-132 는 Google 내부 community guideline (API Improvement Proposals) — IETF RFC 또는 W3C 표준이 아니므로 `official-reference` (Google 공식 벤더 가이드라인, Google API 설계의 de facto standard). `official-standard` (RFC/W3C 수준) 아님.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
### 이 자료가 직접 증명하는 것
|
||||
|
||||
- `AIP132-C1`: List RPC 의 request/response message naming convention (proto 기반)
|
||||
- `AIP132-C2`: `page_size`/`page_token` 이 List request 의 **must** 필드
|
||||
- `AIP132-C3`: `next_page_token` 이 List response 의 **must** 필드, 유/무 set 의미론
|
||||
- `AIP132-C4`: `order_by` 의 normative syntax (`"foo desc, bar"` 형식, comma-separated, space-insignificant)
|
||||
- `AIP132-C5`: `filter` 필드가 optional (`may`) 이며 AIP-160 에서 문법 정의
|
||||
- `AIP132-C6`: List method 의 HTTP verb 는 `GET` 강제
|
||||
|
||||
### 이 자료가 증명하지 않는 것
|
||||
|
||||
- **REST query string 파라미터 명**: AIP-132 는 proto field 명을 정의함. `page_size` → REST query `?page_size=` 매핑은 AIP-127 (HTTP/gRPC Transcoding) 범위. 본 branch 의 `?page=N&size=N` (camelCase 또는 단축 명) 은 AIP-132 직접 결과 아님 — project-internal 매핑 결정
|
||||
- **Sort query param 명**: `?sort=` vs `?order_by=` key 명 자체는 AIP-132 밖. AIP-132 는 proto field 명 `order_by` 만 정의
|
||||
- **Pagination 전략 (offset vs cursor)**: AIP-132 는 `page_size`/`page_token` (cursor-based) 을 정의하나 AIP-132 자체에서 offset-pagination 을 금지하거나 cursor 를 강제하지는 않음. AIP-158 에서 상세 정의
|
||||
- **Sort syntax 의 REST 직접 적용**: `?sort=name,desc` 는 AIP-132 의 `"foo,bar"` + `"foo desc, bar"` 를 URL query string 으로 적용한 해석 — AIP-132 본문은 proto field value format 을 정의
|
||||
- **`?sort=-name` (마이너스 prefix 방식) 또는 `?sort=name:desc` (콜론 방식)**: AIP-132 normative 가 아님. `" desc"` suffix 방식만 normative
|
||||
- **Filter 문법의 연산자**: AIP-160 범위. 본 자료는 필드 존재와 참조만 언급
|
||||
|
||||
### 내 프로젝트에 적용하려면 추가 확인이 필요한 것
|
||||
|
||||
- `page_size`/`page_token` → REST `?page=N&size=N` 변환의 project-internal 매핑 문서화 — AIP-127 참조 권고
|
||||
- Sort syntax 를 `?sort=name,desc` (AIP-132 variant) vs `?sort=-name` (OpenAPI community) vs `?sort=name:desc` (기타) 중 어느 것으로 채택할지 — D7 미결 B14 의 결정 포인트. **AIP-132 기준 채택 시**: `?sort=foo desc, bar` 또는 URL 인코딩 후 `?order_by=foo+desc%2C+bar` 형태가 normative nearest
|
||||
- filter 문법 상세: [[raw/official-docs/google-aip-160-filtering]] 신설 후 AIP-160 참조 (현재 미존재)
|
||||
- AIP-158 (`Pagination`) raw 신설 시 `page_token` 의 cursor semantics 와 본 branch D7 의 `page`/`size` offset pagination 과의 차이 명확화 필요
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-132 는 proto-first 설계 (gRPC + HTTP transcoding). REST-only API 에 직접 적용 시 proto field 명 → REST query param 변환 규칙 (AIP-127) 을 거쳐야 한다. 본 branch 의 `?page=N&size=N` 은 project-internal 선택으로, AIP-132 준수 선언이 아님.
|
||||
- `order_by` 의 `"foo desc, bar"` syntax 는 REST query string 에서 `?order_by=foo+desc%2C+bar` (URL encoded) 또는 `?sort=foo desc, bar` 형태가 될 수 있음. 공백이 URL query string 에서 `+` 또는 `%20` 으로 인코딩되는 점을 고려한 API 문서화 필요.
|
||||
- AIP-132 page_size/page_token 기반 pagination 은 cursor-based (opaque token). 본 branch D7 의 offset pagination (`page`/`size`) 과 의미론적으로 다름. cursor endpoint 추가 결정 시 AIP-158 참조 권고.
|
||||
- `total_size` 는 AIP-132 에서 `may` (optional) — 본 branch 의 `meta.page.total` 이 이에 대응하지만 AIP-132 가 강제하는 것은 아님.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- AIP-158 (Pagination): `raw/official-docs/google-aip-158-pagination.md` (미신설 — `feature-api-contract-baseline` §5.2 Next-Session Raw Boost Plan 신설 예정)
|
||||
- AIP-160 (Filtering): `raw/official-docs/google-aip-160-filtering.md` (미신설 — 신설 예정)
|
||||
- AIP-127 (HTTP/gRPC Transcoding — proto field → REST query param 변환): 미신설
|
||||
- [[raw/official-docs/google-aip-185-resource-versioning]] — 동일 AIP 계열, versioning 근거 (D2)
|
||||
- [[raw/official-docs/api-versioning-google-aip-180]] — backward compatibility (D6)
|
||||
- [[raw/official-docs/jsonapi-pagination-format]] — pagination link key 명명 표준 (D7)
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-136 — Custom Methods"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/136
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, google-aip, custom-method]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-136 — Custom Methods
|
||||
|
||||
> Layer: `raw/official-docs/` — 외부 공식 자료 원문 발췌·출처 기록.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | (future B18 — 미결) Bulk operation URL pattern — `/v1/tickets:batchCreate` Google AIP-136 colon-verb syntax 근거 (collection-based custom method 패턴). D17 LRO cross-ref: custom method (`:cancel`, `:batchCreate`) 가 LRO entry point 가 될 수 있음. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/136
|
||||
- 아카이브 URL: (미등록 — 추후 archive.org 스냅샷 추가 권장)
|
||||
- 저자 / 조직: Google (AIP Editors)
|
||||
- 발행일: 2019-01-25 (approved)
|
||||
- 마지막 확인일: 2026-05-31
|
||||
- Changelog: 2025-05-12 (preposition rationale 확장), 2025-01-09, 2023-11-16, 2023-05-16, 2023-05-09, 2023-03-02
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-api-contract-baseline` 의 미결 B18 (bulk operation URL pattern) 를 정당화하기 위해 수집. Google AIP-136 은 standard CRUD 로 표현 불가능한 동작에 colon-separated verb suffix (`:batchCreate`, `:cancel` 등) 를 사용하는 custom method URI 패턴을 정의하며, collection-scoped custom method 가 batch operation 의 natural fit 임을 보여준다. D17 (LRO) 와의 cross-ref 근거로도 활용 — custom method 가 202 LRO entry point 가 될 수 있음.
|
||||
|
||||
**중요 scope note**: AIP-136 자체는 `:batchCreate`, `:cancel` 같은 구체적 verb 이름을 직접 정의하지 않는다. 그 verb 들은 AIP-231 (Batch methods), AIP-232 (Batch Get), AIP-233 (Batch Create), AIP-234 (Batch Update), AIP-235 (Batch Delete) 에 정의되어 있다. AIP-136 은 custom method 의 **URI syntax** 와 **적용 원칙** 을 정의한다. Idempotency 에 대한 normative 진술도 AIP-136 본문에는 없다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Guidance 1단락] "Resource-oriented design (AIP-121) uses custom methods to provide a means to express arbitrary actions that are difficult to model using only the standard methods. Custom methods are important because they provide a means for an API's vocabulary to adhere to user intent."
|
||||
|
||||
> [§Guidance — HTTP URI bullet] "The HTTP URI **must** use a `:` character followed by the custom verb (`:archive` in the above example), and the verb in the URI **must** match the verb in the name of the RPC."
|
||||
|
||||
> [§Guidance — HTTP method bullets] "`GET` **must** be used for methods retrieving data or resource state." / "`POST` **must** be used if the method has side effects or mutates resources or data."
|
||||
|
||||
> [§Resource-based custom methods] "Custom methods **must** operate on a resource if the API can be modeled as such"
|
||||
|
||||
> [§Collection-based custom methods] "While most custom methods operate on a single resource, some custom methods **may** operate on a collection instead"
|
||||
|
||||
**[Self-Grep verification log — /tmp/source-fetch-20260531091148.txt]**
|
||||
|
||||
- Quote 1 (`express arbitrary actions`): line 698 — PASS
|
||||
- Quote 2 (`use a \`:\ character followed by the custom verb`): line 738 — PASS
|
||||
- Quote 3 (`GET **must** be used for methods retrieving`): line 733 — PASS
|
||||
- Quote 3b (`POST **must** be used if the method has side effects`): line 734 — PASS
|
||||
- Quote 4 (`Custom methods **must** operate on a resource`): line 758 — PASS
|
||||
- Quote 5 (`some custom methods **may** operate on a collection instead`): line 773 — PASS
|
||||
|
||||
검증: V=5 P=5 D=0 C=0
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리. AIP-136 본문에 없는 내용 (batch verb 명칭, idempotency) 은 claim 으로 추출하지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP136-C1 | Custom method 는 standard CRUD 로 표현하기 어려운 임의 동작을 표현하는 수단으로 resource-oriented design 에 정의된 개념이다 | [§Guidance 1단락] "uses custom methods to provide a means to express arbitrary actions that are difficult to model using only the standard methods" | `official-reference` | Google AIP 를 채택한 모든 API 설계 | custom method 가 모든 REST API 에서 best practice 라는 뜻이 아님 — Google API community guideline 내 컨벤션 |
|
||||
| AIP136-C2 | Custom method 의 HTTP URI 는 반드시 `:` 문자 뒤에 custom verb 를 붙여야 하며, URI 의 verb 는 RPC 이름의 verb 와 반드시 일치해야 한다 | [§Guidance — HTTP URI bullet] "The HTTP URI **must** use a `:` character followed by the custom verb [...] and the verb in the URI **must** match the verb in the name of the RPC" | `official-reference` | Google AIP 를 따르는 REST/gRPC-transcoded API | RFC 3986 URL 표준이 `:verb` suffix 를 특별히 정의하지 않는다는 점 — 이는 AIP 내부 컨벤션 |
|
||||
| AIP136-C3 | Custom method 에서 HTTP `POST` 는 side effect 나 resource/data 변경이 있을 때 반드시 사용해야 하고, `GET` 은 데이터·상태 조회에만 반드시 사용해야 한다 | [§Guidance — HTTP method bullets] "`GET` **must** be used for methods retrieving data or resource state." / "`POST` **must** be used if the method has side effects or mutates resources or data." | `official-reference` | Google AIP custom method HTTP method 선택 | HTTP method 선택이 자동으로 idempotency 를 보장한다는 뜻이 아님 — AIP-136 본문에 idempotency 진술 없음 |
|
||||
| AIP136-C4 | Custom method 는 API 가 resource 단위로 모델링 가능하면 반드시 resource 에 적용해야 하며, resource 이름 파라미터는 반드시 `name` 이라 칭하고 URI path 의 유일한 변수여야 한다 | [§Resource-based custom methods] "Custom methods **must** operate on a resource if the API can be modeled as such" + "The parameter for the resource's name **must** be called `name`, and be the only variable in the URI path." | `official-reference` | Resource-based custom method 설계 | 특정 동사 어휘 (`:cancel`, `:batchCreate` 등) 의 normative 정의 — 이는 AIP-231/232/233/234/235 범위 |
|
||||
| AIP136-C5 | Collection-based custom method 는 단일 resource 대신 collection 전체에 적용할 수 있으며, collection 의 부모 resource 파라미터는 반드시 `parent` 라 칭하고 collection key 는 리터럴이어야 한다 | [§Collection-based custom methods] "some custom methods **may** operate on a collection instead" + "If the collection's resource has a parent, that resource **must** be called `parent` and be the only variable in the URI path." + "The collection key [...] **must** be literal." | `official-reference` | Collection-scoped custom method (예: batchCreate, sort 등) | batch method 의 응답 형식 (partial success 처리, 오류 envelope) — AIP-136 본문에 없음. 이는 AIP-231~235 + project-internal envelope 매핑 범위 |
|
||||
|
||||
### Strength 근거
|
||||
|
||||
AIP (API Improvement Proposal) 는 Google 내부 community guideline 로 IETF/W3C 표준이 아님. `official-reference` 로 분류 (CLAUDE.md §5 참조). company-case-study 보다 강하나 `official-standard` (RFC/W3C) 보다 약함.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
### 이 자료가 직접 증명하는 것
|
||||
|
||||
- `AIP136-C2`: `/v1/{resource}:verb` 형식의 colon-separated verb suffix URI syntax 가 Google AIP 에서 normative 하게 정의된 컨벤션임
|
||||
- `AIP136-C3`: Custom method 에서 mutation 은 `POST`, 조회는 `GET` 이라는 HTTP method 선택 원칙
|
||||
- `AIP136-C4`: Resource-scoped custom method 의 파라미터 명명 (`name`) 과 URI 변수 단일 강제
|
||||
- `AIP136-C5`: Collection-scoped custom method 의 파라미터 명명 (`parent`) 과 collection key 리터럴 강제
|
||||
|
||||
### 이 자료가 증명하지 않는 것
|
||||
|
||||
- `:batchCreate`, `:cancel`, `:undelete`, `:batchGet`, `:batchUpdate`, `:batchDelete` 같은 표준 batch verb 의 normative 명칭 — 이는 AIP-231~235 에 있음. AIP-136 은 verb 형식만 정의하고 구체적 어휘는 정의하지 않는다.
|
||||
- Custom method 의 idempotency 분류 — AIP-136 본문에 idempotency 관련 normative 진술 없음
|
||||
- Colon syntax (`:batchCreate`) 가 RFC 3986 URL 표준 자체에서 정의된다는 것 — RFC 3986 은 `:` 를 path segment delimiter 로 정의하지 않음. 이는 AIP 내부 컨벤션이며 REST 클라이언트/라이브러리가 자동 지원하지 않을 수 있다.
|
||||
- 본 branch 의 envelope `BATCH_PARTIAL_FAILURE` category 와 AIP-136 의 batch method 응답 형식이 동일하다는 것 — AIP-136 은 batch 응답 형식을 정의하지 않는다. project-internal 매핑 필요.
|
||||
- AIP 가 IETF/W3C 표준과 동등한 normative 권위를 가진다는 것 — Google API community guideline (`official-reference`) 임
|
||||
|
||||
### 내 프로젝트에 적용하려면 추가 확인이 필요한 것
|
||||
|
||||
- `:batchCreate` verb 명칭의 normative 근거: AIP-233 raw 신설 필요 (`raw/official-docs/google-aip-233-batch-create.md`)
|
||||
- Batch method 의 partial failure 응답 형식: AIP-231 (Batch methods) + project-internal `BATCH_PARTIAL_FAILURE` envelope 매핑
|
||||
- `:cancel` verb 가 LRO entry point 로 사용되는 패턴: AIP-151 (Long-Running Operations) raw 신설 필요 (`raw/official-docs/google-aip-151-long-running-operations.md`)
|
||||
- Spring REST 환경에서 `:verb` suffix path 가 제대로 routing 되는지: Spring MVC PathPattern 설정 검증 필요
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-136 은 colon syntax 의 HTTP 라우팅 관련 주의사항을 본문에서 직접 논하지 않는다. gRPC-to-HTTP transcoding (AIP-127) 컨텍스트가 전제된 문서이므로, 순수 REST 환경에서의 적용은 additional tooling/config 필요.
|
||||
- Batch verb 목록 (`:batchCreate`, `:cancel`, `:undelete`) 은 사용자 요청에서 "AIP-136 표준 verb" 로 언급되었으나, AIP-136 본문에는 존재하지 않는다. 이는 AIP-231~235 의 내용이다. 다음 세션 raw 신설 권고: `google-aip-231-batch-methods-official`, `google-aip-233-batch-create-official`.
|
||||
- AIP-136 의 idempotency 관련 진술 부재: GET 이 side-effect 없음을 명시하므로 `GET` custom method 는 안전(safe)하다고 추론 가능하나, idempotency 자체에 대한 normative 진술은 없다. 이를 claim 으로 추출하지 않는다.
|
||||
- 추가 봐야 할 동일 출처 페이지: AIP-231 (https://google.aip.dev/231), AIP-233 (https://google.aip.dev/233), AIP-151 (https://google.aip.dev/151), AIP-127 (https://google.aip.dev/127)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/branch-notes/feature-api-contract-baseline]] — 본 자료의 parent, D17 (LRO) + B18 (bulk operation URL pattern)
|
||||
- [[raw/official-docs/google-aip-185-resource-versioning]] — 동일 AIP 계열 (D2, D6 근거)
|
||||
- [[raw/official-docs/api-versioning-google-aip-180]] — 동일 AIP 계열 (D6 cross-cite)
|
||||
- (신설 권고) `raw/official-docs/google-aip-151-long-running-operations` — D17 (LRO) 정당화 + `:cancel` verb 명칭
|
||||
- (신설 권고) `raw/official-docs/google-aip-231-batch-methods` — B18 (bulk operation) 정당화 + `:batchCreate` verb 명칭
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-148 — Standard Fields (name · uid · display_name · parent)"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/148
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-resource-identifier-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, google-aip, api-contract]
|
||||
created: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-148 — Standard Fields
|
||||
|
||||
> Layer: `raw/official-docs/` — Google API Improvement Proposal 148 의 원문 발췌·출처 기록.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-resource-identifier-contract]] | D5 (ID generation layer) — `name` 은 server-assigned 가 기본 관례임을 AIP-122 참조로 명시; D6 (prefix 정책) — Google-style 은 typed prefix 없이 flat `name` 필드 단일 식별자; D8 (PII/GDPR) — `uid` 는 opaque system-assigned 로 `display_name` 과 명확히 분리됨; D13 (multi-tenancy) — `parent` 필드로 계층적 resource name 패턴(`collection/{id}/sub-collection/{id}`) 공식화 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/148
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Google LLC (AIP editors)
|
||||
- 발행일: 최초 발행일 미명시; Changelog 기준 최신 수정 2023-10-05
|
||||
- 마지막 확인일: 2026-05-31
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
Google AIP-148 은 Google Cloud API 전반에 적용되는 **표준 필드 명명 규범**이다. `name`(resource identifier) · `uid`(system-assigned opaque UUID4) · `display_name`(사람 친화 가변 필드) · `parent`(계층 resource name) 의 정의와 의무(`MUST`/`SHOULD`) 를 직접 기술하며, ca-skeleton 의 D5/D6/D8/D13 결정의 타사 선례 근거로 보관한다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 3~5문장)
|
||||
|
||||
Self-Grep 통과 확인: `/tmp/source-fetch-1780197218.txt` 기준.
|
||||
|
||||
> [§Resource names and IDs / name] "Every resource must have a string name field, used for the resource name (AIP-122), which should be the first field in the resource."
|
||||
> (line 15 in fetched text)
|
||||
|
||||
> [§Well known string fields / uid] "The output only string uid field refers to a system-assigned unique identifier for a resource. When provided, this field must be a UUID4 and must specify this format via the UUID4 format extension (see AIP-202). Declarative-friendly resources should include this field."
|
||||
> (line 95 in fetched text)
|
||||
|
||||
> [§Other names / display_name] "The string display_name field must be a mutable, user-settable field where the user can provide a human-readable name to be used in user interfaces. Declarative-friendly resources should include this field."
|
||||
> (line 27 in fetched text)
|
||||
|
||||
> [§Other names / display_name — uniqueness] "Display names should not have uniqueness requirements, and should be limited to <= 63 characters."
|
||||
> (line 29 in fetched text)
|
||||
|
||||
> [§Resource names and IDs / parent] "The string parent field refers to the resource name of the parent of a collection, and should be used in most List (AIP-132) and Create (AIP-133) requests."
|
||||
> (line 21 in fetched text)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP148-C1 | 모든 resource 는 `string name` 필드를 가져야 한다 (MUST). 이 필드는 resource name 용도로 사용되며 첫 번째 필드여야 한다 (SHOULD). | [§name] "Every resource must have a string name field, used for the resource name (AIP-122), which should be the first field in the resource." | `official-vendor-doc` | Google Cloud API 스타일을 따르는 REST/gRPC API | `name` 이 server-assigned 임을 직접 명시하지 않음 (AIP-122 위임) |
|
||||
| AIP148-C2 | `uid` 는 시스템이 할당한 output-only UUID4 필드로, 변경 불가·opaque 한 단일 식별자다 | [§uid] "The output only string uid field refers to a system-assigned unique identifier for a resource. When provided, this field must be a UUID4 and must specify this format via the UUID4 format extension (see AIP-202)." | `official-vendor-doc` | Google Cloud API 스타일 resource | `uid` 가 삭제 후 재생성 시 재사용 금지임을 이 AIP 가 직접 명시하지 않음 (AIP-164 위임); ULID/TSID 등 타 형식의 우열을 판단하지 않음 |
|
||||
| AIP148-C3 | `display_name` 은 mutable·user-settable 이며 UI 표시용 human-readable name 이다 (MUST). uniqueness 요건이 없어야 하며 (SHOULD NOT) 63자 이하로 제한해야 한다 (SHOULD). | [§display_name] "The string display_name field must be a mutable, user-settable field where the user can provide a human-readable name to be used in user interfaces." / "Display names should not have uniqueness requirements, and should be limited to <= 63 characters." | `official-vendor-doc` | Google AIP 스타일 resource | `display_name` 이 PII 해당 여부를 판단하지 않음; 길이 63자 제한이 모든 도메인에 적용되는지 증명하지 않음 |
|
||||
| AIP148-C4 | `parent` 필드는 collection 의 부모 resource name 을 참조하며, 대부분의 List·Create 요청에 사용해야 한다 (SHOULD). 이는 계층적 resource naming 패턴을 공식화한다. | [§parent] "The string parent field refers to the resource name of the parent of a collection, and should be used in most List (AIP-132) and Create (AIP-133) requests." | `official-vendor-doc` | 다단계 계층 구조를 가진 Google AIP 스타일 API | `parent` 의 구체적인 path 형식(`collection/{id}/sub-collection/{id}`) 을 이 AIP 가 직접 정의하지 않음 (AIP-122 위임) |
|
||||
| AIP148-C5 | Standard fields 는 해당 개념 설명에만 사용해야 하며 (SHOULD), 다른 목적으로 사용해서는 안 된다 (SHOULD NOT). | [§Guidance] "Standard fields should be used to describe their corresponding concept, and should not be used for any other purpose." | `official-vendor-doc` | AIP-148 이 정의하는 모든 standard field | 이 원칙이 Google 외부 API 설계에 의무 적용된다는 것을 증명하지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `AIP148-C1`: Google AIP 스타일 API 에서 resource name 필드 이름은 `name` 이어야 한다.
|
||||
- `AIP148-C2`: `uid` 는 UUID4 형식의 system-assigned output-only 필드다. client 가 할당하지 않는다.
|
||||
- `AIP148-C3`: `display_name` 은 `uid`/`name` 과 별개의 mutable UI 표시용 필드이며, uniqueness 는 요구하지 않는다.
|
||||
- `AIP148-C4`: 계층 resource 간 부모 참조는 `parent` 필드로 표현한다.
|
||||
- `AIP148-C5`: standard field 명칭은 해당 개념 외 다른 목적에 재사용 금지.
|
||||
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `uid` 가 삭제·재생성 후에도 재사용 금지인지 (AIP-164 로 위임됨).
|
||||
- `name` 이 반드시 server-assigned 인지 (AIP-122 로 위임됨 — AIP-148 자체는 server-assigned 를 직접 강제하지 않음).
|
||||
- ULID / UUID v7 / KSUID 등 Google이 사용하지 않는 형식의 우열.
|
||||
- Google Cloud 외부 팀(예: ca-skeleton) 이 AIP-148 을 준수해야 할 의무.
|
||||
- `uid` UUID4 형식이 Java `java.util.UUID` 의 `randomUUID()` 와 동일한지 (구현 세부사항은 AIP-202 위임).
|
||||
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- AIP-122 (`name` 의 resource naming 형식 및 server-assigned 여부) 별도 raw 보관 필요.
|
||||
- AIP-164 (`uid` 재사용 금지 / soft-delete 후 ID 영구성) 별도 raw 보관 필요.
|
||||
- ca-skeleton 이 `parent` 패턴을 multi-tenancy 에 실제로 적용할지 (D13 결정 시 AIP-122 + 실제 path 설계 병행 필요).
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-148 은 Google 내부 convention 을 공개한 문서이며, IETF RFC 나 ISO 표준이 아니다. `official-vendor-doc` 강도로 취급한다.
|
||||
- `uid` 의 UUID4 강제는 ca-skeleton D1 (format 결정) 에 직접 영향을 주지는 않는다 — ca-skeleton 의 `TicketId` 가 Google AIP `uid` 와 동일 역할은 아니기 때문. 단, *server-assigned opaque UUID4 가 industry 표준 패턴임* 을 뒷받침하는 선례로 사용 가능.
|
||||
- D6 (prefix 정책): AIP-148 은 typed prefix(`tk_`, `usr_`) 를 정의하지 않는다. `name` 단일 필드로 flat 식별. Stripe-style prefix 와의 비교 근거로 "Google 은 flat" 사실을 사용 가능.
|
||||
- D8 (PII): AIP-148 이 `uid` ↔ `display_name` 분리를 정의하나, PII 여부 판단은 이 AIP 범위 밖. GDPR Article 4(1) raw 별도 보관 필요.
|
||||
- 추가로 봐야 할 동일 출처 페이지: [AIP-122](https://google.aip.dev/122) (Resource names), [AIP-202](https://google.aip.dev/202) (Field formats), [AIP-164](https://google.aip.dev/164) (Soft delete).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 동일 주제 다른 official-doc: [[raw/official-docs/api-versioning-google-aip-180]] (같은 AIP 계열 기보관)
|
||||
- AIP-122 (Resource names, server-assigned naming): 미보관 — 별도 `raw/official-docs/google-aip-122-resource-names.md` 로 수집 권고
|
||||
- AIP-164 (Soft delete, uid 영구성): 미보관 — 별도 수집 권고
|
||||
- 이 자료를 인용한 wiki 요약: (미생성 — `/ingest` 후 `wiki/concepts/resource-identifier-conventions.md` 예정)
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-151 — Long-Running Operations"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/151
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, long-running-operation]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-151 — Long-Running Operations
|
||||
|
||||
> Layer: `raw/official-docs/` — 외부 공식 문서의 원문 발췌·출처 기록.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
> 이 자료는 **혼자 존재하지 않는다.** 어느 branch 의 구현 결정의 **근거**로서 보관됨.
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | D17: Long-running operation (LRO) 응답 = 202 Accepted + `Location: /v1/operations/{id}` + envelope `data.{operationId,statusUrl}` · polling endpoint `GET /v1/operations/{id}` 의 status enum {PENDING, RUNNING, SUCCEEDED, FAILED, CANCELLED} — 현재 UNSUPPORTED_DECISION 라벨을 본 raw 의 normative reference 로 해소 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/151
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Google (API Improvement Proposals 커뮤니티)
|
||||
- 발행일: 2019-07-25
|
||||
- 마지막 수정일: 2025-02-04 (Changelog 기준 — errors 섹션 명료화)
|
||||
- 마지막 확인일: 2026-05-31
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-api-contract-baseline` 의 D17 결정(LRO 응답 패턴 — 202 Accepted + Location + polling)이 `UNSUPPORTED_DECISION` 상태였으며, Google AIP-151 이 해당 결정의 1차 normative reference 로 지목되었다. AIP-151 은 비동기 long-running operation 의 응답 형식(`google.longrunning.Operation`)·done/result/error 분기·polling 방식을 정의하며, 본 branch 의 HTTP REST 매핑의 설계 근거로 활용된다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> Self-Grep 통과 — 모든 인용은 `/tmp/source-fetch-1780186240.txt` 에서 `grep -nF` 로 존재 확인됨.
|
||||
|
||||
> [§Preamble, line 792] "Occasionally, an API may need to expose a method that takes a significant amount of time to complete."
|
||||
|
||||
> [§Preamble, line 798–799] "Essentially, the user is given a token that can be used to track progress and retrieve the result."
|
||||
|
||||
> [§Guidance, line 803–805] "Individual API methods that might take a significant amount of time to complete should return a `google.longrunning.Operation` object instead of the ultimate response message."
|
||||
|
||||
> [§Guidance / validate-only, line 857–859] "A successful response with an Operation which is already complete, with the `done` field set to `true`, and a valid (but potentially empty) response message in the `response` field, wrapped in a `google.protobuf.Any` message."
|
||||
|
||||
> [§Guidance / validate-only, line 865–867] "An Operation with the `done` field set to `false`, to indicate long-running validation. In this case, the `name` field must be set, to allow clients to poll the long-running validation operation until it has completed."
|
||||
|
||||
> [§Guidance / validate-only, line 870–872] "Unsuccessful validation must eventually be represented by an operation with `done=true` and the error details provided in the `error` field."
|
||||
|
||||
> [§Errors, line 926–928] "Operations that fail during their execution phase must return an error response (AIP-193), placed in the `Operation.error` `google.rpc.Status` field."
|
||||
|
||||
> [§Note / thumb rule, line 877–879] "User expectations can vary on what is considered 'a significant amount of time' depending on what work is being done. A good rule of thumb is 10 seconds."
|
||||
|
||||
> [§Guidance, line 829–830] "The method must include a `google.longrunning.operation_info` annotation, which must define both response and metadata types."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP151-C1 | 처리 시간이 "significant"한 메서드는 최종 응답 대신 `google.longrunning.Operation` 객체를 반환해야 한다 | [§Guidance, line 803–805] "Individual API methods that might take a significant amount of time to complete should return a `google.longrunning.Operation` object instead of the ultimate response message." | `official-reference` | Google API Design Guide 를 따르는 protobuf/gRPC 기반 API | "significant"의 threshold 를 직접 숫자로 normative 정의하지 않음 (thumb rule 10초는 참고값). REST API 에 그대로 적용 시 HTTP 202 + Location 매핑은 본 문서 외 별도 결정 필요 |
|
||||
| AIP151-C2 | `google.longrunning.operation_info` annotation 에 `response_type` 과 `metadata_type` 양쪽 모두 정의해야 한다 | [§Guidance, line 829–830] "The method must include a `google.longrunning.operation_info` annotation, which must define both response and metadata types." | `official-reference` | Google protobuf/gRPC API | `response_type` / `metadata_type` 의 구체적 내용(필드명, 구조)은 각 API가 정의. REST 매핑에서 이 annotation 이 없어도 기능은 동작할 수 있음 — 규약 준수 여부 문제 |
|
||||
| AIP151-C3 | Operation 이 완료(`done=true`)되면 `response` 필드에 유효한 응답 메시지가 있어야 한다 | [§Guidance/validate-only, line 857–859] "A successful response with an Operation which is already complete, with the `done` field set to `true`, and a valid (but potentially empty) response message in the `response` field, wrapped in a `google.protobuf.Any` message." | `official-reference` | `done=true` 인 Operation 의 성공 분기 | `response` 필드의 구체적 shape 는 API 별로 다름. HTTP REST 전환 시 `result` 객체의 JSON 표현 방식은 본 AIP 외 별도 결정 |
|
||||
| AIP151-C4 | 진행 중인 Operation 은 `done=false` 이며, `name` 필드가 반드시 설정되어야 클라이언트가 polling 할 수 있다 | [§Guidance/validate-only, line 865–867] "An Operation with the `done` field set to `false`, to indicate long-running validation. In this case, the `name` field must be set, to allow clients to poll the long-running validation operation until it has completed." | `official-reference` | 진행 중(`done=false`) Operation 의 polling 패턴 | `name` 필드의 구체적 형식(예: `operations/{id}`)은 AIP-122 (Resource names) 가 별도 정의. REST HTTP 응답의 `Location` header 와 `name` 필드의 매핑은 본 AIP 가 normative 하게 규정하지 않음 |
|
||||
| AIP151-C5 | 실패한 Operation 은 최종적으로 `done=true` + `error` 필드에 오류 상세가 담겨야 한다 | [§Guidance/validate-only, line 870–872] "Unsuccessful validation must eventually be represented by an operation with `done=true` and the error details provided in the `error` field." | `official-reference` | `done=true` 인 Operation 의 실패 분기 | `error` 필드의 구조는 `google.rpc.Status` — REST 매핑 시 HTTP 상태 코드와의 관계는 AIP-193 (Errors) 가 별도 정의. `FAILED`/`CANCELLED` 같은 상태 enum 어휘는 본 AIP 에 없음 |
|
||||
| AIP151-C6 | 실행 단계에서 실패한 Operation 의 오류는 `Operation.error` 의 `google.rpc.Status` 필드에 위치해야 한다 | [§Errors, line 926–928] "Operations that fail during their execution phase must return an error response (AIP-193), placed in the `Operation.error` `google.rpc.Status` field." | `official-reference` | Operation 실행 중 발생한 terminal error | non-terminal error(중간 경고 등)는 `metadata` 에 위치 가능. HTTP REST 전환 시 `google.rpc.Status` → JSON error 객체 매핑은 별도 작업 |
|
||||
| AIP151-C7 | 'significant amount of time' 의 참고 기준은 10초이며, 이 기준은 사용자 기대치와 작업 종류에 따라 달라질 수 있다 | [§Note, line 877–879] "User expectations can vary on what is considered 'a significant amount of time' depending on what work is being done. A good rule of thumb is 10 seconds." | `official-reference` | LRO 적용 여부 판단 시 참고 기준 | 10초는 thumb rule(참고값)이며 normative threshold 아님. API 설계자가 컨텍스트에 따라 다른 기준 적용 가능 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
### 이 자료가 직접 증명하는 것
|
||||
|
||||
- `AIP151-C1`: 장시간 처리 메서드는 최종 응답 대신 `google.longrunning.Operation` 을 반환해야 함 (Google API Design Guide 기준)
|
||||
- `AIP151-C2`: `operation_info` annotation 에 `response_type` + `metadata_type` 양쪽 정의 의무
|
||||
- `AIP151-C3`: 성공 완료(`done=true`) 시 `response` 필드에 유효한 응답 메시지 존재
|
||||
- `AIP151-C4`: 진행 중(`done=false`) 시 `name` 필드 MUST 설정 (polling 가능 조건)
|
||||
- `AIP151-C5`: 실패 완료(`done=true`) 시 `error` 필드에 오류 상세 존재
|
||||
- `AIP151-C6`: 실행 단계 실패 오류는 `Operation.error` (`google.rpc.Status`) 에 위치
|
||||
- `AIP151-C7`: "significant time" 의 참고 기준 = 10초 (thumb rule, non-normative threshold)
|
||||
|
||||
### 이 자료가 증명하지 않는 것
|
||||
|
||||
- **AIP-151 은 IETF/W3C 표준이 아님**: Google API design community guideline (`official-reference` strength). 특정 HTTP 표준이나 REST 규범을 대체하지 않음. 다른 API 설계 조직이 이를 따를 의무 없음.
|
||||
- **Protobuf 컨텍스트 우선**: AIP-151 의 `Operation` resource, `done/result/error`, `operation_info` annotation 은 protobuf 정의. REST/JSON API 에 적용 시 다음은 normative 하지 않음:
|
||||
- HTTP 202 응답 상태 코드 (RFC 9110 §15.3.3 영역)
|
||||
- `Location` response header (RFC 9110 §10.2.2 영역)
|
||||
- JSON envelope `data.operationId` / `data.statusUrl` 필드 명명
|
||||
- polling endpoint URL 패턴 (`/v1/operations/{id}`)
|
||||
- **`name` 필드 형식**: AIP-151 은 `name` 이 설정되어야 한다고만 명시. 구체적 형식(`operations/{id}` 등)은 AIP-122 (Resource names) 가 정의.
|
||||
- **status enum 어휘**: 본 branch 의 `{PENDING, RUNNING, SUCCEEDED, FAILED, CANCELLED}` 5종 enum 은 **AIP-151 에 없음**. AIP-151 은 `done` (boolean) + `result` (oneof response/error) 의 이진 완료 모델만 정의. 5종 enum 은 project-internal 매핑 — AIP-151 이 직접 보증하지 않음.
|
||||
- **Operation 간 선후 관계**: AIP-151 의 `name` field 가 resource name 기반임을 시사하지만 operation 의 순서/큐잉은 본 문서 범위 밖.
|
||||
- **Cancellation method**: AIP-151 HTML 본문에서 cancellation (`operations/{id}:cancel`) 에 대한 명시적 normative 진술 추출 불가 — 별도 확인 필요 (`needs-confirmation`).
|
||||
|
||||
### 내 프로젝트에 적용하려면 추가 확인이 필요한 것
|
||||
|
||||
- **REST HTTP 매핑의 normative 근거 보강**: 202 Accepted + `Location` header 의 정규 근거는 RFC 9110 §15.3.3 (202) + §10.2.2 (Location) — `feature-api-contract-baseline` 의 RFC9110-C22 (§15.3.3) + RFC9110-C21 (§10.2.3 Retry-After) 발췌 완료 후 D17 의 나머지 HTTP 계층 근거 채움.
|
||||
- **status enum 5종의 별도 설계 근거**: PENDING/RUNNING/SUCCEEDED/FAILED/CANCELLED 가 AIP-151 의 done/error 이진 모델과 매핑되는 방식은 ca-skeleton project-internal 결정 — project note 또는 별도 decision record 로 명시 필요.
|
||||
- **envelope 형식(`data.operationId`, `data.statusUrl`) 매핑**: AIP-151 의 `Operation.name` / `done` / `result` 와 본 branch envelope 간 매핑은 `feature-schema-serialization-contract` 또는 project note 에서 별도 결정 필요.
|
||||
- **polling endpoint URL pattern(`/v1/operations/{id}`)의 근거**: AIP-122 (Resource names) + AIP-151 `name` field 의 형식 정의를 추가로 확인 필요.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-151 의 `Operation` resource 는 `google.longrunning.Operation` proto 정의로 `name` (string), `metadata` (Any), `done` (bool), `error` (google.rpc.Status), `response` (Any) 필드로 구성. HTML 파싱에서 proto 정의 코드 블록 추출이 부분적으로 이루어졌고, 필드 목록 전체는 공식 proto reference (https://cloud.google.com/apis/design/design_patterns#long_running_operations) 에서 추가 확인 권장.
|
||||
- AIP-151 의 Changelog 에서 2025-02-04 업데이트가 errors 섹션 명료화 — 본 발췌의 `AIP151-C6` 근거 섹션.
|
||||
- Cancellation (`operations/{id}:cancel`) 은 AIP-151 본문 텍스트에서 verbatim 발췌 불가 (파싱된 plain text 에 미포함 가능). 공식 proto reference 또는 AIP 원문 직접 확인 필요.
|
||||
- AIP 의 `official-reference` strength: Google AIP 는 Google 내부 + 커뮤니티 guideline 이며 IETF/W3C 수준의 국제 표준 아님. 단 Google Cloud API, gRPC, Protobuf 를 활용하는 프로젝트에서는 사실상 표준 (de facto). `official-vendor-doc` 보다 약하고 `official-standard` 보다 확실히 약함.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/api-versioning-google-aip-180]] — AIP-180 (backward compatibility), 같은 AIP 시리즈
|
||||
- [[raw/official-docs/google-aip-185-resource-versioning]] — AIP-185 (resource versioning), 같은 AIP 시리즈
|
||||
- [[raw/official-docs/rfc9110-http-semantics]] — RFC 9110 §15.3.3 202 Accepted + §10.2.2 Location + §10.2.3 Retry-After — D17 LRO 의 HTTP 계층 normative 근거
|
||||
- (미등록, 예정) [[raw/official-docs/google-aip-122-resource-names]] — Operation `name` 필드 형식 규칙
|
||||
- (미등록, 예정) `wiki/concepts/long-running-operation-pattern` — 본 raw 를 인용한 canonical 요약 (생성 시)
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-158 — Pagination"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/158
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, google-aip, cursor-pagination, offset-pagination, page-token]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-158 — Pagination
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | D18 보강: pagination `size` max cap + 0-indexed `page` 결정의 normative reference 추가 (JSON:API 는 size cap / index base 에 agnostic — AIP-158 가 server-side cap 을 규범적으로 권고하는 유일한 공식 출처). 또한 D18 의 깊은 offset → cursor 권고와 cursor endpoint 미결정(future B16)의 normative 근거: AIP-158 가 cursor-based pagination (page_token opaque) 의 공식 권고 source. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/158
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Google (API Improvement Proposals — googleapis.github.io community)
|
||||
- 발행일: 2019-02-18 (created); 2019-02-18 (last updated per AIP changelog)
|
||||
- 마지막 확인일: 2026-05-31
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-api-contract-baseline` D18 의 `UNSUPPORTED_DECISION` 상태를 해소하기 위해 보관. JSON:API (JSONAPI-PAGE-C1~C6) 는 pagination 전략에 agnostic 이고 size cap 의 normative 진술이 없으나, AIP-158 는 `page_size` server-side cap ("should coerce down to the maximum permitted page size") 과 `page_token` 의 opaque-cursor 권고를 normatively 정의한다. 또한 D18 의 cursor 권고(깊은 offset → cursor로 이전)와 향후 cursor endpoint 설계(future B16)의 normative source 역할.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Guidance / 도입부] "APIs often need to provide collections of data, most commonly in the List standard method. However, collections can often be arbitrarily sized, and also often grow over time, increasing lookup time as well as the size of the responses being sent over the wire. Therefore, it is important that collections be paginated."
|
||||
> — line 681–685 in fetched text
|
||||
|
||||
> [§Guidance / page_size] "The page_size field must not be required. If the user does not specify page_size (or specifies 0), the API chooses an appropriate default, which the API should document. The API must not return an error. If the user specifies page_size greater than the maximum permitted by the API, the API should coerce down to the maximum permitted page size. If the user specifies a negative value for page_size, the API must send an INVALID_ARGUMENT error."
|
||||
> — lines 726–733 in fetched text
|
||||
|
||||
> [§Guidance / page_token] "The page_token field must not be required. If the user changes the page_size in a request for subsequent pages, the service must honor the new page size. The user is expected to keep all other arguments to the RPC the same; if any arguments are different, the API should send an INVALID_ARGUMENT error."
|
||||
> — lines 739–744 in fetched text
|
||||
|
||||
> [§Guidance / next_page_token] "If the end of the collection has been reached, the next_page_token field must be empty. This is the only way to communicate 'end-of-collection' to users. If the end of the collection has not been reached (or if the API can not determine in time), the API must provide a next_page_token."
|
||||
> — lines 753–757 in fetched text
|
||||
|
||||
> [§Opacity] "Page tokens provided by APIs must be opaque (but URL-safe) strings, and must not be user-parseable. This is because if users are able to deconstruct these, they will do so. This effectively makes the implementation details of your API's pagination become part of the API surface, and it becomes impossible to update those details without breaking users."
|
||||
> — lines 782–786 in fetched text
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP158-C1 | Collections 는 paginated 되어야 하며, pagination 은 처음부터 제공해야 한다 (나중에 추가 시 backward-incompatible) | [§Guidance 도입부] "collections can often be arbitrarily sized [...] Therefore, it is important that collections be paginated." + "RPCs returning collections of data must provide pagination at the outset, as it is a backwards-incompatible change to add pagination to an existing method." | `official-reference` | List 메서드를 갖는 모든 API collection | 특정 컬렉션의 크기 threshold 를 정의하지 않음; "arbitrarily sized" 는 서술 |
|
||||
| AIP158-C2 | `page_size` 는 required 가 아니어야 하며, API 최대값 초과 시 server 가 최대값으로 cap 적용해야 한다 (SHOULD) | [§Guidance / page_size] "The page_size field must not be required. [...] If the user specifies page_size greater than the maximum permitted by the API, the API should coerce down to the maximum permitted page size." | `official-reference` | `page_size` request field 를 노출하는 모든 List 메서드 | 최대값의 구체적인 숫자(예: 100, 1000)를 normative 하게 지정하지 않음 — server-defined; cap 이 SHOULD 이므로 강제 아님 (coerce vs reject 선택) |
|
||||
| AIP158-C3 | `page_token` 은 required 가 아니어야 하며, 이후 page 요청에서 `page_size` 를 변경하면 service 가 새 page_size 를 honor 해야 한다 (MUST) | [§Guidance / page_token] "The page_token field must not be required. If the user changes the page_size in a request for subsequent pages, the service must honor the new page size." | `official-reference` | cursor-based pagination 을 구현하는 모든 List 메서드 | page_token 의 내부 encoding (base64, JWE 등) 은 normative 영역 밖 |
|
||||
| AIP158-C4 | 컬렉션 끝에 도달하면 `next_page_token` 은 empty 여야 하며(MUST), 이것이 end-of-collection 을 표시하는 유일한 방법이다 | [§Guidance / next_page_token] "If the end of the collection has been reached, the next_page_token field must be empty. This is the only way to communicate 'end-of-collection' to users." | `official-reference` | cursor-based pagination 응답의 `next_page_token` field | total_size 를 포함할 수 있으나 선택적(may); total 추정값은 별도로 명시적 문서화 권고 |
|
||||
| AIP158-C5 | page token 은 opaque (URL-safe) string 이어야 하며(MUST), user-parseable 이면 안 된다(MUST NOT); base64 encoding 만으로는 불충분한 obfuscation | [§Opacity] "Page tokens provided by APIs must be opaque (but URL-safe) strings, and must not be user-parseable. [...] Warning: Base-64 encoding an otherwise-transparent page token is not a sufficient obfuscation mechanism." | `official-reference` | cursor token 을 외부 클라이언트에 노출하는 모든 paginated API | token 의 구체적인 encoding 방식(proto 직렬화, JWE, HMAC 등)은 normative 하게 지정하지 않음 — implementation 선택 영역 |
|
||||
|
||||
### Strength 허용값 참고
|
||||
|
||||
본 문서의 모든 Claim 은 `official-reference` 로 분류한다. AIP (API Improvement Proposals) 는 Google 내부 community guideline 으로 IETF/W3C 국제 표준과 다르며 (`official-standard` 아님), 특정 vendor 의 제품 문서도 아님 (`official-vendor-doc` 아님). REST API 설계 community 에서 널리 참조되는 공식 reference 문서.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `AIP158-C1`: pagination 은 List 메서드에 처음부터 제공해야 하며 나중에 추가 시 backward-incompatible change
|
||||
- `AIP158-C2`: `page_size` server-side cap 이 Google 공식 guideline 에서 권고되는 표준 패턴임 (`should coerce down`)
|
||||
- `AIP158-C3`: cursor-based pagination 에서 `page_token` 은 optional (MUST NOT be required)
|
||||
- `AIP158-C4`: `next_page_token` empty = end-of-collection 의 유일한 공식 시그널
|
||||
- `AIP158-C5`: page token 은 opaque + URL-safe 이어야 하며 base64 만으로 부족함
|
||||
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- AIP-158 는 Google community guideline (`official-reference`) 이며 IETF/W3C 공식 표준(`official-standard`) 이 아님 — 모든 REST API 에 법적 구속력이 있는 표준 아님
|
||||
- `page_size` 최대값의 **구체적인 숫자** (예: 100, 1000) 는 AIP-158 의 normative 영역 밖 — "server-defined" 라고만 명시. `feature-api-contract-baseline` D18 의 max=100 은 project-internal trade-off 유지
|
||||
- cursor token 의 구체적인 encoding (proto 직렬화, JWE, HMAC, base64url 등) 은 본 인용 범위 밖 — implementation 선택 영역
|
||||
- AIP-158 의 `page_size`/`page_token` 필드명은 protobuf + gRPC 컨텍스트 기반. REST JSON API 에서 동일 필드명 강제는 아님 — `feature-api-contract-baseline` 은 `page`/`size` 파라미터 명칭 사용 (Spring `Pageable` 정합)
|
||||
- `ca-tmpl` 의 기본 pagination 이 cursor-based 임을 의미하지 않음. `feature-api-contract-baseline` D7/D18 은 offset-based (`page`/`size`) 우선 채택 — 본 raw 는 cursor *대안 정당화* 와 향후 cursor endpoint 설계(future B16)의 normative source 역할
|
||||
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `page_size > 100` 요청을 400 VALIDATION_FAILED 로 *reject* 할지 vs AIP-158 권고처럼 *coerce down* 할지는 project-internal 결정 (D18 는 400 reject 채택 — UNSUPPORTED_IMPL_DECISION 유지)
|
||||
- cursor endpoint 의 구체적인 token shape (base64url-encoded proto? JWE? HMAC-signed?) 는 future B16 결정 전까지 미정
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-158 changelog: "2019-07-19: Update the opacity requirement from 'should' to 'must'." — opaque 요건이 SHOULD 에서 MUST 로 강화된 이력 있음. 현재 normative strength 는 MUST.
|
||||
- AIP-158 은 protobuf 메시지 포맷으로 예시를 작성하나 §Opacity 와 §Backwards compatibility 의 원칙은 REST JSON API 에도 동일하게 적용 가능.
|
||||
- `total_size` (int32) field 는 선택적(may) 이며 추정값도 허용 — D18 의 `meta.page.total` 과 의미 일치하나 "추정값" 허용 범위는 project 결정 필요.
|
||||
- 추가로 봐야 할 동일 출처 페이지: AIP-132 (List method standard), AIP-160 (Filtering), AIP-159 (Reading across collections).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제의 다른 official-doc:
|
||||
- [[raw/official-docs/jsonapi-pagination-format]] — JSON:API pagination link 표준 (D7 근거, size cap 에 agnostic)
|
||||
- [[raw/official-docs/rfc9110-http-semantics]] — HTTP semantics 기반 (pagination 자체보다 HTTP status 관련)
|
||||
- [[raw/official-docs/google-aip-185-resource-versioning]] — 동일 AIP 계열, D2 근거
|
||||
- [[raw/official-docs/api-versioning-google-aip-180]] — 동일 AIP 계열, D6 근거
|
||||
- 이 자료를 인용한 branch-note: [[raw/branch-notes/feature-api-contract-baseline]] (D18 Supporting Claim 보강)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크 추가)
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-160 — Filtering"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/160
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-design, api-contract, filtering, google-aip]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-160 — Filtering
|
||||
|
||||
> Layer: `raw/official-docs/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
> 이 자료는 **혼자 존재하지 않는다.** 어느 branch의 구현 결정의 **근거**로서 보관됨.
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 | Decision Evidence Map 상태 |
|
||||
|---|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | Filter parameter syntax 선택지 중 하나로 Google AIP-160 DSL 의 존재·정의·예제 syntax 를 기록. `flat ?key=value` / `RSQL` / `JSON:API filter[key]` 대비 AIP-160 DSL 의 옵션을 정당화하는 1차 근거. | `UNSUPPORTED_DECISION` — 본 branch 의 Decision Evidence Map 에 B15 (filter syntax 채택) 행이 아직 없음. 본 raw 는 옵션 존재와 예제 syntax 만 기록하며, AIP-160 DSL **채택** 결정 자체는 별도 trade-off 분석 후 branch 에 Decision row 로 신설 필요. |
|
||||
|
||||
### Decision Evidence Map 체크
|
||||
|
||||
> 본 raw source 가 연결되어야 할 branch Decision 의 현재 상태를 명시한다. hook contract 준수.
|
||||
|
||||
| 대상 Branch | 연결 대상 Decision ID | 현재 상태 | 해소 조건 |
|
||||
|---|---|---|---|
|
||||
| `feature-api-contract-baseline` | B15 (filter syntax 결정) | `UNSUPPORTED_DECISION` — Decision row 자체가 branch 에 미존재 | branch 에 D19 또는 B15 row 를 신설하고 `Supporting Claims: AIP160-C1, AIP160-C2` 로 연결 시 해소 |
|
||||
|
||||
**중요**: `AIP160-C1`~`AIP160-C6` 는 AIP-160 DSL 의 *옵션 존재* 와 *syntax 명세* 를 지지한다. DSL 채택 결정(`feature-api-contract-baseline` §Decision Evidence Map 의 미래 row)이 생성되기 전까지 이 raw 는 **evidence pool** 에 있는 상태이며, 어떤 branch decision 의 Supporting Claim 으로도 아직 참조되지 않는다. raw 만으로 "AIP-160 을 채택한다"는 결론을 내리는 것은 금지됨.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/160
|
||||
- 아카이브 URL: (미기입)
|
||||
- 저자 / 조직: Google (API Improvement Proposals community)
|
||||
- 발행일: 미확인 (AIP 문서는 버전 이력 없이 갱신됨)
|
||||
- 마지막 확인일: 2026-05-31
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-api-contract-baseline` 의 §범위에 filtering 이 in-scope 로 listed 됐으나 filter parameter syntax (AIP-160 DSL vs RSQL vs flat `?key=value` vs JSON:API) 의 정확한 결정이 없다. AIP-160 은 Google 이 공식 채택한 filter string DSL 의 명세이므로, 채택 여부 결정을 위한 trade-off 분석 이전에 DSL 의 정의·연산자·예제를 verbatim 으로 보존한다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5개)
|
||||
|
||||
> [§Filtering in list methods] "When employing filtering, a request message should have exactly one filtering field, string filter."
|
||||
> — 위치: AIP-160 §Guidance > Filtering in list methods (line 9 in fetched text)
|
||||
|
||||
> [§Has operator] "Filtering implementations must provide the : operator, which means 'has'. Its semantics differ based upon the type of the field."
|
||||
> — 위치: AIP-160 §Operators > Has operator (line 61 in fetched text)
|
||||
|
||||
> [§Schematic validation] "If a non-compliant or schematically invalid filter string is specified, the API should error with INVALID_ARGUMENT."
|
||||
> — 위치: AIP-160 §Operators > Schematic validation (line 78 in fetched text)
|
||||
|
||||
> [§Negation] "A service that supports negation must support both formats."
|
||||
> — 위치: AIP-160 §Operators > Negation (line 33 in fetched text); 두 formats = `NOT a` 와 `-a`
|
||||
|
||||
> [§Traversal operator] "The . operator must not be used to traverse through a repeated field."
|
||||
> — 위치: AIP-160 §Operators > Traversal operator (line 58 in fetched text)
|
||||
|
||||
> [§String values] "String values require quotes if they contain special characters. Single quotes and double quotes are both accepted."
|
||||
> — 위치: AIP-160 §Operators > String values (line 69 in fetched text)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP160-C1 | AIP-160 은 filtering 을 사용하는 List method 에 대해 `string filter` 라는 단일 필드를 사용하도록 권고한다 | [§Filtering in list methods] "When employing filtering, a request message should have exactly one filtering field, string filter." | `official-reference` | Google AIP 를 준수하는 List method API | `string filter` 외의 복합 파라미터 방식 (예: `?filter[key]=value`) 을 금지하지 않음 — SHOULD 수준 권고 |
|
||||
| AIP160-C2 | AIP-160 DSL 은 `:` (has), `.` (traversal), `=`/`!=`/`<`/`>`/`<=`/`>=` (comparison), `AND`/`OR`/`NOT`/`-` (logical) 연산자를 정의한다. `:` 는 구현 MUST 이며 나머지는 기능별 선택 | [§Has operator] "Filtering implementations must provide the : operator, which means 'has'." | `official-reference` | AIP-160 filter DSL 을 구현하는 서버 | 어떤 언어·프레임워크에서 파싱해야 하는지, 파서 구현 방법 — AIP-160 은 syntax 만 정의 |
|
||||
| AIP160-C3 | AIP-160 을 위반하거나 schema 를 벗어나는 filter string 에 대해 API 는 `INVALID_ARGUMENT` 로 에러 반환해야 한다 (SHOULD) | [§Schematic validation] "If a non-compliant or schematically invalid filter string is specified, the API should error with INVALID_ARGUMENT." | `official-reference` | AIP-160 filter DSL 을 구현하는 서버 | MUST 가 아닌 SHOULD — 구현체가 이를 무시해도 표준 위반이 아님. 에러 메시지 포맷 / gRPC status code 대응은 AIP-160 범위 밖 |
|
||||
| AIP160-C4 | AIP-160 DSL 에서 부정(negation)을 지원하는 서버는 `NOT a` 와 `-a` 두 형식을 모두 MUST 지원해야 한다 | [§Negation] "A service that supports negation must support both formats." | `official-reference` | AIP-160 filter DSL 의 negation 기능을 구현하는 서버 | 부정 기능 자체를 지원해야 한다는 의무는 없음 — 지원 '시' 두 형식 모두 제공해야 함 |
|
||||
| AIP160-C5 | AIP-160 DSL 의 `.` traversal operator 는 repeated field 를 통한 탐색에 사용할 수 없다 (MUST NOT) | [§Traversal operator] "The . operator must not be used to traverse through a repeated field." | `official-reference` | AIP-160 filter DSL 의 traversal operator 를 구현하는 서버 | repeated field 내 개별 요소 조회는 `:` (has) 연산자를 사용 — `.` 과 `:` 의 혼합 사용 패턴은 별도 설명 필요 |
|
||||
| AIP160-C6 | AIP-160 DSL 에서 특수문자를 포함한 string 값은 따옴표 필요. 작은따옴표·큰따옴표 모두 허용 | [§String values] "String values require quotes if they contain special characters. Single quotes and double quotes are both accepted." | `official-reference` | AIP-160 filter string 을 파싱하는 서버와 filter string 을 생성하는 클라이언트 | 특수문자 escape sequence 의 구체적 목록 — 어떤 문자가 '특수문자'인지 명확히 열거되지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `AIP160-C1`: filtering API 에서 `string filter` 단일 필드를 쓰는 것이 Google AIP community 의 공식 권고임 (옵션의 존재 + 예제 syntax)
|
||||
- `AIP160-C2`: AIP-160 DSL 이 정의하는 연산자 목록과 `:` 의 구현 의무
|
||||
- `AIP160-C3`: 잘못된 filter string 에 대한 `INVALID_ARGUMENT` 에러 반환 권고
|
||||
- `AIP160-C4`: negation 지원 시 `NOT` 과 `-` 두 형식 모두 제공 의무
|
||||
- `AIP160-C5`: `.` traversal operator 가 repeated field 에 사용 불가
|
||||
- `AIP160-C6`: 특수문자 포함 string 값의 따옴표 필요성
|
||||
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- AIP-160 DSL 이 `feature-api-contract-baseline` 에 채택되어야 한다는 결론 — 채택 결정은 별도 trade-off 분석 필요 (RSQL/FIQL, JSON:API `?filter[key]=value`, flat `?key=value` 와의 비교)
|
||||
- AIP-160 filter DSL 이 RFC/W3C 국제 표준임 — AIP 는 **Google 사내 API community guideline** (`official-reference` 수준). IETF 나 W3C 표준이 아님
|
||||
- client 와 server 양쪽의 파싱 라이브러리 지원 현황 — AIP-160 은 syntax 만 정의하며 Java/Spring 용 파서는 별도 라이브러리 (예: `google/cel-java`) 필요
|
||||
- `total_size` 필드 — AIP-160 본문에 해당 내용 없음. pagination 관련 내용은 AIP-158 (Pagination) 참조
|
||||
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- AIP-160 DSL 채택 vs flat `?key=value` 채택 vs RSQL/FIQL 채택 — 각 옵션의 client/server 구현 부담 비교 (별도 trade-off 분석 문서 필요)
|
||||
- Spring + Java 환경에서 AIP-160 DSL 파서 라이브러리의 성숙도·유지보수성
|
||||
- ca-skeleton 의 첫 filtering 사용 사례가 무엇인지 (단순 exact-match 인지, 복합 expression 인지) — 오버엔지니어링 여부 판단
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-160 의 filter DSL 은 Google Cloud API (예: Cloud Asset Inventory, Logging) 에서 실제로 사용되는 DSL 임. Google AIP 는 Google 사내 community guideline 으로 외부 표준이 아님. `official-reference` 수준으로 취급.
|
||||
- `total_size` 필드는 AIP-160 이 아닌 AIP-158 (Pagination) 에서 다룸 — [[raw/official-docs/google-aip-158-pagination.md]] 신설 시 참조.
|
||||
- AIP-160 DSL 이 RFC/W3C 국제 표준이 아니므로, client SDK 가 AIP-160 을 지원하지 않는다면 client 에서 filter string 을 수동으로 조립해야 함 — 이는 DX 부담.
|
||||
- AIP-160 본문에는 OR 가 AND 보다 우선순위가 높다는 비표준적 precedence rule 이 있음 (`a AND b OR c` = `a AND (b OR c)`). 이는 일반 프로그래밍 언어와 반대 — 사용자 혼란 가능성.
|
||||
|
||||
## Self-Grep Verification Record
|
||||
|
||||
모든 핵심 인용은 `/tmp/source-fetch-20260531091218.txt` 에서 `grep -nF` 로 검증됨:
|
||||
|
||||
| Quote | Line | Result |
|
||||
|---|---|---|
|
||||
| "a request message should have exactly one filtering field, string filter" | 9, 82 | PASS |
|
||||
| "Filtering implementations must provide the : operator" | 61 | PASS |
|
||||
| "If a non-compliant or schematically invalid filter string is specified, the API should error with INVALID_ARGUMENT" | 78 | PASS |
|
||||
| "A service that supports negation must support both formats" | 33 | PASS |
|
||||
| "The . operator must not be used to traverse through a repeated field" | 58 | PASS |
|
||||
| "String values require quotes if they contain special characters. Single quotes and double quotes are both accepted" | 69 | PASS |
|
||||
|
||||
검증한 인용 V: 6 / 일치 P: 6 / 폐기 D: 0 / 정정 C: 0
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 동일 AIP 시리즈 (ordering·pagination·LRO):
|
||||
- [[raw/official-docs/google-aip-132-list-method.md]] — List method 일반 (신설 예정)
|
||||
- [[raw/official-docs/google-aip-158-pagination.md]] — Pagination (신설 예정, `total_size` 필드 포함)
|
||||
- [[raw/official-docs/google-aip-151-long-running-operations.md]] — LRO (신설 예정)
|
||||
- [[raw/official-docs/google-aip-185-resource-versioning]] — Resource versioning (기존)
|
||||
- [[raw/official-docs/api-versioning-google-aip-180]] — Backward compatibility (기존)
|
||||
- 관련 filter syntax 대안 비교:
|
||||
- RSQL/FIQL: 별도 raw 미보관 (trade-off 분석 시 신설 권고)
|
||||
- JSON:API filter: [[raw/official-docs/jsonapi-pagination-format]] (기존, pagination 중심)
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: Google AIP-185 — Versioning (resource major version + channel stability)
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/185
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
related_branches: [feature-api-contract-baseline, feature-api-compatibility-deprecation-contract]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
tags: [ca-tmpl, api-versioning, aip-185, google, major-version, stability-channel, official-doc]
|
||||
created: 2026-05-27
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Google AIP-185 — Versioning (resource major version + channel stability)
|
||||
|
||||
> Layer: `raw/official-docs/` — Google API Improvement Proposals 의 API versioning 정책. ca-tmpl API contract baseline 의 major version 결정 (D2) 과 deprecation contract (D6) 의 reference. AIP 는 Google internal API design guideline 이지만 외부에 reference 로 널리 인용됨 (정식 IETF/W3C 표준 아님).
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | D2 (major version 을 URL path 에 노출 — `/v1/...`) + D6 (alpha/beta/stable 채널 분리 또는 stable-only) 결정의 reference |
|
||||
| [[raw/branch-notes/feature-api-compatibility-deprecation-contract]] | major version bump 의 조건 / 기존 major 와 새 major 의 의존성 금지 정책 reference |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
ca-tmpl API contract baseline 에서 "왜 URL path 에 major version 만 노출하는가 (`/v1/`, 절대 `/v1.0/` 아님)", "왜 alpha/beta 를 별도 채널로 분리하는가" 결정의 1차 reference. AIP-180 (backwards compatibility) 과 짝을 이루는 문서 — AIP-180 은 같은 major 안에서의 호환, AIP-185 는 major bump 자체의 규칙.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/185
|
||||
- 관련 AIP: AIP-180 (Backwards compatibility), AIP-181 (Stability levels)
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Google (API Improvement Proposals working group)
|
||||
- 발행일: continuously updated
|
||||
- 마지막 확인일: 2026-05-27 (WebFetch verbatim 확인)
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, captured 2026-05-27)
|
||||
|
||||
> [§Guidance] "All Google API interfaces **must** provide a _major version number_, which is encoded at the end of the protobuf package"
|
||||
|
||||
> [§Guidance] "Google APIs **must not** expose minor or patch version numbers. For example, Google APIs use `v1`, not `v1.0`"
|
||||
|
||||
> [§Guidance] "A new major version of an API **must not** depend on a previous major version of the same API"
|
||||
|
||||
> [§Channel-based versioning] "The alpha and beta channel **must** have their stability level appended to the version, but the stable channel **must not**"
|
||||
|
||||
> [§Channel-based versioning] "The beta channel's functionality **must** be a superset of the stable channel's functionality"
|
||||
|
||||
> [§Deprecating API functionality] "Deprecated API functionality **must not** graduate from alpha to beta, nor beta to stable"
|
||||
|
||||
> [§Release-based versioning] "Both the channel-based and release-based strategies update the _stable_ version in-place"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP185-C1 | 모든 Google API interface 는 **major version number** 를 노출해야 함 (protobuf package 끝에 인코딩) | [§Guidance] "All Google API interfaces **must** provide a _major version number_, which is encoded at the end of the protobuf package" | `official-reference` (Google AIP — community guideline, 표준 아님) | URL path versioning 결정 (`/v1/...`) | REST API 에서 path vs header 중 어느 위치인지는 본 인용 범위 밖 — AIP 는 protobuf 컨텍스트 |
|
||||
| AIP185-C2 | Google API 는 **minor 또는 patch version 을 노출하면 안 됨** (`v1.0` 아닌 `v1`) | [§Guidance] "Google APIs **must not** expose minor or patch version numbers. For example, Google APIs use `v1`, not `v1.0`" | `official-reference` | path 에 `/v1.0/` 같은 minor 표기 금지 결정 | semver 자체를 부정하는 것은 아님 — public surface 노출만 금지, internal release semver 는 별도 |
|
||||
| AIP185-C3 | 새 major version 은 같은 API 의 이전 major version 에 **의존하면 안 됨** | [§Guidance] "A new major version of an API **must not** depend on a previous major version of the same API" | `official-reference` | v2 가 v1 코드를 import 하는 구조 금지 | shared common types (예: google.protobuf.Timestamp) 의 공유는 별도 — 본 인용은 같은 API 의 다른 major 간 의존만 |
|
||||
| AIP185-C4 | alpha / beta 채널은 stability level 을 version 에 **append** 해야 하지만 stable 채널은 **append 하면 안 됨** | [§Channel-based versioning] "The alpha and beta channel **must** have their stability level appended to the version, but the stable channel **must not**" | `official-reference` | `v1beta1`, `v1alpha1` vs `v1` 명명 규칙 | 채널 별 SLA / 호환성 보장 수준은 본 인용 범위 밖 — AIP-181 영역 |
|
||||
| AIP185-C5 | beta 채널 기능은 stable 채널 기능의 **superset** 이어야 함 | [§Channel-based versioning] "The beta channel's functionality **must** be a superset of the stable channel's functionality" | `official-reference` | beta 가 stable 보다 적은 기능을 노출하는 것 금지 | alpha 가 beta 의 superset 인지는 본 인용 범위 밖 (AIP 다른 섹션 또는 AIP-181 위임) |
|
||||
| AIP185-C6 | Deprecated API 기능은 alpha → beta 또는 beta → stable 로 **graduate 되면 안 됨** | [§Deprecating API functionality] "Deprecated API functionality **must not** graduate from alpha to beta, nor beta to stable" | `official-reference` | deprecation 후 채널 승격 금지 정책 | deprecation 통지 window / sunset 일정은 본 인용 범위 밖 — AIP-180 / AIP-214 위임 |
|
||||
| AIP185-C7 | channel-based / release-based 두 versioning 전략 모두 **stable version 을 in-place 로 업데이트** | [§Release-based versioning] "Both the channel-based and release-based strategies update the _stable_ version in-place" | `official-reference` | stable v1 이 시간에 따라 (호환 범위 내) 진화한다는 가정 | stable 안에서 어떤 변경이 호환인지는 본 인용 범위 밖 — AIP-180 위임 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것** (2026-05-27 WebFetch verbatim 확인):
|
||||
- `AIP185-C1`: major version 노출 의무 (protobuf 컨텍스트)
|
||||
- `AIP185-C2`: minor / patch 노출 금지 — `/v1/` 만, `/v1.0/` 금지
|
||||
- `AIP185-C3`: 새 major 가 이전 major 에 의존 금지
|
||||
- `AIP185-C4`: alpha/beta 는 stability level append, stable 은 append 금지
|
||||
- `AIP185-C5`: beta = stable 의 superset
|
||||
- `AIP185-C6`: deprecated 기능은 채널 승격 금지
|
||||
- `AIP185-C7`: stable 은 in-place 업데이트
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- REST URL path 에서 major version 의 정확한 위치 — AIP 는 protobuf 컨텍스트, REST 매핑은 별도 (AIP-122 / Cloud Endpoints 위임)
|
||||
- major version bump 의 trigger (어떤 변경이 major bump 를 요구하는지) — AIP-180 위임
|
||||
- deprecation 통지 window / sunset 일정 — AIP-214 위임
|
||||
- channel 별 SLA / 가용성 보장 — AIP-181 (Stability levels) 위임
|
||||
- Google AIP 는 internal guideline 이며 **IETF/W3C 표준 아님**. 외부 인용 시 "Google API style guide" 로 명시.
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 이 REST 기반 — AIP-185 의 "protobuf package" 규칙을 URL path 로 매핑하는 근거 (AIP-122 또는 별도 reference 확인)
|
||||
- ca-tmpl 이 alpha/beta 채널을 실제로 운영할지 여부 — internal-first skeleton 에서는 stable-only 도 합리적 trade-off
|
||||
- `v1beta1` 같은 명명을 채택할 경우 Spring Boot URL routing 패턴 호환성
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- **AIP-180 과의 관계**: AIP-180 은 같은 major 안에서의 backwards compatibility, AIP-185 는 major bump 자체의 규칙. 두 문서는 짝.
|
||||
- **REST vs gRPC**: AIP 자체는 protobuf/gRPC 중심. REST 매핑은 별도 AIP (AIP-122 등) 또는 Google Cloud Endpoints 문서.
|
||||
- **Stripe 모델과의 차이**: Stripe 는 date-based versioning (`Stripe-Version: 2024-04-10`). AIP-185 는 major-only path versioning. 두 모델 중 ca-tmpl 이 어느 쪽을 택할지는 별도 결정.
|
||||
- **internal-first skeleton 함의**: alpha/beta 채널 분리는 운영 부담이 큼. ca-tmpl 이 stable-only 로 시작하고 필요시 beta 채널 추가하는 것이 합리적 trade-off.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/api-versioning-google-aip-180]] (같은 major 안에서의 호환)
|
||||
- AIP-181 (Stability levels) — 별도 raw 작성 후보
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-api-contract-baseline]]
|
||||
- [[raw/branch-notes/feature-api-compatibility-deprecation-contract]]
|
||||
- 인용하는 project:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
title: "official-doc / Google AIP-233 — Batch Methods: Create"
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/233
|
||||
archive_url:
|
||||
vendor: Google
|
||||
related_branches: [feature-api-contract-baseline]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, api-contract, google-aip, bulk-operation]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-31
|
||||
last_reviewed: 2026-05-31
|
||||
---
|
||||
|
||||
# official-doc / Google AIP-233 — Batch Methods: Create
|
||||
|
||||
> Layer: `raw/official-docs/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-api-contract-baseline]] | D23 (bulk operation URL pattern): `POST /v1/{resource}:batchCreate` colon-verb syntax — `:batchCreate` verb 명칭 자체의 normative 근거. 기존 AIP-136 은 colon-verb *패턴* 만 정의하지만 AIP-233 은 `:batchCreate` *명칭 vocabulary* 를 직접 normative 하게 정의하여 D23 의 `UNSUPPORTED_IMPL_DECISION` 라벨 해소 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/233
|
||||
- 아카이브 URL: (미확인)
|
||||
- 저자 / 조직: Google (AIP editors)
|
||||
- 발행일: (Google AIP 페이지, 정확한 최초 발행일 비노출)
|
||||
- 마지막 확인일: 2026-05-31
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-api-contract-baseline` D23 의 bulk operation URL pattern 결정에서 `:batchCreate` verb 명칭의 출처가 AIP-136 (colon-verb 패턴 일반 원칙) 까지만 corroborate 되어 `UNSUPPORTED_IMPL_DECISION` 라벨이 잔존하고 있었다. AIP-233 이 `:batchCreate` 명칭을 URI pattern 으로 직접 normative 하게 정의하므로, 본 raw 보관이 D23 의 `:batchCreate` 명칭 vocabulary 근거를 완성한다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Core Definition] "Some APIs need to allow users to create multiple resources in a single transaction. A batch create method provides this functionality."
|
||||
|
||||
> [§HTTP Requirements / Verb] "The HTTP verb **must** be `POST`."
|
||||
|
||||
> [§HTTP Requirements / URI Pattern] "The HTTP URI **must** end with `:batchCreate`."
|
||||
|
||||
> [§Request Structure] "The request message **must** include a repeated field which accepts the request messages specifying the resources to create...The field **should** be named `requests`."
|
||||
|
||||
> [§Parent Field Consistency] "If a caller sets this field, and the `parent` field of any child request message does not match, the request **must** fail."
|
||||
|
||||
> [§Response Structure] "The response message **must** include one repeated field corresponding to the resources that were created."
|
||||
|
||||
> [§Atomicity Constraint] "Synchronous batch create **must** be atomic."
|
||||
|
||||
> [§Atomicity Constraint] "Asynchronous batch create **may** support atomic or partial success."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AIP233-C1 | Batch Create 는 단일 트랜잭션에서 여러 리소스를 생성하는 메서드다 | [§Core Definition] "Some APIs need to allow users to create multiple resources in a single transaction. A batch create method provides this functionality." | `official-reference` | Google AIP 를 따르는 API 설계 | 어떤 트랜잭션 구현 방식(DB 트랜잭션 / saga / 2PC)을 사용해야 하는지는 정의하지 않는다 |
|
||||
| AIP233-C2 | Batch Create 의 HTTP verb 는 MUST `POST` 다 | [§HTTP Requirements / Verb] "The HTTP verb **must** be `POST`." | `official-reference` | Google AIP 준수 API — batch create endpoint | PUT/PATCH/DELETE 를 사용하는 다른 batch 유형은 별도 AIP (AIP-234 등) 에서 정의됨 |
|
||||
| AIP233-C3 | Batch Create URI 는 MUST `:batchCreate` 로 끝나야 한다 | [§HTTP Requirements / URI Pattern] "The HTTP URI **must** end with `:batchCreate`." | `official-reference` | Google AIP 준수 API 의 batch create endpoint URI | URI 의 나머지 구조 (`{parent}/` prefix 등) 는 리소스 설계에 따라 달라짐. IETF/W3C 표준이 아닌 Google AIP community guideline |
|
||||
| AIP233-C4 | Request message 는 MUST repeated field 를 포함해야 하며, SHOULD `requests` 로 명명한다 | [§Request Structure] "The request message **must** include a repeated field which accepts the request messages specifying the resources to create...The field **should** be named `requests`." | `official-reference` | Google AIP 준수 API 의 batch create request message | `requests` 이외의 명칭 사용은 SHOULD 위반이지만 MUST 위반이 아니다. REST JSON body 에서 field 이름으로 직접 매핑됨 (protobuf 컨텍스트 — REST 매핑은 추가 설계 필요) |
|
||||
| AIP233-C5 | `parent` field 가 설정된 경우, child request 의 `parent` field 가 다르면 request 는 MUST 실패해야 한다 | [§Parent Field Consistency] "If a caller sets this field, and the `parent` field of any child request message does not match, the request **must** fail." | `official-reference` | Google AIP 준수 API 의 batch create — parent scoped resource 에 한해 적용 | parent field 가 없는 batch create (top-level resource) 에는 적용되지 않는다. 실패 응답 형태 (HTTP status code / error body shape) 는 본 AIP 가 직접 정의하지 않는다 |
|
||||
| AIP233-C6 | Response message 는 MUST 생성된 리소스를 담은 하나의 repeated field 를 포함해야 한다 | [§Response Structure] "The response message **must** include one repeated field corresponding to the resources that were created." | `official-reference` | Google AIP 준수 API 의 batch create response message | repeated field 의 명칭 (예: `books`) 은 리소스 유형에 따라 달라지며 AIP-233 이 직접 정의하지 않는다 |
|
||||
| AIP233-C7 | 동기 Batch Create 는 MUST atomic (all-or-nothing) 이어야 한다 | [§Atomicity Constraint] "Synchronous batch create **must** be atomic." | `official-reference` | Google AIP 준수 API 의 **동기** batch create | 비동기 batch create 에는 적용되지 않는다. atomicity 의 구현 방법 (DB 단일 트랜잭션, distributed transaction 등) 은 정의하지 않는다 |
|
||||
| AIP233-C8 | 비동기 Batch Create 는 atomic 또는 partial success 를 MAY 지원한다 | [§Atomicity Constraint] "Asynchronous batch create **may** support atomic or partial success." | `official-reference` | Google AIP 준수 API 의 **비동기** batch create | partial success 를 지원한다고 해서 어떤 상황에서 partial 을 허용할지 기준을 정의하지 않는다. partial success metadata 구조는 별도 AIP 섹션 (Async Only) 에서 정의됨 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
### 이 자료가 직접 증명하는 것
|
||||
|
||||
- `AIP233-C2`: batch create endpoint 의 HTTP verb 가 MUST `POST` 임
|
||||
- `AIP233-C3`: batch create URI 가 MUST `:batchCreate` suffix 로 끝나야 함 — D23 의 `:batchCreate` *verb 명칭* vocabulary 의 normative 근거
|
||||
- `AIP233-C4`: request message 에 `requests` field (repeated, SHOULD 명칭) 가 MUST 포함됨
|
||||
- `AIP233-C5`: parent field 불일치 시 MUST fail 의 정합성 규칙
|
||||
- `AIP233-C6`: response message 에 created 리소스의 repeated field 가 MUST 포함됨
|
||||
- `AIP233-C7`: 동기 batch create 의 atomicity MUST 요건
|
||||
|
||||
### 이 자료의 authority level 주의사항
|
||||
|
||||
- **AIP-233 은 Google 내부 API community guideline 이다** (`official-reference` 수준). IETF RFC / W3C 표준 / OpenAPI Initiative 표준 수준의 `official-standard` 가 아니다. Google API Design Guide 의 community-governed 문서로 타사에 대한 법적 구속력이 없다.
|
||||
- 본 AIP 는 **protobuf / gRPC 컨텍스트** 에서 기술되어 있다. REST JSON API 에 적용할 때는 field 명칭이 JSON body 로 직접 매핑되지만, protobuf message 구조 (`BatchCreateXxxRequest`, `BatchCreateXxxResponse`) 자체를 채택할 의무는 없다.
|
||||
|
||||
### 이 자료가 증명하지 않는 것
|
||||
|
||||
- **`:batchCreate` 가 IETF 표준** 임을 증명하지 않는다 — Google AIP community guideline 이다
|
||||
- **`feature-api-contract-baseline` D23 의 `BATCH_PARTIAL_FAILURE` envelope category** 와 AIP-233 의 atomicity/partial success 정책의 완전한 정합성: D23 은 HTTP 200 + envelope.success=false + `BATCH_PARTIAL_FAILURE` 로 부분 실패를 표현하지만, AIP-233 의 async partial success 는 `map<int32, google.rpc.Status> failed_requests` + `Operation.error` 구조를 정의한다. 본 branch 는 AIP-233 의 protobuf Operation shape 을 채택하지 않고 **자체 REST envelope** (`data.results[]` 항목별 success/error) 를 사용한다. 이 REST envelope 은 project-internal 결정이며 AIP-233 이 직접 normative 하게 정의하지 않는다.
|
||||
- **`requests` field 명칭이 REST JSON body field 명** 으로 강제됨을 증명하지 않는다 — AIP-233 의 `requests` 명칭은 SHOULD (권고)이며, REST 매핑은 project 내부 결정이다
|
||||
- **batch size limit** (예: 최대 1000 항목): AIP-233 은 문서화 권고만 하며 숫자를 normative 하게 정의하지 않는다
|
||||
|
||||
### AIP-233 atomicity 정책과 D23 `BATCH_PARTIAL_FAILURE` 의 정합성
|
||||
|
||||
D23 은 부분 실패를 HTTP 200 + `BATCH_PARTIAL_FAILURE` 로 처리하며, 이는 **전체 실패가 아닌 부분 성공/실패** 모델이다. AIP-233 의 관점:
|
||||
|
||||
- **동기 batch create** (AIP233-C7) = MUST atomic → D23 의 부분 실패 모델은 동기 endpoint 에 적용 시 AIP-233 atomicity 요건과 **충돌**한다. D23 이 부분 실패를 허용한다면 해당 endpoint 는 AIP-233 기준에서 "비동기 또는 AIP 미준수" 로 분류된다.
|
||||
- **비동기 batch create** (AIP233-C8) = MAY support partial success → D23 의 부분 실패 모델은 비동기 endpoint 에서 AIP-233 과 일치한다.
|
||||
- **결론**: D23 의 `BATCH_PARTIAL_FAILURE` 은 AIP-233 이 허용하는 partial success 의 *의미론* 과 부합하지만, *표현 형식* (REST envelope vs AIP-233 의 Operation metadata 구조) 은 project-internal 결정으로 남는다. D23 이 동기 endpoint 에서 partial 실패를 허용하면 AIP-233 C7 (동기 MUST atomic) 과 충돌 발생 — 이 trade-off 는 본 branch 에서 명시적 결정이 필요하다 (현재 `UNSUPPORTED_IMPL_DECISION` 잔존).
|
||||
|
||||
### 잔존 UNSUPPORTED_IMPL_DECISION (D23 기준 — claim-traceability gate 결과)
|
||||
|
||||
아래 3건은 AIP-233 이 직접 normative 하게 정의하지 않으므로 `UNSUPPORTED_IMPL_DECISION` 라벨이 잔존한다. 이 자료만으로 증명되지 않는다.
|
||||
|
||||
| 항목 | 왜 UNSUPPORTED_IMPL_DECISION | 해소 경로 |
|
||||
|---|---|---|
|
||||
| `data.results[]` REST envelope shape (항목별 success/error 구조) | AIP-233 C6 은 protobuf `repeated Book books` 만 정의. REST envelope 의 `data.results[]` + 항목별 `{success, error}` 구조는 project-internal — boundary branch B14 (BulkEnvelope.partial) SSOT | boundary branch B14 의 BulkEnvelope 스펙이 확정되면 cross-cite 로 보강 |
|
||||
| 부분 실패 표현 = HTTP 200 + `envelope.success=false` 조합 | AIP-233 C8 은 async partial success 의 *허용 여부* 만 정의. 표현 형식(HTTP 200 + false envelope vs AIP 의 `Operation.error` + `failed_requests` map)은 project-internal trade-off | 동기/비동기 endpoint 구분 명확화 후 foundation envelope SSOT 와 cross-cite |
|
||||
| 동기 endpoint 에서 `BATCH_PARTIAL_FAILURE` 허용 여부 | AIP233-C7 (동기 MUST atomic) 과 D23 의 partial failure 허용 이 충돌. D23 이 동기/비동기를 명확히 구분하지 않으면 AIP233-C7 위반 위험 | D23 을 (a) 동기는 all-or-nothing MUST, (b) 비동기 전용으로 partial 허용 으로 명시 분기하거나, (c) ca-skeleton 이 AIP-233 의 동기 atomicity 요건 미준수임을 명시 |
|
||||
|
||||
### 내 프로젝트에 적용하려면 추가 확인이 필요한 것
|
||||
|
||||
- D23 이 동기 vs 비동기 endpoint 를 명확히 구분하는지 확인 (AIP233-C7 충돌 해소 — 위 UNSUPPORTED_IMPL_DECISION 3번)
|
||||
- `feature-operational-error-observability-foundation` 의 `BATCH_PARTIAL_FAILURE` envelope category 정의가 AIP-233 의 partial success semantics 와 의미론적으로 정합하는지 cross-branch review
|
||||
- REST `requests` field 명칭 채택 여부 (D23 현재 `{ requests: [...] }` 를 body shape 로 정의 — AIP233-C4 SHOULD 와 일치하므로 추가 근거 불필요)
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- AIP-233 은 protobuf 기반 gRPC API 를 primary target 으로 한다. REST HTTP transcoding 은 Google HTTP Transcoding (AIP-127) 에서 별도로 다룬다. 본 branch 는 REST JSON API 이므로 protobuf Message 구조 (`BatchCreateXxxRequest`) 를 직접 채택하지 않는다 — D23 의 `{ requests: [...] }` body shape 는 AIP-233 의 `requests` field SHOULD 명칭과 **일치**하므로 이 부분은 자연스럽게 정합됨.
|
||||
- AIP-233 이 정의하는 partial success 의 `map<int32, google.rpc.Status> failed_requests` 구조는 본 branch 의 `data.results[]` (각 항목별 success/error) 와 **의미론적으로 동등**하지만 형식이 다르다. D23 의 REST envelope 매핑은 project-internal.
|
||||
- AIP-233 C7 (동기 MUST atomic) 은 강력한 제약이다. D23 이 synchronous endpoint 에서 `BATCH_PARTIAL_FAILURE` 를 허용하면 AIP-233 준수 여부가 문제가 된다. 이 점은 ca-skeleton 설계 결정으로 명시 필요 (향후 D23 row 의 Open Risk 보강 권고).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/google-aip-136-custom-methods]] — AIP-136: colon-verb URI pattern 의 일반 원칙 (AIP-233 이 `:batchCreate` 를 특정 vocabulary 로 normative 정의하는 것의 상위 원칙)
|
||||
- [[raw/official-docs/google-aip-151-long-running-operations]] — AIP-151: batch create 의 비동기 variant (LRO 반환) 에 대한 Operation shape 정의
|
||||
- [[raw/branch-notes/feature-api-contract-baseline]] — D23 (bulk operation URL pattern): 본 raw 를 인용하는 branch 결정
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Google Antigravity Hooks
|
||||
source_type: official-doc
|
||||
url: https://antigravity.google/docs/hooks
|
||||
archive_url:
|
||||
related_branches: [chore-harness-policy-engine-alignment]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, integration, build-tooling]
|
||||
created: 2026-07-20
|
||||
---
|
||||
|
||||
# Google Antigravity Hooks
|
||||
|
||||
> Layer: `raw/official-docs/` — Antigravity JSON hook의 구성과 stdin/stdout 계약을 확인한 공식 문서 기록.
|
||||
|
||||
## 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/chore-harness-policy-engine-alignment]] | D3 — Antigravity PreToolUse/Stop 어댑터가 공식 JSON·camelCase·decision 계약을 따르도록 한 결정 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://antigravity.google/docs/hooks
|
||||
- 아카이브 URL: 없음
|
||||
- 저자 / 조직: Google
|
||||
- 발행일: 문서에 명시되지 않음
|
||||
- 마지막 확인일: 2026-07-20
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
Claude용 훅을 그대로 복제하지 않고 Antigravity의 실제 이벤트 이름, camelCase 입력, JSON decision 출력을 맞추기 위한 외부 계약 근거로 저장했다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [Input/Output Contract] “Hooks receive input via stdin as JSON and should return output via stdout as JSON. Field names use camelCase.”
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-ANTIGRAVITY-HOOKS-C1 | Hook 입력과 출력은 JSON이며 공통 필드 이름은 camelCase다. | 위 핵심 인용 | `official-vendor-doc` | Antigravity hook adapter의 이벤트 정규화와 직렬화 | Claude/Codex의 hook 형식이 같다는 것 |
|
||||
| GOOGLE-ANTIGRAVITY-HOOKS-C2 | PreToolUse는 tool name matcher를 사용하고 decision으로 allow/deny/ask/force_ask를 반환한다. Stop은 continue decision으로 실행 루프 재진입을 요청한다. | 공식 문서의 PreToolUse·Stop schema 표 | `official-vendor-doc` | import gate와 completion gate의 Antigravity 출력 매핑 | ca-tmpl 정책 자체의 타당성이나 실제 인증 런타임 E2E 성공 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 직접 증명: Antigravity hook 파일 구조, 이벤트별 입력 필드, decision 출력 vocabulary.
|
||||
- 증명하지 않음: 로컬 ca-tmpl 정책이 올바르다는 것, Claude/Codex parity, 실제 로그인된 Antigravity 제품에서의 end-to-end 실행 성공.
|
||||
- 추가 확인: 인증된 Antigravity 환경에서 seeded mutation과 Stop evidence lifecycle을 실제 실행해야 한다.
|
||||
|
||||
## 메모
|
||||
|
||||
- workspace-local `hooks.json`과 plugin hook wiring은 공식 schema에 맞춰 정적 검증했다.
|
||||
- 실제 외부 제품 실행은 branch D3의 후속 `needs-confirmation`으로 남겼다.
|
||||
|
||||
## 관련
|
||||
|
||||
- [[raw/branch-notes/chore-harness-policy-engine-alignment]]
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
title: Google AIP-193 — Errors (google.rpc.Status)
|
||||
source_type: official-doc
|
||||
url: https://google.aip.dev/193
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [ca-error-envelope, google, grpc, custom-envelope, rest-api, error-format, aip, official-doc]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
related_branches: [feature-operational-error-observability-foundation, feature-boundary-validation-mapping-contract, feature-business-rule-validation-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Google AIP-193 — Errors (google.rpc.Status)
|
||||
|
||||
> Layer: `raw/official-docs/` — Google API Improvement Proposal 193 (Errors). REST 와 gRPC 양쪽에 동일 모델 매핑되는 typed error 표준의 1차 근거.
|
||||
> ca-tmpl Topic 4 (Error Envelope) 의 **대안 2 (Google `rpc.Status` / gRPC-derived)** 비교 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-operational-error-observability-foundation]] | ca-tmpl Topic 4 (Error Envelope) 대안 비교 — `details: Any[]` 다형성 + typed `ErrorInfo`/`RetryInfo`/`LocalizedMessage` 의 표준 근거 |
|
||||
| [[raw/branch-notes/feature-boundary-validation-mapping-contract]] | boundary 입력 검증 실패 시 `BadRequest` typed detail 옵션의 표준 근거 |
|
||||
| [[raw/branch-notes/feature-business-rule-validation-contract]] | business rule 실패의 `ErrorInfo.reason + domain` 기반 머신리더블 식별자 패턴 — RFC 7807 `type` URI 와의 비교 |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
가장 정교한 typed error model. `details` array 가 `Any` 패킹으로 다형성을 가지며, 그 안에 `ErrorInfo` / `LocalizedMessage` / `Help` / `RetryInfo` / `QuotaFailure` / `BadRequest` 등이 들어감 → ca-tmpl 의 `details: object` 와 비교했을 때 표현력 trade-off 가 명확해짐. gRPC 생태계 (grpc-gateway, gapic generator) 의 사실상 표준.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://google.aip.dev/193
|
||||
- 기반: `google.rpc.Status` (protobuf), `google.rpc.Code` enum
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Google (AIP Working Group)
|
||||
- 발행일: rolling (AIP-193, current)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Status.code] "The `code` field is the status code, which must be the numeric value of one of the elements of the `google.rpc.Code` enum."
|
||||
|
||||
> [§Status.message] "The `message` field is a developer-facing, human-readable \"debug message\" which should be in English."
|
||||
|
||||
> [§Status.details] "The `details` field allows messages with additional error information to be included in the error response, each packed in a `google.protobuf.Any` message."
|
||||
|
||||
> [§Status.details — ErrorInfo requirement] "All error responses must include an `ErrorInfo` within `details`."
|
||||
|
||||
> [§Status.details — standard payloads] "BadRequest", "PreconditionFailure", "ErrorInfo", "LocalizedMessage", "Help" — 표준 detail payload 로 명시 (RetryInfo, QuotaFailure 등 추가 표준 payload 는 별도 `error_details.proto` 정의)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOG-ERR-C1 | `code` 필드는 **`google.rpc.Code` enum 의 정수 값** 이어야 함 (must) | [§Status.code] "The `code` field is the status code, which must be the numeric value of one of the elements of the `google.rpc.Code` enum." | `official-vendor-doc` | Google API / gRPC `Status` 호환 응답 | HTTP status code 와 1:1 매핑이라는 뜻은 아님 — `google.rpc.Code` 는 별도 enum (NOT_FOUND=5 등) |
|
||||
| GOOG-ERR-C2 | `message` 필드는 **개발자 대상의 영어 debug message** (should) — end-user 표시용 아님 | [§Status.message] "The `message` field is a developer-facing, human-readable \"debug message\" which should be in English." | `official-vendor-doc` | API 응답의 message 필드 표시 정책 | end-user 메시지가 별도 `LocalizedMessage` 로 강제된다는 뜻은 아님 — 본 인용은 `message` 자체의 의도만 정의 |
|
||||
| GOOG-ERR-C3 | `details` 필드는 **`google.protobuf.Any` 로 패킹된** 추가 정보를 array 로 포함 (다형성) | [§Status.details] "The `details` field allows messages with additional error information to be included in the error response, each packed in a `google.protobuf.Any` message." | `official-vendor-doc` | typed error details 표현 | client 가 `Any` 디코딩 비용 없이 처리 가능하다는 뜻은 아님 — `@type` URL 기반 해석 필요 |
|
||||
| GOOG-ERR-C4 | **모든 error 응답** 은 `details` 안에 **`ErrorInfo` 를 반드시 포함** 해야 함 (must) | [§Status.details — ErrorInfo requirement] "All error responses must include an `ErrorInfo` within `details`." | `official-vendor-doc` | AIP-193 준수 API 의 모든 error 응답 | `ErrorInfo.reason` 값 카탈로그가 spec 에 고정되어 있다는 뜻은 아님 — domain 별 자유 정의 |
|
||||
| GOOG-ERR-C5 | 표준 detail payload 로 `BadRequest`, `PreconditionFailure`, `ErrorInfo`, `LocalizedMessage`, `Help` 등이 정의됨 | [§Status.details — standard payloads] "BadRequest", "PreconditionFailure", "ErrorInfo", "LocalizedMessage", "Help" | `official-vendor-doc` | typed details 카탈로그 사용 | `RetryInfo` / `QuotaFailure` 가 본 AIP-193 페이지에 직접 인용되었다는 뜻은 아님 — `error_details.proto` 의 추가 payload (별도 확인) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `GOOG-ERR-C1`: `code` 가 `google.rpc.Code` enum 정수임 (HTTP status code 와 별개)
|
||||
- `GOOG-ERR-C2`: `message` 의 developer-facing English 의도
|
||||
- `GOOG-ERR-C3`: `details: Any[]` 다형성 구조
|
||||
- `GOOG-ERR-C4`: 모든 error 응답에 `ErrorInfo` 필수
|
||||
- `GOOG-ERR-C5`: 표준 detail payload 5종 (BadRequest, PreconditionFailure, ErrorInfo, LocalizedMessage, Help) 의 존재
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `RetryInfo.retry_delay` 가 client 의 표준 재시도 정책으로 강제됨 (별도 `error_details.proto` 참조 필요)
|
||||
- REST mapping 의 정확한 JSON shape (`error.code` 가 정수 vs 문자열 enum name 인지 — AIP-193 본문은 다른 § 에서 정의)
|
||||
- HTTP status code 와 `google.rpc.Code` 간 매핑 표 (별도 AIP — AIP-194 또는 grpc-status-codes-to-http 표)
|
||||
- `@type` 의 정확한 URL prefix 정책 (`type.googleapis.com` 외 cusotm prefix 허용 여부)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 의 `retryable: boolean` 을 `RetryInfo.retry_delay` 로 대체 시 client SDK 영향
|
||||
- `details` 다형성 채택 시 client 가 알아야 할 `@type` 카탈로그의 운영 비용
|
||||
- LocalizedMessage 채택 시 i18n 파이프라인 (`message` vs `LocalizedMessage.message` 분리) 의 구현 비용
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- 응답 shape 예시 (REST 매핑, 해석):
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": 404,
|
||||
"message": "Resource 'projects/foo' not found.",
|
||||
"status": "NOT_FOUND",
|
||||
"details": [
|
||||
{
|
||||
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
|
||||
"reason": "RESOURCE_NOT_FOUND",
|
||||
"domain": "googleapis.com",
|
||||
"metadata": {"resource": "projects/foo"}
|
||||
},
|
||||
{
|
||||
"@type": "type.googleapis.com/google.rpc.LocalizedMessage",
|
||||
"locale": "ko-KR",
|
||||
"message": "..."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
- **장점 (해석)**:
|
||||
- typed details — `RetryInfo` 로 retryable + delay 까지 표준화, ca-tmpl 의 `retryable` boolean 보다 풍부
|
||||
- `LocalizedMessage` 로 i18n 이 spec 수준에서 정의됨
|
||||
- REST/gRPC 일관 — bilingual API 에 유리
|
||||
- `ErrorInfo.reason + domain` 이 RFC 7807 의 `type` URI 역할
|
||||
- **단점 (해석)**:
|
||||
- 복잡도가 매우 높음. `Any` 디코딩이 client 에 부담
|
||||
- 가벼운 CRUD API 에는 과함
|
||||
- 표준 detail 타입 카탈로그를 알아야 효용 발휘
|
||||
- **ca-tmpl custom envelope 와의 차이 (해석)**:
|
||||
- ca-tmpl: `retryable: boolean`, Google: `RetryInfo { retry_delay }`. 후자가 client 에 더 actionable
|
||||
- ca-tmpl: 단일 `details: object`, Google: `details: Any[]` 다형성
|
||||
- ca-tmpl: `category: string`, Google: 정수 `code` + 문자열 `status` enum
|
||||
- **표준 준수 / lock-in / client 호환성 (해석)**:
|
||||
- Google 진영의 사실상 표준. 외부 표준은 아니지만 gRPC 생태계 전체가 따라감 → grpc-gateway, gapic generator 등
|
||||
- lock-in: protobuf/grpc 생태계와 강결합
|
||||
- **localization / i18n 지원 여부 (해석)**:
|
||||
- `LocalizedMessage` detail 로 1급 지원. 5개 대안 중 가장 명시적
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog:
|
||||
- [[raw/official-docs/spring-problem-detail]] (대안 1 구현체 — RFC 7807/9457)
|
||||
- [[raw/official-docs/json-api-errors-spec]] (대안 3 — JSON:API errors)
|
||||
- [[raw/official-docs/graphql-errors-spec]] (대안 4 — GraphQL errors)
|
||||
- [[raw/company-tech-blogs/stripe-error-format]] (대안 5 — Stripe custom envelope)
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] §3 Structured API Response Contract, §6 Operational Error Category
|
||||
- 본 source 의 위치: ca-tmpl Topic 4 — Error Envelope, **대안 2: Google rpc.Status (gRPC-derived)**
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
title: google-java-format — README & FAQ (Official Repo)
|
||||
source_type: official-doc
|
||||
url: https://github.com/google/google-java-format
|
||||
archive_url: https://web.archive.org/web/2026/https://github.com/google/google-java-format
|
||||
related_branches: [feature-static-analysis-quality-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, ci-cd, gradle, static-analysis]
|
||||
created: 2026-06-15
|
||||
---
|
||||
|
||||
# google-java-format — README & FAQ (Official Repo)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-static-analysis-quality-contract]] | D1 — Spotless + google-java-format 채택. 포맷터의 scope(naming 등 다른 style 측면은 정하지 않음), Java 21 최소 런타임 요건, zero-configurability 설계 결정, JDK 16+ 에서 필요한 --add-exports JVM flag 를 공식 확인. |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://github.com/google/google-java-format
|
||||
- 추가 URL (FAQ): https://github.com/google/google-java-format/wiki/FAQ
|
||||
- 아카이브 URL: https://web.archive.org/web/2026/https://github.com/google/google-java-format
|
||||
- 저자 / 조직: Google (open-source)
|
||||
- 발행일: 2015 (리포지토리 최초 릴리즈); 최신 v1.35.0 (March 2026)
|
||||
- 마지막 확인일: 2026-06-15
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
feature-static-analysis-quality-contract D1 의 Spotless + google-java-format 채택 결정을 뒷받침하는 공식 근거. 포맷터의 scope(formatting 전용, naming 등 미포함), Java 21 최소 버전 요건, zero-configurability 설계 철학, JDK 16+ JVM 플래그 요건을 원문으로 확보해 두어 "왜 이 도구를 골랐나" 질문에 직접 인용 가능한 상태로 보관.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 4문장)
|
||||
|
||||
> [README §command-line] "The minimum Java version can be found in `core/pom.xml` (currently Java 21)."
|
||||
|
||||
> [README §command-line note] "There is no configurability as to the formatter's algorithm for formatting. This is a deliberate design decision to unify our code formatting on a single format."
|
||||
|
||||
> [README §as-a-library] "`google-java-format` uses internal javac APIs for parsing Java source. The following JVM flags are required when running on JDK 16 and newer, due to [JEP 396: Strongly Encapsulate JDK Internals by Default](https://openjdk.java.net/jeps/396):"
|
||||
|
||||
> [FAQ §Principles-and-goals / "So formatter output is considered valid Google Style by definition?"] "And of course, many style rules concern issues the formatter has nothing to do with, such as naming."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GJF-README-C1 | google-java-format 의 scope 는 formatting/whitespace 에 한정되며, naming 등 다른 style 측면은 정하지 않는다 | [FAQ §Principles] "many style rules concern issues the formatter has nothing to do with, such as naming." | `official-vendor-doc` | google-java-format 을 Google Java Style 전체 준수 도구로 오해하는 상황 방지 | Checkstyle / SpotBugs 등 다른 static-analysis 도구의 scope 를 증명하지 않음 |
|
||||
| GJF-README-C2 | google-java-format 실행을 위한 최소 Java 런타임 버전은 Java 21(JDK) 이다 | [README §command-line] "The minimum Java version can be found in `core/pom.xml` (currently Java 21)." | `official-vendor-doc` | google-java-format CLI 또는 Spotless googleJavaFormat() step 을 사용하는 모든 Gradle/Maven 빌드 | `core/pom.xml` 기준이므로 버전 업그레이드 시 변경 가능 — 최신 버전 확인 필요 |
|
||||
| GJF-README-C3 | 포맷터 알고리즘에 대한 configurability 가 전혀 없다; 이는 코드 포맷을 단일 형식으로 통일하기 위한 의도적인 설계 결정이다 | [README §command-line note] "There is no configurability as to the formatter's algorithm for formatting. This is a deliberate design decision to unify our code formatting on a single format." | `official-vendor-doc` | google-java-format 을 프로젝트에 도입할 때 "커스텀 indent 폭" 등의 옵션을 기대하는 상황 | `--aosp` flag(4-space indent) 는 예외적으로 존재함 — FAQ에 명시 |
|
||||
| GJF-README-C4 | JDK 16 이상에서 google-java-format 을 라이브러리로 사용하려면 특정 --add-exports JVM flag 가 필요하다 (JEP 396 강한 캡슐화 때문) | [README §as-a-library] "The following JVM flags are required when running on JDK 16 and newer, due to [JEP 396: Strongly Encapsulate JDK Internals by Default]" | `official-vendor-doc` | google-java-format 을 Gradle/Maven 빌드 플러그인(Spotless 등) 또는 라이브러리로 JDK 16+ 에서 실행하는 경우 | CLI JAR 실행(java -jar) 시에도 동일 flag 필요 여부는 실제 실행 검증 필요 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `GJF-README-C1`: google-java-format 이 formatting만 다루고 naming/import 순서 이외의 style 검사는 하지 않음
|
||||
- `GJF-README-C2`: Java 21 이상 JDK 가 필요함 (v1.35.0 기준, core/pom.xml 정의)
|
||||
- `GJF-README-C3`: 포맷 알고리즘 설정 불가 — 의도적 설계 결정
|
||||
- `GJF-README-C4`: JDK 16+ 에서 `--add-exports=jdk.compiler/com.sun.tools.javac.*=ALL-UNNAMED` 6개 flag 필요
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Spotless Gradle plugin 의 `googleJavaFormat()` step 이 자동으로 이 flag 를 처리하는지 여부 (Spotless 공식 문서 별도 확인 필요)
|
||||
- google-java-format 이 ca-tmpl 프로젝트의 실제 빌드에서 오류 없이 동작하는지 (로컬 검증 필요)
|
||||
- Checkstyle, SpotBugs 등 다른 static-analysis 도구와의 rule 중복/충돌 여부
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-tmpl 의 Spotless 설정이 JDK 21 에서 --add-exports flag 를 자동 주입하는지 (`spotless-gradle-plugin-readme.md` C4 참조)
|
||||
- `core/pom.xml` Java 21 버전 명시가 최신 릴리즈(v1.35.0)에서도 유지되는지 확인
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- README 에 scope 진술("naming 등은 대상 아님")이 없고 FAQ 에만 있음 — 두 URL 이 이 파일의 출처임을 frontmatter 와 § 출처에 명시했음.
|
||||
- `--aosp` flag 는 4-space indent 를 허용하는 유일한 configuration 예외이나, Google 내부 통합에서는 노출되지 않는다고 FAQ 에 명시됨.
|
||||
- JDK 16+ flag 목록: `--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED`, `.code=ALL-UNNAMED`, `.file=ALL-UNNAMED`, `.parser=ALL-UNNAMED`, `.tree=ALL-UNNAMED`, `.util=ALL-UNNAMED` (6개).
|
||||
- 추가로 봐야 할 동일 출처 페이지: https://github.com/google/google-java-format/wiki/FAQ (FAQ 전체), https://github.com/google/google-java-format/releases (버전 변경 이력)
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/spotless-gradle-plugin-readme]] — Spotless Gradle plugin(D1/D9 근거), `googleJavaFormat()` step 사용법
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/checkstyle-google-style-reference]] — Checkstyle google_checks.xml (D2 근거), naming/formatting 모듈 분류
|
||||
- Parent branch: [[raw/branch-notes/feature-static-analysis-quality-contract]]
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: official-doc / Google OAuth App Verification — OAuth App State Overview (Testing / Published-Unverified / Published-Verified)
|
||||
source_type: official-doc
|
||||
url: https://developers.google.com/identity/protocols/oauth2/production-readiness/overview
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-google-redirect-uri-policy]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, auth, oauth2, oidc]
|
||||
created: 2026-07-16
|
||||
---
|
||||
|
||||
# official-doc / Google OAuth App Verification — OAuth App State Overview
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] | D5 — "Google IdP scope는 `openid email profile`만 사용 → sensitive scope 회피 → verification 심사 불필요" 결정을, developer-doc 측(App Verification 섹션)에서 공식 확인. Testing+External 앱은 기본적으로 test user allowlist(최대 100명)에 한정되지만, basic identity scope(`openid`/`email`/`profile`)만 요청하면 allowlist 없이 임의 사용자가 접근 가능하다는 예외를 명시. 또한 verification(Published-Verified 상태)이 "public apps that request sensitive and restricted scopes"에 요구된다는 것을 명시해 D5의 "sensitive scope 회피 → verification 불필요" 논리의 반대쪽 근거를 제공.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://developers.google.com/identity/protocols/oauth2/production-readiness/overview
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Google — Identity Platform / App Verification to use Google Authorization APIs 문서군
|
||||
- 발행일: 불명 (rolling reference docs, 게시일 미표기)
|
||||
- 마지막 확인일: 2026-07-16
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
feature-keycloak-google-redirect-uri-policy D5는 "Google IdP scope를 `openid email profile`만 사용하면 verification 심사가 불필요하고 unverified 상태로 학습 환경이 동작한다"고 결정했지만, "unverified app + verification 요건" 부분은 verbatim 근거 없이 `UNSUPPORTED_DECISION`으로 남아 있었다. 본 자료는 Google App Verification 문서군의 "OAuth app state overview" 페이지로, Testing/Published-Unverified/Published-Verified 3개 상태별 접근 범위와 verification 요건을 표로 명시하고, basic identity scope 앱에 대한 allowlist 예외 조항을 담고 있어 그 gap을 developer-doc 측에서 직접 메운다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5문장)
|
||||
|
||||
> [§Google OAuth Platform behavior comparison — 표 행: Publishing Status=Testing, User Type=External] "Only users explicitly added to the test user allowlist can access the app (limited to a hard cap of 100 test users)."
|
||||
|
||||
> [§Google OAuth Platform behavior comparison — 같은 행, Testing/External] "Exception: If the app only requests basic identity scopes (openid, email, profile), any user can access without being on the allowlist."
|
||||
|
||||
> [§Google OAuth Platform behavior comparison — 표 행: Publishing Status=Published, User Type=External, Verification Status=Unverified] "Any Google user can access. Strongly discouraged."
|
||||
|
||||
> [§Google OAuth Platform behavior comparison — 같은 행, Published/External/Unverified] "for apps requesting sensitive or restricted scopes, unverified app warnings (Danger UI) will be displayed to users, and a hard cap of 100 total users applies."
|
||||
|
||||
> [§Google OAuth Platform behavior comparison — 표 행: Publishing Status=Published, User Type=External, Verification Status=Verified] "Any Google user can access. Required for public apps that request sensitive and restricted scopes."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-VERIFY-STATE-C1 | Testing 상태(Publishing Status) + External user type 앱은 test user allowlist에 명시적으로 추가된 사용자만 접근 가능하며, allowlist 상한은 100명이다 | [§표: Testing/External] "Only users explicitly added to the test user allowlist can access the app (limited to a hard cap of 100 test users)." | `official-vendor-doc` | Testing 상태로 유지되는 External 앱의 기본 접근 제한 규칙 | sensitive/restricted scope를 요청하는 앱이 Published로 전환된 뒤에도 동일한 100명 한도가 유지되는지 — Published-Unverified 행은 "총 사용자 100명"이라는 별도 조건(scope 트리거)으로 규정됨(C3 참조), Testing 행의 test-user-allowlist 상한과 동일 quota라는 근거는 본 인용에 없음 |
|
||||
| GOOGLE-VERIFY-STATE-C2 | Testing 상태 앱이 basic identity scope(openid, email, profile)만 요청하면 allowlist 예외가 적용되어, 어떤 사용자도 allowlist 등록 없이 접근할 수 있다 | [§표: Testing/External] "Exception: If the app only requests basic identity scopes (openid, email, profile), any user can access without being on the allowlist." | `official-vendor-doc` | branch D5의 `openid email profile` scope 선택 — Testing 상태에서 allowlist 등록 없이 임의 사용자가 접근 가능함을 공식 근거로 확정 | 이 예외가 Google verification 심사 자체를 완전히 면제한다는 뜻은 아님 — 이 문장은 Testing 상태의 "접근 대상 범위"만 규정하며, 앱을 Published로 전환할 때의 verification 요건은 별도 행(C3·C4)에서 규정됨 |
|
||||
| GOOGLE-VERIFY-STATE-C3 | Published-Unverified 상태(External)는 임의 Google 사용자가 접근 가능하지만 공식 문서가 "Strongly discouraged"로 명시하며, sensitive 또는 restricted scope를 요청하는 앱에는 unverified 경고 UI(Danger UI) 노출과 총 사용자 100명 한도가 적용된다 | [§표: Published/External/Unverified] "Any Google user can access. Strongly discouraged." + "for apps requesting sensitive or restricted scopes, unverified app warnings (Danger UI) will be displayed to users, and a hard cap of 100 total users applies." | `official-vendor-doc` | 앱을 Testing에서 Published로 전환하되 아직 verification을 완료하지 않은 상태의 위험 평가 | basic identity scope만 쓰는 앱이 Published-Unverified 상태에서 100명 cap이나 경고 UI로부터 면제되는지는 이 인용에서 명시적으로 다루지 않음(문장이 "sensitive or restricted scopes 요청 앱"에 한정) |
|
||||
| GOOGLE-VERIFY-STATE-C4 | Published-Verified 상태에서 임의 Google 사용자가 접근 가능하며, 이 verified 상태는 sensitive 및 restricted scope를 요청하는 public 앱에 대해 요구된다("Required for") | [§표: Published/External/Verified] "Any Google user can access. Required for public apps that request sensitive and restricted scopes." | `official-vendor-doc` | sensitive/restricted scope(예: Gmail, Drive 등)를 요청하는 프로덕션 공개 앱의 verification 필요성 판단 — D5의 "sensitive scope 회피 → verification 불필요" 논리의 대칭 근거(= sensitive scope를 쓰면 verification이 required) | basic identity scope만 쓰는 앱이 Published 상태에서 verification이 "불필요"하다고 이 문장이 직접 명시하지는 않음 — "sensitive/restricted → verified 필요"라는 필요조건만 서술하며, 그 역(비-sensitive scope → verified 불필요)은 이 인용 자체로 직접 증명되지 않는 논리적 추정 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard`
|
||||
- `official-vendor-doc` (본 문서 전 claim이 이 값)
|
||||
- `official-reference`
|
||||
- `company-case-study`
|
||||
- `engineering-blog`
|
||||
- `tutorial`
|
||||
- `needs-confirmation`
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `GOOGLE-VERIFY-STATE-C1`: Testing/External 앱의 test user allowlist 상한(100명) 규칙
|
||||
- `GOOGLE-VERIFY-STATE-C2`: basic identity scope(`openid`/`email`/`profile`)만 요청하는 Testing 앱은 allowlist 등록 없이 임의 사용자 접근 가능
|
||||
- `GOOGLE-VERIFY-STATE-C3`: Published-Unverified 상태의 위험(경고 UI + 100명 cap, sensitive/restricted scope 요청 시)
|
||||
- `GOOGLE-VERIFY-STATE-C4`: Published-Verified 상태가 sensitive/restricted scope를 요청하는 public 앱에 required임
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- basic identity scope만 쓰는 앱이 **Published**(Testing이 아닌) 상태에서도 verification 없이 무제한 접근 가능한지 — 이 표에서 basic-scope 예외는 Testing 행에만 명시되고 Published 행에는 별도 언급이 없음. D5의 "학습 환경 unverified 상태" 서술은 Testing 상태를 전제로 한다면 C2로 뒷받침되지만, Published 전환 이후는 C3·C4만 근거로 남는다
|
||||
- Testing 행의 "100 test users" cap과 Published-Unverified 행의 "100 total users" cap이 동일한 quota인지 — 원문이 두 조건을 서로 다른 행(서로 다른 publishing status)에서 별도로 서술하므로 혼동 금지
|
||||
- Keycloak Google IdP 브로커링이 실제로 Google 측 "basic identity scope" 판정 조건을 충족하는 요청을 보내는지(Keycloak default scope 설정이 정확히 `openid profile email`로 전송되는지)는 이 자료로 증명되지 않음 — `keycloak-google-idp-setup`(`KC-GIDP-C5`)이 그 근거
|
||||
- Google Workspace 관리자의 "Trusted" override가 개인(비-Workspace) Google 계정 사용자에게도 적용되는지 — 이 문서의 관리자 override 서술은 "Google Workspace 조직에 속한 사용자가 접근하는 경우"에 한정된다고 명시(§Administrative overrides)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 본 branch 학습 환경(개인 Google 계정, Workspace 아님)에서 Testing+External+basic-identity-scope 조합이 실제로 allowlist 없이 동작하는지 실 등록으로 검증 필요 (branch 전체가 현재 `documented-only`)
|
||||
- D5를 Published 상태까지 포함해 완전히 뒷받침하려면 basic-scope 앱의 Published 행 동작(경고 UI 여부, 100명 cap 적용 여부)을 다루는 별도 Google 문서 보강 필요 — 현재는 Testing 상태 범위로 D5의 UNSUPPORTED_DECISION을 부분 해소
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- WebFetch 툴이 이 페이지에서 (AI 요약 모드로) paraphrase된 "Key Points" 형식만 반환해 verbatim 인용에 부적합했음 — `curl`로 raw HTML을 받아 `<script>`/`<style>` 태그를 제거하고 텍스트만 추출하는 방식으로 verbatim 원문을 확보함(스크래치패드에 저장한 추출 텍스트 대상 self-grep 실행).
|
||||
- 인용 1·2 해석 후보 (미검증): Testing 행의 "test user allowlist" 예외가 basic-scope 앱에서 실제로 Google Cloud Console UI 상 test user 등록 필드 자체를 건너뛸 수 있게 하는지, 아니면 등록은 하되 강제되지 않는 것인지는 원문에서 UI 동작까지 다루지 않음.
|
||||
- 관련(중복 아님) 자료: `google-oauth-manage-app-audience-official`(support.google.com, Testing vs In production + 7일 authorization 만료 규칙)이 같은 branch D5를 뒷받침하는 근접 문서로 이미 raw에 존재. 그 문서는 test-user 등록·7일 만료·Sign in with Google 예외를 다루고, 본 문서는 Published-Unverified/Verified 3단계 상태 + verification 요건(sensitive/restricted scope) + Workspace admin override를 다룸 — 서로 다른 Google 문서 페이지이며 내용이 상호 보완적(중복 아님).
|
||||
- 추가로 봐야 할 동일 출처 페이지: "OAuth verification policies" 페이지(본문에서 링크로만 언급, "governed by OAuth verification policies") — sensitive/restricted scope 목록 자체의 verbatim 확보 필요.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/google-oauth-manage-app-audience-official]] — 같은 branch(D5) 근거, Testing vs In production 상태의 100 test-user + 7일 만료 규칙(상호 보완, 중복 아님)
|
||||
- [[raw/official-docs/google-oauth2-redirect-uri-validation-official]] — 같은 branch의 D1~D4 근거, redirect_uri 검증 규칙
|
||||
- [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak Google IdP default scope(`openid profile email`) 설정 근거, 본 자료의 basic-identity-scope 예외 조건과 직접 대응
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: official-doc / Google Auth Platform — Manage App Audience (Publishing Status: Testing vs In production)
|
||||
source_type: official-doc
|
||||
url: https://support.google.com/cloud/answer/15549945?hl=en
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-google-redirect-uri-policy]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, auth, google-aip, oauth2, oidc]
|
||||
created: 2026-07-16
|
||||
---
|
||||
|
||||
# official-doc / Google Auth Platform — Manage App Audience (Publishing Status: Testing vs In production)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] | D5 — Google OAuth app publishing status(Testing vs In production)의 100 test-user 상한 + 7일 authorization 만료 규칙, 그리고 basic identity scope(`name`/`email`/`profile`)가 이 만료·경고·test-user-list 요건을 면제받는다는 공식 근거. 기존 D5의 "unverified app + 100명 test users" 부분에 걸려 있던 `UNSUPPORTED_DECISION` 라벨을 verification-policy 범위에서 해소.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://support.google.com/cloud/answer/15549945?hl=en
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Google (Google Cloud Platform Console Help — Google Auth Platform)
|
||||
- 발행일: 불명(Google Help Center 문서, 게시일 미표기)
|
||||
- 마지막 확인일: 2026-07-16
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
feature-keycloak-google-redirect-uri-policy D5의 "Google IdP scope는 `openid email profile`만 사용 → sensitive scope 회피 → verification 심사 불필요 → unverified 상태로 100명 test users까지 정상 동작" 결정 중, "unverified + 100명 test users" 부분이 verbatim 근거 없이 `UNSUPPORTED_DECISION`으로 남아 있었다. 본 자료는 Testing/In production publishing status의 공식 규칙과, basic identity scope가 test-user 목록·경고·7일 만료를 면제받는다는 공식 예외 조항을 담고 있어 그 gap을 직접 메운다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5문장)
|
||||
|
||||
> [§Publishing status > Testing] "Projects configured with a publishing status of Testing are limited to up to 100 test users listed in the OAuth consent screen." (line 31)
|
||||
|
||||
> [§Publishing status > Testing] "Google will display a warning message before allowing a specified test user to authorize scopes requested by your project's OAuth clients." (line 32)
|
||||
|
||||
> [§Publishing status > Testing] "Authorizations by a test user will expire seven days from the time of consent." (line 33)
|
||||
|
||||
> [§Publishing status > Testing] "The only exception to this behavior is if your app requests a subset of the following: name, email address, and user profile (through the userinfo.email, userinfo.profile, openid scopes or their OpenID Connect equivalents). For such requests, your users do not need to be in the trusted user list, they will not see a warning message, and their authorizations will not expire after 7 days." (line 35)
|
||||
|
||||
> [§Publishing status > In Production] "Projects configured with a publishing status of In production are available to any user with a Google Account." (line 38)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-APPAUD-C1 | Testing publishing status는 OAuth consent screen에 등록된 최대 100명의 test user로 제한된다 | [§Publishing status > Testing] "Projects configured with a publishing status of Testing are limited to up to 100 test users listed in the OAuth consent screen." (line 31) | `official-vendor-doc` | Testing 상태 프로젝트의 test-user 등록 상한 | 이 100명 상한과 §OAuth user cap의 "100 new users in total"(unverified app screen 노출 시 신규 유저 누적 상한)이 동일 quota 라는 것 — 두 개념은 원문에서 서로 다른 섹션(Testing vs OAuth user cap)으로 구분되어 있음 |
|
||||
| GOOGLE-APPAUD-C2 | Testing 상태에서 Google은 test user가 scope를 승인하기 전에 경고 메시지를 표시한다 | [§Publishing status > Testing] "Google will display a warning message before allowing a specified test user to authorize scopes requested by your project's OAuth clients." (line 32) | `official-vendor-doc` | Testing 상태 + basic scope 예외에 해당하지 않는 모든 OAuth client의 test-user 승인 흐름 | 경고 메시지의 정확한 문구/UI 스크린샷 (본 자료는 존재 사실만 진술) |
|
||||
| GOOGLE-APPAUD-C3 | Test user의 authorization은 동의 시점으로부터 7일 후 만료되며, offline access type으로 발급된 refresh token도 함께 만료된다 | [§Publishing status > Testing] "Authorizations by a test user will expire seven days from the time of consent." (line 33) | `official-vendor-doc` | Testing 상태의 test-user authorization·refresh token 수명 | In production 상태에서의 authorization 수명 (별도 규칙 — 본 quote는 Testing 전용) |
|
||||
| GOOGLE-APPAUD-C4 | 앱이 name/email/user profile 중 일부만 (userinfo.email, userinfo.profile, openid scope 또는 그 OIDC 동등 항목을 통해) 요청하는 경우, 사용자는 trusted user list(=test user list)에 있을 필요가 없고, 경고 메시지를 보지 않으며, authorization이 7일 후 만료되지 않는다. Sign in with Google을 사용해도 이 예외가 적용된다 | [§Publishing status > Testing] "The only exception to this behavior is if your app requests a subset of the following: name, email address, and user profile (through the userinfo.email, userinfo.profile, openid scopes or their OpenID Connect equivalents). For such requests, your users do not need to be in the trusted user list, they will not see a warning message, and their authorizations will not expire after 7 days." (line 35) | `official-vendor-doc` | `openid email profile` (또는 그 부분집합)만 요청하는 OAuth client의 test-user 요건 면제 — 정확히 D5의 Keycloak Google IdP default scope(`openid profile email`, `KC-GIDP-C5`)와 일치 | 앱이 다른 OAuth scope(예: Gmail, Drive)를 **추가로** 요청하면 이 예외가 적용되지 않는다는 것(원문: "If your app requests any other OAuth scopes, then this exception does not apply." — 별도 문장, 본 인용 범위 밖). 또한 Testing 상태 자체를 벗어나게 하지는 않음(여전히 Testing이며, 단지 7일 만료·경고·test-user-list 요건만 면제) |
|
||||
| GOOGLE-APPAUD-C5 | In production publishing status의 프로젝트는 Google 계정을 가진 모든 사용자에게 열려 있다 ("Publish app" 버튼 선택 후 In production으로 간주되며, sensitive/restricted scope 요청 시 verification 대상이 될 수 있음) | [§Publishing status > In Production] "Projects configured with a publishing status of In production are available to any user with a Google Account." (line 38) | `official-vendor-doc` | Testing → In production 전환 후의 사용자 접근 범위 일반 규칙 | verification 프로세스의 세부 심사 기준·소요 기간 (본 인용 범위 밖 — 별도 문장에서 "may be subject to verification"으로만 언급) |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard`
|
||||
- `official-vendor-doc` (본 문서 전 claim이 이 값)
|
||||
- `official-reference`
|
||||
- `company-case-study`
|
||||
- `engineering-blog`
|
||||
- `tutorial`
|
||||
- `needs-confirmation`
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `GOOGLE-APPAUD-C1`: Testing 상태의 100 test-user 등록 상한
|
||||
- `GOOGLE-APPAUD-C2`: Testing 상태에서 test user 승인 전 경고 메시지 표시 사실
|
||||
- `GOOGLE-APPAUD-C3`: Testing 상태 test-user authorization의 7일 만료 규칙
|
||||
- `GOOGLE-APPAUD-C4`: `name`/`email`/`profile`(및 그 OIDC 동등 scope)만 요청하는 앱은 test-user-list 등록·경고·7일 만료 요건을 면제받는다는 공식 예외 — D5의 Keycloak default scope(`openid profile email`)와 정확히 일치하는 조건
|
||||
- `GOOGLE-APPAUD-C5`: In production 상태의 전체 사용자 개방 규칙
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- "100 test users" 상한과 §OAuth user cap의 "100 new users in total"(unverified app screen 노출 시 누적 신규 유저 상한)이 같은 quota인지 여부 — 원문에서 별도 섹션으로 구분되어 있어 혼동 금지. D5 branch note가 "unverified 상태로 100명 test users까지 정상 동작"이라 서술한 부분은 정확히는 GOOGLE-APPAUD-C1(Testing 상태 test-user 등록 상한)에 해당하며, unverified app screen의 "100 new users in total" 누적 상한(§OAuth user cap)과는 별개 개념
|
||||
- unverified app이 basic scope만 요청할 때도 "unverified app" 경고 화면 자체가 완전히 사라지는지 여부 — C4는 test-user-list 요건·7일 만료·(Testing 상태의) 경고 메시지 면제만 진술. In production 상태에서 sensitive/restricted scope 요청 시의 verification 요구는 별개 규칙(C5 및 그 이후 문장)
|
||||
- Keycloak 쪽 구현(default scope 설정이 실제로 Google 서버에 `openid profile email`로 전송되는지, IdP 설정 화면에서 별도 scope 추가가 없는지)은 이 자료로 증명되지 않음 — `keycloak-google-idp-setup` (KC-GIDP-C5) 이 그 근거
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- D5의 "unverified app screen"과 "100 test users" 두 개념이 실제 Google Cloud Console UI에서 어떻게 표시되는지 (Claims To Verify 항목으로 branch note에 등재 권고)
|
||||
- basic scope 예외가 적용된 상태에서 OAuth consent screen에 test user를 아예 등록하지 않아도 인증이 정상 동작하는지 실측 필요 (branch note는 여전히 test user 등록을 `planned` TODO로 유지 중 — 예외 적용 시 등록 자체가 불필요해질 가능성, 재검토 권고)
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- WebFetch(AI 요약 모드)가 이 페이지에서 paraphrase된 "Key Takeaways" 형식만 반환해 verbatim 인용에 부적합했음 — `curl` 로 raw HTML을 받아 스크립트/스타일 태그를 제거하고 텍스트만 추출하는 방식으로 verbatim 원문을 확보함. 이후 동일 도메인(`support.google.com`) 재조사 시 같은 방식(curl + HTML 태그 스트립) 권장.
|
||||
- 인용 1 해석 후보 (미검증): "100 test users" 상한과 "100 new users in total" 누적 상한이 실제로는 서로 다른 목적의 quota(등록 가능 인원 vs 생애주기 누적 승인 인원)로 보이나, 두 quota가 겹치는 시나리오(예: test user 100명을 다 채운 뒤 In production 전환 시 카운트 리셋 여부)는 원문에 명시되지 않음.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Google Auth Platform 문서군의 "Verification status" 페이지(본문 §In Production에서 링크로만 언급됨) — sensitive/restricted scope 판정 기준의 verbatim 확보 필요.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/google-oauth2-redirect-uri-validation-official]] — 같은 branch(D5)가 아닌 D1~D4의 근거, redirect_uri 검증 규칙
|
||||
- [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak Google IdP default scope(`openid profile email`) 설정 근거, 본 자료의 basic-scope 예외 조건과 직접 대응
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: official-doc / Google OAuth 2.0 — Manage OAuth Clients (Application Types & Private/Public Client Classification)
|
||||
source_type: official-doc
|
||||
url: https://support.google.com/cloud/answer/15549257?hl=en
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
related_branches: [feature-keycloak-google-redirect-uri-policy]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [keycloak-patterns, p3b-single-ec2-google, idp-brokering, google-oauth, client-application-type]
|
||||
created: 2026-07-16
|
||||
last_reviewed: 2026-07-16
|
||||
---
|
||||
|
||||
# Google OAuth 2.0 — Manage OAuth Clients (Application Types & Private/Public Client Classification)
|
||||
|
||||
> Layer: `raw/official-docs/` — Google Cloud Platform Console Help 공식 문서 발췌. P3B (단일 EC2 + Google IdP brokering)의 **Google OAuth 2.0 client Application-type 분류**(Web application vs Native[Android/iOS/Desktop/UWP/Chrome Extension] vs TV & Limited-Input) + **Private/Public Client 정의**의 1차 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] | D6 — Google OAuth 2.0 client Application-type 분류(Web application vs Native[Android/iOS/Desktop/UWP] vs TV & Limited-Input) 근거 + "Private Clients는 서버에서 안전하게 client_secret을 저장할 수 있다"는 정의로, Keycloak처럼 server-to-server로 `/token`을 호출하는 confidential client가 **Web application** 타입에 대응한다는 결정을 뒷받침 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://support.google.com/cloud/answer/15549257?hl=en
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Google — Google Cloud Platform Console Help
|
||||
- 발행일: rolling docs (Help Center article, 명시적 발행일 표기 없음)
|
||||
- 마지막 확인일: 2026-07-16
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
branch `feature-keycloak-google-redirect-uri-policy`의 D6("Google OAuth client Application type = Web application; Keycloak이 server-to-server `/token` 호출 → JavaScript origin 비워둠")는 기존에 `UNSUPPORTED_DECISION`이었다(cited raw에 Application type 정의 및 client 분류 verbatim 부재). 본 자료는 Google 공식 Console Help 문서에서 Application type 목록(Web / Native[Android·iOS·Desktop·UWP·Chrome Extension] / TV & Limited-Input)과 Private/Public Client 정의, Authorized JavaScript origins 조건부 요구사항을 verbatim으로 제공하여 D6의 근거 공백을 메운다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5문장)
|
||||
|
||||
> [§Client ID and Client Secret] "Private Clients: These apps, like web server applications, can securely store the client secret because they run on servers you control."
|
||||
|
||||
> [§Client ID and Client Secret] "Public Clients: Native apps or JavaScript-based apps fall under this category. They cannot securely store secrets, as they reside on user devices and as such do not use client secrets."
|
||||
|
||||
> [§Application types → Web Applications] "A web application is accessed by web browsers over a network."
|
||||
|
||||
> [§Application types → Native Applications] "Native Applications (Android, iOS, Desktop, UWP, Chrome Extensions, TV and Limited Input)"
|
||||
|
||||
> [§Application types → Web Applications → Authorized JavaScript origins] "Applications that use client-side JavaScript to access Google APIs must specify authorized JavaScript origins. The origins identify the domains from which your application can send API requests."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-CLIENTTYPE-C1 | Private Clients(웹 서버 애플리케이션 등)는 서버가 사용자 통제 하에 있어 client secret을 안전하게 저장할 수 있다 | [§Client ID and Client Secret] "Private Clients: These apps, like web server applications, can securely store the client secret because they run on servers you control." | `official-vendor-doc` | 서버 측(confidential) OAuth client 일반 — Keycloak처럼 server-to-server 로 Google과 통신하는 client 포함 | 이 문장 단독으로 Google Console의 "Web application" Application-type이 자동으로 "Private Client"로 분류된다고 명시하지는 않음 — "web server applications"라는 예시어와 C3("A web application is accessed by web browsers over a network")를 결합한 구조적 추론 |
|
||||
| GOOGLE-CLIENTTYPE-C2 | Public Clients는 native app 또는 JavaScript 기반 app이며, 사용자 기기에 상주하므로 secret을 안전하게 저장할 수 없고 client secret을 사용하지 않는다 | [§Client ID and Client Secret] "Public Clients: Native apps or JavaScript-based apps fall under this category. They cannot securely store secrets, as they reside on user devices and as such do not use client secrets." | `official-vendor-doc` | Native app / SPA(JavaScript 기반) client 분류의 대조 사례 | Console의 Application-type 목록(Android/iOS/Desktop/UWP/Chrome Extension/TV & Limited-input) 각각이 개별적으로 "Public"이라고 재확인하지는 않음 — "Native apps"라는 총칭과 C4의 Native Applications 목록을 결합한 추론 |
|
||||
| GOOGLE-CLIENTTYPE-C3 | "Web application" Application type은 "웹 브라우저를 통해 네트워크로 접근되는" 애플리케이션으로 정의된다 | [§Application types → Web Applications] "A web application is accessed by web browsers over a network." | `official-vendor-doc` | Google OAuth 2.0 client 등록 시 Application type 선택지 중 "Web application" 버킷의 정의 | 이 문장 자체는 client secret 저장 방식이나 confidential/public 분류를 직접 언급하지 않음(C1과 결합해야 함) |
|
||||
| GOOGLE-CLIENTTYPE-C4 | Native Applications 버킷은 Android, iOS, Desktop, UWP, Chrome Extensions, TV and Limited Input을 포괄한다 | [§Application types → Native Applications] "Native Applications (Android, iOS, Desktop, UWP, Chrome Extensions, TV and Limited Input)" | `official-vendor-doc` | Google Cloud Console OAuth client 생성 시 Web application 이외의 Application-type 버킷 열거 | 이 헤딩 자체가 "Public Client"라고 재확인하지는 않음(C2와 결합 필요) — TV & Limited-input이 별도 sub-flow(OAuth 2.0 TV and limited-input device flow)로 분리 운영된다는 세부는 본 인용 범위 밖 |
|
||||
| GOOGLE-CLIENTTYPE-C5 | client-side JavaScript로 Google API에 접근하는 애플리케이션은 authorized JavaScript origins를 지정해야 한다 | [§Application types → Web Applications → Authorized JavaScript origins] "Applications that use client-side JavaScript to access Google APIs must specify authorized JavaScript origins. The origins identify the domains from which your application can send API requests." | `official-vendor-doc` | Web application 타입 하위의 조건부 요구사항 — client-side JS 사용 여부가 트리거 | 이 문장은 "client-side JS를 쓰지 않으면 이 필드를 비워도 된다"는 역명제를 명시하지 않음 — 긍정 조건("쓰면 반드시 지정")만 서술. Keycloak의 server-to-server 시나리오(JS 미사용)에서 필드를 비우는 것이 안전하다는 결론은 이 인용의 직접 증명 범위 밖(역논리 추론) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `GOOGLE-CLIENTTYPE-C1`~`C2`: Google OAuth client는 Private(서버 보관 secret) vs Public(secret 미사용) 두 클래스로 분류되며, 각 클래스의 정의와 대표 예시(web server apps vs native/JS apps)
|
||||
- `GOOGLE-CLIENTTYPE-C3`~`C4`: Google Cloud Console에서 선택 가능한 Application type 버킷 목록 — Web application(브라우저로 접근) vs Native Applications(Android/iOS/Desktop/UWP/Chrome Extension/TV & Limited-input)
|
||||
- `GOOGLE-CLIENTTYPE-C5`: Authorized JavaScript origins가 필요한 조건(client-side JavaScript로 Google API 접근 시)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- "Web application" Application type이 자동으로 "Private Client"로 분류된다는 단일 명시 문장은 없음 — C1("web server applications")과 C3("accessed by web browsers over a network")를 결합한 구조적 추론
|
||||
- client-side JavaScript를 쓰지 않는 Web application(예: Keycloak의 server-to-server brokering)에서 Authorized JavaScript origins를 **비워도 되는지**의 역명제는 verbatim으로 확인되지 않음 — 긍정 조건만 서술됨
|
||||
- TV & Limited-input 이 Native Applications 헤딩 하위에서 구체적으로 별도 OAuth flow("TV and limited-input device flow")를 쓴다는 것은 본 5개 인용 범위 밖(문서 본문 별도 섹션에 존재 — 원문 확인됨, 단 미인용)
|
||||
- Keycloak이 이 문서에서 다뤄지는 것은 아님 — Keycloak을 confidential/server-side client로 다루는 것은 프로젝트 측 적용 해석
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- Keycloak Google IdP 브로커링에서 실제로 Authorized JavaScript origins를 비운 상태로 등록해도 `/token` 호출이 정상 동작하는지 (branch `feature-keycloak-google-redirect-uri-policy`의 Claims To Verify 항목)
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 이 자료로 branch D6의 Evidence Strength를 `UNSUPPORTED_DECISION` → `official-vendor-doc`(Application type 분류 및 Private Client 정의 부분)로 격상할 수 있는 근거가 마련됨. 단 "JavaScript origin 비움"의 역명제 부분은 여전히 근거 공백 — branch 측 Decision Evidence Map 갱신은 branch-note 작업자 몫(본 raw 문서는 인용·claim만 제공).
|
||||
- 원문에는 Android/iOS/UWP/Chrome Extension/TV/Desktop 각각의 세부 등록 필드(SHA1 fingerprint, Bundle ID, Store ID 등)도 있으나 본 branch(D6)의 결정 범위(Web application vs Native 버킷 구분)와 무관하여 인용하지 않음.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/google-oauth2-redirect-uri-validation-official]] — 동일 Google OAuth 2.0 client의 redirect URI 검증 규칙(D1~D4, D8 근거)
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak Admin Console 측 Google IdP 등록 절차(D1, D5 근거)
|
||||
- 이 자료를 인용한 wiki 요약: (미작성)
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: official-doc / Google Identity — OAuth 2.0 Policies (deployment-tier separation & credential security)
|
||||
source_type: official-doc
|
||||
url: https://developers.google.com/identity/protocols/oauth2/policies
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-idp-brokering-google-client]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, auth, oauth2, google-oidc]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-07-16
|
||||
last_reviewed: 2026-07-16
|
||||
---
|
||||
|
||||
# official-doc / Google Identity — OAuth 2.0 Policies (deployment-tier separation & credential security)
|
||||
|
||||
> Layer: `raw/official-docs/` — 외부 자료 원문 발췌·출처 기록.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]] | D7 — dev/staging/prod 환경별 Google OAuth (client/project) 분리 및 credential 처리 규칙(never-commit)의 공식 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://developers.google.com/identity/protocols/oauth2/policies
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Google (Google Identity Platform 공식 문서)
|
||||
- 발행일: (페이지에 명시된 발행일 없음 — Google Developers 문서, 상시 갱신형)
|
||||
- 마지막 확인일: 2026-07-16
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-keycloak-idp-brokering-google-client` branch의 D7 결정(환경별 OAuth client/project 분리)은 기존에 `UNSUPPORTED_DECISION`으로 라벨링되어 있었다. 이 페이지는 Google이 공식적으로 요구하는 "배포 단계별 별도 project" 규정과 그 적용 범위(= "production" app 정의), 그리고 credential 보안 취급 규칙의 1차 출처다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Use separate projects for testing and production] "Some policies and requirements only apply to production apps. For this reason, you must create separate projects in the Google Cloud Console for each deployment tier, such as development, staging, and production."
|
||||
|
||||
> [§Use separate projects for testing and production] "It isn't for personal use. An app is considered to be for personal use if it's not shared with anyone else or will be used by fewer than 100 people (all of whom are known personally to you)."
|
||||
|
||||
> [§Use separate projects for testing and production] "It isn't used for development, testing, or staging. It isn't for internal use; that is, restricted to people in your Google Workspace or Cloud Identity organization."
|
||||
|
||||
> [§Handle client credentials securely] "Treat your OAuth client credentials with extreme care, as they allow anyone who has them to use your app's identity to gain access to user information. Store your OAuth client information in a secure place and protect it, especially your client secret, just as you would a password." [...] "You must never commit client credentials into publicly available code repositories."
|
||||
|
||||
> [§Register an appropriate OAuth client] "You must create a separate OAuth client for each platform on which your app will run, such as a web server, an Android app, an iOS app, or a limited-input device."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-OAUTHPOLICY-C1 | Google 정책상 **"production" app 에 한해** 배포 단계(development/staging/production)마다 별도 Google Cloud Console project 생성이 요구된다 | [§Use separate projects for testing and production] "Some policies and requirements only apply to production apps. For this reason, you must create separate projects in the Google Cloud Console for each deployment tier, such as development, staging, and production." | `official-vendor-doc` | 이 페이지의 "production" 정의(GOOGLE-OAUTHPOLICY-C2)를 충족하는 앱 | 모든 앱(개인/내부용 포함)이 무조건 환경별 project 를 분리해야 한다는 것은 증명하지 않음 — production 여부가 선결 조건이며, 이 요구사항은 그 조건이 충족될 때만 발동 |
|
||||
| GOOGLE-OAUTHPOLICY-C2 | "Production" app 은 (a) personal use 가 아니고 (b) dev/test/staging 용이 아니고 (c) internal(Workspace/Cloud Identity 조직) 용이 아닌 경우로 정의된다. "공유 안 함 또는 100명 미만(모두 개인적으로 아는 사람)" 은 personal use 로 분류되어 production 정의에서 제외된다 | [§Use separate projects for testing and production] "It isn't for personal use. An app is considered to be for personal use if it's not shared with anyone else or will be used by fewer than 100 people (all of whom are known personally to you)." + "It isn't used for development, testing, or staging. It isn't for internal use; that is, restricted to people in your Google Workspace or Cloud Identity organization." | `official-vendor-doc` | 특정 앱이 GOOGLE-OAUTHPOLICY-C1(project 분리 의무)의 적용 대상인지 판정하는 기준 | 사용자 수·공유 범위가 향후에도 고정된다는 보장은 아님 — 100명 이상으로 확대되거나 개인 범위를 벗어나 공개되면 production 으로 전환되어 C1 이 발동됨을 암시할 뿐, 전환 시점의 절차는 이 인용에 없음 |
|
||||
| GOOGLE-OAUTHPOLICY-C3 | OAuth client credential(특히 client secret)은 비밀번호와 동일하게 취급해야 하며, public code repository 에 절대 커밋해서는 안 된다 (secret manager 사용 권장) | [§Handle client credentials securely] "Treat your OAuth client credentials with extreme care, ... just as you would a password." [...] "You must never commit client credentials into publicly available code repositories." | `official-vendor-doc` | **모든** OAuth 사용 앱 — 이 규칙은 "production" 스코프 절 밖(별도 섹션)에 있고, "Register an appropriate OAuth client" 절이 "every app that uses Google's OAuth 2.0 infrastructure" 를 대상으로 명시하므로 production/personal 구분 없이 적용 | 특정 secret manager 제품(Cloud Secret Manager 등) 사용을 강제하지는 않음 — "where possible" 권고 수준 |
|
||||
| GOOGLE-OAUTHPOLICY-C4 | 앱이 실행되는 플랫폼(web server / Android / iOS / limited-input device)마다 별도 OAuth client 를 등록해야 한다 | [§Register an appropriate OAuth client] "You must create a separate OAuth client for each platform on which your app will run, such as a web server, an Android app, an iOS app, or a limited-input device." | `official-vendor-doc` | 플랫폼 단위 client 분리 원칙 자체 — production/personal 무관하게 "every app" 대상 절에 위치 | Keycloak 서버가 Google 쪽에서 정확히 어떤 client type("web application" 등)에 해당하는지는 이 인용만으로 증명 안 됨 — Keycloak 공식 문서 별도 근거 필요 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `GOOGLE-OAUTHPOLICY-C1`: Google 정책은 **"production" app 요건을 충족하는 경우에만** 배포 단계별 별도 project 생성을 의무화한다.
|
||||
- `GOOGLE-OAUTHPOLICY-C2`: "production" 여부의 판정 기준(공유 범위 100명 미만 + 개인적으로 아는 사람 전원 / dev·test·staging 용도 아님 / Workspace·Cloud Identity 내부용 아님).
|
||||
- `GOOGLE-OAUTHPOLICY-C3`, `GOOGLE-OAUTHPOLICY-C4`: credential 보안 취급과 플랫폼별 client 분리는 **production 여부와 무관하게 "every app"** 에 적용되는 별도 조항.
|
||||
- **핵심 긴장(CRITICAL) — D7 에 대한 조건부 근거**:
|
||||
- branch `feature-keycloak-idp-brokering-google-client` 는 현재 `documented-only` / `planned` 단계의 **개인 학습 프로젝트**다. GOOGLE-OAUTHPOLICY-C2 의 "personal use" 예외 기준(공유 안 함 또는 100명 미만의 개인적으로 아는 사람) 을 문자 그대로 적용하면, 이 프로젝트는 현재 Google 이 정의하는 **"production" app 이 아닐 가능성이 높다.**
|
||||
- 따라서 **GOOGLE-OAUTHPOLICY-C1(환경별 project 분리 의무)은 이 프로젝트에 현재 시점에서 "공식 의무"로 적용되지 않는다** — 이는 무조건적 mandate 가 아니라, **실사용자·실배포 단계가 생겨 "production" 기준을 충족하는 시점부터 조건부로 발동**하는 요구사항이다. D7 을 이 자료로 정당화할 때는 "지금 당장 지켜야 하는 규정"이 아니라 "실 배포/실사용자 확대 시 반드시 준수해야 할 규정을 미리 설계에 반영한다"는 선제적 근거로 표현해야 한다.
|
||||
- 반면 GOOGLE-OAUTHPOLICY-C3(credential never-commit) 는 production 스코프 절 밖에 위치하므로, 개인 학습 프로젝트 단계에서도 **지금 바로 적용되는 무조건적 규칙**으로 취급 가능하다. D7 의 "credential 보안" 절반은 조건 없이 적용, "환경별 project 분리" 절반은 production 전환 시점부터 적용— 이 둘을 같은 강도로 서술하지 않는다.
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Keycloak 이 Google IdP broker 로 등록될 때 Google 이 정의하는 정확히 어떤 client type 에 해당하는지 (GOOGLE-OAUTHPOLICY-C4 의 한계).
|
||||
- "production" 전환 판정을 Google 이 어떻게 감지·집행하는지의 절차(예: 자동 심사, 수동 신고 등) — 이 페이지에는 정의만 있고 집행 메커니즘은 없음.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 현재 사용자 수·공개 범위가 실제로 "personal use" 예외 기준(100명 미만, 개인적으로 아는 사람) 을 충족하는지 재확인.
|
||||
- Keycloak Server Admin Guide 의 client type 권고(별도 raw 발췌 필요, `keycloak-google-idp-setup` 참조)와 대조.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- Google 문서 구조상 "Use separate projects for testing and production" 절은 "production app" 정의 절 바로 뒤에 이어지며, 정의 절이 없으면 분리 요구사항의 스코프를 오독하기 쉽다 — 두 절을 항상 같이 인용해야 함(이번 발췌에서 반영).
|
||||
- "Handle client credentials securely" 와 "Register an appropriate OAuth client" 절은 문서 구조상 production-스코프 절 앞(또는 별도)에 위치 — production 조건과 무관한 general policy 로 판단(위 Usage Boundaries 근거).
|
||||
- WebFetch 1차 결과는 요약/재구성된 텍스트였음(아래 검증 절차 참고) — curl 로 원본 HTML 을 재획득해 실제 페이지 바이트와 대조 후 인용을 확정함.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/google-openid-connect-oidc]] — Google OIDC discovery / scope / claim 표준
|
||||
- [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak 측 Google IdP 등록 절차 공식 문서
|
||||
- [[raw/official-docs/keycloak-identity-brokering-overview-official]] — Keycloak Identity Broker 공식 개요
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: Google OAuth 2.0 Web Server — Redirect URI Validation Rules
|
||||
source_type: official-doc
|
||||
url: https://developers.google.com/identity/protocols/oauth2/web-server#uri-validation
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [keycloak-patterns, p3b-single-ec2-google, idp-brokering, google-oauth, redirect-uri, public-uri, https]
|
||||
related_projects: [keycloak-patterns]
|
||||
related_branches: [feature-keycloak-patterns, feature-keycloak-single-ec2-google-federation, feature-keycloak-google-redirect-uri-policy, feature-keycloak-public-domain-tunneling]
|
||||
created: 2026-05-25
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Google OAuth 2.0 — Redirect URI Validation (공식)
|
||||
|
||||
> Layer: `raw/official-docs/` — Google Identity Platform 공식 문서 발췌. P3B (단일 EC2 + Google IdP brokering)의 **redirect URI 공개 도달성** 제약을 보여주는 1차 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-patterns]] | keycloak-patterns root — Google federation 변형(P3B)의 public domain 의무 제약 명시 |
|
||||
| [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] | P3B 단일 EC2에서 Keycloak broker endpoint URL이 public HTTPS hostname을 가져야 하는 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] | Google Cloud Console authorized redirect URI 등록 정책 (exact match, HTTPS 강제, raw IP 금지) 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-public-domain-tunneling]] | EC2가 raw IP만 가질 때 도메인 + tunneling (Cloudflare Tunnel / ngrok) 필요한 이유 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
Keycloak이 Google을 외부 IdP로 등록하면, Google이 사용자 로그인 후 **Keycloak의 broker endpoint**(`/realms/{realm}/broker/google/endpoint`)로 redirect한다. 이 redirect URI는 Google Cloud Console의 **OAuth 2.0 Client → Authorized redirect URIs**에 등록되어야 하며, Google이 검증 규칙을 강제한다.
|
||||
|
||||
단일 EC2 환경에서는 Keycloak이 `localhost:8080`에 떠 있지만, Google의 브라우저-side redirect는 **사용자 브라우저를 통한 redirect**이므로 사용자가 도달할 수 있는 public hostname이 필요하다. (Google 서버가 Keycloak에 직접 호출하는 게 아니라, 사용자 브라우저가 Google → Keycloak으로 navigate.)
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://developers.google.com/identity/protocols/oauth2/web-server#uri-validation
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Google — Identity Platform Documentation
|
||||
- 발행일: rolling docs
|
||||
- 마지막 확인일: 2026-05-27 (WebFetch 재검증 완료 — 5개 quote 중 4개 verbatim MATCH; quote 5 는 라이브 문서가 다른 형식으로 표현, 2026-05-27 update 본 추가)
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Redirect URI validation rules, 2026-05-27 verified MATCH] "Redirect URIs must use the HTTPS scheme, not plain HTTP. Localhost URIs (including localhost IP address URIs) are exempt from this rule."
|
||||
|
||||
> [§Redirect URI validation rules, 2026-05-27 verified MATCH] "Hosts cannot be raw IP addresses. Localhost IP addresses are exempted from this rule."
|
||||
|
||||
> [§Redirect URI validation rules, 2026-05-27 verified MATCH] "The value must exactly match one of the authorized redirect URIs for the OAuth 2.0 client, which you configured in the API Console. If this value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' error."
|
||||
|
||||
> [§Redirect URI validation rules, 2026-05-27 verified MATCH] "Redirect URIs cannot contain the fragment component."
|
||||
|
||||
> [§Redirect URI validation rules, 2026-05-25 capture — 형식 차이] "Wildcard characters" are not allowed in redirect URIs.
|
||||
|
||||
> [§Redirect URI validation rules, 2026-05-27 verified verbatim] "Redirect URIs cannot contain certain characters including: Wildcard characters (`'*'`)"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-REDIR-C1 | Google OAuth 2.0 redirect URI는 HTTPS scheme 필수 (localhost URI는 예외) | [§Redirect URI validation rules] "Redirect URIs must use the HTTPS scheme, not plain HTTP. Localhost URIs (including localhost IP address URIs) are exempt from this rule." | `official-vendor-doc` | Google Cloud Console의 OAuth 2.0 client 등록 | localhost 예외가 production에서도 유효하다는 뜻은 아님 — 단순 개발 편의 |
|
||||
| GOOGLE-REDIR-C2 | redirect URI의 host는 raw IP 주소 금지 (localhost IP는 예외) | [§Redirect URI validation rules] "Hosts cannot be raw IP addresses. Localhost IP addresses are exempted from this rule." | `official-vendor-doc` | EC2 public IP / GCE 인스턴스 IP 같은 raw IP를 redirect URI로 등록하려는 경우 | Cloudflare Tunnel의 `<UUID>.cfargotunnel.com` 같은 generic subdomain 등록 가능성은 본 인용 범위 밖 (별도 정책 확인 필요) |
|
||||
| GOOGLE-REDIR-C3 | request의 redirect URI 값은 등록된 authorized redirect URI 중 하나와 **정확히 일치**해야 하며, 불일치 시 `redirect_uri_mismatch` 에러 발생 | [§Redirect URI validation rules] "The value must exactly match one of the authorized redirect URIs for the OAuth 2.0 client, which you configured in the API Console. If this value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' error." | `official-vendor-doc` | Google Cloud Console에 등록된 모든 redirect URI 비교 시점 | "정확히 일치"의 trailing slash / case sensitivity / query string 정책 디테일은 본 인용 직접 다루지 않음 — 일반적 OAuth 관례상 byte-level exact match로 추정 (verification 필요) |
|
||||
| GOOGLE-REDIR-C4 | redirect URI는 fragment component (`#...`) 를 포함할 수 없음 | [§Redirect URI validation rules] "Redirect URIs cannot contain the fragment component." | `official-vendor-doc` | Google OAuth 2.0 client redirect URI 등록 | Implicit flow의 fragment 응답 메커니즘과 별개 — 등록 URI 자체의 제약 |
|
||||
| GOOGLE-REDIR-C5 | redirect URI에 wildcard character (`*` 등) 사용 불가 | [§Redirect URI validation rules, 2026-05-27 verified] "Redirect URIs cannot contain certain characters including: Wildcard characters (`'*'`)" | `official-vendor-doc` | 다중 환경 (dev/staging/prod)에서 redirect URI 관리 시 | 각 환경마다 redirect URI를 개별 등록해야 한다는 결론은 본 인용에서 유도 가능, 단 환경 분리 best practice 자체는 별도 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `GOOGLE-REDIR-C1`~`C5`: Google OAuth 2.0 redirect URI 등록 시 5가지 검증 규칙 (HTTPS, no-raw-IP, exact match, no fragment, no wildcard)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- "exact match"의 byte-level 정확한 정의 (trailing slash, query string, encoding normalization) — 일반 관례에 의존
|
||||
- localhost 예외가 production에서 사용 가능한지 (단순 개발 시나리오 권고만)
|
||||
- Cloudflare Tunnel 의 `<UUID>.cfargotunnel.com` 같은 generic subdomain이 "raw IP가 아니므로" 무조건 허용되는지 (별도 vendor 정책 확인 필요)
|
||||
- Google이 IP allowlist / domain ownership verification을 어떤 시점에 강제하는지 (별도 페이지: OAuth 동의 화면 설정)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- EC2 public IP만 가진 환경에서 Cloudflare Tunnel `<UUID>.cfargotunnel.com` 등록이 실제로 통과하는지 (P3B 실험 필요)
|
||||
- Keycloak의 broker endpoint URL이 `KC_HOSTNAME` + realm 이름으로 자동 생성되므로, redirect_uri_mismatch 디버깅 시 Keycloak 측 issuer/hostname 설정 검증 필수
|
||||
- ngrok 무료 plan의 매번 변경되는 URL을 매 세션마다 Google Console에 재등록하는 friction (개발 편의성 비교 시)
|
||||
|
||||
## P3B 함의 (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용이 아닌 패턴 결정 컨텍스트 해석. wiki 추출 시 옮겨야 함.
|
||||
|
||||
- **HTTPS 강제**: `http://` redirect URI는 `localhost` 한정 예외. EC2 public IP/domain은 반드시 **HTTPS**.
|
||||
- **Raw IP 금지**: EC2 public IP (예: `https://3.34.12.5/...`)는 등록 불가. **도메인이 필요**. (localhost 예외이지만 단일 EC2 외부 노출 의미 없음.)
|
||||
- **Exact match**: `https://kc.example.com/realms/dev/broker/google/endpoint` 형태 그대로 등록. trailing slash, port, path 모두 정확히 일치해야 함.
|
||||
- **No fragments / wildcards**: `https://*.example.com/...` 또는 `https://example.com/#foo` 사용 불가.
|
||||
- **개발용 ngrok URL** 사용 시 → 매번 새 URL → Google Console 등록 갱신 필요(=학습 friction).
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 2026-05-27 re-verification: WebFetch 재확인 완료. Quote 1~4 verbatim MATCH. Quote 5 의 라이브 본문은 "Wildcard characters" 단독 문장이 아니라 "Redirect URIs cannot contain certain characters including: Wildcard characters (`'*'`)" 형식의 enumeration 항목 — 2026-05-25 capture 가 단편화한 표현이었음. 라이브 verbatim quote 를 추가 보존. 의미는 동일하므로 GOOGLE-REDIR-C5 의 strength 는 official-vendor-doc 유지.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/cloudflare-tunnel-routing-official]] — public hostname 노출 수단 (raw IP 금지 → 도메인 필요한 결정의 해법)
|
||||
- [[raw/official-docs/keycloak-reverseproxy-official]] — Keycloak이 broker endpoint URL을 어떻게 생성하는지 (`KC_HOSTNAME`)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-keycloak-patterns]]
|
||||
- [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]]
|
||||
- [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]]
|
||||
- [[raw/branch-notes/feature-keycloak-public-domain-tunneling]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: official-doc / Google Identity — Using OAuth 2.0 for Web Server Applications
|
||||
source_type: official-doc
|
||||
url: https://developers.google.com/identity/protocols/oauth2/web-server
|
||||
archive_url:
|
||||
related_branches: [feature-keycloak-google-redirect-uri-policy]
|
||||
related_projects: [keycloak-patterns]
|
||||
tags: [official-doc, keycloak-patterns, auth, oauth2, google-aip]
|
||||
created: 2026-07-16
|
||||
---
|
||||
|
||||
# official-doc / Google Identity — Using OAuth 2.0 for Web Server Applications
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 원문 발췌·출처 기록. 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-google-redirect-uri-policy]] | D6 — Google OAuth client 의 confidential/server-to-server (`Web application`) flow 채택 근거. Keycloak 이 server-side 로 `/token` 을 호출하는 flow 라는 점, "Web application" application type 을 선택하라는 명시적 지침, 그리고 Authorized redirect URIs 요구사항이 이 문서에 근거함. 이 문서는 "JavaScript origins" 를 다루지 않으므로 — JS origins 를 비워두는 결정은 이 문서만으로는 뒷받침되지 않음(별도 근거 필요, `UNSUPPORTED_DECISION` 유지). |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://developers.google.com/identity/protocols/oauth2/web-server
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Google (Google Identity Platform — Google Identity 공식 문서)
|
||||
- 발행일: (문서에 명시적 발행일 없음 — Google Identity 공식 레퍼런스, 상시 갱신)
|
||||
- 마지막 확인일: 2026-07-16
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
Keycloak 이 Google 을 OIDC/OAuth2 IdP 로 브로커링할 때, Google 이 정의하는 "web server application" flow (confidential client, server-side token exchange) 가 정확히 Keycloak 의 동작 방식과 일치하는지 확인하기 위해 저장. `feature-keycloak-google-redirect-uri-policy` D6 (Application type = Web application, JS origins 비움) 의 근거 공백을 메우려는 목적.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 5문장)
|
||||
|
||||
> [§Overview] "This OAuth 2.0 flow is specifically for user authorization. It is designed for applications that can store confidential information and maintain state." (line 33 in fetched text)
|
||||
|
||||
> [§Create authorization credentials — Set a redirect URI] "Select the Web application application type." (line 39 in fetched text)
|
||||
|
||||
> [§Set a redirect URI] "Applications that use languages and frameworks like PHP, Java, Python, Ruby, and .NET must specify authorized redirect URIs." (line 46 in fetched text)
|
||||
|
||||
> [§Step 5: Exchange authorization code for refresh and access tokens] "POST /token HTTP/1.1" / "Host: oauth2.googleapis.com" / "client_id=your_client_id&" / "grant_type=authorization_code" (lines 49-56 in fetched text — literal code sample of the token-exchange HTTP request)
|
||||
|
||||
> [§Step 5 parameter table — `client_secret`] "The client secret obtained from the Cloud Console [Clients page]." — parameter listed as **Optional** in the general parameter table, not marked required in the literal example code block shown above (line 71/74 in fetched text)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-WEBSERVER-C1 | 이 문서가 설명하는 OAuth 2.0 web-server flow 는 confidential information 을 저장하고 state 를 유지할 수 있는 애플리케이션을 위해 설계됨 | "This OAuth 2.0 flow is specifically for user authorization. It is designed for applications that can store confidential information and maintain state." | `official-vendor-doc` | server-side/confidential client 아키텍처(Keycloak 같은 IdP broker 포함)가 이 flow 범주에 해당함을 뒷받침 | "confidential" 의 정확한 기술적 경계(예: client_secret 저장 위치·rotation 정책)는 이 문장만으로 정의되지 않음 |
|
||||
| GOOGLE-WEBSERVER-C2 | OAuth credentials 생성 시 "Web application" application type 을 선택하도록 명시적으로 지시 | "Select the Web application application type." | `official-vendor-doc` | Keycloak Google IdP 등록 시 Google Cloud Console 에서 선택할 Application type 값 = `Web application` | "Web application" type 과 다른 type(예: Desktop, TVs/Limited Input) 간의 세부 기능 차이는 이 한 문장으로 증명되지 않음 |
|
||||
| GOOGLE-WEBSERVER-C3 | PHP/Java/Python/Ruby/.NET 같은 언어·프레임워크를 쓰는 애플리케이션은 authorized redirect URIs 를 반드시 지정해야 함 | "Applications that use languages and frameworks like PHP, Java, Python, Ruby, and .NET must specify authorized redirect URIs." | `official-vendor-doc` | server-side 애플리케이션(Keycloak 포함, JVM 기반)이 Authorized redirect URIs 를 등록해야 하는 근거 | 이 문장은 "JavaScript origins" 요구사항을 언급하지 않음 — JS origins 를 비워도 되는지 여부에 대해서는 침묵(증명도 반증도 아님) |
|
||||
| GOOGLE-WEBSERVER-C4 | 토큰 교환은 `https://oauth2.googleapis.com/token` 에 대한 서버 측 HTTP POST 이며, 예시 코드에는 `code`, `client_id`, `redirect_uri`, `grant_type=authorization_code` 파라미터가 literal 하게 표시됨 | "POST /token HTTP/1.1" / "Host: oauth2.googleapis.com" / "client_id=your_client_id&" / "grant_type=authorization_code" | `official-vendor-doc` | 토큰 엔드포인트 URL 과 HTTP method, 그리고 `client_id`/`grant_type`/`redirect_uri`/`code` 파라미터가 실제 예시에 등장함을 증명 | 이 예시 코드 블록 자체에는 `client_secret` 이 literal 하게 표시되지 않음 — client_secret 이 이 특정 요청에 "항상 필수"라는 것은 이 코드 블록만으로는 증명되지 않음(별도 파라미터 표 참조, 아래 C5) |
|
||||
| GOOGLE-WEBSERVER-C5 | `client_secret` 파라미터는 Cloud Console 에서 발급받는 client secret 이며, 문서의 일반 파라미터 표에서는 **Optional** 로 표기됨 | "The client secret obtained from the Cloud Console [Clients page]." (파라미터 표, Optional 로 라벨링) | `official-vendor-doc` | `client_secret` 이 무엇인지(출처: Cloud Console) 를 증명. confidential client 인 web-server flow 맥락에서는 사실상 필요하지만, 문서의 표 라벨 자체는 "Optional" | 이 표가 "Optional" 이라고 표기한 이유(다른 flow 유형과 공유되는 범용 파라미터 표이기 때문인지)는 이 인용만으로 확정 불가 — web-server flow 한정 "client_secret 필수" 단정은 이 raw 만으로는 `needs-confirmation` |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `GOOGLE-WEBSERVER-C1`: web-server flow 의 대상은 confidential/stateful 애플리케이션
|
||||
- `GOOGLE-WEBSERVER-C2`: Google Cloud Console 에서 "Web application" application type 을 명시적으로 선택해야 함
|
||||
- `GOOGLE-WEBSERVER-C3`: server-side 애플리케이션은 authorized redirect URIs 등록 의무
|
||||
- `GOOGLE-WEBSERVER-C4`: 토큰 교환 엔드포인트(`oauth2.googleapis.com/token`)와 예시 요청의 literal 파라미터 구성
|
||||
- `GOOGLE-WEBSERVER-C5`: `client_secret` 의 출처(Cloud Console) 및 일반 파라미터 표상 Optional 라벨
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- "JavaScript origins 를 비워도 된다"는 명시적 문장은 이 문서에 **존재하지 않음** — 이 문서는 JavaScript origins 자체를 전혀 언급하지 않는다(구조적 침묵). branch D6 의 "JS origins 비움" 결정을 이 문서만으로 FACT 화할 수 없다 — `UNSUPPORTED_DECISION` 유지 필요.
|
||||
- `client_secret` 이 web-server flow 에서 "항상 필수"라는 단정 — 일반 파라미터 표는 Optional 로 표기하며, flow별 필수 여부 구분은 이 인용 범위 밖.
|
||||
- Keycloak 이 실제로 이 Google flow 규격을 완전히 준수해 구현되어 있는지 — 이 문서는 Google 측 사양만 다루고 Keycloak 구현을 증명하지 않음(Keycloak 측은 별도 raw, `keycloak-google-idp-setup` 참조).
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- Google Cloud Console 실제 OAuth client 생성 화면에서 "Web application" 선택 시 "Authorized JavaScript origins" 필드가 실제로 optional/비워둘 수 있는 UI 인지 스크린샷/실험으로 확인 필요.
|
||||
- `client_secret` 이 web-server flow 컨텍스트에서 실제로 required 로 강제되는지 (Optional 라벨이 다른 flow 와 공유되는 범용 표라서 그런 것인지) Google Cloud Console 실제 등록 흐름으로 재확인 필요.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 본 raw 는 WebFetch 결과를 근거로 작성됨 — WebFetch 는 HTML을 markdown 변환 + 소형 모델 요약을 거치므로, 진짜 byte-level HTML 원문은 아니다. 다만 verbatim 재현을 3회 별도 요청하여 핵심 문장을 교차 확인했고, self-grep 으로 저장된 fetch 텍스트와 일치함을 검증함.
|
||||
- "JavaScript origins" 미언급은 fabrication 방지를 위해 의도적으로 "침묵"으로만 기록 — "비워도 된다"는 허용 문장으로 재구성하지 않음.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Google "Setting up OAuth 2.0" (Cloud Console credential 생성 UI 가이드), Google OAuth 2.0 Client ID application type 비교 페이지 — "Web application" vs 기타 type 차이 및 JavaScript origins 필드 조건을 다루는 페이지가 있는지 확인 필요.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/google-oauth2-redirect-uri-validation-official]] — Google redirect_uri 검증 규칙(exact match, HTTPS, wildcard 금지 등) 공식 문서. 본 문서와 함께 D1~D4, D6 근거.
|
||||
- [[raw/official-docs/keycloak-google-idp-setup]] — Keycloak 측 Google IdP 등록 절차(Redirect URI 표시값, Client ID/Secret 입력 위치). 본 문서(Google 측 사양)와 짝을 이루는 Keycloak 측 절차 문서.
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: Google OpenID Connect Discovery 문서 (공식)
|
||||
source_type: official-doc
|
||||
url: https://accounts.google.com/.well-known/openid-configuration
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [keycloak-patterns, p2b-spa-google-federation, google-oidc, discovery, jwks, claim-mapping]
|
||||
related_projects: [keycloak-patterns]
|
||||
related_branches: [feature-keycloak-internal-spa-direct-google-federation, feature-keycloak-idp-brokering-google-client, feature-keycloak-google-claim-attribute-mapping, feature-keycloak-first-broker-login-flow]
|
||||
created: 2026-05-25
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Google OpenID Connect Discovery
|
||||
|
||||
> Layer: `raw/official-docs/` — Google OIDC discovery document + 공식 OpenID Connect 가이드 발췌. Keycloak이 Google을 IdP로 brokering할 때의 endpoint·scope·claim 표준.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] | SPA Direct + Google federation에서 Keycloak이 Google discovery URL을 fetch하여 IdP 구성하는 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]] | Keycloak Google IdP client 등록 시 `authorization_endpoint`/`token_endpoint`/`jwks_uri` 채워야 하는 값의 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] | `sub` / `email` / `email_verified` / `picture` / `name` / `hd` claim을 Keycloak user attribute로 매핑하는 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-first-broker-login-flow]] | first broker login flow에서 `email_verified`·`sub` 기반 user linking 결정 근거 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
Keycloak이 Google을 외부 IdP로 등록하면 discovery URL (`https://accounts.google.com/.well-known/openid-configuration`) 을 fetch하여 endpoint와 JWKS를 자동 구성한다. 본 raw는 그 discovery document와 OIDC 통합 시 사용하는 scope/claim 표준의 발췌 기록.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL (discovery): https://accounts.google.com/.well-known/openid-configuration
|
||||
- 보조 URL (가이드): https://developers.google.com/identity/openid-connect/openid-connect
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Google — Identity Platform Documentation
|
||||
- 발행일: rolling docs (discovery document는 live JSON)
|
||||
- 마지막 확인일: 2026-05-27 (WebFetch 재검증 완료 — discovery JSON 6필드 verbatim MATCH; 가이드 5개 quote 중 3개 verbatim MATCH, 2개 (nonce, hd) 는 라이브 본문이 다른 표현, 2026-05-27 verified 본 추가)
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
### Discovery Document 필드 (verbatim JSON, 2026-05-27 verified MATCH 6개 모두)
|
||||
|
||||
> [discovery JSON, 2026-05-27 verified] `"issuer": "https://accounts.google.com"`
|
||||
|
||||
> [discovery JSON, 2026-05-27 verified] `"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth"`
|
||||
|
||||
> [discovery JSON, 2026-05-27 verified] `"token_endpoint": "https://oauth2.googleapis.com/token"`
|
||||
|
||||
> [discovery JSON, 2026-05-27 verified] `"userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo"`
|
||||
|
||||
> [discovery JSON, 2026-05-27 verified] `"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs"`
|
||||
|
||||
> [discovery JSON, 2026-05-27 verified] `"id_token_signing_alg_values_supported": ["RS256"]`
|
||||
|
||||
### Scope / Claim / Validation 설명 (보조 가이드)
|
||||
|
||||
> [OpenID Connect guide — scope, 2026-05-27 verified MATCH] "The scope parameter must begin with the `openid` value and then include the `profile` value, the `email` value, or both."
|
||||
|
||||
> [OpenID Connect guide — nonce, 2026-05-25 capture — paraphrase] "The nonce parameter is required ... enables replay protection when present."
|
||||
|
||||
> [OpenID Connect guide — nonce, 2026-05-27 verified verbatim] "`nonce` (Required) A random value generated by your app that enables replay protection."
|
||||
|
||||
> [OpenID Connect guide — sub claim, 2026-05-27 verified MATCH] "`sub`: An identifier for the user, unique among all Google Accounts and never reused."
|
||||
|
||||
> [OpenID Connect guide — hd claim, 2026-05-25 capture — paraphrase] "hd: Domain claim for Google Workspace users."
|
||||
|
||||
> [OpenID Connect guide — hd claim, 2026-05-27 verified verbatim] "The domain associated with the Google Workspace or Cloud organization of the user."
|
||||
|
||||
> [OpenID Connect guide — token validation, 2026-05-27 verified MATCH] "For production purposes, retrieve Google's public keys from the keys endpoint and perform the validation locally."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOOGLE-OIDC-C1 | Google OIDC issuer는 `https://accounts.google.com` | [discovery JSON] `"issuer": "https://accounts.google.com"` | `official-vendor-doc` | Keycloak Google IdP의 issuer URL 검증 / ID token `iss` claim 비교 | `accounts.google.com` 외 alias가 사용된다는 뜻은 아님 — `iss` 비교는 정확히 이 문자열로 |
|
||||
| GOOGLE-OIDC-C2 | Google OIDC endpoint URL: `authorization_endpoint = https://accounts.google.com/o/oauth2/v2/auth`, `token_endpoint = https://oauth2.googleapis.com/token`, `userinfo_endpoint = https://openidconnect.googleapis.com/v1/userinfo`, `jwks_uri = https://www.googleapis.com/oauth2/v3/certs` | [discovery JSON] 위 4개 필드 | `official-vendor-doc` | Keycloak Google IdP 수동 등록 / OAuth client 라이브러리 설정 | 각 endpoint의 SLA / rate limit / 응답 schema 디테일은 별도 페이지 |
|
||||
| GOOGLE-OIDC-C3 | Google ID token 서명 알고리즘은 `RS256`만 지원 | [discovery JSON] `"id_token_signing_alg_values_supported": ["RS256"]` | `official-vendor-doc` | ID token signature verification 시 알고리즘 선택 | ES256 / EdDSA 같은 다른 알고리즘이 향후 추가될 가능성은 본 시점 인용에선 불확실 |
|
||||
| GOOGLE-OIDC-C4 | OIDC scope는 `openid` 로 시작하고 `profile`, `email` 중 하나 이상 포함해야 함 | [OpenID Connect guide — scope] "The scope parameter must begin with the openid value and then include the profile value, the email value, or both." | `official-vendor-doc` | Google OIDC authorization request 의 scope 파라미터 | 기타 scope (`https://www.googleapis.com/auth/...`) 추가 가능성은 본 인용에 직접 없음 — OAuth scope spec에서 별도 |
|
||||
| GOOGLE-OIDC-C5 | `nonce` 파라미터는 required, replay 보호 목적 | [OpenID Connect guide — nonce, 2026-05-27 verified] "`nonce` (Required) A random value generated by your app that enables replay protection." | `official-vendor-doc` | Authorization request 의 `nonce` 처리 | nonce 생성/검증의 길이/엔트로피 권고는 본 인용 직접 다루지 않음 — OIDC core spec 참조 |
|
||||
| GOOGLE-OIDC-C6 | `sub` claim은 Google Account 전역에서 unique하고 재사용되지 않음 | [OpenID Connect guide — sub claim, 2026-05-27 verified] "`sub`: An identifier for the user, unique among all Google Accounts and never reused." | `official-vendor-doc` | Keycloak first broker login의 user linking 정책 / DB primary key 설계 | "use sub, not email" 권고 절은 라이브 본문에서 본 sub 정의문에 직접 따라붙지 않음 — 별도 단락. email 변경 가능성은 본 quote 직접 다루지 않음 |
|
||||
| GOOGLE-OIDC-C7 | `hd` claim은 user 의 Google Workspace 또는 Cloud organization 과 연관된 도메인 | [OpenID Connect guide — hd claim, 2026-05-27 verified] "The domain associated with the Google Workspace or Cloud organization of the user." | `official-vendor-doc` | Workspace 도메인 제한 정책 (특정 회사 도메인만 허용) | personal Google account 의 `hd` 값 부재 처리는 본 인용에 명시 없음 — 누락 시 null/없음으로 추정 (검증 필요) |
|
||||
| GOOGLE-OIDC-C8 | Production 환경에서 Google public key를 keys endpoint에서 받아 **로컬 검증** 권장 | [OpenID Connect guide — token validation] "For production purposes, retrieve Google's public keys from the keys endpoint and perform the validation locally." | `official-vendor-doc` | ID token 검증 deployment | Google의 `tokeninfo` endpoint 사용은 dev/디버깅용만 권장 — 본 인용 직접 다루지 않으나 "locally" 권고에서 유추 가능 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `GOOGLE-OIDC-C1`~`C3`: Google OIDC discovery document의 issuer, 4개 endpoint, RS256 서명 알고리즘
|
||||
- `GOOGLE-OIDC-C4`~`C5`: scope 필수 값과 nonce required 정책
|
||||
- `GOOGLE-OIDC-C6`~`C8`: `sub` claim primary key 권고, `hd` claim Workspace 의미, ID token 로컬 검증 권고
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- Keycloak이 5단계 검증 (signature / iss / aud / exp / hd) 을 정확히 어떤 단계로 수행하는지 (Keycloak vendor 문서 참조)
|
||||
- `email_verified` 가 false인 user 처리 정책 (first broker login flow 설정 결정)
|
||||
- `picture`, `name`, `family_name`, `given_name` claim의 인코딩/언어 규칙
|
||||
- Workspace user의 `hd` claim 부재 / 잘못된 값일 때 동작
|
||||
- Google이 향후 ES256 등 알고리즘을 추가할 가능성 / RS256 deprecation timeline
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- Keycloak Google IdP가 `.well-known` 을 자동 fetch하는지 vs 수동 endpoint 입력해야 하는지 (vendor 옵션)
|
||||
- Keycloak이 발급한 access token이 Google ID token claim을 어떻게 포함/변환하는지 (claim-to-claim mapper 설정)
|
||||
- first broker login flow에서 `email_verified=true` AND `sub=...` 기반 자동 link vs 수동 confirmation 선택
|
||||
|
||||
## P2B 패턴에서 의미 (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용이 아닌 패턴 결정 컨텍스트 해석. wiki 추출 시 옮겨야 함.
|
||||
|
||||
- Keycloak의 Google IdP 설정 시 이 discovery URL 그대로 사용 가능 (Keycloak이 `.well-known` 자동 fetch 지원).
|
||||
- Keycloak이 5단계 검증을 내부적으로 수행. 백엔드는 **Google ID token을 직접 검증하지 않음** — Keycloak이 발급한 access token만 검증.
|
||||
- claim mapping에서 사용되는 주요 필드:
|
||||
- `sub` → Keycloak user의 `federated identity ID`
|
||||
- `email`, `email_verified` → Keycloak user `email` 속성 + first broker login flow의 link 기준
|
||||
- `picture`, `name` → Keycloak user attribute / custom claim
|
||||
- `hd` → 정책 게이트 (특정 도메인만 허용)
|
||||
|
||||
### ID Token 검증 5단계 (Google 공식 권고 — 발췌 요약)
|
||||
|
||||
1. signature를 Google certificates (JWKS)로 검증
|
||||
2. `iss` == `https://accounts.google.com`
|
||||
3. `aud` == client_id
|
||||
4. `exp` 만료 확인
|
||||
5. `hd` claim 확인 (Workspace 제한 시)
|
||||
|
||||
> 위 5단계는 user 기존 raw에 정리된 내용. Google 공식 가이드의 verbatim block 인용은 본 raw에 포함되지 않았으므로 (단계별 문장 발췌 없음), production 적용 시 `GOOGLE-OIDC-C8` 의 "perform the validation locally" 권고 + OpenID Connect Core §3.1.3.7 의 표준 5단계와 교차 확인 필요.
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 2026-05-27 re-verification: WebFetch 재확인 완료. Discovery JSON 6 필드 verbatim MATCH (issuer/4 endpoints/id_token_signing_alg_values_supported). 가이드 quote 중 scope, sub, token validation 은 verbatim MATCH. nonce 와 hd 는 2026-05-25 capture 가 paraphrase 였음 — 라이브 verbatim quote 를 추가 보존하고 Claims 표의 Evidence quote 도 라이브 표현으로 교체. 의미는 동일하므로 strength 유지.
|
||||
- `scopes_supported`, `claims_supported`, `response_types_supported` 등 추가 필드는 user 기존 raw에 table로 정리되어 있으나 원문 verbatim 인용으로 보존하기 어려운 형식 — Claims 표에선 명시적 quote가 있는 3개 핵심 필드(`issuer`, 4개 endpoint, `id_token_signing_alg_values_supported`)만 채택.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/keycloak-identity-brokering-overview-official]]
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]]
|
||||
- [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]]
|
||||
- [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]]
|
||||
- [[raw/branch-notes/feature-keycloak-first-broker-login-flow]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Google Identity — OpenID Connect (OIDC) 공식 문서
|
||||
source_type: official-doc
|
||||
url: https://developers.google.com/identity/openid-connect/openid-connect
|
||||
archive_url:
|
||||
related_projects: [keycloak-patterns]
|
||||
related_branches: [feature-keycloak-patterns, feature-keycloak-edge-forwardauth-google-federation, feature-keycloak-idp-brokering-google-client, feature-keycloak-google-claim-attribute-mapping, feature-keycloak-account-linking-sub-vs-email]
|
||||
tags: [keycloak-patterns, p1b-edge-google-federation, idp-brokering, google-oidc, oidc, official-doc]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-25
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Google Identity — OpenID Connect (OIDC) 공식 문서
|
||||
|
||||
> Layer: `raw/official-docs/` — Google Identity Platform "OpenID Connect" 페이지 verbatim.
|
||||
> P1B 토큰 교환 8단계 sequence 의 5–7번 단계 (Keycloak ↔ Google `authorize`/`token` endpoint) + ID token claim (`sub`, `email`) 매핑 정책의 1차 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-keycloak-patterns]] | keycloak-patterns root — Google 이 외부 IdP 로 federation 될 때 OIDC 가 사용된다는 사실 |
|
||||
| [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]] | P1B Edge + Google federation sequence 의 step 5–7 (Keycloak → Google `authorize` → callback `code` → `/token` 교환) 의 정확한 endpoint URL 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]] | Keycloak 의 Google IdP client 등록 시 Discovery document (`https://accounts.google.com/.well-known/openid-configuration`) 사용 결정 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]] | Google ID token claim → Keycloak user attribute 매핑 시 `sub` 가 영구 식별자 + `email` 은 unique identifier 로 사용 금지의 1차 근거 |
|
||||
| [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]] | "email = primary identifier 로 사용 금지" 공식 경고 → Keycloak mapper 가 `sub` 기반 매칭으로 전환하는 결정 근거 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
P1B 에서 Keycloak 이 외부 IdP 로 등록하는 대상이 Google. Keycloak 이 redirect 하는 Google `authorize` endpoint, code → token 교환에 쓰는 `/token` endpoint, 그리고 Keycloak 이 받아 매핑할 ID token claim (`sub`, `email`) 을 **공식 기준**으로 확보. 토큰 교환 sequence 의 5–7번 단계의 1차 근거. `sub` 가 영구 식별자라는 명시적 공식 경고가 `feature-keycloak-account-linking-sub-vs-email` 의 결정 근거.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://developers.google.com/identity/openid-connect/openid-connect
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Google Identity Platform
|
||||
- 발행일: rolling docs
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Send an authentication request to Google] "The following discussion assumes the base URI is `https://accounts.google.com/o/oauth2/v2/auth`."
|
||||
|
||||
> [§Exchange `code` for access token and ID token] "The `POST` request is sent to the token endpoint, which you should retrieve from the Discovery document using the `token_endpoint` metadata value. The following discussion assumes the endpoint is `https://oauth2.googleapis.com/token`."
|
||||
|
||||
> [§An ID token's payload] "When implementing your account management system, you **shouldn't** use the `email` field in the ID token as a unique identifier for a user. Always use the `sub` field as it is unique to a Google Account even if the user changes their email address."
|
||||
|
||||
> [§Google ID Tokens — Claims Table] "The user's email address. Provided only if you included the `email` scope in your request."
|
||||
|
||||
> [§The Discovery document] "The Discovery document for Google's OpenID Connect service may be retrieved from: `https://accounts.google.com/.well-known/openid-configuration`"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GOIDC-C1 | Google 의 OIDC authorization endpoint 의 base URI 는 `https://accounts.google.com/o/oauth2/v2/auth` | [§Send an authentication request to Google] "The following discussion assumes the base URI is `https://accounts.google.com/o/oauth2/v2/auth`." | `official-vendor-doc` | Google Identity Platform OIDC integration | 이 URL 이 항상 고정이라는 뜻 아님 — 공식 권장은 Discovery document 의 `authorization_endpoint` 값 사용 |
|
||||
| GOIDC-C2 | Google 의 OIDC token endpoint 는 `https://oauth2.googleapis.com/token`; POST 요청으로 code 교환 수행 | [§Exchange `code` for access token and ID token] "The `POST` request is sent to the token endpoint, which you should retrieve from the Discovery document using the `token_endpoint` metadata value. The following discussion assumes the endpoint is `https://oauth2.googleapis.com/token`." | `official-vendor-doc` | Google OIDC code flow | refresh token 의 정확한 lifetime / rotation 정책은 본 인용 범위 밖 |
|
||||
| GOIDC-C3 | ID token 의 `sub` 가 영구 식별자; `email` 을 unique identifier 로 사용 금지 (**공식 권고**) — 이유: 사용자가 email 변경해도 `sub` 는 동일 | [§An ID token's payload] "When implementing your account management system, you **shouldn't** use the `email` field in the ID token as a unique identifier for a user. Always use the `sub` field as it is unique to a Google Account even if the user changes their email address." | `official-vendor-doc` | Google ID token 사용자 매핑 정책 | `sub` 가 cross-IdP 에서도 unique 라는 뜻 아님 — Google 계정 내에서만 unique |
|
||||
| GOIDC-C4 | `email` claim 은 `email` scope 를 request 에 포함했을 때에만 제공 | [§Google ID Tokens — Claims Table] "The user's email address. Provided only if you included the `email` scope in your request." | `official-vendor-doc` | Google OIDC scope 요청 정책 | `email_verified` claim 의 의미/제공 조건은 본 인용 범위 밖 (claims table 의 별도 행) |
|
||||
| GOIDC-C5 | Google OIDC Discovery document 의 정확한 URL 은 `https://accounts.google.com/.well-known/openid-configuration` | [§The Discovery document] "The Discovery document for Google's OpenID Connect service may be retrieved from: `https://accounts.google.com/.well-known/openid-configuration`" | `official-vendor-doc` | Google OIDC discovery 사용 (Keycloak IdP "Use discovery endpoint" 설정 포함) | Discovery document 의 모든 metadata 키의 완전한 목록은 본 인용 범위 밖 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `GOIDC-C1`/`C2`: Google authorize/token endpoint 의 정확한 URL (P1B 8단계 sequence 의 step 5/7 endpoint 확정)
|
||||
- `GOIDC-C3`: `sub` 가 영구 식별자 + `email` 을 unique identifier 로 쓰지 말라는 **공식 경고** (P1B account linking 결정 근거)
|
||||
- `GOIDC-C4`: `email` claim 은 `email` scope 가 있어야 받음 (Keycloak Google IdP scope 설정의 근거)
|
||||
- `GOIDC-C5`: Discovery document URL (Keycloak "Use discovery endpoint" 한 줄 설정 근거)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `email_verified=false` 인 Google 계정의 처리 방침 (별도 claims table 항목 / IdP 측 verification 정책)
|
||||
- Google refresh token rotation / TTL 의 정확한 값
|
||||
- Keycloak 의 First Login Flow 가 `sub` 매칭을 자동 수행한다는 뜻 — Keycloak side 의 별도 mapper 설정 필요 (`keycloak-identity-provider-mappers` 참조)
|
||||
- PKCE 강제 여부 (Google OAuth 2.0 별도 페이지)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- Keycloak Google IdP 설정에서 Discovery URL 입력 위치 (Admin Console > Identity Providers > Google > Use discovery endpoint)
|
||||
- Keycloak mapper: Google `sub` claim → Keycloak `username` 또는 `federated identity` 매핑의 정확한 mapper type (Attribute Importer / Username Template Importer)
|
||||
- Authorized redirect URI 등록 시 Keycloak callback 경로 (`/realms/<realm>/broker/google/endpoint`) 의 정확한 형태
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. P1B 결정 컨텍스트 해석.
|
||||
|
||||
- **P1B 토큰 흐름 5-7 단계 근거**:
|
||||
- 5: Keycloak → Google `authorize` (`https://accounts.google.com/o/oauth2/v2/auth`) — `GOIDC-C1`.
|
||||
- 6: 사용자 Google 로그인 → Google → Keycloak callback (`code` 전달).
|
||||
- 7: Keycloak → Google `/token` (`https://oauth2.googleapis.com/token`), Google ID token + access token 수신 — `GOIDC-C2`.
|
||||
- **사용자 매핑 시 주의**: 공식 문서가 명시한 대로 (`GOIDC-C3`) **`email` 을 primary identifier 로 사용 금지**. `sub` 가 영구 식별자. Keycloak 의 First Login Flow 에서 email match 로 기존 계정에 자동 연결하는 것은 보안 위험 (Keycloak 공식 문서도 동일 경고 → `keycloak-first-login-flow.md` 의 `KC-FLF-C2`).
|
||||
- **Discovery 활용**: Keycloak Google IdP 설정은 보통 Discovery URL 한 줄로 endpoint 일괄 가져옴 (`GOIDC-C5`). 수동 URL 입력 시에는 `C1`/`C2` 의 두 endpoint 사용.
|
||||
- **scope**: Keycloak default = `openid profile email`. ID token 의 `email` claim 받으려면 `email` scope 필수 (`GOIDC-C4`).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/google-oidc-discovery-spec]]
|
||||
- [[raw/official-docs/google-oauth2-redirect-uri-validation-official]]
|
||||
- [[raw/official-docs/keycloak-first-login-flow]] (security warning 동일 주제 — email 자동 link 의 위험)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-keycloak-patterns]] (root)
|
||||
- [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]] (P1B)
|
||||
- [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]]
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Google SRE Workbook — On-Call & Monitoring (official-reference)
|
||||
source_type: official-doc
|
||||
url: https://sre.google/workbook/on-call/
|
||||
url_secondary: https://sre.google/workbook/monitoring/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [sre, on-call, monitoring, runbook, playbook, alerting, operational-runbook-contract]
|
||||
related_projects: []
|
||||
related_branches: [feature-operational-runbook-contract]
|
||||
created: 2026-05-27
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# Google SRE Workbook — On-Call & Monitoring (공식 참조)
|
||||
|
||||
> Layer: `raw/official-docs/` — Google SRE Workbook 의 **원문 발췌·출처 기록**.
|
||||
> Strength 분류: `official-reference` — Google SRE Workbook 은 community consensus 형성 문헌(O'Reilly 출판 + Google 내부 사례 기반)이며, **특정 vendor product 의 공식 문서가 아니다**. Spring/Keycloak/AWS 같은 product-doc 과 동급으로 인용하지 말 것.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/` 에 source-summary 로 별도 작성. 원본은 raw 영구 보관.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-operational-runbook-contract]] | **D2 (runbook 을 operational artifact 로 명문화)** 의 근거 — SRE 문헌에서 playbook 이 alert response 의 표준 컴포넌트로 정의됨. **D10 (error registry ↔ runbook coupling)** 의 근거 — "alert 마다 대응되는 playbook entry 가 있어야 한다" 원칙. |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
`feature-operational-runbook-contract` 는 알람 발생 시 운영자가 따라야 할 표준 절차(runbook) 와 에러 코드 레지스트리의 coupling 규칙을 정의한다. SRE Workbook 의 On-Call 챕터는 **playbook 이 alert 의 표준 동반 자산** 이라는 입장을 명문화하며, 운영자 부하·MTTR·human-error 감소가 그 정당성이라고 진술한다. 본 raw 는 D2/D10 결정의 외부 근거로 보관.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL (메인 — On-Call 챕터): https://sre.google/workbook/on-call/
|
||||
- 원본 URL (보조 — Monitoring 챕터): https://sre.google/workbook/monitoring/
|
||||
- 아카이브 URL: (미수집 — 추후 archive.org 스냅샷 추가)
|
||||
- 저자 / 조직: Google SRE / O'Reilly Media — *Site Reliability Workbook* (Beyer, Murphy, Rensin, et al.)
|
||||
- 발행일: 2018 (서적 초판) / web 판본은 rolling
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§On-Call, opening definition] "Being on-call means being available during a set period of time, and being ready to respond to production incidents during that time with appropriate urgency."
|
||||
|
||||
> [§On-Call, Recap] "At Google, the overall goal of being on-call is to provide coverage for critical services, while making sure that we never achieve reliability at the expense of an on-call engineer's health."
|
||||
|
||||
> [§On-Call, Recap] "We target a maximum of two incidents per on-call shift, to ensure adequate time for follow-up."
|
||||
|
||||
> [§On-Call, Forming a New Team] "Playbooks contain high-level instructions on how to respond to automated alerts. They explain the severity and impact of the alert, and include debugging suggestions and possible actions to take to mitigate impact and fully resolve the alert."
|
||||
|
||||
> [§On-Call, Forming a New Team] "In SRE, whenever an alert is created, a corresponding playbook entry is usually created. These guides reduce stress, the mean time to repair (MTTR), and the risk of human error."
|
||||
|
||||
> [§On-Call, Anatomy of Pager Load — Alerting] "Just like new code, new alerts should be thoroughly and thoughtfully reviewed. Each alert should have a corresponding playbook entry."
|
||||
|
||||
> [§On-Call, Identification delay] "Ensure pages link to relevant monitoring consoles, and that consoles highlight where the system is operating out of specification."
|
||||
|
||||
> [§Monitoring, Dependencies] "When choosing the metrics to graph, keep the four golden signals in mind."
|
||||
|
||||
> [§Monitoring, Alert classification] "It's helpful to be able to classify alerts: multiple categories of alerts allow for proportional responses. The ability to set different severity levels for different alerts is also useful: you might file a ticket to investigate a low rate of errors that lasts more than an hour, while a 100% error rate is an emergency that deserves immediate response."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 자료가 직접 말하는 것만 claim 으로 분리. 내 프로젝트에 적용한 결론은 여기 쓰지 않음.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SRE-WB-OC-C1 | On-call 의 정의는 "지정된 시간 동안 production incident 에 적절한 긴급도로 응답할 수 있는 상태" | [§On-Call] "Being on-call means being available during a set period of time, and being ready to respond to production incidents during that time with appropriate urgency." | `official-reference` | SRE 모델을 채택하는 조직의 on-call 정의 | 모든 조직이 동일 on-call 정의를 사용해야 한다는 뜻은 아님 (DevOps/NOC 모델은 별도) |
|
||||
| SRE-WB-OC-C2 | Google 의 on-call 목표는 "critical service coverage" 와 "engineer health" 양립이며 신뢰성을 엔지니어 건강과 맞바꾸지 않는다 | [§On-Call, Recap] "we never achieve reliability at the expense of an on-call engineer's health." | `official-reference` | SRE 문화를 채택하는 조직의 on-call 정책 설계 | Google 외 조직에서도 동일 목표가 실현 가능하다는 뜻은 아님 (인원 규모·서비스 critical 도 차이) |
|
||||
| SRE-WB-OC-C3 | Google SRE 는 shift 당 incident 2건을 상한으로 목표 (follow-up 시간 확보 목적) | [§On-Call, Recap] "We target a maximum of two incidents per on-call shift, to ensure adequate time for follow-up." | `official-reference` | Google 의 on-call rotation 운영 | 다른 조직의 "적정 incident 수" 가 동일해야 한다는 뜻은 아님 — Google 내부 target 의 보고 |
|
||||
| SRE-WB-OC-C4 | Playbook 은 자동 alert 에 대한 high-level 대응 지침이며 severity/impact/debugging suggestion/mitigation action 을 포함한다 | [§On-Call, Forming a New Team] "Playbooks contain high-level instructions on how to respond to automated alerts. They explain the severity and impact of the alert, and include debugging suggestions and possible actions to take to mitigate impact and fully resolve the alert." | `official-reference` | runbook/playbook 의 구성 요소 정의 | 모든 조직이 playbook 에 동일 4요소를 포함해야 한다는 표준은 아님 (SRE 문헌의 권고) |
|
||||
| SRE-WB-OC-C5 | SRE 에서는 **alert 생성 시 대응 playbook entry 도 함께 생성** 하는 것이 일반적이며, 이는 stress·MTTR·human error 를 감소시킨다 | [§On-Call, Forming a New Team] "whenever an alert is created, a corresponding playbook entry is usually created. These guides reduce stress, the mean time to repair (MTTR), and the risk of human error." | `official-reference` | alert ↔ runbook 1:1 coupling 원칙의 근거 | "1:1 coupling 이 모든 환경에서 효율적" 이라는 정량 증명은 본 문헌이 직접 제공하지 않음 (정성적 권고) |
|
||||
| SRE-WB-OC-C6 | 새 alert 는 신규 코드와 동일하게 review 되어야 하며, **각 alert 에는 대응되는 playbook entry 가 있어야 한다** | [§Anatomy of Pager Load — Alerting] "Just like new code, new alerts should be thoroughly and thoughtfully reviewed. Each alert should have a corresponding playbook entry." | `official-reference` | alert pipeline 의 governance / review 정책 | review 절차의 구체적 형식(PR/체크리스트 등) 까지는 본 인용이 규정하지 않음 |
|
||||
| SRE-WB-OC-C7 | Page (alert 통지) 는 관련 monitoring console 로 link 해야 하며, console 은 spec 이탈 지점을 강조해야 한다 | [§Identification delay] "Ensure pages link to relevant monitoring consoles, and that consoles highlight where the system is operating out of specification." | `official-reference` | alert 메시지 본문 설계 (link/context 포함) | "alert 메시지에 반드시 runbook URL 도 포함" 이라는 명시적 권고는 본 인용에 없음 (console link 권고만 직접 진술) |
|
||||
| SRE-WB-OC-C8 | 메트릭 선정 시 **four golden signals** 를 염두에 두어야 한다 (Latency/Traffic/Errors/Saturation — SRE Book 참조) | [§Monitoring] "When choosing the metrics to graph, keep the four golden signals in mind." | `official-reference` | 모니터링 대시보드 / 메트릭 선택 | 본 chapter 자체에는 4개 signal 의 정의는 없음 — SRE Book 의 hyperlink 참조 |
|
||||
| SRE-WB-OC-C9 | alert classification (severity level) 은 proportional response 를 가능하게 하며, 낮은 error rate 는 ticket, 100% error 는 즉시 emergency 로 분류 가능 | [§Monitoring] "It's helpful to be able to classify alerts: multiple categories of alerts allow for proportional responses…" | `official-reference` | alert severity 정책 설계 | severity level 의 표준 개수(예: P1/P2/P3) 가 정해진다는 뜻은 아님 — 분류 자체의 유용성을 진술 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `SRE-WB-OC-C4`, `C5`, `C6`: alert 와 playbook 의 1:1 coupling 이 SRE 문헌상 권고됨 (D10 의 외부 근거로 인용 가능)
|
||||
- `SRE-WB-OC-C7`: alert 메시지에 monitoring console link 를 포함하는 패턴이 공식 권고됨
|
||||
- `SRE-WB-OC-C8`, `C9`: 메트릭 선정·alert severity 분류의 기본 원칙
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- **runbook 의 구체적 markdown 템플릿 / 필드 구조** (SRE 문헌은 "playbook 에 무엇이 들어가야 하는가" 까지 진술하나, 파일 포맷·필드 schema 는 규정하지 않음)
|
||||
- **error code ↔ runbook 1:1 mapping 이 alert ↔ playbook 1:1 mapping 과 동치라는 점** — error registry 라는 개념 자체는 SRE Workbook 에 직접 등장하지 않음. D10 은 SRE 의 alert-playbook coupling 원칙을 **error-runbook coupling 으로 확장 적용** 한 것이며, 그 확장은 본 raw 가 직접 보증하지 않는다 (UNSUPPORTED_EXTENSION 경계)
|
||||
- "alert 메시지에 runbook URL 을 포함하라" 는 직접 권고는 본 raw 의 인용 범위 내에 **없음** (`C7` 은 monitoring console link 까지만 명시). runbook URL 포함 권고는 별도 출처 필요
|
||||
- Google 의 "shift 당 incident 2건" target (`C3`) 이 다른 조직의 기준이 될 수 있다는 보장
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- `feature-operational-runbook-contract` 의 runbook 템플릿 필드 (예: `Symptoms`, `Diagnosis`, `Mitigation`, `Rollback`) 가 본 raw 의 `C4` ("severity/impact/debugging/mitigation") 와 매핑되는지 — 매핑 분석은 wiki/concepts 의 source-summary 에서 수행
|
||||
- error code 레지스트리 ↔ runbook 매핑(D10) 의 추가 외부 근거 — SRE 외 자료 (예: PagerDuty / Atlassian runbook 가이드) 보강 필요
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 본 raw 는 **두 chapter 를 묶어** 보관 — 운영상 on-call 과 monitoring 의 alerting 원칙이 D2/D10 결정에 동시 인용되기 때문. wiki 추출 시 두 source-summary 로 분리할지 단일 문서로 둘지는 추출 시점에 판단.
|
||||
- `C7` 의 "pages link to monitoring consoles" 는 D2 의 "runbook URL 을 alert 본문에 포함" 결정과 정확히 동일하지 않음 — alert → console 까지만 직접 보증, alert → runbook 은 `C5`/`C6` 의 "alert ↔ playbook coupling" 원칙으로 간접 뒷받침. wiki 옮길 때 이 간접성 명시 필수.
|
||||
- Spring/Keycloak/Caddy 문서와 동급으로 "공식 best practice" 라 인용하지 말 것. Strength = `official-reference` (community consensus), NOT `official-vendor-doc`.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 raw 자료: (현재 없음 — 추후 PagerDuty / Atlassian runbook 가이드 보강 시 추가)
|
||||
- 이 자료를 인용한 wiki 요약: (미작성)
|
||||
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-operational-runbook-contract]]
|
||||
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: ArchUnit — 공식 소개 페이지
|
||||
source_type: official-doc
|
||||
url: https://www.archunit.org/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
related_branches: [feature-contract-registry-governance, feature-test-taxonomy-fixture-contract]
|
||||
related_projects: [ca-tmpl]
|
||||
tags: [architecture-test, governance, fitness-function, archunit, ca-skeleton]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
---
|
||||
|
||||
# ArchUnit — 공식 소개 페이지
|
||||
|
||||
> Layer: `raw/official-docs/` — ArchUnit 공식 홈페이지 발췌. registry governance와 architecture test가 **annotation/scan 기반 fitness function**으로 작동할 때의 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-contract-registry-governance]] | Group G-G 대안 평가 — "ArchUnit annotations as registry" 대안의 능력/한계 평가 근거 (markdown SSOT 채택의 비교 기준) |
|
||||
| [[raw/branch-notes/feature-test-taxonomy-fixture-contract]] | ArchUnit 을 verifier (fitness function) 로 사용하는 결정 — contract test 분류 |
|
||||
|
||||
## 컨텍스트 / 왜 저장했는지
|
||||
|
||||
`feature-contract-registry-governance`의 ca-tmpl 대안 후보 중 **"ArchUnit annotations as registry"**가 있었다. 즉 registry를 markdown/YAML로 두는 대신 **@Capability("...")** 같은 annotation을 코드에 박고 ArchUnit으로 scan하는 모델이다. 그 대안의 가능성과 한계를 평가하려면 ArchUnit이 무엇을 검증할 수 있는지 원문이 필요.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://www.archunit.org/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: ArchUnit 프로젝트 (TNG Technology Consulting)
|
||||
- 발행 상태: 지속적으로 갱신 (최신 v1.4.2 / 2026-04 기준)
|
||||
- 마지막 확인일: 2026-05-27
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Homepage tagline] "A free, simple and extensible library for checking the architecture of your Java code using any plain Java unit test framework."
|
||||
|
||||
> [§Capabilities] ArchUnit can "check dependencies between packages and classes, layers and slices, check for cyclic dependencies and more."
|
||||
|
||||
> [§How it works] ArchUnit operates by "analyzing given Java bytecode, importing all classes into a Java code structure," enabling architectural validation within existing test infrastructures.
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| AU-OFF-C1 | ArchUnit 은 plain Java unit test framework 안에서 작동하는 free·simple·extensible library 로, **Java 코드의 architecture 를 검사**하는 목적 | [§Homepage tagline] "A free, simple and extensible library for checking the architecture of your Java code using any plain Java unit test framework." | `official-vendor-doc` | JVM 기반 코드베이스 | non-JVM 언어 (Python, Go, Node.js) 에서 동일 검사가 가능하다는 뜻은 아님 (.NET 포트는 별도) |
|
||||
| AU-OFF-C2 | ArchUnit 의 검사 범위는 **package/class 간 dependency, layer/slice 정의, cyclic dependency 검출 등** | [§Capabilities] "check dependencies between packages and classes, layers and slices, check for cyclic dependencies and more." | `official-vendor-doc` | 정적 (bytecode 기반) 아키텍처 검사 | runtime 상태 (예: 실제 호출 그래프, profile별 활성 bean) 를 검증한다는 뜻은 아님 |
|
||||
| AU-OFF-C3 | ArchUnit 의 작동 메커니즘은 **Java bytecode 를 분석**하여 모든 class 를 Java code structure 로 import 하는 방식 | [§How it works] "analyzing given Java bytecode, importing all classes into a Java code structure" | `official-vendor-doc` | 컴파일된 .class 파일이 존재하는 환경 | source code 만으로 (compile 없이) 검사 가능하다는 뜻은 아님 — bytecode 가 입력 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `AU-OFF-C1`: ArchUnit 의 정체성·라이선스·통합 방식 (plain Java unit test framework)
|
||||
- `AU-OFF-C2`: ArchUnit 이 검사하는 항목의 카테고리 (package/class dependency, layer/slice, cyclic)
|
||||
- `AU-OFF-C3`: bytecode 분석이 작동 메커니즘이라는 사실
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- ArchUnit annotation 을 **도메인 contract registry SSOT** 로 사용하는 것이 공식 권장 패턴이라는 명제 (Homepage 에서 그러한 use case 미언급)
|
||||
- registry 의 필수 column (default, allowed_values, compatibility_impact) 을 annotation 으로 표현 가능하다는 명제
|
||||
- operations/non-code 영역에서 ArchUnit 으로 registry 를 다룰 수 있다는 명제
|
||||
- "annotation = SSOT" 모델이 "markdown SSOT" 보다 우월하다는 명제
|
||||
- **내 프로젝트(ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ArchUnit 의 `LayeredArchitecture`, `noClasses().that().resideIn(...)` 같은 구체적 DSL 시맨틱 (별도 User Guide 인용 필요 — [[raw/official-docs/archunit-annotation-as-registry-evaluation]] 참고)
|
||||
- ArchUnit annotation 접근 API (`getAnnotationOfType`, `JavaAnnotation.get(...)`) 의 정확한 시그니처 (별도 User Guide 인용 필요 — [[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]] 참고)
|
||||
|
||||
## 메모 / Notes (내 프로젝트 해석 — 미검증)
|
||||
|
||||
> 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석.
|
||||
|
||||
- ArchUnit annotation을 registry로 쓰는 대안의 약점:
|
||||
- registry **공통 필수 column**(default, allowed_values, compatibility_impact 등)을 annotation 하나로 다 표현 못 함.
|
||||
- external platform mapping row를 코드 없이 표현 못 함.
|
||||
- operations(non-code)에서 registry를 다루기 어렵다.
|
||||
- 강점: 코드와 registry가 항상 동기화. drift 불가능.
|
||||
- ca-tmpl 결정 = markdown SSOT + YAML registry + ArchUnit은 **scan/enforcement layer**로 사용. 즉 ArchUnit은 registry의 owner가 아니라 verifier.
|
||||
- 본 skeleton의 contract test 결정에 ArchUnit이 다수 등장하는 이유 (예: `noClasses().that().resideIn("..contract..").should().dependOnClassesThat().resideInAPackage("..features.(?!sample).+..")`)
|
||||
|
||||
> **주의 (이전 버전에 있던 한국어 인용 제거됨)**: 이전 버전에 있던 "Java 바이트코드를 분석하여 정의된 규칙 위반을 자동으로 감지하므로, 아키텍처 의도를 코드 수준에서 강제하는 fitness function으로 작동한다" 문장은 **homepage 원문에서 verbatim 으로 확인되지 않음** (해석 가능한 paraphrase 였음). 본 마이그레이션에서 verbatim 원문 인용만 보존하기 위해 메모 영역으로 이동·표기. fitness function 명시 인용은 [[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]] 의 *Building Evolutionary Architectures* 인용을 참조.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/archunit-annotation-as-registry-evaluation]] (annotation-as-registry 대안 평가)
|
||||
- [[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]] (Layer 2 fitness function 정적 검사 가능 범위 평가)
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-contract-registry-governance]]
|
||||
- [[raw/branch-notes/feature-test-taxonomy-fixture-contract]]
|
||||
- canonical contract 섹션:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#21. Contract Registry]]
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract#12. Test Contract]]
|
||||
- 대안 그룹: **Group G-G — Skeleton Governance** (registry/test-taxonomy 양쪽)
|
||||
- 본 source의 위치: 대안 2 — ArchUnit annotations as registry (rejected; verifier로만 사용)
|
||||
- 인용하는 wiki: (미작성)
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: "official-doc / Gradle Java Library Plugin — API vs Implementation Separation"
|
||||
source_type: official-doc
|
||||
url: https://docs.gradle.org/current/userguide/java_library_plugin.html
|
||||
archive_url:
|
||||
related_branches: [feature-skeleton-package-blueprint-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, ci-cd, gradle, api-vs-implementation]
|
||||
status: raw
|
||||
confidence: high
|
||||
created: 2026-05-28
|
||||
last_reviewed: 2026-05-28
|
||||
---
|
||||
|
||||
# official-doc / Gradle Java Library Plugin — API vs Implementation Separation
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> Gradle 공식 User Guide의 Java Library Plugin 섹션. `api` vs `implementation` 구성(configuration) 분리 정책의 공식 근거.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-skeleton-package-blueprint-contract]] | ca-tmpl 8개 Gradle 모듈에서 `api` vs `implementation` dependency 선언 정책의 공식 근거 — 어떤 module이 다른 module type을 공개 ABI로 노출하는지(`api`) vs 내부 구현에만 사용하는지(`implementation`)를 결정하는 기준 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.gradle.org/current/userguide/java_library_plugin.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Gradle Inc. (공식 User Guide)
|
||||
- 발행일: (현재 버전 유지 — "current" URL)
|
||||
- 마지막 확인일: 2026-05-28
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
ca-tmpl Clean Architecture 스켈레톤은 8개 Gradle 모듈(`app-bootstrap`, `domain-core`, `application-core`, `adapter-web`, `adapter-persistence`, `adapter-outbound`, `shared-contract`, `sample-ticket`)을 정의하고 있으나, 모듈 간 dependency 선언 시 `api`와 `implementation` 중 어느 것을 사용해야 하는지 정책이 미정이었다. 이 문서는 그 결정의 공식 Gradle 근거를 제공한다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§ API and implementation separation] "Dependencies appearing in the `api` configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers."
|
||||
|
||||
> [§ API and implementation separation] "Dependencies found in the `implementation` configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath."
|
||||
|
||||
> [§ API and implementation separation] "Prefer the `implementation` configuration over `api` when possible"
|
||||
|
||||
> [§ API and implementation separation / ABI definition] "An API dependency is one that contains at least one type that is exposed in the library binary interface, often referred to as its ABI (Application Binary Interface)."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| GRADLE-JAVALIB-C1 | `api` configuration에 선언된 dependency는 라이브러리 소비자의 compile classpath에 전이적으로(transitively) 노출된다 | "Dependencies appearing in the `api` configurations will be transitively exposed to consumers of the library, and as such will appear on the compile classpath of consumers." | `official-vendor-doc` | Gradle Java Library Plugin을 사용하는 모든 프로젝트 | `java` plugin(non-library)의 동작, 런타임 classpath 동작 |
|
||||
| GRADLE-JAVALIB-C2 | `implementation` configuration에 선언된 dependency는 소비자 compile classpath로 누출(leak)되지 않는다 | "Dependencies found in the `implementation` configuration will, on the other hand, not be exposed to consumers, and therefore not leak into the consumers' compile classpath." | `official-vendor-doc` | Gradle Java Library Plugin을 사용하는 모든 프로젝트 | runtime classpath에서의 동작, Spring Boot executable jar 패키징 동작 |
|
||||
| GRADLE-JAVALIB-C3 | Gradle 공식 문서는 가능한 한 `api` 대신 `implementation`을 사용하도록 권고한다 | "Prefer the `implementation` configuration over `api` when possible" | `official-vendor-doc` | Gradle Java Library Plugin을 사용하는 모든 프로젝트 | 언제 `api`가 반드시 필요한지에 대한 완전한 기준은 포함하지 않음 |
|
||||
| GRADLE-JAVALIB-C4 | API dependency의 정의는 library binary interface(ABI)에 노출되는 type을 하나 이상 포함하는 dependency이다 | "An API dependency is one that contains at least one type that is exposed in the library binary interface, often referred to as its ABI (Application Binary Interface)." | `official-vendor-doc` | Gradle Java Library Plugin의 `api` configuration 사용 판단 | 어떤 type이 ABI에 노출되는지의 상세 기준(superclass, public method parameter 등)은 이 단일 인용으로 완결되지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `GRADLE-JAVALIB-C1`: `api` 선언 시 소비자 compile classpath 전이적 노출 — multi-module 프로젝트에서 module A가 module B를 `api`로 선언하면 B의 dependency가 A의 소비자에게 전이됨
|
||||
- `GRADLE-JAVALIB-C2`: `implementation` 선언 시 소비자 compile classpath 비노출 — module 간 의도치 않은 transitive dependency 방지
|
||||
- `GRADLE-JAVALIB-C3`: `implementation` 우선 사용 권고 — 공식적인 기본 선택 지침
|
||||
- `GRADLE-JAVALIB-C4`: ABI 노출 여부가 `api` 사용의 판단 기준
|
||||
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- ca-tmpl 8개 모듈 각각에서 `api`를 써야 하는 구체적 경우 (예: `domain-core`의 type이 `application-core`의 public port에 노출되는지 여부) — 이는 ca-tmpl 자체 설계 결정
|
||||
- Spring Boot executable jar (`bootJar`) 환경에서 `implementation`의 런타임 포함 여부 — bootJar는 별도 규칙
|
||||
- `testImplementation`, `runtimeOnly` 등 다른 configuration의 동작
|
||||
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `shared-contract`를 `application-core`, `adapter-*`가 참조할 때 `api`로 선언해야 하는지 `implementation`으로 선언해도 되는지 — `shared-contract`의 type이 각 module의 public API에 노출되는지 여부로 결정
|
||||
- `domain-core`를 `application-core`가 참조할 때 `api` vs `implementation` — `application-core`의 port interface 반환 타입에 `domain-core` type이 포함되면 `api` 필요
|
||||
- multi-module에서 `app-bootstrap`이 모든 module을 `implementation`으로 선언 가능한지 — bootstrap은 소비자가 없으므로 `implementation` 사용이 일반적
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- `api` vs `implementation` 정책은 module 간 의존 방향(Module Dependency Rule)과 별개의 결정이다. 의존 방향은 ArchUnit/Gradle dependency 규칙으로 강제하고, `api` vs `implementation`은 각 의존 선언 시 ABI 노출 여부로 판단한다.
|
||||
- ca-tmpl 8개 모듈에서 가장 자주 `api`가 필요한 경우는 port interface의 파라미터/반환 타입에 다른 module의 type이 등장할 때이다 (미검증 추론 — `Claims Extracted` 아님).
|
||||
- 추가로 봐야 할 동일 출처 페이지: Gradle User Guide의 "Java Library Plugin — The java-library plugin configurations" 섹션 (configuration hierarchy 전체), "Building Java projects with Gradle" 섹션.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 official-doc: [[raw/official-docs/supply-chain-gradle-vs-maven-dependency-locking]] — Gradle dependency locking 관련
|
||||
- 이 자료를 활용할 wiki 요약: `wiki/concepts/gradle-api-vs-implementation` (생성 예정, `/ingest` 후)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user