30 KiB
프론트엔드 플랫폼 역량 재검토
1. 문서 목적
이 문서는 도메인 기능과 실제 운영 환경의 배포 증적을 제외하고, 이 저장소가 새 프론트엔드 제품의 출발점으로 제공해야 하는 공통 역량을 다시 평가한다. 평가 기준은 다음과 같다.
- 코드나 설정 파일이 존재하는지만 보지 않는다.
- 부트스트랩부터 화면까지 실제 호출 경로가 연결되는지 확인한다.
- 선언한 레지스트리와 정책이 런타임 및 CI에서 집행되는지 확인한다.
- 기본 번들에 포함할 역량과 필요할 때 설치할 확장 역량을 구분한다.
- 특정 벤더를 채택하더라도 제품 코드가 벤더 API에 직접 결합되지 않는지 확인한다.
최초 검토 기준은 develop의 cb195f8이며, RP-01~RP-10 구현 결과를 이 문서에
누적 반영했다. 이후 구현으로 경로나 세부 내용이 달라질 수 있으므로, 각 항목은
문서의 경로뿐 아니라 해당 테스트와 아키텍처 게이트로 계속 검증해야 한다.
2. 결론
현재 저장소는 다음 기반이 강하다.
- 런타임 설정과 릴리스 매니페스트 검증
- 도메인, 애플리케이션, 프레젠테이션, outbound adapter의 의존 방향
- 공통 HTTP 실패 형태와 제한된 retry 정책
- 앱 셸, 반응형 내비게이션, 테마, 비동기 상태 표면
- Vitest, Testing Library, MSW, Playwright, axe를 이용한 테스트 계층
- CI 게이트 taxonomy와 호환성·보안·성능·릴리스 계약 문서
그러나 “도메인 기능을 바로 추가할 수 있는 프론트엔드 플랫폼” 기준으로는 아직 중요한 연결부가 빠져 있다. 가장 큰 문제는 공통 기능이 없다는 것보다 이미 있는 기능이 실제 기능 화면의 표준 호출 경로로 조립되지 않았다는 점이다.
특히 다음은 선행 해결이 필요하다.
RP-01~RP-09에서 TypeScript 도구 안전망, application runtime 주입, query/mutation inbound adapter, HTTP 실행 계약과 executable route/release recovery 계약, 제거 가능한 reference 수직 슬라이스, form/page, design system과 i18n 실행 경계와 diagnostics/telemetry production wiring은 구현됐다. 현재 선행 해결 대상은 다음과 같다.
- 공급망의 transitive inventory/license/vulnerability/SBOM/provenance 검증
- optional adapter의 opt-in 경계와 제거 가능한 recipe
따라서 현재 상태를 “프론트 공통부가 모두 구현됐다”고 표현하면 범위가 과장된다. 더 정확한 표현은 다음과 같다.
application API, 서버 상태, 폼, 라우팅, 페이지, 디자인 시스템과 테스트 증적의 표준 수직 경로는 갖춰졌다. 현재 남은 저장소 내부 범위는 공급망 검증과 opt-in adapter recipe이며 실제 hosting·IdP·운영 provider는 프로젝트 통합 범위다.
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, logical execution당 bounded diagnostics | terminal event 중복 방지 계약 유지 |
| 오류 모델 | 부분 준비 | error registry와 normalization 존재 | typed discriminated union과 계층별 mapper |
| 검증 | 준비됨 | 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 선택 |
| 라우팅 | 준비됨 | 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 조합 유지 |
| 디자인 토큰 | 준비됨 | 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 유지 |
| 공급망 검사 | 부분 준비 | 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를 노출한다.
RP-04에서 route registry를 실행 계약으로 전환
platform route 계약과 src/features/installed-feature-contracts.js의 직렬화
가능한 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, home smoke, build와 fixture ID 잔여
0개를 검사한다. 설치 모드에서는 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로 표현한다.
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 | 대표 기술 | 기본 제공할 경계 | 실제 설치 조건 |
|---|---|---|---|
| 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:
DiagnosticsPort로 telemetry와 분리해 구현. closed event/level, allowlist와 bounded/no-op adapter 제공 - 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 정합성은 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 설명에 따라 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 같은 프로젝트별 외부 작업을 포함하지 않는다.