282 lines
27 KiB
Markdown
282 lines
27 KiB
Markdown
---
|
|
title: branch / feature-boundary-mapper-viewmodel-contract
|
|
source_type: branch-note
|
|
status: raw
|
|
branch: feature-boundary-mapper-viewmodel-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, mapper, react, clean-architecture]
|
|
created: 2026-07-18
|
|
target_merge:
|
|
status_label: in-progress
|
|
id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-014
|
|
kind: project-work-item
|
|
project: ca-skeleton-frontend-operational-contract
|
|
work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-014
|
|
inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1, DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1]
|
|
refines: []
|
|
overrides: []
|
|
depends_on: [WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-006]
|
|
contract_packet: 1
|
|
contract_packet_sha256: 6a29a98487f6cf6afb2a40f0dd7b31f4f895e00a2b536821ce0de9fc6aded104
|
|
imports: [FE-OC-002@1, FE-OC-007@1, FE-OC-008@1, FE-OC-024@1, FLOW-FE-RESP-001@1, FLOW-FE-RESP-002@1, FLOW-FE-RESP-003@1, FLOW-FE-RESP-004@1, FLOW-FE-RESP-005@1, FLOW-FE-RESP-006@1, FLOW-FE-RESP-008@1]
|
|
---
|
|
|
|
# branch: feature-boundary-mapper-viewmodel-contract
|
|
|
|
> Layer: `raw/branch-notes/` — 단일 브랜치의 TODO·결정·진행 기록. 현재는 `/branch-spec` 로 채운 `planned` 설계 단계다 (frontend 코드 저장소 아직 없음 — 모든 구현 주장은 `planned`).
|
|
|
|
<!-- 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`
|
|
- **완료 조건**: raw DTO direct use가 차단되고 mapper negative fixture가 실패한다
|
|
|
|
<!-- section-id: inherited-project-decisions -->
|
|
### 상속한 프로젝트 결정
|
|
|
|
| Decision Ref | Project Summary | Branch Application | Source |
|
|
|---|---|---|---|
|
|
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-VALIDATION-001@1` | boundary runtime validation은 Zod schema로 수행한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
|
|
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-ARCHITECTURE-001@1` | domain, application, presentation, adapters, bootstrap 책임을 분리한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
|
|
|
|
<!-- section-id: branch-local-decisions -->
|
|
### 브랜치 지역 결정
|
|
|
|
> 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다.
|
|
|
|
| Decision ID | Decision | Relation | Supporting Claims | Status |
|
|
|---|---|---|---|---|
|
|
|
|
<!-- section-id: declared-overrides -->
|
|
### 선언한 예외
|
|
|
|
| Override ID | Overrides | Reason | Approval | Status |
|
|
|---|---|---|---|---|
|
|
<!-- GENERATED: branch-contract:end -->
|
|
|
|
<!-- section-id: branch-goal -->
|
|
## 목표
|
|
|
|
이 브랜치는 hub §20 기준 **Primary contract owner 가 없는 기여(contribute) 브랜치**다. project-wide 계약 `FE-OC-007`(경계에서 JSON envelope·payload 를 runtime schema 로 검증) 과 `FE-OC-024`(sample 은 제거 가능한 contract fixture) 의 교집합인 **"raw DTO 직접 사용 금지 → boundary mapper 가 application model 을 생산하고 application 이 view-model 로 투영"** 책임을, 되묻지 않고 코드를 쓸 수 있는 implementation-ready spec 으로 내린다. 근거 축은 hub §4.2/§4.3 Clean Architecture layering(presentation 은 raw API DTO 를 소유·소비하면 안 되고 application 이 view-model 계약을 소유) + §7.3 응답 처리 순서 stage 7 `DTO → application model mapper`(§2.1.4 `FLOW-FE-RESP-007`) + §9.1 async `success` state 의 `view-model render` 요구다. 측정 가능한 완료 조건(hub §20): **raw DTO 직접 사용 금지 + mapper negative fixture**.
|
|
|
|
- 이슈: (아직 없음 — 저장소 생성 전)
|
|
- PR: (아직 없음)
|
|
|
|
<!-- section-id: branch-scope -->
|
|
## 범위
|
|
|
|
### 포함 범위
|
|
|
|
- raw backend DTO 가 http-adapter 경계를 넘어 application/presentation 으로 흐르지 못하게 하는 **containment 규칙**과 그 경계에 놓이는 **DTO → application model mapper** 의 위치·계약(§7.3 stage 7, `FLOW-FE-RESP-007`).
|
|
- application 이 소유하는 **view-model 계약**(render-ready shape)의 소유 위치·소비 규칙(§4.2/§4.3/§9.1).
|
|
- mapper 를 **total/guarded function** 으로 만드는 규칙: mapper 자체 throw → `UNKNOWN_FAILURE` catch-all (§8.2 total function, §8.5 fixture).
|
|
- 위 규칙을 증명하는 **mapper negative fixture** 와, sample slice 안의 제거 가능한 mapper 시연부(`FE-OC-024` 기여분).
|
|
|
|
### 제외 범위
|
|
|
|
> 의도적으로 제외 — 다른 owner 브랜치 소유. 여기서 detail 을 재정의하지 않고 owner 로 위임한다.
|
|
|
|
- **payload/envelope schema 정의·검증 메커니즘 자체 (Zod `.parse()`, schema 파일)** → `FE-OC-007` owner [[raw/branch-notes/feature-runtime-schema-validation-contract]]. 본 브랜치는 그 검증된 output(validated clone)을 mapper 입력으로 **소비만** 한다.
|
|
- **normalized failure kind 카탈로그와 `UNKNOWN_FAILURE` 의 정규화 shape** → `FE-OC-008` owner [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]]. 본 브랜치는 mapper throw 를 그 catch-all 로 넘길 뿐, kind 목록을 정의하지 않는다.
|
|
- **shared HTTP client·응답 envelope 파싱 파이프라인(§7.3 stage 1~6)** → `FE-OC-006` owner [[raw/branch-notes/feature-api-client-response-envelope-contract]].
|
|
- **import 방향 정적 강제(dependency-cruiser/ESLint restricted import) 규칙 엔진** → `FE-OC-002` owner [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] / architecture lint 브랜치. 본 브랜치는 forbidden-import fixture case 만 제공.
|
|
- **sample feature slice 의 실제 route/page/필드 내용** → `FE-OC-024` owner [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]]. 본 브랜치는 그 slice 안의 mapper stage 만 소유.
|
|
- **async surface state(`initial-loading`/`empty`/`terminal-error`) 렌더링** → `FE-OC-011` owner [[raw/branch-notes/feature-async-ui-state-contract]].
|
|
|
|
## 근거 (필수, 최소 1개+)
|
|
|
|
| Source | 정당화하는 결정 |
|
|
|---|---|
|
|
| [[raw/official-docs/react-ui-library-official]] `#REACT-UI-C1`, `#REACT-UI-C5` | D3 — presentation 이 view-model 을 컴포넌트 props(단방향 데이터 흐름)로 소비한다는 초기 근거. React component 모델·props 전달이 "presentation 은 view-model type 만 import" 규칙과 정합. **간접 근거**(component 모델 일반론이며 mapper 전용 계약은 아님). |
|
|
| [[raw/official-docs/zod-runtime-schema-validation-official]] `#ZOD-VALID-C3`, `#ZOD-VALID-C4` | D2 — `.parse()` 가 반환하는 "strongly-typed deep clone" 이 mapper 의 입력(검증된 payload)이라는 근거. mapper 는 unvalidated JSON 이 아니라 검증 통과한 clone 만 받는다. `.parse()` 실패 throw 는 검증 계층(FE-OC-007) 소관이며 mapper 실행 전이다. |
|
|
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §4.2/§4.3, `FE-D009`, `FE-D010` | D1 — presentation 은 raw API DTO 를 소유·import 하면 안 되고 application 이 view-model 계약을 소유(dependency rule). |
|
|
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §7.3 stage 7 · §2.1.4 `FLOW-FE-RESP-007` | D2 — `DTO → application model mapper` 가 응답 처리 순서 stage 7(검증 stage 4~6 이후, application 결과 반환 stage 8 이전)이라는 위치 근거. stage 7 산출물이 model 이고 view-model 이 아니라는 것도 같은 근거. |
|
|
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §4.2/§9.1 | D3 — application 이 view-model 계약 소유 + async `success` state 는 `view-model render`. |
|
|
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §8.2/§8.5 | D4 — normalization 은 total function; mapper 예외는 `UNKNOWN_FAILURE` catch-all 로 흡수하고 raw value 폐기. negative fixture 필수. |
|
|
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-024`, `FE-D025` | D5 — sample 은 제거 가능한 contract fixture 이며 product import 금지. mapper 시연부는 이 slice 안에 둔다. |
|
|
|
|
## TODO
|
|
|
|
각 항목 옆에 증거 등급 표기. 저장소 미생성이므로 전부 `planned` / `needs-confirmation`.
|
|
|
|
- [ ] http-adapter 경계에서 raw DTO 가 application/presentation 으로 새지 않게 하는 containment 규칙과 mapper 위치(stage 7) 확정 — 등급: `planned`
|
|
- [ ] application 소유 view-model 계약(render-ready shape)의 위치·소비 규칙 명세 — 등급: `planned`
|
|
- [ ] mapper 를 guarded total function 으로 구현(예외 → `UNKNOWN_FAILURE` 위임) — 등급: `planned`
|
|
- [ ] mapper negative fixture(예외 유발 → `UNKNOWN_FAILURE` 기대) + presentation-imports-raw-DTO forbidden fixture case 작성 — 등급: `planned`
|
|
- [ ] sample slice 안 mapper 시연부가 제거 가능하고 product import 0건임을 확인 — 등급: `needs-confirmation`
|
|
|
|
## 진행 중 메모
|
|
|
|
- hub §20 상 본 브랜치는 Primary owner 없음 + `FE-OC-007`·`FE-OC-024` 기여, dependency = [[raw/branch-notes/feature-runtime-schema-validation-contract]] (검증된 payload 를 stage 7 로 넘겨받음). 그 sibling 노트가 이미 stage 7 mapper 를 본 브랜치로 위임(`FE-OC-007`·`FE-OC-024` 기여)하고 있어 정합 확인됨 — drift 없음.
|
|
- ~~hub 내부 경미한 표현 불일치: §7.3 은 stage 7 을 "DTO → view-model mapper"(adapter 경계) 로, §4.2/§4.4 는 adapter 가 "validated model" 을 반환하고 application 이 "view-model 계약" 을 소유한다고 기술.~~ → **해소됨(2026-07-21)**: hub §7.3 stage 7 이 `DTO → application model mapper` 로 정정되고 "view-model 투영은 application 소유" 가 본문에 명시됐다. 같은 사실이 hub §2.1.4 Flow Stage Registry `FLOW-FE-RESP-007` 의 Invariants 로 고정되어, 본 브랜치가 채택한 2-stage 해석이 이제 hub 결정이다.
|
|
|
|
## 결정 사항
|
|
|
|
> 아래 Decision Evidence Map 의 산문형 요약. 각 결정의 근거는 Sources 표 및 hub 참조.
|
|
|
|
- 2026-07-18: **raw DTO containment** — raw backend DTO 는 http-adapter 경계를 넘지 못하고, presentation/use-case 는 application 소유 view-model 만 소비한다. 이유: hub §4.2/§4.3 dependency rule(presentation MUST NOT own raw DTO). 검토한 대안: presentation 이 DTO 에서 직접 파생 — layering(`FE-D009`/`FE-D010`) 위반이라 기각.
|
|
- 2026-07-18: **mapper 위치 = stage 7** — DTO → application model mapper 는 §7.3 처리 순서 stage 7(schema 검증 이후, 결과 반환 이전)에 놓이며 입력은 검증된 clone 이다. 대안: 검증 전 raw JSON 매핑 — 검증 우회라 기각. (2026-07-21 정정: stage 7 산출물은 model 이고 view-model 이 아니다 — hub §7.3 · §2.1.4 `FLOW-FE-RESP-007`.)
|
|
- 2026-07-18: **view-model 소유 = application** — view-model 계약은 application 이 소유(`application/view-models/`), presentation 은 type 만 import. 대안: presentation-local view-model — `FE-D010`(application-owned contract) 위반이라 기각.
|
|
- 2026-07-18: **mapper = total/guarded function** — mapper 예외는 presentation 으로 throw 되지 않고 `UNKNOWN_FAILURE` catch-all 로 흡수(§8.2). negative fixture 로 증명. 대안: 예외 전파 — §8.2 total function 요구 위반이라 기각.
|
|
- 2026-07-18: **mapper 시연부 = 제거 가능한 sample fixture** — mapper 데모 + fixture 는 `sample/contract-fixture/` 안에 두고 product 는 import 금지(`FE-OC-024`/`FE-D025`). 대안: 공용 product util — sample 제거 smoke 위반이라 기각.
|
|
|
|
## 결정-근거 매핑
|
|
|
|
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|
|
|---|---|---|---|---|---|
|
|
| D1 | raw API DTO 는 http-adapter 경계를 넘지 못하고 presentation/use-case 는 application 소유 view-model 만 소비 (raw DTO 직접 사용 금지) — `FE-OC-007`·`FE-OC-024` 기여 | 스켈레톤의 모든 read/query 응답에 항상 적용되는 invariant. 대안(presentation 이 DTO 에서 직접 파생)은 layering 결정 `FE-D009`/`FE-D010` 가 뒤집힐 때만 가능하고 그건 `FE-OC-002` owner 브랜치 소관 — 본 브랜치에서 바꾸지 않음 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §4.2/§4.3, `FE-D009`, `FE-D010`, `FE-OC-007` | project-decision | DTO→model 경계를 물리적으로 adapter 에 둘지 application 에 둘지 미세 미확정 → 구현 §1 |
|
|
| D2 | DTO → application model mapper 는 §7.3 처리 순서 stage 7(검증 stage 4~6 이후, 결과 반환 stage 8 이전)에 위치하고 입력은 검증된 payload(deep clone); view-model 투영은 이 단계가 아니라 application 소유 | success branch(검증 통과)일 때만 mapper 실행. 검증 실패면 mapper 실행 안 하고 `SCHEMA_MISMATCH`/normalized-failure 경로(FE-OC-008)로 감 — 즉 대안은 "실행 안 함" | `raw/official-docs/zod-runtime-schema-validation-official.md#ZOD-VALID-C3`; [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §7.3, `FE-OC-007` | official-doc + project-decision | stage 7 라벨이 dependency sibling 과 일치(확인됨). 검증계층 output 형태 변경 시 mapper 입력 계약 재확인 필요 |
|
|
| D3 | view-model 계약(render-ready shape)은 application 이 소유(`application/view-models/`); presentation 은 view-model type 만 import 하고 async `success` state 가 이를 render | 모든 slice 에서 application 소유가 default. 대안(presentation-local 또는 adapter 소유 view-model)은 `FE-D010`(application-owned contract) 를 layering owner 가 개정할 때만 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §4.2/§9.1, `FE-D010`; `raw/official-docs/react-ui-library-official.md#REACT-UI-C5` | project-decision + official-doc | adapter 의 validated-model 과 application 의 view-model 2-stage 분리 세부 미확정 → 구현 §1 |
|
|
| D4 | mapper 는 total/guarded function — 예외(누락/renamed 필드, non-Error throw)는 presentation 으로 전파되지 않고 `UNKNOWN_FAILURE` catch-all 로 흡수(raw value 폐기) | mapper 예외는 항상 `UNKNOWN_FAILURE` 로. mapper throw 가 presentation 에 도달하도록 허용하는 조건은 없음(N/A) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §8.2, §8.5, `FE-OC-008` | project-decision | `UNKNOWN_FAILURE` 정규화 shape 자체는 `FE-OC-008` owner 소유 → 위임 |
|
|
| D5 | mapper 시연부 + negative fixture 는 제거 가능한 sample slice(`sample/contract-fixture/`) 안에 두고 product feature 는 import 금지 | fixture 는 항상 sample 안. mapper 가 실제 product feature 에 필요해지면 sample 밖으로 graduate 하고 그 feature 브랜치가 소유(대안) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-024`, `FE-D025`, §4.2 | project-decision | sample slice 내용/route 는 `FE-OC-024` owner 소유 → 위임; 본 브랜치는 mapper stage 만 |
|
|
|
|
## 구현 가이드
|
|
|
|
> `planned` blueprint (frontend 저장소 없음). CLAUDE.md §15.5 3-rule 준수: R1 Trace 필수, R2 UNSUPPORTED_IMPL_DECISION, R3 OUT_OF_BRANCH_SCOPE 정제. 경로는 hub §4.6 Planned directory blueprint + §5.1 에서 도출된 `planned` anchor.
|
|
|
|
### 1. 경계 배치 & mapping 파이프라인 (planned)
|
|
|
|
> **Trace**: D1 + D2 + D3 → hub §4.2/§4.3/§4.6, §7.3 stage 7, `FE-OC-007`. raw DTO 는 adapter 에서 멈추고, 검증된 clone 이 model 로, model 이 view-model 로 이어진다.
|
|
>
|
|
> - **(a) 2-stage 매핑 — 근거 있는 결정(2026-07-21 확정)**: mapper 는 stage 7 에서 `application model` 까지만 만들고 view-model 투영은 `application/view-models/` 가 소유한다. 근거: hub §7.3 stage 7 + §2.1.4 `FLOW-FE-RESP-007@1`(Invariants: "이 단계 산출물은 model 이고 view-model 이 아니다"). 본 브랜치가 임의로 고른 trade-off 가 아니라 hub 가 결정한 계약이므로 `UNSUPPORTED_IMPL_DECISION` 라벨을 뗀다.
|
|
> - **UNSUPPORTED_IMPL_DECISION**: (b) mapper 모듈 파일 경로·명명(`src/adapters/http/<op>-model-mapper.js`, `src/application/view-models/<slice>-view-model.js`)은 hub §4.6 이 디렉터리(`adapters/http/`, `application/view-models/`)만 고정하고 파일명은 미규정 — trade-off: op/slice 접미사 convention 을 임의 채택(저장소 생성 시 조정 가능).
|
|
|
|
| 파이프라인 단계 | 입력 | 출력 | 소유 layer (planned 경로) | 규칙 |
|
|
|---|---|---|---|---|
|
|
| raw DTO 수신 | backend 응답 body | (경계 내부에서만 존재) | `adapters/http/` | raw DTO 는 이 layer 밖으로 반환·재노출 금지 |
|
|
| schema 검증 | raw DTO | validated clone | `adapters/http/` (검증 메커니즘은 `FE-OC-007` owner 위임) | 검증 통과분만 다음 단계로 |
|
|
| model 매핑 (2-stage 中 1) | validated clone | domain/application model | `adapters/http/` | validated payload → application-facing model |
|
|
| view-model 투영 (2-stage 中 2) | application model | view-model | `application/view-models/` | render-ready shape 생산; raw status code·DTO 필드 1:1 노출 금지 |
|
|
| 소비 | view-model | 렌더 | `presentation/` | view-model type 만 import (§4.3), raw DTO schema import 금지 |
|
|
|
|
### 2. mapper 함수 계약 (planned)
|
|
|
|
> **Trace**: D2 + D4 → hub §7.3 stage 7, §8.2 total function, `raw/official-docs/zod-runtime-schema-validation-official.md#ZOD-VALID-C3`.
|
|
>
|
|
> - **UNSUPPORTED_IMPL_DECISION**: mapper 함수 signature/형태(순수 함수 `mapToModel(validatedPayload) → model` vs 클래스) 는 hub 가 미규정 — **순수 함수 채택**, trade-off: 테스트·treeshake 용이하나 stateful 전처리가 필요해지면 재검토. field 투영 방식(explicit allowlist 매핑 vs spread) 도 미규정 — **explicit 매핑 채택**, trade-off: 새 필드가 자동 노출되지 않아 안전하나 필드 추가 시 수기 갱신 필요.
|
|
|
|
- 입력: schema 검증을 통과한 payload(= `.parse()` 의 deep clone, `#ZOD-VALID-C3`). unvalidated JSON 을 입력으로 받는 경로 없음.
|
|
- 출력: application model(성공) **또는** 정규화 실패로의 위임(§8.2). mapper 는 실패를 직접 만들지 않고 catch-all 로 넘긴다. view-model 투영은 이 단계가 아니라 `application/view-models/` 소유(2-stage 中 2).
|
|
- guard: mapper 본문은 예외 안전 경계(try 경로) 안에서 실행되어 예외/누락 필드/비-Error throw 시 raw value 를 폐기하고 `UNKNOWN_FAILURE` 로 흡수(§8.2 마지막 문단, §8.5). presentation 으로 throw 통과 금지.
|
|
|
|
### 3. view-model shape 규칙 (planned)
|
|
|
|
> **Trace**: D3 → hub §4.2, §9.1. view-model 은 render-ready 이며 정규 shape 은 async success 렌더의 입력.
|
|
>
|
|
> - **UNSUPPORTED_IMPL_DECISION**: 일반 shape convention(중첩 DTO flatten, 날짜/숫자 포맷팅, optional 필드 부재 표현) 은 hub 가 원칙만 두고 detail 미규정 — **"raw status/DTO 필드명 비노출 + optional 부재는 throw 대신 안전 default/absent 표기" 원칙만 고정**, trade-off: 구체 포맷 규칙은 sample view-model 이 생길 때 확정.
|
|
|
|
- view-model 은 raw HTTP status·backend error code·DTO 필드명을 그대로 노출하지 않는다(§8.1/§8.2 원칙과 정합: raw body/status 로 UI 분기 금지).
|
|
- **OUT_OF_BRANCH_SCOPE**: sample slice 의 **구체 view-model 필드 목록**은 `FE-OC-024` owner [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] 소유 — 여기서 필드를 열거하지 않고 그 브랜치로 위임.
|
|
|
|
### 4. negative fixture & 강제 (planned)
|
|
|
|
> **Trace**: D4 + D5 → hub §8.5, §15.2, `FE-OC-024`, `FE-OC-008`. 규칙이 실제 동작함을 deliberately failing fixture 로 증명.
|
|
>
|
|
> - **UNSUPPORTED_IMPL_DECISION**: 테스트 파일 경로·명명(`tests/unit/mapper-throws-maps-to-unknown-failure.test.js` 등)과 harness 는 hub 가 test stack(`FE-D022` Vitest+RTL+MSW) 만 고정하고 파일명 미규정 — **Vitest unit 채택**, trade-off: 저장소 생성 시 test-taxonomy 브랜치 convention 에 맞춰 조정.
|
|
|
|
| Fixture | 목적 | 기대 결과 | 소유/위임 |
|
|
|---|---|---|---|
|
|
| mapper 강제 throw(누락 필드/비-Error) | mapper total function 증명 | `UNKNOWN_FAILURE` 반환, raw value·stack 비노출 | 본 브랜치 소유(§8.5 "thrown non-Error object, symbol, or mapper exception → UNKNOWN_FAILURE") |
|
|
| presentation 이 raw DTO schema import | raw DTO 직접 사용 금지 강제 증명 | architecture gate FAIL | fixture case 제공(본 브랜치) + 강제 엔진은 `FE-OC-002` owner 위임 [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] |
|
|
| sample 제거 후 product 빌드 | mapper 시연부가 제거 가능 fixture 임을 증명 | product import 0건, smoke PASS | `FE-OC-024` owner 위임 [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] |
|
|
|
|
## 엣지·실패·의존
|
|
|
|
- **실패·엣지 경로**:
|
|
- mapper 가 **valid-but-empty payload** 수신(검증은 통과했으나 빈 결과) → application model 은 정상 생산하되 async `empty` state 로 표현(렌더 판단은 `FE-OC-011` owner 위임, mapper 는 throw 하지 않음).
|
|
- mapper 가 **예상외 추가 필드** 수신 → 실패 아님. explicit allowlist 투영이므로 추가 필드는 무시(검증계층이 이미 shape 통과시킴).
|
|
- mapper **자체 throw**(누락 필드, `null` 접근, non-Error throw) → raw value 폐기 후 `UNKNOWN_FAILURE`(§8.2). presentation 으로 throw 통과 경로 없음.
|
|
- **nested optional 필드 부재** → application model 은 안전 default/absent 로 표기, throw 금지.
|
|
- **다른 계약 의존**:
|
|
- [[raw/branch-notes/feature-runtime-schema-validation-contract]] `FE-OC-007` 에 의존 — 검증된 payload(stage 6 output)를 mapper 입력으로 consume. 그 검증 output 형태가 바뀌면 mapper 입력 계약 영향.
|
|
- [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] `FE-OC-008` 에 의존 — mapper throw 흡수 대상인 `UNKNOWN_FAILURE` 정규화 shape 을 consume.
|
|
- [[raw/branch-notes/feature-api-client-response-envelope-contract]] `FE-OC-006` 에 의존 — mapper 가 꽂히는 §7.3 처리 순서 파이프라인(stage 1~8)을 소유.
|
|
- [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] `FE-OC-002` 에 의존 — raw DTO 를 presentation 에서 금지하는 import 규칙 소유(본 브랜치는 fixture case 제공).
|
|
- [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] `FE-OC-024` 에 기여 — mapper 시연부를 그 sample slice 안에 둠.
|
|
|
|
## 검증해야 할 주장
|
|
|
|
| Claim | Why uncertain | How to verify | Status |
|
|
|---|---|---|---|
|
|
| mapper 자체 throw 가 실제로 `UNKNOWN_FAILURE` 로 라우팅되고 raw value/stack 을 흘리지 않는다 | 저장소·mapper 코드 없음; §8.2 는 원칙만 규정 | mapper negative fixture(Vitest unit) — 강제 throw → `UNKNOWN_FAILURE` 단언, stack 비노출 assert (hub §8.5 fixture) | `needs-confirmation` |
|
|
| presentation 의 raw DTO schema import 가 architecture gate 를 실제로 FAIL 시킨다 | 정적 강제 엔진 미구현 | forbidden-import fixture(dependency-cruiser/ESLint) — 강제 엔진은 `FE-OC-002` owner, fixture case 는 본 브랜치 | `needs-confirmation` |
|
|
| 2-stage 매핑(adapter validated-model → application view-model)이 중복 할당 없이 테스트 가능하다 | 2-stage 자체는 hub 결정(§7.3 · `FLOW-FE-RESP-007`)이며 남은 불확실성은 hop 추가에 따른 중복 할당·성능뿐 | 저장소 생성 후 mapper 단위 테스트 + 성능/할당 프로파일로 확인 | `planned` |
|
|
| view-model 에 raw status/DTO 필드 leakage 가 없다 | sample view-model 필드 미확정(다른 브랜치 소유) | sample view-model 확정 후 component/unit 테스트로 raw status·backend code 비노출 assert | `planned` |
|
|
|
|
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
|
|
|
|
| 관심사 | 상태 | owner | 심각도 | 근거 |
|
|
|---|---|---|---|---|
|
|
|
|
## 마주친 문제
|
|
|
|
- 없음 — `planned` 설계 단계
|
|
|
|
## 묶음 (이 branch에서 파생된 자료)
|
|
|
|
<!-- GENERATED: flow:start -->
|
|
### 가져온 흐름 단계
|
|
|
|
| Stage Ref | Order | Owner | Input | Action | Output |
|
|
|---|---:|---|---|---|---|
|
|
| `FLOW-FE-RESP-001@1` | 1 | [[raw/branch-notes/feature-api-client-response-envelope-contract]] | HTTP 요청 | transport 완료 대기 | raw Response |
|
|
| `FLOW-FE-RESP-002@1` | 2 | [[raw/branch-notes/feature-api-client-response-envelope-contract]] | raw Response | content-type 기대값 검사 | 본문 판독 가능 Response |
|
|
| `FLOW-FE-RESP-003@1` | 3 | [[raw/branch-notes/feature-api-client-response-envelope-contract]] | 본문 판독 가능 Response | JSON parse | unvalidated JSON |
|
|
| `FLOW-FE-RESP-004@1` | 4 | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | unvalidated JSON | envelope 공유 스키마 검증 | discriminated envelope |
|
|
| `FLOW-FE-RESP-005@1` | 5 | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | discriminated envelope | success/failure 분기 검증 | 분기 확정 envelope |
|
|
| `FLOW-FE-RESP-006@1` | 6 | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | 분기 확정 envelope | payload per-operation 스키마 검증 | 검증된 payload(deep clone) |
|
|
| `FLOW-FE-RESP-008@1` | 8 | [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] | application model 또는 실패 신호 | 정규화된 결과 반환 | application result 또는 normalized failure |
|
|
<!-- GENERATED: flow:end -->
|
|
|
|
<!-- GENERATED: project-contract-imports:start -->
|
|
## 가져온 프로젝트 계약
|
|
|
|
| Ref | Owner | 요약 | Branch 적용 |
|
|
|---|---|---|---|
|
|
| `FE-OC-002@1` | [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] | `domain <- application <- presentation` 의존 방향과 application-owned output port를 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-024@1` | [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] | sample은 contract fixture이며 production feature가 의존하면 안 됨 | import 참조로 적용 |
|
|
<!-- GENERATED: project-contract-imports:end -->
|
|
|
|
### Sub-branches (세부 작업)
|
|
|
|
- 없음 — scaffolding 단계
|
|
|
|
### 오류 기록 (이 branch 작업 중 발생)
|
|
|
|
- 없음 — scaffolding 단계
|
|
|
|
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
|
|
|
|
- 없음 — scaffolding 단계
|
|
|
|
### 강의 (이 작업을 위해 학습한 강의)
|
|
|
|
- 없음 — scaffolding 단계
|
|
|
|
### job-posting tie-ins (이 작업에서 파생된 글감)
|
|
|
|
- 없음 — scaffolding 단계
|
|
|
|
## 관련 일일 노트
|
|
|
|
- 없음 — scaffolding 단계
|
|
|
|
## 완료 후 정리
|
|
|
|
- PR 링크: TODO
|
|
- 리뷰 메모: TODO
|
|
- 머지 결과 / 배포 환경: TODO
|
|
- **wiki 추출 대상**: 없음 — `planned` 단계(구현 증거 생성 후 재평가)
|
|
- **추출하지 않을 항목**: 현재 전 항목 `planned` — 외부 산출물 파생 금지
|