--- title: branch / feature-sample-feature-slice-contract-fixture source_type: branch-note status: raw branch: feature-sample-feature-slice-contract-fixture 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, testing, react, clean-architecture] created: 2026-07-18 target_merge: status_label: in-progress id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-016 kind: project-work-item project: ca-skeleton-frontend-operational-contract work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-016 inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SAMPLE-FIXTURE-001@1] refines: [] overrides: [] depends_on: [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] contract_packet: 1 contract_packet_sha256: ba88989473c8fb9032f91d0f50b2c7d1dfc9cb05ad0a6d0354e81d318256dee9 imports: [FE-GATE-006@1, FE-GATE-007@1, FE-GATE-008@1, FE-OC-002@1, FE-OC-005@1, FE-OC-007@1, FE-OC-011@1, FE-OC-012@1] --- # branch: feature-sample-feature-slice-contract-fixture > Layer: `raw/branch-notes/` — 단일 브랜치의 TODO·결정·진행 기록. 현재는 scaffolding 단계다. ## 부모 (필수) - [[raw/project-notes/ca-skeleton-frontend-operational-contract]] ## 브랜치 계약 패킷 - **생성 시 프로젝트 개정**: `1` - **패킷 스키마**: `contract_packet: 1` - **완료 조건**: full contract slice와 sample removal smoke test가 통과한다 ### 상속한 프로젝트 결정 | Decision Ref | Project Summary | Branch Application | Source | |---|---|---|---| | `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-SAMPLE-FIXTURE-001@1` | sample slice는 제거 가능한 contract fixture이며 product import를 금지한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | ### 브랜치 지역 결정 > 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다. | Decision ID | Decision | Relation | Supporting Claims | Status | |---|---|---|---|---| ### 선언한 예외 | Override ID | Overrides | Reason | Approval | Status | |---|---|---|---|---| ## 목표 이 브랜치는 project-wide contract `FE-OC-024`(sample은 contract fixture이며 production feature가 의존하면 안 됨)를 *되묻지 않고 구현할 수 있는 명세*로 내린다. hub 결정 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D025(sample slice = 제거 가능한 contract fixture, product import 금지)에 근거해, skeleton이 "새 feature도 같은 architecture·API failure language·runtime validation·async UI·server-state·quality gate를 재사용하는가"를 증명하는 **단일 end-to-end reference vertical**(API → schema → mapper → application → presentation, hub `FE-SC-002`)을 정의한다. 그리고 그 vertical이 언제든 통째로 제거돼도 production build/smoke가 깨지지 않음을 gate `FE-GATE-020`(`pnpm test:sample-removal`)으로 강제한다. 이 vertical은 여러 계약을 end-to-end로 **행사(exercise)** 하지만 각 계약의 메커니즘은 소유하지 않고 owner branch에 위임한다. frontend 구현 repository가 아직 식별되지 않았으므로 본 노트의 모든 구현 항목은 `planned` 등급이다. - 이슈: 없음 (repository 미생성) - PR: 없음 ## 범위 ### 포함 범위 - sample contract fixture slice의 **존재·격리·제거 가능성** — `FE-OC-024`, `FE-GATE-020`. - `FE-REG-API`의 2개 sample operation 행 **소유(등록 정의)** — `LIST_SAMPLE_RESOURCES`, `CREATE_SAMPLE_RESOURCE` (hub §5.3). - API → schema → mapper → application → presentation을 관통하는 **end-to-end reference vertical wiring** — `FE-SC-002` (hub §20 measurable completion "full contract slice"). - **sample removal smoke test/gate** — `pnpm test:sample-removal` → `artifacts/tests/sample-removal.xml` (hub §14.3, `FE-GATE-020`). - product/production 코드의 **sample import 금지 invariant** — FE-D025. ### 제외 범위 > 의도적으로 제외. 아래는 다른 owner branch가 소유하며 sample vertical은 이들을 *소비/행사* 만 한다 (CLAUDE.md §15.5 R3, OUT_OF_BRANCH_SCOPE 방지). - shared HTTP client 내부(timeout/abort/retry/envelope parsing, idempotency key 생성) → [[raw/branch-notes/feature-api-client-response-envelope-contract]] `FE-OC-006`·`FE-OC-009` 소유. - runtime schema 작성·검증 엔진 (Zod schema shape/validation) → [[raw/branch-notes/feature-runtime-schema-validation-contract]] `FE-OC-007` 소유. - boundary mapper 메커니즘 (2-stage 배치 = stage 7 `DTO→application model`(`FLOW-FE-RESP-007@1`) 이후 application 이 view-model 로 투영, raw DTO 직접 사용 금지 규칙, mapper negative fixture, mapper 모듈 명명) → [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] 소유 (`FE-OC-007`·`FE-OC-024` 기여 branch). 본 branch는 그 mapper가 산출할 sample view-model *필드 목록* 만 확정한다. - styling 시연(디자인 token·arbitrary value policy·async 시각 primitive)의 내용과 화면 구성 → [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] 소유 (`FE-OC-024` 협업 branch). **본 서브트리의 co-tenant 기여자** — 그 branch가 `src/sample/contract-fixture/` 안에 styling 시연 UI 를 놓는다(mapper branch와 동일 패턴). 본 branch는 그 시연부를 §1 제거 단위 *안에* 수용할 뿐 token 어휘·시각 primitive 를 정의하지 않는다. - error 정규화 taxonomy/matrix → [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] `FE-OC-008` 소유. - route registry schema·guard·param validation·redirect-loop 방지 → [[raw/branch-notes/feature-routing-navigation-guard-contract]] `FE-OC-005` 소유. - `QueryCachePort` 정의·TanStack adapter·invalidation·stale 정책 → [[raw/branch-notes/feature-server-state-caching-contract]] `FE-OC-012` 소유. - async surface state model 정의 → [[raw/branch-notes/feature-async-ui-state-contract]] `FE-OC-011` 소유. - CI gate/fixture/artifact taxonomy → [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] `FE-OC-020` 소유. - web-vitals budget/report (sample list는 측정 fixture일 뿐) → [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] `FE-OC-021` 소유. - 정적 import 금지 규칙 *authoring* (dependency-cruiser/ESLint rule) → [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] `FE-OC-002` 소유. - token 발급/저장/refresh lifecycle → external auth owner / [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] `FE-OC-010` 소유. - domain/business rule, product analytics taxonomy, branding/copy (hub §0.5 out of scope). ## 근거 (필수, 최소 1개+) | Source | 정당화하는 결정 | |---|---| | [[raw/official-docs/react-ui-library-official]] REACT-UI-C1 | D5 — sample presentation을 React 컴포넌트로 구성 (project decision FE-D004 consume) | | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D025 · FE-OC-024 | D1·D6 — sample = 제거 가능 fixture, product import 금지 | | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-SC-002 · §20 | D2 — API→schema→mapper→application→presentation full contract slice reference vertical | | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-GATE-020 · §14 | D3 — sample removal smoke gate (`pnpm test:sample-removal`) | | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5 | D4 — sample API operation/route registry 행(`FE-REG-API`/`FE-REG-ROUTE`) | ## TODO - [ ] `src/sample/contract-fixture/` 서브트리 생성 + 모든 sample 코드를 이 한 디렉터리로 격리 — 등급: `planned` - [ ] `pnpm test:sample-removal` smoke + `artifacts/tests/sample-removal.xml` 산출 — 등급: `planned` - [ ] `FE-REG-API` sample operation 2행(`LIST_SAMPLE_RESOURCES`/`CREATE_SAMPLE_RESOURCE`) + schema 참조 wiring — 등급: `planned` - [ ] API→schema→mapper→application→presentation 관통 vertical 구현 (contributing 계약 owner 완료 후) — 등급: `planned` - [ ] §4.1 sample view-model 필드 목록을 backend payload 계약 확정 시 재검토 (현재 임의 채택) — 등급: `needs-confirmation` - [ ] product code의 sample import 금지 정적 규칙 연동 (architecture-enforcement branch 위임) — 등급: `needs-confirmation` ## 진행 중 메모 - 없음 — repository 미생성, 모든 항목 `planned`. ## 결정 사항 > 아래는 Decision Evidence Map의 prose 미러. 각 근거는 hub 결정 register 또는 archived official-doc. - 2026-07-19: sample slice는 **제거 가능한 contract fixture**이며 production/product 코드가 import하지 못한다 (D1). 이유: skeleton의 계약 준수를 증명할 reference가 필요하되 제품 코드가 그것에 결합되면 안 됨. 검토한 대안: fixture 없이 각 계약을 unit test로만 검증 → 계약 간 wiring 회귀를 못 잡음. 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D025 · FE-OC-024. - 2026-07-19: sample slice는 **API → schema → mapper → application → presentation을 관통하는 단일 end-to-end reference vertical**이다 (D2). 이유: 계약 상호작용을 통합 fixture 1개로 증명. 대안: 통합 vertical 없이 계약별 unit fixture만. 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-SC-002 · §20. - 2026-07-19: sample **removal은 전용 smoke gate로 강제**한다 — `src/sample/` 제거 후 production build/smoke green + product import 0 (D3). 이유: removability를 회귀 방지 gate로. 대안: 수동 리뷰. 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-GATE-020 · §14. - 2026-07-19: sample은 **등록된 registry 행만 사용**한다 — `FE-REG-API`의 2 operation, `FE-REG-ROUTE`의 sample 행; call site raw fetch/route literal 금지 (D4). 이유: fixture가 "좋은 예시"여야 함. 대안: ad-hoc token. 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5. - 2026-07-19: sample presentation은 **React 컴포넌트로 구성**한다 (D5, FE-D004 consume). 이유: React가 project UI 기본. 대안: 다른 framework/native. 근거: [[raw/official-docs/react-ui-library-official]] REACT-UI-C1. - 2026-07-19: sample scope는 **fixture wiring으로 한정** — domain/business rule·product analytics 도입 금지, 어떤 product feature의 의존 대상도 되지 않음 (D6). 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D025 · FE-OC-024. ## 결정-근거 매핑 | Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk | |---|---|---|---|---|---| | D1 | sample slice = 제거 가능한 contract fixture, product/production 코드가 import 금지 (`FE-OC-024`) | skeleton이 "새 feature도 같은 계약을 따르는가"를 증명할 reference vertical이 필요한 동안 유지; 대안(fixture 삭제)은 fixture 없이 동일 gate coverage를 증명할 수 있을 때(hub revisit trigger) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D025, FE-OC-024 | `project-decision` | fixture 제거 시 계약들이 end-to-end로 함께 동작하는지 검증할 통합 표면 상실; revisit trigger 충족 여부 미검증 | | D2 | sample = API→schema→mapper→application→presentation을 관통하는 단일 end-to-end reference vertical; contributing 계약(`FE-OC-005/006/007/008/011/012/020/021`)을 행사하나 메커니즘은 미소유 | 통합 fixture 1개로 계약 상호작용을 증명하는 것이 계약별 unit test만보다 나을 때; 대안은 통합 vertical 없이 unit fixture만(계약 간 wiring 회귀 미포착) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-SC-002, §20 | `project-decision` | contributing 계약 owner branch 미완이면 vertical이 실제로 관통 못 함(dependency). vertical의 **mapper stage 는 본 branch 미소유** — [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] 이 소유하고 본 branch 는 sample view-model 필드 목록만 확정(§구현 가이드 §4) | | D3 | sample removal을 전용 smoke gate로 강제 — `src/sample/` 제거 후 production build/smoke green + 잔존 product import 0 (`FE-GATE-020`) | removability를 자동 회귀 gate로 둘 때; 대안은 수동 코드리뷰(회귀 방지 불가) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-GATE-020, §14 | `project-decision` | import 검출 메커니즘(정적 스캔 vs build 실패)이 hub 미명시 → §구현 가이드 UNSUPPORTED_IMPL_DECISION | | D4 | sample은 등록된 registry 행만 사용 — `FE-REG-API` 2 operation 소유 + `FE-REG-ROUTE` sample 행 consume; call site raw fetch/route literal 금지 | fixture가 registry-first "좋은 예시"여야 할 때(항상); 대안은 ad-hoc token(fixture 목적에 반함) → N/A | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5 | `project-decision` | request/response schema shape(`SampleResource*`) 이름만 있고 필드 미정 → schema branch 위임 | | D5 | sample presentation을 React 컴포넌트로 구성 (project decision FE-D004 consume) | React가 project UI 기본인 동안 유지; 대안(native/custom-element/다른 framework)은 FE-D004 revisit trigger 충족 시 | [[raw/official-docs/react-ui-library-official]] REACT-UI-C1, [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D004 | `official-doc` | 컴포넌트가 async surface 4상태(§9.1)를 완전히 표현해야 하나 그 matrix는 async-ui branch 소유 → 위임 | | D6 | sample scope는 fixture wiring으로 한정 — domain/business rule·product analytics 도입 금지, 어떤 product feature도 sample에 의존 금지 | invariant(분기 없음) → N/A; product feature의 sample import = build/gate 실패로 고정 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D025, FE-OC-024 | `project-decision` | contributing 계약 owner가 계약을 바꾸면 sample vertical 갱신 필요(delegated dependency) | ## 구현 가이드 > 모두 `planned` — frontend 구현 repository가 아직 없다. 경로는 hub §4.6 Planned directory blueprint + §5 registry owner map에서 도출한 grounded anchor이나 repository 생성 시 변경 가능. ### 1. Sample 서브트리 & removability 경계 > **Trace**: D1 + D3 + `FE-OC-024`; hub §4.6 blueprint(`src/sample/contract-fixture/`). > > - **UNSUPPORTED_IMPL_DECISION**: sample import 금지의 *검출 메커니즘*(dependency-graph inbound-edge 규칙 vs ESLint no-restricted-imports vs removal smoke의 build 실패) 은 hub가 gate(`FE-GATE-020`)와 command만 주고 미명시. Trade-off: 정적 dependency 규칙(외부→`src/sample/**` inbound import 0) + removal smoke의 이중 방어를 권고하되, 규칙 *authoring* 은 [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] `FE-OC-002` 로 위임(R3). | 항목 | Planned 값 | 근거 | |---|---|---| | sample 코드 위치 | `src/sample/contract-fixture/` (단일 서브트리) | hub §4.6 | | removability 규칙 | `src/sample/` 외부의 어떤 모듈도 `src/sample/`를 import 금지 | D1 (FE-D025) | | 제거 단위 | 서브트리 1개 삭제 = feature 제거 (product 코드 무변경) | D1·D3 | ### 2. Sample removal smoke test / gate > **Trace**: D3 + `FE-GATE-020` + hub §14.3 `pnpm test:sample-removal`. > > - **UNSUPPORTED_IMPL_DECISION**: 제거 방식(CI에서 ephemeral copy 후 `rm -rf` vs build flag로 dir 제외 vs git worktree)과 "smoke" 판정 assert 목록이 hub 미명시. Trade-off: source 비파괴적인 ephemeral copy + `rm` 을 권고하고, smoke는 최소 "`APP_HOME` shell mount 성공 + `SAMPLE_RESOURCE_LIST` route 부재 + build exit 0" 를 assert. > - **UNSUPPORTED_IMPL_DECISION**: step d의 *ID-residue 검출 메커니즘* — §1의 label은 모듈 *import* 검출만 다루고, 제거 후 남은 **문자열 ID 잔재**(`LIST_SAMPLE_RESOURCES`/`CREATE_SAMPLE_RESOURCE` operationId, `SAMPLE_RESOURCE_LIST` routeId, sample query-key)의 검출 방식은 hub 미명시. Trade-off: 남은 서브트리 전체에 대한 **registry ID token grep(고정 문자열 exact-match, 검사 대상 ID 목록은 `FE-REG-API`/`FE-REG-ROUTE`의 sample owner 행에서 생성)** 을 채택 — dependency-graph 스캔은 문자열 리터럴을 못 잡고 build 실패는 dead 상수를 못 잡기 때문. 검사 범위는 `src/` **와 `tests/`** 둘 다로 둔다. 비용(false positive): grep 은 주석/문서의 우연한 언급도 잡는다. 더 중요한 것은 **false negative** 쪽인데, `src/` 만 스캔하면 delegate branch 가 서브트리 *밖에* 놓은 sample 참조를 놓친다 — 예: [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] 의 mapper negative fixture 는 `tests/unit/` 경로를 planned 로 잡고 있어, sample 제거 후 `pnpm build` 는 green 인데 test suite 가 깨지는 상태를 gate 가 통과시킬 수 있다. `tests/` 포함으로 이 구멍을 막는다. > - 그럼에도 `src/`·`tests/` 밖(설정 파일, CI 워크플로, 문서)의 sample 참조는 본 gate 가 검출하지 않는다. 그런 참조를 만든 **delegate branch 가 자기 몫의 제거 책임을 진다** — 본 branch 는 제거 *단위*(§1 서브트리)와 gate 를 소유하고, 각 co-tenant 기여자([[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]], [[raw/branch-notes/feature-tailwind-design-token-styling-contract]])는 자신이 서브트리 밖에 남긴 참조의 제거를 소유한다. | 단계 | Planned 동작 | 기대 결과 | |---|---|---| | a | `src/sample/` 서브트리를 전용 fixture/CI job에서 제거 | — | | b | `pnpm build` | exit 0 + manifest 존재 | | c | production smoke (app boot, home shell 렌더) | pass, sample route/operation 미참조 | | d | 잔존 sample operationId/routeId/query-key 참조 검출 — `src/` **+ `tests/`** 대상 registry ID token grep(위 UNSUPPORTED label) | 0건 | | artifact | `artifacts/tests/sample-removal.xml` | hub §14.3 | ### 3. Sample API operation & schema wiring > **Trace**: D4 + hub §5.3(`FE-REG-API` 소유 행) + `FE-OC-006`. 아래 2행은 hub §5.3에서 owner=본 branch 로 지정된 grounded registry 행이다. > > - **UNSUPPORTED_IMPL_DECISION**: request/response schema *shape*(`SampleResourceListQuery`/`SampleResourceListPayload`/`CreateSampleResourceCommand`/`SampleResourcePayload`)은 hub가 이름만 준다. Trade-off: fixture 안에 최소 placeholder shape을 정의하되 Zod schema *작성·검증 엔진* 은 [[raw/branch-notes/feature-runtime-schema-validation-contract]] `FE-OC-007` 로 위임. keyed mutation의 idempotency key 생성 계약은 [[raw/branch-notes/feature-api-client-response-envelope-contract]] `FE-OC-009` 소유. | operationId | method | path | auth | timeoutMs | idempotency | requestSchema | responseSchema | |---|---|---|---|---|---|---|---| | `LIST_SAMPLE_RESOURCES` | `GET` | `/api/sample/resources` | `external-session` | `10000` | `safe` | `SampleResourceListQuery` | `SampleResourceListPayload` | | `CREATE_SAMPLE_RESOURCE` | `POST` | `/api/sample/resources` | `external-session` | `10000` | `keyed` | `CreateSampleResourceCommand` | `SampleResourcePayload` | - sample query/command use case는 위 operation을 **shared client + application output port**(`ResourceQueryPort`/`ResourceCommandPort`, hub §4.4)로만 호출한다. shared client 메커니즘은 [[raw/branch-notes/feature-api-client-response-envelope-contract]] `FE-OC-006` 위임. ### 4. Sample vertical wiring (domain → application → presentation → routes) > **Trace**: D2 + D5 + `FE-SC-002`; hub §4.2 component responsibility, §4.6 layer dirs, §9.1 async states. > > - **UNSUPPORTED_IMPL_DECISION**: 컴포넌트/파일 이름(예: `SampleResourceListPage.jsx`)과 domain 모델 유무는 hub 미명시. Trade-off: 이름은 operationId를 미러(`SampleResourceListPage`), domain은 fixture이므로 비워두거나 trivial `SampleResource` value만 — layering 규칙 자체는 [[raw/branch-notes/feature-frontend-clean-architecture-layering-contract]] `FE-OC-002` 소유. | 레이어 | sample이 제공(in-scope) | 위임(다른 owner) | |---|---|---| | domain | (선택) trivial `SampleResource` value 또는 없음 | layering 규칙 → `FE-OC-002` | | mapper (boundary) | sample view-model의 **구체 필드 목록**만 확정 — §4.1 표 (`SampleResourceListPayload`/`SampleResourcePayload` → sample view-model) | mapper 메커니즘 자체(2-stage 배치, raw DTO 직접 사용 금지 규칙, negative fixture, 명명 convention) → [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] (`FE-OC-024` 기여분 · `FE-OC-007`) | | application | sample query/command use case + view-model **투영**(mapper 계약 준수), `QueryCachePort` 소비 | view-model 계약 메커니즘 → 위 mapper 행; port 정의 → `FE-OC-002`/`FE-OC-012` | | presentation | React sample page/component, async 4상태 렌더(§9.1) | async state model → `FE-OC-011` | | adapters | 기존 http/query-cache adapter *재사용* (신규 adapter 없음) | adapter 구현 → owner branch | | routes | `APP_HOME`(sample shell)·`SAMPLE_RESOURCE_LIST`(fixture)의 route element/loading/error surface 내용 | route registry/guard → `FE-OC-005` | - vertical의 **mapper stage는 본 branch가 소유하지 않는다** — 메커니즘(adapter→validated model→application view-model 2-stage 배치, "raw DTO 직접 사용 금지" 규칙, mapper negative fixture)은 [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] 소유이고, 그 branch의 mapper 시연부가 본 sample 서브트리 *안에* 놓인다(양방향 기여). 본 branch는 그 계약을 **소비**하며 그 mapper가 산출할 sample view-model의 *필드 목록* 만 확정한다(해당 branch가 명시적으로 `FE-OC-024` owner 에게 위임한 부분 — §4.1). 따라서 위 표 `application` 행의 "view-model"은 *계약 소유* 가 아니라 *투영 수행* 을 뜻하고, `adapters` 행의 "신규 adapter 없음"은 mapper 모듈이 기존 http adapter 재사용 위에 그 branch 몫으로 추가된다는 뜻이다. - `APP_HOME`(`/`, public) 과 `SAMPLE_RESOURCE_LIST`(`/sample/resources`, integration-defined)는 hub §5.2 등록 행이다. 본 branch는 그 route의 *content* 만 제공하고 registry schema·guard·redirect-loop 방지는 [[raw/branch-notes/feature-routing-navigation-guard-contract]] `FE-OC-005` 위임. #### 4.1 Sample view-model 필드 목록 (본 branch 단독 소유) > **Trace**: D2 + D5 → hub §5.3(payload schema 이름), §9.1(async 4상태); [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] 이 `FE-OC-024` owner 에게 명시 위임한 항목(그 노트 §3 OUT_OF_BRANCH_SCOPE). 그 branch 의 mapper 가 *산출할* 결과물의 shape 을 본 branch 가 확정한다 — mapper 메커니즘(2-stage 배치·total function·negative fixture)은 여전히 그 branch 소유. > > - **UNSUPPORTED_IMPL_DECISION**: 아래 **필드 이름·개수·타입은 hub 미명시**다. hub §5.3 은 payload schema 의 *이름*(`SampleResourceListPayload`/`SampleResourcePayload`)만 주고 필드를 열거하지 않으며, backend API 도 아직 없다. Trade-off: fixture 의 목적은 *도메인 표현*이 아니라 *계약 시연*이므로 **§9.1 4상태를 렌더하는 데 필요한 최소 필드만** 임의 채택했다 — 식별자 1개(list key·mutation 대상), 표시 문자열 1개(도메인 의미 도입 금지 D6), 포맷 완료된 시각 1개(포맷팅이 presentation 이 아닌 view-model 책임임을 시연), 그리고 `success` 와 `empty` 를 presentation 이 재계산 없이 구분할 파생 flag 1개. backend 계약이 확정되면 이 표가 1차 갱신 대상이다. | view-model | 필드 | 타입 | 왜 이 필드인가 (시연 목적) | |---|---|---|---| | `SampleResourceListViewModel` (← `SampleResourceListPayload`) | `items` | `SampleResourceItemViewModel[]` | `success` 상태의 render 입력(§9.1) | | | `isEmpty` | `boolean` | `success` vs `empty` 를 presentation 이 재계산 없이 분기(§9.1 "loading boolean 하나로 병합 금지" 정합). `items.length === 0` 의 파생값 | | `SampleResourceItemViewModel` | `id` | `string` | list key + `CREATE_SAMPLE_RESOURCE` 후 invalidation 대상 식별 | | | `label` | `string` | 표시 전용 문자열. 도메인 의미 없음(D6 — fixture 는 business rule 도입 금지) | | | `updatedAtText` | `string` | **포맷 완료된** 표시 문자열. `Date`/epoch 를 넘기지 않아 "포맷팅은 view-model 책임, presentation 은 render 만" 을 시연 | | `SampleResourceViewModel` (← `SampleResourcePayload`) | = `SampleResourceItemViewModel` 과 동일 shape | — | `CREATE_SAMPLE_RESOURCE` 성공 결과를 목록 항목과 같은 shape 으로 투영 → mutation 후 캐시 갱신 시 두 번째 매핑 규칙 불필요 | - 위 view-model 은 raw HTTP status·backend error code·DTO 필드명을 **그대로 노출하지 않는다**(mapper branch D3 계약 준수). optional 필드 부재는 throw 가 아니라 안전 default/absent 로 표기한다. - `SampleResourceListQuery`/`CreateSampleResourceCommand` 는 view-model 이 아니라 *request* schema 이므로 본 표 밖이다 — 그 shape 은 §3 의 UNSUPPORTED_IMPL_DECISION 이 다룬다. ## 엣지·실패·의존 - **실패·엣지 경로**: - sample removal이 production build를 깬다 → product 코드가 sample에 의존한다는 신호 → `FE-GATE-020` 실패, merge/release 차단 (D3). - sample list read 실패(네트워크/schema/error) → 정규화된 frontend error kind로 표시되어야 하나, 정규화 자체는 error-classification 소유; sample은 그 결과를 **렌더만** 한다. - empty result → §9.1 `empty` 상태(빈 사유 + primary action) 표현 — 상태 모델은 async-ui 소유. - `CREATE_SAMPLE_RESOURCE`(keyed mutation) 재시도 → stable idempotency key + backend replay contract 없으면 replay 금지(hub §8.5) — 규칙은 api-client 소유. - runtime config/boot 실패 시 sample route는 mount되지 않음(hub §4.5 boot 2~4단계 실패 → boot error shell) — boot는 env/config branch 소유. - **다른 계약 의존** (sibling branch consume; 계약 변경 시 sample vertical 갱신 필요): - [[raw/branch-notes/feature-api-client-response-envelope-contract]] — `FE-OC-006`·`FE-OC-009` shared client + retry/timeout/idempotency consume. - [[raw/branch-notes/feature-runtime-schema-validation-contract]] — `FE-OC-007` boundary schema validation consume. - [[raw/branch-notes/feature-boundary-mapper-viewmodel-contract]] — vertical의 mapper stage owner. "raw DTO 직접 사용 금지 → mapper가 view-model 생산" 계약을 consume 하고, 그 branch의 mapper 시연부·negative fixture 를 본 sample 서브트리 안에 수용한다 (`FE-OC-007`·`FE-OC-024` 교집합). 계약 변경 시 sample view-model 필드 목록 갱신 필요. - [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] — **서브트리 co-tenant 기여자**(mapper branch와 동일 패턴). 그 branch가 `src/sample/contract-fixture/` 안에 token·async 시각 primitive 시연 UI 를 놓는다(그 노트 §5 "sample UI fixture — 협업 `FE-OC-024`"). 본 branch는 그 파일들을 §1 **제거 단위 안에** 수용하며, 따라서 §2 removal smoke 는 그 시연부까지 함께 제거된 상태를 검증한다. token 어휘·화면 구성은 그 branch 소유. - [[raw/branch-notes/feature-frontend-storage-registry-contract]] — `FE-OC-013` **storage key 계약을 fixture 로 사용**(그 노트가 본 branch 를 dependency 로 선언한 단방향 관계의 반대편 기록). sample slice 가 storage 를 쓸지 여부는 본 branch 결정이며 현재 **미확정** — §9.1 4상태 시연에 storage 가 필수는 아니므로 기본 입장은 "sample 은 storage 를 쓰지 않음"이고, 쓰기로 하면 namespace/version/classification 규약은 그 branch 소유다. repository 생성 시 확정. - [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] — `FE-OC-010` **runtime session state consume**. §3의 두 sample operation이 모두 `auth`=`external-session` 이므로 request 전 `AuthSessionPort.attach(request)` 와 unauthenticated transition 통지를 그 branch에서 공급받는다. Out of scope의 *token lifecycle* 위임과는 별개 관심사(그쪽은 발급/저장/refresh, 이쪽은 런타임 세션 소비). - [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] — `FE-OC-008` normalized error kind consume. - [[raw/branch-notes/feature-routing-navigation-guard-contract]] — `FE-OC-005` route registry/guard consume. - [[raw/branch-notes/feature-server-state-caching-contract]] — `FE-OC-012` `QueryCachePort`/invalidation consume. - [[raw/branch-notes/feature-async-ui-state-contract]] — `FE-OC-011` async surface state model consume. - [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] — `FE-OC-020` gate/fixture/artifact taxonomy consume. - [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] — `FE-OC-021` (sample list가 lab/field 측정 fixture). - [[raw/branch-notes/feature-accessibility-baseline-contract]] — (advisory) `FE-GATE-009`(accessibility)가 Covered 계약에 `FE-OC-024` 를 포함하므로 axe/keyboard 검사가 사실상 sample route 를 대상으로 돈다. a11y 기준·증거는 그 branch 소유이고 hub §20이 본 branch 에 배정하지 않았다 — 발견성 목적의 포인터일 뿐 in-scope 아님. ## 검증해야 할 주장 | Claim | Why uncertain | How to verify | Status | |---|---|---|---| | sample subtree 제거 후 production build/smoke가 통과한다 | 코드/CI 없음; product import 부재가 미검증 | `pnpm test:sample-removal` (§14.3) → `artifacts/tests/sample-removal.xml` exit 0 (`FE-GATE-020`) | `needs-confirmation` | | sample vertical이 API→schema→mapper→application→presentation을 실제로 관통한다 | contributing 계약 owner branch 미완; wiring 미구현 | integration test(MSW) + e2e sample critical read/write (`FE-GATE-007`/`FE-GATE-008`) | `needs-confirmation` | | 어떤 product feature도 sample을 import하지 않는다 | 정적 검출 메커니즘 미정(UNSUPPORTED_IMPL) | dependency-graph 규칙(architecture-enforcement 위임) + removal smoke | `needs-confirmation` | | sample list가 async surface 4상태(loading/success/empty/terminal-error)를 표현한다 | async 상태 matrix는 async-ui branch 소유, 미구현 | component state matrix test (`FE-GATE-006`, §9.1) | `needs-confirmation` | | §4.1의 sample view-model 필드 목록이 §9.1 4상태 렌더에 충분하다 | 필드가 hub 미명시 상태에서 임의 채택됨(UNSUPPORTED_IMPL_DECISION); backend payload 계약 미존재 | mapper 단위 테스트(payload→view-model 투영) + component state matrix test 로 4상태가 이 필드만으로 렌더되는지 확인; backend 계약 확정 시 표 갱신 | `needs-confirmation` | | React 컴포넌트 구성이 sample presentation에 충분하다 | REACT-UI-C1은 컴포넌트 모델 *존재* 만 증명, 프로젝트 적용 보장 아님 | component test로 sample page 렌더 확인 | `needs-confirmation` | ## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때) | 관심사 | 상태 | owner | 심각도 | 근거 | |---|---|---|---|---| ## 마주친 문제 - 없음 — repository 미생성 단계. ## 묶음 (이 branch에서 파생된 자료) ## 가져온 프로젝트 계약 | Ref | Owner | 요약 | Branch 적용 | |---|---|---|---| | `FE-GATE-006@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | component 레벨이 실패하면 merge 를 MUST 차단 | import 참조로 적용 | | `FE-GATE-007@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | MSW 기반 integration 매트릭스가 미충족이면 merge 를 MUST 차단 | import 참조로 적용 | | `FE-GATE-008@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | critical e2e 시나리오가 실패하면 merge·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-005@1` | [[raw/branch-notes/feature-routing-navigation-guard-contract]] | route ID/path/params/access/loading/error owner는 route registry 하나여야 함 | import 참조로 적용 | | `FE-OC-007@1` | [[raw/branch-notes/feature-runtime-schema-validation-contract]] | JSON envelope와 payload는 boundary에서 runtime schema를 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 참조로 적용 | ### Sub-branches (세부 작업) - 없음 — scaffolding 단계 ### 오류 기록 (이 branch 작업 중 발생) - 없음 — scaffolding 단계 ### 면접 준비 (이 작업에서 나올 수 있는 면접 질문) - 없음 — scaffolding 단계 ### 강의 (이 작업을 위해 학습한 강의) - 없음 — scaffolding 단계 ### job-posting tie-ins (이 작업에서 파생된 글감) - 없음 — scaffolding 단계 ## 관련 일일 노트 - 없음 — scaffolding 단계 ## 완료 후 정리 - PR 링크: 없음 (repository 미생성) - 리뷰 메모: 없음 - 머지 결과 / 배포 환경: 없음 — 모든 항목 `planned` - **wiki 추출 대상**: 없음 — verified 항목 없음 - **추출하지 않을 항목**: 전체 (`planned` / `needs-confirmation`)