- REGISTRY-001 Summary 문자열 4곳 (additive, revision 은 1 유지). registry-governance 는 본문 서술 12곳의 '8개'도 함께 9개로 갱신 - OFFLINE-CACHE-001 pin @1->@2 와 Summary·적용점 (behavior-change) - DELEG-FE-007~011 delegate 5곳 접수 완료 — 미접수 위임 0건 - runtime-schema-validation 이 FLOW-FE-EVENT-003/004 소유를 명시 - env-runtime-config 가 FE-REG-CAPABILITY registry 와 FE-GATE-033 gate owner 를 취득하고 claim 2건 추가 - 신규 6개 노트의 delegation pin 을 기존 @1 형식으로 정규화
402 lines
43 KiB
Markdown
402 lines
43 KiB
Markdown
---
|
||
title: branch / feature-frontend-env-runtime-config-contract
|
||
source_type: branch-note
|
||
status: raw
|
||
id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004
|
||
kind: project-work-item
|
||
project: ca-skeleton-frontend-operational-contract
|
||
work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004
|
||
inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RUNTIME-CONFIG-001@1, DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001@1, DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CAPABILITY-001@1]
|
||
refines: []
|
||
overrides: []
|
||
depends_on: [WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001]
|
||
contract_packet: 1
|
||
branch: feature-frontend-env-runtime-config-contract
|
||
parent_branch:
|
||
related_projects: [ca-skeleton-frontend, ca-skeleton]
|
||
governing_docs: [raw/project-notes/ca-skeleton-frontend-operational-contract.md]
|
||
tags: [branch, ca-skeleton, frontend, runtime, security, javascript, externalized-config]
|
||
created: 2026-07-18
|
||
target_merge:
|
||
status_label: in-progress
|
||
contract_packet_sha256: 781ef2d614370c6a8603dfd4c254c8584737803fa6f88b19159cb46a349e841c
|
||
imports: [FE-GATE-004@1, FE-GATE-033@1, FE-OC-002@1, FE-OC-003@1, FE-OC-007@1, FE-OC-008@1, FE-OC-009@1, FE-OC-014@1, FE-OC-016@1, FE-OC-019@1, FE-OC-022@1, FE-OC-023@1, FE-OC-025@1]
|
||
---
|
||
|
||
# branch: feature-frontend-env-runtime-config-contract
|
||
|
||
> Layer: `raw/branch-notes/` — 단일 브랜치의 TODO·결정·진행 기록. 머지/종료 후 verified 결과는 `/ingest`로 `wiki/projects/`에 추출한다.
|
||
|
||
<!-- section-id: branch-parent -->
|
||
## 부모 (필수)
|
||
|
||
- [[raw/project-notes/ca-skeleton-frontend-operational-contract]]
|
||
|
||
<!-- GENERATED: branch-contract:start -->
|
||
<!-- section-id: branch-contract-packet -->
|
||
## 브랜치 계약 패킷
|
||
|
||
- **생성 시 프로젝트 개정**: `1`
|
||
- **패킷 스키마**: `contract_packet: 1`
|
||
- **완료 조건**: build/runtime/secret registry와 boot-invalid matrix가 검증된다
|
||
|
||
<!-- section-id: inherited-project-decisions -->
|
||
### 상속한 프로젝트 결정
|
||
|
||
| Decision Ref | Project Summary | Branch Application | Source |
|
||
|---|---|---|---|
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-RUNTIME-CONFIG-001@1` | deploy별 public value는 runtime config, compiler value는 build-time config로 분리한다 | config registry와 pre-mount runtime config validation에 적용한다 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CONFIG-FALLBACK-001@1` | runtime config fallback 시 environment별 rebuild는 허용하되 artifact의 multi-environment 재사용은 금지한다 | static-only hosting fallback과 artifact 재사용 금지에 적용한다 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-CAPABILITY-001@1` | 신규 runtime capability 6종은 FE-REG-CAPABILITY flag로 default OFF이며 활성화는 owner·gate·runbook을 동반한다 | `FE-REG-CAPABILITY`(hub §5.11) registry owner 로서 capability flag 6개의 schema·기본값·검증을 소유하고 `FE-GATE-033` 을 집행한다 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
|
||
|
||
<!-- section-id: branch-local-decisions -->
|
||
### 브랜치 지역 결정
|
||
|
||
| Decision ID | Decision | Relation | Supporting Claims | Status |
|
||
|---|---|---|---|---|
|
||
| D1 | build-time public·runtime-public·secret config를 분리한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D2 | runtime config fallback은 environment별 rebuild만 허용한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D3 | secret-name key를 build·runtime registry에서 거부한다 | `local` | `raw/official-docs/vite-build-tool-official.md#VITE-C4`, `raw/official-docs/vite-build-tool-official.md#VITE-C5` | `proposed` |
|
||
| D4 | React mount 전에 runtime config를 fetch하고 검증한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D5 | runtime config validation matrix를 고정한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D6 | boot failure 화면은 safe field만 노출한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D7 | 모든 public config는 FE-REG-ENV를 경유한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D8 | boot config validation 시간 예산의 측정 구간을 고정한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
|
||
<!-- section-id: declared-overrides -->
|
||
### 선언한 예외
|
||
|
||
| Override ID | Overrides | Reason | Approval | Status |
|
||
|---|---|---|---|---|
|
||
|
||
없음.
|
||
<!-- GENERATED: branch-contract:end -->
|
||
|
||
<!-- section-id: branch-goal -->
|
||
## 목표
|
||
|
||
이 브랜치는 project-wide 계약 `FE-OC-004`("build-time / runtime-public / secret config를 MUST 분리하고 boot 전에 runtime config를 검증")를 *되묻지 않고 코드를 작성할 수 있는* 구현 명세로 내린다. 구체적으로 (1) 환경 config registry `FE-REG-ENV`(`src/contracts/env.js`)를 single owner로 소유하고, (2) React mount 이전에 실행되는 runtime config fetch·검증 게이트(boot sequence 2~4단계, hub §4.5/§6.3)를 정의하며, (3) 세 종류 config(build-time public / runtime public / secret)의 분리 규칙과 secret 유출 차단 규칙(hub §6.1)을 확정한다. 근거는 hub decision `FE-D012`(deploy별 public value = pre-render runtime config, compiler value = build-time config)·`FE-D013`(runtime config fallback 규칙)과 Vite 공식 문서의 `import.meta.env` build-time 정적 치환·`VITE_` prefix 노출 경계·secret 금지 경고(`VITE-C3`/`VITE-C4`/`VITE-C5`)다. 이 계약은 `FE-OC-016`(release/cache — runtime config cache policy)과 `FE-OC-023`(compatibility — config/API schema version)에 기여한다. **현재 frontend repository는 존재하지 않으므로 본 노트의 모든 구현 항목 등급은 `planned`다.**
|
||
|
||
- 이슈: (없음 — repository 미생성)
|
||
- PR: (없음 — repository 미생성)
|
||
|
||
<!-- section-id: branch-scope -->
|
||
## 범위
|
||
|
||
### 포함 범위
|
||
|
||
- `FE-REG-ENV` 환경 config registry(`src/contracts/env.js`)의 schema·초기 row·single-owner 규칙 (`FE-OC-004`, hub §5.4)
|
||
- build-time public / runtime public / secret 3분류 규칙과 `VITE_` prefix 사용 경계 (`FE-D012`, hub §6.1)
|
||
- secret-name(`SECRET`/`PASSWORD`/`PRIVATE_KEY`/`TOKEN`) key를 build·runtime registry 양쪽에서 거부하는 정적 가드 (hub §6.1, `VITE-C4`/`VITE-C5`)
|
||
- React mount 이전 runtime config fetch(`no-store`) + 검증 게이트와 boot 실패/버전 불일치 분기 (`FE-D012`, hub §4.5/§6.3)
|
||
- runtime config 검증 규칙 카탈로그(required key·URL protocol allowlist·int range·boolean parse·schema/contract version compat·unknown-key strict) (`FE-OC-004`, hub §6.4)
|
||
- boot 실패 시 화면 노출 safe-field allowlist + redaction (hub §6.4)
|
||
- environment별 rebuild fallback 규칙: 한 artifact를 여러 env에 재사용하지 않음 (`FE-D013`)
|
||
- **boot config 검증 시간 예산 `FE-NFR-006`(≤ 500ms, network delay 제외)의 측정 경계와 valid-config timing fixture** — `FE-GATE-004` pass condition의 timing 절반 (hub §14.2, §15.1)
|
||
- **`FE-REG-CAPABILITY` registry(`src/contracts/capabilities.js`)의 schema·초기 6행·single-owner 규칙** — capability flag 는 runtime config 키이므로 env registry owner 가 자연 owner다 (`FE-D026`, hub §5.11)
|
||
- **`FE-GATE-033` capability default-off 게이트** — 기본 config 로 production build 했을 때 비활성 capability 의 adapter 가 어떤 chunk 에도 없음을 증명 (`FE-NFR-020`, hub §15.1)
|
||
|
||
### 제외 범위
|
||
|
||
> 의도적으로 제외 — 다른 owner 브랜치/계약 소유. 각 브랜치는 자신이 소유한 `FE-OC-*` 계약으로 표기(하위 `FE-D*`는 hub decision register 참조).
|
||
|
||
- **normalized error kind 어휘**(`BOOT_CONFIG_FAILURE`, `DEPLOY_MISMATCH`)와 raw body/stack UI 유출 catalog → [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`)
|
||
- **runtime schema(Zod) 구성·parse 메커니즘** 자체 → [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`)
|
||
- **release manifest 정합성 tuple·cache header·rollback·`DEPLOY_MISMATCH` recovery UI** → [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] (`FE-OC-016`, `FE-OC-017`)
|
||
- **config/API schema version breaking-change migration 정책** → [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] (`FE-OC-023`)
|
||
- **8-registry governance(single-owner diff·compatibility 추적)** → [[raw/branch-notes/feature-frontend-contract-registry-governance]] (`FE-OC-022`)
|
||
- **telemetry endpoint redaction/전송** → [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] (`FE-OC-014`) — 본 registry는 `TELEMETRY_ENABLED`/`TELEMETRY_ENDPOINT` key와 분류만 선언, 전송·redaction 메커니즘은 관측 브랜치 소유
|
||
- **token/session lifecycle** → [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] (`FE-OC-010`) — 본 registry는 `AUTH_MODE` key만 선언
|
||
- **Vite/toolchain·`import.meta.env` 노출 메커니즘 자체** → 의존 브랜치 [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`)
|
||
- **composition root 조립 순서 enforcement** → [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] (`FE-OC-002`)
|
||
|
||
## 근거 (필수, 최소 1개+)
|
||
|
||
| Source | 정당화하는 결정 |
|
||
|---|---|
|
||
| [[raw/official-docs/vite-build-tool-official]] | `VITE-C3`(`import.meta.env` build-time 정적 치환) → build-time config는 재빌드로만 바뀐다는 D1/D2 전제; `VITE-C4`(오직 `VITE_` prefix만 client 노출) → D3 노출 경계; `VITE-C5`(`VITE_*`에 secret 금지, 프로덕션 secret은 backend/serverless) → D3 secret 차단 규칙; `VITE-C2`(정적 자산 output) → static-only hosting fallback(D2) 전제 |
|
||
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | 이 branch가 owner인 `FE-D012`/`FE-D013` decision, `FE-OC-004` 계약, `FE-REG-ENV`(§5.4), boot order(§4.5)·boot sequence(§6.3)·runtime config 검증 규칙(§6.4)·boot 실패 safe-output(§6.4)·`FE-RB-001` runbook(§16.1)의 project-decision 근거. 추가로 §14.2 `FE-NFR-006`(boot config validation ≤ 500ms, deterministic mocked fetch)·§15.1 `FE-GATE-004`(valid boot config validation timing 을 pass condition 에 포함)가 D8 시간 예산의 근거 |
|
||
|
||
## TODO
|
||
|
||
각 항목 옆에 증거 등급 표기.
|
||
|
||
- [ ] `FE-REG-ENV` registry schema + 초기 14 key row 구현 (`src/contracts/env.js`) — 등급: `planned`
|
||
- [ ] build/runtime/secret 3분류 + secret-name 거부 정적 가드 구현 — 등급: `planned`
|
||
- [ ] `src/bootstrap/load-runtime-config.js` — mount 이전 `no-store` fetch + boot 분기 구현 — 등급: `planned`
|
||
- [ ] runtime config 검증 규칙(§6.4 8항) 구현 (schema 메커니즘은 `FE-OC-007` 브랜치 consume) — 등급: `planned`
|
||
- [ ] boot 실패 safe-field allowlist + redaction 구현 — 등급: `planned`
|
||
- [ ] **boot invalid-config matrix** 테스트(§20 Measurable completion) 작성 — 등급: `planned`
|
||
- [ ] config schema test(`FE-OC-004` minimum evidence) 작성 — 등급: `planned`
|
||
- [ ] **valid-config timing fixture** 작성 — `FE-NFR-006`(≤ 500ms, mocked network delay 제외) 측정 + `FE-GATE-004` timing report 산출 — 등급: `planned`
|
||
|
||
## 진행 중 메모
|
||
|
||
없음 — scaffolding 단계. repository 미생성이므로 모든 항목 `planned`.
|
||
|
||
## 결정 사항
|
||
|
||
> 각 결정의 근거는 아래 Decision Evidence Map과 1:1 대응. 대안·선택 조건 포함.
|
||
|
||
- 2026-07-18: **build-time public / runtime-public / secret 3분류 분리**(`FE-D012`) / 이유: deploy마다 달라지는 public value(API endpoint 등)를 재빌드 없이 바꾸려면 build-time 정적 치환(`import.meta.env`)이 아닌 pre-render runtime config가 필요 / 검토한 대안: 모든 값을 build-time으로 고정(env별 재빌드) / 근거: `VITE-C3`, hub §6.1·`FE-D012`
|
||
- 2026-07-18: **runtime config fallback = env별 rebuild 허용하되 artifact 재사용 금지**(`FE-D013`) / 이유: hosting이 atomic config publish를 못 할 때 deploy ambiguity를 제한 / 검토한 대안: 단일 artifact를 여러 env에 재사용 + build-time fallback / 근거: hub `FE-D013`
|
||
- 2026-07-18: **secret-name key 양쪽 registry 거부 + `VITE_`는 build metadata·non-secret 상수만** / 이유: `VITE_*`는 번들에 정적 치환되어 client에 노출되므로 secret 금지 / 검토한 대안: 관례 문서화만(정적 강제 없음) / 근거: `VITE-C4`, `VITE-C5`, hub §6.1
|
||
- 2026-07-18: **React mount 이전 runtime config fetch+검증 게이트(boot 2~4단계)** / 이유: 잘못된 config로 product route를 mount하지 않기 위해 / 검토한 대안: mount 이후 lazy config load / 근거: hub §4.5 boot order, §6.3 sequence
|
||
- 2026-07-18: **runtime config 검증 8항 커버리지 + unknown-key strict default** / 이유: config는 신뢰 경계 밖 입력이므로 boot 전 전량 검증 / 검토한 대안: 필수 key 존재만 확인 / 근거: hub §6.4 (schema 메커니즘은 `FE-OC-007` 위임)
|
||
- 2026-07-18: **boot 실패 화면 safe-field allowlist + endpoint/stack redaction** / 이유: 실패 화면으로 endpoint·raw config·stack 유출 금지 / 검토한 대안: raw error 그대로 표시 / 근거: hub §6.4 (error kind 어휘는 `FE-OC-008` 위임)
|
||
- 2026-07-18: **모든 public config는 `FE-REG-ENV` 경유(ad hoc `import.meta.env` 금지)** / 이유: rename·compatibility 영향 추적 single owner / 검토한 대안: 파일마다 `import.meta.env` 직접 접근 / 근거: hub §5.1·§5.4, `FE-D018`
|
||
- 2026-07-20: **`MAX_RETRY_ATTEMPTS` 허용 범위를 retry cap 소유 결정에 정렬(0–2)** / 이유: config가 owner 결정보다 넓은 값을 통과시키면 하류 client가 조용히 clamp 하게 되어 "설정한 값 ≠ 동작하는 값" 이 되므로, 경계 검증을 owner cap 과 동일하게 둔다 / 검토한 대안: config는 0–5를 통과시키고 API client가 clamp(설정-동작 괴리 허용) / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D015`(initial call 이후 최대 2회) — cap 소유는 `FE-OC-009`, 본 branch는 그 cap 을 config 경계에서 재선언만 하고 값 자체를 정하지 않음
|
||
- 2026-07-20: **boot config 검증 시간 예산 `FE-NFR-006` 은 "검증 구간만" 측정하며 mocked network delay 를 제외한다** / 이유: `FE-GATE-004` pass condition 이 timing 을 포함하는데(hub §15.1) 측정 구간을 고정하지 않으면 fetch 대기 시간이 예산을 잠식해 gate 가 무의미해짐 / 검토한 대안: fetch 시작~mount 직전 end-to-end 측정(hosting/network 변동에 좌우) / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-NFR-006`(§14.2, deterministic mocked fetch, ≤ 500ms excluding network delay), §15.1 `FE-GATE-004`
|
||
|
||
## 결정-근거 매핑
|
||
|
||
> `Supporting Claims`: 공식 문서는 `raw/official-docs/<slug>.md#<CLAIM>` (백틱), project decision은 hub wikilink + FE-D/§ 참조. 위임 대상 sibling 브랜치는 소유 `FE-OC-*`로 표기(하위 `FE-D*`는 hub register).
|
||
|
||
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|
||
|---|---|---|---|---|---|
|
||
| D1 | `FE-D012` — deploy별 public value는 pre-render runtime config(`/config.json`), compiler value·asset identity는 build-time config로 분리 | hosting이 HTML보다 먼저 runtime config를 atomic publish 가능 → runtime config 경로; 정적 파일만 제공 → D2 env별 rebuild fallback; SSR/edge 도입 → 본 계약 그대로 적용 않고 별도 project fork(hub §6.2) | `raw/official-docs/vite-build-tool-official.md#VITE-C3`, `raw/official-docs/vite-build-tool-official.md#VITE-C2`; [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D012` §6.1 | `conditional-default + official-doc` | hosting이 runtime config atomic publish를 미지원하면 재검토(hub revisit trigger) |
|
||
| D2 | `FE-D013` — runtime config fallback은 env별 rebuild를 허용하되 한 artifact를 여러 env에 재사용하지 않음 | atomic config publish 불가한 static-only hosting일 때만 env별 rebuild; runtime config endpoint 도입되면 단일 artifact + runtime fetch로 복귀. 어떤 경우에도 동일 artifact를 여러 env로 재배포 금지 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D013`; `raw/official-docs/vite-build-tool-official.md#VITE-C3` | `conditional-default + project-decision` | runtime config endpoint 도입 시 재검토; artifact 재사용 시 deploy ambiguity 재발 |
|
||
| D3 | secret-name(`SECRET`/`PASSWORD`/`PRIVATE_KEY`/`TOKEN`) key를 build·runtime registry 모두 거부; `VITE_` prefix는 build metadata·non-secret compile-time 상수만 | 항상 적용되는 invariant; auth owner가 browser storage를 꼭 써야 하는 경우에만 별도 threat model + owner evidence로 예외(skeleton default 아님, hub §6.1) | `raw/official-docs/vite-build-tool-official.md#VITE-C4`, `raw/official-docs/vite-build-tool-official.md#VITE-C5`; [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §6.1 | `official-doc` | 로그·디버그 등 다른 경로의 우발적 유출은 `VITE-C4`가 커버 안 함 → `FE-OC-019` browser-security와 교차 필요 |
|
||
| D4 | runtime config + release manifest를 React mount 이전에 `no-store` fetch → 검증 → (valid) 조립·mount / (invalid) boot error shell / (mismatch) recovery UI. boot 2~4단계 실패 시 product route mount 안 함 | config invalid → `BOOT_CONFIG_FAILURE`(product route mount 중단); version mismatch → `DEPLOY_MISMATCH`(controlled recovery, reload loop 금지); valid → mount. telemetry adapter 생성 실패는 non-blocking(console-safe fallback) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §4.5 boot order, §6.3 sequence, `FE-OC-004` | `project-decision` | bounded refetch(최대 1회, §16.1)와 recovery UI 경계가 `FE-OC-016`/`FE-OC-025` 소유와 겹침 |
|
||
| D5 | runtime config 검증은 required-key·URL protocol allowlist(prod https)·int range(timeout/retry)·boolean strict parse·config schema version·API contract version·release/build ID coherence·unknown-key strict를 모두 커버 | unknown key는 strict reject default; schema가 명시적으로 passthrough할 때만 additive key 허용. protocol allowlist는 prod https 강제, local 예외는 문서화된 경우만 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §6.4, `FE-OC-004`; schema 메커니즘은 [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`) 위임 | `project-decision` | `REQUEST_TIMEOUT_MS` 경계 값은 hub 미규정(아래 impl §4 `UNSUPPORTED_IMPL_DECISION`); `MAX_RETRY_ATTEMPTS` 범위는 retry cap owner(`FE-OC-009`)에 정렬해 해소(0–2); version compat 정책은 `FE-OC-023` 위임 |
|
||
| D6 | boot 실패 화면은 safe-field(`error.kind`,`error.code`,`buildId`,`configSchemaVersion`,`releaseId`,`supportReference`)만 노출; endpoint·query·header·raw config·stack은 화면 금지 | 항상 적용되는 redaction invariant — 어떤 실패 종류에서도 forbidden field는 user-facing screen에 표시 안 함 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §6.4, `FE-OC-004`; error kind 어휘는 [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`) 위임 | `project-decision` | `supportReference` 생성 방식 미규정(impl §5 `UNSUPPORTED_IMPL_DECISION`); telemetry로의 상관 전송은 `FE-OC-014` 소유 |
|
||
| D7 | 모든 build/runtime public config key는 `FE-REG-ENV`(`src/contracts/env.js`) 등록 후 사용; registry 밖 `import.meta.env`·config key 직접 사용은 violation. `public-sensitive`=browser 가시이나 로그·telemetry 원문 금지 | 항상 적용(hub `FE-D018` 8-registry single-owner invariant); code generation SSOT 채택 시 registry 형태 재검토 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.1 §5.4 `FE-D018` | `project-decision` | registry diff·single-owner 강제와 compatibility 추적은 `FE-OC-022`/`FE-OC-023` 위임 |
|
||
| D8 | boot config 검증 시간 예산 `FE-NFR-006`(≤ 500ms) 은 *검증 구간만* 측정한다 — config 본문이 메모리에 있는 시점부터 normalized config 반환 또는 `BOOT_CONFIG_FAILURE` 확정까지. fetch·mocked network delay·mount 이후는 제외. `FE-GATE-004` 는 이 branch 가 config invalid matrix + valid-config timing fixture 를, schema 브랜치가 content-type/JSON/envelope/payload invalid matrix 를 각각 제공해 함께 PASS 시킨다 | deterministic mocked fetch 환경에서 항상 측정(hub §14.2 context); 실제 network 를 타는 환경에서는 이 예산을 주장하지 않음(lab 값을 production 수치로 표현 금지) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §14.2 `FE-NFR-006`, §15.1 `FE-GATE-004`, `FE-OC-004`; invalid fixture 절반은 [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`) 소유 — 해당 노트도 timing fixture 소유를 본 branch 로 귀속 | `project-decision` | 측정 시작점·통계(단일 실행 vs 중앙값)는 hub 미규정(impl §6 `UNSUPPORTED_IMPL_DECISION`); 검증 대상 fixture 규모가 커지면 500ms 예산 재검토 필요 |
|
||
|
||
## 구현 가이드
|
||
|
||
> `planned` blueprint — frontend repository 미생성. 경로는 hub §4.6 Planned directory blueprint + §5.1 registry owner map에서 도출되어 grounded지만, 코드가 없으므로 전체가 `planned`다.
|
||
|
||
### 1. 환경 config registry `FE-REG-ENV`
|
||
|
||
> **Trace**: D7 (hub §5.1·§5.4 `FE-REG-ENV`, `FE-D018`) + D3. Planned path `src/contracts/env.js` (§5.1 owner map).
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: registry의 JS 표현(row 배열 `export const ENV_REGISTRY = [...]` vs key→meta object map)은 hub가 schema 컬럼만 규정하고 JS 구조는 미규정 → row 배열 선택. trade-off: 순서 보존 + snapshot diff(`FE-OC-022`)가 단순.
|
||
|
||
초기 14 key(hub §5.4 그대로 — 신규 발명 아님):
|
||
|
||
| 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 (허용 범위 0–2, cap owner `FE-OC-009`) | 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`(예: `API_BASE_URL`, `TELEMETRY_ENDPOINT`) = browser 가시이나 로그·telemetry에 원문 금지 (hub §5.4). secret 분류 아님.
|
||
- ad hoc 사용 위반(hub §5.1): registry 없는 `import.meta.env` 또는 config key 사용.
|
||
|
||
### 2. runtime / secret 3분류 + secret-name 거부 가드
|
||
|
||
> **Trace**: D1 (`FE-D012`, hub §6.1, `VITE-C3`) + D3 (`VITE-C4`, `VITE-C5`, hub §6.1).
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: secret-name 거부 매칭 알고리즘(case-insensitive substring `/(SECRET|PASSWORD|PRIVATE_KEY|TOKEN)/i` vs 정확 word 매칭) 미규정 — hub는 4개 토큰만 나열(§6.1) → case-insensitive substring(fail-safe) 선택. trade-off: `TOKENIZER` 같은 정당한 이름 false-positive 위험 → 문서화된 명시적 예외 목록으로 완화.
|
||
|
||
| Class | 예시 | Browser 가시 | 변경 메커니즘 | Cache | 규칙 |
|
||
|---|---|---|---|---|---|
|
||
| build-time public | `VITE_BUILD_ID`, `VITE_COMMIT_SHA`, `VITE_ROUTER_BASE_PATH` | yes | rebuild(정적 치환) | bundled | compiler behavior·asset identity만 |
|
||
| runtime public | `API_BASE_URL`, public feature flag, `TELEMETRY_ENDPOINT` | yes | runtime config publish | `no-store` | React mount 이전 검증 |
|
||
| secret | client secret, private key, DB credential, refresh token material | 번들 금지 | server/auth owner | N/A | frontend env·bundle·HTML 어디에도 금지 |
|
||
|
||
- `VITE_` prefix는 build metadata + non-secret compile-time 상수(base path, `/config.json` 위치)에만 (hub §6.1, `VITE-C4`).
|
||
- 이름에 `SECRET`/`PASSWORD`/`PRIVATE_KEY`/`TOKEN` 포함 key는 build·runtime registry 양쪽에서 거부 (hub §6.1). 프로덕션 secret은 backend/serverless 소관 (`VITE-C5`).
|
||
|
||
### 3. mount 이전 runtime config 로더 + boot 분기
|
||
|
||
> **Trace**: D4 (hub §4.5 boot order, §6.3 sequence). Planned paths `src/bootstrap/load-runtime-config.js`, `src/bootstrap/composition-root.js`, `src/bootstrap/main.jsx` (§4.6). 의존: build/`import.meta.env` 노출은 [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`); composition root 조립은 [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] (`FE-OC-002`) 소유.
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: (a) boot error shell 컴포넌트 명/경로(예: `presentation/boundaries/BootErrorShell.jsx`) — hub는 "boot error shell" 개념만, 명명/경로 미규정 → presentation/boundaries 하위에 두어 layering(`FE-OC-002`) 위반 회피. (b) pre-mount config fetch 클라이언트(raw `fetch` vs shared client) — boot 2단계 시점엔 shared client(`FE-OC-006`)가 아직 조립 전 → raw `fetch` 선택. trade-off: shared client의 timeout/retry 정책은 config 로드에 적용 안 됨(부트 전용 최소 fetch).
|
||
|
||
**본 branch 소유 구간은 단계 번호가 아니라 *의미*로 정의한다: "runtime config 취득 + 검증 완료까지, React mount 이전".** 그 앞(build identity 읽기)과 뒤(adapter 조립·mount)는 타 owner 구간이다. hub 의 두 절이 나열 순서를 서로 다르게 쓰므로(§4.5 는 config 검증 → release manifest 정합성, §6.3 sequence 는 두 fetch → 검증) 번호 기반 참조는 깨지기 쉽다. 아래 목록은 §6.3 실행 순서를 따르고, 각 행에 hub §4.5 번호를 명시 매핑한다.
|
||
|
||
| 실행 순서(hub §6.3 기준) | hub §4.5 번호 | Owner |
|
||
|---|---|---|
|
||
| build identity 읽기 *(build-time config, §1)* | 1 | build/toolchain (`FE-OC-003`) — 본 branch 는 key 분류만 |
|
||
| runtime config fetch — `GET {VITE_RUNTIME_CONFIG_URL}` `no-store` | 2 | **본 branch** |
|
||
| release manifest fetch — `GET {RELEASE_MANIFEST_URL}` `no-store` | 4의 입력 취득 | **본 branch** (정합성 판정 자체는 `FE-OC-016`) |
|
||
| config envelope·schema·compatibility 검증 *(§4)* | 3 | **본 branch** (schema 메커니즘은 `FE-OC-007` consume) |
|
||
| registry snapshot → auth adapter → HTTP/storage/telemetry/query-cache adapter → application facade → router → React root mount | 5–10 | composition root (`FE-OC-002`) |
|
||
|
||
분기(hub §6.3):
|
||
|
||
- **valid & compatible** → normalized public config로 dependency 조립 + mount.
|
||
- **invalid config** → `BOOT_CONFIG_FAILURE` → boot error shell, product route mount 안 함. automatic refetch 최대 1회(hub §16.1).
|
||
- **version mismatch** → `DEPLOY_MISMATCH` → controlled recovery UI, reload loop 금지. *(recovery UI 상세는 [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] 소유 — 본 branch는 트리거/분기까지만.)*
|
||
- telemetry adapter 생성 실패 → console-safe fallback로 계속(boot 실패 아님, hub §4.5).
|
||
|
||
### 4. runtime config 검증 규칙
|
||
|
||
> **Trace**: D5 (hub §6.4). schema 구성·parse 메커니즘은 [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`) 위임 — 본 §는 *무엇을* 검증하고 *어떤 boot 결과*로 이어지는지만.
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: `REQUEST_TIMEOUT_MS` 정수 범위(제안 1000–60000)는 hub가 default(10000)만 주고 경계 미규정 → 0/음수 timeout 방지용으로 제안. trade-off: 상한 60000 은 임의값이며 api-client owner(`FE-OC-009`)가 total timeout 정책을 lock 할 때 재확인 필요.
|
||
> - **해소됨(구 `UNSUPPORTED_IMPL_DECISION`)**: `MAX_RETRY_ATTEMPTS` 허용 범위는 **0–2** — [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D015`(initial call 이후 최대 2회)의 cap 을 config 경계에서 그대로 재선언한다. 이전 초안의 0–5 는 owner cap 보다 넓어 "config 는 통과, client 는 clamp" 하는 설정-동작 괴리를 만들었으므로 폐기. cap 값의 owner 는 `FE-OC-009` 이고 본 branch 는 값을 정하지 않으므로, cap 이 개정되면 이 범위도 따라 개정한다(본 노트 단독 변경 금지).
|
||
|
||
검증 MUST 커버(hub §6.4):
|
||
|
||
- required key 존재 (§1 Required=yes 전부)
|
||
- URL protocol allowlist — prod policy는 `https`, local 예외는 문서화된 경우만
|
||
- timeout/retry 정수 범위 — `REQUEST_TIMEOUT_MS` 1000–60000(제안), `MAX_RETRY_ATTEMPTS` 0–2(cap owner `FE-OC-009` 에 정렬)
|
||
- boolean parse — truthy-string 모호성 없이(`"false"`가 true 되지 않게)
|
||
- config schema version 호환 (`CONFIG_SCHEMA_VERSION`)
|
||
- API contract version 호환 (`API_CONTRACT_VERSION`)
|
||
- provider가 둘 다 노출하면 release/build ID coherence
|
||
- unknown-key 정책: default strict, schema가 명시적 passthrough일 때만 additive 허용
|
||
|
||
*compat 실패 시 migration/version bump 정책은 [[raw/branch-notes/feature-frontend-contract-compatibility-governance]] (`FE-OC-023`) 위임.*
|
||
|
||
### 5. boot 실패 safe-output (redaction)
|
||
|
||
> **Trace**: D6 (hub §6.4). normalized error kind 어휘와 raw body/stack UI 유출 catalog는 [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`) 위임.
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: `supportReference` 생성 방식(무작위 correlation id vs `releaseId`+timestamp) 미규정 — hub는 field만 나열 → 무작위 opaque id 선택. trade-off: deploy timing 유출 방지하나, 트리아지용으로 telemetry event와 매핑되어야 함(`FE-OC-014` 소유).
|
||
|
||
boot error shell 노출 허용 field(allowlist, hub §6.4):
|
||
|
||
```text
|
||
error.kind
|
||
error.code
|
||
buildId
|
||
configSchemaVersion
|
||
releaseId (if present)
|
||
supportReference
|
||
```
|
||
|
||
화면 금지: endpoint, query, header, raw config object, stack (hub §6.4).
|
||
|
||
### 6. boot config 검증 시간 예산 (`FE-NFR-006`) + `FE-GATE-004` 소유 분할
|
||
|
||
> **Trace**: D8 (hub §14.2 `FE-NFR-006` — deterministic mocked fetch, ≤ 500ms excluding network delay; hub §15.1 `FE-GATE-004` pass condition). invalid fixture 의 나머지 절반(content-type/JSON/envelope/payload)은 [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`) 소유 — 해당 노트도 timing fixture 소유를 본 branch 로 귀속시키므로 양방향 일치.
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: 측정 시작·종료 지점을 hub 가 규정하지 않음("excluding network delay" 만 명시) → **시작 = config 원문(text/object)이 validator 에 전달되는 시점, 종료 = normalized config 반환 또는 `BOOT_CONFIG_FAILURE` 확정 시점**으로 고정. trade-off: JSON parse 비용이 예산 안에 포함되어 보수적으로 측정되지만, transport 구현(fetch·캐시·mock)에 무관한 재현 가능 구간이 된다.
|
||
> - **UNSUPPORTED_IMPL_DECISION**: 회차·통계(단일 실행 vs 다회 중앙값)를 hub 가 미규정 → **동일 fixture 5회 실행의 중앙값을 판정값으로 쓰고 최댓값도 report 에 함께 기록**. trade-off: CI 노이즈로 인한 flake 를 줄이지만 tail latency 를 판정에서 제외하므로, 최댓값이 예산의 2배를 넘으면 report 를 근거로 재검토한다.
|
||
|
||
**측정 대상(무엇을 재는가).** `FE-NFR-006` 은 *검증 구간만* 잰다. 포함: required-key 검사, URL protocol allowlist, int range, boolean strict parse, config/API version 호환 비교, release/build ID coherence, unknown-key strict 판정(§4 8항 전부). 제외: `GET {VITE_RUNTIME_CONFIG_URL}`·`GET {RELEASE_MANIFEST_URL}` 의 network 대기, mock 이 주입한 인위적 지연, 검증 이후의 adapter 조립·mount(그 구간은 `FE-OC-002` 소유이며 본 예산의 대상이 아님).
|
||
|
||
**fixture 가 network delay 를 배제하는 방법.** transport 를 deterministic mock 으로 대체하고(hub §14.2 context), config 본문을 *이미 메모리에 있는 값*으로 validator 에 직접 전달한다. 즉 fixture 는 fetch 를 거치지 않거나, 지연을 주입한 mock 을 쓰더라도 타이머를 fetch resolve *이후*에 시작한다. 따라서 mock 지연을 늘려도 측정값이 변하지 않아야 하며, 이 불변식 자체를 fixture 의 self-check 로 둔다(지연 0ms 와 지연 200ms 두 실행의 측정값 차이가 노이즈 범위 내).
|
||
|
||
**valid-config fixture 형태.** §1 registry 의 runtime key 10개를 모두 채운 valid config 1건(= 실제 boot 가 받는 최대 폭). 판정: 중앙값 ≤ 500ms.
|
||
|
||
**`FE-GATE-004@1` 소유 분할** (hub §15.1 의 Covered FE-OC 가 다수라 fixture 소유를 명시해야 중복·누락이 없다 — 어느 계약이 묶여 있는지는 hub §15.1 소유):
|
||
|
||
| `FE-GATE-004` 구성요소 | 소유 |
|
||
|---|---|
|
||
| config invalid matrix (required key 부재·protocol 위반·range 위반·boolean 모호·unknown key·version 비호환) | **본 branch** (`FE-OC-004`) |
|
||
| valid-config timing fixture + timing report (`FE-NFR-006`) | **본 branch** (`FE-OC-004`) |
|
||
| content-type / JSON / envelope / payload invalid matrix | `FE-OC-007` |
|
||
| 각 invalid 입력의 기대 error kind 어휘 | `FE-OC-008` |
|
||
| version 비호환 시 migration 판정 | `FE-OC-023` |
|
||
|
||
gate 는 두 소유자의 fixture 가 모두 있어야 PASS 하므로, 어느 한쪽만 준비된 상태에서 `FE-GATE-004` 를 PASS 로 올리지 않는다.
|
||
|
||
## 엣지·실패·의존
|
||
|
||
- **실패·엣지 경로**:
|
||
- config fetch non-2xx / JSON parse 실패 / schema 비호환 → `BOOT_CONFIG_FAILURE`, product route mount 중단, auto refetch 최대 1회 (hub §6.3·§16.1).
|
||
- config/API/release version mismatch → `DEPLOY_MISMATCH`, controlled recovery UI, reload loop 금지 (hub §6.3).
|
||
- invalid value(timeout/retry 범위 밖, boolean truthy-string, required key 부재) → boot fail (hub §5.4).
|
||
- URL protocol 위반(prod에서 non-https) → boot fail (hub §6.4).
|
||
- `TELEMETRY_ENABLED=true`인데 `TELEMETRY_ENDPOINT` 부재 → telemetry degrade(boot fail 아님, hub §5.4).
|
||
- telemetry adapter 생성 실패 → console-safe fallback, boot 계속 (hub §4.5).
|
||
- secret-name key가 env에 존재 → registry 거부(build/runtime), boot·build fail (hub §6.1).
|
||
- **다른 계약 의존**:
|
||
- [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`) — `import.meta.env`·`VITE_` prefix 노출과 build identity 주입. 이 계약(checkJs·Vite build)이 바뀌면 build-time config 접근 방식 영향.
|
||
- [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] (`FE-OC-002`) — composition root(`bootstrap`) 소유. boot 2~4단계는 이 composition root 안의 단계로 slot in.
|
||
- [[raw/branch-notes/feature-runtime-schema-validation-contract]] (`FE-OC-007`) — config 검증에 쓰는 Zod schema 메커니즘 consume. **`FE-GATE-004` 협업**: 본 branch 가 config invalid matrix + valid-config timing fixture(`FE-NFR-006`)를, 그쪽이 content-type/JSON/envelope/payload invalid matrix 를 제공(impl §6 분할표).
|
||
- [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] (`FE-OC-019`) — D3 의 secret 차단은 *key 이름 기반 정적 거부*까지만 담당하고, 번들 scan·로그/telemetry 유출 등 *실제 노출 경로* 차단은 그쪽 소유. 두 계약이 함께 있어야 "secret 이 브라우저에 안 간다"가 성립한다.
|
||
- [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`) — `BOOT_CONFIG_FAILURE`/`DEPLOY_MISMATCH` normalized kind consume.
|
||
- [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] (`FE-OC-016`/`FE-OC-017`) — release manifest 정합성·`DEPLOY_MISMATCH` recovery·cache header. 본 branch는 검증된 config를 provide, recovery는 그쪽 소유.
|
||
- [[raw/branch-notes/feature-api-client-response-envelope-contract]] (`FE-OC-006`) — 검증된 `API_BASE_URL`/`REQUEST_TIMEOUT_MS`/`MAX_RETRY_ATTEMPTS`를 consume(하류 소비자).
|
||
- [[raw/branch-notes/feature-frontend-operational-runbook-contract]] (`FE-OC-025`) — boot config failure containment/escalation runbook(`FE-RB-001`, hub §16.1)의 technical escalation.
|
||
|
||
## 검증해야 할 주장
|
||
|
||
| Claim | Why uncertain | How to verify | Status |
|
||
|---|---|---|---|
|
||
| build/runtime/secret 분류가 실제로 강제됨(secret-name key가 양쪽 registry에서 거부) | 코드·정적 가드 미존재, 규칙 문서만 있음 | config schema test + secret-name 거부 negative fixture (`FE-OC-004` minimum evidence) | `needs-confirmation` |
|
||
| boot invalid-config matrix의 각 invalid 입력이 기대 boot 결과(`BOOT_CONFIG_FAILURE`/`DEPLOY_MISMATCH`/boot fail)로 매핑 | 다양한 실패 조합의 실제 boot 분기 미검증 | **boot invalid-config matrix** 테스트(§20 Measurable completion) | `needs-confirmation` |
|
||
| runtime config가 `no-store`로 fetch되고 React mount 이전에 검증됨(실패 시 product route mount 안 됨) | 조립 순서·no-store가 코드로 보장되는지 미검증 | boot ordering 통합 테스트(mount 이전 fetch·실패 시 route 미mount 확인) | `needs-confirmation` |
|
||
| boot 실패 화면이 safe-field만 노출(endpoint·raw config·stack 미유출) | redaction 강제 여부 미검증 | boot error shell redaction negative fixture | `needs-confirmation` |
|
||
| capability flag 6개가 전부 OFF인 기본 config 로 build 하면 대응 adapter 가 번들에 없다 | 정적 import 하나면 조용히 포함됨. tree-shaking 이 보장하지 않음 | `FE-GATE-033` capability bundle report — 각 adapter 모듈 경로가 어떤 chunk 에도 없는지 대조 | `planned` |
|
||
| capability 전부 OFF 일 때 initial JS gzip 증가가 0 KiB 다 | `FE-NFR-001` 예산을 신규 기능이 잠식할 수 있음 | bundle report 비교 — 확장 전후 initial chunk 크기 (`FE-NFR-020`) | `planned` |
|
||
| 한 artifact를 여러 env에 재사용하지 않음(`FE-D013`) | 배포 프로세스 속성 — unit test로 완전 증명 불가 | 배포 파이프라인 assertion + env별 artifact hash 대조(문서화된 deploy check) | `needs-confirmation` |
|
||
| non-`VITE_` build 변수가 client 번들로 유출되지 않음 | 번들 정적 치환 경계는 실제 빌드로만 확인 | build 후 bundle scan (`FE-OC-019` browser-security와 교차) | `needs-confirmation` |
|
||
| valid config 검증이 `FE-NFR-006` 예산(≤ 500ms, mocked network delay 제외) 안에 들어옴 | 코드·검증 로직 미존재. 8항 검증 + schema 라이브러리(`FE-OC-007`)의 deep clone/parse 비용이 미측정이라 500ms 가 여유인지 빠듯한지 알 수 없음 | runtime key 10개를 채운 valid-config timing fixture 5회 실행의 중앙값 측정(impl §6) → `FE-GATE-004` timing report | `needs-confirmation` |
|
||
| timing fixture 의 측정값이 mocked network delay 에 영향받지 않음(예산이 검증 구간만 잰다) | 측정 시작점이 fetch resolve 이후인지 코드로 강제되는지 미검증 | 동일 fixture 를 mock 지연 0ms / 200ms 로 각각 실행해 측정값 차이가 노이즈 범위 내인지 확인(impl §6 self-check) | `needs-confirmation` |
|
||
|
||
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
|
||
|
||
> `/coverage`가 채우는 생성물이며 손으로 유지하지 않는다.
|
||
|
||
| 관심사 | 상태 | owner | 심각도 | 근거 |
|
||
|---|---|---|---|---|
|
||
| TODO — `/coverage` 실행 전 | missing | (없음) | 미평가 | TODO |
|
||
|
||
## 마주친 문제
|
||
|
||
없음 — scaffolding 단계
|
||
|
||
## 묶음 (이 branch에서 파생된 자료)
|
||
|
||
<!-- GENERATED: project-contract-imports:start -->
|
||
## 가져온 프로젝트 계약
|
||
|
||
| Ref | Owner | 요약 | Branch 적용 |
|
||
|---|---|---|---|
|
||
| `FE-GATE-004@1` | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | invalid fixture 가 예상 kind 로 거부되지 않으면 merge 를 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-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-014@1` | [[raw/branch-notes/feature-frontend-observability-logging-trace-contract]] | telemetry는 best-effort이며 render·API success를 차단하면 안 되고 PII·token을 전송하면 안 됨 | 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-019@1` | [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | import 참조로 적용 |
|
||
| `FE-OC-022@1` | [[raw/branch-notes/feature-frontend-contract-registry-governance]] | 8개 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-025@1` | [[raw/branch-notes/feature-frontend-operational-runbook-contract]] | boot, chunk mismatch, API degradation, telemetry failure, rollback runbook을 MUST 유지 | import 참조로 적용 |
|
||
<!-- GENERATED: project-contract-imports:end -->
|
||
|
||
### Sub-branches (세부 작업)
|
||
|
||
없음 — scaffolding 단계
|
||
|
||
### 오류 기록 (이 branch 작업 중 발생)
|
||
|
||
없음 — scaffolding 단계
|
||
|
||
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
|
||
|
||
없음 — scaffolding 단계
|
||
|
||
### 강의 (이 작업을 위해 학습한 강의)
|
||
|
||
없음 — scaffolding 단계
|
||
|
||
### job-posting tie-ins (이 작업에서 파생된 글감)
|
||
|
||
없음 — scaffolding 단계
|
||
|
||
## 관련 일일 노트
|
||
|
||
없음 — scaffolding 단계
|
||
|
||
## 완료 후 정리
|
||
|
||
- PR 링크:
|
||
- 리뷰 메모:
|
||
- 머지 결과 / 배포 환경:
|
||
- **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출):
|
||
- `actually-implemented` 항목:
|
||
- `locally-verified` 항목:
|
||
- `prod-verified` 항목:
|
||
- **추출하지 않을 항목** (planned / documented-only / abandoned):
|