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

This commit is contained in:
DongHyeonka
2026-07-25 12:53:13 +09:00
parent 6c53ded9cb
commit d71669eb59
2329 changed files with 138239 additions and 172816 deletions
@@ -1 +0,0 @@
../../vault/10-projects/ca-skeleton-frontend-operational-contract/project-notes/ca-skeleton-frontend-operational-contract.md
File diff suppressed because it is too large Load Diff
@@ -1 +0,0 @@
../../vault/10-projects/ca-skeleton-operational-contract/project-notes/ca-skeleton-operational-contract.md
File diff suppressed because it is too large Load Diff
@@ -1 +0,0 @@
../../vault/10-projects/invest-money-flow-system/project-notes/invest-money-flow-system.md
@@ -0,0 +1,251 @@
---
title: 자금흐름 관측 시스템 (Money-Flow Observation System)
source_type: project-note
status: draft
confidence: low
tags: [project-note, invest, personal-invest, finance]
related_projects: []
last_reviewed: 2026-06-08
diagrams: []
architecture_review: 2026-06-08
status_label: active
project_revision: 1
semantic_surface_exclusions:
- artifact-registry|legacy hub has no project-local Artifact Registry; harness/source/typed-contracts.json is authoritative until migration
- contract-gate-registry|legacy hub has no project-local Contract/Gate Registry; harness/source/typed-contracts.json is authoritative until migration
- flow-stage-registry|legacy hub has no project-local Flow/Stage Registry; harness/source/typed-contracts.json is authoritative until migration
---
# 자금흐름 관측 시스템 (Money-Flow Observation System)
> Layer: `raw/project-notes/` (primary, hub) → 검증된 사실은 `wiki/invest-concepts/`·`wiki/invest-strategy/` 로 추출.
> 본 문서는 **개인 투자 "눈 기르기" 프로젝트의 최상위 hub**. 전체 돈의 흐름을 *분야 → 대장주 → 추종주 → 분야간 연관* 으로 체계적으로 관측하기 위한 마스터 설계. 모든 조사(research)·전략·계획·개념 카드가 본 문서로 upward link.
> ⚠️ 면허 자문 아님 — [[wiki/invest-strategy/strategy]] §고지.
## 1. 프로젝트 개요
- **한 줄 요약**: 전체 주식시장의 *돈의 흐름*을 **거시 자산군 → 산업 섹터/테마 → 대장주 → 추종주** 4층으로 쪼개고, *분야끼리의 상승·하락 연관관계*까지 매일 관측해, "어디서 돈이 빠져 어디로 가는지" 읽는 눈을 데이터로 기른다.
- **기간**: 2026-06-08 ~ in-progress (장기 운영형)
- **현재 상태**: `active`
- **나의 역할 / Role**: 운영자 겸 학습자 (개인 투자 + 시장 관측 훈련)
- **저장소 / Repo**: 해당 없음 (이 wiki 자체가 시스템)
## 2. 문제 정의
### 2.1 현재 상태의 문제
- 문제 1: **분야별로 하나씩 카드를 만드는 piecemeal 방식**이라, 전체 그림(어떤 큰 분야들이 있고 어떻게 엮이는지)이 한눈에 안 보인다.
- 문제 2: **대장주 → 추종주 서열**이 체계로 없다. "엔비디아 뜨면 하이닉스·소부장이 따라오나?"를 매일 채점할 틀이 없다.
- 문제 3: **분야간 연관(로테이션)**이 없다. "달러↑면 어디서 돈이 빠지고 어디로 가나", "금리↑면 성장주 빠지고 가치/방산으로 가나" 같은 *상승·하락 연쇄*를 추적할 구조가 없다.
- 문제 4: 무엇이 *검증된 사실*이고 무엇이 *뇌피셜(가설)*인지 섞여서, 근거 없는 단정에 휘둘릴 위험.
### 2.2 왜 지금 해결해야 하는가
- 트리거: 100만원 실제 투자 시작([[wiki/invest-plan/active-plan]]) → 시장을 읽는 눈이 실전에서 필요.
- 비용: 눈 없이 매매하면 뉴스·테마에 휘둘려 패닉셀/FOMO (전략 ③ 위반).
- 기회: 매일 관측이 쌓이면 *어떤 연결이 진짜고 어떤 게 헛소문인지* 데이터로 분별 → 장기 의사결정 품질↑.
### 2.3 성공 기준
- 기준 1: **분야 지도(field-map)에 거시 자산군 + 한국 주요 섹터/테마가 카드로 등재**되고, 각 산업 섹터 카드가 *대장주 1~3 + 추종주 2~5* 를 가진다 (현재 13카드, 목표 20+).
- 기준 2: 각 카드의 모든 관계 행에 `[검증]/[가설]` 라벨이 있고, **`[검증]` 비율이 시간이 지나며 증가**한다 (관측→research 승급 추적).
- 기준 3: **매일 `/invest-daily` 가 "분야 관찰"로 대장주↔추종주 동조 + 분야간 연관을 실측 대조**한다 (예측 vs 실측 채점 누적).
- 기준 4: 분야간 연관(로테이션) 가설이 **별도 카드/섹션으로 명시**되고 검증 대상이 된다.
<!-- section-id: architecture-components -->
## 3. 시스템 아키텍처
> 소프트웨어 컴포넌트가 아니라 *문서 레이어 + 관측 루프*가 아키텍처다. 4층 관측 모델 + 검증 파이프라인.
### 3.1 4층 관측 모델 + 검증 루프 (Mermaid)
```mermaid
flowchart TD
subgraph L0["거시 자산군 (Macro)"]
DOL[달러] ; RATE[미 10Y 금리] ; OIL[원유] ; GOLD[금] ; USEQ[미국주식] ; BTC[비트코인]
end
subgraph L1["산업 섹터/테마 (Sector)"]
SEMI[반도체] ; BAT[2차전지] ; DEF[방산] ; SHIP[조선] ; BIO[바이오] ; NET[인터넷]
end
subgraph L2["대장주 (Leader)"]
LD["섹터별 선행 종목<br/>예: 엔비디아·SK하이닉스"]
end
subgraph L3["추종주 (Follower)"]
FL["대장주 따라가는 소형주<br/>예: 한미반도체·HPSP"]
end
L0 -->|"인과/상관 (엣지)"| L1
L1 -->|대장주 견인| L2
L2 -->|동조 낙수| L3
L0 -. "분야간 로테이션<br/>(돈이 빠져 옮겨감)" .-> L0
OBS["매일 /invest-daily<br/>예측 vs 실측 채점"] --> VER["반복 패턴<br/>/invest-research 검증"]
VER --> ING["/invest-ingest<br/>[가설]→[검증] 승급"]
ING -.->|카드 강화| L1
```
> 핵심: 위 4층(L0~L3)이 *지식*이고, 아래 OBS→VER→ING 가 *그걸 매일 단련하는 루프*. 카드의 연결은 처음엔 `[가설]`, 관측·검증으로 `[검증]` 승급.
### 3.2 컴포넌트(문서 레이어) 책임 분담
| 레이어 | 문서 | 역할 |
|---|---|---|
| 지도 허브 | [[wiki/invest-concepts/field-map]] | 전체 분야(노드) 목차 + 2층 분류 |
| 거시 카드 (L0) | [[wiki/invest-concepts/field-dollar]] 등 6장 | 자산군별 drivers·연결·관찰지표 |
| 섹터 카드 (L1+대장주/추종주 L2·L3) | [[wiki/invest-concepts/field-semiconductors]] 등 7장 | 섹터 drivers·연결 + **대장주/추종주 표** |
| 일일 관측 | `raw/invest-daily/` (`/invest-daily`) | 매일 예측 vs 실측 채점 (루프 엔진) |
| 심층 검증 | `raw/invest-research/` (`/invest-research`) | 의심 관계 3표 적대적 검증 |
| 승급 | `/invest-ingest` | 검증된 관계를 카드에 `[검증]` 반영 |
| 전략/계획 | [[wiki/invest-strategy/strategy]] · [[wiki/invest-plan/active-plan]] | 매매 규칙 + 활성 계획 |
| 원장 | [[raw/invest-ledger/ledger]] | 실제 매매 사실 기록 |
### 3.3 외부 의존성
| 외부 | 용도 | 장애 시 |
|---|---|---|
| deep-research(WebSearch/Fetch) | 수치·관계 검증 | 검증 보류 → `[가설]` 유지 |
| 시장 데이터(증권사·지수) | 일일 관측 입력 | 수동 입력 / 비움(추측 금지) |
<!-- section-id: runtime-flow -->
## 4. 핵심 시퀀스
<!-- section-id: sequence -->
### 4.1 일일 관측 루프 (대장주↔추종주 + 분야간 연관 채점)
**시나리오**: 매일 아침 `/invest-daily` 실행 시 카드 예측을 실측과 대조.
```mermaid
sequenceDiagram
autonumber
actor Me as 나
participant CMD as /invest-daily
participant CARD as 분야 카드(field-map)
participant MKT as 시장 데이터
participant NOTE as raw/invest-daily/오늘.md
Me->>CMD: 실행
CMD->>MKT: 거시·섹터·대장주 시세 조사(출처+시점)
CMD->>CARD: 오늘 움직인 카드의 "연결"·"대장주/추종주" 예측 읽기
CMD->>NOTE: 분야 관찰 표 채움
alt 예측대로 (확인)
CMD->>NOTE: "달러↑→금↓ 맞음 ✓ / 엔비디아↑→하이닉스 따라옴 ✓"
else 어긋남 (반증)
CMD->>NOTE: "예측과 다름 ✗ + 왜인지 가설 메모"
end
CMD-->>Me: 경로 + "반복 패턴은 /invest-research 로 검증"
Note over Me,CARD: 같은 패턴 반복 확인 → /invest-research → /invest-ingest → 카드 [가설]→[검증]
```
## 5. 데이터 모델
> 엔터티 5개 미만 — 글로만. 노드(분야 카드) ─wikilink엣지─ 노드. 카드 안에 대장주/추종주 행(종목). Obsidian 그래프 = 데이터 모델 시각화.
## 6. 기술 결정
> **Legacy reference (v1).** 아래 비교표는 rationale을 보존한다. project-wide 결정의 stable owner와 branch 상속 기준은 §6.1 registry다.
| 결정 영역 | 선택 | 검토한 대안 | 채택 이유 | 트레이드오프 | 근거 |
|---|---|---|---|---|---|
| 지식 구조 | 분야 카드(노드)+wikilink(엣지) | 단일 거대 문서 / 관계카드 | Obsidian 그래프=지도, 분야별 근거 추적 | 카드 수 관리 부담 | [[docs/superpowers/specs/2026-06-08-invest-field-map-design]] |
| 근거 규율 | 모든 관계 `[검증]/[가설]` 라벨 | 라벨 없음 | 뇌피셜·환각 차단(출처 없는 단정 금지) | 작성 번거로움 | [[wiki/invest-strategy/strategy]] §고지 |
| 종목 서열 | 대장주/추종주 표(섹터 카드) | 종목별 개별 카드 | 유지 부담↓, 동조 관측에 충분 | 종목 단위 깊이↓ | (본 노트 §2.2) |
| 검증 방식 | deep-research 3표 적대적 | 단일 패스 | 금융 수치 환각 방어 | 무거움(분당) | [[raw/invest-research/2026-06-08-broad-equity-etf-100man-candidates]] |
<!-- section-id: project-decisions -->
## 6.1 안정 결정 레지스트리
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|---|---:|---|---|---|---|---|
| `DEC-INVEST-MONEY-FLOW-SYSTEM-KNOWLEDGE-GRAPH-001` | 1 | `knowledge-graph` | 분야 카드를 node, wikilink를 edge로 사용한다 | `active` | [[raw/project-notes/invest-money-flow-system]] | §6 `지식 구조`; [[docs/superpowers/specs/2026-06-08-invest-field-map-design]] |
| `DEC-INVEST-MONEY-FLOW-SYSTEM-EVIDENCE-LABEL-001` | 1 | `evidence-label` | 모든 관계를 검증 또는 가설로 명시한다 | `active` | [[raw/project-notes/invest-money-flow-system]] | §6 `근거 규율`; [[wiki/invest-strategy/strategy]] §고지 |
| `DEC-INVEST-MONEY-FLOW-SYSTEM-EQUITY-HIERARCHY-001` | 1 | `equity-hierarchy` | 섹터 카드 안에 대장주·추종주 표를 둔다 | `active` | [[raw/project-notes/invest-money-flow-system]] | §6 `종목 서열`; §2.2 |
| `DEC-INVEST-MONEY-FLOW-SYSTEM-RESEARCH-VALIDATION-001` | 1 | `research-validation` | 관계 승급 검증은 deep-research 3표 적대적 절차를 사용한다 | `active` | [[raw/project-notes/invest-money-flow-system]] | §6 `검증 방식`; [[raw/invest-research/2026-06-08-broad-equity-etf-100man-candidates]] |
<!-- section-id: implementation-boundaries -->
## 7. 비기능 요구사항
- **지속가능성(가장 중요)**: 매일 *전 분야*가 아니라 *그날 움직인 분야*만 관측 → 부담 분산. 분야는 배치로 천천히 확장.
- **정확성**: 모든 수치 출처+조사시점. 미검증은 `[가설]` 명시.
- 성능/가용성/보안/DR: 해당 없음(개인 문서 시스템).
<!-- section-id: project-work-items -->
## 8.0 실행계획
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|---|---|---|---|---|---|
| `WI-INVEST-MONEY-FLOW-SYSTEM-001` | `feature-macro-asset-cards` | 거시 자산군 카드 6개가 생성되고 상호 link된다 | `DEC-INVEST-MONEY-FLOW-SYSTEM-KNOWLEDGE-GRAPH-001@1`, `DEC-INVEST-MONEY-FLOW-SYSTEM-EVIDENCE-LABEL-001@1` | - | `documented-only` |
| `WI-INVEST-MONEY-FLOW-SYSTEM-002` | `feature-kr-sector-leader-follower` | 한국 섹터 카드 6개가 각각 대장주·추종주 표를 가진다 | `DEC-INVEST-MONEY-FLOW-SYSTEM-EQUITY-HIERARCHY-001@1`, `DEC-INVEST-MONEY-FLOW-SYSTEM-EVIDENCE-LABEL-001@1` | `WI-INVEST-MONEY-FLOW-SYSTEM-001` | `documented-only` |
| `WI-INVEST-MONEY-FLOW-SYSTEM-003` | `feature-cross-field-rotation-map` | 분야간 상승·하락 연쇄가 최소 1개 관계로 명시되고 검증 대상화된다 | `DEC-INVEST-MONEY-FLOW-SYSTEM-KNOWLEDGE-GRAPH-001@1`, `DEC-INVEST-MONEY-FLOW-SYSTEM-EVIDENCE-LABEL-001@1` | `WI-INVEST-MONEY-FLOW-SYSTEM-002` | `planned` |
| `WI-INVEST-MONEY-FLOW-SYSTEM-004` | `feature-leader-follower-verification` | 섹터별 가설 중 최소 1개가 research evidence로 검증 승급된다 | `DEC-INVEST-MONEY-FLOW-SYSTEM-RESEARCH-VALIDATION-001@1`, `DEC-INVEST-MONEY-FLOW-SYSTEM-EQUITY-HIERARCHY-001@1` | `WI-INVEST-MONEY-FLOW-SYSTEM-002` | `planned` |
| `WI-INVEST-MONEY-FLOW-SYSTEM-005` | `feature-additional-kr-themes` | 원자력·자동차·엔터·로봇 후보가 card와 link 구조로 추가된다 | `DEC-INVEST-MONEY-FLOW-SYSTEM-KNOWLEDGE-GRAPH-001@1`, `DEC-INVEST-MONEY-FLOW-SYSTEM-EQUITY-HIERARCHY-001@1` | `WI-INVEST-MONEY-FLOW-SYSTEM-002` | `planned` |
| `WI-INVEST-MONEY-FLOW-SYSTEM-006` | `feature-accumulation-signal-rules` | 검증된 분야 최소 1개에 전략 연동 매수신호 규칙이 기록된다 | `DEC-INVEST-MONEY-FLOW-SYSTEM-RESEARCH-VALIDATION-001@1`, `DEC-INVEST-MONEY-FLOW-SYSTEM-EVIDENCE-LABEL-001@1` | `WI-INVEST-MONEY-FLOW-SYSTEM-004` | `planned` |
## 8.1 Legacy Branch 로드맵
> **Legacy reference (v1).** 기존 priority 표는 navigation용으로 보존하며 stable ID·decision pin·dependency의 SSOT는 위 Work Item Registry다.
> 소프트웨어 branch 대신 *분야 배치*로 분해. 각 배치가 "끝났다"의 측정가능 조건.
| 배치 slug | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-macro-asset-cards` | 거시 자산군 6카드 + 상호 연결 | P1 ✅ done | - |
| `feature-kr-sector-leader-follower` | 한국 섹터 6카드 + 각 대장주/추종주 표 | P2 ✅ done(가설) | macro |
| `feature-cross-field-rotation-map` | **분야간 연관(로테이션) 카드/섹션** — "달러↑→어디 빠지고 어디로" 같은 상승/하락 연쇄를 명시·검증 대상화 | P3 ⏳ | sector |
| `feature-leader-follower-verification` | 섹터별 `/invest-research`로 대장주/추종주 동조를 `[가설]``[검증]` 1개+ 승급 | P4 | sector |
| `feature-additional-kr-themes` | 원자력·자동차·엔터·로봇 등 추가 테마 카드 배치 | P5 | sector |
| `feature-accumulation-signal-rules` | 검증된 분야부터 "언제 모으기 시작" 매수신호 규칙(전략 연동) | P6 | verification |
> ⚠️ **다음 핵심 = P3 분야간 연관(로테이션)**. 지금 카드들은 *분야 안*(대장주↔추종주)과 *일부 분야간*(달러↔금) 만 있고, "돈이 A에서 빠져 B로 간다"는 *로테이션 지도*가 아직 약함. 이게 당신이 말한 "각 분야별 상승·하락 연관"의 핵심.
## 8. 묶음
<!-- GENERATED: branches:start -->
<!-- GENERATED: branches:end -->
> generated reverse view는 child branch의 v2 contract migration 후 채운다. 기존 수기 Cluster는 그 전까지 보존한다.
### 8.6 Derived/연결 문서
- 전략: [[wiki/invest-strategy/strategy]]
- 활성 계획: [[wiki/invest-plan/active-plan]]
- 분야 지도 허브: [[wiki/invest-concepts/field-map]]
- 거시 카드: [[wiki/invest-concepts/field-dollar]] · [[wiki/invest-concepts/field-us-rates]] · [[wiki/invest-concepts/field-oil]] · [[wiki/invest-concepts/field-gold]] · [[wiki/invest-concepts/field-us-equity]] · [[wiki/invest-concepts/field-bitcoin]]
- 섹터 카드: [[wiki/invest-concepts/field-semiconductors]] · [[wiki/invest-concepts/field-bigtech-ai]] · [[wiki/invest-concepts/field-secondary-battery]] · [[wiki/invest-concepts/field-defense]] · [[wiki/invest-concepts/field-shipbuilding]] · [[wiki/invest-concepts/field-bio-pharma]] · [[wiki/invest-concepts/field-internet-platform]]
- cluster 색인: [[wiki/invest/invest-hub]]
- 원장: [[raw/invest-ledger/ledger]]
### 8.2 근거 자료 (조사 증거)
- [[raw/invest-research/2026-06-08-broad-equity-etf-100man-candidates]] — 광범위 ETF 후보·MDD
- [[raw/invest-research/2026-06-08-isa-vs-general-account-no-income]] — 무소득 ISA vs 일반계좌
- [[raw/invest-research/2026-06-08-korean-broad-etf-ticker-comparison]] — 국내상장 ETF 종목 비교
- [[raw/invest-research/2026-06-05-passive-diversification-behavior]] — 패시브·분산·행동격차
- [[raw/invest-research/2026-06-05-stoploss-takeprofit-tax-accounts]] — 손절·익절·절세계좌
- 설계: [[docs/superpowers/specs/2026-06-08-invest-field-map-design]]
## 9. 검증 등급
| 영역 | 등급 | 근거 |
|---|---|---|
| 4층 관측 모델·카드 구조 | `documented-only` | 본 설계 + 카드 13장 생성 |
| 대장주/추종주 종목·동조 | `planned`(전부 `[가설]`) | 미검증 — research 필요 |
| 분야간 로테이션 | `planned` | P3 미착수 |
| ETF·세금·계좌 결정 | `locally-verified` | research 3표 검증 |
## 10. 면접·외부 공개 답변 경계
- 외부 공개 대상 아님 (개인 투자 관리). `[가설]` 종목 관계는 어디에도 사실로 인용 금지.
## 13. 관련 개념
- [[wiki/invest-concepts/field-map]] · [[wiki/invest-strategy/strategy]] · [[wiki/invest-plan/active-plan]]
## 14. 다음 단계
> 📋 **완성까지의 상세 마스터 플랜**: [[docs/superpowers/plans/2026-06-08-invest-system-buildout]] — 완성 정의 + 전체 분야 목표표(거시 8 + 한국 섹터 17) + 로테이션 설계 + Phase 0~6 로드맵. *이 플랜을 완수하면 목표 구조가 완성된다.*
- [ ] **Phase 1 — 분야 분류 완성**: 거시 2 + 한국 섹터 10 카드 `[가설]` 스캐폴드 (자동차·금융·철강·화학·원자력·로봇·게임·엔터·화장품·통신유틸).
- [ ] **Phase 2 — 분야간 로테이션 지도** (`field-rotation`) — "달러/금리↑ → 어디서 빠져 어디로" 상승·하락 연쇄 (당신이 원한 핵심).
- [ ] 섹터별 `/invest-research`로 대장주/추종주 동조 검증 → `[가설]``[검증]`.
- [ ] 추가 한국 테마(원자력·자동차·엔터·로봇) 배치.
- [ ] `/project-spec` 로 본 노트를 더 깊게(조사 기반) 보강 + readiness 게이트.
@@ -1 +0,0 @@
../../vault/10-projects/keycloak-patterns-overview/project-notes/keycloak-patterns-overview.md
@@ -0,0 +1,936 @@
---
title: keycloak-patterns Overview (canonical SSOT)
source_type: project-note
status: raw
confidence: medium
tags: [project-note, keycloak-patterns, oauth2, oidc, auth]
related_projects: [keycloak-patterns]
last_reviewed: 2026-07-14
diagrams: [keycloak-patterns/architecture-p1a-edge-no-google-2026-05-26, keycloak-patterns/architecture-p1b-edge-google-2026-05-26, keycloak-patterns/architecture-p2a-cluster-internal-no-google-2026-05-26, keycloak-patterns/architecture-p2b-cluster-internal-google-2026-05-26, keycloak-patterns/architecture-p3a-single-ec2-no-google-2026-05-26, keycloak-patterns/architecture-p3b-single-ec2-google-2026-05-26]
architecture_review: 2026-05-26
status_label: active
project_revision: 1
url:
semantic_surface_exclusions:
- artifact-registry|legacy hub has no project-local Artifact Registry; harness/source/typed-contracts.json is authoritative until migration
- contract-gate-registry|legacy hub has no project-local Contract/Gate Registry; harness/source/typed-contracts.json is authoritative until migration
- flow-stage-registry|legacy hub has no project-local Flow/Stage Registry; harness/source/typed-contracts.json is authoritative until migration
---
# keycloak-patterns Overview
> 본 문서는 keycloak-patterns 프로젝트의 **canonical SSOT** (Single Source of Truth)입니다.
> 모든 branch-note는 본 문서를 기준으로 작업하며, 본 문서가 정의하지 않은 결정은 sub-branch 내부에서 자체 결정.
>
> **위계 (CLAUDE.md §2/§15)**:
> - 본 문서: 프로젝트 전반 정의 (canonical SSOT)
> - `raw/branch-notes/feature-keycloak-patterns.md`: 작업 root (전체 진행 인덱스)
> - `raw/branch-notes/feature-keycloak-<pattern-name>.md`: 6 패턴별 sub-branch (예: `feature-keycloak-edge-forwardauth-no-google`)
> - `raw/branch-notes/feature-keycloak-<implementation-topic>.md`: 패턴별 세부 단계 sub-sub-branch (예: `feature-keycloak-oauth2-proxy-oidc-flow`)
> - 계층 정보는 frontmatter `parent_branch:` + 각 파일의 `## Parent` 섹션에서 추적
> - 구현 코드: `/home/donghyeon/workspace/keycloak-patterns/` (별도 git repo, LLM Wiki 외부)
## 1. 프로젝트 정의
### 한 줄 설명
vanilla JS 클라이언트 + Keycloak Authorization Server + Spring Boot 연동의 **4가지 인증 통합 아키텍처 패턴(AP1~AP4)** 을 비교·이해·구현하는 학습 + 구현 프로젝트. (분류축 교정 2026-07-14 — 기존 "6가지 배치×federation" 은 §2 로 재편; 배포 토폴로지·Google federation 은 각 패턴에 얹는 cross-cutting 변형.)
### 본인 역할
- 개인 프로젝트
- 본인이 맡은 영역: 전 영역 (인프라 + 백엔드 + 프론트 + Keycloak 운영 학습)
- 기간: 2026-05-25 ~ 미정
### 목표 (WHY)
면접에서 "왜 이 배치를 택했나" / "Google 로그인이 붙으면 흐름이 어떻게 바뀌나" / "BFF vs SPA Direct OIDC trade-off는?"에 자신 있게 답할 수 있는 수준의 이해 + **4 인증-아키텍처 패턴 실 구현**.
> **분류축 교정 (2026-07-14)**: 기존 목표는 "배치×federation 6패턴 이해 + P3A 한정 실 구현" 이었으나, 그 6축은 *배포 토폴로지 × Google* 이라 keycloak 인증 아키텍처를 2종만 exercise 하고 AP2·AP3 는 누락돼 있었다. §2 에서 primary 축을 **인증 통합 아키텍처 4패턴** 으로 교정하고, 이 4패턴 실 구현을 새 목표로 삼는다. 상세 근거·매핑은 §2.
### 성공 기준 (측정가능, R1)
> "잘 이해했다" 류 정성 표현 금지. 각 패턴이 "끝났다"고 말할 검증 가능한 결과로 정의한다. done-bar = **E2E 검증 + 그 패턴의 signature 함정 의도 재현 → 해결** (2026-07-14 사용자 확정).
**패턴 공통 done (4 패턴 각각 + Google cross-cutting 1회):**
1. **E2E**: 브라우저 로그인 → 토큰 발급 → 보호 API `200 OK` 를 로컬(`docker compose up`)에서 관찰 — curl 로그 또는 스크린샷 증거 첨부. 등급 `locally-verified`.
2. **Signature 함정 재현 → 해결**: 그 패턴의 대표 실패를 의도적으로 재현(4xx / 토큰 누출)한 뒤 고치고, before/after 를 기록.
| 패턴 | E2E 성공 신호 | 재현 → 해결할 signature 함정 |
|---|---|---|
| **AP1** SPA-direct + Resource Server | SPA 가 받은 access_token 으로 `/api` 200 | (a) `aud` 미검증 → 타 client 토큰 통과 재현 → audience validator 로 401 (b) `KC_HOSTNAME` 미설정 → `iss` mismatch 401 재현 → 설정으로 해결 |
| **AP2** Token-Mediating Backend | 백엔드(confidential client)가 발급받은 access_token 을 브라우저에 전달, 브라우저가 RS 직접 호출 200 | refresh token 이 브라우저에 **노출 안 됨**(백엔드만 보유) 을 네트워크 탭 / 응답 바디로 확인 |
| **AP3** BFF | 브라우저에 토큰 0개(session cookie 만) 확인, BFF proxy 경유 API 200 | CSRF surface(cookie 자동첨부) 재현 → SameSite / CSRF token 으로 차단 |
| **AP4** Edge forward-auth | 미인증 요청 → Keycloak redirect, 인증 후 backend 가 `X-Forwarded-User` 수신 200 | `X-Forwarded-User` 위조로 우회 재현 → NetworkPolicy / SG 로 차단 |
| (cross) **Google brokering** | Google 계정 로그인 → Keycloak 사용자 매핑 → 위 패턴 흐름 재개 200 | `email_verified=false` auto-linking 계정탈취 재현 → Confirm Link Existing Account 로 차단 |
**프로젝트 완료 신호**: 위 표의 4 패턴 + Google cross-cutting 이 모두 `locally-verified` + 트레이드오프 매트릭스 branch 가 4패턴의 {토큰 위치 · 검증 주체 · XSS/CSRF surface · 선택 기준 · keycloak 설정} 을 한 표로 답할 수 있음.
## 2. 인증 아키텍처 분류 (canonical 분류 축 — 2026-07-14 교정)
> **분류축 교정 (2026-07-14).** 기존 분류축은 *배치 위치 3 × Google federation 2 = 6 패턴* 이었으나, 이는 **배포 토폴로지 × federation** 축이라 keycloak *인증 아키텍처* 는 2종(edge-forward-auth, SPA-direct)만 exercise 하고 나머지는 변형이었다(P2≡P3 는 auth 동일, B=A+realm 설정). 멘토가 말한 "4 패턴" 은 **인증 통합 아키텍처**(누가 토큰을 쥐고, 누가 인증을 강제하나) 축이며, 이것이 keycloak client 통합의 canonical 축이다. 아래로 primary 축을 교체하고, 기존 6 축은 §2.2 cross-cutting 변형으로 강등한다.
>
> 근거: [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] (IETF — 브라우저앱 아키텍처 3종 BFF / Token-Mediating Backend / Browser-based OAuth Client 을 *보안강도 내림차순* 으로 정의), [[raw/company-tech-blogs/curity-bff-pattern-spa]].
### 2.1 Primary 축 — 인증 통합 아키텍처 4 패턴
축: **누가 access/refresh token 을 보관하고, 누가 인증을 강제하는가.** IETF `draft-ietf-oauth-browser-based-apps` 의 3 패턴 + 별도 인프라 프록시 강제(oauth2-proxy) = 4.
| ID | 패턴 | 토큰 위치 | 인증 강제 주체 | 백엔드 keycloak 역할 | 보안강도 (IETF) |
|----|------|----------|---------------|---------------------|-----------------|
| **AP1** | Browser-based OAuth Client (SPA-direct + Resource Server) | 브라우저(JS) | SPA 자신 (public client + PKCE) | Resource Server — JWKS 로 JWT 검증 | 낮음 (토큰 브라우저 노출) |
| **AP2** | Token-Mediating Backend | access → 브라우저, refresh → 백엔드 | 백엔드 (confidential client) 가 토큰 획득 후 access token 만 전달 | confidential client + RS | 중 |
| **AP3** | Backend-for-Frontend (BFF) | 백엔드 (session) | 백엔드 (confidential client), 모든 API proxy | confidential client + session holder | 높음 (토큰 브라우저 미노출) |
| **AP4** | Edge / Gateway forward-auth | 프록시 (session) | 별도 reverse proxy (oauth2-proxy / Traefik) | 프록시가 OIDC, 백엔드는 헤더 신뢰 (인증코드 0줄) | 프록시 network 격리에 의존 |
> IETF 보안강도 내림차순 = BFF(AP3) > Token-Mediating(AP2) > Browser-client(AP1). AP4 는 IETF 3종 밖(별도 인프라 프록시)이나 실무의 4번째 패턴.
### 2.2 Cross-cutting 변형 (별도 패턴 아님 — 각 AP 에 얹음)
- **배포 토폴로지**: single-EC2(학습·실 구현) / cluster-internal / edge. 인증 아키텍처를 바꾸지 않고 hostname·issuer·network 경계만 바꿈. signature 함정: `KC_HOSTNAME` iss mismatch, reverse-proxy 헤더.
- **Google IdP brokering (federation)**: realm 에 Google 을 외부 IdP 로 등록. 노트 실측대로 SPA/Backend **코드 0줄 변경** — 어느 AP 에도 동일하게 얹힘. signature 함정: First Broker Login email auto-linking.
### 2.3 기존 6 패턴 → 신 4 패턴 매핑 (기존 작업 재배치, 폐기 아님)
> 기존 34 branch-note 는 폐기하지 않고 아래로 re-map. 실제 rename/re-parent 은 `wiki-doc-author mode=migrate` 로 점진 수행(자동 mv 금지 — wikilink 영향 검토).
| 기존 (배치×federation) | 신 primary (auth 축) | 신 cross-cutting | 기존 sub-branch |
|---|---|---|---|
| P1A Edge no-google | **AP4** Edge forward-auth | 배포=edge | [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] |
| P1B Edge + Google | **AP4** | +Google brokering | [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]] |
| P2A Internal SPA-direct no-google | **AP1** SPA-direct + RS | 배포=internal | [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] |
| P2B Internal + Google | **AP1** | 배포=internal, +Google | [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] |
| P3A Single-EC2 no-google | **AP1** SPA-direct + RS | 배포=single-EC2 (실 구현 base) | [[raw/branch-notes/feature-keycloak-single-ec2-no-google]] |
| P3B Single-EC2 + Google | **AP1** | 배포=single-EC2, +Google | [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] |
| (없음) | **AP2** Token-Mediating Backend | — | 신규 — 기존 6 에 없던 패턴 |
| (bff-vs-spa-direct, out-of-scope 비교문서) | **AP3** BFF | — | [[raw/branch-notes/feature-keycloak-bff-vs-spa-direct]] → AP3 비교 근거(FOLD-IN); 실 구현은 신규 `feature-keycloak-bff-oauth2login-session`·`-bff-csrf-samesite-defense` |
**핵심**: 기존 "6 구현" 은 실제로 auth 아키텍처 2종(AP1, AP4)의 배포·federation 변형이었고 AP2·AP3 는 누락돼 있었다. 신 축은 중복(P2≡P3, B=A+federation)을 제거하고 누락(AP2, AP3)을 채운다 → "6 이 맞나 4 가 맞나" 의 답: **둘은 다른 축이었고, keycloak 을 다 배우려면 auth 축 4패턴이 맞다.**
## 3. 공통 컴포넌트 & 용어
- **Keycloak**: OIDC/OAuth2 Authorization Server. Realm / Client / User / Identity Provider 구성.
- **Client (vanilla JS / SPA)**: Authorization Code Flow + **PKCE**. client 유형은 패턴별로 다름 — **AP1 은 public client**(토큰 브라우저 보유), **AP2·AP3 은 백엔드가 confidential client**(client secret 보유, 토큰을 백엔드가 획득). AP4 는 SPA 가 아니라 프록시가 OIDC client.
- **Backend (API)**: Spring Boot. 패턴별 역할 상이 — AP1/AP4 는 Resource Server(JWT signature + `iss`/`aud`/`exp` 검증), AP2/AP3 는 confidential OAuth client(+ AP3 은 session holder + proxy).
- **Edge Proxy** (P1만): oauth2-proxy 또는 Traefik ForwardAuth — 인증 안 된 요청을 Keycloak으로 redirect, 인증 완료 시 backend로 통과.
- **IdP Brokering** (B 변형): Keycloak이 Google을 외부 IdP로 등록. 사용자 Google 계정으로 로그인 → Google → Keycloak 사용자 매핑 (First Broker Login Flow) → Keycloak token 발급.
- **Token 종류**:
- `authorization code`: 1회용 코드 (브라우저 redirect 매개)
- `access token` (JWT): API 호출용. 짧은 만료 (515분)
- `refresh token`: access token 갱신용. 긴 만료 (130일)
- `ID token` (JWT): 사용자 식별 정보. 백엔드는 보통 사용 안 함, 클라이언트가 사용자 표시용으로 사용.
<!-- section-id: architecture-components -->
## 3-1. 시스템 아키텍처 (System Architecture)
> 6개 패턴 각각의 컴포넌트 구성도. **`templates/diagram-standards.md` v2 (minimalist) 준수** — 5초/30초 룰, 정점 ≤ 10, 간선 ≤ 8, callout ≤ 1, 80% 회색/흰색 + 강조 색 1~2 정도.
>
> 작성 도구: **draw.io** (`.drawio` 파일, 저장 경로 `raw/diagrams/keycloak-patterns/`). Mermaid `graph TD` 는 시스템 아키텍처용으로 사용 금지 — Mermaid 는 시퀀스/ER 다이어그램 전용.
>
> **공통 시각 어휘** (모든 6 패턴 공통):
> - **주황 box + 주황 굵은 화살** = 그 패턴의 주인공 (Edge proxy, tunnel, brokering 등)
> - **파란 box + 파란 굵은 화살** = SPA-direct OIDC 또는 Keycloak brokering 핵심 경로
> - **흰색 box + 회색 가는 화살** = 보조 컴포넌트 / 부차 경로
> - **회색 점선 box** = External system (Google OIDC 등)
> - **빨간 callout** = 그 패턴의 가장 큰 보안/운영 함정 (정확히 1개)
> - **③, ④ 같은 번호** = 시각적 흐름 순서. 본문이 같은 번호로 받아 설명함.
### 3-1-1. P1A — Edge ForwardAuth (no Google)
![[raw/diagrams/keycloak-patterns/architecture-p1a-edge-no-google-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: edge proxy가 인증 게이트일 때, 백엔드는 어떻게 인증 코드 0줄로 동작하는가?
**4단계 흐름** (다이어그램 ①~④):
1. `① HTTPS` (강조 — 진입 경로) — User browser 가 Edge zone 의 oauth2-proxy 에 요청
2. `② OIDC redirect` — proxy 가 미인증 요청을 Keycloak 으로 redirect (Authorization Code + PKCE)
3. `③ 로그인 + token` — Keycloak 로그인 UI 후 token 발급 (점선 = 사용자 매개 redirect)
4. `④ X-Forwarded-User` (강조 — 핵심 위탁) — proxy 가 인증 사용자명을 헤더로 backend 에 전달
**핵심 함정** (헤더 spoofing):
- backend 가 `X-Forwarded-User` 헤더만으로 사용자 식별 → ingress 우회 경로 존재 시 위조 가능
- 해결: NetworkPolicy (k8s) 또는 SG (AWS) 로 proxy → backend 만 통과시키고, 가능하면 mTLS 추가
**컴포넌트 책임 (P1A)**:
| 컴포넌트 | 역할 | 스택 |
|---|---|---|
| Edge Proxy | OIDC 인증 게이트 — 인증 안 된 요청을 Keycloak으로 redirect | oauth2-proxy 또는 Traefik ForwardAuth |
| Backend API | 비즈니스 로직만. 인증 검증은 proxy에 위임. 헤더로 사용자 식별 | Spring Boot 3.x (Spring Security 미사용) |
| Keycloak | Authorization Server. 사용자 DB + OIDC discovery | Keycloak 25.x + PostgreSQL 16 |
**P1A 트레이드오프**:
- 장점: backend 가 인증 코드 0줄. 다국적 polyglot 백엔드에 균일하게 인증 적용 용이.
- 단점: backend 가 헤더 신뢰 모델 → 네트워크 격리 실패 시 전면 우회.
**출처 (Sources)**:
- oauth2-proxy ForwardAuth — [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]]
- Traefik ForwardAuth — [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]]
### 3-1-2. P1B — Edge ForwardAuth + Google federation
![[raw/diagrams/keycloak-patterns/architecture-p1b-edge-google-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: P1A 에 Google 로그인을 붙이면 Keycloak IdP brokering 만으로 SPA/Backend 변경 없이 가능한가?
**5단계 흐름** (다이어그램 ①~⑤):
1. `① HTTPS` — User → oauth2-proxy
2. `② OIDC redirect` — proxy → Keycloak (OIDC AS)
3. `③ Google 로그인` (강조 — 외부 IdP 위탁 핵심) — Keycloak → Google OIDC
4. `④ id_token (email_verified)` (점선 = 외부 호출) — Google → Keycloak, First Broker Login Flow 진입
5. `⑤ X-Forwarded-User` — proxy → backend (P1A 와 동일)
**핵심 함정** (First Broker Login Flow — email-match auto-linking):
- Keycloak 기본 옵션이 email 기반 자동 linking 제공
- Google 이 `email_verified=false` 인 사용자도 통과시키면 본인 외 사용자의 기존 계정 탈취 가능
- 해결: First Broker Login Flow 에서 `Confirm Link Existing Account` 강제 + `email_verified=true` 필수
**P1A 대비 추가/변화**:
- Keycloak ← Google IdP brokering 설정 추가
- Backend / proxy 코드는 변경 0줄 — Keycloak Realm 설정만 추가
**P1B 트레이드오프**:
- 장점: SPA/Backend 코드 변경 0줄로 Google SSO 추가
- 단점: First Broker Login Flow 설정 실수 시 계정 탈취 위험. Google API 의존성 운영 부담.
**출처 (Sources)**:
- Keycloak IdP brokering — [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]]
- First Broker Login Flow 보안 — [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]]
### 3-1-3. P2A — Cluster-internal SPA-direct (no Google)
![[raw/diagrams/keycloak-patterns/architecture-p2a-cluster-internal-no-google-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: Edge proxy 없이 SPA 가 직접 OIDC 할 때, Backend 는 어떻게 JWT 신뢰를 닫는가?
**4단계 흐름** (다이어그램 ①~④):
1. `① HTTPS GET (SPA)` — User → nginx 가 호스팅하는 vanilla JS SPA 자원 수령
2. `② OIDC + PKCE` (강조 — SPA 가 토큰 보유) — SPA → Keycloak, 직접 token 흐름 (P1 과의 결정적 차이)
3. `③ Bearer access_token` (강조 — API 호출) — SPA → Backend, `Authorization: Bearer ...`
4. `④ JWKS` (강조 — 신뢰 닫기) — Backend → Keycloak 에서 검증 공개키 조회
**핵심 함정** (XSS surface):
- SPA 가 access/refresh token 을 브라우저 메모리/스토리지에 보유 → XSS 1건 = 세션 전체 탈취
- 해결: refresh token 보호가 필요하면 BFF(P1) 로 전환, 또는 httpOnly cookie 전략 검토
**P1A 대비 차이**:
- SPA 가 토큰 직접 보유 → XSS surface ↑, BFF 패턴 검토 가치 있음
- Backend 가 JWT validator 코드 보유 (`iss`, `aud`, `exp`, signature) — Spring Security 6.x Resource Server
**P2A 트레이드오프**:
- 장점: 컴포넌트 단순 (proxy 1개 제거). frontend 가 OIDC 흐름 완전 제어 가능.
- 단점: XSS surface 확대 + backend 가 JWT 검증 코드 보유 → polyglot 백엔드 마다 구현 필요.
**출처 (Sources)**:
- Spring Security Resource Server — [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]]
- PKCE 흐름 (RFC 7636) — [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]]
### 3-1-4. P2B — Cluster-internal + Google federation
![[raw/diagrams/keycloak-patterns/architecture-p2b-cluster-internal-google-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: P2A 에 Google brokering 을 추가할 때, SPA/Backend 코드는 그대로 둘 수 있는가?
**5단계 흐름** (다이어그램 ①~⑤):
1. `① HTTPS GET` — User → nginx SPA
2. `② OIDC + PKCE` — SPA → Keycloak (P2A 와 동일)
3. `③ Google 로그인` (강조 — 외부 IdP 위탁) — Keycloak → Google
4. `④ id_token` (점선 = 외부 호출) — Google → Keycloak, First Broker Login Flow
5. `⑤ Bearer + JWKS` — SPA → Backend (Bearer), Backend → Keycloak (JWKS)
**핵심 함정** (P1B + P2A 중첩):
- P1B 의 email-match auto-linking + P2A 의 SPA XSS surface 가 모두 적용됨
- 해결: `Confirm Link Existing Account` 강제 + `email_verified=true` + 클라이언트 CSP / sanitize 강화 / 필요 시 BFF(P1) 로 이주
**P2A 대비 추가/변화**:
- Keycloak Realm 에 Google IdP 등록만 추가 (SPA/Backend 변경 0줄)
- First Broker Login Flow 보안 옵션 추가 검토 필요
**P2B 트레이드오프**:
- 장점: SPA/Backend 코드 0줄 변경으로 Google SSO 추가
- 단점: 두 함정 (auto-linking + XSS) 가 중첩되어 보안 운영 부담 ↑
**출처 (Sources)**:
- Keycloak IdP brokering — [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]]
- First Broker Login Flow 보안 — [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]]
### 3-1-5. P3A — Single EC2 (no Google) — **실 구현 대상**
![[raw/diagrams/keycloak-patterns/architecture-p3a-single-ec2-no-google-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: 사용자 요청이 어떤 컴포넌트를 어떤 순서로 거치며 인증되는가?
**5단계 흐름** (다이어그램 ①~⑤):
1. `① HTTPS GET /` — User browser 가 nginx 에서 SPA 정적 자원 받음
2. `② OIDC + PKCE` (강조 — 핵심 경로) — Browser 가 Keycloak 으로 직접 redirect, Authorization Code + PKCE 흐름
3. `③ Bearer token + /api` — SPA 가 받은 access_token 으로 API 호출
4. `④ proxy_pass` — nginx 가 Spring Boot 로 reverse proxy
5. `⑤ JWKS` (강조 — 검증 경로) — Spring Boot 가 Keycloak 에서 JWT 검증 키 조회
**핵심 함정** (`KC_HOSTNAME`):
- Browser 는 EC2 public hostname 으로 Keycloak 호출 → JWT 의 `iss` claim = public host
- Backend 는 `localhost:8180` 로 JWKS 조회 → `iss` 비교 시 mismatch → 401
- 해결: docker-compose 에 `KC_HOSTNAME=<public-host>` + `KC_HTTP_ENABLED=true` 명시
**부차 함정** (`redirect_uri`):
- Keycloak client 의 Valid Redirect URIs 등록 시 `localhost` 만 등록 / browser 가 `127.0.0.1` 접근 → mismatch
- 해결: 등록과 접근 hostname 1:1 일치 또는 둘 다 등록
**P3A 트레이드오프**:
- 장점: 학습 / 개발 환경 최단 셋업. 단일 docker-compose 로 끝남.
- 단점: SPoF — EC2 1대 다운 = 전체 정지. 운영급은 P2A + Keycloak HA cluster.
**출처 (Sources)**:
- KC_HOSTNAME 함정 — [[raw/branch-notes/feature-keycloak-iss-claim-hostname-mismatch]]
- redirect_uri 함정 — [[raw/branch-notes/feature-keycloak-docker-compose-stack]]
- OIDC PKCE — [[raw/official-docs/oauth2-pkce-rfc-7636]] (또는 해당 official-doc)
**다이어그램 편집**: Obsidian draw.io 플러그인으로 위 임베드 더블클릭. 또는 [draw.io 데스크탑 앱](https://www.drawio.com/) 사용.
### 3-1-6. P3B — Single EC2 + Google federation
![[raw/diagrams/keycloak-patterns/architecture-p3b-single-ec2-google-2026-05-26.drawio]]
**다이어그램이 답하는 질문**: P3A 에 Google 을 붙이려면 왜 외부 HTTPS endpoint(tunnel/RP) 가 강제되는가?
**6단계 흐름** (다이어그램 ①~⑥):
1. `① HTTPS` (강조 — 공개 진입) — User → HTTPS tunnel (cloudflared / ngrok / Caddy)
2. `② localhost (HTTP)` (강조 — tunnel 가 localhost 위탁) — tunnel → nginx
3. `③ proxy_pass /api` — nginx → Spring Boot Backend
4. `④ JWKS` — Backend → Keycloak 검증 키 조회
5. `⑤ Google 로그인 (공개 HTTPS)` (강조 — 외부 IdP) — Keycloak → Google
6. `⑥ id_token` (점선 = 외부 호출) — Google → Keycloak
**핵심 함정** (`KC_HOSTNAME` 공개 hostname 강제):
- Google 이 검증하는 `redirect_uri` 와 Keycloak issuer 가 모두 공개 HTTPS host 여야 함
- P3A 처럼 `localhost` 로 설정 시 Google 흐름 실패 또는 issuer 불일치 401
- 해결: `KC_HOSTNAME=<public-host>` + Keycloak Realm Client 의 `Valid Redirect URIs` 를 public URL 로
**P3A 대비 추가 요구사항**: EC2 를 외부 HTTPS 로 노출 (Google 이 redirect_uri 검증). cloudflared / ngrok / 정식 도메인 + Caddy 중 택일.
**P3B 트레이드오프**:
- 장점: P3A 단순성을 유지하면서 Google SSO 추가 가능
- 단점: tunnel/RP 운영 부담 + KC_HOSTNAME 설정 함정 (P3A 의 함정이 hostname 만 바뀌어 재발)
**출처 (Sources)**:
- KC_HOSTNAME 함정 — [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]]
- cloudflared / ngrok 비교 — [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]]
### 3-1-7. AP2 (Token-Mediating) · AP3 (BFF) — needs-diagram
> 신 primary 축의 AP2·AP3 은 기존 6 `.drawio`(P1A~P3B = AP1 배포 변형 + AP4)에 대응 아키텍처 다이어그램이 없다. **needs-diagram** — 사용자가 draw.io 로 작성 후 아래 백틱을 풀어 활성 임베드로 전환한다. (미작성 파일을 활성 임베드로 두면 9a 린터가 `BROKEN_LINK` 로 잡으므로 placeholder 는 백틱 코드로 비활성.)
- AP2 Token-Mediating Backend: `![[raw/diagrams/keycloak-patterns/architecture-ap2-token-mediating-2026-07-14.drawio.svg]]`
- AP3 Backend-for-Frontend: `![[raw/diagrams/keycloak-patterns/architecture-ap3-bff-2026-07-14.drawio.svg]]`
작성 시 `rules/diagram-standards.md` v2 (minimalist) 준수 + `wiki-diagram-reviewer` ≥95 별도 확인(게이트는 존재만 판정, 품질 ≥95 는 사용자가 별도 실행).
<!-- section-id: sequence -->
## 3-2. 핵심 시퀀스 (Key Sequences — Mermaid)
> 토큰 교환 흐름은 패턴마다 다름. happy path + 주요 error path 함께. `templates/project-template.md` §4 표준 준수 — `autonumber`, `actor` vs `participant` 구분, alt/opt/loop 블록, `Note over` 비자명한 동작.
<!-- section-id: runtime-flow -->
### P3A: vanilla JS + PKCE + Keycloak (단일 EC2, no Google)
```mermaid
sequenceDiagram
autonumber
actor User
participant SPA as vanilla JS SPA (nginx)
participant KC as Keycloak (Authorization Server)
participant API as Spring Boot Resource Server
User->>SPA: 로그인 클릭
SPA->>SPA: PKCE code_verifier 생성, code_challenge=SHA256(verifier)
SPA->>KC: GET /realms/<realm>/protocol/openid-connect/auth?client_id=<spa>&response_type=code&code_challenge=...&redirect_uri=...
KC-->>User: 로그인 폼 redirect
User->>KC: id/password 입력
alt 자격 증명 유효
KC-->>SPA: 302 redirect with authorization code
SPA->>KC: POST /token (code + code_verifier)
KC-->>SPA: 200 OK {access_token, id_token, refresh_token}
SPA->>API: GET /api/v1/<resource> + Authorization: Bearer <access_token>
API->>API: JWT 검증 (iss, aud, exp, signature with JWKS)
alt JWT 유효
API-->>SPA: 200 OK {resource}
SPA-->>User: 화면 표시
else aud claim mismatch
API-->>SPA: 401 Unauthorized {error: invalid_token}
SPA-->>User: 에러 + 재로그인 유도
end
else 자격 증명 무효
KC-->>SPA: 302 redirect with error=access_denied
SPA-->>User: 에러 표시
end
```
> 위 P3A 시퀀스 = **AP1**(SPA-direct + Resource Server)의 single-EC2 배포. 아래는 나머지 3 패턴의 핵심 시퀀스(happy + error path).
### AP2: Token-Mediating Backend (백엔드 confidential client, access token 만 브라우저 전달)
```mermaid
sequenceDiagram
autonumber
actor User
participant B as Browser (SPA)
participant BE as Backend (confidential client)
participant KC as Keycloak
participant API as Resource API
User->>B: 로그인 클릭
B->>BE: GET /login
BE->>KC: Authorization Code (confidential client + secret)
KC-->>User: 로그인 폼
User->>KC: 자격 증명
alt 로그인 성공
KC-->>BE: access_token + refresh_token
Note over BE: refresh_token 은 백엔드만 보유 (브라우저 미전달)
BE-->>B: access_token 만 전달
B->>API: GET /resource + Bearer access_token
API-->>B: 200 OK
else access_token 만료 (재발급은 백엔드 경유)
API-->>B: 401 invalid_token
B->>BE: POST /token/refresh
BE->>KC: refresh_grant (백엔드 보유 refresh_token)
KC-->>BE: 새 access_token
BE-->>B: 새 access_token
end
```
### AP3: Backend-for-Frontend (BFF) — 토큰 0개, session cookie 만
```mermaid
sequenceDiagram
autonumber
actor User
participant B as Browser (SPA)
participant BFF as BFF (Spring oauth2Login)
participant KC as Keycloak
participant API as Resource API
User->>B: 로그인 클릭
B->>BFF: GET /oauth2/authorization/keycloak
BFF->>KC: Authorization Code (confidential client)
KC-->>User: 로그인 폼
User->>KC: 자격 증명
alt 로그인 성공
KC-->>BFF: 302 + authorization code
BFF->>KC: POST /token (code + client_secret)
KC-->>BFF: access/refresh token (BFF session 에 저장)
BFF-->>B: Set-Cookie: SESSION (httpOnly) — 브라우저에 토큰 없음
B->>BFF: GET /api/resource (cookie 자동 첨부)
BFF->>API: GET /resource + Bearer (BFF 가 토큰 부착)
API-->>BFF: 200 OK
BFF-->>B: 200 OK
else CSRF (cookie 자동첨부 악용)
Note over B,BFF: 외부 사이트가 cookie 실린 상태변경 요청 위조
BFF-->>B: 403 (SameSite=Lax + CSRF token 검증 실패로 차단)
end
```
### Gateway forward-auth (oauth2-proxy, 백엔드 인증코드 0줄)
```mermaid
sequenceDiagram
autonumber
actor User
participant Proxy as oauth2-proxy (ForwardAuth)
participant KC as Keycloak
participant API as Backend API
User->>Proxy: GET /app (미인증)
Proxy->>KC: OIDC redirect (Authorization Code + PKCE)
KC-->>User: 로그인 폼
User->>KC: 자격 증명
alt 인증 성공
KC-->>Proxy: token (proxy session 보관)
Proxy->>API: GET /app + X-Forwarded-User: <sub>
API-->>Proxy: 200 OK (헤더만으로 사용자 식별)
Proxy-->>User: 200 OK
else 헤더 위조 우회 시도 (signature 함정)
Note over API: ingress 우회 경로로 X-Forwarded-User 직접 주입
API-->>User: 200 (❌ network 격리 실패 시 위조 성공)
Note over API: 방어 = NetworkPolicy/SG 로 proxy→API 만 통과 (+ mTLS)
end
```
### P2B → AP4/AP1 + Google: Google IdP federation 추가 흐름 (sub-branch에 상세)
(Google brokering 은 AP1~AP4 어디에도 코드 0줄로 얹히는 cross-cutting. 상세 다이어그램은 각 sub-branch — [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]], [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] — 에서.)
## 4. 인프라 / 기술 스택
| 영역 | 선택 |
|------|------|
| 언어 | Java 21 (Backend), JavaScript ES2022+ (vanilla, no framework) |
| 프레임워크 | Spring Boot 3.x + Spring Security 6.x (Resource Server) |
| Authorization Server | Keycloak 25.x (latest stable as of 2026-05) |
| DB (Keycloak) | PostgreSQL 16 |
| Web Server (SPA) | nginx (static file serving) |
| 컨테이너 | Docker + Docker Compose |
| 배포 환경 (P3A 한정) | 단일 EC2 (학습용) — HTTPS termination 선택적 |
| OIDC client library | 직접 PKCE 구현 또는 `oidc-client-ts` |
<!-- section-id: implementation-boundaries -->
## 5. 작업 범위 (Project-level Scope)
### 포함 범위 (2026-07-14 교정 — 4 인증-아키텍처 패턴 실 구현)
- **4 인증-아키텍처 패턴(AP1~AP4) 실 구현** — 모두 single-EC2 로컬 스택 위에서 E2E(`locally-verified`) + signature 함정 재현→해결 (§1 성공기준, §8.0 branch 분해).
- **Google IdP brokering** 을 cross-cutting 변형으로 1회 실 구현(어느 패턴에 얹어도 SPA/Backend 코드 0줄 변경 검증 포함).
- 4 패턴 통합 trade-off 매트릭스 (토큰 위치 / 검증 주체 / XSS·CSRF surface / 선택 기준 / keycloak 설정).
- 각 패턴의 공식 문서 · 기술블로그 출처 raw 보존 + 채택/대안/비교 구조 명시.
- 각 패턴에서 토큰 종류의 교환 시점 · 저장 위치 · 만료 정책 정리.
### 제외 범위
- **배포 토폴로지 별도 구현**: 인증 아키텍처는 single-EC2 로 실 구현하고, cluster-internal / edge 는 hostname·issuer·network 차이만 문서화(별도 k8s/Traefik 환경 구축 안 함 — §2.2 cross-cutting).
- 다른 OIDC Provider(GitHub / Auth0 / Cognito) federation. Google만.
- React/Vue 등 SPA 프레임워크 (vanilla JS 유지).
- 모바일 / 네이티브 앱 흐름 (PKCE for native).
- mTLS, FAPI(Financial-grade API), DPoP 등 고급 보안 옵션.
### Deferred — keycloak SERVER-side 심화 트랙 (2026-07-14 명시, 지금 안 함)
> 사용자 확정: 4 client-integration 패턴 E2E 를 먼저 끝낸 뒤 별도 학습 트랙으로 착수. "keycloak 다 알기" 의 나머지 절반(server/운영 측면)이며, **본 프로젝트 현 phase 의 out-of-scope 이되 폐기가 아니라 후속 트랙으로 예약**한다.
- **SPI (Service Provider Interface)** — custom authenticator / mapper / event listener 작성.
- **HA cluster** — Infinispan 분산 캐시, active-active Keycloak 다중 노드.
- **multi-realm / 멀티테넌시** — realm-per-tenant vs client-per-tenant.
- **Admin REST API 자동화** — realm/client export·import 를 코드로 (`feature-keycloak-realm-client-export` 씨앗 존재).
- **LDAP / user federation** — 외부 사용자 저장소 연동.
- **token revocation 심화** — JWT stateless 한계 + blacklist / introspection endpoint.
**인접 관심사 커버리지 note (9-coverage — silent 누락 방지):**
- **인가(Authorization) — keycloak roles → Spring `@PreAuthorize`**: 본 4 패턴은 authN(인증) 토큰 흐름에 집중한다. RBAC 인가는 별개 관심사이며 기존 씨앗 `feature-keycloak-idp-mappers-claim-to-role` 존재 — 4 패턴 E2E 후 각 패턴에 얹음(현 phase 명시적 out-of-scope, 폐기 아님).
- **Logout / session termination (front/back-channel)**: AP3 BFF session 종료 · AP1 토큰 만료로 부분 커버. refresh rotation + logout 후 session·token 무효화는 `WI-KEYCLOAK-PATTERNS-OVERVIEW-007`(`feature-keycloak-refresh-rotation-and-logout`, §8.0 registry)이 **현 phase** 로 커버한다. **통합 front/back-channel logout 흐름 심화**만 deferred (2026-07-23 경계 명확화 — §8.0 WI-007 과의 이중 서술 해소).
## 기술 결정
> **Legacy reference (v1).** 아래 비교표는 rationale과 대안을 보존한다. stable decision owner와 branch 상속 기준은 §6.1 registry다.
> 프로젝트 차원 기술 결정. 각 결정은 검토한 대안 + 외부근거 wikilink 필수 (근거 없으면 `UNSUPPORTED_DECISION`). 결정별 *깊은* 대안 비교는 branch 단계(`/branch-spec` + `wiki-decision-researcher`)로 위임 — 본 표는 hub 차원 stack/축 결정의 근거 소싱까지.
| 결정 영역 | 선택 | 검토한 대안 | 채택 이유 | 트레이드오프 | 근거 자료 |
|---|---|---|---|---|---|
| **분류 primary 축** | 인증 통합 아키텍처 4패턴 (AP1~AP4) | (a) 배포×federation 6패턴(기존) (b) IETF 3패턴만 (c) 멘토 "4패턴" | 6축은 auth 아키텍처 2종만 exercise + AP2/AP3 누락. IETF 3 + edge-proxy = 4 가 keycloak client 통합 canonical 축이며 중복(P2≡P3, B=A+federation) 제거 + 누락(AP2·AP3) 채움 | edge-proxy(AP4)는 IETF 3종 밖 실무 확장 — 표준 인용은 IETF 3까지만 유효 | [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]], [[raw/company-tech-blogs/curity-bff-pattern-spa]] |
| **AP1 SPA client 유형** | public client + Authorization Code + PKCE | implicit flow / password grant | implicit·password 는 OAuth 2.1 에서 사실상 배제. PKCE 가 public client 표준 | 토큰이 브라우저에 노출(XSS surface) — AP2/AP3 로 완화 가능 | [[raw/official-docs/oauth2-pkce-rfc-7636]], [[raw/official-docs/oauth-v2-1-draft-ietf]] |
| **AP3 BFF 토큰 위치** | 백엔드 session (브라우저 = cookie 만) | 브라우저 저장 (localStorage / memory) | "토큰을 브라우저 밖에 두는 것이 XSS 로부터 보호하는 유일한 방법"(Curity) — 최고 보안강도 | stateful(session store 필요), 모바일 별도 흐름, CSRF surface 증가 | [[raw/company-tech-blogs/curity-bff-pattern-spa]], [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |
| **AP2 Token-Mediating Backend** | 백엔드 confidential client 가 토큰 획득, access token 만 브라우저 전달 | AP1(전부 브라우저) / AP3(전부 백엔드) | BFF 보다 경량(모든 요청 proxy 불필요) + AP1 보다 refresh token 보호 | access token 은 여전히 브라우저 노출 | [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |
| **AP4 Edge forward-auth** | oauth2-proxy ForwardAuth | Traefik ForwardAuth / nginx `auth_request` / Spring Cloud Gateway TokenRelay | 백엔드 인증코드 0줄, polyglot 균일 적용 | 헤더 신뢰 모델 → network 격리 실패 시 전면 우회 | [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]], [[raw/official-docs/oauth2-proxy-nginx-integration-official]] |
| **Google federation** | Keycloak IdP brokering + First Broker Login hardening | SPA/Backend 가 직접 Google OIDC 호출 | keycloak 이 brokering 흡수 → 앱 코드 0줄. First Broker Login 으로 account linking 제어 | email auto-linking 계정탈취 위험 → Confirm Link Existing Account 필수 | [[raw/official-docs/keycloak-identity-brokering-overview-official]], [[raw/official-docs/keycloak-first-broker-login-flow]] |
> 소싱 bound(회당 6): 위 6개로 마감. 추가 결정(HTTPS termination D2~D5 등)은 §13 에 기존 근거 보존, deferred server-side 트랙 결정은 후속 `/project-spec` 회차로 이월(`deferred`).
## 프로젝트 레벨 고정 결정 (Fixed Decisions — branch 간 충돌 방지)
> **Legacy reference (v1).** F1~F5의 현재 stable owner는 아래 §6.1 registry다. F1은 taxonomy decision에 병합하며 중복 owner row를 만들지 않는다.
> 여러 branch 가 공유하므로 hub 가 1회 고정. branch 는 재정의 금지, 본 절을 참조만 (SSOT).
| # | 고정 결정 | SSOT 위치 | 이유 / 충돌 방지 |
|---|---|---|---|
| F1 | **인증 패턴 taxonomy = §2 (AP1~AP4 + cross-cutting)** | §2 (본 노트) | 모든 branch 는 §2 의 AP-ID 를 인용. 패턴을 branch 에서 재정의하면 6-vs-4 혼선 재발 |
| F2 | **done-bar = E2E + signature 함정 재현→해결** | §1 성공기준 | 4 패턴 branch 가 동일 완료 기준 상속. 등급은 `src/` 검증 후 `locally-verified` |
| F3 | **단일 공유 realm `keycloak-patterns`, 패턴당 client 1개** (spa-public / token-mediating-confidential / bff-confidential / edge-proxy) | §4 스택 + baseline branch | client 분리로 `aud` claim 충돌 방지. AP1 audience validator 가 client별 aud 검증 가능 |
| F4 | **confidential client secret = env var, 미커밋** | baseline branch | AP2·AP3 는 client secret 보유. `.env`/`KC_*` 로 주입, realm export JSON 에 평문 금지 |
| F5 | **E2E 실 구현 배포 = single-EC2 docker-compose** (cluster-internal/edge 는 문서만) | §2.2, §5 | 배포 토폴로지는 cross-cutting 이라 auth 아키텍처를 바꾸지 않음 — 실 구현 1벌로 4 패턴 모두 검증 |
<!-- section-id: project-decisions -->
## 6.1 안정 결정 레지스트리
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|---|---:|---|---|---|---|---|
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001` | 1 | `auth-taxonomy` | canonical 분류축은 AP1~AP4 인증 통합 아키텍처와 cross-cutting 변형이다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §2; §기술 결정 `분류 primary 축`; F1 |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001` | 1 | `spa-client` | AP1은 public client와 Authorization Code + PKCE를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP1 SPA client 유형`; [[raw/official-docs/oauth2-pkce-rfc-7636]] |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001` | 1 | `bff-session` | AP3는 token을 backend session에 두고 browser에는 cookie만 둔다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP3 BFF 토큰 위치`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001` | 1 | `token-mediating` | AP2 backend가 token을 획득하고 access token만 browser에 전달한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP2 Token-Mediating Backend`; [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001` | 1 | `edge-forwardauth` | AP4는 oauth2-proxy ForwardAuth를 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `AP4 Edge forward-auth`; [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]] |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001` | 1 | `idp-brokering` | Google federation은 Keycloak IdP brokering과 hardened First Broker Login을 사용한다 | `active` | [[raw/project-notes/keycloak-patterns-overview]] | §기술 결정 `Google federation`; [[raw/official-docs/keycloak-first-broker-login-flow]] |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001` | 1 | `acceptance` | done-bar는 E2E success와 signature security failure 재현·해결 evidence다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F2; §1 성공 기준 |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001` | 1 | `realm-client` | 단일 realm keycloak-patterns에서 인증 패턴별 client를 분리한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F3; §4 stack |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001` | 1 | `secret-boundary` | confidential client secret은 env var로 주입하고 commit·realm export 평문을 금지한다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F4 |
| `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001` | 1 | `deployment` | AP1~AP4 E2E 구현 topology는 single-EC2 docker-compose다 | `documented-only` | [[raw/project-notes/keycloak-patterns-overview]] | F5; §2.2; §5 |
<!-- section-id: project-work-items -->
## 8.0 실행계획
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|---|---|---|---|---|---|
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `feature-keycloak-docker-compose-stack` | Keycloak·PostgreSQL·nginx·Spring 4 containers가 healthy이고 admin console에 접속된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-DEPLOYMENT-001@1` | - | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `feature-keycloak-realm-client-export` | realm import와 client 4개 등록 후 unauthenticated protected endpoint가 401이고 export가 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-REALM-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-001` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS PKCE login·token 수령·protected API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `feature-keycloak-spring-rs-audience-validator` | foreign audience token 수용 실패를 재현하고 validator 적용 후 401을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-005` | `feature-keycloak-iss-claim-hostname-mismatch` | hostname 미설정 iss mismatch 401과 설정 후 복구 log가 존재한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-006` | `feature-keycloak-spa-token-storage-tradeoff` | 저장 위치별 browser token read/XSS surface가 재현되고 선택이 기록된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-007` | `feature-keycloak-refresh-rotation-and-logout` | refresh rotation과 logout 후 session·token 무효화가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SPA-CLIENT-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-004` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `feature-keycloak-token-mediating-confidential-client` | confidential backend의 code-token 교환과 server-side refresh 보관이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `in-progress` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-009` | `feature-keycloak-token-mediating-access-handoff` | browser가 access token으로 API 200을 받고 refresh token은 network response에 존재하지 않는다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-TOKEN-MEDIATING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-008` | `in-progress` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `feature-keycloak-bff-oauth2login-session` | browser token 0개와 SESSION cookie만으로 BFF proxy API 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-SECRET-BOUNDARY-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `in-progress` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-011` | `feature-keycloak-bff-csrf-samesite-defense` | CSRF를 재현하고 SameSite와 CSRF token 적용 후 403을 검증한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-BFF-SESSION-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-010` | `in-progress` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `feature-keycloak-oauth2-proxy-oidc-flow` | unauthenticated redirect와 login 후 X-Forwarded-User backend 200이 재현된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-002` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-013` | `feature-keycloak-nginx-auth-request-integration` | nginx auth_request 통합과 4KB cookie split case가 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-014` | `feature-keycloak-header-spoofing-defense` | forwarded-user spoofing 우회를 재현하고 network isolation 후 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-EDGE-FORWARDAUTH-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `feature-keycloak-idp-brokering-google-client` | Google login·Keycloak user mapping 200과 application diff 0줄이 검증된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `feature-keycloak-first-broker-login-flow` | unsafe auto-linking을 재현하고 Confirm Link Existing Account로 차단한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-017` | `feature-keycloak-google-claim-attribute-mapping` | Google email·name claim이 Keycloak attribute로 매핑된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-015` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-018` | `feature-keycloak-account-linking-sub-vs-email` | sub와 email linking key의 security comparison과 선택이 기록된다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-IDP-BROKERING-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-016` | `planned` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-019` | `feature-keycloak-four-pattern-tradeoff-matrix` | 4패턴 비교표의 모든 cell이 구현 WI evidence를 가리킨다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1`, `DEC-KEYCLOAK-PATTERNS-OVERVIEW-ACCEPTANCE-001@1` | `WI-KEYCLOAK-PATTERNS-OVERVIEW-003`, `WI-KEYCLOAK-PATTERNS-OVERVIEW-008`, `WI-KEYCLOAK-PATTERNS-OVERVIEW-010`, `WI-KEYCLOAK-PATTERNS-OVERVIEW-012` | `in-progress` |
| `WI-KEYCLOAK-PATTERNS-OVERVIEW-020` | `feature-keycloak-patterns` | project governance hub가 AP1~AP4 taxonomy와 child progress index를 유지한다 | `DEC-KEYCLOAK-PATTERNS-OVERVIEW-AUTH-TAXONOMY-001@1` | - | `in-progress` |
## 실행계획 (Branch decomposition, R4)
> **Legacy reference (v1).** 아래 2-tier grouping과 priority 설명은 보존한다. Tier-1은 파일이 아닌 group label이며 stable branch ID·decision pin·dependency의 SSOT는 위 Work Item Registry다.
> **`/project-spec` 핸드오프 섹션.** 2-tier — **Tier-1 = 패턴 parent**(project 직접 자식, `parent_branch:` 비어있음), **Tier-2 = 실제 "1 branch = 1 PR" 단위**(각 Tier-2 가 "1개로 끝낼 양"). 각 branch 의 *네이밍 + 측정가능 목표조건 + 우선순위 + 의존*만 적는다 — 결정 내용·메커니즘은 `/branch <slug>` 생성 후 `/branch-spec` 가 깊게 채운다. slug 는 `rules/naming-conventions.md` §2.1 준수(`feature-` + content-descriptive, numbered hierarchy 없음). 규모: **7 Tier-1 / 19 Tier-2 ≈ 19 PR** — "바로 끝낼 양" 아님(수 주 분량). 기존 27 sub-sub-branch 를 4-패턴으로 re-map + AP2 만 신규.
### Tier-1 개요 (7 parent)
| Tier-1 parent slug | 역할 | Tier-2 수 | 우선순위 |
|---|---|---|---|
| `feature-keycloak-local-stack-baseline` | 4 패턴 공유 로컬 스택 | 2 | P1 |
| `feature-keycloak-spa-direct-resource-server` | AP1 | 5 | P2 |
| `feature-keycloak-token-mediating-backend` | AP2 (신규) | 2 | P3 |
| `feature-keycloak-bff-session-proxy` | AP3 | 2 | P3 |
| `feature-keycloak-edge-forwardauth-proxy` | AP4 | 3 | P3 |
| `feature-keycloak-google-idp-brokering` | Google cross-cutting | 4 | P4 |
| `feature-keycloak-four-pattern-tradeoff-matrix` | 종합 매트릭스 | 1 | P5 |
> **명명 정합 (2026-07-14 감사 — 파일 ↔ hub 매칭 검증)**:
> - **Tier-2 실 구현 19개**: **14개 = 기존 branch-note 파일과 슬러그 정확히 일치 ✓**. 5개 = 신규 예정(`token-mediating-confidential-client`·`-access-handoff`, `bff-oauth2login-session`·`-csrf-samesite-defense`, `four-pattern-tradeoff-matrix`) → `/branch` 로 생성.
> - **Tier-1 그룹명 7개는 파일이 아니라 그룹 라벨**이다(파일로 만들면 기존 pattern 노트 §12.1 와 중복되므로 만들지 않음). 각 Tier-2 의 물리적 `parent_branch:` 는 현재 옛 pattern 노트(§12.1)를 가리키고, AP 그룹 소속은 **본 분해표가 SSOT**. 링크 깨짐 0.
> - **재-parent 매핑**(각 그룹 실 작업 착수 시 `wiki-doc-author mode=migrate` 로 반영 — 지금은 cosmetic 이라 미실행): Google Tier-2 4개(현 parent P1B `edge-forwardauth-google-federation`) → Google 그룹, `spring-rs-audience-validator`·`spa-token-storage-tradeoff`(현 parent P2A `internal-spa-direct-no-google`) → AP1 그룹. 나머지는 현 parent 가 이미 AP anchor(single-ec2/edge)와 정합.
### 그룹 0 — 공유 baseline (parent `feature-keycloak-local-stack-baseline`)
| Tier-2 sub-branch | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-keycloak-docker-compose-stack` | `docker compose up` → Keycloak+PostgreSQL+nginx+Spring 4 컨테이너 healthy + KC admin 콘솔 접속 | P1 | - |
| `feature-keycloak-realm-client-export` | realm `keycloak-patterns` import + client 4개(spa-public / token-mediating-confidential / bff-confidential / edge-proxy) 등록 + 보호 endpoint 토큰없이 `401` + JSON export 재현 | P1 | `feature-keycloak-docker-compose-stack` |
### 그룹 1 — AP1 SPA-direct + Resource Server (parent `feature-keycloak-spa-direct-resource-server`)
| Tier-2 sub-branch | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-keycloak-vanilla-js-spa-pkce` | vanilla JS 가 PKCE(code_verifier/challenge)로 로그인 → access_token 수령 → `/api` `200` | P2 | baseline |
| `feature-keycloak-spring-rs-audience-validator` | Spring RS 가 JWKS 검증 + `aud` 미검증 → 타 client 토큰 통과 재현 → audience validator 로 `401` | P2 | `feature-keycloak-vanilla-js-spa-pkce` |
| `feature-keycloak-iss-claim-hostname-mismatch` | `KC_HOSTNAME` 미설정 → `iss` mismatch `401` 재현 → 설정으로 해결(로그 before/after) | P2 | `feature-keycloak-vanilla-js-spa-pkce` |
| `feature-keycloak-spa-token-storage-tradeoff` | 저장위치별 XSS surface 시연(JS 에서 토큰 read 가능 재현) + 저장 전략 결정 기록 | P3 | `feature-keycloak-vanilla-js-spa-pkce` |
| `feature-keycloak-refresh-rotation-and-logout` | refresh rotation 동작 + 로그아웃 시 세션/토큰 무효화 확인 | P3 | `feature-keycloak-spring-rs-audience-validator` |
### 그룹 2 — AP2 Token-Mediating Backend (parent `feature-keycloak-token-mediating-backend`) — 신규
| Tier-2 sub-branch | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-keycloak-token-mediating-confidential-client` | 백엔드(confidential client)가 code→token 교환 성공(client_secret) + refresh 를 서버 세션 보관 | P3 | baseline |
| `feature-keycloak-token-mediating-access-handoff` | access_token 만 브라우저 전달 → 브라우저가 RS 직접 호출 `200` + refresh 가 네트워크탭/응답 바디에 **부재** 확인 | P3 | `feature-keycloak-token-mediating-confidential-client` |
### 그룹 3 — AP3 BFF (parent `feature-keycloak-bff-session-proxy`)
| Tier-2 sub-branch | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-keycloak-bff-oauth2login-session` | Spring `oauth2Login` 로그인 → 브라우저 토큰 0개(SESSION cookie 만) + BFF proxy 경유 API `200` | P3 | baseline |
| `feature-keycloak-bff-csrf-samesite-defense` | cookie 자동첨부 CSRF 재현 → SameSite + CSRF token 으로 `403` 차단 | P3 | `feature-keycloak-bff-oauth2login-session` |
### 그룹 4 — AP4 Edge forward-auth (parent `feature-keycloak-edge-forwardauth-proxy`)
| Tier-2 sub-branch | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-keycloak-oauth2-proxy-oidc-flow` | oauth2-proxy 앞단 → 미인증 redirect → 인증 후 backend `X-Forwarded-User` `200` | P3 | baseline |
| `feature-keycloak-nginx-auth-request-integration` | nginx `auth_request` 통합 동작 + 4kb cookie 분할 함정 확인 | P3 | `feature-keycloak-oauth2-proxy-oidc-flow` |
| `feature-keycloak-header-spoofing-defense` | `X-Forwarded-User` 위조 우회 재현 → network 격리(SG/NetworkPolicy)로 차단 | P3 | `feature-keycloak-oauth2-proxy-oidc-flow` |
> (감사 정정 2026-07-14) `feature-keycloak-traefik-forwardauth-alternative` 은 impl branch 아님 — 본문이 스스로 "선택 기준 정리까지만" 이고 P3A 는 nginx+oauth2-proxy 채택. **FOLD-IN**(비교 근거)으로 강등, 아래 fold-in 목록 참조.
### 그룹 5 — Google IdP brokering cross-cutting (parent `feature-keycloak-google-idp-brokering`)
| Tier-2 sub-branch | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-keycloak-idp-brokering-google-client` | realm 에 Google IdP 등록 → Google 계정 로그인 → Keycloak 사용자 매핑 `200` + SPA/Backend diff 0줄 검증 | P4 | AP1 group |
| `feature-keycloak-first-broker-login-flow` | `email_verified=false` auto-linking 계정탈취 재현 → Confirm Link Existing Account 로 차단 | P4 | `feature-keycloak-idp-brokering-google-client` |
| `feature-keycloak-google-claim-attribute-mapping` | Google claim → Keycloak attribute 매핑(email/name) 확인 | P4 | `feature-keycloak-idp-brokering-google-client` |
| `feature-keycloak-account-linking-sub-vs-email` | 계정 linking 키 `sub` vs `email` 보안 비교 → 결정 기록 | P4 | `feature-keycloak-first-broker-login-flow` |
### 그룹 6 — 종합 (parent 없음, 최종)
| Tier-2 sub-branch | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-keycloak-four-pattern-tradeoff-matrix` | 4 패턴을 {토큰 위치 · 검증 주체 · XSS/CSRF surface · 선택 기준 · keycloak 설정} 열로 한 표에 정리 + 각 셀이 구현 branch 검증 증거 link | P5 | AP1~AP4 4개 group |
> **기존 sub-branch 흡수/승격**: 위 Tier-2 대부분은 기존 27 sub-sub-branch(§12.1 legacy)의 실 구현 승격이다. 예외 — 학습노트로 fold-in(별도 impl branch 아님): `feature-keycloak-pkce-flow-stages`·`feature-keycloak-spring-rs-role-mapping`·`feature-keycloak-refresh-token-rotation`(→ AP1 그룹 근거), `feature-keycloak-bff-vs-spa-direct`(→ AP3 비교 근거), `feature-keycloak-traefik-forwardauth-alternative`(→ AP4 비교 근거, P3A 는 nginx+oauth2-proxy 채택), `feature-keycloak-federation-spa-zero-change`·`feature-keycloak-three-leg-trust-chain`·`feature-keycloak-account-linking-spa-ux`(→ Google 그룹 근거). **AP2 그룹 2개(신규)만 완전 신규 파일.** 실제 파일 rename/re-parent 는 `wiki-doc-author mode=migrate` 로 점진(자동 mv 금지).
>
> **배포 토폴로지 sub-branch 는 documentation-only**(F5): `feature-keycloak-public-domain-tunneling`·`feature-keycloak-reverse-proxy-headers`·`feature-keycloak-https-termination-caddy-nginx`·`feature-keycloak-google-redirect-uri-policy` 는 single-EC2 실 구현 밖 배포 변형이라 §13 HTTPS termination 근거로 문서만 유지(별도 impl branch 아님). **인가(RBAC)** `feature-keycloak-idp-mappers-claim-to-role` 는 §5 deferred(authZ)로 이월.
>
> **중복 정합 완료 (2026-07-14 감사 — 각 파일에 정합 노트 삽입)**: (1) `spring-rs-role-mapping` ↔ `spring-rs-audience-validator` — Spring RS 셋업·`aud` 검증은 **audience-validator 가 owner**, role-mapping 의 role→RBAC 부분만 deferred authZ. (2) `idp-mappers-claim-to-role` ↔ `google-claim-attribute-mapping` — attribute-mapping 은 **google-claim-attribute-mapping 이 owner**, idp-mappers 의 claim→role 부분만 deferred authZ. (확인된 비-중복: account-linking sub-vs-email↔spa-ux, federation-spa-zero-change↔three-leg-trust-chain, refresh 2개 — 상호보완이라 유지.)
## 6. 본인이 한 작업 (사실만)
각 항목 옆에 증거 등급 표기:
가능한 등급: `actually-implemented` | `locally-verified` | `prod-verified` | `documented-only` | `planned` | `needs-confirmation`
- 6 패턴 분류 축 정의 (배치 × federation) — 등급: `documented-only` *(2026-07-14 인증 아키텍처 4패턴으로 축 교정됨 — 아래 참조)*
- 6 sub-branch 작성 (목표 / 다이어그램 / 토큰 sequence / 장단점) — 등급: `documented-only`
- 28 raw 외부 자료 보존 (공식 문서 + 기술블로그) — 등급: `documented-only`
- **분류축 교정 + hub §2 재편** (2026-07-14): 배치×federation 6패턴 → 인증 아키텍처 4패턴(AP1~AP4) primary + cross-cutting. 측정가능 성공기준(§1)·기술결정 소싱표(6/6)·Branch 분해표(7 branch) 추가. IETF browser-based-apps raw 보존 — 등급: `documented-only`
- AP1~AP4 실 구현 (코드) — 등급: `planned` (Branch 분해표 Phase 2~3)
## 7. 마주친 문제 / 트러블슈팅
> Phase 1(문서화) 단계에서 발견한 함정. Phase 2(P3A 실 구현) 시 마주칠 가능성 높음.
- **iss claim mismatch (단일 EC2)**:
- 원인: Keycloak `KC_HOSTNAME` 미설정 시 browser와 backend가 다른 hostname을 보고, JWT `iss` claim이 mismatch → backend JWT validation 실패.
- 해결: `KC_HOSTNAME=<hostname>` + `KC_HTTP_ENABLED=true` 명시. browser/backend 모두 같은 issuer 사용.
- **Spring Security `aud` claim 미검증 (default)**:
- 원인: Spring Security 기본 JWT validator는 `iss`, `exp`만 검증, `aud` 검증 안 함. 다른 client용 토큰이 본 backend로 흘러들 위험.
- 해결: custom `OAuth2TokenValidator<Jwt>``aud=<expected-client-id>` 검증 추가.
- **redirect_uri mismatch (localhost vs 127.0.0.1)**:
- 원인: Keycloak client 설정의 `Valid Redirect URIs``localhost`만 등록했는데 browser가 `127.0.0.1`로 접근 (또는 반대).
- 해결: 등록과 사용 hostname을 1:1 일치시키거나 둘 다 등록.
- **Google First Broker Login Flow의 email-match auto-linking 보안 위험**:
- 원인: 기본 First Broker Login Flow가 email 기반 자동 linking 옵션 제공. 그러나 Google이 email_verified=false 인 사용자 통과 가능 → 본인 외 사용자의 기존 계정 탈취 가능.
- 해결: First Broker Login Flow에 "Confirm Link Existing Account" + email_verified=true 강제 + manual confirm.
## 8. 자신 없는 부분
> 면접에서 받을 가능성이 있지만 본인이 확실히 답할 수 없는 영역. P3A 구현 + Phase 3 sub-sub-branch 학습 후 보강 예정.
- BFF (Backend-for-Frontend) 패턴 실 구현 경험 부재 — 문서만 봤음
- Keycloak SPI (Service Provider Interface)로 custom IdP 작성
- 운영 환경에서 token revocation 처리 (JWT 자체는 stateless, blacklist 필요 시)
- Keycloak multi-realm 운영 (테넌트별 realm 분리 vs 단일 realm + client별 분리)
- HTTPS termination 위치 (nginx vs Caddy vs ALB) trade-off
- Keycloak 자체의 HA 구성 (Infinispan + cluster)
## 9. 관련 자료
- 저장소 URL: `/home/donghyeon/workspace/keycloak-patterns/` (별도 git repo, **아직 비어 있음** — Phase 2 진입 시 생성)
- 관련 PR / 커밋: 없음
- canonical SSOT (본 문서): [[raw/project-notes/keycloak-patterns-overview]]
## 10. 진행 단계 (Phase)
| Phase | 내용 | 상태 |
|-------|------|------|
| **Phase 0** | 배치×federation 6패턴 정의 + 34 branch-note + 6 `.drawio` + 외부자료 보존 | ✅ 2026-05-27 완료 |
| **Phase 1** | **분류축 교정**: 인증 아키텍처 4패턴(AP1~AP4) 재편 + 측정가능 성공기준(§1) + 기술결정 소싱(6/6) + Branch 분해표(7 branch) | ✅ 2026-07-14 완료 |
| **Phase 2** | `feature-keycloak-local-stack-baseline` + AP1~AP4 E2E + signature 함정 재현 (Branch 분해표 P1~P3) | ⏳ Pending |
| **Phase 3** | Google IdP brokering cross-cutting + 4패턴 trade-off 매트릭스 (Branch 분해표 P4~P5) | ⏳ Pending |
| **Phase 4** | AP1~AP4 `locally-verified` 승급 + `wiki/projects/keycloak-patterns/` 추출 | ⏳ Pending |
| **Phase 5** (deferred) | keycloak server-side 심화 트랙 (SPI / HA / multi-realm / LDAP — §5 Deferred) | ⏳ Deferred |
## 11. wiki 추출 정책
- **Phase 4 완료 시점에 추출**: P3A의 `actually-implemented` / `locally-verified` 항목만 `wiki/projects/keycloak-patterns/`로 추출.
- **추출하지 않음**: P1A/P1B/P2A/P2B/P3B는 `documented-only` 유지, wiki/projects 승급 안 함. 단, 학습 노트 가치가 있으면 별도 `wiki/concepts/keycloak-deployment-patterns.md`로 합성 검토 (Phase 4 이후).
## 12. 묶음 (이 프로젝트에 묶이는 모든 raw 자료)
<!-- GENERATED: sources:start -->
- [[raw/company-tech-blogs/curity-bff-pattern-spa]]
- [[raw/company-tech-blogs/keycloak-google-login-codemancers]]
- [[raw/company-tech-blogs/keycloak-jwt-role-extraction-betweendata]]
- [[raw/official-docs/aws-alb-target-security-group-restriction-official]]
- [[raw/official-docs/aws-cloudfront-origin-shared-secret-header-official]]
- [[raw/official-docs/aws-security-group-referencing-official]]
- [[raw/official-docs/chrome-third-party-cookie-policy-google-official]]
- [[raw/official-docs/cloudflare-tunnel-routing-official]]
- [[raw/official-docs/docker-compose-depends-on-healthcheck]]
- [[raw/official-docs/docker-compose-networking-extra-hosts-official]]
- [[raw/official-docs/docker-engine-20-10-release-notes-official]]
- [[raw/official-docs/docker-host-network-driver-official]]
- [[raw/official-docs/docker-port-publishing-loopback-bind-official]]
- [[raw/official-docs/google-oauth-app-verification-state-overview-official]]
- [[raw/official-docs/google-oauth-manage-app-audience-official]]
- [[raw/official-docs/google-oauth2-client-application-types-official]]
- [[raw/official-docs/google-oauth2-policies-environment-separation-official]]
- [[raw/official-docs/google-oauth2-redirect-uri-validation-official]]
- [[raw/official-docs/google-oauth2-web-server-flow-official]]
- [[raw/official-docs/google-oidc-discovery-spec]]
- [[raw/official-docs/google-openid-connect-oidc]]
- [[raw/official-docs/istio-mtls-cert-rotation-official]]
- [[raw/official-docs/k8s-network-policy-official]]
- [[raw/official-docs/keycloak-2500-hostname-v2-release-official]]
- [[raw/official-docs/keycloak-2600-hostname-v1-removed-official]]
- [[raw/official-docs/keycloak-account-console-unlink-lockout-guard-official]]
- [[raw/official-docs/keycloak-client-initiated-account-linking]]
- [[raw/official-docs/keycloak-client-pkce-method-enforcement-official]]
- [[raw/official-docs/keycloak-configuring-database]]
- [[raw/official-docs/keycloak-first-broker-login-flow]]
- [[raw/official-docs/keycloak-first-broker-login-verify-authenticators-official]]
- [[raw/official-docs/keycloak-first-login-flow]]
- [[raw/official-docs/keycloak-getting-started-docker]]
- [[raw/official-docs/keycloak-google-idp-setup]]
- [[raw/official-docs/keycloak-health-checks]]
- [[raw/official-docs/keycloak-hostname-configuration]]
- [[raw/official-docs/keycloak-identity-broker-spi]]
- [[raw/official-docs/keycloak-identity-brokering-overview-official]]
- [[raw/official-docs/keycloak-identity-provider-mappers]]
- [[raw/official-docs/keycloak-identity-provider-redirector-default-idp-official]]
- [[raw/official-docs/keycloak-identity-provider-sync-mode-official]]
- [[raw/official-docs/keycloak-identity-provider-trust-email-official]]
- [[raw/official-docs/keycloak-idp-hide-on-login-page-toggle-official]]
- [[raw/official-docs/keycloak-idp-hint-client-suggested-official]]
- [[raw/official-docs/keycloak-import-export-realms]]
- [[raw/official-docs/keycloak-oidc-logout-endpoint-official]]
- [[raw/official-docs/keycloak-refresh-token-rotation-reuse-admin-official]]
- [[raw/official-docs/keycloak-refresh-token-rotation-sessions-official]]
- [[raw/official-docs/keycloak-reverseproxy-official]]
- [[raw/official-docs/keycloak-securing-apps-overview-official]]
- [[raw/official-docs/keycloak-server-containers-docker]]
- [[raw/official-docs/nginx-auth-request-module-official]]
- [[raw/official-docs/nginx-core-module-location-internal-official]]
- [[raw/official-docs/ngrok-http-tunnel-official]]
- [[raw/official-docs/oauth-v2-1-draft-ietf]]
- [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]]
- [[raw/official-docs/oauth2-pkce-rfc-7636]]
- [[raw/official-docs/oauth2-proxy-behaviour-cookie-vs-bearer-official]]
- [[raw/official-docs/oauth2-proxy-cookie-redirect-flags-official]]
- [[raw/official-docs/oauth2-proxy-endpoints-official]]
- [[raw/official-docs/oauth2-proxy-endpoints-signout-official]]
- [[raw/official-docs/oauth2-proxy-keycloak-oidc-provider-official]]
- [[raw/official-docs/oauth2-proxy-nginx-integration-official]]
- [[raw/official-docs/oauth2-proxy-overview-config-official]]
- [[raw/official-docs/oauth2-proxy-session-storage-official]]
- [[raw/official-docs/oauth2-token-revocation-rfc-7009]]
- [[raw/official-docs/oidc-client-ts-library]]
- [[raw/official-docs/openid-connect-core-id-token-validation]]
- [[raw/official-docs/owasp-html5-storage-xss-spa]]
- [[raw/official-docs/proxy-pass-request-body-nginx-official]]
- [[raw/official-docs/security-jwt-rfc-7519-validation]]
- [[raw/official-docs/silent-check-sso-third-party-cookies-keycloak-official]]
- [[raw/official-docs/spring-security-authorization-defense-in-depth]]
- [[raw/official-docs/spring-security-authorize-http-requests]]
- [[raw/official-docs/spring-security-method-security]]
- [[raw/official-docs/spring-security-nested-authorities-claim-issue-15201]]
- [[raw/official-docs/spring-security-resource-server-jwt]]
- [[raw/official-docs/third-party-cookie-blocking-safari-webkit-official]]
- [[raw/official-docs/traefik-forwardauth-middleware-official]]
- [[raw/official-docs/traefik-hub-oidc-middleware-official]]
- [[raw/official-docs/traefik-oidc-community-plugin-lukaszraczylo-official]]
<!-- GENERATED: sources:end -->
> 본 project-note는 cluster의 entry point. 모든 branch / sources / errors / interviews / lectures 가 여기로 upward link. hub 측에서도 카테고리별 명시.
### 12.1 브랜치
<!-- GENERATED: branches:start -->
- [[raw/branch-notes/feature-keycloak-account-linking-sub-vs-email]]
- [[raw/branch-notes/feature-keycloak-bff-csrf-samesite-defense]]
- [[raw/branch-notes/feature-keycloak-bff-oauth2login-session]]
- [[raw/branch-notes/feature-keycloak-docker-compose-stack]]
- [[raw/branch-notes/feature-keycloak-first-broker-login-flow]]
- [[raw/branch-notes/feature-keycloak-four-pattern-tradeoff-matrix]]
- [[raw/branch-notes/feature-keycloak-google-claim-attribute-mapping]]
- [[raw/branch-notes/feature-keycloak-header-spoofing-defense]]
- [[raw/branch-notes/feature-keycloak-idp-brokering-google-client]]
- [[raw/branch-notes/feature-keycloak-iss-claim-hostname-mismatch]]
- [[raw/branch-notes/feature-keycloak-nginx-auth-request-integration]]
- [[raw/branch-notes/feature-keycloak-oauth2-proxy-oidc-flow]]
- [[raw/branch-notes/feature-keycloak-patterns]]
- [[raw/branch-notes/feature-keycloak-realm-client-export]]
- [[raw/branch-notes/feature-keycloak-refresh-rotation-and-logout]]
- [[raw/branch-notes/feature-keycloak-spa-token-storage-tradeoff]]
- [[raw/branch-notes/feature-keycloak-spring-rs-audience-validator]]
- [[raw/branch-notes/feature-keycloak-token-mediating-access-handoff]]
- [[raw/branch-notes/feature-keycloak-token-mediating-confidential-client]]
- [[raw/branch-notes/feature-keycloak-vanilla-js-spa-pkce]]
<!-- GENERATED: branches:end -->
> generated reverse view는 child branch의 v2 contract migration 후 채운다. 아래 수기 legacy inventory는 그 전까지 navigation으로 보존한다.
> ⚠️ **Legacy inventory (구 6패턴 축).** 아래 목록은 Phase 0 의 배치×federation 구조다. **현 실행계획은 "Branch 분해 / 실행계획 (R4)" 표** — 아래 branch 들은 §2.3 매핑대로 AP1~AP4 로 re-map/승격 대상(실제 rename 은 `wiki-doc-author mode=migrate`). 신규 작업 진입점은 분해표를 따른다.
> Root branch + 6개 Tier-2 sub-branches + 27개 Tier-3 sub-sub-branches.
- **Root**: [[raw/branch-notes/feature-keycloak-patterns]] — 전체 진행 인덱스 hub
- **Tier-2 sub-branches** (구 6 패턴 → §2.3 매핑: P1x→AP4, P2x/P3x→AP1):
- [[raw/branch-notes/feature-keycloak-edge-forwardauth-no-google]] — P1A Edge / Ingress (no Google)
- [[raw/branch-notes/feature-keycloak-edge-forwardauth-google-federation]] — P1B Edge / Ingress + Google federation
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-no-google]] — P2A Cluster-internal SPA-direct (no Google)
- [[raw/branch-notes/feature-keycloak-internal-spa-direct-google-federation]] — P2B Cluster-internal + Google federation
- [[raw/branch-notes/feature-keycloak-single-ec2-no-google]] — P3A Single EC2 (no Google) — **실 구현 대상**
- [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] — P3B Single EC2 + Google federation
- **Tier-3 sub-sub-branches** (각 패턴 4~6개): root branch [[raw/branch-notes/feature-keycloak-patterns]] 의 Cluster 섹션 참조.
### 12.2 근거 자료 (프로젝트 전체 차원 foundational 조사)
- 개별 official-doc / company-tech-blog 들은 각 sub-branch 의 Sources 표에서 cited.
- [[raw/official-docs/oauth2-browser-based-apps-ietf-draft]] — IETF draft-ietf-oauth-browser-based-apps-27. **4-패턴 인증 아키텍처 taxonomy(§2.1)** 가 준거로 삼는 업계 표준 3대 아키텍처(BFF / Token-Mediating Backend / Browser-based OAuth Client, decreasing order of security) 정의의 foundational 근거. AP4(edge forward-auth)만 IETF 3종 밖 실무 확장.
### 12.3 오류 기록 (branch 외 발생한 환경·운영 이슈)
- (없음 — Phase 3 P3A 구현 진입 시 발생 예상)
### 12.4 면접 준비
- (없음 — sub-branch 별로 면접 후보 누적 후 별도 raw/interviews/ 신설 예정)
### 12.5 강의
- (없음 — 필요 시 Keycloak Summit / OIDC 강의 추가)
### 12.6 파생 wiki 문서
- canonical 검증 사실: (없음 — Phase 4 시 `wiki/projects/keycloak-patterns/` 신설)
- 관련 일반 개념: (없음 — Phase 4 이후 `wiki/concepts/keycloak-deployment-patterns.md` 검토)
- 포트폴리오: (없음)
- 블로그 글: (없음)
## 13. Phase 5 Additional Evidence Raws (2026-05-27)
> Phase 5B 외부 근거 추가 보강. HTTPS termination 결정 (P3B 의 tunnel/RP 선택, §3-1-6 의 cloudflared / ngrok / Caddy 비교) 영역에 5개 신규 raw 파일 (`raw/official-docs/` 하위) 추가. 각 raw 는 frontmatter `related_projects: [keycloak-patterns]` 보유.
>
> **출처 신뢰도 (CLAUDE.md §5 정합)**: 모두 `source_type: official-doc` (IETF RFC, OWASP cheat sheet, vendor 공식 reference). company-tech-blog 없음.
>
> **사용 경계**: 본 섹션은 raw evidence 의 cluster-level index. 각 raw 의 정확한 Claim ID / Usage Boundary 는 raw 파일 자체의 `## Claims Extracted` 섹션 참조. 본 project-note 는 owning sub-branch 에 매핑할 뿐, raw 의 verbatim claim 을 그대로 keycloak best practice 로 단정하지 않음.
### 13.1 HTTPS Termination / TLS Policy (5 raw)
P3B (Single EC2 + Google federation) 의 HTTPS termination 결정 — Google IdP 가 검증하는 `redirect_uri` 와 Keycloak issuer 가 모두 공개 HTTPS host 여야 함 (§3-1-6 핵심 함정 `KC_HOSTNAME`). 아래 5개 raw 가 termination 전략 선택지 (D2~D5) 의 외부 근거.
| raw | 채택 위치 (decision / sub-branch) | 사용 근거 |
| --- | --- | --- |
| [[raw/official-docs/rfc8996-tls10-tls11-deprecation]] | https-termination D5 (TLS 버전 policy), [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] | RFC 8996 (TLS 1.0/1.1 Deprecation, IETF 2021) 이 P3B 의 HTTPS termination (tunnel/RP 어느 쪽이든) 이 최소 TLS 1.2+ 강제 해야 하는 baseline. Google OIDC discovery endpoint 도 TLS 1.2+ 요구. |
| [[raw/official-docs/owasp-hsts-cheat-sheet]] | https-termination D5 (HSTS header policy), [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] | OWASP HSTS Cheat Sheet 가 `Strict-Transport-Security` header 의 baseline (`max-age`, `includeSubDomains`, `preload`). Caddy / Certbot+nginx / Cloudflare tunnel 어느 termination 도 HSTS 활성화 해야 함. preload 진입 결정은 branch-note 에서 별도 trade-off. |
| [[raw/official-docs/caddy-automatic-https-docs]] | https-termination D2 (Caddy option), [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] | Caddy 공식 "Automatic HTTPS" doc. P3B termination 선택지 중 정식 도메인 + Caddy 옵션 — Caddy 가 ACME (Let's Encrypt / ZeroSSL) 자동 발급·갱신·OCSP stapling 을 기본 제공. 트레이드오프: 단일 binary, config 간결성 vs nginx 운영 표준성. |
| [[raw/official-docs/certbot-user-guide]] | https-termination D3 (Certbot + nginx option), [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] | Certbot 공식 user guide. P3B termination 선택지 중 정식 도메인 + nginx + Certbot 옵션 — Certbot 이 Let's Encrypt ACME 클라이언트의 reference 구현. cron/systemd timer 기반 갱신, nginx plugin 의 in-place reload. 트레이드오프: 운영 표준성 (nginx) vs config 분리도 (Caddy 대비). |
| [[raw/official-docs/aws-acm-managed-renewal]] | https-termination D4 (AWS ALB/CloudFront option), [[raw/branch-notes/feature-keycloak-single-ec2-google-federation]] | AWS ACM Managed Renewal 공식 reference. P3B termination 선택지 중 AWS ALB / CloudFront 앞단 옵션 — ACM 이 publicly trusted cert 의 13개월 자동 갱신을 platform-side 에서 책임. EC2 내부 (Keycloak) 는 HTTP 또는 self-signed 로 충분. 트레이드오프: AWS lock-in vs 운영 부담 zero. |
**비교 매트릭스** (4개 termination option):
| 옵션 | cert 발급 자동화 | 인프라 위치 | lock-in | P3B 적합도 |
|---|---|---|---|---|
| cloudflared tunnel | Cloudflare 측 | 외부 (no inbound) | Cloudflare | 학습/dev 최적 (가장 가벼움) |
| Caddy + 도메인 | Caddy 자체 (ACME) | EC2 내 | none (open source) | 단일 binary, prod 가능 |
| nginx + Certbot + 도메인 | Certbot (cron) | EC2 내 | none | 운영 표준 (가장 친숙) |
| AWS ALB/CloudFront + ACM | ACM 자동 | AWS platform | AWS | prod 권장 (운영 부담 최소) |
**Out of scope** (P3B termination 선택 후 별도 분기): mTLS termination, FAPI 준수 termination, EV cert, multi-domain SAN, custom CA. ngrok 은 학습용 short-lived tunnel 로 cloudflared 대안 (별도 raw 미수집).
---
## 14. 아키텍처 검토 체크리스트 (작성/갱신 시 self-check)
- [x] 한 줄 요약 + 현재 상태 + 나의 역할 채워짐 (§1)
- [x] 측정 가능한 성공 기준 1개 이상 — **§1 성공 기준(측정가능, R1) 추가 (2026-07-14)**: 4 패턴 각각 E2E `200` + signature 함정 재현→해결, done-bar 정량화 완료. ✓
- [x] 아키텍처 다이어그램 1개 이상 첨부 (§3-1) — 기존 6 `.drawio` (2026-05-26, minimalist) ✓. **단 신 축 AP2·AP3 은 needs-diagram (§3-1-7) — 사용자 작성 대기.**
- [x] 다이어그램의 모든 컴포넌트가 라벨 + 역할 표기 ✓
- [x] 다이어그램의 모든 화살표가 프로토콜·데이터 종류 라벨링 ✓
- [x] 외부 시스템이 점선 + 회색으로 시각적 구분 ✓ (Google OIDC = dashed gray box)
- [x] 범례(Legend) 다이어그램 내부 + §3-1 도입부에 포함 ✓
- [x] 신뢰 경계 / 네트워크 경계 표시 ✓ (Edge zone / Internal / EC2 / Public HTTPS)
- [x] 시퀀스 다이어그램 1개 이상 (Mermaid) — happy path + error path 함께 (§3-2 P3A) ✓
- [x] Cluster 섹션의 root branch 목록 채워짐 (§12.1) ✓
- [x] 마지막 architecture review 날짜 frontmatter `architecture_review:` 에 기록 — 2026-05-26 ✓
@@ -1 +0,0 @@
../../vault/10-projects/llm-wiki-server-migration/project-notes/llm-wiki-server-migration.md
@@ -0,0 +1,568 @@
---
title: LLM Wiki Server Migration
source_type: project-note
status: draft
confidence: medium
tags: [project-note, llm-wiki, architecture, application, persistence, api-design, static-analysis]
related_projects: [llm-wiki-server-migration, llm-wiki]
last_reviewed: 2026-06-29
diagrams: []
architecture_review: 2026-06-29
status_label: active
project_revision: 1
semantic_surface_exclusions:
- artifact-registry|legacy hub has no project-local Artifact Registry; harness/source/typed-contracts.json is authoritative until migration
- contract-gate-registry|legacy hub has no project-local Contract/Gate Registry; harness/source/typed-contracts.json is authoritative until migration
- flow-stage-registry|legacy hub has no project-local Flow/Stage Registry; harness/source/typed-contracts.json is authoritative until migration
---
# LLM Wiki Server Migration
> Layer: `raw/project-notes/` (primary, hub) -> `/ingest` 후 검증된 사실은 `wiki/projects/` 로 추출.
> 본 문서는 현재 파일 기반 LLM Wiki 를 서버/API/DB/local runner 기반 시스템으로 이전하기 위한 프로젝트 hub 초안이다.
> 현재 등급은 `draft` 이며, 실제 구현·로컬 검증 전까지 외부 공개 가능한 프로젝트 성과로 취급하지 않는다.
## 1. 프로젝트 개요
- **한 줄 요약**: LLM Wiki Server Migration 은 현재 Markdown/Git 중심 LLM Wiki 를 개인용 서버, DB, desktop app, local agent runner 로 확장해 문서 작성·검증·상태 관리·스케줄링을 체계화하는 프로젝트다.
- **기간**: 2026-06-29 ~ in-progress.
- **현재 상태**: `active`.
- **나의 역할 / Role**: 설계자, 구현자, 사용자, 운영자.
- **저장소 / Repo**:
- 현재 지식 저장소: `/home/donghyeon/dev/llm-wiki-private`
- 예정 코드 저장소: 미정. 초기에는 본 repo 안의 project-note 로 요구사항을 관리하고, 구현 착수 시 별도 app repo 또는 monorepo 를 결정한다.
### 1.1 핵심 아이디어
현재 LLM Wiki 는 파일, 규칙, hook, agent skill 을 조합해 문서 품질을 관리한다. 이 방식은 Git diff 와 CLI 친화성이 강하지만, 문서 상태 추적, stale 관리, 작업 큐, dashboard, 정기 점검 같은 운영 기능은 수동 절차에 가깝다.
이 프로젝트는 LLM Wiki 를 "문서 모음"에서 "문서 운영 시스템"으로 확장한다.
```text
Desktop App / Linux App
|
v
Local Agent Runner <-- locally logged-in Codex / Claude Code / other CLI
|
v
Personal Wiki Server API
|
v
DB control plane + Git/Markdown document store
```
초기 방향은 **Git/Markdown 을 문서 원본(SSOT)으로 유지하고, DB 는 index / metadata / state / job queue / audit log 로 둔다**. DB-first 는 가능하지만, 1차 MVP 에서는 diff, rollback, agent 호환성, vault portability 를 우선한다.
## 2. 문제 정의
### 2.1 현재 상태의 문제
- **문서 상태가 파일 안에 흩어져 있다**: `status`, `confidence`, `last_reviewed`, claim coverage, broken link, stale 여부를 파일마다 읽어야 한다.
- **정적 분석 결과가 저장·추적되지 않는다**: `wiki_structure_lint.py`, coverage/depth review, forbidden word grep 결과가 일회성 command output 으로 끝난다.
- **LLM 작업의 실행 경계가 약하다**: 각 CLI agent 가 파일을 직접 읽고 수정하므로, 작업 큐, 승인 상태, 실행 로그, rollback plan 이 서버 레벨에서 관리되지 않는다.
- **문서 freshness 관리가 수동이다**: 오래된 문서, stale source, broken wikilink, 미승급 `documented-only` 항목을 주기적으로 찾아야 하지만 현재는 사람이 시작해야 한다.
- **앱 UX 가 없다**: Obsidian 과 CLI 는 강하지만, 프로젝트별 상태판, review inbox, stale queue, branch-note lifecycle 을 한 화면에서 보는 도구가 없다.
- **CLI model 인증 경계가 불분명해질 수 있다**: 서버가 개인 CLI 인증을 직접 보관하면 계정 공유, token 관리, 약관 검토 위험이 커진다.
### 2.2 왜 지금 해결해야 하는가
- **트리거**: LLM Wiki 문서 수가 늘어나면서 개별 branch-note 품질뿐 아니라 전체 문서 시스템의 lifecycle 관리가 필요해졌다.
- **비용**: 상태 추적을 수동으로 계속하면 오래된 문서가 canonical 처럼 읽히거나, LLM 이 규칙을 놓친 문서를 누적시킬 수 있다.
- **기회**: local agent runner 와 서버 API 를 분리하면 개인 CLI 로그인 상태를 유지하면서도 작업 큐, 승인, 검증, audit log 를 체계화할 수 있다.
### 2.3 성공 기준
- **S1. 문서 inventory API**: `raw/`, `wiki/` 문서의 path, source_type, status, confidence, tags, related_projects, last_reviewed 를 DB index 로 조회할 수 있다.
- **S2. deterministic gate 저장**: lint/link/tag/stale 검사 결과가 DB 에 run 단위로 저장되고, 문서별 최신 gate 상태를 조회할 수 있다.
- **S3. local runner 작업 큐**: 서버가 job 을 만들고 local runner 가 pull/execute/report 하는 흐름이 동작한다. 서버는 개인 CLI token 을 저장하지 않는다.
- **S4. approval-first patch flow**: LLM 이 만든 수정안은 바로 적용되지 않고, diff/proposal 로 저장된 뒤 사용자가 승인하면 Git working tree 에 반영된다.
- **S5. scheduled stale review**: 매일 00:00 KST 에 stale 후보를 계산하고, auto-modify 가 아니라 review inbox item 을 만든다.
- **S6. Git/Markdown portability 유지**: 서버와 DB 없이도 Markdown vault 자체가 읽히고, Git history 로 복구 가능해야 한다.
- **S7. security boundary 명시**: CLI provider 별 공식 API/SDK/CLI 허용 범위, local credential 사용 방식, 금지 automation 을 별도 branch 에서 검토한다.
<!-- section-id: architecture-components -->
## 3. 시스템 아키텍처
### 3.1 아키텍처 다이어그램 (draw.io XML)
초안 단계에서는 draw.io 파일을 아직 만들지 않았다. 첫 architecture branch 에서 `raw/diagrams/llm-wiki-server-migration/architecture-overview-YYYY-MM-DD.drawio` 를 생성한다.
현재 텍스트 구조:
```text
┌───────────────────────────┐
│ Desktop App / Linux App │
│ - dashboard │
│ - review inbox │
│ - document editor shell │
└─────────────┬─────────────┘
│ HTTPS / localhost API
v
┌───────────────────────────┐
│ Personal Wiki Server API │
│ - docs index API │
│ - job queue API │
│ - gate result API │
│ - approval workflow │
└───────┬─────────────┬─────┘
│ │
v v
┌──────────────┐ ┌──────────────────┐
│ Postgres DB │ │ Git/Markdown repo │
│ metadata │ │ document SSOT │
│ state/jobs │ │ raw/wiki files │
│ audit log │ │ commits/diff │
└──────────────┘ └──────────────────┘
^
│ job pull/report
┌───────┴───────────────────┐
│ Local Agent Runner │
│ - invokes local CLI/SDK │
│ - no central token storage │
│ - returns proposal/diff │
└───────────────────────────┘
```
> Diagram rule note: 위 블록은 임시 설명용 text sketch 이다. project-template 상 정식 시스템 아키텍처는 draw.io 로 작성해야 한다.
### 3.2 컴포넌트 책임 분담
| 컴포넌트 | 역할 | 기술 스택 후보 | 의존하는 외부 |
|---|---|---|---|
| Desktop App | dashboard, review inbox, document navigation, approval UI | Tauri 또는 Electron | Server API |
| Personal Wiki Server API | 문서 index, job queue, gate result, approval workflow, scheduler orchestration | FastAPI / Spring Boot / NestJS 중 택1 | DB, Git repo, local runner |
| DB | metadata, parsed frontmatter, link graph, claim graph, gate runs, job state, audit log | PostgreSQL 우선, SQLite MVP 가능 | Server API |
| Git/Markdown Store | 실제 문서 원본. `raw/`, `wiki/`, `rules/`, `templates/` 보존 | Git + Markdown | filesystem, optional remote |
| Local Agent Runner | 로컬 로그인 CLI/SDK 를 호출하고 proposal/diff 를 서버에 보고 | Rust/Go/Python/Node 중 택1 | Codex/Claude Code/other CLI |
| Static Gate Engine | frontmatter/link/tag/stale/forbidden-word/coverage precheck 실행 | 기존 Python hooks 재사용 | Git/Markdown Store |
| Scheduler | 매일 stale scan, periodic lint, source review job 생성 | Server internal scheduler 또는 OS scheduler | DB, Static Gate Engine |
| Policy Registry | provider 별 허용 실행 방식, secrets boundary, automation 금지사항 기록 | Markdown + DB indexed policy | official docs raw |
### 3.3 외부 의존성
| 외부 시스템 | 용도 | 통신 방식 | 장애 시 영향 |
|---|---|---|---|
| Local Codex / Claude Code / other CLI | 문서 초안, review, patch proposal 생성 | local process invocation 또는 official SDK | agent job 실패. deterministic gate 와 manual edit 는 유지 |
| Git remote | backup/sync/collaboration 후보 | Git protocol / HTTPS | remote sync 실패. local Git 은 계속 사용 가능 |
| Official vendor docs | CLI/API policy, SDK 사용 경계 근거 | manual archive to raw/official-docs | policy branch 가 `needs-confirmation` 으로 남음 |
| OS scheduler | local daily task trigger 후보 | cron / launchd / Windows Task Scheduler | server internal scheduler 로 대체 가능 |
### 3.4 배포 다이어그램
초기 배포는 개인 로컬 환경 기준이다.
- **Mode A: local-only MVP**
- server: localhost
- DB: local PostgreSQL 또는 SQLite
- Git/Markdown: local filesystem
- runner: same machine
- **Mode B: personal home server**
- server/DB: private server
- runner: user workstation
- Git/Markdown: private Git remote + local clone
- 주의: server 는 CLI credential 을 저장하지 않고 runner 에 job 을 위임한다.
<!-- section-id: runtime-flow -->
## 4. 핵심 시퀀스
<!-- section-id: sequence -->
### 4.1 문서 정리 작업 요청
**시나리오**: 사용자가 desktop app 에서 특정 project-note 정리를 요청하고, local runner 가 로컬 CLI 를 호출해 proposal 을 만든다.
```mermaid
sequenceDiagram
autonumber
actor User
participant App as Desktop App
participant API as Wiki Server API
participant DB as DB
participant Runner as Local Agent Runner
participant CLI as Local CLI Model
participant Git as Git/Markdown Repo
participant Gate as Static Gate Engine
User->>App: 문서 정리 요청
App->>API: POST /jobs {docPath, taskType}
API->>DB: INSERT job(status=queued)
Runner->>API: GET /jobs/next
API-->>Runner: job payload
Runner->>Git: read doc + related rules
Runner->>CLI: generate proposal
CLI-->>Runner: patch proposal
Runner->>API: POST /jobs/{id}/result {proposal}
API->>Git: apply patch to temp worktree
API->>Gate: run lint/link/tag checks
Gate-->>API: pass
API->>DB: save proposal(status=needs-approval)
API-->>App: review item created
User->>App: approve proposal
App->>API: POST /proposals/{id}/approve
API->>Git: apply patch in working tree
API->>DB: audit approved/applied
```
### 4.2 매일 00:00 stale review
**시나리오**: scheduler 가 오래된 문서를 자동 수정하지 않고 stale review item 을 만든다.
```mermaid
sequenceDiagram
autonumber
participant Scheduler as Scheduler
participant API as Wiki Server API
participant Gate as Static Gate Engine
participant DB as DB
participant Runner as Local Agent Runner
participant CLI as Local CLI Model
Scheduler->>API: trigger daily stale scan
API->>Gate: scan last_reviewed/status/link health
Gate-->>API: stale candidates
API->>DB: INSERT review_items
opt agent review enabled
Runner->>API: pull stale-review job
Runner->>CLI: read-only review
CLI-->>Runner: review summary
Runner->>API: attach review summary
API->>DB: update review item
end
```
### 4.3 deterministic gate before apply
**시나리오**: LLM proposal 이 적용되기 전 deterministic gate 가 최소 구조 위반을 잡는다.
```mermaid
sequenceDiagram
autonumber
actor User
participant App as Desktop App
participant API as Wiki Server API
participant Git as Git/Markdown Repo
participant Gate as Static Gate Engine
participant DB as DB
API->>Git: apply patch to temp worktree
API->>Gate: run lint/link/tag checks
alt gate pass
API-->>App: proposal ready for approval
User->>App: approve proposal
App->>API: POST /proposals/{id}/approve
API->>Git: apply patch to main working tree
API->>DB: audit status=applied
else gate fail
API->>DB: audit status=blocked + findings
API-->>App: show gate failures
end
```
## 5. 데이터 모델
초기 엔터티는 운영 상태 추적에 필요한 최소 모델로 둔다. 문서 본문은 1차 MVP 에서 Git/Markdown 이 SSOT 이며, DB 의 `document_index` 는 path 와 parsed metadata 를 저장한다.
```mermaid
erDiagram
DOCUMENT_INDEX ||--o{ DOCUMENT_VERSION_SNAPSHOT : indexes
DOCUMENT_INDEX ||--o{ LINK_EDGE : has
DOCUMENT_INDEX ||--o{ GATE_RUN : checked_by
DOCUMENT_INDEX ||--o{ REVIEW_ITEM : creates
JOB ||--o{ JOB_EVENT : records
JOB ||--o{ PROPOSAL : produces
PROPOSAL ||--o{ GATE_RUN : validated_by
PROVIDER_PROFILE ||--o{ JOB : executes
DOCUMENT_INDEX {
uuid id PK
string path
string layer
string source_type
string status
string confidence
string[] tags
date last_reviewed
string git_blob_sha
}
DOCUMENT_VERSION_SNAPSHOT {
uuid id PK
uuid document_id FK
string git_commit_sha
string content_hash
timestamp indexed_at
}
LINK_EDGE {
uuid id PK
uuid from_document_id FK
string to_path
string link_type
string status
}
GATE_RUN {
uuid id PK
uuid document_id FK
uuid proposal_id FK
string gate_name
string status
json result
timestamp ran_at
}
JOB {
uuid id PK
string task_type
string status
string target_path
uuid provider_profile_id FK
timestamp created_at
}
JOB_EVENT {
uuid id PK
uuid job_id FK
string event_type
json payload
timestamp created_at
}
PROPOSAL {
uuid id PK
uuid job_id FK
string status
string patch_ref
string summary
timestamp created_at
}
REVIEW_ITEM {
uuid id PK
uuid document_id FK
string reason
string status
timestamp due_at
}
PROVIDER_PROFILE {
uuid id PK
string provider
string execution_mode
string credential_location
}
```
## 6. 기술 결정
> **Legacy reference (v1).** 아래 비교표는 rationale을 보존한다. stable decision owner와 branch 상속 기준은 §6.1 registry다.
| 결정 영역 | 선택 | 검토한 대안 | 채택 이유 | 트레이드오프 | 근거 자료 |
|---|---|---|---|---|---|
| 문서 SSOT | 1차 MVP 는 Git/Markdown SSOT + DB index/control plane | DB-first, object storage-first | 현재 LLM Wiki 의 Git diff, Obsidian, CLI agent 호환성을 유지하기 위함 | DB 기반 rich editor 구현은 늦어진다 | 본 문서 §1.1 |
| DB 역할 | metadata / state / queue / audit log / gate result | 문서 본문 전체 저장 | 상태 질의와 문서 원본을 분리해 복구성을 높인다 | DB 와 Git index sync 필요 | 본 문서 §5 |
| agent 실행 | local runner 가 로컬 로그인 CLI/SDK 호출 | 서버가 provider token 보관, browser UI automation | credential centralization 을 피하고 사용자 로컬 환경을 활용한다 | runner 설치와 online 상태가 필요 | 별도 policy branch 필요 |
| 수정 적용 | proposal -> deterministic gate -> approval -> apply | LLM direct write, auto-commit | LLM 작성 오류와 규칙 위반을 apply 전에 차단한다 | 작업 속도는 느려진다 | 본 문서 §4.3 |
| scheduler | stale review item 생성, 자동 수정 금지 | 매일 자동 수정/커밋 | 개인 지식창고의 신뢰도를 유지하고 과잉 자동화를 피한다 | 사용자가 review inbox 를 처리해야 한다 | 본 문서 §4.2 |
| desktop app | Tauri 우선 검토 | Electron, web-only | 개인용 local integration, filesystem bridge, 가벼운 배포를 기대 | frontend/native boundary 설계 필요 | 별도 branch 필요 |
| server stack | 미정. FastAPI / Spring Boot / NestJS 비교 후 선택 | 단일 stack 선결정 | 이 문서는 project hub 이며, stack 결정은 별도 branch 에서 근거와 trade-off 를 박는다 | 초기 구현 착수 전 결정 필요 | `feature-server-stack-selection-contract` 예정 |
<!-- section-id: project-decisions -->
## 6.1 안정 결정 레지스트리
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|---|---:|---|---|---|---|---|
| `DEC-LLM-WIKI-SERVER-MIGRATION-DOCUMENT-SSOT-001` | 1 | `document-ssot` | MVP는 Git·Markdown을 document SSOT로 유지하고 DB를 index·control plane으로 사용한다 | `active` | [[raw/project-notes/llm-wiki-server-migration]] | §6 `문서 SSOT`; §1.1 |
| `DEC-LLM-WIKI-SERVER-MIGRATION-DATABASE-ROLE-001` | 1 | `database-role` | DB는 metadata·state·queue·audit log·gate result를 저장한다 | `active` | [[raw/project-notes/llm-wiki-server-migration]] | §6 `DB 역할`; §5 |
| `DEC-LLM-WIKI-SERVER-MIGRATION-AGENT-EXECUTION-001` | 1 | `agent-execution` | local runner가 locally authenticated CLI 또는 SDK를 호출한다 | `needs-confirmation` | [[raw/project-notes/llm-wiki-server-migration]] | §6 `agent 실행`; 별도 policy branch 필요 |
| `DEC-LLM-WIKI-SERVER-MIGRATION-APPLY-WORKFLOW-001` | 1 | `apply-workflow` | 변경은 proposal·deterministic gate·approval·apply 순서로 적용한다 | `active` | [[raw/project-notes/llm-wiki-server-migration]] | §6 `수정 적용`; §4.3 |
| `DEC-LLM-WIKI-SERVER-MIGRATION-SCHEDULER-001` | 1 | `scheduler` | scheduler는 stale review item만 생성하고 문서를 자동 수정하지 않는다 | `active` | [[raw/project-notes/llm-wiki-server-migration]] | §6 `scheduler`; §4.2 |
| `DEC-LLM-WIKI-SERVER-MIGRATION-DESKTOP-RUNTIME-001` | 1 | `desktop-runtime` | desktop runtime 후보는 Tauri 우선 검토이며 채택은 확정 전이다 | `needs-confirmation` | [[raw/project-notes/llm-wiki-server-migration]] | §6 `desktop app`; 별도 branch 필요 |
| `DEC-LLM-WIKI-SERVER-MIGRATION-SERVER-STACK-001` | 1 | `server-stack` | FastAPI·Spring Boot·NestJS 중 server stack을 선택한다 | `needs-confirmation` | [[raw/project-notes/llm-wiki-server-migration]] | §6 `server stack`; `feature-server-stack-selection-contract` 예정 |
<!-- section-id: implementation-boundaries -->
## 7. 비기능 요구사항
- **성능**: 1차 MVP 목표는 단일 사용자 기준 문서 5,000개 index rebuild 60초 이내, 단일 문서 gate run 5초 이내. 실제 측정 전까지 `planned`.
- **가용성**: local-only MVP 는 개인 도구이므로 SLO 를 두지 않는다. home server 모드에서는 server down 시 Git/Markdown 직접 편집이 fallback 이다.
- **확장성**: multi-user SaaS 는 범위 밖. 단일 사용자, 여러 device/runner 후보까지만 고려한다.
- **보안**: 서버는 provider personal CLI token 을 저장하지 않는다. local runner credential boundary 를 문서화한다. API 는 local-only 모드에서도 token 또는 local secret 을 둔다.
- **운영 / Observability**: job event, proposal lifecycle, gate result, scheduler run 을 audit log 로 남긴다.
- **재해 복구 / DR**: Git remote backup 을 1차 복구 수단으로 둔다. DB 는 재인덱싱 가능해야 한다.
- **컴플라이언스**: 개인용 도구이므로 외부 개인정보 처리 컴플라이언스는 1차 범위 밖. 단, secret/token/PII 가 문서에 들어갈 수 있으므로 local secret scan 은 별도 branch 후보로 둔다.
<!-- section-id: project-work-items -->
## 8.0 실행계획
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|---|---|---|---|---|---|
| `WI-LLM-WIKI-SERVER-MIGRATION-001` | `feature-repository-source-of-truth-contract` | Git-first·DB-first 결정표, rollback/fallback, sync invariant 5개 이상이 문서화된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-DOCUMENT-SSOT-001@1`, `DEC-LLM-WIKI-SERVER-MIGRATION-DATABASE-ROLE-001@1` | - | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-002` | `feature-server-stack-selection-contract` | 3개 stack 비교와 선택 기준 5개 이상을 근거와 함께 기록한다 | `DEC-LLM-WIKI-SERVER-MIGRATION-SERVER-STACK-001@1` | - | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-003` | `feature-document-metadata-data-model` | document_index·link_edge·gate_run·job·proposal schema가 migration 가능한 형태로 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-DOCUMENT-SSOT-001@1`, `DEC-LLM-WIKI-SERVER-MIGRATION-DATABASE-ROLE-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-001` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-004` | `feature-static-analysis-document-gates` | 기존 lint·link·tag·stale 검사가 server-side gate interface와 result schema로 노출된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-APPLY-WORKFLOW-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-003` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-005` | `feature-local-agent-runner-protocol` | registration·job pull·result·heartbeat·failure protocol이 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-AGENT-EXECUTION-001@1`, `DEC-LLM-WIKI-SERVER-MIGRATION-SERVER-STACK-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-002` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-006` | `feature-cli-provider-policy-boundary` | provider별 official CLI·SDK 경계와 금지 automation이 official source에 연결된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-AGENT-EXECUTION-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-005` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-007` | `feature-server-api-job-queue` | job·proposal·review-item API와 state machine이 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-DATABASE-ROLE-001@1`, `DEC-LLM-WIKI-SERVER-MIGRATION-APPLY-WORKFLOW-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-003` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-008` | `feature-desktop-review-workbench` | review inbox·document list·proposal diff·approve/reject 요구사항이 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-DESKTOP-RUNTIME-001@1`, `DEC-LLM-WIKI-SERVER-MIGRATION-APPLY-WORKFLOW-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-007` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-009` | `feature-scheduled-stale-review-automation` | 00:00 scan·review-item creation·auto-modify 금지 조건이 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-SCHEDULER-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-004` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-010` | `feature-git-sync-export-backup` | remote sync·DB reindex·Markdown export/import recovery 절차가 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-DOCUMENT-SSOT-001@1`, `DEC-LLM-WIKI-SERVER-MIGRATION-DATABASE-ROLE-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-001` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-011` | `feature-security-secrets-auth-boundary` | local API auth·runner secret·provider credential non-storage·audit masking 기준이 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-AGENT-EXECUTION-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-005` | `planned` |
| `WI-LLM-WIKI-SERVER-MIGRATION-012` | `feature-observability-audit-log-contract` | job·proposal·gate·scheduler event taxonomy와 minimum audit fields가 정의된다 | `DEC-LLM-WIKI-SERVER-MIGRATION-DATABASE-ROLE-001@1` | `WI-LLM-WIKI-SERVER-MIGRATION-007` | `planned` |
## 8.1 실행계획
> **Legacy reference (v1).** 기존 priority 표는 보존하며 stable ID·decision pin·dependency의 SSOT는 위 Work Item Registry다.
| branch slug | 달성 목표 조건 (측정가능) | 우선순위 | 의존 |
|---|---|---|---|
| `feature-repository-source-of-truth-contract` | Git-first vs DB-first 결정표, rollback/fallback 시나리오, sync invariant 5개 이상을 문서화한다 | P1 | - |
| `feature-server-stack-selection-contract` | FastAPI / Spring Boot / NestJS 후보 비교표와 선택 기준 5개 이상을 작성한다 | P1 | - |
| `feature-document-metadata-data-model` | `document_index`, `link_edge`, `gate_run`, `job`, `proposal` 스키마 초안을 migration 가능한 형태로 작성한다 | P2 | `feature-repository-source-of-truth-contract` |
| `feature-static-analysis-document-gates` | 기존 lint/link/tag/stale 검사를 server-side gate interface 로 감싸고 결과 schema 를 정의한다 | P2 | `feature-document-metadata-data-model` |
| `feature-local-agent-runner-protocol` | runner registration, job pull, result report, heartbeat, failure code protocol 을 정의한다 | P2 | `feature-server-stack-selection-contract` |
| `feature-cli-provider-policy-boundary` | Codex/Claude Code/other CLI 의 official API/SDK/CLI 사용 경계와 금지 automation 을 raw official docs 근거로 정리한다 | P2 | `feature-local-agent-runner-protocol` |
| `feature-server-api-job-queue` | job/proposal/review-item API endpoint 초안과 state machine 을 정의한다 | P3 | `feature-document-metadata-data-model` |
| `feature-desktop-review-workbench` | review inbox, document list, proposal diff, approve/reject 화면 요구사항을 정의한다 | P3 | `feature-server-api-job-queue` |
| `feature-scheduled-stale-review-automation` | 매일 00:00 stale scan 조건, review item 생성 규칙, auto-modify 금지 조건을 정의한다 | P3 | `feature-static-analysis-document-gates` |
| `feature-git-sync-export-backup` | Git remote sync, DB 재인덱싱, Markdown export/import 복구 절차를 정의한다 | P4 | `feature-repository-source-of-truth-contract` |
| `feature-security-secrets-auth-boundary` | local API auth, runner secret, provider credential non-storage, audit log masking 기준을 정의한다 | P4 | `feature-local-agent-runner-protocol` |
| `feature-observability-audit-log-contract` | job/proposal/gate/scheduler event taxonomy 와 최소 audit fields 를 정의한다 | P4 | `feature-server-api-job-queue` |
## 8. 묶음
<!-- GENERATED: sources:start -->
- [[raw/company-tech-blogs/deliberate-practice-software-developers-redgreencode]]
- [[raw/company-tech-blogs/senior-engineer-competency-mubin-shaikh]]
- [[raw/company-tech-blogs/skillable-hands-on-lab-structure]]
<!-- GENERATED: sources:end -->
### 8.1 브랜치
<!-- GENERATED: branches:start -->
<!-- GENERATED: branches:end -->
> generated reverse view는 child branch의 v2 contract migration 후 채운다. 현재 branch-note가 없다는 기존 설명은 그대로 유지한다.
아직 생성된 branch-note 없음. 위 §8.0 의 branch slug 는 실행계획이며, 실제 생성 전까지 wikilink 로 만들지 않는다.
### 8.2 근거 자료
Foundational source 는 아직 raw 로 archive 하지 않았다. 첫 source 수집 후보:
- OpenAI Codex official docs / manual — Codex CLI, SDK, MCP, non-interactive execution, credentials boundary 확인.
- Anthropic Claude Code official docs — CLI/SDK, automation, credentials boundary 확인.
- Google Gemini CLI / API official docs — local CLI/API boundary 확인.
- SQLite / PostgreSQL official docs — MVP DB 선택 근거.
- Tauri / Electron official docs — desktop app runtime 선택 근거.
### 8.3 오류 기록
- 아직 없음.
### 8.4 면접 준비
- 아직 없음. 후보 질문: "LLM 문서 시스템에서 Git-first 와 DB-first 를 어떻게 비교했는가?"
### 8.5 블로그·채용공고 연계 글감
- 아직 없음. 후보 글감: "개인 LLM Wiki 를 문서 운영 시스템으로 확장하기".
### 8.6 파생 wiki 문서
- canonical 검증 사실: 아직 없음.
- 관련 일반 개념: 아직 없음.
- 포트폴리오: 아직 없음.
- 블로그 글: 아직 없음.
## 9. 검증 등급
| 영역 | 등급 | 근거 |
|---|---|---|
| 아키텍처 다이어그램 | `planned` | draw.io 미작성 |
| 시퀀스 다이어그램 | `documented-only` | 본 문서 §4 Mermaid 초안 |
| 기술 결정 | `documented-only` | 본 문서 §6 초안. official docs raw archive 전 |
| 비기능 요구사항 | `planned` | 목표값만 있음. 측정 없음 |
| local runner policy | `needs-confirmation` | provider official docs 기반 별도 branch 필요 |
### 9.1 실제 구현 내용 (`actually-implemented`)
- 없음. 본 문서는 프로젝트 착수 초안이다.
### 9.2 로컬/dev 검증 (`locally-verified`)
- 없음.
### 9.3 운영 검증 (`prod-verified`)
- 없음.
### 9.4 문서/계획만 존재 (`documented-only`
- Git/Markdown SSOT + DB index/control plane 방향.
- Local Agent Runner 가 로컬 CLI/SDK 를 호출하고 서버가 provider token 을 저장하지 않는 경계.
- Approval-first patch flow.
- Scheduled stale review.
- Static gate result persistence.
## 10. 면접·외부 공개 답변 경계
### 10.1 자신 있게 답할 수 있는 범위
- 현재 LLM Wiki 의 한계와 서버/DB/control plane 으로 확장하려는 문제 정의.
- Git-first 와 DB-first 의 trade-off.
- local runner 로 개인 CLI 인증 경계를 분리하려는 설계 의도.
- 자동 수정이 아니라 proposal + approval + deterministic gate 를 기본으로 두는 이유.
### 10.2 적당히 답할 수 있는 범위
- desktop app 후보(Tauri/Electron/web-only) 비교 방향.
- PostgreSQL vs SQLite MVP 선택 방향.
- stale review scheduler 의 초기 정책.
### 10.3 답하면 안 되는 / 공식 문서 다시 확인 해야 하는 범위
- 특정 CLI provider 약관상 허용/금지의 확정 판단. 별도 official docs raw archive 와 policy branch 가 필요하다.
- 성능 수치 달성 여부. 아직 구현과 측정이 없다.
- 보안적으로 안전하다는 단정. credential boundary 설계와 검증 전이다.
- multi-user SaaS 로 확장 가능하다는 주장. 현재 범위는 개인용이다.
### 10.4 과장 금지 지점
- "서버로 옮겼다"라고 말하지 않는다. 현재는 project-note 초안이다.
- "AI 가 문서를 자동 관리한다"라고 말하지 않는다. 초기 방향은 review item/proposal 생성이다.
- "CLI provider 정책을 준수한다"라고 단정하지 않는다. official docs 확인 전에는 `needs-confirmation` 이다.
- "DB 가 문서 신뢰도를 보장한다"라고 말하지 않는다. 신뢰도는 evidence, deterministic gate, review process 로 관리한다.
## 11. 아키텍처 검토 체크리스트
- [x] 한 줄 요약 + 현재 상태 + 나의 역할 채워짐 (§1)
- [x] 측정 가능한 성공 기준 1개 이상 (§2.3)
- [ ] 아키텍처 다이어그램 (`.drawio.svg`) 1개 이상 첨부 (§3.1) — 미작성
- [ ] 다이어그램이 [[rules/diagram-standards]] v2 minimalist 통과 — 미검증
- [ ] 외부 시스템이 점선 + 회색 fill 로 시각적 구분 — draw.io 작성 후 확인
- [x] 시퀀스 다이어그램 1개 이상 (Mermaid) — §4 총 3개
- [x] 데이터 모델 ER 그림 — §5 초안
- [x] 주요 기술 결정 표에 트레이드오프 명시 (§6)
- [x] 비기능 요구사항 명시 (§7)
- [x] Branch 분해표 채워짐 (§8.0)
- [x] Cluster 섹션 작성 (§8)
- [x] 검증 등급 명시 (§9)
- [x] 면접 답변 경계 명시 (§10)
- [x] 마지막 architecture review 날짜 frontmatter `architecture_review:` 에 기록
## 12. 다이어그램 파일 관리 가이드
- 예정 위치: `raw/diagrams/llm-wiki-server-migration/`
- 첫 다이어그램 후보:
- `architecture-overview-2026-06-29.drawio`
- `architecture-deployment-local-2026-06-29.drawio`
- `architecture-runner-boundary-2026-06-29.drawio`
- 생성 후 frontmatter `diagrams:` 에 활성 파일을 추가한다.
## 13. 관련 개념
- [[llm-wiki]] — 전체 vault / MOC.
- [[rules/linking-rules]] — raw/wiki upward link 와 derived gate.
- [[rules/naming-conventions]] — project-note 와 branch-note naming.
- [[rules/tag-taxonomy]] — project-note tags.
- [[rules/advisory-depth]] — 권고/설계 문서의 overclaim 방지.
## 14. 다음 단계
- [ ] `feature-repository-source-of-truth-contract` branch-note 생성.
- [ ] provider official docs 를 raw/official-docs 로 archive 한 뒤 `feature-cli-provider-policy-boundary` 작성.
- [ ] draw.io architecture overview 생성.
- [ ] server stack selection branch 에서 FastAPI / Spring Boot / NestJS 비교.
- [ ] data model branch 에서 DB-first 전환 가능성을 별도 open risk 로 정리.
@@ -1 +0,0 @@
../../vault/10-projects/nplus1-presentation-prep/project-notes/nplus1-presentation-prep.md
@@ -0,0 +1,282 @@
---
title: N+1 Presentation Preparation Contract
source_type: project-note
status: raw
confidence: medium
tags: [project-note, nplus1-presentation-prep, learning, hibernate, hands-on-lab]
related_projects: [nplus1-presentation-prep, ca-tmpl]
created: 2026-07-20
last_reviewed: 2026-07-20
diagrams: [nplus1-presentation-prep/architecture-lab-loop-2026-07-20.drawio, sequence-api-replay-lab-mermaid]
architecture_review: 2026-07-20
status_label: active
project_revision: 1
semantic_surface_exclusions:
- artifact-registry|legacy hub has no project-local Artifact Registry; harness/source/typed-contracts.json is authoritative until migration
- contract-gate-registry|legacy hub has no project-local Contract/Gate Registry; harness/source/typed-contracts.json is authoritative until migration
- flow-stage-registry|legacy hub has no project-local Flow/Stage Registry; harness/source/typed-contracts.json is authoritative until migration
---
# N+1 Presentation Preparation Contract
> Layer: `raw/project-notes/` (primary, hub) → `/ingest` 후 검증된 사실만 `wiki/projects/`로 추출한다.
> 이 문서는 N+1 재현·측정·발표 준비 작업의 최상위 project hub이자 project decision/work-item SSOT다.
> `status_label`: `active`
## 1. 프로젝트 개요
- **한 줄 요약**: ca-tmpl의 feed 조회를 단계별로 재현하고, HTTP·PostgreSQL·Hibernate 관찰값을 근거 등급과 함께 설명할 수 있는 N+1 학습 랩을 만든다.
- **기간**: 2026-07-08 ~ 진행 중
- **현재 상태**: `active`
- **나의 역할 / Role**: 학습 랩 설계자·검증자·발표 준비자
- **저장소 / Repo**: ca-tmpl 로컬 저장소의 `lab/nplus1-highlight-feed`, `lab/nplus1-api-replay` Git branch를 사용한다. 원격 URL은 이 문서에서 확인하지 않았다.
## 2. 문제 정의
### 2.1 현재 상태의 문제
- 마지막 최적화 상태만 보면 lazy collection N+1부터 one-query read까지의 원인·선택·관찰값 변화를 순서대로 재현하기 어렵다.
- 테스트 결과만 읽으면 학습자가 HTTP 응답과 실제 PostgreSQL row를 함께 관찰하는 실행 경로가 드러나지 않는다.
- 로컬 측정 결과를 production 성능·배포 증거로 확대 해석할 위험이 있다.
### 2.2 왜 지금 해결해야 하는가
- **트리거**: N+1 주제를 구현 결과 나열이 아니라 재현 가능한 발표·학습 흐름으로 준비해야 한다.
- **비용**: 단계별 checkpoint와 근거 등급이 없으면 어떤 해법이 어떤 문제를 해결했는지 다시 검증하기 어렵다.
- **기회**: 동일한 관찰 루프를 반복하면 쿼리 수 최적화와 read-model 분리를 서로 다른 선택으로 비교할 수 있다.
### 2.3 성공 기준
- L1, L2, L3, L4, L5, L6, L14, L15, L16, Crown, L12의 정확히 11개 replay checkpoint가 guide와 대응한다.
- 각 checkpoint가 reset → HTTP → PostgreSQL 관찰 순서와 기대 관찰점을 가진다.
- local·Testcontainers·production 증거가 같은 등급으로 섞이지 않고 각 결과에 evidence grade가 기록된다.
- 두 직접 자식 branch가 아래 Work Item Registry의 pinned decision refs와 dependency를 그대로 상속한다.
<!-- section-id: architecture-components -->
## 3. 시스템 아키텍처
### 3.1 아키텍처 다이어그램 (draw.io XML)
**질문**: 학습자가 checkout한 N+1 checkpoint는 어떤 경로를 거쳐 검토 가능한 관찰 기록이 되는가?
![[raw/diagrams/nplus1-presentation-prep/architecture-lab-loop-2026-07-20.drawio]]
다이어그램은 Learner → Lab Checkpoint → Feed Module → PostgreSQL → Evidence Record → Presentation 경로를 나타낸다. 이는 정적 구조와 증거 승격 경계를 설명하며, 시간 순서의 세부 호출은 §4 Mermaid가 소유한다.
### 3.2 컴포넌트 책임 분담
| 컴포넌트 | 역할 | 기술 스택 | 의존하는 외부 |
|---|---|---|---|
| Learner | checkpoint를 checkout하고 관찰 절차를 실행한다 | Git, HTTP client, psql | 로컬 실행 환경 |
| Lab Checkpoint | 학습용 reset·feed 경로를 profile 안에서 노출한다 | Spring profile, HTTP API | Feed Module |
| Feed Module | checkpoint별 조회 전략을 실행한다 | Spring Data JPA, Hibernate | PostgreSQL |
| PostgreSQL | fixture row와 SQL 실행 결과를 제공한다 | PostgreSQL, Docker Compose/Testcontainers | 없음 |
| Evidence Record | 쿼리·entity load·row 관찰값과 등급을 기록한다 | Markdown, test report | 각 checkpoint 결과 |
### 3.3 외부 의존성
| 외부 시스템 | 용도 | 통신 방식 | 장애 시 영향 |
|---|---|---|---|
| Docker runtime | 로컬 PostgreSQL과 Compose/Testcontainers 실행 | local container API | DB 기반 replay와 integration evidence를 수집할 수 없다 |
| PostgreSQL | fixture·native query·row 확인 | JDBC, psql | SQL 관찰 단계가 실패하며 in-memory 결과로 대체하지 않는다 |
### 3.4 배포 다이어그램
운영 배포 토폴로지는 이 프로젝트의 검증 범위가 아니다. `lab` profile이 운영 환경에서 비활성이라는 deployment-level 증거는 아직 `needs-confirmation`이다.
<!-- section-id: runtime-flow -->
## 4. 핵심 시퀀스
<!-- section-id: sequence -->
### 4.1 API replay lab flow
**시나리오**: 학습자가 checkpoint를 checkout한 뒤 lab fixture를 reset하고 feed·DB·Hibernate 관찰값을 기록한다.
```mermaid
sequenceDiagram
autonumber
actor Learner
participant API as Lab API
participant Feed as Feed Module
participant DB as PostgreSQL
participant Stats as Hibernate Statistics
Learner->>API: POST /api/lab/feed:reset {count}
alt lab profile active and input valid
API->>DB: replace marker-owned fixture
DB-->>API: row counts
API-->>Learner: 200 reset result
Learner->>API: GET /api/lab/feed
API->>Feed: execute checkpoint strategy
Feed->>DB: SELECT feed rows
DB-->>Feed: result rows
Feed->>Stats: read statement and load counts
Stats-->>Feed: observation values
Feed-->>API: feed and observations
API-->>Learner: 200 replay result
else lab profile inactive
API-->>Learner: 404 route not registered
else input outside guard
API-->>Learner: 400 VALIDATION_FAILED
end
```
성공 경로의 수치는 checkpoint마다 다르므로 프로젝트 문서가 하나의 고정 수치를 일반화하지 않는다. 관찰값은 각 branch의 Evidence 섹션에서 환경과 함께 판정한다.
## 5. 데이터 모델
별도 프로젝트 데이터 모델을 소유하지 않는다. ca-tmpl feed model과 `created_by = nplus1-lab` marker fixture를 사용하며, 이 문서는 단계·관찰·근거 등급 계약만 소유한다.
## 6. 기술 결정
| 결정 영역 | 선택 | 검토한 대안 | 채택 이유 | 트레이드오프 | 근거 자료 |
|---|---|---|---|---|---|
| 학습 루프 | Measure→Break→Diagnose→Fix→Re-measure→Generalize | 마지막 결과만 설명 | 각 단계의 원인·수정·재측정을 연결한다 | checkpoint와 관찰 기록 유지 비용이 생긴다 | [[raw/branch-notes/experiment-nplus1-highlight-feed]] |
| 실행 substrate | ca-tmpl production substrate + profile/sibling 격리 | 독립 예제 앱 | 실제 모듈 경계를 사용하면서 학습 경로를 정상 runtime과 분리한다 | profile 오활성 여부는 별도 배포 검증이 필요하다 | [[raw/branch-notes/experiment-nplus1-feed-api-replay]] |
| 증거 등급 | local·Testcontainers 결과는 `locally-verified` | 로컬 결과를 운영 결과로 표현 | 검증 환경이 증명하는 범위를 보존한다 | production 결론에는 추가 검증이 필요하다 | [[raw/official-docs/test-taxonomy-testcontainers-official]] |
| CQRS 범위 | same-store CQRS-lite까지 | 별도 physical read store를 즉시 도입 | 쿼리 최적화와 application read-model 분리를 현재 실습 범위에서 비교한다 | full CQRS의 동기화·운영 문제는 다루지 않는다 | [[raw/official-docs/cqrs-pattern-azure-architecture-center]] |
<!-- section-id: project-decisions -->
## 6.1 안정 결정 레지스트리
> 프로젝트가 소유하는 project-wide decision SSOT다. 두 branch packet의 `Project Summary`와 byte-equivalent한 요약을 유지한다.
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|---|---:|---|---|---|---|---|
| `DEC-NPLUS1-PRESENTATION-PREP-LEARNING-001` | 1 | `learning` | Measure→Break→Diagnose→Fix→Re-measure→Generalize를 랩 완료 루프로 사용한다. | `active` | [[raw/project-notes/nplus1-presentation-prep]] | [[raw/branch-notes/experiment-nplus1-highlight-feed]] |
| `DEC-NPLUS1-PRESENTATION-PREP-SUBSTRATE-001` | 1 | `substrate` | ca-tmpl production substrate를 사용하고 학습 API·측정 경로는 profile과 sibling 경로로 격리한다. | `active` | [[raw/project-notes/nplus1-presentation-prep]] | [[raw/branch-notes/experiment-nplus1-feed-api-replay]] |
| `DEC-NPLUS1-PRESENTATION-PREP-EVIDENCE-001` | 1 | `evidence` | local·Testcontainers 결과는 locally-verified로만 기록하고 prod evidence로 승격하지 않는다. | `active` | [[raw/project-notes/nplus1-presentation-prep]] | [[raw/official-docs/test-taxonomy-testcontainers-official]] |
| `DEC-NPLUS1-PRESENTATION-PREP-SCOPE-001` | 1 | `scope` | same-store CQRS-lite까지를 현재 범위로 두고 full CQRS는 ca-tmpl contract escalation 이후에만 허용한다. | `active` | [[raw/project-notes/nplus1-presentation-prep]] | [[raw/official-docs/cqrs-pattern-azure-architecture-center]] |
<!-- section-id: implementation-boundaries -->
## 7. 비기능 요구사항
- **성능**: production RPS·P99 목표는 설정하지 않는다. checkpoint별 쿼리·entity load 관찰값만 환경과 함께 기록한다.
- **가용성**: 운영 SLO는 이 프로젝트 범위가 아니다.
- **확장성**: 로컬 단일 학습 실행만 검증 범위로 둔다.
- **보안**: 학습 reset/API는 `lab` profile에 한정하고, 정상 profile에서는 route/use case가 등록되지 않아야 한다.
- **운영 / Observability**: Hibernate Statistics, HTTP response, PostgreSQL row 확인을 같은 checkpoint evidence에 연결한다.
- **재해 복구 / DR**: 해당 없음. marker-owned local fixture는 reset으로 재생성한다.
- **컴플라이언스**: 해당 없음. production 데이터는 이 랩의 입력으로 사용하지 않는다.
<!-- section-id: project-work-items -->
## 8.0 실행계획
> 두 직접 자식 branch의 stable handoff SSOT다. Applies Decisions는 revision 1 project decisions 네 개를 모두 pin한다.
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|---|---|---|---|---|---|
| `WI-NPLUS1-PRESENTATION-PREP-001` | `experiment-nplus1-highlight-feed` | L2 ToOne EAGER 격리 측정값을 확정하고, L1~L6·L14~L16·Crown·L12의 증거 등급과 사용자 commit-range review를 본문 Closure에 반영한다. | `DEC-NPLUS1-PRESENTATION-PREP-LEARNING-001@1`, `DEC-NPLUS1-PRESENTATION-PREP-SUBSTRATE-001@1`, `DEC-NPLUS1-PRESENTATION-PREP-EVIDENCE-001@1`, `DEC-NPLUS1-PRESENTATION-PREP-SCOPE-001@1` | - | `in-progress` |
| `WI-NPLUS1-PRESENTATION-PREP-002` | `experiment-nplus1-feed-api-replay` | 11개 replay tag와 guide mapping을 고정하고, clean clone/worktree에서 Compose·HTTP·PostgreSQL smoke 및 full-check 상태를 재검증하며 lab profile의 deployment 비활성 증거를 기록한다. | `DEC-NPLUS1-PRESENTATION-PREP-LEARNING-001@1`, `DEC-NPLUS1-PRESENTATION-PREP-SUBSTRATE-001@1`, `DEC-NPLUS1-PRESENTATION-PREP-EVIDENCE-001@1`, `DEC-NPLUS1-PRESENTATION-PREP-SCOPE-001@1` | `WI-NPLUS1-PRESENTATION-PREP-001` | `in-progress` |
## 8. 묶음 (이 프로젝트에 묶이는 모든 raw 자료)
<!-- GENERATED: blog-topics:start -->
- [[raw/blog-topics/nplus1-lab-checkoutable-api-replay-2026-07-15]]
<!-- GENERATED: blog-topics:end -->
### 8.1 브랜치 (project의 직접 자식 branch)
<!-- GENERATED: branches:start -->
- [[raw/branch-notes/experiment-nplus1-feed-api-replay]]
- [[raw/branch-notes/experiment-nplus1-highlight-feed]]
<!-- GENERATED: branches:end -->
> 아래 표는 사람이 빠르게 식별하기 위한 lookup view다. 완료 조건·decision pin·dependency의 SSOT는 `## 8.0 Work Item Registry / 실행계획`이다.
| Branch | Work Item | 현재 단계 |
|---|---|---|
| `experiment-nplus1-highlight-feed` | `WI-NPLUS1-PRESENTATION-PREP-001` | `in-progress` |
| `experiment-nplus1-feed-api-replay` | `WI-NPLUS1-PRESENTATION-PREP-002` | `in-progress` |
### 8.2 근거 자료 (프로젝트 전체 차원 foundational 조사)
프로젝트에 직접 매달린 source는 없다. 각 source는 자신이 정당화하는 branch의 `## Sources / 근거`에서 추적한다.
### 8.3 오류 기록 (branch 외 발생한 환경·운영 이슈)
프로젝트에 직접 매달린 error-note는 없다. replay 과정의 오류는 해당 branch cluster가 소유한다.
### 8.4 면접 준비
프로젝트에 직접 매달린 interview-prep 문서는 없다.
### 8.5 블로그·채용공고 연계 글감
직접 자식은 없다. checkout replay 글감은 [[raw/branch-notes/experiment-nplus1-feed-api-replay]]의 child로 관리한다.
### 8.6 파생 wiki 문서
아직 생성하지 않았다. `reviewed` 이상 canonical로 승급되기 전에는 interview·portfolio·blog를 파생하지 않는다.
## 9. 검증 등급
| 영역 | 등급 | 근거 |
|---|---|---|
| 아키텍처 다이어그램 | `documented-only` | [[raw/diagrams/nplus1-presentation-prep/architecture-lab-loop-2026-07-20.drawio]] |
| 시퀀스 다이어그램 | `documented-only` | §4는 branch의 lab API·DB 관찰 경계를 요약하며 별도 실행 증거를 주장하지 않는다 |
| 기술 결정 | `documented-only` | §6.1 stable registry와 두 branch packet이 동일한 revision 1 refs를 사용한다 |
| replay 구현·로컬 측정 | `locally-verified` | [[raw/branch-notes/experiment-nplus1-feed-api-replay]]의 Docker HTTP + PostgreSQL smoke 기록 |
| 전체 checkpoint closure | `needs-confirmation` | WI-001의 L2 실측과 WI-002의 clean clone/deployment 확인이 남아 있다 |
### 9.1 실제 구현 내용 (`actually-implemented`)
- 11개 replay commit/tag와 `lab` profile의 reset·feed 관찰 경로는 branch note에 코드 존재 근거와 함께 기록되어 있다.
### 9.2 로컬/dev 검증 (`locally-verified`)
- final L12와 historical L1 checkpoint의 Docker HTTP·PostgreSQL smoke 결과는 [[raw/branch-notes/experiment-nplus1-feed-api-replay]]에 환경·명령 경계와 함께 기록되어 있다.
### 9.3 운영 검증 (`prod-verified`)
- 없음. 이 프로젝트는 production 성능·권한·배포를 검증했다고 주장하지 않는다.
### 9.4 문서/계획만 존재 (`documented-only`
- 다른 clean machine/worktree의 전체 11-stage 재현과 deployment manifest의 `lab` profile 비활성 확인은 `needs-confirmation`이다.
## 10. 면접·외부 공개 답변 경계
### 10.1 자신 있게 답할 수 있는 범위
- 각 checkout 단계에서 무엇을 측정하고 다음 단계가 어떤 문제를 다루는지 branch evidence를 근거로 설명할 수 있다.
- Crown one-query 경로와 L12 same-store CQRS-lite two-query read-model이 같은 선택이 아님을 설명할 수 있다.
### 10.2 적당히 답할 수 있는 범위
- 로컬 Docker Compose/Testcontainers에서 관찰한 SQL·HTTP 결과는 환경과 evidence grade를 함께 제시할 때만 답한다.
### 10.3 답하면 안 되는 / 공식 자료를 다시 확인해야 하는 범위
- production latency·throughput·권한 경계·다중 인스턴스 동작은 검증하지 않았다.
- 다른 환경에서 11개 tag가 모두 같은 결과를 낸다고 단정하지 않는다.
### 10.4 과장 금지 지점
- local·Testcontainers 결과를 production evidence로 표현하지 않는다.
- `addScalar` runtime mapping을 SQL compile-time 검증으로 표현하지 않는다.
- same-store CQRS-lite를 별도 read store·동기화 파이프라인을 가진 full CQRS로 표현하지 않는다.
## 11. 아키텍처 검토 체크리스트
- [x] 한 줄 요약·상태·역할을 기록했다.
- [x] 11 checkpoint와 evidence grade의 측정 가능한 성공 기준을 기록했다.
- [x] 정적 구조는 draw.io, 시간축은 Mermaid sequence diagram으로 분리했다.
- [x] Mermaid에 happy path와 profile/input error path를 함께 넣었다.
- [x] project decision 4개와 Work Item 2개를 stable ID·pinned revision으로 고정했다.
- [x] 생성 children block이 두 직접 자식 branch와 일치한다.
- [ ] draw.io에 대한 독립 `wiki-diagram-reviewer` ≥95 판정은 이 문서 작성 범위에서 수행하지 않았다.
- [ ] WI-001·WI-002의 남은 완료 조건을 충족한 뒤 project status와 evidence grade를 재검토한다.
## 12. 다이어그램 파일 관리 가이드
- 정적 구조 SSOT: `raw/diagrams/nplus1-presentation-prep/architecture-lab-loop-2026-07-20.drawio`
- 시간축 SSOT: 이 문서 §4의 Mermaid `sequenceDiagram`
- 구조 또는 흐름이 바뀌면 새 날짜의 draw.io를 추가하고 `architecture_review``last_reviewed`를 함께 갱신한다.
- 검증 환경·수치 변경은 다이어그램 안이 아니라 branch evidence와 이 문서 §9에 기록한다.
## 13. 관련 개념
- [[raw/official-docs/spring-data-jpa-projections-spring-official]] — projection/read shape의 공식 경계.
- [[raw/official-docs/cqrs-pattern-azure-architecture-center]] — same-store read/write model 분리와 별도 store CQRS의 범위 구분.
- [[raw/official-docs/test-taxonomy-testcontainers-official]] — 실제 dependency를 사용하는 integration evidence의 근거.
@@ -1 +0,0 @@
../../vault/10-projects/project-infra-overview/project-notes/project-infra-overview.md
+196
View File
@@ -0,0 +1,196 @@
---
title: 프로젝트 인프라 개요
source_type: project-note
status: raw
confidence: unknown
tags: [project-note, project-overview, infra, stub]
related_projects: []
last_reviewed:
diagrams: []
architecture_review:
status_label: stub
project_revision: 1
url:
semantic_surface_exclusions:
- artifact-registry|stub project has no project-local Artifact Registry; harness/source/typed-contracts.json is authoritative until facts are supplied
- contract-gate-registry|stub project has no project-local Contract/Gate Registry; harness/source/typed-contracts.json is authoritative until facts are supplied
- flow-stage-registry|stub project has no project-local Flow/Stage Registry; harness/source/typed-contracts.json is authoritative until facts are supplied
---
# 프로젝트 인프라 개요
> **작성 안내**
> 이 파일은 첫 `/ingest` → `/tag` → `/lint` 사이클 검증용 raw 문서입니다.
> 아래 `<...>` 자리표시자를 **본인 프로젝트의 사실**로 교체하세요.
> 일반론·추측·계획은 적지 말고 실제 한 일·확인한 것만 기록합니다.
> 작성 후 `/ingest raw/project-notes/project-infra-overview.md`로 파이프라인을 검증합니다.
>
> **관련 문서**:
> - [[CLAUDE]] — LLM Wiki 운영 규칙
> - [[llm-wiki]] — vault MOC
> - [[raw/project-notes/ca-skeleton-operational-contract]] — sister project note (ca-tmpl 운영 계약)
> - [[templates/project-template]] — `wiki/projects/` 승급 시 사용할 템플릿
---
<!-- section-id: project-decisions -->
## 6.1 안정 결정 레지스트리
> `NEEDS_CONFIRMATION`: 이 문서에는 아직 placeholder가 남아 있어서, 문서 자체 근거만으로 확정할 결정이 없다. 사실이 채워질 때까지 registry는 비워 둔다.
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|---|---:|---|---|---|---|---|
<!-- section-id: project-work-items -->
## 8.0 실행계획
> `NEEDS_CONFIRMATION`: 기존 branch decomposition row가 없으므로 stable WI를 생성하지 않는다.
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|---|---|---|---|---|---|
## 1. 프로젝트 한 줄 설명
<무엇을 만드는/만든 프로젝트인지 1–2문장>
## 2. 본인 역할
- 개인/팀 여부: <개인 프로젝트 | 팀 프로젝트 (N인)>
- 본인이 맡은 영역: <예: 백엔드 API, 인프라/배포, DB 모델링 등>
- 기간: <YYYY-MM ~ YYYY-MM>
## 3. 기술 스택
- 언어:
- 프레임워크:
- DB:
- 캐시 / 메시징:
- 인프라 / 배포:
- 모니터링 / 로깅:
- 기타:
<!-- section-id: sequence -->
## 4. 인프라 구성 요약
<어떤 환경에서 돌고 있는지. 도식이 있으면 붙이고, 없으면 글로 풀어 쓴다. 로컬/dev/staging/prod 중 어디까지 실제로 띄워 봤는지 밝힌다.>
<!-- section-id: architecture-components -->
### 4.1 시스템 아키텍처 (draw.io)
> `templates/project-template.md` §3.1 표준에 따라 작성. 저장 경로: `raw/diagrams/<project-slug>/architecture-overview-YYYY-MM-DD.drawio.svg`.
> 다이어그램이 생기면 아래 wikilink 갱신:
```markdown
실제 사용 예 (drawio 파일 생성 후 placeholder 부분을 실제 값으로 치환):
![[raw/diagrams/<project-slug>/architecture-overview-YYYY-MM-DD.drawio.svg]]
```
(아직 다이어그램 없음. drawio 생성 후 위 code block 밖으로 wikilink 빼기.)
<!-- section-id: runtime-flow -->
### 4.2 핵심 시퀀스 (Mermaid)
> 주요 user flow 1개 이상. happy path + error path 함께.
```mermaid
sequenceDiagram
autonumber
actor User
participant System
User->>System: <action>
System-->>User: <response>
```
(아직 시퀀스 미정. 작업 진입 후 채움.)
## 5. 본인이 한 작업 (사실만)
각 항목 옆에 증거 등급을 표기합니다.
가능한 등급: `actually-implemented` | `locally-verified` | `prod-verified` | `documented-only` | `planned` | `needs-confirmation`
- <작업 1 설명> — 등급: `<...>`
- <작업 2 설명> — 등급: `<...>`
- <작업 3 설명> — 등급: `<...>`
## 6. 마주친 문제 / 트러블슈팅
<실제 겪은 이슈만. 원인 → 시도 → 해결 순. 일반론 X>
- 이슈 1:
- 원인:
- 시도:
- 해결:
<!-- section-id: implementation-boundaries -->
## 7. 자신 없는 부분
<면접에서 나올 수 있지만 본인이 확실히 답하지 못하는 영역. `/interviewize`가 "모른다고 답해야 할 범위"를 정리할 때 쓴다.>
## 8. 관련 자료
- 저장소 URL:
- 관련 PR / 커밋:
- README 경로:
- 설계 문서:
## 9. 묶음 (이 프로젝트에 묶이는 모든 raw 자료)
> 본 project-note가 cluster의 entry point다. branch / errors / interviews / lectures / job-postings / sources 는 모두 여기로 upward link 를 건다. hub 쪽에서도 카테고리별로 적어 둔다.
### 9.1 브랜치 (작업 단위 hub)
<!-- GENERATED: branches:start -->
<!-- GENERATED: branches:end -->
> generated reverse view는 child branch의 v2 contract migration 후 채운다. 아래 수기 Cluster는 그 전까지 보존한다.
| Branch migration status | Reason |
|---|---|
| `NEEDS_CONFIRMATION` | placeholder를 실제 project 사실로 교체하기 전에는 branch row를 만들지 않는다 |
> 최상위 root branch들. sub-branch들은 root branch hub의 Cluster 섹션 참조.
- (없음 — 본 프로젝트 작업 시작 전. branch 생성 시 wikilink 추가.)
### 9.2 근거 자료 (프로젝트 전체 차원 foundational 조사)
- (없음)
### 9.3 오류 기록 (branch 외 발생한 환경·운영 이슈)
- (없음)
### 9.4 면접 준비 (프로젝트 전체 차원 면접 질문)
- (없음)
### 9.5 Job postings (프로젝트 관련 채용공고)
- (없음)
### 9.6 파생 wiki 문서
- canonical 검증 사실: (없음)
- 관련 일반 개념: (없음)
- 포트폴리오: (없음)
- 블로그 글: (없음)
## 10. 아키텍처 검토 체크리스트 (작성/갱신 시 self-check)
> 본 project-note가 hub 역할을 제대로 하려면 모두 ✓ 여야 함. 현재는 placeholder 상태이므로 모두 미달.
- [ ] 한 줄 요약 + 현재 상태 + 나의 역할 채워짐 (§1, §2)
- [ ] 측정 가능한 성공 기준 1개 이상
- [ ] 아키텍처 다이어그램 (`.drawio.svg`) 1개 이상 첨부 (§4.1)
- [ ] 다이어그램의 모든 컴포넌트가 라벨 + 역할 + 기술 스택 표기
- [ ] 다이어그램의 모든 화살표가 프로토콜·데이터 종류 라벨링
- [ ] 외부 시스템이 점선 또는 색으로 시각적 구분
- [ ] 범례(Legend) 다이어그램에 포함
- [ ] 신뢰 경계 / 네트워크 경계 표시
- [ ] 시퀀스 다이어그램 1개 이상 (Mermaid) — happy path + error path 함께 (§4.2)
- [ ] Cluster 섹션의 root branch 목록 채워짐 (§9.1)
- [ ] 마지막 architecture review 날짜 frontmatter `architecture_review:` 에 기록
---
> 다 쓴 뒤에도 `status`는 `raw` 그대로 둡니다(이건 raw 문서니까요). 그 상태에서 `/ingest`를 실행하면 `wiki/projects/`에 변환 문서가 만들어집니다.