- 멀티프로토콜은 기존 ResourceQueryPort/ResourceCommandPort 를 재사용해 신규 port 0개. 프로토콜은 registry 데이터이지 타입이 아니다. - Image CDN 은 I-O 가 없으므로 adapter 가 아니라 application 정책. - boot 에 capability 해석 단계 삽입 — 실패하지 않고, 비활성 adapter 는 정적 import 하지 않는다(FE-NFR-020 방어). - worker/SW 엔트리는 별도 실행 컨텍스트라 상위 layer 를 import 하지 않는다.
2542 lines
210 KiB
Markdown
2542 lines
210 KiB
Markdown
---
|
||
title: CA Skeleton Frontend Operational Contract
|
||
source_type: project-note
|
||
status: draft
|
||
confidence: medium
|
||
tags: [project-note, ca-skeleton, frontend, architecture, testing, observability, security]
|
||
related_projects: [ca-skeleton-frontend, ca-skeleton]
|
||
last_reviewed: 2026-07-18
|
||
diagrams:
|
||
- raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio
|
||
- raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio
|
||
architecture_review:
|
||
status: passed-scoped
|
||
reviewed_at: 2026-07-18
|
||
reviewer: wiki-diagram-reviewer
|
||
scores:
|
||
overview: 100
|
||
deployment: 100
|
||
scope:
|
||
overview: clean-architecture dependency ownership view
|
||
deployment: static asset and /config.json delivery slice
|
||
files:
|
||
- raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio
|
||
- raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio
|
||
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
|
||
imports: [FE-GATE-018@1, FE-GATE-026@1, FE-OC-002@1, FE-OC-003@1, FE-OC-004@1, FE-OC-005@1, FE-OC-006@1, FE-OC-007@1, FE-OC-008@1, FE-OC-009@1, FE-OC-010@1, FE-OC-011@1, FE-OC-012@1, FE-OC-013@1, FE-OC-014@1, FE-OC-015@1, FE-OC-016@1, FE-OC-017@1, FE-OC-018@1, FE-OC-019@1, FE-OC-020@1, FE-OC-021@1, FE-OC-022@1, FE-OC-023@1, FE-OC-024@1, FE-OC-025@1]
|
||
---
|
||
|
||
# CA Skeleton Frontend Operational Contract
|
||
|
||
> 이 문서는 도메인·비즈니스 기능을 제거한 frontend skeleton의 prospective operational contract다.
|
||
> 현재 LLM Wiki workspace에서 manifest·lockfile·Vite config·`src/main` entry pattern을 검색했으나 일치 파일을 찾지 못했다. frontend 구현 repository 위치는 아직 식별되지 않았다.
|
||
> test, CI, deploy artifact는 별도 전용 탐색 command를 실행하지 않았으므로 존재 여부가 `UNVERIFIED`다.
|
||
> 따라서 본문에 적힌 architecture, command, threshold, file path, component, test, runbook은 모두 `planned` 또는 `documented-only`다.
|
||
> 이 문서만으로 `actually-implemented`, `locally-verified`, `prod-verified`를 주장할 수 없다.
|
||
|
||
---
|
||
|
||
## 0. 문서 사용 계약
|
||
|
||
### 0.1 규범 키워드
|
||
|
||
이 문서의 규범 문장은 다음 의미로 사용한다.
|
||
|
||
| 키워드 | 의미 | 위반 처리 |
|
||
| --- | --- | --- |
|
||
| `MUST` | 구현과 검증에 반드시 반영할 project-wide invariant | acceptance gate 실패 |
|
||
| `MUST NOT` | 허용하지 않는 구현·운영 상태 | acceptance gate 실패 |
|
||
| `SHOULD` | 기본적으로 따르되 예외 근거와 owner 승인이 있으면 변경 가능 | risk 또는 decision row 필요 |
|
||
| `MAY` | 조건부 선택 사항 | 활성화 시 owner·test·runbook 필요 |
|
||
|
||
규범 키워드는 구현 완료 사실이 아니라 앞으로 구현이 따라야 할 계약을 뜻한다.
|
||
|
||
### 0.2 증거 등급 경계
|
||
|
||
| 등급 | 현재 허용 여부 | 이 문서에서의 의미 |
|
||
| --- | --- | --- |
|
||
| `planned` | 허용 | 목표, 기본값, command, artifact path가 문서에만 있음 |
|
||
| `documented-only` | 허용 | 근거 raw 또는 설계 문서가 있으나 대응 코드·실행 결과가 없음 |
|
||
| `actually-implemented` | 현재 금지 | repository의 구체 path와 commit이 확인되어야 함 |
|
||
| `locally-verified` | 현재 금지 | 재현 가능한 command의 exit code와 artifact가 있어야 함 |
|
||
| `prod-verified` | 현재 금지 | release ID, 운영 측정, incident 또는 dashboard evidence가 있어야 함 |
|
||
|
||
현재 workspace에서 다음 탐색은 결과가 없었다.
|
||
|
||
```bash
|
||
rg --files | rg '(^|/)(package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json|bun\.lockb?|vite\.config\.[^/]+|src/main\.(jsx|js))$'
|
||
```
|
||
|
||
이 결과가 증명하는 범위는 **현재 LLM Wiki workspace에서 위 정규식에 해당하는 entry artifact를 찾지 못했다**는 사실뿐이다. 전체 `src/`, test, CI, deploy artifact의 부재나 원격·별도 workspace의 부재로 확장 해석하지 않는다.
|
||
|
||
### 0.3 현재 판정
|
||
|
||
```text
|
||
Contract maturity: documented-only
|
||
Implementation entry evidence: searched patterns not found in current wiki workspace
|
||
Diagram files: scoped reviewer PASS — overview 100/100, deployment 100/100
|
||
Test evidence: UNVERIFIED — dedicated search/command not recorded
|
||
CI evidence: UNVERIFIED — dedicated search/command not recorded
|
||
Deployment evidence: UNVERIFIED — dedicated search/command not recorded
|
||
Readiness: NOT_READY
|
||
```
|
||
|
||
`NOT_READY`는 설계 문서가 무효라는 뜻이 아니다. 구현·검증·운영 주장을 승격할 evidence gate가 아직 닫히지 않았다는 뜻이다.
|
||
|
||
### 0.4 원래 목표 → 가정 → 조치
|
||
|
||
- **목표**: 새 frontend feature가 추가되어도 API 호출, 실패 분류, runtime validation, async UI, telemetry, release rollback을 같은 규칙으로 수행한다.
|
||
- **가정 A**: client-only SPA가 browser에서 실행되고 backend API와 분리 배포된다.
|
||
- 무효 조건: SSR, server component, edge rendering이 필수인 제품으로 범위가 바뀐다.
|
||
- 확인 방법: repository 생성 시 deployment target과 rendering mode를 `FE-D003`에 기록한다.
|
||
- **가정 B**: source language는 JavaScript ESM이며 compile-time type coverage가 제한된다.
|
||
- 무효 조건: TypeScript strict mode로 project constraint가 변경된다.
|
||
- 확인 방법: `package.json`, `jsconfig.json` 또는 `tsconfig.json`과 source extension을 확인한다.
|
||
- **가정 C**: backend가 structured JSON envelope와 stable error vocabulary를 제공하거나 frontend adapter가 이를 정규화할 수 있다.
|
||
- 무효 조건: 여러 backend가 서로 다른 protocol·schema를 제공하고 통합 adapter를 둘 수 없다.
|
||
- 확인 방법: OpenAPI 또는 captured fixture를 runtime schema와 대조한다.
|
||
- **문제**: 이 가정 아래에서 owner·default·failure·test가 없으면 page마다 다른 retry, storage, route, error UI가 생기고 release mismatch를 일관되게 복구할 수 없다.
|
||
- **조치**: stable `FE-D*`, `FE-OC-*`, registry owner, acceptance gate, runbook을 project hub에 고정하고 상세 구현은 single-owner branch로 위임한다.
|
||
- **반대 논거**: 단일 화면 prototype이라면 이 계약의 초기 비용이 기능 가치보다 클 수 있다.
|
||
- 확인 방법: route 1개, 외부 API 0개, 배포 0회인 throwaway prototype인지 확인한다.
|
||
- 처리: 그런 경우 이 skeleton을 채택하지 않고 별도 experiment로 격리한다.
|
||
|
||
### 0.5 범위
|
||
|
||
In scope:
|
||
|
||
- client-only React SPA의 boot, routing, API boundary, state, cache, storage, render failure, telemetry, build, release, rollback 계약
|
||
- JavaScript의 typecheck-equivalent gate와 runtime schema validation
|
||
- backend API 및 auth provider와 연결되는 얇은 integration port
|
||
- static hosting과 browser runtime의 failure mode
|
||
- sample feature slice를 통한 contract enforcement
|
||
|
||
Out of scope:
|
||
|
||
- domain-specific page, business rule, copy, branding, product analytics taxonomy
|
||
- token 발급, token 저장, refresh token rotation, logout propagation의 lifecycle 소유
|
||
- backend authorization 판정 대체
|
||
- SSR, RSC, edge rendering, native mobile runtime
|
||
- DB, Kafka, JVM, server thread pool, container orchestration 세부 구현
|
||
- 특정 CDN·cloud vendor의 console 절차
|
||
|
||
인증 lifecycle은 [[raw/project-notes/keycloak-patterns-overview]]가 다룬다. 본 skeleton은 외부 auth owner가 제공하는 최소 session interface만 소비한다.
|
||
|
||
---
|
||
|
||
## 1. 프로젝트 개요
|
||
|
||
### 1.1 한 줄 요약
|
||
|
||
도메인 기능 없이도 새 React SPA가 같은 architecture, API failure language, runtime validation, quality gate, release rollback을 재사용하도록 만드는 frontend operational skeleton이다.
|
||
|
||
### 1.2 현재 상태
|
||
|
||
| 항목 | 값 |
|
||
| --- | --- |
|
||
| 기간 | 2026-07-18 ~ in-progress |
|
||
| status | `draft`, `active` |
|
||
| 역할 | 설계자 / 향후 구현자 |
|
||
| implementation repository | current wiki workspace의 entry artifact search에서 미식별; remote/other workspace `UNVERIFIED` |
|
||
| architecture diagram | 2개 scoped review 100/100; implementation·full release topology는 `UNVERIFIED` |
|
||
| test / CI / deploy | dedicated evidence search/command 미기록, `UNVERIFIED` |
|
||
| 외부 공개 가능 범위 | 설계 의도·검토 대안·계약 구조만 |
|
||
|
||
### 1.3 해결하려는 문제
|
||
|
||
1. page마다 `fetch`, timeout, retry, error mapping을 다시 만들면 동일 status가 서로 다른 UX로 나타난다.
|
||
2. JavaScript boundary에 runtime validation이 없으면 malformed JSON과 schema drift가 render tree 내부의 `TypeError`로 늦게 나타난다.
|
||
3. route, env, query key, storage key, telemetry event, release token이 분산되면 rename과 rollback 영향 범위를 계산하기 어렵다.
|
||
4. build-time config와 runtime config를 구분하지 않으면 한 environment의 endpoint가 다른 release bundle에 굳어지거나 public bundle에 secret이 들어갈 수 있다.
|
||
5. hashed chunk와 HTML·runtime config가 서로 다른 release를 가리키면 `ChunkLoadError`, boot loop, stale cache가 발생할 수 있다.
|
||
6. architecture rule이 문장에만 있으면 presentation이 adapter를 직접 import하고 application port owner가 흐려진다.
|
||
|
||
### 1.4 성공 조건
|
||
|
||
아래는 목표이며 아직 측정 결과가 아니다.
|
||
|
||
| ID | 성공 조건 | 현재 상태 |
|
||
| --- | --- | --- |
|
||
| `FE-SC-001` | repository, lockfile, bootstrap command가 존재하고 fresh clone install/build가 exit 0 | `planned` |
|
||
| `FE-SC-002` | sample slice가 API → schema → mapper → application → presentation을 관통 | `planned` |
|
||
| `FE-SC-003` | 금지 import fixture가 architecture gate를 실패시킴 | `planned` |
|
||
| `FE-SC-004` | failure taxonomy의 각 blocking row에 최소 1개 automated test가 있음 | `planned` |
|
||
| `FE-SC-005` | route/API-operation/env/storage/error/query/telemetry/release registry의 ad hoc token이 0건 | `planned` |
|
||
| `FE-SC-006` | lint, checkJs, runtime-schema, unit, component, integration, e2e, a11y, build, bundle, security gate가 CI에서 분리 실행 | `planned` |
|
||
| `FE-SC-007` | release mismatch와 rollback runbook이 staging drill evidence를 남김 | `planned` |
|
||
| `FE-SC-008` | 두 draw.io 파일이 `wiki-diagram-reviewer` 기준을 통과하고 contract ID와 일치 | `documented-only` — reviewer 100/100, 구현 topology는 UNVERIFIED |
|
||
|
||
---
|
||
|
||
## 2. Stable Contract Index
|
||
|
||
### 2.1 Contract lifecycle
|
||
|
||
`FE-OC-*` ID는 rename하지 않는다. 의미가 바뀌면 기존 ID를 `superseded`로 남기고 새 ID를 추가한다. branch는 이 표를 복사해 재정의하지 않고 owner로서 상세 mechanism과 test를 제공한다.
|
||
|
||
| Contract ID | Single owner | Normative summary | Minimum evidence | Status |
|
||
| --- | --- | --- | --- | --- |
|
||
| `FE-OC-001` | project hub (this file) | 모든 구현 주장은 evidence grade를 MUST 표시하고 repo evidence가 없는 상태에서 구현 완료를 MUST NOT 주장 | evidence ledger | `documented-only` |
|
||
| `FE-OC-002` | `feature-frontend-clean-architecture-layering-contract` | `domain <- application <- presentation` 의존 방향과 application-owned output port를 MUST 지킴 | dependency rule report | `planned` |
|
||
| `FE-OC-003` | `feature-frontend-project-bootstrap-toolchain-contract` | package manager, engine, lockfile, source language, checkJs command를 한 곳에서 MUST 고정 | manifest + lockfile | `planned` |
|
||
| `FE-OC-004` | `feature-frontend-env-runtime-config-contract` | build-time, runtime-public, secret config를 MUST 분리하고 boot 전에 runtime config를 검증 | config schema test | `planned` |
|
||
| `FE-OC-005` | `feature-routing-navigation-guard-contract` | route ID/path/params/access/loading/error owner는 route registry 하나여야 함 | route registry snapshot | `planned` |
|
||
| `FE-OC-006` | `feature-api-client-response-envelope-contract` | 모든 HTTP는 shared client를 MUST 통과하고 timeout·abort·response parsing을 page에서 구현하면 안 됨 | API contract tests | `planned` |
|
||
| `FE-OC-007` | `feature-runtime-schema-validation-contract` | JSON envelope와 payload는 boundary에서 runtime schema를 MUST 통과 | schema fixtures | `planned` |
|
||
| `FE-OC-008` | `feature-frontend-error-classification-boundary-contract` | 모든 failure는 stable frontend error kind로 MUST 정규화하고 raw body·stack을 UI에 노출하면 안 됨 | error catalog tests | `planned` |
|
||
| `FE-OC-009` | `feature-api-client-response-envelope-contract` | retry는 safe/idempotent request에 한정하고 cap·jitter·`Retry-After`를 MUST 적용 | deterministic retry tests | `planned` |
|
||
| `FE-OC-010` | `feature-frontend-auth-session-integration-contract` | skeleton은 session state를 소비하되 token lifecycle을 MUST 소유하지 않음 | port contract test | `planned` |
|
||
| `FE-OC-011` | `feature-async-ui-state-contract` | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | component state matrix | `planned` |
|
||
| `FE-OC-012` | `feature-server-state-caching-contract` | query key와 invalidation은 registry factory만 MUST 사용 | cache tests | `planned` |
|
||
| `FE-OC-013` | `feature-frontend-storage-registry-contract` | storage key는 namespace·version·classification을 MUST 가지며 token/secret 저장을 금지 | storage registry tests | `planned` |
|
||
| `FE-OC-014` | `feature-frontend-observability-logging-trace-contract` | telemetry는 best-effort이며 render·API success를 차단하면 안 되고 PII·token을 전송하면 안 됨 | redaction + sink failure test | `planned` |
|
||
| `FE-OC-015` | `feature-frontend-render-recovery-boundary-contract` | expected operational error와 render defect를 MUST 분리하고 reload loop를 금지 | error boundary tests | `planned` |
|
||
| `FE-OC-016` | `feature-frontend-release-cache-rollback-contract` | HTML, asset, runtime config, release manifest cache policy를 MUST 구분 | header evidence | `planned` |
|
||
| `FE-OC-017` | `feature-frontend-release-cache-rollback-contract` | rollback은 immutable prior release로 수행하고 build/config/API compatibility를 MUST 검증 | rollback drill artifact | `planned` |
|
||
| `FE-OC-018` | `feature-frontend-build-bundle-supply-chain-contract` | frozen lockfile, dependency review, secret scan, SBOM 또는 dependency inventory를 release gate에 MUST 포함 | security artifacts | `planned` |
|
||
| `FE-OC-019` | `feature-frontend-browser-security-boundary-contract` | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | scan + lint tests | `planned` |
|
||
| `FE-OC-020` | `feature-frontend-test-taxonomy-contract` | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | CI workflow | `planned` |
|
||
| `FE-OC-021` | `feature-web-vitals-performance-budget-contract` | NFR은 device/network/cache/build context와 함께 MUST 측정 | machine-readable report | `planned` |
|
||
| `FE-OC-022` | `feature-frontend-contract-registry-governance` | 9개 registry는 single primary owner와 compatibility impact를 MUST 기록 | registry diff check | `planned` |
|
||
| `FE-OC-023` | `feature-frontend-contract-compatibility-governance` | API/config/storage/release schema의 breaking change는 migration 또는 version bump 없이 배포하면 안 됨 | compatibility report | `planned` |
|
||
| `FE-OC-024` | `feature-sample-feature-slice-contract-fixture` | sample은 contract fixture이며 production feature가 의존하면 안 됨 | sample removal smoke | `planned` |
|
||
| `FE-OC-025` | `feature-frontend-operational-runbook-contract` | boot, chunk mismatch, API degradation, telemetry failure, rollback, realtime 연결, background 실행 runbook을 MUST 유지 | drill records | `planned` |
|
||
| `FE-OC-026` | project hub (this file) | 외부 답변은 evidence grade를 MUST 보존하고 목표 수치를 측정 결과처럼 말하면 안 됨 | answer boundary checklist | `documented-only` |
|
||
| `FE-OC-027` | `feature-frontend-binary-file-io-store-contract` | 파일 선택·다운로드·로컬 바이너리 저장은 등록된 port를 MUST 경유하고, 원시 `File`/`Blob` handle과 object URL 수명은 adapter 경계를 MUST NOT 벗어남 | binary I-O fixtures | `planned` |
|
||
| `FE-OC-028` | `feature-frontend-cache-tier-cross-tab-invalidation-contract` | 캐시 계층과 탭 간 무효화는 `QueryCachePort` 정책과 registry를 MUST 경유하고, release·config·API version이 불일치하는 영속 캐시를 MUST NOT 재사용 | cache tier + cross-tab tests | `planned` |
|
||
| `FE-OC-029` | `feature-frontend-large-object-transfer-contract` | 대용량 전송은 presigned 획득과 byte 전송의 credential 경계를 MUST 분리하고, 재개 가능 전송의 part 상태·무결성·취소를 MUST 소유 | transfer fixtures | `planned` |
|
||
| `FE-OC-030` | `feature-frontend-multi-protocol-api-transport-contract` | 모든 protocol adapter는 동일한 application output port를 구현하고 protocol별 성공/실패를 정규화된 failure로 MUST 매핑하며, transport status만으로 성공을 판정하면 안 됨 | protocol mapping tests | `planned` |
|
||
| `FE-OC-031` | `feature-frontend-realtime-subscription-lifecycle-contract` | 실시간 구독은 연결·재연결·재개·이벤트 검증·해제를 MUST 계약하고, 스키마 미검증 이벤트를 application으로 MUST NOT 전달하며 unmount 후 열린 구독을 MUST NOT 남김 | realtime lifecycle tests | `planned` |
|
||
| `FE-OC-032` | `feature-frontend-background-execution-worker-contract` | 백그라운드 실행은 명시 owner·update UX·idempotency 조건을 MUST 갖추고, precaching으로 release coherence를 MUST NOT 훼손 | background execution tests | `planned` |
|
||
|
||
<!-- section-id: contract-gate-registry -->
|
||
### 2.1.1 Contract Registry (typed)
|
||
|
||
> 위 §2.1 을 기계가 읽는 형식으로 고정한 것이다. 같은 사실이며 새 계약을 만들지 않는다.
|
||
> 소비 문서는 이 표를 **복사하지 않고** frontmatter `imports` 에 `FE-OC-0NN@1` 로 pin 한다.
|
||
> owner 가 revision 을 올리면 pin 이 낡은 문서가 `STALE_IMPORTED_CONTRACT` 로 잡히고, 두 문서가 같은 계약을 소유하면 `DUPLICATE_CONTRACT_OWNER` 로 막힌다. 남의 계약 표를 다시 적으면 `FOREIGN_CONTRACT_RESTATEMENT` 다.
|
||
> `Owner` 값은 문서 slug 다. `FE-OC-001`·`FE-OC-026` 은 §20 서두가 밝힌 대로 이 project hub 가 owner 다.
|
||
> `Trigger` 는 §15.1 에서 해당 계약을 Covered FE-OC 로 가진 gate 다 — 여기서 새로 만든 값이 아니다.
|
||
> gate(`FE-GATE-*`) 행의 `Owner` 와 `Revision` 은 이 표가 SSOT 다. 각 gate 의 fixture·`Covered FE-OC`·pass condition 규범은 §15.1 이 계속 보유하며 여기로 옮기지 않는다 — 이 표는 *누가 소유하고 몇 번째 판인가*, §15.1 은 *무엇을 검사하는가* 다.
|
||
> Owner 는 §15.1 의 `Evidence artifact` 를 §20 `Measurable completion` 이 실제로 산출하는 branch 다. `FE-GATE-017` 만 검토 대상 다이어그램이 hub frontmatter `diagrams:` 소유이므로 hub 가 owner 다.
|
||
|
||
| Contract ID | Concern Key | Revision | Type | Owner | Trigger | Required Effect | Enforcement | Status |
|
||
|---|---|---|---|---|---|---|---|---|
|
||
| `FE-OC-001` | `fe.evidence-grade` | 1 | operational-contract | `ca-skeleton-frontend-operational-contract` | 구현 주장을 문서에 쓸 때 | 모든 구현 주장은 evidence grade를 MUST 표시하고 repo evidence가 없는 상태에서 구현 완료를 MUST NOT 주장 | evidence ledger | active |
|
||
| `FE-OC-002` | `fe.clean-architecture-layering` | 1 | operational-contract | `feature-frontend-clean-architecture-layering-contract` | layer 간 import 를 추가·변경할 때 | `domain <- application <- presentation` 의존 방향과 application-owned output port를 MUST 지킴 | dependency rule report | active |
|
||
| `FE-OC-003` | `fe.project-bootstrap-toolchain` | 1 | operational-contract | `feature-frontend-project-bootstrap-toolchain-contract` | toolchain·manifest·lockfile 을 변경할 때 | package manager, engine, lockfile, source language, checkJs command를 한 곳에서 MUST 고정 | manifest + lockfile | active |
|
||
| `FE-OC-004` | `fe.env-runtime-config` | 1 | operational-contract | `feature-frontend-env-runtime-config-contract` | config key 를 추가하거나 boot 순서를 바꿀 때 | build-time, runtime-public, secret config를 MUST 분리하고 boot 전에 runtime config를 검증 | config schema test | active |
|
||
| `FE-OC-005` | `fe.routing-navigation-guard` | 1 | operational-contract | `feature-routing-navigation-guard-contract` | route 를 추가·변경할 때 | route ID/path/params/access/loading/error owner는 route registry 하나여야 함 | route registry snapshot | active |
|
||
| `FE-OC-006` | `fe.api-client.shared-transport` | 1 | operational-contract | `feature-api-client-response-envelope-contract` | HTTP 요청을 보낼 때 | 모든 HTTP는 shared client를 MUST 통과하고 timeout·abort·response parsing을 page에서 구현하면 안 됨 | API contract tests | active |
|
||
| `FE-OC-007` | `fe.runtime-schema-validation` | 1 | operational-contract | `feature-runtime-schema-validation-contract` | 외부 응답을 경계에서 받을 때 | JSON envelope와 payload는 boundary에서 runtime schema를 MUST 통과 | schema fixtures | active |
|
||
| `FE-OC-008` | `fe.error-classification-boundary` | 1 | operational-contract | `feature-frontend-error-classification-boundary-contract` | failure 가 발생할 때 | 모든 failure는 stable frontend error kind로 MUST 정규화하고 raw body·stack을 UI에 노출하면 안 됨 | error catalog tests | active |
|
||
| `FE-OC-009` | `fe.api-client.retry-policy` | 1 | operational-contract | `feature-api-client-response-envelope-contract` | 요청이 실패해 재시도를 판단할 때 | retry는 safe/idempotent request에 한정하고 cap·jitter·`Retry-After`를 MUST 적용 | deterministic retry tests | active |
|
||
| `FE-OC-010` | `fe.auth-session-integration` | 1 | operational-contract | `feature-frontend-auth-session-integration-contract` | session 상태를 읽거나 갱신할 때 | skeleton은 session state를 소비하되 token lifecycle을 MUST 소유하지 않음 | port contract test | active |
|
||
| `FE-OC-011` | `fe.async-ui-state` | 1 | operational-contract | `feature-async-ui-state-contract` | async surface 를 렌더할 때 | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | component state matrix | active |
|
||
| `FE-OC-012` | `fe.server-state-caching` | 1 | operational-contract | `feature-server-state-caching-contract` | server state 를 캐시하거나 무효화할 때 | query key와 invalidation은 registry factory만 MUST 사용 | cache tests | active |
|
||
| `FE-OC-013` | `fe.storage-registry` | 1 | operational-contract | `feature-frontend-storage-registry-contract` | browser storage 에 값을 쓸 때 | storage key는 namespace·version·classification을 MUST 가지며 token/secret 저장을 금지 | storage registry tests | active |
|
||
| `FE-OC-014` | `fe.observability-logging-trace` | 1 | operational-contract | `feature-frontend-observability-logging-trace-contract` | telemetry event 를 emit 할 때 | telemetry는 best-effort이며 render·API success를 차단하면 안 되고 PII·token을 전송하면 안 됨 | redaction + sink failure test | active |
|
||
| `FE-OC-015` | `fe.render-recovery-boundary` | 1 | operational-contract | `feature-frontend-render-recovery-boundary-contract` | render 중 예외가 boundary 에 도달할 때 | expected operational error와 render defect를 MUST 분리하고 reload loop를 금지 | error boundary tests | active |
|
||
| `FE-OC-016` | `fe.release.cache-policy` | 1 | operational-contract | `feature-frontend-release-cache-rollback-contract` | release asset 을 배포하거나 cache header 를 정할 때 | HTML, asset, runtime config, release manifest cache policy를 MUST 구분 | header evidence | active |
|
||
| `FE-OC-017` | `fe.release.rollback` | 1 | operational-contract | `feature-frontend-release-cache-rollback-contract` | rollback 을 수행할 때 | rollback은 immutable prior release로 수행하고 build/config/API compatibility를 MUST 검증 | rollback drill artifact | active |
|
||
| `FE-OC-018` | `fe.build-bundle-supply-chain` | 1 | operational-contract | `feature-frontend-build-bundle-supply-chain-contract` | 의존성을 설치하거나 production build 를 만들 때 | frozen lockfile, dependency review, secret scan, SBOM 또는 dependency inventory를 release gate에 MUST 포함 | security artifacts | active |
|
||
| `FE-OC-019` | `fe.browser-security-boundary` | 1 | operational-contract | `feature-frontend-browser-security-boundary-contract` | bundle·HTML·env 에 값을 넣을 때 | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | scan + lint tests | active |
|
||
| `FE-OC-020` | `fe.test-taxonomy` | 1 | operational-contract | `feature-frontend-test-taxonomy-contract` | gate 나 fixture 를 추가·변경할 때 | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | CI workflow | active |
|
||
| `FE-OC-021` | `fe.web-vitals-performance-budget` | 1 | operational-contract | `feature-web-vitals-performance-budget-contract` | NFR 을 측정하거나 보고할 때 | NFR은 device/network/cache/build context와 함께 MUST 측정 | machine-readable report | active |
|
||
| `FE-OC-022` | `fe.contract-registry` | 1 | operational-contract | `feature-frontend-contract-registry-governance` | 9개 registry 중 하나를 변경할 때 | 9개 registry는 single primary owner와 compatibility impact를 MUST 기록 | registry diff check | active |
|
||
| `FE-OC-023` | `fe.contract-compatibility` | 1 | operational-contract | `feature-frontend-contract-compatibility-governance` | API·config·storage·release schema 를 변경할 때 | API/config/storage/release schema의 breaking change는 migration 또는 version bump 없이 배포하면 안 됨 | compatibility report | active |
|
||
| `FE-OC-024` | `fe.sample-feature-slice-contract` | 1 | operational-contract | `feature-sample-feature-slice-contract-fixture` | sample slice 를 만들거나 제거할 때 | sample은 contract fixture이며 production feature가 의존하면 안 됨 | sample removal smoke | active |
|
||
| `FE-OC-025` | `fe.operational-runbook` | 1 | operational-contract | `feature-frontend-operational-runbook-contract` | 운영 장애가 발생하거나 drill 을 돌릴 때 | boot, chunk mismatch, API degradation, telemetry failure, rollback, realtime 연결, background 실행 runbook을 MUST 유지 | drill records | active |
|
||
| `FE-OC-026` | `fe.answer-boundary` | 1 | operational-contract | `ca-skeleton-frontend-operational-contract` | 외부 공개 답변을 작성할 때 | 외부 답변은 evidence grade를 MUST 보존하고 목표 수치를 측정 결과처럼 말하면 안 됨 | answer boundary checklist | active |
|
||
| `FE-OC-027` | `fe.binary-file-io-store` | 1 | operational-contract | `feature-frontend-binary-file-io-store-contract` | 파일을 고르거나 내려받거나 바이너리를 로컬에 쓸 때 | 파일 선택·다운로드·로컬 바이너리 저장은 등록된 port를 MUST 경유하고, 원시 `File`/`Blob` handle과 object URL 수명은 adapter 경계를 MUST NOT 벗어남 | binary I-O fixtures | active |
|
||
| `FE-OC-028` | `fe.cache-tier-cross-tab` | 1 | operational-contract | `feature-frontend-cache-tier-cross-tab-invalidation-contract` | 캐시를 영속화하거나 다른 탭에 무효화를 전파할 때 | 캐시 계층과 탭 간 무효화는 `QueryCachePort` 정책과 registry를 MUST 경유하고, release·config·API version이 불일치하는 영속 캐시를 MUST NOT 재사용 | cache tier + cross-tab tests | active |
|
||
| `FE-OC-029` | `fe.large-object-transfer` | 1 | operational-contract | `feature-frontend-large-object-transfer-contract` | 대용량 객체를 올리거나 스트리밍으로 내려받을 때 | 대용량 전송은 presigned 획득과 byte 전송의 credential 경계를 MUST 분리하고, 재개 가능 전송의 part 상태·무결성·취소를 MUST 소유 | transfer fixtures | active |
|
||
| `FE-OC-030` | `fe.multi-protocol-transport` | 1 | operational-contract | `feature-frontend-multi-protocol-api-transport-contract` | REST 이외 protocol 로 operation 을 호출할 때 | 모든 protocol adapter는 동일한 application output port를 구현하고 protocol별 성공/실패를 정규화된 failure로 MUST 매핑하며, transport status만으로 성공을 판정하면 안 됨 | protocol mapping tests | active |
|
||
| `FE-OC-031` | `fe.realtime-subscription-lifecycle` | 1 | operational-contract | `feature-frontend-realtime-subscription-lifecycle-contract` | 스트림을 구독하거나 해제할 때 | 실시간 구독은 연결·재연결·재개·이벤트 검증·해제를 MUST 계약하고, 스키마 미검증 이벤트를 application으로 MUST NOT 전달하며 unmount 후 열린 구독을 MUST NOT 남김 | realtime lifecycle tests | active |
|
||
| `FE-OC-032` | `fe.background-execution` | 1 | operational-contract | `feature-frontend-background-execution-worker-contract` | worker·service worker·background sync 를 등록하거나 갱신할 때 | 백그라운드 실행은 명시 owner·update UX·idempotency 조건을 MUST 갖추고, precaching으로 release coherence를 MUST NOT 훼손 | background execution tests | active |
|
||
| `FE-GATE-001` | `fe.gate.manifest-lockfile` | 1 | gate | `feature-frontend-project-bootstrap-toolchain-contract` | 의존성을 설치하거나 lockfile 을 변경할 때 | lockfile 이 manifest 와 어긋나면 merge·release 를 MUST 차단 | install log | active |
|
||
| `FE-GATE-002` | `fe.gate.lint` | 1 | gate | `feature-frontend-architecture-enforcement-lint-contract` | 소스를 수정해 merge 를 요청할 때 | 금지된 API·import 가 남아 있으면 merge 를 MUST 차단 | lint report | active |
|
||
| `FE-GATE-003` | `fe.gate.typecheck` | 1 | gate | `feature-frontend-project-bootstrap-toolchain-contract` | 타입 주석이나 checkJs 설정을 변경할 때 | production diagnostic 이 남아 있으면 merge 를 MUST 차단 | check-types report | active |
|
||
| `FE-GATE-004` | `fe.gate.runtime-schema` | 1 | gate | `feature-runtime-schema-validation-contract` | 경계에서 외부 응답·boot config 를 받을 때 | invalid fixture 가 예상 kind 로 거부되지 않으면 merge 를 MUST 차단 | schema + timing report | active |
|
||
| `FE-GATE-005` | `fe.gate.unit` | 1 | gate | `feature-frontend-test-taxonomy-contract` | unit 레벨 테스트를 추가·변경할 때 | unit 레벨이 실패하면 merge 를 MUST 차단하고 warning 으로 낮추면 안 됨 | unit XML | active |
|
||
| `FE-GATE-006` | `fe.gate.component` | 1 | gate | `feature-frontend-test-taxonomy-contract` | component 레벨 테스트를 추가·변경할 때 | component 레벨이 실패하면 merge 를 MUST 차단 | component XML | active |
|
||
| `FE-GATE-007` | `fe.gate.integration` | 1 | gate | `feature-frontend-test-taxonomy-contract` | integration 레벨 테스트를 추가·변경할 때 | MSW 기반 integration 매트릭스가 미충족이면 merge 를 MUST 차단 | integration XML | active |
|
||
| `FE-GATE-008` | `fe.gate.e2e` | 1 | gate | `feature-frontend-test-taxonomy-contract` | critical 사용자 시나리오를 변경할 때 | critical e2e 시나리오가 실패하면 merge·release 를 MUST 차단 | Playwright report | active |
|
||
| `FE-GATE-009` | `fe.gate.accessibility` | 1 | gate | `feature-accessibility-baseline-contract` | sample route 의 UI 를 변경할 때 | automated threshold 미달이거나 manual checklist 서명이 없으면 merge·release 를 MUST 차단 | a11y artifacts | active |
|
||
| `FE-GATE-010` | `fe.gate.architecture` | 1 | gate | `feature-frontend-architecture-enforcement-lint-contract` | layer 간 import 를 추가·변경할 때 | 금지된 layer import 가 통과하면 merge 를 MUST 차단 | dependency report | active |
|
||
| `FE-GATE-011` | `fe.gate.build` | 1 | gate | `feature-frontend-build-bundle-supply-chain-contract` | production build 를 만들 때 | clean production build 가 실패하거나 기대 artifact 가 없으면 merge·release 를 MUST 차단 | build manifest | active |
|
||
| `FE-GATE-012` | `fe.gate.bundle` | 1 | gate | `feature-frontend-build-bundle-supply-chain-contract` | 번들 구성이나 chunk 분할을 바꿀 때 | 번들 NFR threshold 초과면 release 를 MUST 차단 | bundle report | active |
|
||
| `FE-GATE-013` | `fe.gate.security` | 1 | gate | `feature-frontend-build-bundle-supply-chain-contract` | 의존성·시크릿·라이선스 표면을 변경할 때 | secret·vulnerability·license·dependency review 정책 위반이면 merge·release 를 MUST 차단 | SARIF/inventory/dependency diff report | active |
|
||
| `FE-GATE-014` | `fe.gate.config-compatibility` | 1 | gate | `feature-frontend-contract-compatibility-governance` | config schema 를 변경해 release 할 때 | 지원 대상 config 버전이 boot 에 실패하면 release 를 MUST 차단 | compatibility report | active |
|
||
| `FE-GATE-015` | `fe.gate.release-coherence` | 1 | gate | `feature-frontend-release-cache-rollback-contract` | HTML·asset·config 를 한 release 로 묶을 때 | 혼재된 release 조합이 감지되지 않으면 release 를 MUST 차단 | release verification | active |
|
||
| `FE-GATE-016` | `fe.gate.rollback-drill` | 1 | gate | `feature-frontend-release-cache-rollback-contract` | 직전 release 로 되돌릴 때 | rollback 과 smoke 증거가 없으면 production promotion 을 MUST 차단 | drill record | active |
|
||
| `FE-GATE-017` | `fe.gate.diagram-review` | 1 | gate | `ca-skeleton-frontend-operational-contract` | hub 소유 아키텍처 다이어그램을 갱신할 때 | scoped 다이어그램 2종이 reviewer threshold 미달이면 documentation readiness 를 MUST 차단 | reviewer report | active |
|
||
| `FE-GATE-018` | `fe.gate.field-web-vitals` | 1 | gate | `feature-web-vitals-performance-budget-contract` | field 측정 창을 마감해 보고할 때 | p75 목표 미달이거나 표본 임계가 미해결이면 field readiness 를 MUST 차단 | field Web Vitals report | active |
|
||
| `FE-GATE-019` | `fe.gate.hosting-header` | 2 | gate | `feature-frontend-release-cache-rollback-contract` | hosting 의 header(cache·security) 설정을 배포할 때 | 선언한 Cache-Control·content-type·security header 와 실제 응답이 다르면 release 를 MUST 차단 | hosting header report | active |
|
||
| `FE-GATE-020` | `fe.gate.sample-removal` | 1 | gate | `feature-sample-feature-slice-contract-fixture` | sample slice 를 제거하거나 제품이 참조할 때 | sample 제거 후 build·smoke 가 실패하면 merge·release 를 MUST 차단 | sample-removal report | active |
|
||
| `FE-GATE-021` | `fe.gate.runbook-boot-config` | 1 | gate | `feature-frontend-operational-runbook-contract` | boot config 실패 drill 을 돌릴 때 | `FE-RB-001` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-001` record | active |
|
||
| `FE-GATE-022` | `fe.gate.runbook-chunk-mismatch` | 1 | gate | `feature-frontend-operational-runbook-contract` | chunk·release manifest 실패 drill 을 돌릴 때 | `FE-RB-002` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-002` record | active |
|
||
| `FE-GATE-023` | `fe.gate.runbook-api-degradation` | 1 | gate | `feature-frontend-operational-runbook-contract` | API degradation drill 을 돌릴 때 | `FE-RB-003` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-003` record | active |
|
||
| `FE-GATE-024` | `fe.gate.runbook-telemetry` | 1 | gate | `feature-frontend-operational-runbook-contract` | telemetry degradation drill 을 돌릴 때 | `FE-RB-004` 의 containment·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-004` record | active |
|
||
| `FE-GATE-025` | `fe.gate.runbook-release-rollback` | 1 | gate | `feature-frontend-operational-runbook-contract` | release 차단 결함으로 rollback 을 판단할 때 | `FE-RB-005` 의 rollback 결정·escalation·recovery 단언이 실패하면 production promotion 을 MUST 차단 | `FE-RB-005` record | active |
|
||
| `FE-GATE-026` | `fe.gate.lab-performance` | 1 | gate | `feature-web-vitals-performance-budget-contract` | lab 성능을 측정해 보고할 때 | lab threshold 미달이거나 재현 메타데이터가 없으면 release 를 MUST 차단 | lab performance report | active |
|
||
| `FE-GATE-027` | `fe.gate.binary-file-io` | 1 | gate | `feature-frontend-binary-file-io-store-contract` | 파일 I-O 또는 로컬 바이너리 저장 코드를 변경할 때 | picker·다운로드·quota·object URL 해제 fixture 가 실패하면 merge 를 MUST 차단 | binary I-O report | active |
|
||
| `FE-GATE-028` | `fe.gate.cache-tier-cross-tab` | 1 | gate | `feature-frontend-cache-tier-cross-tab-invalidation-contract` | 캐시 영속화나 탭 간 전파를 변경할 때 | version 파티션·탭 간 무효화 fixture 가 실패하면 merge 를 MUST 차단 | cache tier report | active |
|
||
| `FE-GATE-029` | `fe.gate.large-object-transfer` | 1 | gate | `feature-frontend-large-object-transfer-contract` | 대용량 전송 경로를 변경할 때 | presign 만료·part 재시도·무결성·credential 경계 fixture 가 실패하면 merge·release 를 MUST 차단 | transfer report | active |
|
||
| `FE-GATE-030` | `fe.gate.multi-protocol-transport` | 1 | gate | `feature-frontend-multi-protocol-api-transport-contract` | protocol adapter 나 codec 을 변경할 때 | protocol 별 성공/실패 정규화 fixture 가 실패하면 merge 를 MUST 차단 | protocol mapping report | active |
|
||
| `FE-GATE-031` | `fe.gate.realtime-lifecycle` | 1 | gate | `feature-frontend-realtime-subscription-lifecycle-contract` | 실시간 연결·구독 코드를 변경할 때 | backoff·resume·구독 해제·이벤트 검증 fixture 가 실패하면 merge·release 를 MUST 차단 | realtime lifecycle report | active |
|
||
| `FE-GATE-032` | `fe.gate.background-execution` | 1 | gate | `feature-frontend-background-execution-worker-contract` | worker·service worker·background sync 를 변경할 때 | SW update UX·sync idempotency·worker timeout fixture 가 실패하면 merge·release 를 MUST 차단 | background execution report | active |
|
||
| `FE-GATE-033` | `fe.gate.capability-default-off` | 1 | gate | `feature-frontend-env-runtime-config-contract` | capability flag 나 adapter 등록을 변경할 때 | 기본 config build 에 비활성 capability 의 adapter 가 포함되면 merge·release 를 MUST 차단 | capability bundle report | active |
|
||
|
||
<!-- section-id: artifact-registry -->
|
||
### 2.1.3 Artifact Registry (typed)
|
||
|
||
> 두 개 이상의 branch 가 같은 파일의 필드를 **각자** 정하고 있던 artifact 만 등록한다. 단일 branch 전용 artifact 는 desync 원인이 아니므로 넣지 않는다.
|
||
> `Schema Ref` 는 실제 JSON Schema 파일이며 검사기가 존재를 확인한다. 필드 추가·rename 은 `Schema Owner` 단독 결정이고, 소비 branch 는 본문에 스키마를 옮겨 적지 않고 frontmatter `imports` 에 `ART-FE-0NN@1` 로 pin 한다.
|
||
> **JSON artifact 필드 명명은 camelCase** 로 통일한다 — `artifacts/**` 의 report 파일에 한하며, telemetry attribute 어휘(§11.1 allowlist, snake_case)는 별개 규약이다.
|
||
|
||
| Artifact ID | Revision | Name | Schema Owner | Producer | Consumers | Schema Ref | Status |
|
||
|---|---|---|---|---|---|---|---|
|
||
| `ART-FE-001` | 1 | build manifest | `feature-frontend-project-bootstrap-toolchain-contract` | `feature-frontend-project-bootstrap-toolchain-contract` | `feature-frontend-build-bundle-supply-chain-contract`, `feature-frontend-release-cache-rollback-contract`, `feature-frontend-test-taxonomy-contract` | `harness/source/artifact-schemas/ca-skeleton-frontend/build-manifest.schema.json` | active |
|
||
| `ART-FE-002` | 1 | bundle report | `feature-frontend-build-bundle-supply-chain-contract` | `feature-frontend-build-bundle-supply-chain-contract` | `feature-web-vitals-performance-budget-contract`, `feature-frontend-test-taxonomy-contract` | `harness/source/artifact-schemas/ca-skeleton-frontend/bundle-report.schema.json` | active |
|
||
| `ART-FE-003` | 1 | release verification | `feature-frontend-release-cache-rollback-contract` | `feature-frontend-release-cache-rollback-contract` | `feature-frontend-contract-compatibility-governance` | `harness/source/artifact-schemas/ca-skeleton-frontend/release-verification.schema.json` | active |
|
||
| `ART-FE-004` | 1 | a11y report | `feature-accessibility-baseline-contract` | `feature-accessibility-baseline-contract` | `feature-frontend-test-taxonomy-contract` | `harness/source/artifact-schemas/ca-skeleton-frontend/a11y-report.schema.json` | active |
|
||
|
||
<!-- section-id: flow-stage-registry -->
|
||
### 2.1.4 Flow Stage Registry (typed)
|
||
|
||
> §7.3 응답 처리 순서 8단계에 **단계별 owner** 를 붙인 것이다. 순서 자체는 §7.3 이 계속 소유하고, 이 표는 *각 단계를 누가 소유하며 그 단계가 지켜야 할 불변식이 무엇인가* 를 고정한다.
|
||
> 이 표가 없을 때 stage 4~6 의 throw/non-throw 경계와 stage 7 산출물(model vs view-model)이 branch 마다 다르게 적혀 있었다. 단계 계약을 바꾸려면 owner 가 revision 을 올리고, 인접 단계 branch 는 pin 이 낡아 `STALE_IMPORTED_CONTRACT` 로 잡힌다.
|
||
> `FLOW-FE-RESP-*` 는 요청/응답 한 번의 여정이고 `FLOW-FE-EVENT-*` 는 인바운드 프레임 하나의 여정이다. 두 흐름은 3~4단계에서 같은 runtime schema owner 를 공유하지만 순서를 합치지 않는다 — 스트림에는 요청이 없고 응답에는 재개 커서가 없다. 구독 해제(cleanup)는 어느 흐름도 소유하지 않고 `RealtimeSubscriptionPort` 계약이 소유한다.
|
||
|
||
| Stage ID | Order | Owner | Input | Action | Output | Invariants | Revision |
|
||
|---|---:|---|---|---|---|---|---|
|
||
| `FLOW-FE-RESP-001` | 1 | `feature-api-client-response-envelope-contract` | HTTP 요청 | transport 완료 대기 | raw Response | timeout·abort 는 이 단계가 소유하고 이후 단계로 예외를 넘기지 않는다 | 1 |
|
||
| `FLOW-FE-RESP-002` | 2 | `feature-api-client-response-envelope-contract` | raw Response | content-type 기대값 검사 | 본문 판독 가능 Response | 기대와 다르면 본문을 파싱하지 않고 실패로 전환 | 1 |
|
||
| `FLOW-FE-RESP-003` | 3 | `feature-api-client-response-envelope-contract` | 본문 판독 가능 Response | JSON parse | unvalidated JSON | parse 실패는 raw body 를 버리고 실패로 전환 | 1 |
|
||
| `FLOW-FE-RESP-004` | 4 | `feature-runtime-schema-validation-contract` | unvalidated JSON | envelope 공유 스키마 검증 | discriminated envelope | 경계 검증은 `.safeParse()` non-throwing — throw 를 상위로 누출하지 않는다 | 1 |
|
||
| `FLOW-FE-RESP-005` | 5 | `feature-runtime-schema-validation-contract` | discriminated envelope | success/failure 분기 검증 | 분기 확정 envelope | 200 이어도 envelope 이 invalid 하면 success 로 반환하지 않는다 | 1 |
|
||
| `FLOW-FE-RESP-006` | 6 | `feature-runtime-schema-validation-contract` | 분기 확정 envelope | payload per-operation 스키마 검증 | 검증된 payload(deep clone) | payload invalid 는 `SCHEMA_MISMATCH`; mapper 는 검증 통과분만 받는다 | 1 |
|
||
| `FLOW-FE-RESP-007` | 7 | `feature-boundary-mapper-viewmodel-contract` | 검증된 payload | DTO → application model 매핑 | application model | 이 단계 산출물은 model 이고 view-model 이 아니다 — view-model 투영은 `application/view-models/` 소유(§4.2·§4.4 2-stage) | 1 |
|
||
| `FLOW-FE-RESP-008` | 8 | `feature-frontend-error-classification-boundary-contract` | application model 또는 실패 신호 | 정규화된 결과 반환 | application result 또는 normalized failure | 총함수 — 미매핑 예외는 `UNKNOWN_FAILURE` 로 귀결하고 throw 를 presentation 으로 통과시키지 않는다 | 1 |
|
||
| `FLOW-FE-EVENT-001` | 1 | `feature-frontend-realtime-subscription-lifecycle-contract` | 열린 연결 | 프레임 수신 대기 | raw frame | 연결 오류는 이 단계가 소유하고 이후 단계로 예외를 넘기지 않는다 | 1 |
|
||
| `FLOW-FE-EVENT-002` | 2 | `feature-frontend-realtime-subscription-lifecycle-contract` | raw frame | transport decode (SSE 필드 / WebSocket frame / poll 응답 본문) | unvalidated event JSON | decode 실패는 프레임을 버리고 실패로 전환하며 연결을 즉시 끊지 않는다 | 1 |
|
||
| `FLOW-FE-EVENT-003` | 3 | `feature-runtime-schema-validation-contract` | unvalidated event JSON | event envelope 공유 스키마 검증 | discriminated event envelope | 경계 검증은 non-throwing 이며 throw 를 상위로 누출하지 않는다 | 1 |
|
||
| `FLOW-FE-EVENT-004` | 4 | `feature-runtime-schema-validation-contract` | discriminated event envelope | `eventSchema` per-event 검증 | 검증된 event payload | 미검증 프레임은 `EVENT_SCHEMA_MISMATCH` 로 드롭하고 application 에 도달시키지 않는다 | 1 |
|
||
| `FLOW-FE-EVENT-005` | 5 | `feature-frontend-error-classification-boundary-contract` | 검증된 event payload 또는 실패 신호 | 정규화된 이벤트 또는 failure 반환 | application event 또는 normalized failure | 총함수 — 미매핑 예외는 `UNKNOWN_FAILURE` 로 귀결한다 | 1 |
|
||
|
||
<!-- section-id: delegation-registry -->
|
||
### 2.1.2 Delegation Registry (typed)
|
||
|
||
> 한 branch 가 다른 branch 에 관심사를 넘길 때 여기에 행을 만든다. `Status` 가 `accepted` 가 되려면
|
||
> **delegate 쪽 문서가 frontmatter `accepts_delegations` 로 접수해야** 한다. 접수 전에는 `proposed` 이고
|
||
> `UNACCEPTED_DELEGATION` 으로 계속 잡힌다 — "A 가 넘겼는데 B 는 받은 적 없는" 공백이 조용히 남지 않게 하는 장치다.
|
||
> `DELEG-FE-001`~`DELEG-FE-006` 은 2026-07-20 문서 간 정합성 감사에서 **미접수 위임으로 발견됐고, 이후 delegate 6곳이 모두 `accepts_delegations` 로 접수해 현재는 전부 `accepted`** 다(2026-07-21 frontmatter 왕복 대조 6/6 일치, `UNACCEPTED_DELEGATION` 0건).
|
||
> `DELEG-FE-007`~`DELEG-FE-011` 은 2026-07-28 runtime capability 확장에서 추가했다. 신규 6개 능력 도메인이 서로 또는 기존 branch 에 넘기는 관심사이며, delegate 5곳의 `accepts_delegations` 를 같은 변경에서 채워 접수까지 마쳤다.
|
||
> 즉 이 표는 지금 열려 있는 공백 목록이 아니라 닫힌 위임의 등록부다.
|
||
|
||
| Delegation ID | Concern Key | Revision | Delegator | Delegate | Scope | Status |
|
||
|---|---|---|---|---|---|---|
|
||
| `DELEG-FE-001` | `fe.deleg.dynamic-class-lint` | 1 | `feature-tailwind-design-token-styling-contract` | `feature-frontend-browser-security-boundary-contract` | dynamic/untrusted class-string 구성 금지의 정적 lint 강제 | accepted |
|
||
| `DELEG-FE-002` | `fe.deleg.lint-toolchain-substrate` | 1 | `feature-frontend-architecture-enforcement-lint-contract` | `feature-frontend-project-bootstrap-toolchain-contract` | `eslint.config.js`·`.dependency-cruiser.cjs` 설치와 base flat-config substrate | accepted |
|
||
| `DELEG-FE-003` | `fe.deleg.composition-root-review` | 1 | `feature-frontend-architecture-enforcement-lint-contract` | `feature-frontend-clean-architecture-layering-contract` | composition-root business-rule 혼입에 대한 코드리뷰 체크리스트 | accepted |
|
||
| `DELEG-FE-004` | `fe.deleg.color-contrast` | 1 | `feature-accessibility-baseline-contract` | `feature-tailwind-design-token-styling-contract` | color contrast token 값 결정 | accepted |
|
||
| `DELEG-FE-005` | `fe.deleg.injectable-random` | 1 | `feature-api-client-response-envelope-contract` | `feature-frontend-clean-architecture-layering-contract` | full-jitter backoff 를 결정론 테스트 가능하게 하는 random source 주입 형태 | accepted |
|
||
| `DELEG-FE-006` | `fe.deleg.reload-once-action` | 1 | `feature-async-ui-state-contract` | `feature-frontend-render-recovery-boundary-contract` | `reload-once` action 의 실제 실행(5-condition guard 경유) | accepted |
|
||
| `DELEG-FE-007` | `fe.deleg.sw-host-for-push` | 1 | `feature-frontend-realtime-subscription-lifecycle-contract` | `feature-frontend-background-execution-worker-contract` | WebPush 가 요구하는 service worker 등록·수명주기 호스팅 | accepted |
|
||
| `DELEG-FE-008` | `fe.deleg.binary-handle-ownership` | 1 | `feature-frontend-large-object-transfer-contract` | `feature-frontend-binary-file-io-store-contract` | 전송 대상 `File`/`Blob` handle 과 object URL 수명 소유 | accepted |
|
||
| `DELEG-FE-009` | `fe.deleg.persistent-cache-key` | 1 | `feature-frontend-cache-tier-cross-tab-invalidation-contract` | `feature-frontend-storage-registry-contract` | 영속 캐시의 physical key·namespace·classification·quota fallback 소유 | accepted |
|
||
| `DELEG-FE-010` | `fe.deleg.decoded-payload-validation` | 1 | `feature-frontend-multi-protocol-api-transport-contract` | `feature-runtime-schema-validation-contract` | codec 디코드 이후 payload 의 runtime schema 검증 | accepted |
|
||
| `DELEG-FE-011` | `fe.deleg.sw-release-coherence` | 1 | `feature-frontend-background-execution-worker-contract` | `feature-frontend-release-cache-rollback-contract` | service worker 버전과 release·rollback coherence 판정 | accepted |
|
||
|
||
### 2.2 Universal acceptance questions
|
||
|
||
각 `FE-OC-*` owner branch는 완료 전에 다음 질문에 답해야 한다.
|
||
|
||
1. 이 contract가 막는 concrete failure는 무엇인가?
|
||
2. input과 output은 무엇인가?
|
||
3. project-wide default와 limit은 무엇인가?
|
||
4. 허용되는 예외와 승인 owner는 누구인가?
|
||
5. 금지 구현은 무엇인가?
|
||
6. failure가 어떤 normalized error와 UX로 나타나는가?
|
||
7. 어떤 telemetry가 남고 어떤 data가 redacted되는가?
|
||
8. 어떤 test가 위반 시 실패하는가?
|
||
9. 어떤 evidence artifact가 생성되는가?
|
||
10. release 또는 rollback에 미치는 영향은 무엇인가?
|
||
|
||
하나라도 비어 있으면 branch는 `documented-only`를 넘을 수 없다.
|
||
|
||
---
|
||
|
||
## 3. Stable Decision Register
|
||
|
||
> **Legacy reference (v1).** 기존 `FE-D*` 식별자와 세부 rationale은 이력·설명용으로 보존한다. project-wide 결정의 현재 owner와 branch 상속 기준은 아래 `## 6.1 Project Decision Registry / 안정 결정 레지스트리`다.
|
||
|
||
### 3.1 Decision status
|
||
|
||
| status | 의미 |
|
||
| --- | --- |
|
||
| `conditional-default` | 현재 project default지만 trigger가 오면 재검토 |
|
||
| `accepted-documented-only` | 문서상 채택, 코드 evidence 없음 |
|
||
| `deferred` | owner와 trigger만 있고 선택 미확정 |
|
||
| `superseded` | 후속 FE-D row로 대체, 삭제 금지 |
|
||
|
||
<!-- section-id: legacy-decision-rows -->
|
||
### 3.2 Decision rows
|
||
|
||
> `FE-D*` 는 v1 결정 레지스터다. project-wide 결정의 현재 owner 는 §6.1 Project Decision Registry(`DEC-...`)이며 이 표는 이력·설명용으로 보존한다.
|
||
> `Affected FE-OC` 열은 결정과 계약의 **대응 관계**이지 계약 내용의 사본이 아니다.
|
||
|
||
| Decision ID | Decision | Status | Owner | Affected FE-OC | Evidence / rationale | Revisit trigger | Supersedes |
|
||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||
| `FE-D001` | package manager default는 `pnpm`; `packageManager` field와 `pnpm-lock.yaml`을 commit | `conditional-default` | `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003`, `FE-OC-020` | project-local reproducibility default, 외부 source claim 아님 | 조직 표준이 npm/yarn/Bun을 강제하거나 target CI가 pnpm을 지원하지 않음 | — |
|
||
| `FE-D002` | source는 JavaScript ESM, typecheck-equivalent는 `tsc --allowJs --checkJs --noEmit` | `accepted-documented-only` | `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003`, `FE-OC-007`, `FE-OC-020` | 사용자 제약 + runtime schema 필요성 | TypeScript strict 전환 승인 | — |
|
||
| `FE-D003` | Vite client-only SPA를 build baseline으로 사용 | `accepted-documented-only` | `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003`, `FE-OC-016`, `FE-OC-021` | [[raw/official-docs/vite-build-tool-official]] `VITE-C2` | SSR/SEO/edge rendering이 product requirement가 됨 | — |
|
||
| `FE-D004` | UI composition은 React를 사용 | `accepted-documented-only` | `feature-async-ui-state-contract` | `FE-OC-002`, `FE-OC-011`, `FE-OC-015` | [[raw/official-docs/react-ui-library-official]] `REACT-UI-C1` | native/custom-element 또는 다른 framework로 project fork | — |
|
||
| `FE-D005` | styling default는 Tailwind theme token + component primitive | `conditional-default` | `feature-tailwind-design-token-styling-contract` | `FE-OC-011`, `FE-OC-019`, `FE-OC-021` | [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C1`, `TAILWIND-UTIL-C2`, `TAILWIND-UTIL-C4` | runtime theming 또는 product design system이 다른 compiler를 요구 | — |
|
||
| `FE-D006` | server state policy는 application-owned `QueryCachePort`가 정의하고 TanStack Query adapter가 구현하며 client store에 복제하지 않음 | `accepted-documented-only` | `feature-server-state-caching-contract` | `FE-OC-011`, `FE-OC-012` | [[raw/official-docs/tanstack-query-server-state-official]] `TSQ-C1`, `TSQ-C3`, `TSQ-C5`; port ownership·non-duplication은 project decision | offline-first normalized entity cache가 필요 | — |
|
||
| `FE-D007` | boundary runtime validation은 Zod schema로 수행 | `accepted-documented-only` | `feature-runtime-schema-validation-contract` | `FE-OC-007`, `FE-OC-008` | [[raw/official-docs/zod-runtime-schema-validation-official]] `ZOD-VALID-C2`, `ZOD-VALID-C3`, `ZOD-VALID-C4` | bundle budget 또는 generated schema pipeline이 대체안을 요구 | — |
|
||
| `FE-D008` | routing은 React Router Declarative Mode를 default로 사용 | `conditional-default` | `feature-routing-navigation-guard-contract` | `FE-OC-005`, `FE-OC-015` | [[raw/official-docs/react-router-official]] `REACT-ROUTER-C1`, `REACT-ROUTER-C4` | data router/framework mode가 loader·SSR requirement로 필요 | — |
|
||
| `FE-D009` | `domain`, `application`, `presentation`, `adapters`, `bootstrap` responsibility를 분리 | `accepted-documented-only` | `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002` | [[raw/project-notes/ca-skeleton-operational-contract]]의 운영 계약 철학을 frontend에 적용 | sample slice가 불필요한 ceremony를 증명하거나 FSD fork 승인 | — |
|
||
| `FE-D010` | output port interface는 `application`이 소유하고 adapter가 구현 | `accepted-documented-only` | `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002` | dependency inversion의 project decision | port가 domain invariant 자체를 표현해야 하는 concrete case 발생 | — |
|
||
| `FE-D011` | composition root는 `bootstrap` 하나이며 concrete adapter를 application에 주입 | `accepted-documented-only` | `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002`, `FE-OC-004` | owner ambiguity 제거 | framework DI container 도입 | — |
|
||
| `FE-D012` | deploy별 public value는 pre-render runtime config, compiler value는 build-time config로 분리 | `conditional-default` | `feature-frontend-env-runtime-config-contract` | `FE-OC-004`, `FE-OC-016`, `FE-OC-023` | environment-specific rebuild 감소; project inference | hosting이 runtime config atomic publish를 지원하지 않음 | — |
|
||
| `FE-D013` | runtime config fallback은 environment별 rebuild를 허용하되 한 artifact를 여러 env에 재사용하지 않음 | `conditional-default` | `feature-frontend-env-runtime-config-contract` | `FE-OC-004`, `FE-OC-016`, `FE-OC-023` | fallback의 deploy ambiguity 제한 | runtime config endpoint 도입 | — |
|
||
| `FE-D014` | default request timeout은 total 10s; 별도 connect timeout은 browser API가 직접 제공하지 않으므로 주장하지 않음 | `conditional-default` | `feature-api-client-response-envelope-contract` | `FE-OC-006`, `FE-OC-009`, `FE-OC-021` | project-local initial limit | measured p95가 10s를 정당하게 초과하거나 streaming 도입 | — |
|
||
| `FE-D015` | retry는 initial call 이후 최대 2회, exponential backoff + full jitter, cap 2s | `conditional-default` | `feature-api-client-response-envelope-contract` | `FE-OC-009`, `FE-OC-021` | retry storm 억제를 위한 project default | backend SLO·rate limit contract 확정 | — |
|
||
| `FE-D016` | mutation 자동 retry는 stable idempotency key와 backend replay contract가 있을 때만 허용 | `accepted-documented-only` | `feature-api-client-response-envelope-contract` | `FE-OC-009`, `FE-OC-023` | duplicate write 방지 invariant | mutation이 naturally idempotent임이 schema로 증명 | — |
|
||
| `FE-D017` | auth lifecycle은 외부 owner, skeleton은 `AuthSessionPort`만 소비 | `accepted-documented-only` | `feature-frontend-auth-session-integration-contract` | `FE-OC-010` | [[raw/project-notes/keycloak-patterns-overview]] | skeleton이 독립 auth product로 scope 변경 | — |
|
||
| `FE-D018` | route/API-operation/env/storage/error/query/telemetry/release/capability token은 9개 registry로 관리 | `accepted-documented-only` | `feature-frontend-contract-registry-governance` | `FE-OC-013`, `FE-OC-022` | rename·compatibility 영향 추적 | code generation SSOT 채택 | — |
|
||
| `FE-D019` | service worker와 offline asset cache는 default off | `superseded` | `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017`, `FE-OC-023` | stale asset·config mismatch surface 축소 | `FE-D034`가 2026-07-28에 대체 — SW 역할을 precaching과 push/sync 호스트로 분리 | — |
|
||
| `FE-D020` | hashed asset은 immutable, HTML·runtime config·release manifest는 revalidate/no-store 정책 분리 | `accepted-documented-only` | `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017` | release coherence invariant | hosting cache primitive 제약 | — |
|
||
| `FE-D021` | telemetry는 best-effort queue + redaction, sink failure는 UI를 실패시키지 않음 | `accepted-documented-only` | `feature-frontend-observability-logging-trace-contract` | `FE-OC-014` | operational isolation | regulated audit event처럼 delivery guarantee가 필요한 별도 channel 도입 | — |
|
||
| `FE-D022` | test stack default는 Vitest + RTL + MSW + Playwright + axe | `conditional-default` | `feature-frontend-test-taxonomy-contract` | `FE-OC-020` | Vite/browser/component/e2e responsibility 분리 | organization test platform이 대체 | — |
|
||
| `FE-D023` | static release는 immutable release directory + atomic active pointer로 배포 | `conditional-default` | `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017`, `FE-OC-025` | rollback 가능 artifact requirement | provider가 다른 atomic primitive만 제공 | — |
|
||
| `FE-D024` | dependency lock, secret scan, vulnerability scan, license inventory, dependency review를 merge/release gate로 분리 (lock 은 `FE-GATE-001`, 나머지 4개는 `FE-GATE-013`) | `accepted-documented-only` | `feature-frontend-build-bundle-supply-chain-contract` | `FE-OC-018`, `FE-OC-019`, `FE-OC-020` | supply-chain scope 최소값 | organization security policy가 더 강한 gate 지정 | — |
|
||
| `FE-D025` | sample slice는 제거 가능한 contract fixture이며 product import를 금지 | `accepted-documented-only` | `feature-sample-feature-slice-contract-fixture` | `FE-OC-024` | backend skeleton의 sample-fixture 운영 원칙을 frontend에 적용 | fixture 없이 동일 gate coverage를 증명 | — |
|
||
| `FE-D026` | 신규 runtime capability 6종은 `FE-REG-CAPABILITY` flag로 default OFF이며 활성화는 owner·gate·runbook을 동반한다 | `conditional-default` | `feature-frontend-env-runtime-config-contract` | `FE-OC-004`, `FE-OC-022`, `FE-OC-027`, `FE-OC-028`, `FE-OC-029`, `FE-OC-030`, `FE-OC-031`, `FE-OC-032` | project-local default, 외부 source claim 아님 | 특정 capability가 제품 필수가 되어 상시 활성이 요구됨 | — |
|
||
| `FE-D027` | 로컬 바이너리 backend는 IndexedDB를 default로 하고 OPFS는 대용량 순차 write에 opt-in, Cache Storage는 service worker 호스팅 response cache 전용이다 | `conditional-default` | `feature-frontend-binary-file-io-store-contract` | `FE-OC-013`, `FE-OC-027` | project-local default, 외부 source claim 아님 | OPFS 브라우저 지원 또는 quota 정책이 바뀜 | — |
|
||
| `FE-D028` | 탭 간 무효화는 BroadcastChannel 우선에 `storage` event fallback을 쓰고 leader election 없이 무효화 key만 전파한다 | `conditional-default` | `feature-frontend-cache-tier-cross-tab-invalidation-contract` | `FE-OC-012`, `FE-OC-028` | project-local default, 외부 source claim 아님; 값 전파는 PII·stale 표면을 넓힘 | 다중 탭 실시간 협업이 제품 요구가 됨 | — |
|
||
| `FE-D029` | presigned URL 획득은 shared client를 경유하고 실제 byte 전송은 session credential을 첨부하지 않는 transfer adapter가 수행한다 | `accepted-documented-only` | `feature-frontend-large-object-transfer-contract` | `FE-OC-006`, `FE-OC-019`, `FE-OC-029` | credential 유출 방지 invariant, project decision | 스토리지가 same-origin proxy만 제공 | — |
|
||
| `FE-D030` | 재개 가능 전송은 part size·병렬도·part 재시도 상한을 registry로 고정하고 part 상태를 `BlobStorePort`에 보존한다 | `conditional-default` | `feature-frontend-large-object-transfer-contract` | `FE-OC-029` | project-local default, 외부 source claim 아님 | 스토리지 제공자가 다른 multipart 제약을 요구 | — |
|
||
| `FE-D031` | transport default는 REST이고 GraphQL·gRPC-Web·Connect-Web은 `FE-REG-API`의 protocol 필드로 opt-in하며 미지원 환경은 REST gateway로 fallback한다 | `conditional-default` | `feature-frontend-multi-protocol-api-transport-contract` | `FE-OC-006`, `FE-OC-007`, `FE-OC-030` | project-local default, 외부 source claim 아님 | backend가 단일 비-REST protocol만 제공 | — |
|
||
| `FE-D032` | 실시간 transport는 SSE를 우선하고 양방향이 필요하면 WebSocket, 둘 다 불가할 때만 최소 간격·backoff·visibility gating을 갖춘 bounded polling을 쓴다 | `conditional-default` | `feature-frontend-realtime-subscription-lifecycle-contract` | `FE-OC-031` | project-local default, 외부 source claim 아님; polling은 마지막 수단 | backend가 SSE를 제공하지 않거나 양방향이 기본 요구가 됨 | — |
|
||
| `FE-D033` | 실시간 연결은 full jitter backoff와 30초 cap을 쓰고 재시도 상한 후 terminal 상태로 전이하며 resume은 `Last-Event-ID` 또는 cursor로 수행하고 unmount 시 구독을 해제한다 | `conditional-default` | `feature-frontend-realtime-subscription-lifecycle-contract` | `FE-OC-011`, `FE-OC-031` | 재연결 폭주·구독 누수 억제, project decision | backend가 서버 주도 재연결 정책을 계약으로 제공 | — |
|
||
| `FE-D034` | service worker는 역할을 분리해 release asset precaching은 default off로 유지하고 push·background sync·Cache Storage 호스트 역할만 capability opt-in으로 허용하며 update UX 계약을 요구한다 | `conditional-default` | `feature-frontend-background-execution-worker-contract` | `FE-OC-016`, `FE-OC-017`, `FE-OC-023`, `FE-OC-032` | precaching은 release coherence 훼손 원인이나 push·sync는 SW 없이는 불가; project decision | offline-first가 제품 요구가 되고 update UX가 설계됨 | `FE-D019` |
|
||
| `FE-D035` | background sync 재생은 `idempotency: keyed` operation만 허용한다 | `accepted-documented-only` | `feature-frontend-background-execution-worker-contract` | `FE-OC-009`, `FE-OC-032` | `FE-D016`의 중복 write 방지 invariant를 지연 재생에 확장 | mutation이 naturally idempotent임이 schema로 증명됨 | — |
|
||
| `FE-D036` | Web Worker 작업은 structured-clone 또는 Transferable로만 통신하고 timeout과 terminate를 계약하며 worker 안에서 application port를 재구현하지 않는다 | `accepted-documented-only` | `feature-frontend-background-execution-worker-contract` | `FE-OC-002`, `FE-OC-032` | worker 안 로직 중복이 layer 경계를 우회하는 것을 차단, project decision | SharedArrayBuffer 기반 병렬 처리가 요구됨 | — |
|
||
|
||
`FE-D026`~`FE-D036`의 `Evidence / rationale`에 적힌 `project-local default, 외부 source claim 아님`은 `FE-D001`이 쓰는 표기와 같다. 대응 raw 근거 문서가 아직 없다는 사실을 감추지 않고 명시하는 장치이며, 수집 계획은 `FE-Q-011`이 소유한다.
|
||
|
||
### 3.3 Decision change protocol
|
||
|
||
1. 변경 제안자는 새 `FE-D*`를 만들지, 기존 row의 compatible clarification인지 분류한다.
|
||
2. owner는 영향을 받는 `FE-OC-*`와 registry row를 나열한다.
|
||
3. `compatibility_impact`를 `none`, `additive`, `behavior-change`, `breaking` 중 하나로 기록한다.
|
||
4. `behavior-change`와 `breaking`은 migration·rollback·test evidence 없이 merge하지 않는다.
|
||
5. 기존 의미를 대체하면 기존 row를 `superseded`로 바꾸고 `Supersedes` chain을 연결한다.
|
||
6. source link가 추가되면 실제 raw 파일만 사용한다. placeholder wikilink를 만들지 않는다.
|
||
7. implementation repository가 생기면 commit·path·test artifact를 evidence ledger에 추가한다.
|
||
8. hub와 owner branch가 모순되면 project-wide default를 바꾸기 전 이 register를 먼저 갱신한다.
|
||
|
||
<!-- section-id: project-decisions -->
|
||
## 6.1 안정 결정 레지스트리
|
||
|
||
> Project contract v2의 project-wide decision SSOT. 기존 `FE-D*`는 아래 stable ID로 일대일 이관되며 branch는 `DEC-...@1`만 pin한다.
|
||
|
||
| Decision ID | Revision | Domain | Decision Summary | Status | Owner | Evidence |
|
||
|---|---:|---|---|---|---|---|
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TOOLCHAIN-001` | 1 | `toolchain` | package manager default는 pnpm이며 packageManager field와 pnpm-lock.yaml을 commit한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D001` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-LANGUAGE-001` | 1 | `language` | JavaScript ESM과 tsc allowJs/checkJs/noEmit을 typecheck-equivalent baseline으로 사용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D002` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001` | 1 | `build` | Vite client-only SPA를 build baseline으로 사용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D003`; [[raw/official-docs/vite-build-tool-official]] `VITE-C2` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-UI-001` | 1 | `ui` | UI composition은 React를 사용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D004`; [[raw/official-docs/react-ui-library-official]] `REACT-UI-C1` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001` | 1 | `styling` | styling default는 Tailwind theme token과 component primitive다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D005`; [[raw/official-docs/tailwind-css-utility-first-official]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SERVER-STATE-001` | 1 | `server-state` | application-owned QueryCachePort와 TanStack Query adapter를 사용하고 client store에 server state를 복제하지 않는다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D006`; [[raw/official-docs/tanstack-query-server-state-official]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001` | 1 | `validation` | boundary runtime validation은 Zod schema로 수행한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D007`; [[raw/official-docs/zod-runtime-schema-validation-official]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ROUTING-001` | 1 | `routing` | routing default는 React Router Declarative Mode다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D008`; [[raw/official-docs/react-router-official]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001` | 1 | `architecture` | domain, application, presentation, adapters, bootstrap 책임을 분리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D009` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-PORT-OWNERSHIP-001` | 1 | `port-ownership` | output port interface는 application이 소유하고 adapter가 구현한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D010` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-COMPOSITION-ROOT-001` | 1 | `composition-root` | bootstrap을 단일 composition root로 두고 concrete adapter를 application에 주입한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D011` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RUNTIME-CONFIG-001` | 1 | `runtime-config` | deploy별 public value는 runtime config, compiler value는 build-time config로 분리한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D012` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001` | 1 | `config-fallback` | runtime config fallback 시 environment별 rebuild는 허용하되 artifact의 multi-environment 재사용은 금지한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D013` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TIMEOUT-001` | 1 | `timeout` | request total timeout default는 10초이며 별도 browser connect timeout은 주장하지 않는다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D014` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RETRY-001` | 1 | `retry` | retry는 initial call 이후 최대 2회, exponential backoff와 full jitter, 2초 cap을 사용한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D015` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-IDEMPOTENCY-001` | 1 | `idempotency` | mutation auto-retry는 stable idempotency key와 backend replay contract가 있을 때만 허용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D016` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001` | 1 | `auth-boundary` | auth lifecycle은 외부 owner가 소유하고 skeleton은 AuthSessionPort만 소비한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D017`; [[raw/project-notes/keycloak-patterns-overview]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001` | 1 | `registry` | route, API operation, env, storage, error, query, telemetry, release token, capability를 9개 registry로 관리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D018` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-OFFLINE-CACHE-001` | 2 | `offline-cache` | service worker의 release asset precaching은 default off이고 push·background sync·Cache Storage 호스트 역할만 capability opt-in으로 허용한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D034` (`FE-D019` supersede) |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CACHE-POLICY-001` | 1 | `cache-policy` | hashed asset은 immutable, HTML·runtime config·release manifest는 revalidate/no-store로 분리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D020` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001` | 1 | `telemetry` | telemetry는 best-effort queue와 redaction을 사용하며 sink failure가 UI를 실패시키지 않는다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D021` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001` | 1 | `test-stack` | test stack default는 Vitest, RTL, MSW, Playwright, axe다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D022` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001` | 1 | `deployment` | static release는 immutable release directory와 atomic active pointer로 배포한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D023` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SUPPLY-CHAIN-001` | 1 | `supply-chain` | dependency lock, secret scan, vulnerability scan, license inventory, dependency review를 merge/release gate로 분리한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D024` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SAMPLE-FIXTURE-001` | 1 | `sample-fixture` | sample slice는 제거 가능한 contract fixture이며 product import를 금지한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D025` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CAPABILITY-001` | 1 | `capability` | 신규 runtime capability 6종은 FE-REG-CAPABILITY flag로 default OFF이며 활성화는 owner·gate·runbook을 동반한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D026` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BINARY-STORE-001` | 1 | `binary-store` | 로컬 바이너리 backend는 IndexedDB를 default로 하고 OPFS는 대용량 순차 write에 opt-in, Cache Storage는 service worker 호스팅 response cache 전용이다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D027` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CROSS-TAB-001` | 1 | `cross-tab` | 탭 간 무효화는 BroadcastChannel 우선에 storage event fallback을 쓰고 leader election 없이 무효화 key만 전파한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D028` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TRANSFER-CREDENTIAL-001` | 1 | `transfer-credential` | presigned URL 획득은 shared client를 경유하고 실제 byte 전송은 session credential을 첨부하지 않는 transfer adapter가 수행한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D029` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RESUMABLE-TRANSFER-001` | 1 | `resumable-transfer` | 재개 가능 전송은 part size·병렬도·part 재시도 상한을 registry로 고정하고 part 상태를 BlobStorePort에 보존한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D030` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-PROTOCOL-001` | 1 | `protocol` | transport default는 REST이고 GraphQL·gRPC-Web·Connect-Web은 FE-REG-API의 protocol 필드로 opt-in하며 미지원 환경은 REST gateway로 fallback한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D031` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REALTIME-TRANSPORT-001` | 1 | `realtime-transport` | 실시간 transport는 SSE를 우선하고 양방향이 필요하면 WebSocket, 둘 다 불가할 때만 최소 간격·backoff·visibility gating을 갖춘 bounded polling을 쓴다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D032` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REALTIME-LIFECYCLE-001` | 1 | `realtime-lifecycle` | 실시간 연결은 full jitter backoff와 30초 cap을 쓰고 재시도 상한 후 terminal 상태로 전이하며 resume은 Last-Event-ID 또는 cursor로 수행하고 unmount 시 구독을 해제한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D033` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SERVICE-WORKER-ROLE-001` | 1 | `service-worker-role` | service worker는 역할을 분리해 release asset precaching은 default off로 유지하고 push·background sync·Cache Storage 호스트 역할만 capability opt-in으로 허용하며 update UX 계약을 요구한다 | `conditional-default` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D034` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BACKGROUND-SYNC-001` | 1 | `background-sync` | background sync 재생은 idempotency keyed operation만 허용한다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D035` |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-WORKER-TASK-001` | 1 | `worker-task` | Web Worker 작업은 structured-clone 또는 Transferable로만 통신하고 timeout과 terminate를 계약하며 worker 안에서 application port를 재구현하지 않는다 | `accepted-documented-only` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | §3.2 `FE-D036` |
|
||
|
||
> **개정 기록 (§3.3 protocol)**
|
||
>
|
||
> - 2026-07-21 · `DEC-...-SUPPLY-CHAIN-001` · `compatibility_impact: additive` · revision 유지(1). Decision Summary 에 `dependency review` 를 추가했다. 이는 새 결정이 아니라 **불완전한 요약의 정정**이다 — `FE-OC-018` 과 §13.1 이 처음부터 dependency review 를 요구했고 §3.2 `FE-D024` 도 이를 포함하는데 이 registry 행만 4개 control 로 적혀 있었다. 기존 4개 control 의 동작은 바뀌지 않고, gate 정의(§15.1 `FE-GATE-013`)도 이미 dependency-review fixture 를 포함한 채 revision 1 이므로 같은 판정을 적용한다.
|
||
> - 2026-07-28 · `DEC-...-OFFLINE-CACHE-001` · `compatibility_impact: behavior-change` · revision 1→2. `FE-D034` 가 `FE-D019` 를 supersede 하면서 service worker 를 "전면 off" 에서 "precaching off + 역할별 capability opt-in" 으로 바꿨다. WebPush·Background Sync·Cache Storage 가 service worker 없이는 동작하지 않기 때문이다. behavior-change 이므로 §3.3 4단계에 따라 `FE-GATE-032`(SW update UX + rollback 시 SW 되돌림)와 `FE-RB-007` 이 migration·rollback evidence 를 담당한다. 이 결정을 pin 한 [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] 는 `@2` 로 갱신했다.
|
||
> - 2026-07-28 · `DEC-...-REGISTRY-001` · `compatibility_impact: additive` · revision 유지(1). 9번째 registry `FE-REG-CAPABILITY` 를 추가했다. 기존 8개 registry 의 owner·schema·동작은 바뀌지 않으므로 `DEC-...-SUPPLY-CHAIN-001` 선례와 같은 판정을 적용한다. 다만 Summary 문자열이 바뀌므로 이를 복사해 둔 소비 branch 4곳([[raw/branch-notes/feature-frontend-storage-registry-contract]]·[[raw/branch-notes/feature-frontend-observability-logging-trace-contract]]·[[raw/branch-notes/feature-frontend-contract-registry-governance]]·[[raw/branch-notes/feature-frontend-contract-compatibility-governance]])의 상속 표를 함께 갱신했다.
|
||
> - 2026-07-28 · `DEC-...-CAPABILITY-001` 외 10건 신규 등록. 근거 raw 문서가 아직 없으므로 §3.2 대응 행의 `Evidence / rationale` 에 `project-local default, 외부 source claim 아님` 을 명시했고, 수집 계획은 `FE-Q-011` 이 소유한다.
|
||
> - Summary 셀은 소비 branch 의 상속 표와 **문자열이 정확히 일치해야 한다**(`wiki_consistency_check.py` 의 `CONFLICTS_WITH_PROJECT_DECISION`). 분류·근거 같은 메타는 이 기록에 적고 Summary 에 섞지 않는다. 조사 앞에 공백을 넣지 않는다 — `…release token을 8개 registry로` 형태가 기준이다.
|
||
|
||
---
|
||
|
||
<!-- section-id: architecture-components -->
|
||
## 4. System Architecture Contract
|
||
|
||
### 4.1 Architecture diagrams
|
||
|
||
![[raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio]]
|
||
|
||
![[raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio]]
|
||
|
||
두 파일은 `wiki-diagram-reviewer`의 `rules/diagram-standards.md` v2 심사에서 각각 100/100 PASS를 받았다. PASS scope는 overview의 Clean Architecture dependency ownership view와 deployment의 static asset·`/config.json` delivery slice다. §12 전체 release/rollback topology, 실제 구현 topology, hosting 상태는 이 review가 증명하지 않는다. 근거: `docs/superpowers/specs/2026-07-18-ca-skeleton-frontend-operational-contract-review/diagram-review.md`.
|
||
|
||
### 4.2 Component responsibility
|
||
|
||
| Component | Owns | Consumes | MUST NOT own | Evidence status |
|
||
| --- | --- | --- | --- | --- |
|
||
| `domain` | framework-neutral model, value semantics, pure policy | standard JavaScript only | React, router, Query, fetch, storage, telemetry | `planned` |
|
||
| `application` | use case, input/output port, `QueryCachePort` policy, orchestration, view-model contract | domain | concrete adapter, browser global, React component | `planned` |
|
||
| `presentation` | page/component, user event, view state rendering | application public API | raw API DTO, fetch, storage key, telemetry transport | `planned` |
|
||
| `adapters/http` | application output port implementation, envelope/schema/error mapping | application port, browser fetch | use-case policy, component rendering | `planned` |
|
||
| `adapters/storage` | storage port implementation, serialization, quota mapping | application port, Web Storage | token lifecycle, domain policy | `planned` |
|
||
| `adapters/telemetry` | telemetry port implementation, queue, redaction, sink | application port, browser transport | UX decision, navigation | `planned` |
|
||
| `adapters/query-cache` | application-owned `QueryCachePort` implementation, TanStack Query key/invalidation bridge | application port, TanStack Query | use-case policy, page-local query key | `planned` |
|
||
| `adapters/capability` | capability flag 해석, 브라우저 feature detection, 비활성 사유 | application port, runtime config, browser globals | 활성화 여부의 제품 판단 | `planned` |
|
||
| `adapters/file` | 파일 선택·저장 dialog, object URL 생성/해제 | application port, File System Access / input element | 파일 내용 해석, 도메인 검증 | `planned` |
|
||
| `adapters/blob-store` | IndexedDB·OPFS·Cache Storage 백엔드, quota 매핑, eviction | application port, browser storage API | 저장 대상의 의미, 도메인 정책 | `planned` |
|
||
| `adapters/cache-persistence` | 캐시 직렬화, version partition, 복원 거부 | application port, 자체 영속 백엔드 | 캐시 정책 결정(`QueryCachePort` 소유) | `planned` |
|
||
| `adapters/cross-tab` | BroadcastChannel·`storage` event 전송, 메시지 봉투 | application port, browser globals | 무효화 대상 결정 | `planned` |
|
||
| `adapters/transfer` | part 분할·병렬·재시도·무결성·진행 보고, credential-less 전송 | application port, fetch, Streams | presign 발급, 업로드 대상 도메인 규칙 | `planned` |
|
||
| `adapters/protocol` | GraphQL·gRPC-Web·Connect-Web codec 과 status 정규화 | application output port, fetch | operation 정의, use-case policy | `planned` |
|
||
| `adapters/realtime` | 연결 수명주기, 재연결 backoff, resume cursor, 프레임 디코드, 구독 해제 | application port, EventSource·WebSocket·fetch | 이벤트의 도메인 의미, 상태 병합 정책 | `planned` |
|
||
| `adapters/worker` | worker 생성·통신·timeout·terminate | application port, Worker API | 도메인 계산 규칙 | `planned` |
|
||
| `adapters/service-worker` | SW 등록·갱신 상태, background sync 큐 등록 | application port, ServiceWorker API | precache 정책 결정(`FE-D034` 소유), mutation 의미 | `planned` |
|
||
| `bootstrap` | config load, capability 해석, adapter construction, dependency injection, React mount | all runtime modules | business rule, page-specific orchestration | `planned` |
|
||
|
||
### 4.3 Dependency matrix
|
||
|
||
화살표는 source import 방향이다.
|
||
|
||
| From | May import | MUST NOT import | Planned enforcement |
|
||
| --- | --- | --- | --- |
|
||
| `domain` | domain sibling modules | application, presentation, adapters, bootstrap, React, browser globals | dependency-cruiser + ESLint restricted imports |
|
||
| `application` | domain, application-owned ports/contracts | presentation, concrete adapters, bootstrap, React, `window`, `localStorage`, `fetch` | architecture fixture |
|
||
| `presentation` | application facade, view-model types, shared UI primitive | adapters, raw DTO schema, registry storage implementation | restricted import rule |
|
||
| `adapters/*` | application-owned output ports, domain value contract if required | presentation, bootstrap internals, other adapter concrete implementation | dependency graph snapshot |
|
||
| `bootstrap` | presentation root, application factory, all selected adapters | page-specific business rule | composition-root review |
|
||
| `test fixtures` | public contracts, explicit test helpers, 그리고 테스트 대상 계층 + 선택된 test stack 패키지 (예시적) | production secret, real telemetry endpoint | test config guard |
|
||
|
||
`test fixtures` 행의 **May import 열은 예시(illustrative)이고 MUST NOT 열이 규범(normative)** 이다. 즉 `tests/**` 는 "production secret 모듈과 real telemetry endpoint 설정을 import 하지 않는다"는 forbidden-only 규칙으로 강제한다. allow-only 로 읽으면 `FE-D022` 가 의무화한 test stack(Vitest·RTL·MSW·Playwright·axe) 과 테스트 대상 계층 import 가 전부 금지되어 정상 테스트가 실패한다.
|
||
|
||
Normative dependency summary:
|
||
|
||
- `application -> adapters` concrete import는 `MUST NOT`이다.
|
||
- output port definition은 `application`이 `MUST` 소유한다.
|
||
- adapter는 application port를 구현하지만 application은 adapter 이름을 알면 안 된다.
|
||
- presentation은 application facade를 호출하며 raw backend envelope를 직접 다루면 안 된다.
|
||
- bootstrap만 concrete adapter를 조립할 수 있다.
|
||
- `adapters/transfer`는 `UploadTransferPort` 입력으로 **바이너리 descriptor**를 받는다. `adapters/file`·`adapters/blob-store`의 concrete 모듈을 import하지 않으며, descriptor 조립은 `bootstrap` 또는 application orchestration이 한다 (`DELEG-FE-008`).
|
||
- `adapters/realtime/push`는 `ServiceWorkerHostPort` **인터페이스**를 통해 SW 등록 상태를 읽는다. `adapters/service-worker`의 concrete 모듈을 import하지 않는다 (`DELEG-FE-007`).
|
||
- `src/workers/*.worker.js`와 `public/sw.js`는 **별도 실행 컨텍스트**이므로 `presentation`·`application`·`domain`을 import하지 않는다. 공유가 필요하면 `domain`의 순수 모듈만 참조하고 그 사실을 `FE-REG-CAPABILITY` 행에 기록한다.
|
||
- worker 엔트리는 `adapters/worker`가 소유한다. `bootstrap`이 직접 `new Worker()`를 부르지 않는다.
|
||
|
||
### 4.4 Port ownership matrix
|
||
|
||
| Port | Definition owner | Planned implementation | Consumer | Input / output | Failure vocabulary |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `ResourceQueryPort` | `application` | `adapters/http` | query use case | query object → validated model | `ApiFailure` |
|
||
| `ResourceCommandPort` | `application` | `adapters/http` | command use case | command + idempotency context → model | `ApiFailure` |
|
||
| `QueryCachePort` | `application` | `adapters/query-cache` (`TanStack Query`) | application query/mutation orchestration | registry query key + cache command → cache state/invalidation result | `QUERY_CACHE_FAILURE` |
|
||
| `AuthSessionPort` | `application` integration boundary | external auth adapter | routing + API client interceptor | opaque session state / request header callback | `AuthRequired`, `AuthIntegrationFailure` |
|
||
| `StoragePort` | `application` | `adapters/storage` | preference/session-neutral use case | classified key + serializable value | `StorageUnavailable`, `StorageQuotaExceeded` |
|
||
| `TelemetryPort` | `application` | `adapters/telemetry` | application + boundary | sanitized event → best-effort ack | `TelemetryDropped` internal only |
|
||
| `ClockPort` | `application` | browser/system clock adapter | retry/release logic | now / monotonic duration | no user-facing error |
|
||
| `ReleaseInfoPort` | `application` | runtime config/release adapter | boot + chunk recovery | release manifest → compatible release info | `RELEASE_MANIFEST_FAILURE`, `DEPLOY_MISMATCH` |
|
||
| `CapabilityPort` | `application` | `adapters/capability` | bootstrap + capability 소비 use case | capability ID → 활성 여부 + 사유 | `CAPABILITY_DISABLED`, `CAPABILITY_UNSUPPORTED` |
|
||
| `FileDialogPort` | `application` | `adapters/file` | 파일 입출력 use case | 선택 제약(accept·multiple·max) → 파일 descriptor 목록 / 저장 요청 → 저장 결과 | `FILE_PICKER_DISMISSED`, `FILE_REJECTED` |
|
||
| `BlobStorePort` | `application` | `adapters/blob-store` | 로컬 바이너리 보관 use case | 등록 key + 바이너리 descriptor → 저장/조회/삭제 결과 | `BLOB_STORE_UNAVAILABLE`, `BLOB_STORE_QUOTA_EXCEEDED` |
|
||
| `CachePersistencePort` | `application` | `adapters/cache-persistence` | `QueryCachePort` 구현 보조 | 캐시 스냅샷 + version tuple → 영속/복원 결과 | `CACHE_PERSISTENCE_FAILURE` |
|
||
| `CrossTabSyncPort` | `application` | `adapters/cross-tab` | 캐시 무효화 orchestration | 무효화 key 메시지 → 발행/수신 구독 | `CROSS_TAB_CHANNEL_UNAVAILABLE` |
|
||
| `UploadTransferPort` | `application` | `adapters/transfer/upload` | 업로드 use case | 전송 계획(source descriptor + presign 결과) → 진행 스트림 + 완료 결과 | `PRESIGN_EXPIRED`, `UPLOAD_PART_FAILED`, `TRANSFER_INTEGRITY_MISMATCH` |
|
||
| `StreamingDownloadPort` | `application` | `adapters/transfer/download` | 다운로드 use case | operation + range/resume 위치 → 진행 스트림 + 완료 결과 | `STREAM_INTERRUPTED` |
|
||
| `RealtimeSubscriptionPort` | `application` | `adapters/realtime/{sse,websocket,polling}` | 스트림 소비 use case | 구독 descriptor + resume cursor → 검증된 이벤트 스트림 + 해제 handle | `REALTIME_CONNECT_FAILED`, `REALTIME_DISCONNECTED`, `REALTIME_RESUME_GAP`, `EVENT_SCHEMA_MISMATCH` |
|
||
| `PushSubscriptionPort` | `application` | `adapters/realtime/push` | 알림 등록 use case | 권한 요청 + 공개키 → 구독 descriptor | `PUSH_PERMISSION_DENIED`, `PUSH_SUBSCRIPTION_EXPIRED` |
|
||
| `WorkerTaskPort` | `application` | `adapters/worker` | CPU 오프로드 use case | task 이름 + 직렬화 가능 입력 + timeout → 결과 또는 종료 | `WORKER_UNAVAILABLE`, `WORKER_TASK_TIMEOUT` |
|
||
| `ServiceWorkerHostPort` | `application` | `adapters/service-worker` | bootstrap + release 감시 | 등록 요청 → 등록/갱신 상태 스트림 | `SW_REGISTRATION_FAILED` |
|
||
| `BackgroundSyncPort` | `application` | `adapters/service-worker/sync` | 지연 mutation use case | keyed mutation descriptor → 큐 등록 결과 | `BACKGROUND_SYNC_UNSUPPORTED`, `BACKGROUND_SYNC_REPLAY_REJECTED` |
|
||
|
||
`AuthSessionPort`는 token 문자열을 domain/application model로 반환하지 않는 형태를 우선한다. header supplier나 opaque credential attachment callback을 사용하고, 구현 세부는 auth owner가 정한다.
|
||
|
||
**멀티프로토콜(`FE-OC-030`)은 신규 port가 0개다.** GraphQL·gRPC-Web·Connect-Web은 기존 `ResourceQueryPort`/`ResourceCommandPort`의 다른 **구현체**다. 프로토콜이 application에 새 인터페이스로 새면 `FE-D010`의 dependency inversion이 무너지고, backend가 REST에서 gRPC로 옮겨갈 때 use case를 다시 써야 한다. 프로토콜 선택은 `FE-REG-API.protocol` 필드 = **데이터**이지 타입이 아니다.
|
||
|
||
**Image CDN은 adapter가 아니라 `application`의 순수 정책이다.** URL 파생에는 I-O가 없다. `MediaUrlPolicy`를 `application/policies/`에 두고 CDN base·허용 transform은 `FE-REG-ENV` 행에서 읽는다. port로 만들면 테스트에 불필요한 test double만 늘어난다.
|
||
|
||
### 4.5 Composition root
|
||
|
||
Planned location:
|
||
|
||
```text
|
||
src/bootstrap/main.jsx
|
||
src/bootstrap/composition-root.js
|
||
```
|
||
|
||
Boot order는 다음을 `MUST` 따른다.
|
||
|
||
1. build identity 읽기
|
||
2. runtime config fetch
|
||
3. config envelope·schema·compatibility 검증
|
||
4. release manifest 정합성 확인
|
||
5. registry snapshot load
|
||
6. capability 해석 — flag × 브라우저 feature detection → 활성 capability 집합 확정
|
||
7. auth integration adapter 주입
|
||
8. HTTP/storage/telemetry/query-cache adapter 생성
|
||
9. 활성 capability 의 adapter 생성 (비활성 capability 의 adapter 는 생성하지 않음)
|
||
10. application facade 생성
|
||
11. router 생성
|
||
12. React root mount
|
||
|
||
2~4단계가 실패하면 product route를 mount하지 않고 boot error shell만 렌더한다. telemetry adapter 생성 실패는 console-safe fallback으로 계속 진행할 수 있다.
|
||
|
||
6단계와 9단계는 다음을 `MUST` 지킨다.
|
||
|
||
- 6단계는 실패하지 않는다. flag가 ON인데 브라우저가 미지원이면 해당 capability를 비활성으로 확정하고 `capability.activation.rejected` telemetry를 남긴 뒤 boot를 계속한다. capability 부재가 boot를 막으면 skeleton이 특정 브라우저에 묶인다.
|
||
- 9단계는 비활성 capability의 adapter 모듈을 **정적 import하지 않는다**. 정적 import하면 `FE-NFR-020`(비활성 시 initial JS 증가 0)이 깨지고 `FE-GATE-033`이 실패한다.
|
||
|
||
### 4.6 Planned directory blueprint
|
||
|
||
```text
|
||
src/
|
||
bootstrap/
|
||
main.jsx
|
||
composition-root.js
|
||
load-runtime-config.js
|
||
domain/
|
||
models/
|
||
policies/
|
||
application/
|
||
ports/
|
||
use-cases/
|
||
view-models/
|
||
policies/
|
||
presentation/
|
||
app/
|
||
routes/
|
||
pages/
|
||
components/
|
||
boundaries/
|
||
adapters/
|
||
http/
|
||
storage/
|
||
telemetry/
|
||
query-cache/
|
||
auth/
|
||
release/
|
||
capability/
|
||
file/
|
||
blob-store/
|
||
cache-persistence/
|
||
cross-tab/
|
||
transfer/
|
||
upload/
|
||
download/
|
||
protocol/
|
||
graphql/
|
||
grpc-web/
|
||
connect-web/
|
||
realtime/
|
||
sse/
|
||
websocket/
|
||
polling/
|
||
push/
|
||
worker/
|
||
service-worker/
|
||
sync/
|
||
contracts/
|
||
routes.js
|
||
api-operations.js
|
||
env.js
|
||
storage-keys.js
|
||
errors.js
|
||
query-keys.js
|
||
telemetry.js
|
||
release-tokens.js
|
||
capabilities.js
|
||
workers/
|
||
sample/
|
||
contract-fixture/
|
||
public/
|
||
sw.js
|
||
tests/
|
||
unit/
|
||
component/
|
||
integration/
|
||
e2e/
|
||
artifacts/
|
||
quality/
|
||
tests/
|
||
performance/
|
||
security/
|
||
release/
|
||
runbooks/
|
||
```
|
||
|
||
경로는 `planned`이며 repository가 생성될 때 변경될 수 있다. responsibility mapping이 유지되지 않으면 `FE-D009` 변경 절차를 거쳐야 한다.
|
||
|
||
---
|
||
|
||
## 5. Contract Registries
|
||
|
||
### 5.1 Registry owner map
|
||
|
||
| Registry ID | Registry | Planned path | Single owner | Ad hoc use failure |
|
||
| --- | --- | --- | --- | --- |
|
||
| `FE-REG-ROUTE` | route ID/path/params/access | `src/contracts/routes.js` | `feature-routing-navigation-guard-contract` | component에 literal route path 추가 |
|
||
| `FE-REG-API` | API method/path/operation/auth/timeout/idempotency/schema | `src/contracts/api-operations.js` | `feature-api-client-response-envelope-contract` | raw request config 또는 unregistered operation 사용 |
|
||
| `FE-REG-ENV` | build/runtime public config | `src/contracts/env.js` | `feature-frontend-env-runtime-config-contract` | registry 없는 `import.meta.env` 또는 config key 사용 |
|
||
| `FE-REG-STORAGE` | storage key/version/classification | `src/contracts/storage-keys.js` | `feature-frontend-storage-registry-contract` | raw `localStorage` key literal 사용 |
|
||
| `FE-REG-ERROR` | frontend error kind/code/default UX | `src/contracts/errors.js` | `feature-frontend-error-classification-boundary-contract` | raw status/message로 UI 분기 |
|
||
| `FE-REG-QUERY` | query key factory/invalidation | `src/contracts/query-keys.js` | `feature-server-state-caching-contract` | page 안에서 ad hoc array key 생성 |
|
||
| `FE-REG-TELEMETRY` | event/attribute/redaction | `src/contracts/telemetry.js` | `feature-frontend-observability-logging-trace-contract` | 자유 문자열 event 전송 |
|
||
| `FE-REG-RELEASE` | build/config/API/release token | `src/contracts/release-tokens.js` | `feature-frontend-release-cache-rollback-contract` | string version 비교 또는 cache key 직접 작성 |
|
||
|
||
### 5.2 Route registry minimum schema
|
||
|
||
| Field | Required | Rule |
|
||
| --- | --- | --- |
|
||
| `routeId` | yes | stable `UPPER_SNAKE_CASE`; rename은 breaking |
|
||
| `path` | yes | centralized literal; component 내부 literal 금지 |
|
||
| `paramsSchema` | conditional | dynamic param이 있으면 runtime validation |
|
||
| `searchSchema` | conditional | query string을 application input으로 넘기기 전 validation |
|
||
| `access` | yes | `public`, `session-required`, `integration-defined` |
|
||
| `loadingSurface` | yes | route-level fallback owner |
|
||
| `errorSurface` | yes | route-level error owner |
|
||
| `chunkId` | generated | release manifest와 매핑 |
|
||
|
||
Initial planned rows:
|
||
|
||
| routeId | path | access | Notes |
|
||
| --- | --- | --- | --- |
|
||
| `APP_HOME` | `/` | `public` | sample shell |
|
||
| `SAMPLE_RESOURCE_LIST` | `/sample/resources` | `integration-defined` | contract fixture |
|
||
| `NOT_FOUND` | `*` | `public` | no API retry |
|
||
|
||
### 5.3 API operation registry minimum schema
|
||
|
||
모든 shared-client request는 아래 필드가 채워진 `FE-REG-API` row를 먼저 가져야 한다. raw path·timeout·auth·schema를 call site에서 다시 정의하면 registry violation이다.
|
||
|
||
| Field | Required | Rule |
|
||
| --- | --- | --- |
|
||
| `method` | yes | uppercase HTTP method |
|
||
| `path` | yes | path template; query value와 host를 포함하지 않음 |
|
||
| `operationId` | yes | stable `UPPER_SNAKE_CASE`; telemetry·test·owner key |
|
||
| `auth` | yes | `none` 또는 `external-session` |
|
||
| `timeoutMs` | yes | default `10000`; override는 decision change 필요 |
|
||
| `idempotency` | yes | `safe`, `keyed`, `none` 중 하나 |
|
||
| `requestSchema` | yes | body가 없으면 explicit `none`; params/search도 검증 |
|
||
| `responseSchema` | yes | success envelope의 payload schema reference |
|
||
| `owner` | yes | owning feature or branch slug |
|
||
|
||
Initial planned rows:
|
||
|
||
| operationId | method | path | auth | timeoutMs | idempotency | requestSchema | responseSchema | owner |
|
||
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||
| `LIST_SAMPLE_RESOURCES` | `GET` | `/api/sample/resources` | `external-session` | `10000` | `safe` | `SampleResourceListQuery` | `SampleResourceListPayload` | `feature-sample-feature-slice-contract-fixture` |
|
||
| `CREATE_SAMPLE_RESOURCE` | `POST` | `/api/sample/resources` | `external-session` | `10000` | `keyed` | `CreateSampleResourceCommand` | `SampleResourcePayload` | `feature-sample-feature-slice-contract-fixture` |
|
||
|
||
### 5.4 Environment registry minimum schema
|
||
|
||
| Key | Phase | Classification | Required | Default | Failure |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `VITE_BUILD_ID` | build | public metadata | yes | none | build fail |
|
||
| `VITE_COMMIT_SHA` | build | public metadata | yes in CI | local sentinel allowed | release evidence fail |
|
||
| `VITE_ROUTER_BASE_PATH` | build | non-secret compile-time constant | yes | `/` | route mount fail |
|
||
| `VITE_RUNTIME_CONFIG_URL` | build | non-secret compile-time constant | yes | `/config.json` | boot fail |
|
||
| `APP_ENV` | runtime | public | yes | none | boot fail |
|
||
| `API_BASE_URL` | runtime | public-sensitive | yes | none | boot fail |
|
||
| `REQUEST_TIMEOUT_MS` | runtime | public | no | `10000` | invalid value boot fail |
|
||
| `MAX_RETRY_ATTEMPTS` | runtime | public | no | `2` after initial | invalid value boot fail |
|
||
| `TELEMETRY_ENABLED` | runtime | public | yes | `false` | invalid value boot fail |
|
||
| `TELEMETRY_ENDPOINT` | runtime | public-sensitive | conditional | none | telemetry degrade |
|
||
| `AUTH_MODE` | runtime | public | yes | `external` | unsupported mode boot fail |
|
||
| `CONFIG_SCHEMA_VERSION` | runtime | public | yes | none | compatibility fail |
|
||
| `API_CONTRACT_VERSION` | runtime | public | yes | none | compatibility fail |
|
||
| `RELEASE_MANIFEST_URL` | runtime | public | yes | `/release-manifest.json` | mismatch detection degrade/fail per policy |
|
||
|
||
`public-sensitive`는 browser에서 볼 수 있지만 로그·telemetry에 원문을 남기지 않는 endpoint-like value를 뜻한다. secret 분류가 아니다.
|
||
|
||
### 5.5 Storage registry minimum schema
|
||
|
||
| Field | Required | Rule |
|
||
| --- | --- | --- |
|
||
| `logicalName` | yes | 의미 이름, raw key가 아님 |
|
||
| `physicalKey` | yes | `<app>:<scope>:v<schema>:<name>` |
|
||
| `backend` | yes | `memory`, `sessionStorage`, `localStorage`, `indexedDB` |
|
||
| `classification` | yes | `public-preference`, `opaque-cache`, `sensitive-forbidden` |
|
||
| `schemaVersion` | yes | incompatible change 시 increment |
|
||
| `ttl` | conditional | persistent cache는 expiry 필수 |
|
||
| `migration` | conditional | previous version을 읽으면 migration 또는 discard |
|
||
| `quotaFallback` | yes | memory/no-persist/feature-disable 중 하나 |
|
||
|
||
Initial planned rows:
|
||
|
||
| logicalName | backend | classification | TTL / fallback |
|
||
| --- | --- | --- | --- |
|
||
| `COLOR_SCHEME` | `localStorage` | `public-preference` | no TTL / system default |
|
||
| `CHUNK_RELOAD_GUARD` | `sessionStorage` | `opaque-cache` | session / no second auto reload |
|
||
| `QUERY_PERSISTENCE` | disabled | `sensitive-forbidden` default | opt-in contract required |
|
||
| `AUTH_TOKEN` | forbidden | `sensitive-forbidden` | external auth owner only |
|
||
|
||
### 5.6 Error registry minimum schema
|
||
|
||
| Field | Required | Rule |
|
||
| --- | --- | --- |
|
||
| `kind` | yes | frontend stable enum |
|
||
| `defaultRetryable` | yes | request context가 override 가능 |
|
||
| `severity` | yes | telemetry routing hint, user copy와 분리 |
|
||
| `userMessageKey` | yes | raw backend message 사용 금지 |
|
||
| `action` | yes | `retry`, `reauth`, `navigate`, `reload-once`, `contact-support`, `none` |
|
||
| `telemetryEvent` | yes | registry event에 매핑 |
|
||
| `redaction` | yes | cause/body/header drop rule |
|
||
|
||
Planned `kind` enum:
|
||
|
||
```text
|
||
NETWORK_UNREACHABLE
|
||
REQUEST_TIMEOUT
|
||
REQUEST_ABORTED
|
||
CONTENT_TYPE_MISMATCH
|
||
MALFORMED_JSON
|
||
ENVELOPE_MISMATCH
|
||
SCHEMA_MISMATCH
|
||
AUTH_REQUIRED
|
||
AUTH_INTEGRATION_FAILURE
|
||
FORBIDDEN
|
||
NOT_FOUND
|
||
CONFLICT
|
||
VALIDATION_REJECTED
|
||
UNKNOWN_CLIENT_FAILURE
|
||
RATE_LIMITED
|
||
SERVER_FAILURE
|
||
CHUNK_LOAD_FAILURE
|
||
BOOT_CONFIG_FAILURE
|
||
RELEASE_MANIFEST_FAILURE
|
||
DEPLOY_MISMATCH
|
||
STORAGE_UNAVAILABLE
|
||
STORAGE_QUOTA_EXCEEDED
|
||
RENDER_FAILURE
|
||
TELEMETRY_FAILURE
|
||
QUERY_CACHE_FAILURE
|
||
UNKNOWN_FAILURE
|
||
```
|
||
|
||
### 5.7 Query key registry minimum schema
|
||
|
||
Query key는 factory로만 생성한다.
|
||
|
||
```text
|
||
queryKeys.resource.all()
|
||
queryKeys.resource.list(filters)
|
||
queryKeys.resource.detail(resourceId)
|
||
```
|
||
|
||
| Rule | Normative behavior |
|
||
| --- | --- |
|
||
| namespace | feature prefix를 첫 element로 사용 |
|
||
| serialization | object key ordering을 canonicalize |
|
||
| identity | PII, token, raw URL을 key에 넣지 않음 |
|
||
| invalidation | mutation outcome과 mapping된 factory만 invalidate |
|
||
| version | API/schema breaking change 시 namespace version bump |
|
||
| persistence | default disabled; opt-in 시 release/config version partition |
|
||
|
||
### 5.8 Telemetry registry minimum schema
|
||
|
||
| Field | Required | Rule |
|
||
| --- | --- | --- |
|
||
| `eventName` | yes | stable dotted name |
|
||
| `trigger` | yes | 발생 시점 단일 정의 |
|
||
| `requiredAttributes` | yes | low-cardinality only |
|
||
| `optionalAttributes` | yes | absence-safe |
|
||
| `forbiddenAttributes` | yes | token, email, raw URL/query/body, storage value |
|
||
| `sampling` | yes | error/security event는 별도 정책 |
|
||
| `delivery` | yes | best-effort, audit channel 아님 |
|
||
|
||
Initial planned events:
|
||
|
||
| Event | Trigger | Required attributes |
|
||
| --- | --- | --- |
|
||
| `app.boot.failed` | config/release validation 실패 | `error_kind`, `build_id`, `config_schema_version` |
|
||
| `api.request.failed` | terminal normalized API failure | `error_kind`, `http_status_group`, `attempt_count_bucket`, `route_id` |
|
||
| `ui.render.failed` | React boundary catch | `route_id`, `build_id`, `component_boundary` |
|
||
| `release.mismatch.detected` | chunk/config/API version mismatch | `build_id`, `active_release_id`, `mismatch_kind` |
|
||
| `telemetry.delivery.dropped` | sink/queue failure | `reason`, `queue_size_bucket` |
|
||
|
||
### 5.9 Release token registry minimum schema
|
||
|
||
| Token | Source | Compatibility role |
|
||
| --- | --- | --- |
|
||
| `appVersion` | manifest | human release label |
|
||
| `buildId` | CI build | asset/HTML coherence |
|
||
| `commitSha` | VCS | source traceability |
|
||
| `configSchemaVersion` | runtime config schema | boot compatibility |
|
||
| `apiContractVersion` | frontend/backend agreement | schema compatibility |
|
||
| `assetManifestHash` | build output | chunk integrity/mismatch |
|
||
| `releaseId` | deploy system | rollback target |
|
||
| `builtAt` | CI | diagnostics, not cache identity |
|
||
|
||
### 5.10 Registry change protocol
|
||
|
||
1. owner branch에 decision 또는 change row를 먼저 추가한다.
|
||
2. registry schema validation을 갱신한다.
|
||
3. compatibility impact를 기록한다.
|
||
4. breaking이면 version bump와 migration·discard·fallback 중 하나를 정한다.
|
||
5. producer와 consumer test를 함께 갱신한다.
|
||
6. snapshot artifact를 생성한다.
|
||
7. release note에 affected `FE-OC-*`와 rollback condition을 적는다.
|
||
8. orphan token scan이 0건이어야 merge할 수 있다.
|
||
|
||
---
|
||
|
||
## 6. Build-time, Runtime, Secret Configuration
|
||
|
||
### 6.1 Three-way distinction
|
||
|
||
| Class | Example | Visible to browser | Change mechanism | Cache policy | Rule |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| build-time public | `BUILD_ID`, `COMMIT_SHA`, `ROUTER_BASE_PATH` | yes | rebuild | bundled | compiler behavior·asset identity만 |
|
||
| runtime public | `API_BASE_URL`, feature-public flag, telemetry endpoint | yes | runtime config publish | `no-store` | boot before React mount |
|
||
| secret | client secret, private key, DB credential, refresh token policy material | should not be bundled | server/auth owner | N/A | frontend env·bundle·HTML에 넣지 않음 |
|
||
|
||
`VITE_*` prefix는 build ID·commit SHA 같은 build metadata와 base path·`/config.json` 위치 같은 non-secret compile-time constant에만 사용한다. API endpoint, telemetry endpoint, public feature flag처럼 배포 후 달라질 수 있는 값은 `/config.json`에서 읽는다. 이름에 `SECRET`, `PASSWORD`, `PRIVATE_KEY`, `TOKEN`이 포함된 key는 build와 runtime registry 모두에서 거부한다. auth owner가 browser storage를 사용해야 한다면 별도 threat model과 owner evidence가 필요하며 본 skeleton default가 아니다.
|
||
|
||
### 6.2 Conditional deployment defaults
|
||
|
||
- If hosting이 runtime config를 HTML보다 먼저 atomic publish할 수 있음 → `FE-D012` runtime config 사용.
|
||
- If hosting이 정적 파일만 제공하고 atomic config publish가 불가능함 → environment별 rebuild를 허용하되 artifact를 env 간 재사용하지 않음.
|
||
- If SSR/edge runtime이 도입됨 → 본 config contract를 그대로 적용하지 않고 별도 project fork decision 필요.
|
||
|
||
<!-- section-id: runtime-flow -->
|
||
### 6.3 Boot sequence
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant Browser
|
||
participant HTML as index.html
|
||
participant Boot as bootstrap
|
||
participant Config as /config.json
|
||
participant Release as release-manifest.json
|
||
participant Schema as Zod schemas
|
||
participant App as React App
|
||
|
||
Browser->>HTML: GET index.html
|
||
HTML-->>Browser: no-cache app shell
|
||
Browser->>Boot: load hashed entry chunk
|
||
Boot->>Config: GET runtime config (no-store)
|
||
Boot->>Release: GET release manifest (no-store)
|
||
Boot->>Schema: validate config + compatibility
|
||
alt valid and compatible
|
||
Schema-->>Boot: normalized public config
|
||
Boot->>App: compose dependencies and mount
|
||
else invalid config
|
||
Schema-->>Boot: BOOT_CONFIG_FAILURE
|
||
Boot-->>Browser: boot error shell, product routes not mounted
|
||
else version mismatch
|
||
Schema-->>Boot: DEPLOY_MISMATCH
|
||
Boot-->>Browser: controlled recovery UI, no reload loop
|
||
end
|
||
```
|
||
|
||
### 6.4 Runtime config validation
|
||
|
||
Validation MUST cover:
|
||
|
||
- required key presence
|
||
- URL protocol allowlist (`https` in production policy; local exception documented)
|
||
- integer range for timeout/retry
|
||
- boolean parsing without truthy string ambiguity
|
||
- config schema version compatibility
|
||
- API contract version compatibility
|
||
- release/build ID coherence when provider exposes both
|
||
- unknown key policy: additive keys allowed only if schema explicitly passthroughs; default strict for safety
|
||
|
||
Boot failure output MUST contain safe fields only:
|
||
|
||
```text
|
||
error.kind
|
||
error.code
|
||
buildId
|
||
configSchemaVersion
|
||
releaseId (if present)
|
||
supportReference
|
||
```
|
||
|
||
Endpoint, query, header, raw config object, stack은 user-facing screen에 표시하지 않는다.
|
||
|
||
---
|
||
|
||
## 7. API Client Operational Contract
|
||
|
||
### 7.1 Shared client boundary
|
||
|
||
모든 API request는 application output port를 구현한 shared HTTP adapter를 통과해야 한다.
|
||
|
||
Page/component MUST NOT:
|
||
|
||
- 직접 `fetch` 호출
|
||
- `AbortController` timeout 구현 복제
|
||
- backend status를 user copy로 직접 변환
|
||
- raw response body를 log
|
||
- page-local retry loop 생성
|
||
- auth token을 storage에서 읽음
|
||
|
||
### 7.2 Request context
|
||
|
||
각 logical request는 다음 context를 가진다.
|
||
|
||
| Field | Required | Rule |
|
||
| --- | --- | --- |
|
||
| `operationId` | yes | registry-backed stable name |
|
||
| `method` | yes | uppercase HTTP method |
|
||
| `routeId` | yes | raw URL 대신 low-cardinality route ID |
|
||
| `timeoutMs` | yes | default 10000, operation override는 owner decision 필요 |
|
||
| `idempotency` | yes | `safe`, `keyed`, `none` |
|
||
| `attempt` | yes | initial=0, retry=1..N |
|
||
| `abortReason` | optional | `navigation`, `user`, `timeout`, `superseded` |
|
||
| `authMode` | yes | `none`, `external-session` |
|
||
|
||
### 7.3 Response envelope
|
||
|
||
Expected success shape:
|
||
|
||
```text
|
||
success: true
|
||
data: <payload>
|
||
meta.requestId
|
||
meta.traceId
|
||
meta.correlationId (optional if backend contract omits)
|
||
```
|
||
|
||
Expected failure shape:
|
||
|
||
```text
|
||
success: false
|
||
error.code
|
||
error.category
|
||
error.message
|
||
error.retryable
|
||
error.details (optional, client-safe)
|
||
meta.requestId
|
||
meta.traceId
|
||
```
|
||
|
||
Processing order:
|
||
|
||
1. HTTP transport completion
|
||
2. content-type expectation check
|
||
3. JSON parse
|
||
4. envelope schema validation
|
||
5. success/failure branch validation
|
||
6. payload schema validation
|
||
7. DTO → application model mapper
|
||
8. application result 또는 normalized failure 반환
|
||
|
||
Stage 7은 adapter 경계에서 **validated model**까지만 만든다. view-model 투영은 §4.2/§4.4가 정한 대로 `application`이 소유하며 `application/view-models/`에 둔다(2-stage 매핑). 따라서 `QueryCachePort`가 담는 것은 view-model이 아니라 model이다.
|
||
|
||
`200`이더라도 JSON/envelope/payload가 invalid하면 success로 반환하지 않는다. `4xx/5xx` body가 invalid하면 status 기반 safe fallback error를 만들고 raw body는 버린다.
|
||
|
||
### 7.4 Timeout and abort
|
||
|
||
| Situation | Classification | Retry | Telemetry | UX |
|
||
| --- | --- | --- | --- | --- |
|
||
| 10s total timeout | `REQUEST_TIMEOUT` | safe/keyed만 policy 적용 | terminal일 때 1 event | retry action |
|
||
| navigation cancel | `REQUEST_ABORTED` | no | debug counter only, error event 없음 | stale surface 제거 |
|
||
| user cancel | `REQUEST_ABORTED` | no | optional interaction event | neutral canceled state |
|
||
| superseded query | `REQUEST_ABORTED` | no | none | latest request 유지 |
|
||
| external signal abort | reason에 따라 | no unless timeout owner | redacted reason | context-specific |
|
||
|
||
Browser `fetch`는 portable connect/read timeout을 분리 제공하지 않으므로 이 문서는 total timeout만 기본값으로 둔다. 별도 transport가 도입되기 전 connect timeout을 구현 사실처럼 말하지 않는다.
|
||
|
||
### 7.5 Retry algorithm
|
||
|
||
Initial default:
|
||
|
||
```text
|
||
maxRetries = 2
|
||
baseDelayMs = 250
|
||
maxDelayMs = 2000
|
||
algorithm = min(maxDelayMs, baseDelayMs * 2^retryIndex) * random(0, 1)
|
||
jitter = full jitter
|
||
```
|
||
|
||
Normative rules:
|
||
|
||
- initial request는 retry count에 포함하지 않는다.
|
||
- retry schedule은 `ClockPort`와 injectable random source로 test 가능해야 한다.
|
||
- `REQUEST_ABORTED`, `MALFORMED_JSON`, `ENVELOPE_MISMATCH`, `SCHEMA_MISMATCH`, `401`, `403`, `404`, `409`, `422`는 default non-retryable이다.
|
||
- network failure, timeout, `429`, `502`, `503`, `504`는 safe/keyed request에서만 retry candidate다.
|
||
- generic `500`은 automatic retry default off; operation owner가 safe condition을 증명해야 opt-in 가능하다.
|
||
- browser offline signal은 hint일 뿐 최종 truth로 사용하지 않는다.
|
||
- retry 중 component가 unmount되거나 query가 superseded되면 남은 timer와 request를 취소한다.
|
||
|
||
### 7.6 `Retry-After`
|
||
|
||
`429` 또는 backend가 명시한 retryable response에 `Retry-After`가 있으면 다음 순서를 따른다.
|
||
|
||
1. delta-seconds 또는 HTTP-date parse
|
||
2. invalid/negative면 local backoff 사용
|
||
3. valid delay가 30s를 넘으면 automatic retry하지 않고 terminal `RATE_LIMITED` UX로 전환
|
||
4. valid delay가 30s 이하면 local backoff와 비교해 더 긴 값을 사용
|
||
5. navigation/user abort 발생 시 wait 취소
|
||
|
||
`Retry-After` raw value를 telemetry에 남기지 않고 normalized delay bucket만 남긴다.
|
||
|
||
### 7.7 Idempotency
|
||
|
||
Mutation retry conditions:
|
||
|
||
- backend contract가 `Idempotency-Key`를 지원한다고 registry에 명시
|
||
- 한 logical user action에 하나의 key 사용
|
||
- retry마다 같은 key 재사용
|
||
- 새 user action은 새 key 사용
|
||
- key는 telemetry, URL, user message에 노출하지 않음
|
||
- concurrent double-submit은 같은 logical action이면 client-side single-flight 또는 UI disable로 합침
|
||
- backend가 replay 여부를 반환하면 result metadata로만 소비
|
||
|
||
Key 생성 책임은 auth token lifecycle과 분리한다. key persistence가 필요하면 storage registry에 TTL·classification·migration을 추가하기 전에는 memory-only다.
|
||
|
||
### 7.8 Auth integration boundary
|
||
|
||
Skeleton owns:
|
||
|
||
- route가 요구하는 session state 소비
|
||
- request 전 `AuthSessionPort.attach(request)` 호출
|
||
- `401`을 `AUTH_REQUIRED`로 정규화
|
||
- `403`을 `FORBIDDEN`으로 정규화
|
||
- auth owner callback으로 unauthenticated transition 알림
|
||
- navigation guard는 UX hint이며 backend authorization을 대체하지 않는다는 규칙
|
||
|
||
Skeleton does not own:
|
||
|
||
- authorization code exchange
|
||
- token 저장 위치
|
||
- access token refresh
|
||
- refresh token rotation
|
||
- logout propagation
|
||
- revocation
|
||
- identity provider redirect detail
|
||
- backend permission decision
|
||
|
||
`401` recovery는 session state transition만 소유하며 credential 획득·저장·회전은 계속 외부 auth owner가 소유한다. logical request당 recovery callback은 최대 1회다.
|
||
|
||
| Current session state | Event | Next state | Skeleton action |
|
||
| --- | --- | --- | --- |
|
||
| `authenticated` | first `401` | `recovery-pending` | external owner의 bounded recovery callback 1회 호출 |
|
||
| `recovery-pending` | owner reports session restored | `authenticated` | 아래 replay policy 적용 |
|
||
| `recovery-pending` | owner reports no session | `unauthenticated` | terminal `AUTH_REQUIRED` 반환 |
|
||
| `recovery-pending` | adapter throws/rejects/invalid result | `integration-failed` | terminal `AUTH_INTEGRATION_FAILURE` 반환 |
|
||
| any | second `401` for same logical request | `unauthenticated` | 추가 recovery 없이 terminal `AUTH_REQUIRED` 반환 |
|
||
|
||
Recovery 이후 replay policy:
|
||
|
||
- `safe` request는 같은 logical request context로 최대 1회 replay할 수 있다.
|
||
- `keyed` mutation은 같은 stable idempotency key와 active backend replay contract를 유지할 때만 최대 1회 replay할 수 있다.
|
||
- `none`인 unkeyed mutation은 recovery 성공 후에도 `MUST NOT` replay한다. UI는 명시적 재시도를 요구한다.
|
||
- replay와 일반 retry를 합친 총 시도 횟수는 operation registry와 test fixture가 추적하며 recovery loop를 만들 수 없다.
|
||
|
||
<!-- section-id: sequence -->
|
||
### 7.9 Request sequence
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
actor User
|
||
participant UI as Presentation
|
||
participant App as Application
|
||
participant Query as QueryCachePort / TanStack adapter
|
||
participant HTTP as HTTP Adapter
|
||
participant Auth as AuthSessionPort
|
||
participant API as Backend API
|
||
participant Schema as Runtime Schema
|
||
|
||
User->>UI: route enter or action
|
||
UI->>App: execute use case
|
||
App->>Query: query/mutation with registry key
|
||
Query->>HTTP: application output port
|
||
HTTP->>Auth: attach opaque session context
|
||
Auth-->>HTTP: request-ready callback result
|
||
HTTP->>API: request + timeout signal
|
||
alt success envelope
|
||
API-->>HTTP: JSON response
|
||
HTTP->>Schema: envelope + payload validate
|
||
Schema-->>HTTP: normalized model
|
||
HTTP-->>Query: result
|
||
Query-->>App: cache state
|
||
App-->>UI: view-model
|
||
UI-->>User: success or empty
|
||
else retry candidate
|
||
API-->>HTTP: network/429/502/503/504
|
||
HTTP->>HTTP: bounded backoff + jitter
|
||
HTTP-->>Query: result or terminal failure
|
||
Query-->>App: refreshing or terminal failure
|
||
App-->>UI: safe view state and action
|
||
else contract/auth failure
|
||
API-->>HTTP: invalid schema / 401 / 403
|
||
HTTP-->>Query: non-retryable normalized failure
|
||
Query-->>App: normalized failure
|
||
App-->>UI: safe action and message key
|
||
else query-cache adapter failure
|
||
Query-->>App: QUERY_CACHE_FAILURE
|
||
App-->>UI: uncached-safe fallback or terminal error
|
||
end
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Frontend Failure Taxonomy
|
||
|
||
### 8.1 Normalized failure shape
|
||
|
||
```text
|
||
kind
|
||
code
|
||
httpStatus (optional)
|
||
retryable
|
||
operationId
|
||
attemptCount
|
||
requestId (optional)
|
||
traceId (optional)
|
||
userMessageKey
|
||
action
|
||
causeClass (internal allowlist only)
|
||
```
|
||
|
||
Raw response body, token, authorization header, full URL/query, stack, storage value는 normalized failure에 포함하지 않는다.
|
||
|
||
### 8.2 Failure matrix
|
||
|
||
| Trigger | Normalized kind | Auto retry | Fallback | User UX | Telemetry rule |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| DNS/offline/CORS-like opaque network failure | `NETWORK_UNREACHABLE` | safe/keyed, max 2 | cached safe data if available | offline/network message + manual retry | terminal 1회, raw URL 금지 |
|
||
| total timeout | `REQUEST_TIMEOUT` | safe/keyed, max 2 | stale data 유지 가능 | timeout message + retry | elapsed bucket, attempts |
|
||
| navigation abort | `REQUEST_ABORTED` | no | latest route state | error toast 금지 | error event 금지 |
|
||
| user abort | `REQUEST_ABORTED` | no | neutral canceled state | canceled label if needed | interaction-only |
|
||
| JSON operation의 response `Content-Type` 불일치 | `CONTENT_TYPE_MISMATCH` | no | prior safe cache 또는 error | incompatible response message | expected/actual media type category only |
|
||
| response not valid JSON | `MALFORMED_JSON` | no | prior safe cache 또는 error | contract failure message | content-type/status group only |
|
||
| top-level envelope missing/invalid | `ENVELOPE_MISMATCH` | no | prior safe cache 또는 error | service response incompatible | schema version, no body |
|
||
| payload schema invalid | `SCHEMA_MISMATCH` | no | prior safe cache 또는 error | update/support message | schema ID + safe issue path count |
|
||
| HTTP 401 | `AUTH_REQUIRED` | no in skeleton | external auth callback | sign-in/re-auth action | route ID, no principal/token |
|
||
| auth attach/recovery adapter throws, rejects, or returns invalid state | `AUTH_INTEGRATION_FAILURE` | no | unauthenticated-safe shell | sign-in/support action | phase + safe adapter outcome only |
|
||
| HTTP 403 | `FORBIDDEN` | no | keep shell | permission message, no retry | operation + status |
|
||
| HTTP 404 | `NOT_FOUND` | no | route/resource not-found | navigate back/home | low severity |
|
||
| HTTP 409 | `CONFLICT` | no | refetch authoritative data | conflict resolution action | operation + safe backend code |
|
||
| HTTP 422 | `VALIDATION_REJECTED` | no | preserve user input | field/form safe details | field names allowlist only |
|
||
| other 4xx | `UNKNOWN_CLIENT_FAILURE` | no | preserve safe shell/state | generic request correction/support action | operation + status group only |
|
||
| HTTP 429 | `RATE_LIMITED` | safe/keyed + bounded `Retry-After` | stale data if safe | countdown/manual retry | delay bucket, attempts |
|
||
| HTTP 500 | `SERVER_FAILURE` | default no | stale safe data | service failure | error code/status group |
|
||
| HTTP 502 | `SERVER_FAILURE` | safe/keyed, max 2 | stale safe data | temporary service failure | attempts + terminal |
|
||
| HTTP 503 | `SERVER_FAILURE` | safe/keyed, max 2 | stale safe data | temporary service failure | `Retry-After` bucket |
|
||
| HTTP 504 | `SERVER_FAILURE` | safe/keyed, max 2 | stale safe data | gateway timeout | attempts + duration bucket |
|
||
| other 5xx | `SERVER_FAILURE` | default no | safe fallback | service failure | status group only |
|
||
| chunk fetch fails | `CHUNK_LOAD_FAILURE` | one controlled reload only after release check | current shell | update/reload action | build/release IDs |
|
||
| runtime config missing/invalid | `BOOT_CONFIG_FAILURE` | one refetch allowed | boot error shell | support reference | safe config schema fields |
|
||
| release manifest fetch, parse, or schema validation failure | `RELEASE_MANIFEST_FAILURE` | one bounded refetch at boot only | boot/update shell | update/support action | phase + build ID, no raw manifest |
|
||
| HTML/asset/config release mismatch | `DEPLOY_MISMATCH` | no request retry | controlled reload once or rollback | update message | mismatch kind + IDs |
|
||
| storage API unavailable/security error | `STORAGE_UNAVAILABLE` | no | memory-only | usually silent, feature note if needed | backend type + reason enum |
|
||
| storage quota exceeded | `STORAGE_QUOTA_EXCEEDED` | no | evict allowed cache then memory-only | non-blocking notice if feature affected | quota bucket, no values |
|
||
| React render throws | `RENDER_FAILURE` | no auto retry | nearest boundary shell | retry route / reload action | component boundary + build ID |
|
||
| telemetry endpoint/network fails | `TELEMETRY_FAILURE` | bounded internal queue only | console-safe/drop | no product error | self-metric, no recursion |
|
||
| `QueryCachePort` read/write/invalidate throws or returns an invalid result | `QUERY_CACHE_FAILURE` | no automatic request retry | operation-declared uncached mode만 허용, 아니면 terminal | retry/support action; stale 표시를 위조하지 않음 | phase + query namespace, raw key/data 금지 |
|
||
| unknown thrown value | `UNKNOWN_FAILURE` | no | nearest safe boundary | generic reference | type allowlist only |
|
||
|
||
Normalization은 total function이어야 한다. response/adapter/browser exception이 위 named branch와 일치하지 않거나 mapper 자체가 실패하면 최종 catch-all이 raw value를 폐기하고 `UNKNOWN_FAILURE`를 반환한다. normalized failure를 만들지 못한 채 throw를 presentation으로 통과시키는 경로는 허용하지 않는다.
|
||
|
||
### 8.3 Retry decision order
|
||
|
||
```text
|
||
if aborted by navigation/user/superseded -> do not retry
|
||
else if parse/envelope/schema/auth/authz/not-found/conflict/validation -> do not retry
|
||
else if method is safe -> apply status/network policy
|
||
else if idempotency mode is keyed and backend contract is active -> apply status/network policy
|
||
else -> do not retry
|
||
```
|
||
|
||
Backend `error.retryable=true`는 necessary hint일 수 있지만 frontend가 unsafe mutation을 자동 retry할 충분 조건은 아니다. method/idempotency/client cap을 함께 만족해야 한다.
|
||
|
||
### 8.4 UX action vocabulary
|
||
|
||
| Action | When allowed | MUST NOT do |
|
||
| --- | --- | --- |
|
||
| `retry` | terminal retryable failure | infinite spinner 또는 hidden loop |
|
||
| `reauth` | `AUTH_REQUIRED` + external owner available | token lifecycle 직접 구현 |
|
||
| `navigate` | not-found/forbidden route recovery | history loop |
|
||
| `reload-once` | confirmed chunk/deploy mismatch | session guard 없이 반복 reload |
|
||
| `contact-support` | schema/internal repeated failure | raw stack/body 노출 |
|
||
| `none` | abort, telemetry-only degradation | user에게 false error 표시 |
|
||
|
||
### 8.5 Required negative fixtures
|
||
|
||
| Fixture | Expected normalized result |
|
||
| --- | --- |
|
||
| JSON operation + `text/html` response | `CONTENT_TYPE_MISMATCH` |
|
||
| auth attach callback throw/reject | `AUTH_INTEGRATION_FAILURE` |
|
||
| bounded recovery invalid state | `AUTH_INTEGRATION_FAILURE` |
|
||
| release manifest network/parse/schema failure | `RELEASE_MANIFEST_FAILURE` |
|
||
| QueryCachePort adapter throw 또는 invalid cache result | `QUERY_CACHE_FAILURE` |
|
||
| unregistered `418` or other unmapped 4xx | `UNKNOWN_CLIENT_FAILURE` |
|
||
| thrown non-`Error` object, symbol, or mapper exception | `UNKNOWN_FAILURE` |
|
||
| recovery succeeds for unkeyed mutation | no replay; terminal action requires explicit user retry |
|
||
|
||
---
|
||
|
||
## 9. Async UI, Query Cache, Routing, Storage
|
||
|
||
### 9.1 Async surface state model
|
||
|
||
Required visible states:
|
||
|
||
| State | Data | Activity | UI requirement |
|
||
| --- | --- | --- | --- |
|
||
| `initial-loading` | none | first request | stable skeleton, focus theft 금지 |
|
||
| `success` | present | idle | view-model render |
|
||
| `empty` | valid empty | idle | empty reason + primary action if applicable |
|
||
| `terminal-error` | none or unusable | stopped | safe message + registry action |
|
||
|
||
Additional non-blocking states:
|
||
|
||
| State | Data | Activity | UI requirement |
|
||
| --- | --- | --- | --- |
|
||
| `refreshing` | stale/present | background | existing content 유지, subtle indicator |
|
||
| `stale-degraded` | cached | retry exhausted | stale label + manual retry |
|
||
| `mutation-pending` | current view | write in flight | duplicate action 차단 |
|
||
| `mutation-conflict` | authoritative refetch needed | stopped | conflict action |
|
||
|
||
`loading` boolean 하나로 empty/error/refreshing을 합치면 contract violation이다.
|
||
|
||
### 9.2 Query cache defaults
|
||
|
||
Application use case는 `QueryCachePort`만 호출한다. `bootstrap/composition-root.js`가 `adapters/query-cache`의 TanStack Query implementation을 생성해 application facade에 주입하며, presentation과 application은 TanStack Query client를 직접 import하지 않는다.
|
||
|
||
Initial defaults, all `planned`:
|
||
|
||
| Area | Default | Exception trigger |
|
||
| --- | --- | --- |
|
||
| query key | registry factory | none |
|
||
| stale time | 30s for sample read | operation owner measurement |
|
||
| garbage collection | 5m | memory profile evidence |
|
||
| refetch on focus | enabled for stale query | high-cost operation owner opt-out |
|
||
| retry | API policy callback | no page-local number |
|
||
| mutation retry | off unless keyed | explicit backend idempotency contract |
|
||
| cache persistence | off | offline requirement + storage threat model |
|
||
| invalidation | mutation result → registry namespace | broad `invalidateQueries()` without reason 금지 |
|
||
|
||
Cache data가 release/config/API schema version과 incompatible하면 reuse하지 않고 discard한다. cache migration을 선택하면 compatibility branch가 fixture와 rollback을 소유한다.
|
||
|
||
### 9.3 Route behavior
|
||
|
||
- route param과 search param은 application 호출 전에 runtime validation한다.
|
||
- unknown route는 API request 없이 not-found surface로 간다.
|
||
- session-required route는 `AuthSessionPort` state를 UX hint로 사용한다.
|
||
- backend authorization result가 최종 권한 판단이다.
|
||
- route-level lazy chunk는 release manifest의 chunk ID와 연결한다.
|
||
- route error element와 React error boundary의 owner를 중복하지 않는다.
|
||
- redirect는 최대 hop count를 test해 loop를 차단한다.
|
||
|
||
### 9.4 Storage behavior
|
||
|
||
- Web Storage 접근은 `StoragePort` adapter 안에서 try/catch한다.
|
||
- unavailable, security exception, quota exceeded를 구분한다.
|
||
- allowed cache eviction 순서를 registry에 기록한다.
|
||
- user preference write 실패는 product flow를 중단하지 않고 memory fallback을 사용한다.
|
||
- mutation/idempotency record처럼 correctness에 영향을 주는 값은 storage fallback을 임의 적용하지 않는다.
|
||
- token, secret, raw API response, error body, PII는 default registry에 등록할 수 없다.
|
||
|
||
---
|
||
|
||
## 10. Rendering, Accessibility, and User Safety
|
||
|
||
### 10.1 Error boundary ownership
|
||
|
||
| Boundary | Catches | Does not catch | Recovery |
|
||
| --- | --- | --- | --- |
|
||
| boot shell | config/release/bootstrap failure | product route errors | config refetch, support, rollback signal |
|
||
| route boundary | lazy chunk/render failure for route | expected API result | route retry or controlled reload |
|
||
| feature boundary | component subtree render defect | normalized operational failure | component reset |
|
||
| async boundary | normalized query/mutation state | thrown render defect | registry action |
|
||
|
||
Operational failures는 normal state로 반환하고 render boundary에 throw하지 않는 것이 default다. programmer defect 또는 invariant breach만 render boundary가 잡는다.
|
||
|
||
### 10.2 Reload loop prevention
|
||
|
||
Controlled reload conditions:
|
||
|
||
1. failure kind가 `CHUNK_LOAD_FAILURE` 또는 `DEPLOY_MISMATCH`
|
||
2. release manifest fetch 성공
|
||
3. active release가 current build와 다름
|
||
4. `CHUNK_RELOAD_GUARD`가 current release pair에 대해 unset
|
||
5. guard를 먼저 기록한 후 reload
|
||
|
||
같은 release pair에서 두 번째 failure가 나면 auto reload를 중단하고 rollback/support surface를 보여준다.
|
||
|
||
### 10.3 Accessibility baseline
|
||
|
||
Planned requirements:
|
||
|
||
- keyboard로 모든 interactive action 접근
|
||
- visible focus indicator
|
||
- route change 후 deterministic focus target
|
||
- loading state의 적절한 live region, 반복 announcement 억제
|
||
- error message와 action의 programmatic association
|
||
- color만으로 state를 구분하지 않음
|
||
- modal focus trap과 restore
|
||
- axe critical/serious violation 0을 blocking default로 사용
|
||
- reduced-motion preference 존중
|
||
|
||
WCAG 적합성 자체는 실제 audit 없이 주장하지 않는다. automated axe 통과는 manual keyboard/screen-reader review를 대체하지 않는다.
|
||
|
||
---
|
||
|
||
## 11. Telemetry and Observability Contract
|
||
|
||
### 11.1 Required context
|
||
|
||
Allowed low-cardinality context:
|
||
|
||
```text
|
||
app_version
|
||
build_id
|
||
release_id
|
||
config_schema_version
|
||
api_contract_version
|
||
route_id
|
||
operation_id
|
||
error_kind
|
||
http_status_group
|
||
attempt_count_bucket
|
||
duration_bucket
|
||
component_boundary
|
||
active_release_id
|
||
mismatch_kind
|
||
reason
|
||
queue_size_bucket
|
||
```
|
||
|
||
이 목록은 **exhaustive default-deny allowlist**다. §5.8 initial planned events의 `requiredAttributes`는 전부 이 목록 안에 있어야 하며, 새 event 나 attribute 를 등록할 때 이 목록과 §5.8 을 함께 갱신한다. 목록 밖 attribute 는 transport boundary 에서 제거된다.
|
||
|
||
Forbidden:
|
||
|
||
```text
|
||
access_token
|
||
refresh_token
|
||
authorization_header
|
||
cookie
|
||
email
|
||
user_name
|
||
raw_user_id
|
||
raw_url
|
||
query_string
|
||
request_body
|
||
response_body
|
||
storage_value
|
||
stack_in_user_message
|
||
```
|
||
|
||
### 11.2 Delivery behavior
|
||
|
||
- telemetry send는 user request critical path를 block하지 않는다.
|
||
- queue는 bounded여야 하며 overflow 시 oldest-drop 또는 newest-drop 정책을 registry에 명시한다.
|
||
- telemetry failure를 telemetry로 재귀 전송하지 않는다.
|
||
- page hide 시 `sendBeacon` 사용 여부는 adapter decision이며 delivery guarantee로 표현하지 않는다.
|
||
- local/dev는 console-safe sink를 허용한다.
|
||
- production endpoint가 없거나 invalid하면 telemetry만 degrade하고 app은 계속 실행한다.
|
||
- security/audit delivery가 필요하면 best-effort product telemetry와 별도 contract를 만든다.
|
||
|
||
### 11.3 Trace correlation
|
||
|
||
- W3C `traceparent`가 외부 auth/backend contract에서 허용되면 전파한다.
|
||
- browser가 받은 `requestId`/`traceId`는 safe support reference로 내부 state에 보관할 수 있다.
|
||
- raw trace header를 user에게 노출하지 않는다.
|
||
- new request retry는 같은 logical operation correlation을 유지하되 attempt를 구분한다.
|
||
- trace propagation 미지원 backend에서는 local operation ID로 degrade한다.
|
||
|
||
---
|
||
|
||
## 12. Release, Cache, Version, and Rollback Contract
|
||
|
||
### 12.1 Artifact set
|
||
|
||
한 release는 최소 다음 artifact를 가진다.
|
||
|
||
```text
|
||
dist/index.html
|
||
dist/assets/<content-hash>.*
|
||
dist/config.json
|
||
dist/release-manifest.json
|
||
dist/config/runtime-config.schema.json
|
||
artifacts/release/build-manifest.json
|
||
artifacts/release/dependency-inventory.*
|
||
artifacts/release/checksums.txt
|
||
```
|
||
|
||
실제 path는 repository가 생기면 owner branch에서 확정한다. 현재는 expected artifact contract다.
|
||
|
||
### 12.2 Cache policy
|
||
|
||
| Surface | Default cache policy | Reason |
|
||
| --- | --- | --- |
|
||
| hashed JS/CSS/font/image | long-lived immutable | content hash identity |
|
||
| `index.html` | `no-cache` / revalidate | active entry point 교체 |
|
||
| `/config.json` | `no-store` 또는 URL에 explicit version | deploy-specific public config |
|
||
| `release-manifest.json` | `no-store` or immediate revalidate | mismatch detection |
|
||
| source map | public hosting disabled; secured artifact store | stack/source exposure boundary |
|
||
| service worker | default off | stale release complexity |
|
||
|
||
Header syntax은 hosting provider 확정 후 adapter runbook에 기록한다. 현재 문서는 policy만 소유한다.
|
||
|
||
### 12.3 Compatibility tuple
|
||
|
||
Frontend boot compatibility는 다음 tuple로 판정한다.
|
||
|
||
```text
|
||
(buildId, configSchemaVersion, apiContractVersion, assetManifestHash, releaseId)
|
||
```
|
||
|
||
Rules:
|
||
|
||
- config schema major incompatibility → boot fail
|
||
- API contract incompatible → product route mount fail 또는 explicitly supported compatibility adapter
|
||
- asset manifest mismatch → controlled reload once
|
||
- release ID mismatch but all versions compatible → warning telemetry 후 continue 가능
|
||
- string lexical compare로 version compatibility를 판정하지 않음
|
||
|
||
### 12.4 Atomic deploy expectation
|
||
|
||
Preferred order:
|
||
|
||
1. immutable asset upload
|
||
2. release manifest upload
|
||
3. runtime config upload
|
||
4. asset reachability smoke
|
||
5. active HTML pointer switch
|
||
6. post-switch boot/e2e smoke
|
||
|
||
Provider가 이 order를 지원하지 않으면 equivalent atomic primitive와 rollback semantics를 decision row에 기록한다.
|
||
|
||
### 12.5 Rollback invariant
|
||
|
||
Rollback target MUST include a coherent set of:
|
||
|
||
- prior HTML
|
||
- prior asset manifest and assets
|
||
- compatible runtime config
|
||
- compatible API contract or backend compatibility window
|
||
- release manifest
|
||
|
||
HTML만 과거로 돌리고 runtime config를 최신에 남기는 rollback은 금지한다. cache purge가 필요한 provider라면 purge 완료가 아니라 실제 old/new reachability probe 결과로 recovery를 판정한다.
|
||
|
||
---
|
||
|
||
<!-- section-id: implementation-boundaries -->
|
||
## 13. Supply-chain and Security Boundaries
|
||
|
||
### 13.1 Supply-chain minimums
|
||
|
||
| Control | Planned default | Blocking condition | Evidence artifact |
|
||
| --- | --- | --- | --- |
|
||
| package manager | pnpm + committed lockfile | lockfile drift | `artifacts/quality/lockfile-check.txt` |
|
||
| install | frozen lockfile | dependency resolution mutation | install log |
|
||
| dependency review | direct/transitive diff | unreviewed high-risk change | dependency diff report |
|
||
| vulnerability scan | severity policy owner branch | threshold violation without approved expiry | SARIF/JSON report |
|
||
| secret scan | source + built asset scan | credential pattern hit | scan report |
|
||
| license inventory | dependency license list | denied/unknown license unresolved | inventory |
|
||
| SBOM/inventory | tool selected by owner | missing release inventory | CycloneDX/SPDX or equivalent |
|
||
| provenance | CI build metadata | buildId/commit mismatch | build manifest |
|
||
|
||
Scanner name과 severity threshold는 repository/organization policy가 없어 현재 `deferred`다. 특정 도구를 사용했다고 주장하지 않는다.
|
||
|
||
### 13.2 Browser security boundary
|
||
|
||
- browser bundle은 public artifact로 간주한다.
|
||
- secret을 obfuscation으로 보호할 수 있다고 가정하지 않는다.
|
||
- `dangerouslySetInnerHTML`은 default prohibited import/API rule 대상이다.
|
||
- unavoidable HTML rendering은 sanitizer owner, allowlist, malicious fixture, CSP interaction evidence가 필요하다.
|
||
- `eval`, dynamic code execution, untrusted script URL은 금지 default다.
|
||
- CSP, HSTS, frame policy, referrer policy는 hosting/backend header owner와 frontend compatibility test가 공동 책임이다.
|
||
- CORS는 backend/browser enforcement이며 frontend에서 wildcard로 해결할 수 있다고 말하지 않는다.
|
||
- route guard는 authorization control이 아니다.
|
||
- client validation은 backend validation을 대체하지 않는다.
|
||
- source map은 production public path에 기본 배포하지 않는다.
|
||
|
||
### 13.3 Dependency update policy
|
||
|
||
- security update bot 선택은 `deferred`다.
|
||
- update PR은 lockfile, unit/component/integration/e2e, build, bundle, security gate를 통과해야 한다.
|
||
- major update는 `FE-D*` impact check와 registry compatibility check를 요구한다.
|
||
- suppression은 reason, owner, expiry, affected package, compensating control을 가진다.
|
||
- expiry가 지난 suppression은 gate failure다.
|
||
|
||
---
|
||
|
||
## 14. Measurable Non-functional Requirements
|
||
|
||
### 14.1 Measurement contexts
|
||
|
||
수치는 context와 함께만 판정한다.
|
||
|
||
| Context ID | Device/runtime | Network/cache | Route/data | Purpose |
|
||
| --- | --- | --- | --- | --- |
|
||
| `FE-NFR-C01` | Playwright Chromium, CI runner spec recorded | cold browser cache, throttling profile recorded | app shell + sample list fixture | repeatable lab baseline |
|
||
| `FE-NFR-C02` | desktop Chromium/Firefox/WebKit matrix | normal CI network, mocked API | sample critical flow | functional compatibility |
|
||
| `FE-NFR-C03` | production browser field data | real network, 28-day window | top route IDs | future field SLO; current unavailable |
|
||
| `FE-NFR-C04` | build runner image + Node/pnpm versions recorded | N/A | production build | bundle reproducibility |
|
||
|
||
CI runner CPU와 throttling 값이 확정되지 않았으므로 command를 실행할 때 report metadata에 실제 값을 기록한다. context가 없는 숫자는 evidence로 인정하지 않는다.
|
||
|
||
### 14.2 Initial target matrix
|
||
|
||
| NFR ID | Metric | Context | Initial target | Current evidence |
|
||
| --- | --- | --- | --- | --- |
|
||
| `FE-NFR-001` | initial JS gzip | `FE-NFR-C04` | ≤ 200 KiB | none |
|
||
| `FE-NFR-002` | any lazy route chunk gzip | `FE-NFR-C04` | ≤ 120 KiB | none |
|
||
| `FE-NFR-003` | LCP lab | `FE-NFR-C01` | ≤ 2.5s | none |
|
||
| `FE-NFR-004` | CLS lab | `FE-NFR-C01` | ≤ 0.10 | none |
|
||
| `FE-NFR-005` | interaction latency lab | `FE-NFR-C01` | ≤ 200ms for named interaction | none |
|
||
| `FE-NFR-006` | boot config validation | deterministic mocked fetch | ≤ 500ms excluding network delay | none |
|
||
| `FE-NFR-007` | API request total timeout | shared client | 10s default | none |
|
||
| `FE-NFR-008` | automatic retry count | deterministic fake clock | ≤ 2 after initial | none |
|
||
| `FE-NFR-009` | axe critical/serious | sample routes | 0 violations | none |
|
||
| `FE-NFR-010` | telemetry blocking time | sink-failure fixture | product action not blocked | none |
|
||
| `FE-NFR-011` | auth redirect loop | route graph test | navigation attempt당 automatic auth redirect ≤ 1, 동일 source→target pair 반복 0 | none |
|
||
| `FE-NFR-012` | controlled reload | mismatch fixture | at most 1 per release pair/session | none |
|
||
| `FE-NFR-013` | LCP field p75 | `FE-NFR-C03` | ≤ 2.5s | none |
|
||
| `FE-NFR-014` | CLS field p75 | `FE-NFR-C03` | ≤ 0.10 | none |
|
||
| `FE-NFR-015` | INP field p75 | `FE-NFR-C03` | ≤ 200ms | none |
|
||
|
||
Field Web Vitals는 consent/privacy boundary, route-ID aggregation, 28-day window, production release ID를 함께 기록해야 한다. minimum eligible sample threshold는 telemetry baseline을 얻은 뒤 owner가 확정할 `deferred` decision이므로, 그 전에는 `FE-GATE-018`을 PASS로 올릴 수 없다. lab result를 production percentile로 표현하지 않는다.
|
||
|
||
### 14.3 Planned commands and expected assertions
|
||
|
||
아래 command는 repository가 생긴 뒤 package script로 제공할 contract다. **이 검토에서 실행되지 않았다.**
|
||
|
||
| Command | Status | Expected assertion | Planned artifact |
|
||
| --- | --- | --- | --- |
|
||
| `pnpm install --frozen-lockfile` | `PLANNED_NOT_EXECUTED` | manifest와 lockfile drift 없음 | `artifacts/quality/install.txt` |
|
||
| `pnpm lint` | `PLANNED_NOT_EXECUTED` | lint error 0, forbidden imports 0 | `artifacts/quality/lint.txt` |
|
||
| `pnpm check:types` | `PLANNED_NOT_EXECUTED` | `checkJs` diagnostic 0 | `artifacts/quality/check-types.txt` |
|
||
| `pnpm test:runtime-schema` | `PLANNED_NOT_EXECUTED` | invalid fixtures 전부 reject | `artifacts/tests/runtime-schema.xml` |
|
||
| `pnpm test:unit` | `PLANNED_NOT_EXECUTED` | unit suite exit 0 | `artifacts/tests/unit.xml` |
|
||
| `pnpm test:component` | `PLANNED_NOT_EXECUTED` | async/error/a11y component fixtures exit 0 | `artifacts/tests/component.xml` |
|
||
| `pnpm test:integration` | `PLANNED_NOT_EXECUTED` | MSW API/failure matrix exit 0 | `artifacts/tests/integration.xml` |
|
||
| `pnpm test:e2e` | `PLANNED_NOT_EXECUTED` | critical flows browser matrix exit 0 | `artifacts/tests/e2e/` |
|
||
| `pnpm test:a11y` | `PLANNED_NOT_EXECUTED` | critical/serious axe finding 0 | `artifacts/tests/a11y.json` |
|
||
| `pnpm review:a11y-manual` | `PLANNED_NOT_EXECUTED` | sample route 별 keyboard/focus manual checklist 서명 완료 | `artifacts/tests/a11y-manual/<route>.md` |
|
||
| `pnpm build` | `PLANNED_NOT_EXECUTED` | production build exit 0 + manifest present | `artifacts/release/build-manifest.json` |
|
||
| `pnpm check:bundle` | `PLANNED_NOT_EXECUTED` | `FE-NFR-001`, `FE-NFR-002` threshold 만족 | `artifacts/performance/bundle.json` |
|
||
| `pnpm test:performance` | `PLANNED_NOT_EXECUTED` | `FE-NFR-003`, `FE-NFR-004`, `FE-NFR-005` context metadata + threshold result | `artifacts/performance/lab.json` |
|
||
| `pnpm scan:security` | `PLANNED_NOT_EXECUTED` | policy threshold 위반 없음 | `artifacts/security/scan.sarif` |
|
||
| `pnpm verify:release` | `PLANNED_NOT_EXECUTED` | compatibility tuple coherent | `artifacts/release/verification.json` |
|
||
| `pnpm collect:web-vitals-evidence` | `PLANNED_NOT_EXECUTED` | 28-day context + p75 + eligible sample metadata 기록 | `artifacts/performance/field-web-vitals.json` |
|
||
| `pnpm verify:hosting-headers` | `PLANNED_NOT_EXECUTED` | HTML/config/manifest/hashed-asset header policy 일치 + 선언된 security header 집합 일치 | `artifacts/release/hosting-headers.json` |
|
||
| `pnpm test:sample-removal` | `PLANNED_NOT_EXECUTED` | sample subtree 제거 후 production build/smoke 성공 | `artifacts/tests/sample-removal.xml` |
|
||
| `pnpm drill:runbook -- FE-RB-001` | `PLANNED_NOT_EXECUTED` | boot config containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-001/<release-id>/record.json` |
|
||
| `pnpm drill:runbook -- FE-RB-002` | `PLANNED_NOT_EXECUTED` | chunk mismatch와 `RELEASE_MANIFEST_FAILURE` containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-002/<release-id>/record.json` |
|
||
| `pnpm drill:runbook -- FE-RB-003` | `PLANNED_NOT_EXECUTED` | API degradation containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-003/<release-id>/record.json` |
|
||
| `pnpm drill:runbook -- FE-RB-004` | `PLANNED_NOT_EXECUTED` | telemetry degradation containment/recovery assertions 통과 | `artifacts/runbooks/FE-RB-004/<release-id>/record.json` |
|
||
| `pnpm drill:runbook -- FE-RB-005` | `PLANNED_NOT_EXECUTED` | rollback decision/recovery assertions 통과 | `artifacts/runbooks/FE-RB-005/<release-id>/record.json` |
|
||
|
||
Script 이름을 바꾸는 것은 허용되지만 acceptance gate와 artifact mapping을 동시에 갱신해야 한다.
|
||
|
||
---
|
||
|
||
## 15. Acceptance Gate Matrix
|
||
|
||
<!-- section-id: gate-matrix -->
|
||
### 15.1 Gate ownership
|
||
|
||
현재 stable gate registry는 26개 row이며, 새 gate를 추가하거나 supersede할 때 이 수와 promotion formula를 함께 갱신한다.
|
||
|
||
> 이 표가 gate 의 **정의**다. `Covered FE-OC` 열은 gate 와 계약의 대응 관계이지 계약 내용의 사본이 아니다.
|
||
> branch 는 이 표를 옮겨 적지 않는다 — gate ID 를 행 키로 쓰고 자기 control 만 적는다(예: [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]]).
|
||
|
||
| Gate ID | Gate | Blocking scope | Covered FE-OC | Covered FE-NFR | Required fixtures | Pass condition | Evidence artifact | Current |
|
||
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||
| `FE-GATE-001` | manifest/lockfile | merge + release | `FE-OC-003`, `FE-OC-018`, `FE-OC-020` | — | lockfile drift | frozen install exit 0 | install log | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-002` | lint | merge | `FE-OC-002`, `FE-OC-003`, `FE-OC-019`, `FE-OC-020` | — | forbidden API/import | error 0 | lint report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-003` | typecheck-equivalent | merge | `FE-OC-002`, `FE-OC-003`, `FE-OC-007`, `FE-OC-020` | — | JSDoc/checkJs negative fixture | production diagnostic 0; fixture fails as expected | check-types report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-004` | runtime schema | merge | `FE-OC-004`, `FE-OC-006`, `FE-OC-007`, `FE-OC-008`, `FE-OC-023` | `FE-NFR-006` | content-type/JSON/envelope/payload/config invalid matrix + deterministic valid-config timing fixture | every invalid fixture rejected with expected kind; valid boot config validation ≤ 500ms excluding mocked network delay | schema + timing report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-005` | unit | merge | `FE-OC-006`, `FE-OC-009`, `FE-OC-012`, `FE-OC-013`, `FE-OC-014`, `FE-OC-022` | `FE-NFR-007`, `FE-NFR-008`, `FE-NFR-010` | retry clock, mapper, all registries | exit 0 | unit XML | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-006` | component | merge | `FE-OC-005`, `FE-OC-011`, `FE-OC-015`, `FE-OC-019`, `FE-OC-024` | `FE-NFR-009` | async states, render boundary, keyboard | exit 0 | component XML | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-007` | integration | merge | `FE-OC-006`, `FE-OC-007`, `FE-OC-008`, `FE-OC-009`, `FE-OC-010`, `FE-OC-012` | `FE-NFR-007`, `FE-NFR-008` | MSW status/failure/auth-recovery taxonomy | all matrix and negative-fixture rows covered | integration XML | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-008` | e2e | merge + release | `FE-OC-004`, `FE-OC-005`, `FE-OC-009`, `FE-OC-010`, `FE-OC-011`, `FE-OC-015`, `FE-OC-016`, `FE-OC-024` | `FE-NFR-011`, `FE-NFR-012` | boot, route, mutation, chunk mismatch, repeated guarded-route redirect pair | critical scenarios exit 0; navigation attempt당 automatic auth redirect ≤ 1이고 동일 source→target pair가 반복되지 않음 | Playwright report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-009` | accessibility | merge + release | `FE-OC-019`, `FE-OC-020`, `FE-OC-024` | `FE-NFR-009` | axe + manual checklist | automated threshold + signed manual review | a11y artifacts | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-010` | architecture | merge | `FE-OC-002`, `FE-OC-020` | — | forbidden import fixtures including direct TanStack client import | allowed passes, forbidden fails | dependency report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-011` | build | merge + release | `FE-OC-003`, `FE-OC-016`, `FE-OC-018` | — | clean production build | exit 0 + expected artifacts | build manifest | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-012` | bundle | release | `FE-OC-018`, `FE-OC-021` | `FE-NFR-001`, `FE-NFR-002` | app + lazy chunks | NFR thresholds pass | bundle report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-013` | security | merge + release | `FE-OC-018`, `FE-OC-019`, `FE-OC-020` | — | secret/vulnerability/license/dependency-review fixtures | policy pass | SARIF/inventory/dependency diff report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-014` | config compatibility | release | `FE-OC-004`, `FE-OC-023` | — | old/new config versions | supported passes, incompatible fails boot | compatibility report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-015` | release coherence | release | `FE-OC-016`, `FE-OC-017`, `FE-OC-023` | `FE-NFR-012` | mixed HTML/assets/config | mismatch detected, coherent set passes | release verification | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-016` | rollback drill | production promotion | `FE-OC-017` | `FE-NFR-012` | prior release pair | rollback + smoke evidence | drill record | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-017` | scoped diagram review | documentation readiness | `FE-OC-002`, `FE-OC-016` | — | overview dependency view + static asset/runtime-config delivery slice | reviewer score threshold satisfied for both scoped diagrams | reviewer report | `PASS_SCOPED` |
|
||
| `FE-GATE-018` | production field Web Vitals | field readiness | `FE-OC-021` | `FE-NFR-013`, `FE-NFR-014`, `FE-NFR-015` | eligible route samples over recorded 28-day window | p75 targets pass and deferred minimum sample threshold is resolved | field Web Vitals report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-019` | hosting header policy (cache + security) | release | `FE-OC-016`, `FE-OC-019` | — | HTML/config/manifest/hashed asset responses + 선언된 security header 집합 | declared Cache-Control/content-type/security-header policy matches actual hosting | hosting header report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-020` | sample removal | merge + release | `FE-OC-024` | — | sample subtree removed in dedicated fixture | production build and smoke pass with no product import | sample-removal report | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-021` | `FE-RB-001` drill | production promotion | `FE-OC-025` | — | boot config failure | containment, escalation, recovery assertions pass | `FE-RB-001` record | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-022` | `FE-RB-002` drill | production promotion | `FE-OC-025` | `FE-NFR-012` | chunk/release mismatch + release manifest fetch/parse/schema failure | `CHUNK_LOAD_FAILURE`와 `RELEASE_MANIFEST_FAILURE` containment, escalation, recovery assertions pass | `FE-RB-002` record | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-023` | `FE-RB-003` drill | production promotion | `FE-OC-025` | `FE-NFR-007`, `FE-NFR-008` | API degradation | containment, escalation, recovery assertions pass | `FE-RB-003` record | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-024` | `FE-RB-004` drill | production promotion | `FE-OC-025` | `FE-NFR-010` | telemetry degradation | containment, escalation, recovery assertions pass | `FE-RB-004` record | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-025` | `FE-RB-005` drill | production promotion | `FE-OC-025` | — | blocking release defect | rollback decision, escalation, recovery assertions pass | `FE-RB-005` record | `FAIL_UNVERIFIED` |
|
||
| `FE-GATE-026` | lab performance | release | `FE-OC-021` | `FE-NFR-003`, `FE-NFR-004`, `FE-NFR-005` | recorded runner/throttling/cache context + named interactions | every lab threshold passes and report contains reproducibility metadata | lab performance report | `FAIL_UNVERIFIED` |
|
||
|
||
### 15.2 Negative fixture requirement
|
||
|
||
Gate가 실제로 동작한다고 말하려면 최소 하나의 deliberately failing fixture가 필요하다.
|
||
|
||
| Gate | Negative fixture example |
|
||
| --- | --- |
|
||
| architecture | `presentation` imports `adapters/http` |
|
||
| checkJs | application port called with wrong shape |
|
||
| runtime schema | success envelope without `data` |
|
||
| retry | POST without idempotency key receives 503 |
|
||
| storage | token key registration attempt |
|
||
| telemetry | event includes raw URL/query |
|
||
| release | HTML build A + asset manifest B |
|
||
| reload guard | second chunk failure in same release pair |
|
||
| lab performance | context metadata missing 또는 one named threshold exceeded |
|
||
|
||
Negative fixture를 실행하지 않고 rule 존재만 확인한 결과는 `locally-verified` 증거로 부족하다.
|
||
|
||
### 15.3 Promotion rule
|
||
|
||
```text
|
||
MERGE_READY = FE-GATE-001, FE-GATE-002, FE-GATE-003, FE-GATE-004, FE-GATE-005, FE-GATE-006, FE-GATE-007, FE-GATE-008, FE-GATE-009, FE-GATE-010, FE-GATE-011, FE-GATE-013, FE-GATE-020 PASS
|
||
RELEASE_READY = MERGE_READY AND FE-GATE-012, FE-GATE-014, FE-GATE-015, FE-GATE-019, FE-GATE-026 PASS
|
||
PROD_PROMOTION_READY = RELEASE_READY AND FE-GATE-016, FE-GATE-021, FE-GATE-022, FE-GATE-023, FE-GATE-024, FE-GATE-025 PASS
|
||
FIELD_SLO_READY = PROD_PROMOTION_READY AND FE-GATE-018 PASS
|
||
DOCUMENTATION_READY = FE-GATE-017 PASS_SCOPED AND evidence ledger updated
|
||
PROJECT_READY = all applicable blocking gates PASS
|
||
```
|
||
|
||
현재는 `PROJECT_READY = false`, 즉 `NOT_READY`다.
|
||
|
||
---
|
||
|
||
## 16. Operational Runbooks
|
||
|
||
Runbook은 provider-specific console command를 현재 발명하지 않는다. 공통 trigger, diagnosis evidence, mitigation invariant, recovery assertion을 고정하고 provider command는 release branch가 hosting 확정 후 채운다.
|
||
|
||
아래 시간·rate window는 모두 implementation/telemetry evidence가 없는 `planned conditional default`이며 measured SLO가 아니다. 각 runbook의 first drill 결과와 hosting/backend baseline이 생기면 owner가 유지·변경한다.
|
||
|
||
### 16.1 `FE-RB-001` — Boot config failure
|
||
|
||
| Field | Planned contract |
|
||
| --- | --- |
|
||
| Primary owner | `feature-frontend-operational-runbook-contract` |
|
||
| Technical escalation | `feature-frontend-env-runtime-config-contract` → `feature-frontend-release-cache-rollback-contract` |
|
||
| Activation condition | initial boot config validation 실패 후 bounded refetch 1회도 실패 |
|
||
| Conditional window | detection 즉시 containment; owner triage 시작 목표 5분 |
|
||
| Evidence path | `artifacts/runbooks/FE-RB-001/<release-id>/` |
|
||
|
||
**Trigger**
|
||
|
||
- boot shell에 `BOOT_CONFIG_FAILURE`
|
||
- config fetch non-2xx, JSON parse failure, schema incompatibility
|
||
|
||
**Immediate containment**
|
||
|
||
1. product routes mount를 중단한다.
|
||
2. safe support reference와 build/config version만 표시한다.
|
||
3. automatic refetch는 최대 1회로 제한한다.
|
||
|
||
**Diagnosis evidence**
|
||
|
||
- current `buildId`, `releaseId`, `configSchemaVersion`
|
||
- runtime config HTTP status와 content-type
|
||
- release manifest compatibility tuple
|
||
- config publish timestamp는 진단용이며 compatibility identity로 쓰지 않음
|
||
|
||
**Mitigation options**
|
||
|
||
- If config artifact만 잘못됨 → current build와 호환되는 config republish.
|
||
- If new config schema가 old build와 incompatible → coherent prior release rollback.
|
||
- If endpoint outage → provider restore 또는 approved build-time fallback release.
|
||
|
||
**Escalation**
|
||
|
||
- config owner가 schema/publish 원인을 분류하지 못하거나 coherent republish가 불가능하면 release owner에게 rollback decision을 넘긴다.
|
||
- auth/API/product owner에게는 boot이 성공한 뒤 별도 downstream failure가 확인될 때만 확대한다.
|
||
|
||
**Recovery assertions**
|
||
|
||
- clean session boot 성공
|
||
- product root mount
|
||
- config validation artifact pass
|
||
- repeated boot error telemetry 없음
|
||
|
||
**Evidence**
|
||
|
||
`artifacts/runbooks/FE-RB-001/<release-id>/` planned.
|
||
|
||
### 16.2 `FE-RB-002` — Chunk load / release manifest / deploy mismatch
|
||
|
||
| Field | Planned contract |
|
||
| --- | --- |
|
||
| Primary owner | `feature-frontend-operational-runbook-contract` |
|
||
| Technical escalation | `feature-frontend-release-cache-rollback-contract` → hosting/CDN owner |
|
||
| Activation condition | `RELEASE_MANIFEST_FAILURE`, chunk failure 후 manifest mismatch·unreachable asset 확인, 또는 controlled reload 1회 실패 |
|
||
| Conditional window | detection 즉시 reload guard; release owner triage 시작 목표 5분 |
|
||
| Evidence path | `artifacts/runbooks/FE-RB-002/<release-id>/` |
|
||
|
||
**Trigger**
|
||
|
||
- `CHUNK_LOAD_FAILURE`
|
||
- `RELEASE_MANIFEST_FAILURE`
|
||
- asset 404 or integrity mismatch
|
||
- release manifest fetch/parse/schema validation failure
|
||
- release manifest active ID differs from loaded build
|
||
|
||
**Immediate containment**
|
||
|
||
1. current user input이 있으면 destructive reload 전에 경고한다.
|
||
2. release manifest를 `no-store`로 한 번 조회한다.
|
||
3. manifest fetch/parse/schema가 실패하면 release mismatch를 추정해 reload하지 않고 update/support shell로 격리한다.
|
||
4. manifest가 valid하고 active release mismatch가 확인된 경우에만 reload guard를 먼저 기록하고 한 번만 reload한다.
|
||
|
||
**Diagnosis evidence**
|
||
|
||
- loaded build ID
|
||
- active release ID
|
||
- release manifest fetch status, content-type, parse/schema validation outcome; raw manifest 제외
|
||
- requested chunk ID, raw URL 제외
|
||
- asset manifest hash
|
||
- HTML/config/asset cache headers
|
||
|
||
**Mitigation options**
|
||
|
||
- If active release가 새 버전이고 assets reachable → one reload.
|
||
- If asset set incomplete → active pointer를 prior coherent release로 rollback.
|
||
- If release manifest artifact가 missing/malformed/incompatible → coherent manifest를 republish하거나 prior coherent release로 rollback.
|
||
- If CDN propagation 중 → active switch를 되돌리고 reachability probe 재실행.
|
||
|
||
**Escalation**
|
||
|
||
- asset set incomplete 또는 active pointer incoherent이면 release owner가 rollback 여부를 결정한다.
|
||
- origin은 정상이나 edge가 불일치하면 hosting/CDN owner에게 header·propagation evidence와 함께 넘긴다.
|
||
|
||
**Recovery assertions**
|
||
|
||
- entry와 lazy route asset 모두 2xx
|
||
- release manifest fetch·parse·schema validation과 release tuple coherence pass
|
||
- second auto reload 없음
|
||
- release coherence gate pass
|
||
- route e2e pass
|
||
|
||
**Evidence**
|
||
|
||
`artifacts/runbooks/FE-RB-002/<release-id>/` planned.
|
||
|
||
### 16.3 `FE-RB-003` — Backend API degradation
|
||
|
||
| Field | Planned contract |
|
||
| --- | --- |
|
||
| Primary owner | `feature-frontend-operational-runbook-contract` |
|
||
| Technical escalation | `feature-api-client-response-envelope-contract` → backend operation owner → release compatibility owner |
|
||
| Activation condition | terminal network/timeout/429/5xx rate가 configured threshold를 rolling 5분 동안 초과하거나 schema mismatch 1건 발생 |
|
||
| Conditional window | rate threshold 값은 baseline 후 확정; 최초 분류 목표 10분 |
|
||
| Evidence path | `artifacts/runbooks/FE-RB-003/<release-id>/` |
|
||
|
||
**Trigger**
|
||
|
||
- network/timeout/502/503/504 terminal rate 증가
|
||
- `429` 지속
|
||
- schema/envelope mismatch 발생
|
||
|
||
**Triage split**
|
||
|
||
| Signal | Likely class | First action |
|
||
| --- | --- | --- |
|
||
| network across all operations | network/CORS/DNS/provider | browser + backend reachability 확인 |
|
||
| 429 only | capacity/rate policy | `Retry-After`와 request burst 확인 |
|
||
| 5xx only | backend failure | requestId/traceId로 backend owner 전달 |
|
||
| schema mismatch after release | compatibility | frontend/backend release tuple 확인 |
|
||
| one operation only | endpoint contract | operation ID fixture 대조 |
|
||
|
||
**Containment**
|
||
|
||
- retry cap을 runtime에서 임의 확대하지 않는다.
|
||
- safe cached data가 있으면 stale-degraded로 제공한다.
|
||
- mutation은 idempotency contract 없이는 재시도하지 않는다.
|
||
- schema mismatch는 retry하지 않고 compatibility rollback을 검토한다.
|
||
|
||
**Escalation**
|
||
|
||
- network/429/5xx는 `operationId`, request/trace reference, attempt count를 backend operation owner에게 넘긴다.
|
||
- release 직후 schema mismatch면 frontend/backend release owners가 tuple을 대조하고 어느 쪽을 rollback할지 공동 결정한다.
|
||
|
||
**Recovery assertions**
|
||
|
||
- terminal failure rate가 baseline window로 복귀
|
||
- retry amplification 없음
|
||
- sample critical read/write e2e pass
|
||
- schema fixtures pass
|
||
|
||
**Evidence**
|
||
|
||
`artifacts/runbooks/FE-RB-003/<release-id>/` planned.
|
||
|
||
### 16.4 `FE-RB-004` — Telemetry sink failure
|
||
|
||
| Field | Planned contract |
|
||
| --- | --- |
|
||
| Primary owner | `feature-frontend-operational-runbook-contract` |
|
||
| Technical escalation | `feature-frontend-observability-logging-trace-contract` → telemetry platform owner |
|
||
| Activation condition | adapter init 실패 또는 sink failure/queue overflow가 rolling 5분 window에서 발생 |
|
||
| Conditional window | product flow 즉시 격리; platform triage 시작 목표 15분 |
|
||
| Evidence path | `artifacts/runbooks/FE-RB-004/<release-id>/` |
|
||
|
||
**Trigger**
|
||
|
||
- sink non-2xx/network failure
|
||
- queue overflow/drop counter 증가
|
||
- telemetry adapter initialization failure
|
||
|
||
**Containment**
|
||
|
||
- product flow를 계속 수행한다.
|
||
- bounded queue 이상 적재하지 않는다.
|
||
- telemetry failure를 동일 sink로 재귀 보고하지 않는다.
|
||
- console fallback은 safe fields에 한정한다.
|
||
|
||
**Diagnosis evidence**
|
||
|
||
- endpoint classification, raw endpoint 제외
|
||
- queue size bucket
|
||
- dropped event count
|
||
- build/release ID
|
||
- redaction test result
|
||
|
||
**Mitigation**
|
||
|
||
- sink restore
|
||
- telemetry runtime flag disable
|
||
- queue policy 조정은 owner decision + memory test 후만
|
||
|
||
**Escalation**
|
||
|
||
- client redaction/queue defect면 observability owner가 우선 수정한다.
|
||
- client contract가 정상이고 sink/ingest가 실패하면 safe endpoint classification과 drop counters만 telemetry platform owner에게 전달한다.
|
||
|
||
**Recovery assertions**
|
||
|
||
- product e2e unaffected
|
||
- delivery self-check event 성공
|
||
- queue drains within planned bound
|
||
- forbidden attribute scan pass
|
||
|
||
**Evidence**
|
||
|
||
`artifacts/runbooks/FE-RB-004/<release-id>/` planned.
|
||
|
||
### 16.5 `FE-RB-005` — Release rollback
|
||
|
||
| Field | Planned contract |
|
||
| --- | --- |
|
||
| Primary owner | `feature-frontend-operational-runbook-contract` |
|
||
| Technical escalation | `feature-frontend-release-cache-rollback-contract` → release approver/hosting owner |
|
||
| Activation condition | release-blocking boot/chunk/render/API/security defect가 확인되고 forward fix가 incident window 안에 안전하다고 증명되지 않음 |
|
||
| Conditional window | blocking defect 확인 즉시 decision; provider-dependent recovery target은 hosting 확정 전 `TBD` |
|
||
| Evidence path | `artifacts/runbooks/FE-RB-005/<release-id>/` |
|
||
|
||
**Trigger**
|
||
|
||
- boot/config incompatibility
|
||
- widespread chunk mismatch
|
||
- critical render/API compatibility defect
|
||
- security gate post-release finding
|
||
|
||
**Preconditions**
|
||
|
||
- prior immutable release exists
|
||
- prior runtime config and API compatibility known
|
||
- rollback actor and audit record owner identified
|
||
|
||
**Procedure invariant**
|
||
|
||
1. target release tuple 선택
|
||
2. prior assets reachability 확인
|
||
3. prior runtime config compatibility 확인
|
||
4. active pointer atomic switch
|
||
5. provider-specific cache action 수행
|
||
6. boot + route + API critical smoke
|
||
7. telemetry/reload-loop 확인
|
||
8. rollback record 저장
|
||
|
||
**MUST NOT**
|
||
|
||
- source rebuild를 rollback으로 부름
|
||
- HTML만 이전 버전으로 교체
|
||
- config/API compatibility 확인 없이 pointer 변경
|
||
- smoke 없이 incident close
|
||
|
||
**Escalation**
|
||
|
||
- release owner가 target tuple과 evidence를 준비하고 named release approver가 pointer switch를 승인한다.
|
||
- atomic switch나 cache invalidation이 provider primitive에서 실패하면 hosting owner에게 즉시 확대한다.
|
||
|
||
**Recovery assertions**
|
||
|
||
- `FE-GATE-014`, `FE-GATE-015` pass
|
||
- critical e2e pass
|
||
- no repeated `DEPLOY_MISMATCH`
|
||
- incident timeline에 release IDs 기록
|
||
|
||
**Evidence**
|
||
|
||
`artifacts/runbooks/FE-RB-005/<release-id>/` planned.
|
||
|
||
---
|
||
|
||
## 17. Evidence Ledger
|
||
|
||
### 17.1 Evidence records
|
||
|
||
| Evidence ID | Artifact / observation | Grade | Supports | Does not prove |
|
||
| --- | --- | --- | --- | --- |
|
||
| `FE-EV-001` | 본 project note | `documented-only` | contract scope, IDs, defaults | code existence, test pass |
|
||
| `FE-EV-002` | [[raw/project-notes/ca-skeleton-operational-contract]] | `documented-only` reference | backend sibling의 operational contract pattern | frontend implementation |
|
||
| `FE-EV-003` | [[raw/official-docs/vite-build-tool-official]] | source reference | Vite decision research | chosen config implemented |
|
||
| `FE-EV-004` | [[raw/official-docs/react-ui-library-official]] | source reference | React decision research | component tree exists |
|
||
| `FE-EV-005` | [[raw/official-docs/tailwind-css-utility-first-official]] | source reference | styling decision research | Tailwind configured |
|
||
| `FE-EV-006` | [[raw/official-docs/tanstack-query-server-state-official]] | source reference | query state decision research | cache policy implemented |
|
||
| `FE-EV-007` | [[raw/official-docs/zod-runtime-schema-validation-official]] | source reference | runtime validation decision research | schema/tests exist |
|
||
| `FE-EV-008` | [[raw/official-docs/react-router-official]] | source reference | routing decision research | route registry exists |
|
||
| `FE-EV-009` | §0.2의 exact `rg --files` + manifest/lock/Vite/`src/main` regex search | observed read-only check | current wiki workspace에서 해당 entry artifact pattern 미발견 | 전체 source/test/CI/deploy 부재 또는 remote/other workspace 부재 |
|
||
| `FE-EV-010` | overview draw.io + `docs/superpowers/specs/2026-07-18-ca-skeleton-frontend-operational-contract-review/diagram-review.md` | `documented-only`, reviewer PASS 100/100 scoped | Clean Architecture dependency ownership view가 §4.2~§4.4와 정합 | source code, import-rule 구현, runtime topology |
|
||
| `FE-EV-011` | deployment draw.io + `docs/superpowers/specs/2026-07-18-ca-skeleton-frontend-operational-contract-review/diagram-review.md` | `documented-only`, reviewer PASS 100/100 scoped | static asset와 `/config.json` delivery slice가 declared boundary와 정합 | §12 전체 release/rollback topology, 실제 hosting·deploy |
|
||
| `FE-EV-012` | test/CI evidence | `UNVERIFIED` | dedicated search/command가 기록되지 않았다는 경계 | artifact 부재 또는 gate pass/fail |
|
||
| `FE-EV-013` | release/deploy evidence | `UNVERIFIED` | dedicated search/command가 기록되지 않았다는 경계 | artifact 부재, rollback 또는 runtime behavior |
|
||
|
||
### 17.2 Evidence promotion protocol
|
||
|
||
To mark `actually-implemented`:
|
||
|
||
- repository URL/path
|
||
- commit SHA
|
||
- source path
|
||
- matching `FE-OC-*` and `FE-D*`
|
||
|
||
To mark `locally-verified`:
|
||
|
||
- all above
|
||
- exact command
|
||
- tool/runtime version
|
||
- exit code
|
||
- machine-readable artifact path
|
||
- negative fixture result where applicable
|
||
|
||
To mark `prod-verified`:
|
||
|
||
- all above
|
||
- release ID
|
||
- environment
|
||
- measurement window
|
||
- dashboard/log/incident evidence
|
||
- rollback or recovery evidence where relevant
|
||
|
||
---
|
||
|
||
## 18. Binary Readiness Scorecard
|
||
|
||
### 18.1 Formula
|
||
|
||
```text
|
||
PASS_STATES = {PASS, PASS_SCOPED}
|
||
READY iff every blocking row Current is in PASS_STATES
|
||
otherwise NOT_READY
|
||
```
|
||
|
||
`PASS_SCOPED`는 Blocking question과 Required evidence가 명시적으로 같은 제한 범위를 물을 때만 허용한다. 점수 평균으로 blocking failure를 상쇄하지 않는다.
|
||
|
||
### 18.2 Current scorecard
|
||
|
||
| Readiness ID | Blocking question | Required evidence | Current | Reason |
|
||
| --- | --- | --- | --- | --- |
|
||
| `FE-RDY-001` | implementation repository가 식별됐는가 | repo URL/path + commit | `FAIL` | entry artifact pattern 미발견; repo location은 `UNVERIFIED` |
|
||
| `FE-RDY-002` | package manifest와 frozen lockfile이 있는가 | manifest + lockfile | `FAIL` | evidence 없음 |
|
||
| `FE-RDY-003` | architecture port ownership이 코드로 강제되는가 | dependency test | `FAIL` | implementation evidence 없음 |
|
||
| `FE-RDY-004` | overview dependency view가 reviewer gate를 통과했는가 | reviewer report | `PASS_SCOPED` | reviewer 100/100; 구현 evidence와 별개 |
|
||
| `FE-RDY-005` | deployment의 static asset/runtime-config delivery slice가 reviewer gate를 통과했는가 | reviewer report | `PASS_SCOPED` | reviewer 100/100; §12 전체·실제 deploy mapping은 `UNVERIFIED` |
|
||
| `FE-RDY-006` | runtime config boot gate가 검증됐는가 | schema + boot tests | `FAIL` | test evidence `UNVERIFIED` |
|
||
| `FE-RDY-007` | failure taxonomy가 test matrix로 강제되는가 | integration artifacts | `FAIL` | test evidence `UNVERIFIED` |
|
||
| `FE-RDY-008` | auth boundary가 token lifecycle을 침범하지 않는가 | port/import tests | `FAIL` | implementation evidence 없음 |
|
||
| `FE-RDY-009` | 8 registry가 single owner로 구현됐는가 | registry snapshots | `FAIL` | implementation evidence 없음 |
|
||
| `FE-RDY-010` | lint/checkJs/runtime-schema/unit/component/integration/e2e/a11y가 통과했는가 | CI artifacts | `FAIL` | CI evidence `UNVERIFIED` |
|
||
| `FE-RDY-011` | build/bundle/security gate가 통과했는가 | release artifacts | `FAIL` | build evidence `UNVERIFIED` |
|
||
| `FE-RDY-012` | lab·field NFR context와 측정값이 있는가 | lab + field reports | `FAIL` | target만 존재; `FE-GATE-018`, `FE-GATE-026`은 `FAIL_UNVERIFIED` |
|
||
| `FE-RDY-013` | release compatibility tuple이 검증됐는가 | release verification | `FAIL` | release evidence `UNVERIFIED` |
|
||
| `FE-RDY-014` | rollback drill이 수행됐는가 | drill record | `FAIL` | deploy/drill evidence `UNVERIFIED` |
|
||
| `FE-RDY-015` | runbook이 실제 hosting command와 evidence path를 가지는가 | provider runbook | `FAIL` | provider 미정 |
|
||
| `FE-RDY-016` | evidence ledger에 과장 없는 grade가 유지되는가 | ledger review | `PASS` | 현재 문서 경계 명시 |
|
||
|
||
**Current verdict: `NOT_READY`**
|
||
|
||
Repository identity와 implementation/test/CI/deploy evidence 또는 blocking gate가 미검증이면 verdict는 유지된다. scoped diagram PASS는 이를 상쇄하지 않는다. 문서 분량이나 decision row 수로 readiness를 승격하지 않는다.
|
||
|
||
---
|
||
|
||
## 19. Risks and Open Questions
|
||
|
||
### 19.1 Risk register
|
||
|
||
| Risk ID | Risk | Owner | Trigger | Mitigation | Resolution condition | Status |
|
||
| --- | --- | --- | --- | --- | --- | --- |
|
||
| `FE-RISK-001` | remote implementation repo가 따로 존재해 문서가 실제 stack과 drift | project owner | repo URL 발견 | inventory 후 decision/contract map 재검토 | repo commit과 ledger 연결 | `open` |
|
||
| `FE-RISK-002` | runtime config와 HTML publish가 atomic하지 않음 | release owner | hosting 선택 | coherent release pointer 또는 env rebuild fallback | mismatch drill pass | `open` |
|
||
| `FE-RISK-003` | pnpm이 target CI/org 표준과 충돌 | toolchain owner | CI platform 확정 | `FE-D001` 재검토 | frozen install gate pass | `open` |
|
||
| `FE-RISK-004` | JavaScript checkJs coverage가 complex API를 놓침 | toolchain/schema owners | recurring runtime defects | TypeScript 또는 generated types 비교 | negative fixtures + defect trend 기준 충족 | `open` |
|
||
| `FE-RISK-005` | auth route guard가 security control로 오해됨 | auth/routing owners | guarded route 구현 | backend authz requirement 문서·test | e2e에서 403 처리 확인 | `open` |
|
||
| `FE-RISK-006` | retry가 backend overload를 증폭 | API owner | 429/5xx spike | cap/jitter/Retry-After + telemetry | load/degradation test pass | `open` |
|
||
| `FE-RISK-007` | cache persistence가 PII 또는 stale schema를 남김 | query/storage owners | offline persistence opt-in | classification/version/TTL/migration gate | threat model + compatibility tests | `open` |
|
||
| `FE-RISK-008` | telemetry failure가 memory growth 유발 | telemetry owner | sink outage | bounded queue/drop policy | soak test pass | `open` |
|
||
| `FE-RISK-009` | chunk auto reload가 user input 손실 | release/presentation owners | lazy chunk failure | dirty-state guard + one reload cap | e2e recovery pass | `open` |
|
||
| `FE-RISK-010` | bundle threshold가 실제 device UX와 무관 | performance owner | first measurement | context/field data로 threshold revisit | decision update with evidence | `open` |
|
||
| `FE-RISK-011` | supply-chain scanner policy가 미정이라 gate가 형식적 | security owner | repo bootstrap | scanner/severity/suppression decision | SARIF gate pass | `open` |
|
||
| `FE-RISK-012` | draw.io와 text contract의 component/edge drift | architecture owner | diagram 또는 FE-D 변경 | reviewer + contract ID annotation in caption | review report resolves all edges | `open` |
|
||
|
||
### 19.2 Open questions
|
||
|
||
| Question ID | Question | Owner | Decision trigger | Required evidence | Resolution condition |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `FE-Q-001` | 실제 repo 위치와 ownership은? | project owner | implementation handoff | URL/path/commit | ledger update |
|
||
| `FE-Q-002` | target Node/pnpm version은? | toolchain owner | repo creation | CI runner/org standard | manifest `engines` + fresh clone pass |
|
||
| `FE-Q-003` | static hosting provider와 atomic deploy primitive는? | release owner | first deploy | provider docs/config | `FE-D023` confirmed |
|
||
| `FE-Q-004` | runtime config endpoint를 hosting이 지원하는가? | config/release owners | hosting choice | staging publish experiment | `FE-D012` or `FE-D013` final |
|
||
| `FE-Q-005` | backend envelope/OpenAPI source는 어디인가? | API owner | first integration | versioned schema/fixture | runtime schema generated or mapped |
|
||
| `FE-Q-006` | auth integration adapter는 어떤 owner가 제공하는가? | auth owner | guarded route | session interface + lifecycle doc | port contract test |
|
||
| `FE-Q-007` | browser support matrix는? | product owner | first release | product analytics/requirement | CI browser matrix fixed |
|
||
| `FE-Q-008` | telemetry sink와 consent policy는? | telemetry/privacy owners | production telemetry | data inventory + endpoint | redaction and delivery tests |
|
||
| `FE-Q-009` | service worker/offline이 필요한가? | product/release owners | offline requirement | UX/update design | `FE-D019` retained or superseded |
|
||
| `FE-Q-010` | vulnerability/license blocking threshold는? | security owner | CI setup | organization policy | security gate configured |
|
||
|
||
---
|
||
|
||
<!-- section-id: project-work-items -->
|
||
## 8.0 실행계획
|
||
|
||
> Project contract v2의 branch handoff SSOT. `Dependencies`는 stable WI ID만 사용하고 `Applies Decisions`는 revision 1에 pin한다.
|
||
|
||
| Work Item ID | branch slug | 완료 조건 (측정가능) | Applies Decisions | Dependencies | Status |
|
||
|---|---|---|---|---|---|
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `feature-frontend-project-bootstrap-toolchain-contract` | manifest·engines·pnpm lock·checkJs scripts와 frozen install evidence가 존재한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TOOLCHAIN-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-LANGUAGE-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1` | - | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `feature-frontend-clean-architecture-layering-contract` | directory 책임·port owner·allowed import matrix가 문서와 fixture로 고정된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-PORT-OWNERSHIP-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-COMPOSITION-ROOT-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-003` | `feature-frontend-architecture-enforcement-lint-contract` | allowed fixture는 통과하고 forbidden fixture는 실패하며 lint report가 생성된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004` | `feature-frontend-env-runtime-config-contract` | build/runtime/secret registry와 boot-invalid matrix가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RUNTIME-CONFIG-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005` | `feature-api-client-response-envelope-contract` | API registry와 timeout·abort·retry·idempotency deterministic tests가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TIMEOUT-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RETRY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-IDEMPOTENCY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006` | `feature-runtime-schema-validation-contract` | content-type·JSON·envelope·payload invalid fixture가 기대 failure kind로 정규화된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007` | `feature-frontend-error-classification-boundary-contract` | normalization matrix와 raw body·stack leakage negative test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-008` | `feature-frontend-auth-session-integration-contract` | AuthSessionPort·bounded 401 replay와 token lifecycle import 금지가 test로 고정된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-PORT-OWNERSHIP-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-009` | `feature-routing-navigation-guard-contract` | registry route·param validation·404·redirect-loop·session UX test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ROUTING-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-008`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-010` | `feature-server-state-caching-contract` | QueryCachePort와 TanStack adapter의 invalidation·stale test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SERVER-STATE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-011` | `feature-frontend-storage-registry-contract` | namespace·version·classification·quota fallback test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012` | `feature-frontend-observability-logging-trace-contract` | telemetry registry·redaction·bounded queue·sink failure test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-013` | `feature-async-ui-state-contract` | required와 non-blocking state matrix component test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-UI-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SERVER-STATE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-010` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-014` | `feature-boundary-mapper-viewmodel-contract` | raw DTO direct use가 차단되고 mapper negative fixture가 실패한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-015` | `feature-frontend-render-recovery-boundary-contract` | boot·route·feature·async boundary ownership과 recovery fixture가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-UI-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-013` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-016` | `feature-sample-feature-slice-contract-fixture` | full contract slice와 sample removal smoke test가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SAMPLE-FIXTURE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-007`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-009`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-010` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017` | `feature-frontend-test-taxonomy-contract` | gate·fixture·artifact mapping과 test level별 최소 1개 test가 존재한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-018` | `feature-tailwind-design-token-styling-contract` | theme token·arbitrary value policy·sample UI가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-019` | `feature-accessibility-baseline-contract` | sample route에서 axe·keyboard·focus evidence가 남는다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-013` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020` | `feature-frontend-build-bundle-supply-chain-contract` | frozen build·inventory·scan·bundle report가 CI artifact로 생성된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SUPPLY-CHAIN-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-021` | `feature-frontend-browser-security-boundary-contract` | CSP·header·secret·storage·telemetry browser-boundary fixture가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-AUTH-BOUNDARY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-008`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-011`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-022` | `feature-frontend-contract-registry-governance` | 8개 registry snapshot·schema validation·single-owner check가 통과한다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-002` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-023` | `feature-frontend-contract-compatibility-governance` | version tuple·additive/breaking fixture·migration/rollback rule가 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-022`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-011` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-024` | `feature-frontend-release-cache-rollback-contract` | release tuple·cache header·mixed fixture failure·rollback drill이 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CACHE-POLICY-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-OFFLINE-CACHE-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-023` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-025` | `feature-web-vitals-performance-budget-contract` | context metadata와 lab·bundle·28-day field report가 생성된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-BUILD-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-016` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-026` | `feature-frontend-operational-runbook-contract` | 5개 drill의 trigger·window·escalation·evidence assertion이 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-024`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-005`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004` | `planned` |
|
||
| `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-027` | `feature-frontend-ci-quality-gates-contract` | blocking gate가 분리되고 dependency graph와 artifact retention이 검증된다 | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SUPPLY-CHAIN-001@1`, `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-DEPLOYMENT-001@1` | `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-017`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-020`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-024`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-025`, `WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-026` | `planned` |
|
||
|
||
## 20. Branch Decomposition / Execution Plan
|
||
|
||
> **Legacy reference (v1).** 아래 표는 기존 FE-OC ownership·priority 설명을 보존한다. branch handoff ID·decision pin·dependency의 SSOT는 위 Work Item Registry다.
|
||
|
||
Branch는 project-wide contract를 상세 implementation-ready spec으로 내린다. `Primary contract IDs`는 single owner만 가지며 `Contributes to`는 acceptance fixture·adapter·gate 협업만 뜻한다. `FE-OC-001`과 `FE-OC-026`의 primary owner는 이 project hub다. 아래 27개 branch-note는 2026-07-18 `/branch` scaffolding으로 생성되어 §21.2 Cluster에 연결됐다. 파일 존재는 implementation evidence가 아니며, 각 row의 mechanism·decision·test가 채워지기 전까지 상태는 계속 `planned`다.
|
||
|
||
| Branch slug | Primary contract IDs | Contributes to | Measurable completion | Priority | Dependency |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| `feature-frontend-project-bootstrap-toolchain-contract` | `FE-OC-003` | `FE-OC-018`, `FE-OC-020` | manifest/engines/pnpm lock/checkJs scripts + frozen install evidence | P1 | — |
|
||
| `feature-frontend-clean-architecture-layering-contract` | `FE-OC-002` | `FE-OC-004`, `FE-OC-011`, `FE-OC-020` | directory responsibility + port owner + allowed import matrix | P1 | `feature-frontend-project-bootstrap-toolchain-contract` |
|
||
| `feature-frontend-architecture-enforcement-lint-contract` | — | `FE-OC-002`, `FE-OC-020` | allowed fixture pass, forbidden fixture fail, report emitted | P1 | `feature-frontend-clean-architecture-layering-contract`, `feature-frontend-test-taxonomy-contract` |
|
||
| `feature-frontend-env-runtime-config-contract` | `FE-OC-004` | `FE-OC-016`, `FE-OC-023` | build/runtime/secret registry + boot invalid matrix | P1 | `feature-frontend-project-bootstrap-toolchain-contract` |
|
||
| `feature-api-client-response-envelope-contract` | `FE-OC-006`, `FE-OC-009` | `FE-OC-007`, `FE-OC-008`, `FE-OC-010`, `FE-OC-012`, `FE-OC-023` | API operation registry + timeout/abort/retry/idempotency deterministic tests | P1 | `feature-frontend-env-runtime-config-contract`, `feature-frontend-clean-architecture-layering-contract` |
|
||
| `feature-runtime-schema-validation-contract` | `FE-OC-007` | `FE-OC-008`, `FE-OC-023` | content-type/JSON/envelope/payload invalid fixtures map to expected kinds | P1 | `feature-api-client-response-envelope-contract` |
|
||
| `feature-frontend-error-classification-boundary-contract` | `FE-OC-008` | `FE-OC-011`, `FE-OC-015`, `FE-OC-020` | total normalization matrix + raw body/stack leakage negative tests | P1 | `feature-api-client-response-envelope-contract`, `feature-runtime-schema-validation-contract` |
|
||
| `feature-frontend-auth-session-integration-contract` | `FE-OC-010` | `FE-OC-005`, `FE-OC-006`, `FE-OC-019` | AuthSessionPort + bounded 401 state/replay + no token lifecycle import tests | P1 | `feature-frontend-clean-architecture-layering-contract` |
|
||
| `feature-routing-navigation-guard-contract` | `FE-OC-005` | `FE-OC-010`, `FE-OC-015`, `FE-OC-024` | registry routes, param validation, 404, redirect-loop, session UX tests | P2 | `feature-frontend-auth-session-integration-contract`, `feature-frontend-error-classification-boundary-contract` |
|
||
| `feature-server-state-caching-contract` | `FE-OC-012` | `FE-OC-011`, `FE-OC-022`, `FE-OC-024` | application-owned QueryCachePort + TanStack adapter/invalidation/stale tests | P2 | `feature-api-client-response-envelope-contract` |
|
||
| `feature-frontend-storage-registry-contract` | `FE-OC-013` | `FE-OC-022`, `FE-OC-023` | namespace/version/classification/quota fallback tests | P2 | `feature-frontend-clean-architecture-layering-contract` |
|
||
| `feature-frontend-observability-logging-trace-contract` | `FE-OC-014` | `FE-OC-008`, `FE-OC-021`, `FE-OC-025` | telemetry registry, redaction, bounded queue, sink failure tests | P2 | `feature-frontend-env-runtime-config-contract` |
|
||
| `feature-async-ui-state-contract` | `FE-OC-011` | `FE-OC-015`, `FE-OC-020`, `FE-OC-024` | required + non-blocking state matrix component tests | P2 | `feature-frontend-error-classification-boundary-contract`, `feature-server-state-caching-contract` |
|
||
| `feature-boundary-mapper-viewmodel-contract` | — | `FE-OC-007`, `FE-OC-024` | raw DTO direct use prohibited; mapper negative fixture | P2 | `feature-runtime-schema-validation-contract` |
|
||
| `feature-frontend-render-recovery-boundary-contract` | `FE-OC-015` | `FE-OC-005`, `FE-OC-011`, `FE-OC-025` | boot/route/feature/async boundary ownership + recovery fixtures | P2 | `feature-frontend-error-classification-boundary-contract`, `feature-async-ui-state-contract` |
|
||
| `feature-sample-feature-slice-contract-fixture` | `FE-OC-024` | `FE-OC-005`, `FE-OC-006`, `FE-OC-007`, `FE-OC-008`, `FE-OC-011`, `FE-OC-012`, `FE-OC-020`, `FE-OC-021` | full contract slice + sample removal smoke | P2 | `feature-api-client-response-envelope-contract`, `feature-runtime-schema-validation-contract`, `feature-frontend-error-classification-boundary-contract`, `feature-routing-navigation-guard-contract`, `feature-server-state-caching-contract` |
|
||
| `feature-frontend-test-taxonomy-contract` | `FE-OC-020` | `FE-OC-002`, `FE-OC-003`, `FE-OC-007`, `FE-OC-008`, `FE-OC-019`, `FE-OC-021`, `FE-OC-024`, `FE-OC-025` | gate/fixture/artifact mapping + one test per level | P1 | `feature-frontend-project-bootstrap-toolchain-contract` |
|
||
| `feature-tailwind-design-token-styling-contract` | — | `FE-OC-011`, `FE-OC-019`, `FE-OC-021` | theme tokens + arbitrary value policy + sample UI | P3 | `feature-frontend-project-bootstrap-toolchain-contract` |
|
||
| `feature-accessibility-baseline-contract` | — | `FE-OC-019`, `FE-OC-020`, `FE-OC-021`, `FE-OC-024` | axe + keyboard/focus manual evidence for sample routes | P3 | `feature-async-ui-state-contract` |
|
||
| `feature-frontend-build-bundle-supply-chain-contract` | `FE-OC-018` | `FE-OC-003`, `FE-OC-016`, `FE-OC-019`, `FE-OC-020`, `FE-OC-021` | frozen build, inventory, scan, bundle report | P3 | `feature-frontend-project-bootstrap-toolchain-contract`, `feature-frontend-test-taxonomy-contract` |
|
||
| `feature-frontend-browser-security-boundary-contract` | `FE-OC-019` | `FE-OC-010`, `FE-OC-013`, `FE-OC-014`, `FE-OC-018`, `FE-OC-020` | CSP/header/secret/storage/telemetry browser-boundary fixtures | P3 | `feature-frontend-auth-session-integration-contract`, `feature-frontend-storage-registry-contract`, `feature-frontend-observability-logging-trace-contract` |
|
||
| `feature-frontend-contract-registry-governance` | `FE-OC-022` | `FE-OC-004`, `FE-OC-005`, `FE-OC-006`, `FE-OC-008`, `FE-OC-012`, `FE-OC-013`, `FE-OC-014`, `FE-OC-016`, `FE-OC-020`, `FE-OC-023` | 8 registry snapshots, schema validation, single-owner checks | P2 | `feature-frontend-project-bootstrap-toolchain-contract`, `feature-frontend-clean-architecture-layering-contract` |
|
||
| `feature-frontend-contract-compatibility-governance` | `FE-OC-023` | `FE-OC-004`, `FE-OC-006`, `FE-OC-007`, `FE-OC-012`, `FE-OC-013`, `FE-OC-016`, `FE-OC-017` | version tuple matrix + additive/breaking fixtures + migration/rollback rule | P3 | `feature-frontend-contract-registry-governance`, `feature-runtime-schema-validation-contract`, `feature-frontend-storage-registry-contract` |
|
||
| `feature-frontend-release-cache-rollback-contract` | `FE-OC-016`, `FE-OC-017` | `FE-OC-004`, `FE-OC-015`, `FE-OC-021`, `FE-OC-023`, `FE-OC-025` | release tuple, headers, mixed fixture fail, rollback drill | P3 | `feature-frontend-build-bundle-supply-chain-contract`, `feature-frontend-env-runtime-config-contract`, `feature-frontend-contract-compatibility-governance` |
|
||
| `feature-web-vitals-performance-budget-contract` | `FE-OC-021` | `FE-OC-016`, `FE-OC-020` | context metadata + lab/bundle/28-day field reports | P3 | `feature-frontend-build-bundle-supply-chain-contract`, `feature-sample-feature-slice-contract-fixture` |
|
||
| `feature-frontend-operational-runbook-contract` | `FE-OC-025` | `FE-OC-004`, `FE-OC-006`, `FE-OC-014`, `FE-OC-016`, `FE-OC-017` | five drills with trigger/window/escalation/evidence assertions | P3 | `feature-frontend-release-cache-rollback-contract`, `feature-api-client-response-envelope-contract`, `feature-frontend-observability-logging-trace-contract`, `feature-frontend-env-runtime-config-contract` |
|
||
| `feature-frontend-ci-quality-gates-contract` | — | `FE-OC-020`, `FE-OC-021`, `FE-OC-022`, `FE-OC-023`, `FE-OC-024`, `FE-OC-025` | separate blocking gates, dependency graph, artifact retention | P3 | `feature-frontend-test-taxonomy-contract`, `feature-frontend-build-bundle-supply-chain-contract`, `feature-frontend-release-cache-rollback-contract`, `feature-web-vitals-performance-budget-contract`, `feature-frontend-operational-runbook-contract` |
|
||
|
||
Branch creation workflow:
|
||
|
||
```text
|
||
/branch <slug>
|
||
/branch-spec <slug> <existing evidence URLs if needed>
|
||
/depth <slug>
|
||
/coverage <slug>
|
||
```
|
||
|
||
Branch completion MUST update this table, Cluster, evidence ledger, and readiness scorecard. `planned` row를 단순히 branch file 생성만으로 `actually-implemented`로 올리지 않는다.
|
||
|
||
---
|
||
|
||
## 21. 묶음
|
||
|
||
<!-- GENERATED: project-contract-imports:start -->
|
||
## 가져온 프로젝트 계약
|
||
|
||
| Ref | Owner | 요약 | Branch 적용 |
|
||
|---|---|---|---|
|
||
| `FE-GATE-018@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | p75 목표 미달이거나 표본 임계가 미해결이면 field readiness 를 MUST 차단 | import 참조로 적용 |
|
||
| `FE-GATE-026@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | lab threshold 미달이거나 재현 메타데이터가 없으면 release 를 MUST 차단 | import 참조로 적용 |
|
||
| `FE-OC-002@1` | [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] | `domain <- application <- presentation` 의존 방향과 application-owned output port를 MUST 지킴 | import 참조로 적용 |
|
||
| `FE-OC-003@1` | [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] | package manager, engine, lockfile, source language, checkJs command를 한 곳에서 MUST 고정 | import 참조로 적용 |
|
||
| `FE-OC-004@1` | [[raw/branch-notes/feature-frontend-env-runtime-config-contract]] | build-time, runtime-public, secret config를 MUST 분리하고 boot 전에 runtime config를 검증 | import 참조로 적용 |
|
||
| `FE-OC-005@1` | [[raw/branch-notes/feature-routing-navigation-guard-contract]] | route ID/path/params/access/loading/error owner는 route registry 하나여야 함 | import 참조로 적용 |
|
||
| `FE-OC-006@1` | [[raw/branch-notes/feature-api-client-response-envelope-contract]] | 모든 HTTP는 shared client를 MUST 통과하고 timeout·abort·response parsing을 page에서 구현하면 안 됨 | import 참조로 적용 |
|
||
| `FE-OC-007@1` | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | JSON envelope와 payload는 boundary에서 runtime schema를 MUST 통과 | import 참조로 적용 |
|
||
| `FE-OC-008@1` | [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] | 모든 failure는 stable frontend error kind로 MUST 정규화하고 raw body·stack을 UI에 노출하면 안 됨 | import 참조로 적용 |
|
||
| `FE-OC-009@1` | [[raw/branch-notes/feature-api-client-response-envelope-contract]] | retry는 safe/idempotent request에 한정하고 cap·jitter·`Retry-After`를 MUST 적용 | import 참조로 적용 |
|
||
| `FE-OC-010@1` | [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] | skeleton은 session state를 소비하되 token lifecycle을 MUST 소유하지 않음 | import 참조로 적용 |
|
||
| `FE-OC-011@1` | [[raw/branch-notes/feature-async-ui-state-contract]] | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | import 참조로 적용 |
|
||
| `FE-OC-012@1` | [[raw/branch-notes/feature-server-state-caching-contract]] | query key와 invalidation은 registry factory만 MUST 사용 | import 참조로 적용 |
|
||
| `FE-OC-013@1` | [[raw/branch-notes/feature-frontend-storage-registry-contract]] | storage key는 namespace·version·classification을 MUST 가지며 token/secret 저장을 금지 | import 참조로 적용 |
|
||
| `FE-OC-014@1` | [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] | telemetry는 best-effort이며 render·API success를 차단하면 안 되고 PII·token을 전송하면 안 됨 | import 참조로 적용 |
|
||
| `FE-OC-015@1` | [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]] | expected operational error와 render defect를 MUST 분리하고 reload loop를 금지 | import 참조로 적용 |
|
||
| `FE-OC-016@1` | [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] | HTML, asset, runtime config, release manifest cache policy를 MUST 구분 | import 참조로 적용 |
|
||
| `FE-OC-017@1` | [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] | rollback은 immutable prior release로 수행하고 build/config/API compatibility를 MUST 검증 | import 참조로 적용 |
|
||
| `FE-OC-018@1` | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | frozen lockfile, dependency review, secret scan, SBOM 또는 dependency inventory를 release gate에 MUST 포함 | import 참조로 적용 |
|
||
| `FE-OC-019@1` | [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | import 참조로 적용 |
|
||
| `FE-OC-020@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | import 참조로 적용 |
|
||
| `FE-OC-021@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | NFR은 device/network/cache/build context와 함께 MUST 측정 | import 참조로 적용 |
|
||
| `FE-OC-022@1` | [[raw/branch-notes/feature-frontend-contract-registry-governance]] | 9개 registry는 single primary owner와 compatibility impact를 MUST 기록 | import 참조로 적용 |
|
||
| `FE-OC-023@1` | [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] | API/config/storage/release schema의 breaking change는 migration 또는 version bump 없이 배포하면 안 됨 | import 참조로 적용 |
|
||
| `FE-OC-024@1` | [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] | sample은 contract fixture이며 production feature가 의존하면 안 됨 | import 참조로 적용 |
|
||
| `FE-OC-025@1` | [[raw/branch-notes/feature-frontend-operational-runbook-contract]] | boot, chunk mismatch, API degradation, telemetry failure, rollback, realtime 연결, background 실행 runbook을 MUST 유지 | import 참조로 적용 |
|
||
<!-- GENERATED: project-contract-imports:end -->
|
||
|
||
<!-- GENERATED: sources:start -->
|
||
- [[raw/official-docs/owasp-content-security-policy-cheat-sheet]]
|
||
- [[raw/official-docs/react-router-official]]
|
||
- [[raw/official-docs/react-ui-library-official]]
|
||
- [[raw/official-docs/tailwind-css-utility-first-official]]
|
||
- [[raw/official-docs/tanstack-query-server-state-official]]
|
||
- [[raw/official-docs/vite-build-tool-official]]
|
||
- [[raw/official-docs/zod-runtime-schema-validation-official]]
|
||
<!-- GENERATED: sources:end -->
|
||
|
||
### 21.1 부모·형제 문서 맥락
|
||
|
||
- Backend sibling operational contract: [[raw/project-notes/ca-skeleton-operational-contract]]
|
||
- Auth lifecycle boundary: [[raw/project-notes/keycloak-patterns-overview]]
|
||
|
||
본 project-note는 `raw/project-notes/` root이므로 upward link 면제다.
|
||
|
||
### 21.2 브랜치
|
||
|
||
<!-- GENERATED: branches:start -->
|
||
- [[raw/branch-notes/feature-accessibility-baseline-contract]]
|
||
- [[raw/branch-notes/feature-api-client-response-envelope-contract]]
|
||
- [[raw/branch-notes/feature-async-ui-state-contract]]
|
||
- [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]]
|
||
- [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]]
|
||
- [[raw/branch-notes/feature-frontend-auth-session-integration-contract]]
|
||
- [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]]
|
||
- [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]]
|
||
- [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]]
|
||
- [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]]
|
||
- [[raw/branch-notes/feature-frontend-contract-compatibility-governance]]
|
||
- [[raw/branch-notes/feature-frontend-contract-registry-governance]]
|
||
- [[raw/branch-notes/feature-frontend-env-runtime-config-contract]]
|
||
- [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]]
|
||
- [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]]
|
||
- [[raw/branch-notes/feature-frontend-operational-runbook-contract]]
|
||
- [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]]
|
||
- [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]]
|
||
- [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]]
|
||
- [[raw/branch-notes/feature-frontend-storage-registry-contract]]
|
||
- [[raw/branch-notes/feature-frontend-test-taxonomy-contract]]
|
||
- [[raw/branch-notes/feature-routing-navigation-guard-contract]]
|
||
- [[raw/branch-notes/feature-runtime-schema-validation-contract]]
|
||
- [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]]
|
||
- [[raw/branch-notes/feature-server-state-caching-contract]]
|
||
- [[raw/branch-notes/feature-tailwind-design-token-styling-contract]]
|
||
- [[raw/branch-notes/feature-web-vitals-performance-budget-contract]]
|
||
<!-- GENERATED: branches:end -->
|
||
|
||
> generated reverse view는 child branch의 v2 contract migration 후 채운다. 아래 수기 목록은 그 전까지 legacy navigation으로 보존한다.
|
||
|
||
- [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] — project bootstrap·toolchain contract
|
||
- [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] — Clean Architecture layer·port ownership contract
|
||
- [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] — architecture dependency lint contract
|
||
- [[raw/branch-notes/feature-frontend-env-runtime-config-contract]] — build/runtime environment config contract
|
||
- [[raw/branch-notes/feature-api-client-response-envelope-contract]] — shared API client·response envelope contract
|
||
- [[raw/branch-notes/feature-runtime-schema-validation-contract]] — runtime schema validation contract
|
||
- [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] — normalized failure classification contract
|
||
- [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] — external auth session integration boundary
|
||
- [[raw/branch-notes/feature-routing-navigation-guard-contract]] — route registry·navigation guard contract
|
||
- [[raw/branch-notes/feature-server-state-caching-contract]] — server-state query cache contract
|
||
- [[raw/branch-notes/feature-frontend-storage-registry-contract]] — browser storage registry contract
|
||
- [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] — frontend telemetry·logging·trace contract
|
||
- [[raw/branch-notes/feature-async-ui-state-contract]] — async UI state contract
|
||
- [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] — boundary mapper·view-model contract
|
||
- [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]] — render failure recovery boundary
|
||
- [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] — removable sample feature contract fixture
|
||
- [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] — frontend test taxonomy contract
|
||
- [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] — Tailwind design-token styling contract
|
||
- [[raw/branch-notes/feature-accessibility-baseline-contract]] — accessibility baseline contract
|
||
- [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] — build·bundle·supply-chain contract
|
||
- [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] — browser security boundary contract
|
||
- [[raw/branch-notes/feature-frontend-contract-registry-governance]] — eight-registry governance contract
|
||
- [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] — contract compatibility governance
|
||
- [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] — release·cache·rollback contract
|
||
- [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] — Web Vitals·performance budget contract
|
||
- [[raw/branch-notes/feature-frontend-operational-runbook-contract]] — frontend operational runbook contract
|
||
- [[raw/branch-notes/feature-frontend-ci-quality-gates-contract]] — CI quality-gate orchestration contract
|
||
|
||
### 21.3 근거 자료
|
||
|
||
- [[raw/official-docs/vite-build-tool-official]] — build tool/config decision input
|
||
- [[raw/official-docs/react-ui-library-official]] — UI composition decision input
|
||
- [[raw/official-docs/tailwind-css-utility-first-official]] — styling decision input
|
||
- [[raw/official-docs/tanstack-query-server-state-official]] — server-state/cache decision input
|
||
- [[raw/official-docs/zod-runtime-schema-validation-official]] — runtime schema decision input
|
||
- [[raw/official-docs/react-router-official]] — route decision input
|
||
|
||
### 21.4 Diagrams
|
||
|
||
- `raw/diagrams/ca-skeleton-frontend/architecture-overview-2026-07-18.drawio` — active, dependency ownership scope reviewer PASS 100/100
|
||
- `raw/diagrams/ca-skeleton-frontend/architecture-deployment-2026-07-18.drawio` — active, static asset/runtime-config delivery scope reviewer PASS 100/100
|
||
|
||
### 21.5 오류·면접·블로그 글감·파생 문서
|
||
|
||
- Errors: 아직 없음
|
||
- Interview prep: 아직 없음
|
||
- Blog topics / job-posting tie-ins: 아직 없음
|
||
- Derived canonical: 아직 없음
|
||
|
||
Canonical 또는 derived 문서는 implementation evidence와 promotion gate를 통과하기 전 생성하지 않는다.
|
||
|
||
---
|
||
|
||
## 22. External Answer Boundary
|
||
|
||
### 22.1 현재 답할 수 있는 것
|
||
|
||
- frontend operational contract의 목표와 scope
|
||
- `FE-D*`에서 검토한 선택과 조건부 default
|
||
- `FE-OC-*`의 owner·failure·acceptance 구조
|
||
- 왜 port를 application이 소유하고 adapter가 구현하도록 설계했는지
|
||
- 왜 build/runtime/secret config를 분리했는지
|
||
- 왜 retry에 cap, jitter, idempotency 조건을 둔 설계인지
|
||
- 두 architecture diagram의 scoped reviewer PASS와 그 범위; implementation·§12 전체 deploy/rollback evidence와는 분리한 설명
|
||
|
||
### 22.2 설계라고 명시해야 답할 수 있는 것
|
||
|
||
- planned folder/file/class name
|
||
- pnpm/Vitest/RTL/MSW/Playwright/axe toolchain
|
||
- timeout, retry, bundle, performance threshold
|
||
- release/rollback sequence
|
||
- telemetry and storage policy
|
||
|
||
표현 예:
|
||
|
||
```text
|
||
현재 구현 증거는 없고, 문서상 기본값으로 설계했다.
|
||
repository 생성 후 <gate/artifact>로 검증할 예정이다.
|
||
```
|
||
|
||
### 22.3 현재 답하면 안 되는 것
|
||
|
||
- frontend를 구현·운영했다는 주장
|
||
- pnpm install/build/test를 실행했다는 주장
|
||
- bundle 200 KiB를 달성했다는 주장
|
||
- LCP/CLS/interaction target을 측정했다는 주장
|
||
- WCAG 적합성을 확보했다는 주장
|
||
- retry/rollback이 운영 장애에서 효과가 있었다는 주장
|
||
- auth token refresh를 구현했다는 주장
|
||
- diagram review가 실제 implementation·hosting·§12 전체 release/rollback을 증명한다는 주장
|
||
|
||
### 22.4 Derived output gate
|
||
|
||
면접·포트폴리오·블로그 산출물은 다음을 모두 만족한 canonical에서만 파생한다.
|
||
|
||
1. source canonical status가 `reviewed` 이상
|
||
2. 구현 주장은 `actually-implemented` 이상 evidence 보유
|
||
3. 측정 주장은 `locally-verified` 또는 `prod-verified` evidence 보유
|
||
4. readiness scorecard의 관련 gate PASS
|
||
5. 이 §의 금지 표현 위반 없음
|
||
|
||
---
|
||
|
||
## 23. 아키텍처 검토 체크리스트
|
||
|
||
### 23.1 Document structure
|
||
|
||
- [x] project overview와 evidence boundary가 있음
|
||
- [x] stable `FE-D*` decision register가 있음
|
||
- [x] stable `FE-OC-*` contract index가 있음
|
||
- [x] application-owned port와 composition root가 명시됨
|
||
- [x] route/API-operation/env/storage/error/query/telemetry/release 8개 registry owner가 있음
|
||
- [x] failure taxonomy와 retry/fallback/UX/telemetry가 있음
|
||
- [x] build/runtime/secret config 구분이 있음
|
||
- [x] NFR context와 planned command가 있음
|
||
- [x] acceptance gate와 evidence artifact가 있음
|
||
- [x] runbook 5종이 있음
|
||
- [x] binary readiness가 `NOT_READY`로 계산됨
|
||
- [x] risks/questions에 owner·trigger·resolution이 있음
|
||
- [x] branch가 contract ID에 매핑됨
|
||
- [x] Cluster와 external answer boundary가 있음
|
||
|
||
### 23.2 Diagram review
|
||
|
||
- [x] overview `.drawio` 파일이 존재하고 frontmatter에 등록됨
|
||
- [x] deployment `.drawio` 파일이 존재하고 frontmatter에 등록됨
|
||
- [x] overview diagram이 [[rules/diagram-standards]] reviewer gate 통과 — 100/100
|
||
- [x] deployment diagram이 [[rules/diagram-standards]] reviewer gate 통과 — 100/100
|
||
- [x] overview component label과 dependency ownership view가 §4.2~§4.4 scope와 일치
|
||
- [x] overview dependency edge가 scoped §4.3 matrix와 일치
|
||
- [x] deployment static asset·`/config.json` delivery slice가 declared boundary와 일치
|
||
- [ ] deployment diagram으로 §12 전체 release/rollback topology 또는 실제 hosting을 검증
|
||
- [x] reviewer report가 `FE-EV-010`, `FE-EV-011`에 연결됨
|
||
|
||
### 23.3 Implementation review
|
||
|
||
- [ ] repository URL/path/commit 확인
|
||
- [ ] manifest + lockfile 확인
|
||
- [ ] composition root 확인
|
||
- [ ] negative architecture fixture 확인
|
||
- [ ] config boot failure fixture 확인
|
||
- [ ] failure taxonomy coverage 확인
|
||
- [ ] all acceptance artifacts 확인
|
||
- [ ] release/rollback drill 확인
|
||
|
||
---
|
||
|
||
## 24. Diagram and Contract Change Management
|
||
|
||
### 24.1 Diagram lifecycle
|
||
|
||
- architecture 변경 시 새 날짜 파일을 만들거나 동일 파일 변경 사유를 version control에 남긴다.
|
||
- 폐기 diagram은 삭제보다 `raw/diagrams/ca-skeleton-frontend/archived/` 이동을 우선한다.
|
||
- frontmatter `diagrams`에는 active file만 둔다.
|
||
- `architecture_review.reviewed_at`은 reviewer gate 통과 뒤에만 채운다.
|
||
- draw.io는 static architecture/deployment용이고 sequence는 본문 Mermaid를 사용한다.
|
||
|
||
### 24.2 Drift checks
|
||
|
||
Planned checks:
|
||
|
||
```bash
|
||
CONTRACT_STABLE_ID_RE='FE-D[0-9]{3}|FE-(SC|OC|NFR|GATE|RB|EV|RDY|RISK|Q)-[0-9]{3}|FE-NFR-C[0-9]{2}|FE-REG-[A-Z]+(-[A-Z]+)*'
|
||
rg --pcre2 -o "$CONTRACT_STABLE_ID_RE" raw/project-notes/ca-skeleton-frontend-operational-contract.md | sort -u
|
||
test -z "$(comm -23 <(rg --pcre2 -o "$CONTRACT_STABLE_ID_RE" raw/project-notes/ca-skeleton-frontend-operational-contract.md | sort -u) <(rg --pcre2 -n '^\| `FE-|^### 16\.[0-9]+ `FE-RB-' raw/project-notes/ca-skeleton-frontend-operational-contract.md | rg --pcre2 -o "$CONTRACT_STABLE_ID_RE" | sort -u))"
|
||
rg -n 'application.*adapters|presentation.*adapters' <implementation-repo>/src <implementation-repo>/tests
|
||
rg -n 'architecture-(overview|deployment)-2026-07-18.drawio' raw/project-notes/ca-skeleton-frontend-operational-contract.md
|
||
```
|
||
|
||
두 번째 command는 reference set에서 definition set을 뺀 결과가 empty인지 검사한다. 세 번째 command는 repository path가 없어 실행 대상이 아직 없다.
|
||
|
||
---
|
||
|
||
## 25. Next Steps
|
||
|
||
### P0 — Evidence and architecture
|
||
|
||
- [ ] 실제 frontend repository 위치와 owner 확인
|
||
- [ ] `FE-EV-009` 범위를 repo/commit evidence로 갱신
|
||
- [x] overview/deployment scoped diagram reviewer 결과 반영
|
||
- [ ] future diagram 변경 시 §4와 scoped delivery boundary drift 재검토
|
||
|
||
### P1 — Bootstrap and blocking contracts
|
||
|
||
- [ ] `feature-frontend-project-bootstrap-toolchain-contract` 생성·설계·depth gate
|
||
- [ ] `feature-frontend-clean-architecture-layering-contract` 생성
|
||
- [ ] `feature-frontend-test-taxonomy-contract` 생성·negative fixture taxonomy 확정
|
||
- [ ] `feature-frontend-architecture-enforcement-lint-contract` 생성
|
||
- [ ] `feature-frontend-env-runtime-config-contract` 생성
|
||
- [ ] `feature-api-client-response-envelope-contract` 생성
|
||
- [ ] `feature-runtime-schema-validation-contract` 생성
|
||
- [ ] `feature-frontend-error-classification-boundary-contract` 생성
|
||
- [ ] `feature-frontend-auth-session-integration-contract` 생성
|
||
|
||
### P2 — State, UX, telemetry, fixture
|
||
|
||
- [ ] route/query/storage/telemetry registry 구현 branch 전개
|
||
- [ ] async state와 mapper boundary 전개
|
||
- [ ] sample feature contract fixture 구현
|
||
|
||
### P3 — Release readiness
|
||
|
||
- [ ] build/bundle/supply-chain gate 구현
|
||
- [ ] accessibility와 performance context 측정
|
||
- [ ] release/cache/rollback contract 구현
|
||
- [ ] CI gate dependency와 artifact retention 확정
|
||
- [ ] staging rollback drill 수행
|
||
|
||
### Promotion
|
||
|
||
- [ ] 모든 implementation claim에 repo/commit/path 연결
|
||
- [ ] 모든 locally-verified claim에 command/exit/artifact 연결
|
||
- [ ] readiness blocking rows PASS 후 `status` 재검토
|
||
- [ ] canonical `wiki/projects/` 승급은 별도 ingest review에서 수행
|
||
|
||
---
|
||
|
||
## 26. Verification Status Summary
|
||
|
||
| Area | Grade | Evidence |
|
||
| --- | --- | --- |
|
||
| operational contract text | `documented-only` | this file |
|
||
| technology decision sources | `documented-only` | §21.3 existing raw sources |
|
||
| architecture diagrams | `documented-only`, scoped reviewer PASS 100/100 each | dependency ownership view + static asset/runtime-config delivery slice; implementation/§12 full topology `UNVERIFIED` |
|
||
| implementation | `planned` | entry artifact pattern search만 미발견; repo location `UNVERIFIED` |
|
||
| tests / CI | `planned` | dedicated search/command 미기록, `UNVERIFIED` |
|
||
| NFR measurement | `planned` | target/context only |
|
||
| release / rollback | `planned` | policy/runbook only; dedicated deploy evidence `UNVERIFIED` |
|
||
| production operation | `planned` | release evidence `UNVERIFIED` |
|
||
|
||
최종 현재 판정은 `NOT_READY`다. diagram review는 통과했지만 repository, test, deploy, release evidence가 채워질 때까지 이 판정을 유지한다.
|