533 lines
36 KiB
Markdown
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)
|