Files
clean-architecture-frontend…/docs/architecture/frontend-platform-capability-review.md
T
2026-08-01 19:39:59 +09:00

533 lines
36 KiB
Markdown

# 프론트엔드 플랫폼 역량 재검토
> **정본 안내 (non-authoritative for runtime capability decisions)**
>
> Runtime Config/boot, Fetch HTTP client, Router, Query/Mutation, realtime 공통 경계, Web Worker,
> Service Worker, offline command와 Background Sync의 구현 결정은
> [프론트엔드 런타임 Capability 저장소 정합형 구현 결정 폐쇄 상세 설계](./2026-07-30-frontend-runtime-capability-repository-aligned-implementation-closed-deep-design.md)가 정본이다.
> 본 문서의 해당 서술이 정본과 충돌하면 정본을 따른다.
## 1. 문서 목적
이 문서는 도메인 기능과 실제 운영 환경의 배포 증적을 제외하고, 이 저장소가 새
프론트엔드 제품의 출발점으로 제공해야 하는 공통 역량을 다시 평가한다. 평가
기준은 다음과 같다.
- 코드나 설정 파일이 존재하는지만 보지 않는다.
- 부트스트랩부터 화면까지 실제 호출 경로가 연결되는지 확인한다.
- 선언한 레지스트리와 정책이 런타임 및 CI에서 집행되는지 확인한다.
- 기본 번들에 포함할 역량과 필요할 때 설치할 확장 역량을 구분한다.
- 특정 벤더를 채택하더라도 제품 코드가 벤더 API에 직접 결합되지 않는지 확인한다.
최초 검토 기준은 `develop``cb195f8`이며, RP-01~RP-12 구현 결과를 이 문서에
누적 반영했다. 이후 구현으로 경로나 세부 내용이 달라질 수 있으므로, 각 항목은
문서의 경로뿐 아니라 해당 테스트와 아키텍처 게이트로 계속 검증해야 한다.
## 2. 결론
현재 저장소는 다음 기반이 강하다.
- 런타임 설정과 릴리스 매니페스트 검증
- 도메인, 애플리케이션, 프레젠테이션, outbound adapter의 의존 방향
- 공통 HTTP 실패 형태와 제한된 retry 정책
- 앱 셸, 반응형 내비게이션, 테마, 비동기 상태 표면
- Vitest, Testing Library, MSW, Playwright, axe를 이용한 테스트 계층
- CI 게이트 taxonomy와 호환성·보안·성능·릴리스 계약 문서
RP-01~RP-12에서 TypeScript 도구 안전망, application runtime 주입,
query/mutation inbound adapter, HTTP 실행 계약과 executable route/release
recovery 계약, 제거 가능한 reference 수직 슬라이스, form/page, design system과
i18n 실행 경계, diagnostics/telemetry production wiring, registry/test 증거,
local 공급망 검증과 제거 가능한 optional adapter recipe가 구현됐다. 저장소 내부
P0/P1 acceptance와 P2 recipe 기본값은 `LOCAL_TEMPLATE_READY`다. 다만 실제 제품
도메인과 hosting, IdP, vulnerability/signing provider, analytics consent/provider,
지원 browser/접근성·field 증거는 프로젝트가 선택하고 검증해야 한다.
따라서 더 정확한 표현은 다음과 같다.
> application API, 서버 상태, 폼, 라우팅, 페이지, 디자인 시스템, 테스트와
> local 공급망 증적의 표준 수직 경로와 opt-in adapter recipe는 갖춰졌다.
> 실제 capability 설치와 hosting·IdP·취약점/서명/운영 provider는 프로젝트
> 통합 범위이며, 없는 외부 증거를 완료로 표시하지 않는다.
## 3. 판정 기준
| 판정 | 의미 |
| --- | --- |
| 준비됨 | 구현, 실제 조립, 자동 검증이 모두 존재한다. |
| 부분 준비 | 핵심 구현은 있으나 실제 호출 경로, 정책 집행, 예제가 불완전하다. |
| 미제공 | 새 기능을 만들 때 팀이 직접 선택·설계해야 한다. |
| 프로젝트 선택 | 기본 번들에 강제하면 비용이 더 크며, 경계와 recipe만 제공한다. |
## 4. 역량 매트릭스
| 영역 | 현재 판정 | 근거 | 필요한 다음 상태 |
| --- | --- | --- | --- |
| 부트·런타임 설정 | 준비됨 | `src/bootstrap`, runtime schema, release 검사 | 현 상태 유지, TS 전환 시 동일 게이트 유지 |
| 계층 의존 방향 | 준비됨 | dependency-cruiser + TS-aware static graph, unresolved/parse/layer/cycle negative fixture | 새 rule shape와 source extension도 같은 fail-closed graph에 추가 |
| application facade | 준비됨 | module-augmented feature input registry, typed output catalog, provider, production composition test | feature별 input contribution과 제거 gate 유지 |
| REST HTTP client | 준비됨 + hardening delta | reference vertical의 path/search/body projection, attempt timeout/retry, abort/cleanup은 `COMPOSED` | auth fail-close/final invariant, total deadline, bounded decoder, status/media/CSRF/conditional/pagination은 `DESIGNED_NOT_IMPLEMENTED` |
| Browser RPC 공통 계약/runtime | 준비됨/미조립 | V3 operation/profile registry, typed application port, bounded unary/server-stream lifecycle와 unavailable adapter는 `AVAILABLE_NOT_COMPOSED` | actual descriptor/generated client, protocol transport, proxy/provider/browser conformance |
| GraphQL·Connect·gRPC-Web·Protobuf REST Gateway 제품 adapter | 설계됨/제품 미선택 | wire dependency/generated source/provider는 없고 VD-26/VD-27/VD-29/VD-30 production contract 승인 | 제품 operation/provider 선택 전 `NOT_SELECTED`; 선택 branch wire adapter 구현 뒤에만 `AVAILABLE_NOT_COMPOSED` |
| retry | 준비됨 + hardening delta | REST 단일 소유, runtime max attempts, Query retry off, logical terminal diagnostics | total elapsed/sleep, 401 single-flight와 protocol별 exact retry/effect certainty 구현 |
| 오류 모델 | 준비됨 | registry-derived `AppFailure`, 공통 `Result`, HTTP normalization과 invalid-kind fixture | 새 failure kind는 registry·copy·telemetry 계약과 함께 추가 |
| Schema·Mapper | reference 준비됨 + governance delta | reference Zod → mapper → domain/application path는 `COMPOSED` | typed codec/mapper proof, semantic fingerprint/provenance, bounded decode와 generated drift gate는 `DESIGNED_NOT_IMPLEMENTED` |
| 인증 연동 | 준비됨/프로젝트 선택 | opaque auth owner와 demo seam 존재 | 인증 방식별 recipe; 기본 token 저장소는 추가하지 않음 |
| 서버 상태 | 기본 경로 준비됨 + lifecycle delta | reference query/mutation, cancellation, stale, basic optimistic/conflict/rollback은 `COMPOSED` | strict bound query policy/key, result/page ceiling, mutation concurrency/CAS rollback은 `DESIGNED_NOT_IMPLEMENTED` |
| 클라이언트 상태 | 준비됨/프로젝트 선택 | local/URL/query/context 소유권, session external store, typed workflow recipe | 실제 cross-page workflow가 생길 때 하나의 store 선택 |
| 범용 global store | 프로젝트 선택 | runtime library 없음, typed facade/fake와 server-state duplication gate | VD-10 조건에 따라 Zustand/Redux Toolkit/state machine 중 하나 선택 |
| 라우팅 | 준비됨 | Data Router, typed runtime map, codec, 분리된 route-input provider, metadata consumer, bounded chunk recovery | 새 lazy route도 router 역참조 없이 contribution으로 추가 |
| 앱 셸·반응형 | 준비됨 | native modal Drawer, compact/desktop layout, Escape/link dismiss/focus restore, pseudo reflow와 RTL direction | compact browser matrix 유지 |
| 페이지 템플릿 | 준비됨 | Standard/Collection/Detail/Form/Status와 public design-system entry | feature별 slot 조합 유지 |
| 디자인 토큰 | 준비됨 | primitive/semantic/component CSS, 48-token 자동 계약, dark/forced-colors/reduced-motion | 제품 brand token은 외부 프로젝트에서 확장 |
| 공통 UI | 준비됨 | action/form/feedback/overlay/navigation primitive와 pattern, compatibility export | public story와 visual state matrix 유지 |
| 아이콘 | 준비됨 | Lucide static vendor facade와 semantic icon/IconButton 접근성 계약 | 의미 icon 추가 시 bundle/접근성 기준 적용 |
| 폼 | 준비됨 | Zod 기반 local facade, error summary/focus, 422 allowlist, dirty/pending/conflict 정책 | 복합 form 요구가 생기면 VD-04 조건으로 vendor adapter 평가 |
| 국제화 | 준비됨 | 137-key typed catalog, locale provider, Intl formatter, safe fallback/alias, pseudo·RTL gate | 실제 locale·번역 승인은 프로젝트에서 연결 |
| logging/diagnostics | 준비됨 | 별도 `DiagnosticsPort`, 8-event registry, allowlist, bounded/no-op adapter와 production producer | 실제 프로젝트의 remote sink는 port 뒤에서 선택 |
| telemetry | 준비됨/프로젝트 선택 | 5-event registry, bounded queue, redaction/value policy, boot·HTTP·render·release·drop producer | analytics/RUM/error vendor와 consent는 프로젝트에서 선택 |
| 비동기 상태 불변식 | 준비됨 | 배타적 typed overlay, stale latch, 실제 retry/conflict action | reference 화면에서 전체 상태 전시 |
| 단위·통합·E2E | 준비됨 | source/test strict typecheck, shared MSW 19개 scenario, 실제 bootstrap, built-dist 3엔진·compact E2E | 제품별 critical flow를 같은 catalog/gate에 추가 |
| UI 회귀 검증 | 준비됨 | dev-only Storybook interaction/axe와 pinned Chromium visual baseline 4종 | cloud review와 다중 OS/device는 프로젝트 선택 |
| 샘플 제거 | 준비됨 | feature/catalog/test 제거 후 type/architecture/registry/test/home/build 9단계 검증 | 새 contribution도 같은 제거 gate에 포함 |
| registry·compatibility 집행 | 준비됨 | 10개 registry type/reference/consumer/orphan, 승인 digest와 actual semantic diff, breaking evidence | public 계약 변경 시 baseline review 유지 |
| 공급망 검사 | 준비됨/프로젝트 선택 | 561개 transitive inventory/integrity/license, actual diff, CycloneDX, local provenance, secret/reproducible build gate | 실제 vulnerability scanner와 signed attestation 없이는 promotion `FAIL_UNVERIFIED` |
| realtime delivery | 참조 런타임 준비됨/프로젝트 미선택 | RT-01~04 공통 event authority, fetch-stream SSE, closed WebSocket, bounded Polling, Web Push window/worker와 handoff/reconnect 조정자는 `AVAILABLE_NOT_COMPOSED`; backend/provider/browser evidence는 없음 | [Realtime events, Web Push, and bounded polling](./realtime-events-web-push-and-bounded-polling.md)의 RT-05 제품 조립·운영 증거를 capability별로 통과 |
| offline·file·browser data | 부분 준비/프로젝트 선택 | opt-in recipe와 gate; capability별 구현·조합·미구현·미선택·platform 제한 상태가 서로 다름 | [Browser data capability completion ledger](./browser-data-capability-completion-ledger.md)의 상태와 work package를 통과한 capability만 설치 |
REST/GraphQL/Connect/gRPC-Web/REST Gateway, Schema/Mapper와 Server State의 상세 current/target 판정은
[API contract, Schema, Mapper와 Server State](./api-contract-schema-mapper-and-server-state.md)를
따른다. Browser Protobuf 축의 선택 기준은
[Protobuf browser transport와 REST Gateway](./protobuf-browser-transport-and-rest-gateway.md)를
따른다. 현재 REST reference 경로의 `COMPOSED` 판정을 GraphQL/Connect/gRPC-Web/
Gateway 또는 REST v2 hardening 완료 증거로 재사용하지 않는다.
## 5. 우선순위별 발견 사항
### 5.1 P0: 기능 개발을 막는 항목
#### RP-02에서 application 런타임 우회 해결
`src/bootstrap/composition-root.ts`가 만든 typed application input API는
production `ApplicationProvider`에 주입된다. raw auth, storage, telemetry와
release port는 closure 안에 남고 UI는 session, preference, diagnostics와 runtime
query만 사용한다.
목표 상태:
- `Application`은 UI가 호출할 query/command use case를 제공한다.
- `ApplicationProvider`는 이 API만 React tree에 제공한다.
- 페이지는 HTTP, storage, auth SDK, telemetry sink를 직접 호출하지 않는다.
- bootstrap만 concrete outbound adapter를 알고 조합한다.
- 실제 bootstrap부터 reference page까지 연결한 통합 테스트가 있다.
#### RP-03에서 표준 서버 상태 bridge 구현
`src/presentation/adapters/query` 한 경계만 `@tanstack/**`를 import한다.
`useApplicationQuery``useApplicationMutation`은 application result를 React
lifecycle에 연결하며 cancellation, stale failure, duplicate submit, optimistic
rollback, conflict resolution과 invalidation을 검증한다. 다른 presentation
경로의 직접 TanStack import는 negative fixture가 거절한다.
목표 상태:
- canonical target인 `src/adapters/inbound/react/platform/query`에 벤더 연동을
한정한다. 마이그레이션 중에는 기존 `presentation`을 같은 inbound 경계로
취급하되 새 대체 경로를 만들지 않는다.
- `useApplicationQuery`, `useApplicationMutation` 또는 같은 역할의 typed
controller hook을 제공한다.
- HTTP retry와 query retry 중 한 계층만 재시도 책임을 갖는다.
- loading, empty, refreshing, stale, offline, error, conflict, optimistic rollback을
reference feature에서 보여 준다.
#### RP-01에서 TypeScript 검사 도구 안전망 구현
초기 source는 JS/JSX와 `strict + allowJs + checkJs`를 사용했다. RP-01 후속
migration으로 현재 product source, tests, Node scripts와 지원 config는 모두
TS/TSX이며 `allowJs`는 꺼져 있다.
- ESLint의 계층·보안 규칙은 product와 negative fixture의 TS/TSX를 함께 검사한다.
- registry scanner와 governance/baseline은 `.ts``.tsx` 경로를 사용한다.
- app, Node scripts/config, tests는 분리된 strict project로 모두 검사된다.
- architecture gate는 fixture를 포함한 실행 source의 `.js/.jsx/.mjs/.cjs`
재유입을 거절한다.
전환은 tooling glob과 CI를 먼저 고친 뒤 계약 계층부터 화면·테스트·운영 script
순서로 완료했다. Node 24가 운영 `.ts` script를 직접 실행하고 NodeNext strict
typecheck가 같은 경로를 검증한다.
#### RP-03에서 HTTP 선언과 실행의 차이 해결
HTTP request builder는 path segment escaping, canonical optional/array search,
Zod default/trim 결과의 실제 query/body 전송을 담당한다. runtime timeout과
0/1/N max retry가 client factory에 주입되고 caller abort와 timeout을 다른 typed
failure로 투영한다. validation 조기 반환은 fetch/timer 0회이며 success, schema
failure, abort, timeout과 exhausted retry는 scheduler/listener cleanup을
검증한다. HTTP 사건의 semantic telemetry 연결은 RP-09 범위다.
client를 거대한 범용 함수로 계속 확장하지 말고 transport, request builder, auth,
timeout, retry, decoder, mapper 책임을 분리해야 한다. application에는 범용 HTTP
메서드보다 feature가 요구하는 gateway interface를 노출한다.
#### RP-04에서 route registry를 실행 계약으로 전환
platform route 계약과 `src/features/installed-feature-contracts.ts`의 직렬화
가능한 contribution을 기준으로
`src/presentation/routes/app-router.tsx`가 Data Router route object와
navigation을 생성한다. `route-runtime.tsx`는 lazy component의 실행 map만
소유하며 contract/runtime 누락과 orphan은 TypeScript negative fixture와 registry
gate가 모두 거절한다.
현재 보장:
- serializable contract와 executable runtime map을 분리한다.
- `satisfies Record<RouteId, RouteRuntime>`로 양방향 완전성을 검사한다.
- params/search는 Zod codec으로 경계에서 parse하고 URL builder도 같은 codec을
사용한다.
- loading/error/chunk/access/title/navigation metadata를 실제 route object에
연결한다.
- route change 시 boundary reset, title, focus와 scroll을 검증한다.
- Vite manifest의 실제 dynamic entry와 route chunk ID를 release manifest에
연결하고, no-store manifest 재조회와 build/release 쌍별 1회 reload를
production application input까지 연결한다.
#### RP-05에서 제거 가능한 reference feature 구현
`src/features/reference-feature`가 domain, application input, outbound gateway,
DTO/schema, mapper, route/API/query contract, query/mutation controller와 page를
한 소유 경계에 둔다. production composition은 generic feature input catalog를
통해 이 input을 주입하며 UI는 HTTP나 output port를 직접 보지 않는다.
`test:sample-removal`은 임시 복제본에서 feature source/tests를 삭제하고 installed
contract/runtime/adapter catalog를 빈 목록으로 재생성한다. 그 뒤 typecheck,
architecture, registry, unit/integration, coverage, source evidence, home smoke,
build와 fixture ID 잔여 0개를 검사한다. feature-owned coverage/evidence policy도
함께 제거되며 generic registry 성공 경로는 공통 unit test가 유지한다. 설치
모드에서는 MSW를 사용한 bootstrap → router → application → HTTP → schema →
mapper → query cache → page 수직 테스트가 실행된다.
#### 비동기·복구 상태의 불변식이 닫혀 있지 않다
공통 async model과 gallery가 있지만 선언 가능한 상태 조합 중 일부는 사용자
행동과 모순될 수 있다. stale data가 있는 degraded 상태와 refreshing, mutation
pending과 conflict, retry button과 실제 handler 존재 여부를 typed state로
닫아야 한다. 상태를 boolean 여러 개로 조합하지 않고 다음과 같은 discriminated
state와 action capability로 표현한다.
```text
initial-loading
ready
refreshing-with-data
empty
degraded-with-data
terminal-error
mutation-pending
mutation-conflict
```
RP-04에서 lazy import failure는 `ChunkRecoveryBoundary` → application recovery
input → `ReleaseInfoPort.refresh()`의 no-store manifest 조회 → build/release 쌍
guard → browser navigation adapter의 1회 reload로 연결됐다. 일반 render
failure는 이 경로에서 제외되고, 반복 실패·offline·malformed manifest·storage
실패는 지원 표면으로 fail-closed된다.
#### RP-09에서 diagnostics/telemetry 실행 깊이 보강
`DiagnosticsPort``TelemetryPort`를 분리하고 boot, HTTP logical outcome,
render, cache, storage, route, release mismatch와 delivery drop을 production
producer에 연결했다. HTTP retry는 attempt별 terminal event를 발행하지 않고
logical execution 종료 시 한 번만 bounded outcome을 남긴다. allowlist와
value policy가 raw URL/query/body/storage value/error object를 거절하고 queue와
sink failure는 nonrecursive drop evidence로 제한된다.
registry/compatibility 검사는 다음까지 확장한다.
- ID와 enum/type의 양방향 완전성
- referenced schema/message/token의 존재
- 실행 코드에서 소비되지 않는 orphan 항목
- 기준 commit과 현재 commit 사이의 실제 contract diff
- breaking change의 version/migration/rollback metadata
공급망 검사는 단순 문자열 secret 탐지에 머물지 않고 transitive dependency,
known vulnerability, license policy, SBOM/provenance를 pinned tool로 검사해야 한다.
도구 장애와 취약점 발견을 구분하고, 예외에는 owner·사유·만료일을 요구한다.
### 5.2 P1: 공통 플랫폼 기본 제공 항목
- RP-06에서 완료한 schema 기반 form facade와 field/error/pending/dirty/422 정책 유지
- RP-06에서 완료한 standard, collection, detail, form, status page template의 public entry 정리
- 접근 가능한 drawer, menu, popover, select 같은 interaction primitive
- token → primitive → pattern → template로 이어지는 디자인 시스템
- Lucide를 감싼 local icon registry와 `IconButton`
- typed message key, locale provider, formatter, pseudo-locale/RTL smoke
- RP-09에서 완료한 redacted structured diagnostics와 telemetry wiring 유지
- Storybook 또는 동급 isolated UI workshop
- Playwright visual baseline, shared MSW scenarios, built-dist E2E
- React Hooks, JSX accessibility, TanStack Query 관련 lint
- source와 tests를 모두 포함하는 TypeScript project references
- registry/compatibility의 실제 diff와 orphan reference 검사
- transitive vulnerability, license, SBOM/provenance 공급망 gate
#### RP-08에서 국제화 실행 경계 구현
`src/presentation/i18n`은 shell, route, async/form error, page template와
design-system 기본 copy의 canonical 경계다. `MessageKey`와 key별
`MessageParameters`가 잘못된 key/보간을 compile time에 막고, runtime
`resolveMessage`는 unknown locale/key와 누락 보간에서 raw 값 대신 안전한
fallback을 반환한다. application mapper는 locale-formatted date를 반환하지
않고 timestamp를 유지하며 presentation formatter가 `UTC` 또는 명시 timezone을
적용한다.
`LocaleProvider``ko-KR`, `en-US`, `en-XA`, `ar-EG` smoke set과 document
`lang/dir`을 동기화한다. `en-XA`는 긴 문구 reflow, `ar-EG`는 logical CSS,
Drawer, Tabs arrow와 pagination 방향 icon을 검증하기 위한 개발 locale이다.
실제 아랍어 번역 완료를 뜻하지 않는다. `check:i18n`과 negative fixture는 common
UI literal, backend raw message render와 raw HTML interpolation을 거절한다.
새 key rename은 canonical type에는 넣지 않고 runtime alias/migration window로
호환한다.
### 5.3 P2: 경계와 recipe를 제공할 선택 항목
다음 기능을 모든 앱의 초기 번들에 설치할 필요는 없다. 대신 port 또는 local
vendor facade, 선택 조건, 실패 정책, 테스트 fixture를 문서로 제공한다.
| capability | 대표 기술 | 기본 제공할 경계 | 실제 설치 조건 |
| --- | --- | --- | --- |
| foreground realtime | SSE, WebSocket | closed stream/event registry, resume/gap/snapshot, bounded queue와 lifecycle | 측정된 one-way event 또는 duplex interaction 요구와 backend replay/auth owner가 있을 때 |
| background notification | Web Push, persistent notification | permission, subscription register/revoke, Service Worker push/click와 safe route | user-visible background notification과 provider/privacy owner가 승인됐을 때 |
| bounded polling | conditional HTTP query | visible-only single-flight lease, request/time budget와 terminal stop | relaxed freshness 또는 의미가 보존되는 stream fallback이면 충분할 때 |
| structured offline storage | IndexedDB | feature repository, codec/schema 분리, resumable migration, revision/idempotency, blocked/quota recovery | offline record/command가 제품 요구일 때 |
| large local binary | OPFS + IndexedDB journal | immutable chunk, generation/integrity, crash reconciliation, bounded GC | 실제 large local object와 retention owner가 있을 때 |
| public HTTP representation cache | Cache Storage, Service Worker | auth/private 배제, exact match, candidate integrity, update/rollback | install/offline shell 또는 승인된 public cache가 필요할 때 |
| file selection/transfer/delivery | native input, File/Blob, picker, multipart, stream save | opaque file ref, bounded inspection, upload session, handoff/save 구분, object-URL lease | backend 재검증을 포함한 file workflow가 있을 때 |
| generated API | OpenAPI, GraphQL, Connect/gRPC-Web, Protobuf | generated client를 semantic gateway 뒤에 감싸고 transport/gateway 축을 분리하는 규칙 | 서버 계약 형식과 selected operation/provider가 확정됐을 때 |
| feature flag | local/remote flag provider | typed flag key, default, stale behavior | staged rollout가 필요할 때 |
| worker | Web Worker | request/result/cancel protocol | UI thread를 막는 CPU 작업이 있을 때 |
| multi-tab | BroadcastChannel | event versioning, source ID, conflict policy | 탭 간 동기화가 필요할 때 |
| browser capability | clipboard, notification, media | permission/result port | 해당 UX가 있을 때 |
| client workflow | Zustand, Redux Toolkit, state machine | state ownership decision과 local facade | cross-feature workflow가 실제로 생길 때 |
| large data UI | virtualization, data grid | owned component facade | 데이터 규모가 측정 기준을 넘을 때 |
| analytics/error sink | vendor SDK, OpenTelemetry | redaction, consent, sampling adapter | 운영 provider와 정책이 정해졌을 때 |
catalog의 12개 항목 수는 유지하며 OPFS는 offline recipe의 large-object
sub-capability, Cache Storage는 Service Worker/PWA recipe의 독립 cache policy
sub-capability로 깊이를 보강했다. 현재 상태는 모두
제품 선택 기준으로 `RECIPE_AVAILABLE / NOT_INSTALLED`다. 다만 File/Blob/picker/
download, IndexedDB/OPFS/StorageManager, Cache Storage에는 실제 native API를
호출하는 정책 주입형 reference runtime이 `AVAILABLE_NOT_COMPOSED` 상태로 있으며,
bootstrap과 installed feature에서는 import하지 않는다.
`config/recipes/frontend-capability-recipes.json`이 선택/금지 조건, failure,
cleanup, security/privacy, bundle budget, fallback과 제거 절차의 SSOT이며,
`recipes/frontend-capabilities`에 production-excluded TypeScript port와
fake/unavailable adapter가 있다. file/IndexedDB/OPFS/Cache의 상세한 기술 소유권,
journal, migration, cache activation, test와 운영 복구는
`docs/architecture/browser-file-and-origin-storage.md`와 VD-11을 따른다.
presigned capability, multipart/resume, bounded streaming과 Image CDN은
`docs/architecture/presigned-transfer-and-image-cdn.md`와 VD-12를 따르며, 공통
도입 절차는 `docs/architecture/optional-adapter-recipes.md`를 따른다.
SSE, WebSocket, Web Push와 bounded polling은
`docs/architecture/realtime-events-web-push-and-bounded-polling.md`와 VD-28을
따른다. copyable recipe/fake와 별개로 RT-01~04 reusable source와 deterministic
gate는 `AVAILABLE_NOT_COMPOSED`다. foreground transport, 제품 event registry와
Web Push provider selection은 여전히 `NOT_SELECTED`이며 production bootstrap과
worker에는 조립하지 않는다.
이 capability들을 한꺼번에 "준비됨"으로 표시하지 않는다.
[Browser data capability completion ledger](./browser-data-capability-completion-ledger.md)가
`COMPOSED`, `AVAILABLE_NOT_COMPOSED`, `DESIGNED_NOT_IMPLEMENTED`,
`NOT_SELECTED`, `PLATFORM_LIMITED`를 구분하는 현재 상태의 기준이다. 특히
session/account Query lifecycle, Range resumable download, cross-store quota
orchestration, top-level transfer composition과 Image descriptor HTTP provider는
설계가 승인됐어도 runtime 구현 전에는 `DESIGNED_NOT_IMPLEMENTED`다. Query
persistence, Service Worker offline fetch와 app-managed background
upload/download는 제품이 별도로 선택하기 전에는 `NOT_SELECTED`, 그 cross-browser
guarantee는 `PLATFORM_LIMITED` 상태를 유지한다.
서버의 Redis, MongoDB, PostgreSQL, MinIO를 브라우저가 직접 연결하는 구조는 기본
frontend adapter catalog에 넣지 않는다. 브라우저는 권한 있는 backend API/BFF를
통해 이 자원에 접근해야 한다. 프론트에서 대응되는 변화 지점은 데이터베이스
vendor가 아니라 HTTP/GraphQL/Connect/gRPC-Web/REST Gateway, realtime, file
transfer, cache, storage, worker, browser capability 같은 프로토콜·런타임
capability다.
### 성능 최적화는 모두 adapter 문제인가
아니다. 먼저 측정하고 병목의 소유 계층에 맞는 수단을 적용한다.
| 문제 | 기본 제공할 수단 | adapter/facade가 필요한 경우 |
| --- | --- | --- |
| 초기 JS가 큼 | route/feature lazy loading, bundle budget, dependency inventory | remote module이나 별도 delivery 전략이 있을 때 |
| 중복 네트워크 | TanStack deduplication/cache, abort, bounded retry | offline cache나 generated client를 교체할 때 |
| 느린 화면 전환 | prefetch policy, stable shell, cached-data surface | route별 prefetch provider가 필요할 때 |
| 긴 main-thread task | profiler 기준으로 계산 분리 | Web Worker message adapter |
| 대량 목록 | pagination과 server filter를 우선 | virtualizer/data-grid facade |
| 이미지 전송량 | width/height, lazy loading, responsive source 규칙 | Image CDN URL builder adapter |
| 재방문/offline | HTTP cache contract | Service Worker/IndexedDB adapter |
| 불필요한 render | 상태 소유권 축소와 component boundary | 보통 adapter가 아니며 측정 후 memoization |
기본 skeleton은 bundle budget, lazy route, query cancellation/cache, responsive
image 규칙, stable layout, lab performance test를 제공한다. Web Worker,
virtualization, Image CDN, Service Worker는 실제 병목과 제품 요구가 확인될 때
설치한다. 라이브러리를 미리 많이 넣는 것은 최적화가 아니라 초기 번들·공급망
표면을 늘리는 일이 될 수 있다.
## 6. 질문별 직접 답변
### 프론트도 inbound/outbound로 나누는가
나눈다. 현재 구조에서는 `presentation`이 사실상 inbound adapter이고
`src/adapters`가 outbound adapter다. 이름과 문서가 이 역할을 명확히 드러내지
않아 모두 같은 adapter처럼 보인 것이다.
| 역할 | 프론트 예 |
| --- | --- |
| input/inbound port | `ListResources`, `CreateResource` 같은 application API |
| inbound adapter | React page/controller, router, form event, push-event translator |
| output/outbound port | resource gateway, session, storage, clock, diagnostics |
| outbound adapter | HTTP, auth SDK, browser storage, TanStack cache, telemetry sink |
React, router, form library, icon library마다 application port를 만들 필요는 없다.
UI 내부 교체만 필요한 라이브러리는 React inbound adapter 내부 vendor facade로
충분하다. port는 application 정책과 외부 소유권 사이의 경계에 둔다.
### 왜 현재 모두 `adapters` 아래에 있는가
실제로 모두 있지는 않다. UI driver가 `presentation`이라는 이름으로 분리돼 있고,
`adapters`에는 주로 outbound 구현이 있다. 다만 다음 두 대안 중 하나를 명시적으로
선택해야 한다.
1. 변경량을 줄여 `presentation = inbound adapter`로 문서화하고
`adapters/outbound`만 명시한다.
2. TypeScript/feature migration과 함께 `adapters/inbound/react`
`adapters/outbound`로 재구성한다.
이 저장소는 input API 부재와 flat contracts 문제도 함께 고쳐야 하므로 두 번째
구조가 장기적으로 더 명확하다. 단, 대규모 rename 자체를 기능 개선으로 세지 말고
architecture gate와 수직 reference feature가 먼저 또는 같은 브랜치에서
증명되어야 한다.
### TypeScript로 바꾸는 것이 좋은가
좋다. 특히 registry ID, Result/error union, port generic, route params/search,
component variant를 컴파일 시점에 닫을 수 있다. 다만 일괄 rename은 권장하지
않는다. tooling → core contracts → application ports/use cases → outbound →
bootstrap → React TSX → tests 순서로 이동한다.
### store 기본 설정이 필요한가
상태 전략은 기본 제공해야 하지만 범용 global store dependency는 필수로 넣지
않는다.
- local interaction: `useState`/`useReducer`
- shareable navigation state: URL
- server state: TanStack Query
- form state: form facade
- low-frequency cross-cutting state: context 또는 typed external store
- complex cross-feature workflow: Zustand/Redux Toolkit/state machine 중 선택
- persistence: `StoragePort`
서버 데이터를 global store에 복사하지 않는 규칙이 중요하다.
[TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview),
[Redux Toolkit](https://redux-toolkit.js.org/introduction/getting-started),
[Zustand](https://zustand.docs.pmnd.rs/)의 역할은 서로 같지 않다.
### retry, API client, logger, token manager, error, validation은 어디에 있는가
- retry: `src/adapters/http/retry-policy.ts`, runtime max-attempt/cleanup 계약까지 구현
- API client: `src/adapters/http/client.ts`, path/search/body projection과
runtime schema/mapper를 사용하며 feature gateway 뒤에서 실행
- logger: `DiagnosticsPort`로 telemetry와 분리해 구현. closed event/level,
allowlist와 bounded/no-op adapter 제공
- token manager: 의도적으로 없음. opaque external auth owner가 credential을 소유
- error: `src/contracts/errors.ts`의 registry-derived `AppFailure`, 공통
`Result`와 HTTP normalization으로 구현
- validation: runtime/API/route/form Zod와 domain invariant를 소유 경계별로 분리
token manager를 기본으로 추가하지 않는 이유는 token lifecycle이 인증 방식마다
다르고 localStorage token을 일반 해법으로 만들면 보안 위험이 커지기 때문이다.
BFF HttpOnly cookie 또는 OIDC/Auth SDK가 credential을 소유하도록 두고, SPA
memory token이 필요한 프로젝트만 auth adapter를 추가한다.
### Lucide React를 쓰면 디자인 시스템이 되는가
아니다. [Lucide React](https://lucide.dev/guide/packages/lucide-react)는
tree-shakable SVG icon source로 적절하지만 select, dialog, menu, focus management
같은 UI behavior는 제공하지 않는다. Lucide는 local icon facade 뒤에 두고,
복잡한 interaction은 [Radix Primitives](https://www.radix-ui.com/primitives/docs/overview/introduction)
또는 React Aria 계열과 같은 headless primitive를 owned wrapper 뒤에서 선택한다.
### 테스트는 현재 어떤 상태인가
테스트 도구 구성은 강한 편이다. 다만 다음이 빠져 있다.
- TS source와 test 전체 typecheck
- 실제 composition root부터 page까지의 통합
- query/mutation controller와 optimistic rollback
- route registry/runtime map 정합성은 RP-04에서 unit, component, negative
registry/type fixture와 built artifact 검증으로 구현됨
- runtime timeout/retry와 path/query/parsed body
- shared MSW scenario catalog
- isolated component stories와 interaction test
- stable-environment visual regression
- built `dist` 대상 release E2E
- 위험 기반 coverage gate
### 라우팅 전략은 무엇이 적절한가
현재 client-only clean architecture와 TanStack Query 조합은 유지하되, 목표
skeleton은 React Router Data Mode의 route object, blocker, scroll restoration,
route error 경계를 사용한다. 서버 상태의 소유자는 계속 application input과
TanStack Query이며 loader/action이 같은 데이터를 별도로 요청하지 않는다.
[React Router 공식 mode 설명](https://reactrouter.com/start/modes)에 따라
Framework Mode는 SSR/static generation, route module, framework-owned data
loading을 실제 요구할 때만 선택한다.
### 바로 쓸 수 있는 디자인 패턴은 무엇을 제공해야 하는가
패턴 이름만 나열하지 않고 다음 executable blueprint를 제공해야 한다.
- page controller: route/form event를 application input으로 변환
- query/mutation adapter: server state lifecycle을 React에 연결
- command/query use case: 읽기와 상태 변경 의도를 분리
- gateway: application이 외부 데이터 소유자를 추상화
- mapper/anti-corruption layer: transport DTO를 core model로 변환
- Result + failure mapper: throw와 사용자 메시지 경계를 통제
- strategy: retry, cache, auth recovery, feature flag 정책 교체
- observer/external store: session/theme/realtime 구독
- state machine: 복잡한 workflow에만 선택적으로 사용
- compound component/headless wrapper: 접근 가능한 복합 UI를 소유
- page template: layout과 상태 표면을 데이터 소유권에서 분리
## 7. 실전 투입 준비 완료 기준
막연한 백분율 대신 아래 조건을 모두 자동 또는 명시적 검토로 확인한다.
1. reference feature가 route → controller → input use case → output gateway →
adapter → mapper → query cache → UI 상태 표면을 통과한다.
2. application input API 외에는 UI에서 outbound dependency에 접근할 수 없다.
3. TypeScript source와 tests가 strict 검사되고 JS 우회 경로가 없다.
4. HTTP path/query/body/auth/timeout/retry/cancel/decode 실패가 계약 테스트된다.
5. typed route registry와 runtime map이 양방향 완전성을 가진다.
6. list/detail/form/status page template과 form error 정책이 준비돼 있다.
7. 디자인 시스템 primitive/pattern이 isolated workshop, interaction, a11y,
visual test를 가진다.
8. sample/reference feature 전체 삭제 후 typecheck/test/build가 통과한다.
9. optional adapter는 설치 조건, 보안 경계, 실패 정책, 테스트 recipe가 있다.
10. 새 feature 추가 문서가 파일 경로, 금지 의존, 실패 상태, 테스트, 검증 명령까지
안내한다.
이 기준은 배포 provider, 실제 인증 tenant, 운영 telemetry vendor, production field
data 같은 프로젝트별 외부 작업을 포함하지 않는다.
## 8. 관련 상세 문서
- [API contract, Schema, Mapper와 Server State](./api-contract-schema-mapper-and-server-state.md)
- [Realtime events, Web Push, and bounded polling](./realtime-events-web-push-and-bounded-polling.md)
- [프론트 포트·어댑터와 기능 경계](./frontend-ports-adapters-and-boundaries.md)
- [TypeScript·상태·데이터 흐름](./typescript-state-and-data-flow.md)
- [라우팅·페이지·재사용 패턴](./routing-pages-and-patterns.md)
- [프론트 플랫폼 구현 로드맵](./frontend-platform-implementation-roadmap.md)
- [디자인 시스템 플랫폼](../styling/design-system-platform.md)
- [프론트 플랫폼 테스트 전략](../testing/frontend-platform-testing-strategy.md)