26 KiB
프론트엔드 플랫폼 역량 재검토
1. 문서 목적
이 문서는 도메인 기능과 실제 운영 환경의 배포 증적을 제외하고, 이 저장소가 새 프론트엔드 제품의 출발점으로 제공해야 하는 공통 역량을 다시 평가한다. 평가 기준은 다음과 같다.
- 코드나 설정 파일이 존재하는지만 보지 않는다.
- 부트스트랩부터 화면까지 실제 호출 경로가 연결되는지 확인한다.
- 선언한 레지스트리와 정책이 런타임 및 CI에서 집행되는지 확인한다.
- 기본 번들에 포함할 역량과 필요할 때 설치할 확장 역량을 구분한다.
- 특정 벤더를 채택하더라도 제품 코드가 벤더 API에 직접 결합되지 않는지 확인한다.
검토 기준 브랜치는 develop, 기준 커밋은 cb195f8이다. 이후 구현으로 경로나
세부 내용이 달라질 수 있으므로, 각 항목은 문서의 경로뿐 아니라 해당 테스트와
아키텍처 게이트로 계속 검증해야 한다.
2. 결론
현재 저장소는 다음 기반이 강하다.
- 런타임 설정과 릴리스 매니페스트 검증
- 도메인, 애플리케이션, 프레젠테이션, outbound adapter의 의존 방향
- 공통 HTTP 실패 형태와 제한된 retry 정책
- 앱 셸, 반응형 내비게이션, 테마, 비동기 상태 표면
- Vitest, Testing Library, MSW, Playwright, axe를 이용한 테스트 계층
- CI 게이트 taxonomy와 호환성·보안·성능·릴리스 계약 문서
그러나 “도메인 기능을 바로 추가할 수 있는 프론트엔드 플랫폼” 기준으로는 아직 중요한 연결부가 빠져 있다. 가장 큰 문제는 공통 기능이 없다는 것보다 이미 있는 기능이 실제 기능 화면의 표준 호출 경로로 조립되지 않았다는 점이다.
특히 다음은 선행 해결이 필요하다.
RP-01~RP-03에서 TypeScript 도구 안전망, application runtime 주입, query/mutation inbound adapter와 HTTP 실행 계약은 구현됐다. 현재 선행 해결 대상은 다음과 같다.
- 선언과 실행이 일치하는 typed route 계약
- 전체를 제거할 수 있는 실제 reference feature
- 폼, 페이지 템플릿, 확장된 디자인 시스템과 컴포넌트 워크벤치
따라서 현재 상태를 “프론트 공통부가 모두 구현됐다”고 표현하면 범위가 과장된다. 더 정확한 표현은 다음과 같다.
운영·안전 계약과 범용 앱 셸은 갖춰졌지만, 기능 개발자가 사용하는 application API, 서버 상태, 폼, 라우팅, 페이지 패턴의 표준 수직 경로는 아직 보강이 필요하다.
3. 판정 기준
| 판정 | 의미 |
|---|---|
| 준비됨 | 구현, 실제 조립, 자동 검증이 모두 존재한다. |
| 부분 준비 | 핵심 구현은 있으나 실제 호출 경로, 정책 집행, 예제가 불완전하다. |
| 미제공 | 새 기능을 만들 때 팀이 직접 선택·설계해야 한다. |
| 프로젝트 선택 | 기본 번들에 강제하면 비용이 더 크며, 경계와 recipe만 제공한다. |
4. 역량 매트릭스
| 영역 | 현재 판정 | 근거 | 필요한 다음 상태 |
|---|---|---|---|
| 부트·런타임 설정 | 준비됨 | src/bootstrap, runtime schema, release 검사 |
현 상태 유지, TS 전환 시 동일 게이트 유지 |
| 계층 의존 방향 | 부분 준비 | .dependency-cruiser.cjs, src/application/ports |
inbound/outbound 명명과 contracts 소유권까지 집행 |
| application facade | 준비됨 | typed input/output catalog, provider, production composition test | feature input use case를 contribution으로 확장 |
| HTTP client | 준비됨 | path/search/body projection, runtime timeout/retry, abort/cleanup test | feature gateway 뒤에서 사용 |
| retry | 준비됨 | HTTP 단일 소유, runtime max attempts, Query retry off | RP-09에서 telemetry 연결 |
| 오류 모델 | 부분 준비 | error registry와 normalization 존재 | typed discriminated union과 계층별 mapper |
| 검증 | 부분 준비 | runtime/API Zod parse 결과를 실제 request에 사용 | route/form/domain 경계를 추가 |
| 인증 연동 | 준비됨/프로젝트 선택 | opaque auth owner와 demo seam 존재 | 인증 방식별 recipe; 기본 token 저장소는 추가하지 않음 |
| 서버 상태 | 부분 준비 | 제한된 query/mutation bridge와 lifecycle test | RP-05 reference route에서 실제 feature 연결 |
| 클라이언트 상태 | 부분 준비 | local state, theme context, session external store | 상태 소유권 표와 typed external-store 예제 |
| 범용 global store | 프로젝트 선택 | 별도 라이브러리 없음 | 필요 조건에 따라 Zustand/Redux Toolkit/state machine 선택 |
| 라우팅 | 부분 준비 | lazy route, access hint, registry 존재 | typed runtime map, codec, recovery, metadata 집행 |
| 앱 셸·반응형 | 부분 준비 | header/sidebar/content/theme 구현 | 접근 가능한 mobile drawer와 focus 복원 |
| 페이지 템플릿 | 미제공 | 각 페이지가 직접 레이아웃 조립 | list/detail/form/status 등 슬롯 기반 템플릿 |
| 디자인 토큰 | 부분 준비 | semantic color/theme 토큰 존재 | typography, spacing, motion, layer 등 3단계 토큰 |
| 공통 UI | 부분 준비 | Button, TextField, Card, Alert, Badge, Dialog | form/navigation/overlay/data/layout primitives 확장 |
| 아이콘 | 미제공 | 문자 기호를 직접 사용 | Lucide를 로컬 icon facade 뒤에서 사용 |
| 폼 | 미제공 | 수동 문자열 검증 예제만 존재 | schema 기반 form facade와 422/dirty/pending 정책 |
| 국제화 | 미제공 | 한국어 문자열·locale이 하드코딩 | typed message/formatter/locale/RTL 경계 |
| logging/diagnostics | 미제공 | telemetry port는 있으나 logger 없음 | redaction이 적용된 diagnostics/logging 경계 |
| telemetry | 부분 준비 | registry, queue, redaction 존재 | HTTP·boot·cache·storage·route 사건에 실제 연결 |
| 비동기 상태 불변식 | 준비됨 | 배타적 typed overlay, stale latch, 실제 retry/conflict action | reference 화면에서 전체 상태 전시 |
| 단위·통합·E2E | 준비됨 | Vitest, RTL, MSW, Playwright 3엔진 | TS 테스트 검사, 실제 bootstrap 통합, 위험 시나리오 보강 |
| UI 회귀 검증 | 미제공 | axe/reflow는 있으나 visual baseline 없음 | Storybook 또는 동급 workshop과 시각 회귀 |
| 샘플 제거 | 부분 준비 | fixture 제거 테스트 존재 | sample domain/registry/runtime 전체 제거 검증 |
| registry·compatibility 집행 | 부분 준비 | registry와 gate는 있으나 실제 before/after 및 orphan 검사가 제한적 | type/reference/orphan/diff/migration을 자동 검증 |
| 공급망 검사 | 부분 준비 | lockfile·문서·gate는 있으나 실제 transitive 취약점/license/SBOM 깊이가 부족 | pinned scanner와 policy exception/증적 연결 |
| realtime·offline·file 등 | 프로젝트 선택 | 현재 없음 | port/adapter recipe와 선택 기준 제공 |
5. 우선순위별 발견 사항
5.1 P0: 기능 개발을 막는 항목
RP-02에서 application 런타임 우회 해결
src/bootstrap/composition-root.js가 만든 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를 사용한다. 이는 좋은
중간 안전망이지만 다음 도구는 TS migration을 그대로 따라가지 못한다.
- ESLint의 계층·보안 glob은 JS/JSX 중심이다.
- registry scanner는
.ts와.tsx를 찾지 않는다. - registry governance 경로가
.js확장자로 고정되어 있다. - tests는 현재
tsconfig.json검사 범위에서 빠진다.
따라서 파일 확장자를 먼저 바꾸면 새 TS 코드가 일부 자동 검사에서 빠질 수 있다. TypeScript 전환은 TypeScript의 JavaScript migration 가이드 처럼 점진적으로 진행하되, 이 저장소에서는 tooling glob과 CI를 먼저 고쳐야 한다.
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를 노출한다.
route registry가 실행 계약이 아니다
route registry에는 paramsSchema, searchSchema, loadingSurface,
errorSurface, chunkId가 있지만 실제 router tree, lazy module, navigation
목록은 별도로 작성된다. 여러 필드는 선언만 되고 런타임에 사용되지 않는다.
chunk recovery use case와 redirect loop guard도 실제 route flow에 연결되지 않는다.
목표 상태:
- 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, focus, scroll, navigation cancellation을 검증한다.
reference feature가 완전히 제거되지 않는다
현재 sample removal gate는 src/sample/contract-fixture만 삭제한다. sample API
operation, schema, mapper, domain model, query key는 다른 production 경로에 남는다.
반면 화면에 노출된 /sample/resources는 실제 query 수직 흐름을 실행하지 않는다.
reference feature는 domain, application input/output, schemas, operation, mapper, query controller, pages, tests를 한 소유 경계 아래 모아야 한다. 해당 모듈과 registry contribution을 제거한 뒤 typecheck, architecture, test, build가 모두 통과해야 “제거 가능”으로 판정한다.
비동기·복구 상태의 불변식이 닫혀 있지 않다
공통 async model과 gallery가 있지만 선언 가능한 상태 조합 중 일부는 사용자 행동과 모순될 수 있다. stale data가 있는 degraded 상태와 refreshing, mutation pending과 conflict, retry button과 실제 handler 존재 여부를 typed state로 닫아야 한다. 상태를 boolean 여러 개로 조합하지 않고 다음과 같은 discriminated state와 action capability로 표현한다.
initial-loading
ready
refreshing-with-data
empty
degraded-with-data
terminal-error
mutation-pending
mutation-conflict
chunk recovery와 release coherence도 policy 함수가 존재하는 것으로 완료되지 않는다. 실제 lazy import failure가 manifest 재확인, build 비교, 단 한 번의 guarded reload, 반복 실패 지원 표면까지 이어지고 E2E로 검증되어야 한다.
telemetry, registry, 공급망 gate의 실행 깊이가 부족하다
telemetry registry에는 여러 사건이 있지만 실제 production producer는 제한적이다. boot, API attempt/final failure, auth recovery, storage/cache degradation, release/chunk recovery를 registry 사건에 연결해야 한다.
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: 공통 플랫폼 기본 제공 항목
- schema 기반 form facade와 field/error/pending/dirty/422 정책
- standard, collection, detail, form, status page template
- 접근 가능한 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
- redacted structured diagnostics/logger와 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
5.3 P2: 경계와 recipe를 제공할 선택 항목
다음 기능을 모든 앱의 초기 번들에 설치할 필요는 없다. 대신 port 또는 local vendor facade, 선택 조건, 실패 정책, 테스트 fixture를 문서로 제공한다.
| capability | 대표 기술 | 기본 제공할 경계 | 실제 설치 조건 |
|---|---|---|---|
| realtime | WebSocket, SSE | subscribe/unsubscribe, reconnect, resume, heartbeat | 서버가 push event를 제공할 때 |
| offline storage | IndexedDB | versioned repository, migration, quota failure | offline read/write가 제품 요구일 때 |
| background/cache | Service Worker, PWA | cache ownership, update, rollback recipe | installable/offline 앱일 때 |
| file transfer | presigned HTTP, multipart | progress, cancel, size/type validation | 업로드·대용량 다운로드가 있을 때 |
| generated API | OpenAPI, GraphQL, gRPC-Web | generated client를 gateway 뒤에 감싸는 규칙 | 서버 계약 형식이 확정됐을 때 |
| 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와 정책이 정해졌을 때 |
서버의 Redis, MongoDB, PostgreSQL, MinIO를 브라우저가 직접 연결하는 구조는 기본 frontend adapter catalog에 넣지 않는다. 브라우저는 권한 있는 backend API/BFF를 통해 이 자원에 접근해야 한다. 프론트에서 대응되는 변화 지점은 데이터베이스 vendor가 아니라 HTTP/GraphQL/gRPC-Web, 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 구현이 있다. 다만 다음 두 대안 중 하나를 명시적으로
선택해야 한다.
- 변경량을 줄여
presentation = inbound adapter로 문서화하고adapters/outbound만 명시한다. - 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, Redux Toolkit, Zustand의 역할은 서로 같지 않다.
retry, API client, logger, token manager, error, validation은 어디에 있는가
- retry:
src/adapters/http/retry-policy.js, 부분 준비 - API client:
src/adapters/http/client.js, 부분 준비 - logger: 없음. telemetry와 분리하거나 diagnostics port로 합치는 결정 필요
- token manager: 의도적으로 없음. opaque external auth owner가 credential을 소유
- error:
src/contracts/errors.js와 HTTP normalization, 부분 준비 - validation: runtime/API Zod는 존재, route/form/domain 분리는 미완성
token manager를 기본으로 추가하지 않는 이유는 token lifecycle이 인증 방식마다 다르고 localStorage token을 일반 해법으로 만들면 보안 위험이 커지기 때문이다. BFF HttpOnly cookie 또는 OIDC/Auth SDK가 credential을 소유하도록 두고, SPA memory token이 필요한 프로젝트만 auth adapter를 추가한다.
Lucide React를 쓰면 디자인 시스템이 되는가
아니다. Lucide React는 tree-shakable SVG icon source로 적절하지만 select, dialog, menu, focus management 같은 UI behavior는 제공하지 않는다. Lucide는 local icon facade 뒤에 두고, 복잡한 interaction은 Radix Primitives 또는 React Aria 계열과 같은 headless primitive를 owned wrapper 뒤에서 선택한다.
테스트는 현재 어떤 상태인가
테스트 도구 구성은 강한 편이다. 다만 다음이 빠져 있다.
- TS source와 test 전체 typecheck
- 실제 composition root부터 page까지의 통합
- query/mutation controller와 optimistic rollback
- route registry/runtime map 정합성
- 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 설명에 따라 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. 실전 투입 준비 완료 기준
막연한 백분율 대신 아래 조건을 모두 자동 또는 명시적 검토로 확인한다.
- reference feature가 route → controller → input use case → output gateway → adapter → mapper → query cache → UI 상태 표면을 통과한다.
- application input API 외에는 UI에서 outbound dependency에 접근할 수 없다.
- TypeScript source와 tests가 strict 검사되고 JS 우회 경로가 없다.
- HTTP path/query/body/auth/timeout/retry/cancel/decode 실패가 계약 테스트된다.
- typed route registry와 runtime map이 양방향 완전성을 가진다.
- list/detail/form/status page template과 form error 정책이 준비돼 있다.
- 디자인 시스템 primitive/pattern이 isolated workshop, interaction, a11y, visual test를 가진다.
- sample/reference feature 전체 삭제 후 typecheck/test/build가 통과한다.
- optional adapter는 설치 조건, 보안 경계, 실패 정책, 테스트 recipe가 있다.
- 새 feature 추가 문서가 파일 경로, 금지 의존, 실패 상태, 테스트, 검증 명령까지 안내한다.
이 기준은 배포 provider, 실제 인증 tenant, 운영 telemetry vendor, production field data 같은 프로젝트별 외부 작업을 포함하지 않는다.