feat: add optional frontend adapter recipes
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# VD-10: 선택형 frontend capability recipe
|
||||
|
||||
- 상태: Accepted
|
||||
- 결정일: 2026-07-26
|
||||
- 적용 브랜치: `feature-frontend-optional-adapter-recipes`
|
||||
- 현재 선택 capability: 없음
|
||||
- 재검토: 실제 프로젝트가 realtime, offline, PWA, file, generated API,
|
||||
feature flag, worker, multi-tab, browser permission, client workflow,
|
||||
large-data UI 또는 production analytics/error provider를 요구할 때
|
||||
|
||||
## 배경
|
||||
|
||||
서버의 PostgreSQL, MongoDB, Redis, Kafka, MinIO 같은 기술을 브라우저가 직접
|
||||
소비하지는 않는다. 프론트의 변화 지점은 권한 있는 HTTP/BFF, push event,
|
||||
offline persistence, file protocol, browser runtime, 사용자 동의와 UI 성능
|
||||
경계다. 이 capability를 “언젠가 필요할 수 있다”는 이유로 모두 설치하면 초기
|
||||
bundle, 공급망, runtime config, 보안 표면과 업데이트 비용만 늘어난다.
|
||||
|
||||
반대로 문서에 이름만 적으면 실제 프로젝트에서 port 위치, cancellation,
|
||||
fallback, fake와 제거 기준을 다시 설계해야 한다. 따라서 production runtime에
|
||||
아무것도 설치하지 않되 검증 가능한 vendor-neutral recipe를 저장소 밖이 아닌
|
||||
별도 opt-in 경계에 유지한다.
|
||||
|
||||
## 결정
|
||||
|
||||
1. `config/recipes/frontend-capability-recipes.json`이 12개 recipe의 선택 기준,
|
||||
금지 조건, port/fake, failure matrix, lifecycle cleanup, owner,
|
||||
security/privacy, gzip budget, fallback, server-state 정책과 제거 절차의
|
||||
machine-readable SSOT다.
|
||||
2. 현재 실제 소비 요구와 project owner가 없으므로 12개 상태는 모두
|
||||
`RECIPE_AVAILABLE`이며 `INSTALLED`가 아니다. production runtime dependency와
|
||||
composition registration은 0개다.
|
||||
3. `recipes/frontend-capabilities`의 TypeScript port와 fake/unavailable adapter는
|
||||
실행 가능한 설계 예시다. `src` 또는 production entry가 이 디렉터리를 import할
|
||||
수 없다.
|
||||
4. 프로젝트가 capability를 선택하면 필요한 최소 contract를
|
||||
application-owned output port 또는 presentation facade로 이동하고, concrete
|
||||
vendor adapter는 local adapter 경계에 둔다. recipe 디렉터리를 production에서
|
||||
그대로 import하지 않는다.
|
||||
5. WebSocket/SSE처럼 연결은 outbound이고 수신 event는 inbound인 양방향 기술도
|
||||
한 종류의 “adapter”로 뭉개지 않는다. 연결·credential·reconnect 정책과
|
||||
event validation·input invocation을 분리한다.
|
||||
6. Zustand/Redux Toolkit/state machine은 실제 cross-page client-only workflow가
|
||||
확인된 경우 하나만 선택한다. URL, component state, Context, TanStack Query가
|
||||
이미 소유한 상태를 복제하지 않는다.
|
||||
7. browser credential은 localStorage, URL, recipe store, telemetry 또는
|
||||
BroadcastChannel에 넣지 않는다. 브라우저가 database/object store에 직접
|
||||
접속하는 recipe도 금지한다.
|
||||
8. lifecycle이 있는 capability는 unsubscribe, close, unregister, dispose,
|
||||
cancel 또는 `AbortSignal`을 계약과 contract test에 포함해야 한다.
|
||||
9. 선택하지 않은 recipe sentinel이나 vendor dependency가 production bundle에
|
||||
들어가면 gate를 실패시킨다.
|
||||
10. recipe 전체를 제거한 임시 worktree에서 base typecheck, architecture,
|
||||
unit/component/integration test와 production build가 통과해야 한다.
|
||||
|
||||
## 선택과 설치 절차
|
||||
|
||||
```text
|
||||
measured product/runtime need
|
||||
-> project owner + security/privacy classification
|
||||
-> recipe trigger/forbidden/fallback review
|
||||
-> VD-10 amendment with one selected capability
|
||||
-> application port or presentation facade copied into src
|
||||
-> one concrete adapter under local adapter boundary
|
||||
-> composition-only wiring
|
||||
-> contract/failure/cleanup/integration tests
|
||||
-> bundle + dependency baseline approval
|
||||
-> INSTALLED only after all evidence passes
|
||||
```
|
||||
|
||||
도입 커밋에는 owner, 선택 이유, 대안, gzip 차이, runtime config, browser support,
|
||||
failure UX, observability, rollback과 제거 명령을 기록한다. vendor가 필요한
|
||||
behavior를 fake만으로 확인하고 `INSTALLED`로 바꾸지 않는다.
|
||||
|
||||
## 증적
|
||||
|
||||
- catalog: `config/recipes/frontend-capability-recipes.json`
|
||||
- contracts/fakes: `recipes/frontend-capabilities`
|
||||
- 상세 runbook: `docs/architecture/optional-adapter-recipes.md`
|
||||
- contract test: `tests/recipes/optional-capability-contracts.test.ts`
|
||||
- negative fixture:
|
||||
`tests/fixtures/optional-recipes/forbidden`
|
||||
- validation:
|
||||
`scripts/check-optional-recipes.mjs`
|
||||
- removal:
|
||||
`scripts/test-optional-recipe-removal.mjs`
|
||||
- evidence:
|
||||
`artifacts/quality/optional-recipes.json`,
|
||||
`artifacts/quality/optional-recipe-fixtures.json`,
|
||||
`artifacts/tests/optional-recipes.xml`,
|
||||
`artifacts/tests/optional-recipe-removal.xml`
|
||||
|
||||
## Rollback
|
||||
|
||||
현재 branch는 runtime dependency나 production composition을 바꾸지 않으므로
|
||||
recipe catalog, example과 gate를 함께 revert하면 RP-11 상태로 돌아간다. 실제
|
||||
프로젝트에서 선택한 capability는 그 capability의 port/adapter/composition/
|
||||
dependency commit만 revert한다. 여러 vendor 도입을 하나의 되돌릴 수 없는
|
||||
commit으로 묶지 않는다.
|
||||
@@ -12,7 +12,7 @@
|
||||
- 기본 번들에 포함할 역량과 필요할 때 설치할 확장 역량을 구분한다.
|
||||
- 특정 벤더를 채택하더라도 제품 코드가 벤더 API에 직접 결합되지 않는지 확인한다.
|
||||
|
||||
최초 검토 기준은 `develop`의 `cb195f8`이며, RP-01~RP-10 구현 결과를 이 문서에
|
||||
최초 검토 기준은 `develop`의 `cb195f8`이며, RP-01~RP-12 구현 결과를 이 문서에
|
||||
누적 반영했다. 이후 구현으로 경로나 세부 내용이 달라질 수 있으므로, 각 항목은
|
||||
문서의 경로뿐 아니라 해당 테스트와 아키텍처 게이트로 계속 검증해야 한다.
|
||||
|
||||
@@ -27,27 +27,21 @@
|
||||
- Vitest, Testing Library, MSW, Playwright, axe를 이용한 테스트 계층
|
||||
- CI 게이트 taxonomy와 호환성·보안·성능·릴리스 계약 문서
|
||||
|
||||
그러나 “도메인 기능을 바로 추가할 수 있는 프론트엔드 플랫폼” 기준으로는 아직
|
||||
중요한 연결부가 빠져 있다. 가장 큰 문제는 공통 기능이 없다는 것보다 이미 있는
|
||||
기능이 실제 기능 화면의 표준 호출 경로로 조립되지 않았다는 점이다.
|
||||
|
||||
특히 다음은 선행 해결이 필요하다.
|
||||
|
||||
RP-01~RP-09에서 TypeScript 도구 안전망, application runtime 주입,
|
||||
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은 구현됐다. 현재 선행 해결
|
||||
대상은 다음과 같다.
|
||||
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 증거는 프로젝트가 선택하고 검증해야 한다.
|
||||
|
||||
1. optional adapter의 opt-in 경계와 제거 가능한 recipe
|
||||
|
||||
따라서 현재 상태를 “프론트 공통부가 모두 구현됐다”고 표현하면 범위가 과장된다.
|
||||
더 정확한 표현은 다음과 같다.
|
||||
따라서 더 정확한 표현은 다음과 같다.
|
||||
|
||||
> application API, 서버 상태, 폼, 라우팅, 페이지, 디자인 시스템, 테스트와
|
||||
> local 공급망 증적의 표준 수직 경로는 갖춰졌다. 현재 남은 저장소 내부 범위는
|
||||
> opt-in adapter recipe이며 실제 hosting·IdP·취약점/서명/운영 provider는
|
||||
> 프로젝트 통합 범위다.
|
||||
> local 공급망 증적의 표준 수직 경로와 opt-in adapter recipe는 갖춰졌다.
|
||||
> 실제 capability 설치와 hosting·IdP·취약점/서명/운영 provider는 프로젝트
|
||||
> 통합 범위이며, 없는 외부 증거를 완료로 표시하지 않는다.
|
||||
|
||||
## 3. 판정 기준
|
||||
|
||||
@@ -71,8 +65,8 @@ i18n 실행 경계와 diagnostics/telemetry production wiring은 구현됐다.
|
||||
| 검증 | 준비됨 | runtime/API/route/form Zod parse 결과를 실행 경계에서 사용하고 domain invariant와 분리 | feature별 schema 소유권 유지 |
|
||||
| 인증 연동 | 준비됨/프로젝트 선택 | opaque auth owner와 demo seam 존재 | 인증 방식별 recipe; 기본 token 저장소는 추가하지 않음 |
|
||||
| 서버 상태 | 준비됨 | reference route의 query/mutation, cancellation, stale, optimistic/conflict/rollback | feature별 query contribution recipe 유지 |
|
||||
| 클라이언트 상태 | 부분 준비 | local state, theme context, session external store | 상태 소유권 표와 typed external-store 예제 |
|
||||
| 범용 global store | 프로젝트 선택 | 별도 라이브러리 없음 | 필요 조건에 따라 Zustand/Redux Toolkit/state machine 선택 |
|
||||
| 클라이언트 상태 | 준비됨/프로젝트 선택 | 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, metadata consumer, bounded chunk recovery | reference feature route와 release E2E로 사용 범위 확장 |
|
||||
| 앱 셸·반응형 | 준비됨 | 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 조합 유지 |
|
||||
@@ -89,7 +83,7 @@ i18n 실행 경계와 diagnostics/telemetry production wiring은 구현됐다.
|
||||
| 샘플 제거 | 준비됨 | 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·offline·file 등 | 프로젝트 선택 | 현재 없음 | port/adapter recipe와 선택 기준 제공 |
|
||||
| realtime·offline·file 등 | 준비됨/프로젝트 선택 | 12개 opt-in TypeScript port/fake/unavailable, failure/security/bundle/removal gate | 실제 요구·owner 승인 시 해당 recipe만 설치 |
|
||||
|
||||
## 5. 우선순위별 발견 사항
|
||||
|
||||
@@ -292,6 +286,13 @@ vendor facade, 선택 조건, 실패 정책, 테스트 fixture를 문서로 제
|
||||
| large data UI | virtualization, data grid | owned component facade | 데이터 규모가 측정 기준을 넘을 때 |
|
||||
| analytics/error sink | vendor SDK, OpenTelemetry | redaction, consent, sampling adapter | 운영 provider와 정책이 정해졌을 때 |
|
||||
|
||||
12개 항목의 현재 상태는 모두 `RECIPE_AVAILABLE / NOT_INSTALLED`다.
|
||||
`config/recipes/frontend-capability-recipes.json`이 선택/금지 조건, failure,
|
||||
cleanup, security/privacy, bundle budget, fallback과 제거 절차의 SSOT이며,
|
||||
`recipes/frontend-capabilities`에 production-excluded TypeScript port와
|
||||
fake/unavailable adapter가 있다. 도입 절차는
|
||||
`docs/architecture/optional-adapter-recipes.md`를 따른다.
|
||||
|
||||
서버의 Redis, MongoDB, PostgreSQL, MinIO를 브라우저가 직접 연결하는 구조는 기본
|
||||
frontend adapter catalog에 넣지 않는다. 브라우저는 권한 있는 backend API/BFF를
|
||||
통해 이 자원에 접근해야 한다. 프론트에서 대응되는 변화 지점은 데이터베이스
|
||||
|
||||
@@ -1003,6 +1003,31 @@ RP-12는 recipe별 merge commit이다. optional adapter 문제 시 해당 recipe
|
||||
revert하고 RP-11을 유지한다. 여러 vendor를 되돌릴 수 없는 한 commit에 묶지
|
||||
않는다.
|
||||
|
||||
**구현 증거 (2026-07-26)**
|
||||
|
||||
- VD-10에서 실제 project 요구가 선택되지 않았음을 기록하고 12개 capability를
|
||||
모두 `RECIPE_AVAILABLE`, production runtime dependency 0개로 유지했다.
|
||||
- machine-readable catalog에 recipe별 trigger/forbidden 조건, boundary,
|
||||
port/fake, failure matrix, lifecycle cleanup, project owner 요구,
|
||||
security/privacy, gzip budget, fallback, server-state 정책과 제거 순서를
|
||||
등록했다.
|
||||
- production-excluded `recipes/frontend-capabilities`에 12개 vendor-neutral
|
||||
TypeScript port와 deterministic fake, fail-closed unavailable adapter를
|
||||
제공한다. 프로젝트는 선택한 최소 계약만 application/presentation 경계로
|
||||
복사하고 concrete adapter를 composition에서 연결한다.
|
||||
- realtime ordering/unsubscribe, offline migration/close, worker cancel,
|
||||
multi-tab dedupe, permission result, workflow reset, large-data stale
|
||||
generation, analytics consent/redaction/queue와 나머지 facade contract를
|
||||
runnable test로 검증한다.
|
||||
- cleanup 누락, 승인되지 않은 dependency, vendor direct import, credential
|
||||
storage/URL/telemetry 경로, server-state store 복제와 production recipe import
|
||||
negative fixture를 blocking gate에 연결했다.
|
||||
- recipe와 recipe test를 통째로 제거한 임시 사본에서 base typecheck,
|
||||
architecture, 전체 test와 production build를 실행하며, opt-in하지 않은
|
||||
sentinel이 built `dist`에 없는지 검사한다.
|
||||
- 상세 도입/배치/검증/제거 절차는
|
||||
`docs/architecture/optional-adapter-recipes.md`에 기록했다.
|
||||
|
||||
## 11. Vendor decision gate
|
||||
|
||||
| ID | 시점 | 결정 | 기본값 또는 미결정 시 처리 | 차단 범위 |
|
||||
|
||||
@@ -885,27 +885,32 @@ capability의 기본 정책, port 또는 안전한 no-op 구현과 composition
|
||||
|
||||
| Adapter | 도입 조건 | 기본 상태 |
|
||||
| --- | --- | --- |
|
||||
| WebSocket/SSE | 실시간 server event 필요 | 미설치 recipe |
|
||||
| IndexedDB | 큰 offline data 또는 durable queue 필요 | 미설치 recipe |
|
||||
| Service Worker/PWA | offline shell과 installability 필요 | 미설치 recipe |
|
||||
| WebSocket/SSE | 실시간 server event 필요 | opt-in recipe 제공, 미설치 |
|
||||
| IndexedDB | 큰 offline data 또는 durable queue 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Service Worker/PWA | offline shell과 installability 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Offline mutation queue | 재연결 후 명령 재처리 필요 | 미설치 recipe |
|
||||
| Feature flag | remote rollout/kill switch 필요 | 미설치 recipe |
|
||||
| Feature flag | remote rollout/kill switch 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Translation catalog vendor | 원격 catalog·복수 namespace 운영 필요 | 기본 locale facade 뒤에 미설치 |
|
||||
| Analytics | 사용자 동의 기반 product analytics 필요 | 미설치 recipe |
|
||||
| Error-reporting SDK | 운영 예외 집계 필요 | 미설치 recipe |
|
||||
| OpenTelemetry | 조직 trace 연계 필요 | 미설치 recipe |
|
||||
| Web Worker | CPU 작업이 main thread를 막음 | 미설치 recipe |
|
||||
| Notification | 사용자 권한 기반 browser notification 필요 | 미설치 recipe |
|
||||
| Clipboard/File/Media | 해당 browser capability 필요 | 미설치 recipe |
|
||||
| Analytics | 사용자 동의 기반 product analytics 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Error-reporting SDK | 운영 예외 집계 필요 | opt-in recipe 제공, 미설치 |
|
||||
| OpenTelemetry | 조직 trace 연계 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Web Worker | CPU 작업이 main thread를 막음 | opt-in recipe 제공, 미설치 |
|
||||
| Notification | 사용자 권한 기반 browser notification 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Clipboard/File/Media | 해당 browser capability 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Image CDN adapter | responsive image transform 필요 | 미설치 recipe |
|
||||
| Virtualization | 대량 list rendering이 측정상 병목 | 미설치 recipe |
|
||||
| OpenAPI generator | backend 계약에서 client 생성 필요 | 미설치 recipe |
|
||||
| Zustand/Redux/Jotai | 복잡한 cross-page client state 확인 | 미설치 recipe |
|
||||
| XState 등 state machine | 장기 workflow 상태 전이가 복잡함 | 미설치 recipe |
|
||||
| Virtualization | 대량 list rendering이 측정상 병목 | opt-in recipe 제공, 미설치 |
|
||||
| OpenAPI generator | backend 계약에서 client 생성 필요 | opt-in recipe 제공, 미설치 |
|
||||
| Zustand/Redux/Jotai | 복잡한 cross-page client state 확인 | opt-in recipe 제공, 미설치 |
|
||||
| XState 등 state machine | 장기 workflow 상태 전이가 복잡함 | opt-in recipe 제공, 미설치 |
|
||||
| Cloud visual-review service | 외부 승인·호스팅 workflow 필요 | 로컬 Storybook/visual gate 뒤에 미설치 |
|
||||
|
||||
선택 adapter는 “나중에 쓸 수 있으므로” 기본 bundle에 넣지 않는다. 도입
|
||||
조건, 보안 영향, bundle 비용과 제거 방법이 확인된 경우에만 추가한다.
|
||||
현재 구현된 공통 catalog, TypeScript contract/fake와 blocking gate는
|
||||
`docs/architecture/optional-adapter-recipes.md`와
|
||||
`config/recipes/frontend-capability-recipes.json`을 따른다. 이 recipe source를
|
||||
production에서 직접 import하는 것은 금지하며 선택한 contract만 application
|
||||
소유 경계로 이동한다.
|
||||
|
||||
## 19. 새 outbound adapter 추가 recipe
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
# Optional frontend adapter recipes
|
||||
|
||||
이 문서는 도메인과 무관한 선택형 frontend capability를 실제 프로젝트에
|
||||
도입하는 실행 가이드다. 기본 스켈레톤에는 vendor runtime을 설치하지 않는다.
|
||||
`RECIPE_AVAILABLE`은 계약·fake·failure policy가 준비됐다는 뜻이며 실제 provider,
|
||||
runtime behavior 또는 production readiness를 뜻하지 않는다.
|
||||
|
||||
## 1. 현재 상태와 파일 지도
|
||||
|
||||
| 항목 | 경로 | production 포함 |
|
||||
| --- | --- | --- |
|
||||
| 선택/금지/예산 SSOT | `config/recipes/frontend-capability-recipes.json` | 정책만 |
|
||||
| catalog JSON schema | `schemas/config/frontend-capability-recipes.schema.json` | 아니오 |
|
||||
| TypeScript port | `recipes/frontend-capabilities/contracts.ts` | 아니오 |
|
||||
| fake/unavailable | `recipes/frontend-capabilities/fake-adapters.ts` | 아니오 |
|
||||
| contract test | `tests/recipes/optional-capability-contracts.test.ts` | 아니오 |
|
||||
| 정적/번들 gate | `scripts/check-optional-recipes.mjs` | build 도구 |
|
||||
| negative fixture | `scripts/check-optional-recipe-fixtures.mjs` | 아니오 |
|
||||
| 완전 제거 gate | `scripts/test-optional-recipe-removal.mjs` | 아니오 |
|
||||
|
||||
현재 `productionRuntimeDependencies`는 빈 배열이며 12개 recipe 모두 선택되지
|
||||
않았다. TypeScript example은 product source가 import할 library가 아니라 선택
|
||||
시 복사하고 좁힐 출발점이다.
|
||||
|
||||
## 2. 어느 경계에 두는가
|
||||
|
||||
| capability 성격 | port 소유자 | adapter 방향 | concrete 위치 예 |
|
||||
| --- | --- | --- | --- |
|
||||
| application이 외부 결과를 요청 | application | outbound | `src/adapters/<capability>` |
|
||||
| URL/browser event가 의도를 전달 | application input | inbound | `src/presentation/adapters` |
|
||||
| React rendering behavior만 교체 | presentation | local facade | `src/presentation/<capability>` |
|
||||
| feature 전용 protocol | feature application | in/out 분리 | `src/features/<name>/adapters` |
|
||||
|
||||
WebSocket 연결 생성, reconnect와 credential attachment는 outbound다. 수신 JSON
|
||||
검증과 application input 호출은 inbound다. Service Worker update event,
|
||||
BroadcastChannel event도 같은 원칙을 적용한다. generated DTO와 vendor SDK
|
||||
type은 facade 밖으로 노출하지 않는다.
|
||||
|
||||
## 3. 12개 recipe 선택표
|
||||
|
||||
| recipe | 설치하는 경우 | 설치하면 안 되는 경우 | 핵심 fallback |
|
||||
| --- | --- | --- | --- |
|
||||
| realtime | ordered push/resume protocol이 확정됨 | polling이 충분하거나 ordering owner 없음 | bounded polling/stale UI |
|
||||
| offline/IndexedDB | durable offline data/queue가 제품 요구 | credential 저장, DB 직접 연결, HTTP cache로 충분 | online-only + offline state |
|
||||
| Service Worker/PWA | install/offline shell과 cache owner 승인 | update/rollback UX 없음 | hosting cache 기반 network app |
|
||||
| file transfer | progress/cancel/size/type 정책 필요 | long-lived credential URL | bounded normal request |
|
||||
| generated API | versioned source와 drift CI가 있음 | DTO가 domain/UI로 노출됨 | typed request builder + schema |
|
||||
| feature flag | rollout/kill switch owner와 default 있음 | authorization에 사용 | typed local default |
|
||||
| Web Worker | profiler가 main-thread 병목을 증명 | 단순 network I/O | chunked/deferred execution |
|
||||
| multi-tab | 비민감 event 동기화가 필요 | server가 conflict authority | focus 시 authoritative refresh |
|
||||
| browser permission | user gesture 기반 기능 필요 | boot 요청, denied UX 없음 | manual input/instruction |
|
||||
| client workflow | cross-page client-only state가 실재 | query/server state 복제 | URL/local/context/query |
|
||||
| large data UI | 실측 scale이 budget 초과 | pagination으로 충분, a11y 미정 | accessible pagination |
|
||||
| analytics/error sink | provider·consent·retention 승인 | arbitrary payload/redaction 우회 | bounded local diagnostics |
|
||||
|
||||
정확한 failure matrix, security/privacy, gzip budget과 제거 순서는 JSON catalog가
|
||||
SSOT다. 문서와 catalog가 다르면 gate가 검사하는 catalog를 우선 고치고 이 표도
|
||||
같이 갱신한다.
|
||||
|
||||
## 4. 공통 구현 순서
|
||||
|
||||
1. 문제를 vendor 이름이 아닌 capability와 측정값으로 기록한다.
|
||||
2. catalog의 trigger와 forbidden 조건을 모두 검토한다.
|
||||
3. project owner, security/privacy reviewer, gzip budget과 재검토 날짜를 VD-10
|
||||
amendment에 기록한다.
|
||||
4. existing URL/local/context/query/application port로 해결되지 않는지 확인한다.
|
||||
5. 필요한 contract만 `recipes`에서 해당 application/presentation 경계로 복사해
|
||||
실제 payload와 failure union으로 좁힌다.
|
||||
6. concrete SDK는 `src/adapters/...` 또는 local presentation facade adapter에서만
|
||||
import한다.
|
||||
7. composition root가 concrete adapter를 주입한다. page/use case가 constructor를
|
||||
직접 호출하지 않는다.
|
||||
8. fake, unavailable, timeout/cancel, cleanup, malformed input, redaction과
|
||||
integration test를 작성한다.
|
||||
9. runtime config schema, dependency inventory/approval, SBOM, bundle budget,
|
||||
browser support와 runbook을 갱신한다.
|
||||
10. 실제 provider integration과 negative behavior가 통과한 뒤에만 catalog 상태를
|
||||
별도 project catalog에서 `INSTALLED`로 바꾼다.
|
||||
|
||||
## 5. capability별 필수 검증
|
||||
|
||||
### Realtime
|
||||
|
||||
- runtime schema로 envelope/version/event ID/sequence/timestamp를 검증한다.
|
||||
- reconnect는 exponential backoff 상한, visibility/offline 상태, auth refresh와
|
||||
resume token expiry를 정의한다.
|
||||
- duplicate/out-of-order는 domain use case에 전달하기 전에 정책화한다.
|
||||
- route unmount/logout에서 unsubscribe하고 heartbeat timer를 종료한다.
|
||||
|
||||
### Offline/Service Worker
|
||||
|
||||
- store/cache 이름과 schema는 release와 독립적인 migration version을 가진다.
|
||||
- quota, corrupt row, partial migration, downgrade/rollback을 fixture로 만든다.
|
||||
- authenticated response와 credential은 기본 cache 대상이 아니다.
|
||||
- stale worker loop를 막고 unregister 후 owned cache 삭제가 가능한지 검증한다.
|
||||
|
||||
### File/generated API
|
||||
|
||||
- upload는 client MIME을 신뢰하지 않고 size/type/server rejection을 모두 다룬다.
|
||||
- progress는 unknown total을 허용하며 navigation/unmount에서 AbortSignal로
|
||||
취소한다.
|
||||
- generated code는 facade 뒤 DTO이며 runtime response schema와 contract drift
|
||||
gate를 유지한다.
|
||||
|
||||
### Flag/worker/multi-tab/browser
|
||||
|
||||
- flag unknown/unavailable/stale에서 명시적 typed fallback을 사용하고 access
|
||||
control로 사용하지 않는다.
|
||||
- worker는 task ID/generation/cancel을 사용해 stale result를 폐기하고 crash를
|
||||
normalized failure로 바꾼다.
|
||||
- multi-tab은 source/event/version으로 self-echo와 duplicate를 막고 payload를
|
||||
비민감 invalidation hint로 제한한다.
|
||||
- browser permission은 user gesture에서만 요청하고 denied/dismissed/unsupported를
|
||||
서로 다른 UX 결과로 처리한다.
|
||||
|
||||
### Client workflow/large data/analytics
|
||||
|
||||
- workflow store는 server entity/collection을 복제하지 않고 query key나 ID 참조만
|
||||
보관한다. logout/reset/version mismatch 정책을 테스트한다.
|
||||
- virtualization은 profiler와 production-like row count로 정당화하며 keyboard,
|
||||
focus restoration, screen reader와 stale row identity를 검증한다.
|
||||
- analytics는 essential diagnostics와 consent-required event를 분리하고 closed
|
||||
event/attribute registry, pre-queue redaction, sampling, bounded queue와
|
||||
retention을 적용한다.
|
||||
|
||||
## 6. 검증 명령
|
||||
|
||||
```bash
|
||||
corepack pnpm check:types:recipes
|
||||
corepack pnpm test:recipes
|
||||
corepack pnpm build
|
||||
corepack pnpm check:optional-recipes
|
||||
corepack pnpm check:optional-recipe-fixtures
|
||||
corepack pnpm test:optional-recipe-removal
|
||||
```
|
||||
|
||||
negative gate는 cleanup 누락, unselected dependency, local adapter 밖 vendor
|
||||
import, credential localStorage/URL/telemetry 경로, workflow store의 server-state
|
||||
복제와 production source의 recipe import를 거절한다. removal gate는 recipe와
|
||||
recipe test를 삭제한 임시 사본에서 base typecheck, architecture, test와 build를
|
||||
실행한다.
|
||||
|
||||
## 7. 제거 체크리스트
|
||||
|
||||
1. 신규 호출과 background 작업을 중지한다.
|
||||
2. subscription, worker, channel, media track, observer를 cleanup한다.
|
||||
3. persisted store/cache/event queue의 migrate 또는 purge 정책을 실행한다.
|
||||
4. composition registration과 runtime config를 제거한다.
|
||||
5. concrete adapter, facade/port와 vendor dependency를 제거한다.
|
||||
6. dependency baseline, SBOM과 bundle baseline을 갱신한다.
|
||||
7. typecheck/test/build, production bundle absence와 도메인 기능 fallback을
|
||||
검증한다.
|
||||
|
||||
provider 장애 시 fake로 바꾸어 production을 PASS 처리하지 않는다. 문서화된
|
||||
unavailable fallback만 사용하고 provider가 필수인 promotion은
|
||||
`FAIL_UNVERIFIED` 또는 blocked 상태로 유지한다.
|
||||
@@ -179,6 +179,11 @@ export type AppFailure =
|
||||
|
||||
vendor를 선택하더라도 feature 외부에는 hook/facade만 export한다. 제품 코드가
|
||||
store instance의 `getState`와 `setState`를 임의 호출하지 않게 한다.
|
||||
현재 `recipes/frontend-capabilities`의 `ClientWorkflowPort`와
|
||||
`FakeClientWorkflowAdapter`가 vendor-neutral opt-in 예제를 제공한다. 기본
|
||||
production에는 Zustand/Redux Toolkit/state-machine dependency가 없고,
|
||||
`check:optional-recipe-fixtures`가 server response collection을 client workflow
|
||||
store에 복제하는 패턴을 거절한다.
|
||||
|
||||
### 3.3 persistence
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- independent review for new direct production dependencies
|
||||
- CycloneDX 1.6 SBOM and inventory component/edge coherence
|
||||
- source/lock/SBOM/dist-linked local provenance statement
|
||||
- source, scripts, tests, tracked config/schema, public, built asset and generated
|
||||
- source, opt-in recipes, scripts, tests, tracked config/schema, public, built asset and generated
|
||||
release metadata secret scan
|
||||
- two-build `SOURCE_DATE_EPOCH` reproducibility check
|
||||
|
||||
|
||||
@@ -1299,6 +1299,16 @@ TypeScript test, Storybook, coverage, visual과 built-dist 명령은
|
||||
- flaky test를 owner/만료일 없이 skip
|
||||
- CI gate에 `continue-on-error`
|
||||
|
||||
### 선택형 adapter recipe gate
|
||||
|
||||
선택형 capability example은 `tests/recipes`에서 contract/fake/unavailable을
|
||||
실행하지만 production entry에는 포함하지 않는다. `check:optional-recipes`는
|
||||
12개 catalog 완전성, unselected dependency, production source import와 built
|
||||
bundle sentinel 부재를 검사한다. negative fixture는 lifecycle cleanup 누락,
|
||||
vendor direct import, credential storage/URL/telemetry 경로와 workflow store의
|
||||
server-state 복제를 거절한다. `test:optional-recipe-removal`은 recipe 전체를
|
||||
제거한 사본에서 base typecheck/test/build를 다시 실행한다.
|
||||
|
||||
## 20. 단계별 도입 순서
|
||||
|
||||
1. `tsconfig.test.json`과 test typecheck gate를 추가한다.
|
||||
|
||||
Reference in New Issue
Block a user