477 lines
30 KiB
Markdown
477 lines
30 KiB
Markdown
# 프론트엔드 플랫폼 역량 재검토
|
|
|
|
## 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은 구현됐다. 현재 선행 해결
|
|
대상은 다음과 같다.
|
|
|
|
1. 공급망의 transitive inventory/license/vulnerability/SBOM/provenance 검증
|
|
2. 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 가이드](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html)
|
|
처럼 점진적으로 진행하되, 이 저장소에서는 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로 표현한다.
|
|
|
|
```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 | 대표 기술 | 기본 제공할 경계 | 실제 설치 조건 |
|
|
| --- | --- | --- | --- |
|
|
| 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 구현이 있다. 다만 다음 두 대안 중 하나를 명시적으로
|
|
선택해야 한다.
|
|
|
|
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.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](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. 관련 상세 문서
|
|
|
|
- [프론트 포트·어댑터와 기능 경계](./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)
|