feat: execute HTTP and query runtime contracts

This commit is contained in:
donghyeon-ka
2026-07-26 14:05:12 +09:00
parent 8aaaa033c0
commit ad55e21a3d
29 changed files with 1326 additions and 185 deletions
@@ -33,13 +33,13 @@
특히 다음은 선행 해결이 필요하다.
1. React 화면이 호출할 application input API와 런타임 주입 경계
2. TanStack Query를 사용하는 표준 query/mutation inbound adapter
3. TypeScript 전환 전에 TS 파일까지 검사하도록 만드는 도구 안전망
4. path/query/body projection과 runtime timeout/retry가 정확히 연결된 HTTP 계층
5. 선언과 실행이 일치하는 typed route 계약
6. 전체를 제거할 수 있는 실제 reference feature
7. 폼, 페이지 템플릿, 확장된 디자인 시스템과 컴포넌트 워크벤치
RP-01~RP-03에서 TypeScript 도구 안전망, application runtime 주입,
query/mutation inbound adapter와 HTTP 실행 계약은 구현됐다. 현재 선행 해결
대상은 다음과 같다.
1. 선언과 실행이 일치하는 typed route 계약
2. 전체를 제거할 수 있는 실제 reference feature
3. 폼, 페이지 템플릿, 확장된 디자인 시스템과 컴포넌트 워크벤치
따라서 현재 상태를 “프론트 공통부가 모두 구현됐다”고 표현하면 범위가 과장된다.
더 정확한 표현은 다음과 같다.
@@ -63,13 +63,13 @@
| --- | --- | --- | --- |
| 부트·런타임 설정 | 준비됨 | `src/bootstrap`, runtime schema, release 검사 | 현 상태 유지, TS 전환 시 동일 게이트 유지 |
| 계층 의존 방향 | 부분 준비 | `.dependency-cruiser.cjs`, `src/application/ports` | inbound/outbound 명명과 `contracts` 소유권까지 집행 |
| application facade | 부분 준비 | `create-application.js`는 있으나 `main.jsx`에서 우회 | UI에는 input API만 주입 |
| HTTP client | 부분 준비 | timeout, abort, retry, auth, envelope, Zod가 존재 | path/query, parsed body, runtime 설정, 정리 로직 보완 |
| retry | 부분 준비 | safe/keyed 요청 정책 존재 | retry 소유자 단일화, 설정 연결, telemetry |
| 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 존재 | route/form/domain 경계를 분리하고 실제 parse 결과 사용 |
| 검증 | 부분 준비 | runtime/API Zod parse 결과를 실제 request에 사용 | route/form/domain 경계를 추가 |
| 인증 연동 | 준비됨/프로젝트 선택 | opaque auth owner와 demo seam 존재 | 인증 방식별 recipe; 기본 token 저장소는 추가하지 않음 |
| 서버 상태 | 미제공에 가까운 부분 준비 | QueryClientProvider와 cache port는 존재 | query/mutation hook과 화면 reference flow |
| 서버 상태 | 부분 준비 | 제한된 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 집행 |
@@ -82,7 +82,7 @@
| 국제화 | 미제공 | 한국어 문자열·locale이 하드코딩 | typed message/formatter/locale/RTL 경계 |
| logging/diagnostics | 미제공 | telemetry port는 있으나 logger 없음 | redaction이 적용된 diagnostics/logging 경계 |
| telemetry | 부분 준비 | registry, queue, redaction 존재 | HTTP·boot·cache·storage·route 사건에 실제 연결 |
| 비동기 상태 불변식 | 부분 준비 | 공통 model/surface는 있으나 일부 모순 상태를 허용 | query/mutation 상태 조합과 action latch를 닫음 |
| 비동기 상태 불변식 | 준비 | 배타적 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 전체 제거 검증 |
@@ -94,13 +94,12 @@
### 5.1 P0: 기능 개발을 막는 항목
#### application 계층이 런타임에서 우회된다
#### RP-02에서 application 런타임 우회 해결
`src/bootstrap/composition-root.js` application을 생성하지만
`src/bootstrap/main.jsx`는 이를 라우터에 주입하지 않는다. UI에는 auth, storage,
telemetry 같은 raw outbound dependency와 concrete QueryClient가 전달된다.
`src/application/create-application.js`도 use case 중심 input API보다 outbound
capability를 다시 노출하는 형태다.
`src/bootstrap/composition-root.js`가 만든 typed application input API는
production `ApplicationProvider`에 주입된다. raw auth, storage, telemetry와
release port는 closure 안에 남고 UI는 session, preference, diagnostics와 runtime
query만 사용한다.
목표 상태:
@@ -110,12 +109,13 @@ capability를 다시 노출하는 형태다.
- bootstrap만 concrete outbound adapter를 알고 조합한다.
- 실제 bootstrap부터 reference page까지 연결한 통합 테스트가 있다.
#### 서버 상태 라이브러리는 마운트됐지만 사용할 수 없다
#### RP-03에서 표준 서버 상태 bridge 구현
`QueryClientProvider`는 존재하지만 저장소의 제품 코드에서 `useQuery`
`useMutation`을 사용하지 않는다. 동시에 presentation의 `@tanstack/**` import는
금지돼 있다. 현재 `QueryCachePort`는 명령형 read/write/invalidate만 제공하여
React 구독, 요청 상태, cancellation, optimistic update를 대신할 수 없다.
`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가 거절한다.
목표 상태:
@@ -128,7 +128,7 @@ React 구독, 요청 상태, cancellation, optimistic update를 대신할 수
- loading, empty, refreshing, stale, offline, error, conflict, optimistic rollback을
reference feature에서 보여 준다.
#### TypeScript 전환 전에 검사 도구가 TS를 인식해야 한다
#### RP-01에서 TypeScript 검사 도구 안전망 구현
현재 source는 모두 JS/JSX이고 `strict + allowJs + checkJs`를 사용한다. 이는 좋은
중간 안전망이지만 다음 도구는 TS migration을 그대로 따라가지 못한다.
@@ -143,18 +143,14 @@ TypeScript 전환은
[TypeScript의 JavaScript migration 가이드](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html)
처럼 점진적으로 진행하되, 이 저장소에서는 tooling glob과 CI를 먼저 고쳐야 한다.
#### HTTP 계약에 선언과 실행의 차이가 있다
#### RP-03에서 HTTP 선언과 실행의 차이 해결
현재 HTTP 계층은 공통화 수준이 높지만 다음 정확성 문제가 남아 있다.
- runtime config의 `REQUEST_TIMEOUT_MS`, `MAX_RETRY_ATTEMPTS`가 client 생성에
전달되지 않는다.
- operation path에 path parameter와 search parameter를 투영하는 표준 builder가
다.
- sample filter는 cache key에만 반영되고 실제 요청 URL에는 반영되지 않는다.
- Zod의 parsed/transformed request body 대신 원본 body를 전송한다.
- request validation의 조기 반환 경로에서 timeout/listener 정리가 늦어진다.
- HTTP failure, retry, recovery가 telemetry 사건과 이어지지 않는다.
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
@@ -141,17 +141,26 @@ RP-02 구현으로 다음 경계는 실행 경로에 연결됐다.
- presentation의 direct fetch/browser storage/concrete adapter/TanStack import와
application의 React/concrete adapter import는 negative fixture가 거절한다.
RP-03 구현으로 HTTP와 server-state 경계도 다음처럼 연결됐다.
- `src/presentation/adapters/query`만 TanStack Query import를 허용하며
application query/mutation을 cancellation, invalidation, deduplication,
optimistic rollback과 conflict 해제에 연결한다.
- HTTP request builder는 path escaping과 canonical search를 소유하고 Zod가
변환한 search/body를 실제 request에 사용한다.
- runtime timeout과 max retry attempts가 transport factory에 주입되며
validation, success, abort, timeout과 exhausted retry의 timer/listener
정리를 테스트한다.
- HTTP가 자동 network retry를 소유하고 query/mutation adapter의 vendor retry는
비활성화한다.
후속 브랜치에서 닫아야 할 실행 불일치는 다음과 같다.
1. `QueryClientProvider`는 존재하지만 실제 product route에서
`useQuery` 또는 `useMutation`을 연결하는 query bridge가 없다.
2. route registry의 `paramsSchema`, `searchSchema`, `loadingSurface`,
1. route registry의 `paramsSchema`, `searchSchema`, `loadingSurface`,
`errorSurface`, `chunkId` 일부는 실행 route와 연결되지 않았다.
3. 제거 테스트는 `src/sample/contract-fixture`만 제거하며, sample API
2. 제거 테스트는 `src/sample/contract-fixture`만 제거하며, sample API
operation, Zod schema, mapper, domain model과 query key는 다른 경로에
남는다.
4. runtime의 `REQUEST_TIMEOUT_MS`, `MAX_RETRY_ATTEMPTS`는 검증되지만
concrete HTTP client 구성에 전달되지 않는다.
이 문서의 목표 구조는 기존 기반을 폐기하는 것이 아니라 이러한
불일치를 제거하는 것이다.
@@ -387,6 +387,20 @@ retry 조건:
timer와 event listener는 성공, 실패, validation 조기 반환, external abort 모든
경로에서 정리되어야 한다.
RP-03의 현재 구현은 다음 계약을 자동 검증한다.
- `request-builder.ts`가 path 값을 escape하고 search key를 정렬하며 array 순서를
보존한다.
- operation의 `requestSource`가 search/body schema를 선택하고 Zod의
default/trim 결과만 URL 또는 JSON payload에 전달한다.
- runtime `REQUEST_TIMEOUT_MS``MAX_RETRY_ATTEMPTS`가 transport factory에
주입된다.
- query adapter의 자동 retry는 끄고 HTTP만 bounded network retry를 소유한다.
- `AsyncOverlay`는 refreshing/stale-degraded/mutation-pending/
mutation-conflict를 TypeScript union으로 배타화한다.
- `useApplicationQuery``useApplicationMutation`은 cancellation, stale latch,
duplicate submit, optimistic rollback, conflict resolution을 제공한다.
## 6. 인증과 token 소유권
기본 skeleton은 token manager를 제공하지 않는다.