chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,973 @@
|
||||
# API contract, Schema, Mapper와 Server State platform
|
||||
|
||||
> **정본 안내 (non-authoritative for runtime capability decisions)**
|
||||
>
|
||||
> Runtime Config/boot, Fetch HTTP client, Router, Query/Mutation, realtime 공통 경계, Web Worker,
|
||||
> Service Worker, offline command와 Background Sync의 구현 결정은
|
||||
> [프론트엔드 런타임 Capability 저장소 정합형 구현 결정 폐쇄 상세 설계](./2026-07-30-frontend-runtime-capability-repository-aligned-implementation-closed-deep-design.md)가 정본이다.
|
||||
> 본 문서의 해당 서술이 정본과 충돌하면 정본을 따른다.
|
||||
|
||||
|
||||
- 상태: capability별 current/target 분리, production design accepted
|
||||
- 기준일: 2026-07-28
|
||||
- 범위: REST, GraphQL over HTTP, Connect-Web/Connect, gRPC-Web,
|
||||
Protobuf/REST Gateway, runtime Schema, boundary Mapper, TanStack Query 기반
|
||||
Server State Cache
|
||||
- 관련 결정:
|
||||
- [VD-23 API transport selection과 REST execution](./decisions/VD-23-api-transport-selection-and-rest-execution.md)
|
||||
- [VD-24 Runtime schema와 boundary mapper](./decisions/VD-24-runtime-schema-and-boundary-mapper.md)
|
||||
- [VD-25 Server state cache lifecycle](./decisions/VD-25-server-state-cache-lifecycle.md)
|
||||
- [VD-26 Persisted GraphQL operation](./decisions/VD-26-persisted-graphql-operation.md)
|
||||
- [VD-27 gRPC-Web unary와 server stream](./decisions/VD-27-grpc-web-unary-and-server-stream.md)
|
||||
- [VD-29 Connect-Web와 browser Protobuf runtime](./decisions/VD-29-connect-web-and-browser-protobuf-runtime.md)
|
||||
- [VD-30 Protobuf contract와 REST Gateway](./decisions/VD-30-protobuf-contract-and-rest-gateway.md)
|
||||
- browser Protobuf/gateway 상세 설계:
|
||||
[Protobuf browser transport와 REST Gateway](./protobuf-browser-transport-and-rest-gateway.md)
|
||||
- backend handoff:
|
||||
[Backend API와 Server State contract](./backend-api-and-server-state-contract.md)
|
||||
- 운영 절차:
|
||||
[API contract와 server-state recovery](../operations/api-contract-and-server-state-recovery.md)
|
||||
|
||||
## 1. 목적
|
||||
|
||||
이 문서는 다음 질문을 하나의 production 계약으로 닫는다.
|
||||
|
||||
- REST, GraphQL, Connect와 gRPC-Web 중 무엇을 어디에 사용하는가
|
||||
- Protobuf contract와 REST Gateway가 transport/runtime과 어떻게 분리되는가
|
||||
- request/response가 어느 지점까지 untrusted wire data인가
|
||||
- TypeScript type, generated code와 runtime validation의 역할은 무엇인가
|
||||
- DTO를 domain/application projection으로 누가 변환하는가
|
||||
- server response를 어떤 query identity와 lifecycle로 cache하는가
|
||||
- schema, mapper, transport와 cache가 바뀔 때 어떻게 배포·관측·rollback하는가
|
||||
|
||||
각 protocol은 서로 대체 가능한 URL 호출 문법이 아니다. transport-specific
|
||||
codec, proxy와 failure semantics는 adapter가 소유한다. application은 transport
|
||||
종류, URL, GraphQL document, protobuf message나 TanStack Query를 직접 알지 않고
|
||||
feature-owned gateway와 application input만 호출한다.
|
||||
|
||||
## 2. 상태 모델
|
||||
|
||||
이 문서는 browser data 설계와 동일한 primary current-status literal을 사용한다.
|
||||
|
||||
| primary status | 의미 |
|
||||
| --- | --- |
|
||||
| `COMPOSED` | production bootstrap 또는 설치된 feature 호출 경로에 concrete runtime이 실제 연결돼 있다. |
|
||||
| `AVAILABLE_NOT_COMPOSED` | 실행 가능한 reference runtime과 test가 있지만 production graph에는 연결하지 않았다. |
|
||||
| `DESIGNED_NOT_IMPLEMENTED` | 계약·불변조건·failure와 promotion 기준은 승인됐지만 해당 runtime 또는 필수 orchestration이 없다. |
|
||||
| `NOT_SELECTED` | 제품 요구, owner와 비용이 승인되지 않아 의도적으로 선택하지 않았다. |
|
||||
| `PLATFORM_LIMITED` | target browser/protocol이 요구 semantics를 공통으로 보장하지 못한다. |
|
||||
|
||||
primary status와 다음 readiness 축을 섞지 않는다.
|
||||
|
||||
```text
|
||||
Selection
|
||||
NOT_SELECTED | SELECTED | REMOVING
|
||||
|
||||
TrafficAdmission
|
||||
DISABLED | SHADOW | CANARY | ENABLED
|
||||
|
||||
RuntimeHealth
|
||||
UNKNOWN | AVAILABLE | DEGRADED | UNAVAILABLE | INCOMPATIBLE
|
||||
|
||||
PromotionEvidence
|
||||
MISSING | PARTIAL | COMPLETE | EXPIRED
|
||||
```
|
||||
|
||||
`COMPOSED`는 traffic이 켜졌거나 provider가 conformant라는 뜻이 아니다.
|
||||
`AVAILABLE_NOT_COMPOSED`도 제품 bundle에 dependency가 들어갔다는 뜻이 아니다.
|
||||
|
||||
## 3. 현재 capability ledger
|
||||
|
||||
| capability | primary current status | 현재 증거 | 목표 또는 잔여 |
|
||||
| --- | --- | --- | --- |
|
||||
| installed REST reference vertical | `COMPOSED` | operation registry → request schema → HTTP → envelope/payload schema → mapper → application input → Query 화면 경로 | 아래 REST hardening delta와 실제 제품 provider 계약 |
|
||||
| shared REST JSON executor | `COMPOSED` | path/search/body codec projection, shared deadline/retry-sleep budget, AbortSignal, bounded auth recovery, exact envelope/media/status, safe failure와 diagnostics | 204/304/412 execution join과 actual provider conformance |
|
||||
| REST v2 security/execution baseline | `COMPOSED` | collision-aware operation composition, path placeholder↔codec key exact join, prefix-preserving HTTPS/loopback provider, named bearer/CSRF profile와 credential-mode ceiling, auth fail-before-fetch, bounded JSON, outbound correlation/status·physical-attempt 관측 | cookie-CSRF/CORS provider evidence, 204/304/412 conditional execution과 compatibility artifact |
|
||||
| GraphQL provider-neutral reference adapter | `DESIGNED_NOT_IMPLEMENTED` | source/dependency/codegen/runtime 없음 | persisted-operation-only transport, GraphQL response decoder, mapper binding과 contract harness |
|
||||
| product GraphQL composition | `NOT_SELECTED` | endpoint/schema/persisted manifest/owner 없음 | 제품 query가 REST보다 GraphQL aggregation을 정당화할 때 선택 |
|
||||
| GraphQL batching, subscription, `@defer`/`@stream` | `NOT_SELECTED` | 없음 | 각각 독립 ADR, proxy/browser lifecycle과 cache semantics 필요 |
|
||||
| provider-neutral Browser RPC V3 contract/runtime | `AVAILABLE_NOT_COMPOSED` | operation/profile/schema/mapper/encoder/transport exact join, typed application port, bounded unary retry/deadline/abort, server-stream idle/total/message/terminal/generation fence와 fail-closed unavailable adapter test | selected descriptor/generated client와 protocol-specific bounded transport를 붙이고 actual provider/browser conformance |
|
||||
| gRPC-Web unary reference adapter | `DESIGNED_NOT_IMPLEMENTED` | source/dependency/generated message 없음 | fixed method registry, protobuf codec, trailers/status/deadline와 proxy conformance |
|
||||
| gRPC-Web server-stream reference adapter | `DESIGNED_NOT_IMPLEMENTED` | source 없음 | bounded frame/idle/total budget, sequence/resume application protocol과 stream port |
|
||||
| product gRPC-Web composition | `NOT_SELECTED` | service descriptor/proxy/owner 없음 | browser-facing gRPC-Web gateway가 실제 이점을 줄 때 선택 |
|
||||
| gRPC-Web client-streaming/bidi guarantee | `PLATFORM_LIMITED` | gRPC-Web browser baseline이 해당 semantics를 제공하지 않음 | REST upload, WebSocket/WebTransport 또는 별도 protocol을 선택 |
|
||||
| Protobuf schema/codegen governance | `DESIGNED_NOT_IMPLEMENTED` | `.proto`, Buf config, descriptor와 generated output 없음 | authenticated source, immutable descriptor, deterministic codegen과 compatibility evidence |
|
||||
| Connect-Web unary/server-stream reference adapter | `DESIGNED_NOT_IMPLEMENTED` | `@connectrpc/*`, `@bufbuild/protobuf`, generated service와 provider 없음 | exact Connect/gRPC-Web transport row, bounded decode와 actual browser/provider conformance |
|
||||
| product Connect protocol composition | `NOT_SELECTED` | service/provider/owner 없음 | Protobuf-first backend의 selected browser operation이 있을 때만 선택 |
|
||||
| Connect browser client-streaming/bidi guarantee | `PLATFORM_LIMITED` | Connect protocol 기능과 browser request-stream 지원은 다름 | 별도 duplex/application protocol 선택 |
|
||||
| Protobuf REST Gateway reference contract/harness | `DESIGNED_NOT_IMPLEMENTED` | HttpRule/transcoder/OpenAPI/provider fixture 없음 | selected kind의 deterministic/provider conformance |
|
||||
| product Protobuf REST Gateway composition | `NOT_SELECTED` | route/provider/owner 없음 | curated BFF, grpc-gateway 또는 Envoy transcoder 중 하나와 public HTTP contract 승인 |
|
||||
| feature runtime request/response Schema | `COMPOSED` | Zod request/payload schemas와 installed schema registry가 reference feature에 연결 | byte/depth/node ceiling, unknown-field profile, schema artifact/digest와 multi-protocol source governance |
|
||||
| schema/mapper v2 reference baseline | `COMPOSED` | collision-aware schema codec/mapper contribution install, operation schema/mapper reference resolution, request reject/response strip 방향, bounded collection, typed no-throw mapping result와 operation별 cast-free result guard | actual codec fingerprint, source provenance와 multi-protocol compatibility policy |
|
||||
| generated contract artifact governance | `DESIGNED_NOT_IMPLEMENTED` | `generated-api` recipe만 있고 generator/provider 선택 없음 | OpenAPI/GraphQL/proto source authentication, pinned generation, drift/breaking gate와 N/N-1 |
|
||||
| feature boundary Mapper | `COMPOSED` | installed mapper metadata composer와 response-schema exact join 뒤 typed no-throw MappingResult → immutable domain/application view 실행 | numeric/date/null/enum canonical rules와 generated-artifact join |
|
||||
| TanStack Query memory Server State | `COMPOSED` | QueryClient, cancellation, stale-degraded UI, session-generation cancel/clear fence와 invalidation coordinator | account identity projection과 bounded topic↔namespace many-to-many registry |
|
||||
| reference bound-query/server-state profile | `COMPOSED` | bound definition, strict canonical input, scope-private opaque identity, active lease/LRU/collision/entry-byte ceiling, per-profile policy와 result admission | account projection, conditional HTTP execution join과 pagination composition |
|
||||
| mutation duplicate coordinator baseline | `COMPOSED` | QueryClient runtime/scope 단위 exact semantic input identity로 identical만 join하고 distinct input을 합치지 않으며 late scope result를 폐기 | logical-key serialization과 effect certainty/reconcile |
|
||||
| optimistic ordered-layer runtime | `AVAILABLE_NOT_COMPOSED` | out-of-order commit/rollback, authoritative external update 재적용과 expired-scope 제거 test | 제품 mutation의 deterministic membership/revision contract 승인 뒤 definition에 연결 |
|
||||
| conditional validator CAS sidecar | `AVAILABLE_NOT_COMPOSED` | scope/representation/cache revision exact binding, ETag validation과 bounded capacity test; session 전환 clear는 production infrastructure에 연결 | HTTP If-None-Match/304 query transaction과 query removal lifecycle join |
|
||||
| bounded cursor chain runtime | `AVAILABLE_NOT_COMPOSED` | page invariant, cursor loop, snapshot drift, page/item/byte/cursor ceiling과 abort test | backend CursorPage DTO/next cursor 계약 후 reference/infinite-query binding |
|
||||
| cross-context server-state invalidation | `COMPOSED` | singular opaque topic 기반 invalidate-only coordinator와 session-generation local reset | account projection과 bounded topic↔namespace many-to-many registry |
|
||||
| normalized GraphQL entity cache | `NOT_SELECTED` | 없음 | TanStack operation-result cache로 해결되지 않는 측정된 요구가 있을 때 별도 선택 |
|
||||
| persisted query cache | reference `DESIGNED_NOT_IMPLEMENTED`, product `NOT_SELECTED` | Web Storage persistence는 금지, IndexedDB persister 없음 | VD-13의 scope/retention/restore gate를 별도 통과 |
|
||||
| offline mutation queue | `NOT_SELECTED` | foreground mutation만 존재 | backend idempotency/cursor/conflict protocol과 durable command owner 필요 |
|
||||
|
||||
현재 REST reference는 `Response.json()`이 아니라 byte-bounded reader를 사용하고,
|
||||
external auth owner는 allowlisted header patch만 반환한다. 인증 통합 실패는 fetch
|
||||
전에 닫히며 correlation, success status와 physical attempt가 terminal observation에
|
||||
반영된다. 다만 이 baseline을 conditional response, complete pagination,
|
||||
provider conformance나 GraphQL/gRPC runtime의 증거로 재사용하지 않는다.
|
||||
마찬가지로 Browser RPC V3 공통 coordinator의 `AVAILABLE_NOT_COMPOSED` 판정은
|
||||
vendor wire adapter의 구현 판정이 아니다. `@connectrpc/*`, official grpc-web,
|
||||
generated message와 descriptor가 없는 현재 상태에서 Connect/gRPC-Web 각 row는
|
||||
계속 `DESIGNED_NOT_IMPLEMENTED`다.
|
||||
|
||||
## 4. 최상위 경계
|
||||
|
||||
```text
|
||||
presentation
|
||||
-> feature application input
|
||||
-> feature use case
|
||||
-> feature gateway port
|
||||
-> operation registry
|
||||
-> REST adapter
|
||||
-> GraphQL adapter
|
||||
-> Connect adapter
|
||||
-> gRPC-Web adapter
|
||||
-> bounded wire decoder
|
||||
-> runtime schema / semantic validation
|
||||
-> boundary mapper
|
||||
-> immutable application projection
|
||||
-> server-state query adapter
|
||||
-> registry-owned query identity and policy
|
||||
-> mapped application result only
|
||||
```
|
||||
|
||||
금지 경로:
|
||||
|
||||
```text
|
||||
page -> fetch / GraphQL SDK / generated Connect/gRPC client
|
||||
page -> raw URL / query document / protobuf message
|
||||
transport DTO -> domain or presentation public type
|
||||
Response / GraphQL response / generated message -> Query cache
|
||||
Query cache -> authorization or business conflict authority
|
||||
```
|
||||
|
||||
application port는 use-case 의미를 표현한다. 예를 들어
|
||||
`listResources(filters)`, `createResource(command)`는 허용하지만
|
||||
`executeGraphql(document, variables)`, `grpcCall(service, method, bytes)`와
|
||||
`request(url, options)`는 허용하지 않는다.
|
||||
|
||||
## 5. Protocol-neutral operation contract
|
||||
|
||||
각 외부 호출은 build-time registry의 discriminated row 하나로 고정한다.
|
||||
application caller는 `operationId`와 schema가 허용한 input만 제출한다.
|
||||
|
||||
```text
|
||||
ApiOperationContractV3
|
||||
registryVersion
|
||||
operationId
|
||||
owner
|
||||
protocol = REST | GRAPHQL_HTTP | CONNECT_HTTP | GRPC_WEB
|
||||
semantics = QUERY | COMMAND | SERVER_STREAM
|
||||
authProfileId
|
||||
csrfProfileId
|
||||
replayPolicy = SAFE | IDEMPOTENT | KEYED_COMMAND | NON_REPLAYABLE
|
||||
idempotencyKeyPolicy = NONE | REQUIRED
|
||||
requestSchemaId
|
||||
responseSchemaId
|
||||
mapperId
|
||||
errorProfileId
|
||||
deadlineProfileId
|
||||
retryProfileId
|
||||
serverStateProfileId | null
|
||||
invalidationTopicRefs[] = { topicId, topicVersion }
|
||||
dataClassification
|
||||
compatibility
|
||||
globalApiContractVersion
|
||||
protocolArtifactId
|
||||
minimumServerVersion
|
||||
retirementEpoch | null
|
||||
protocolBinding
|
||||
```
|
||||
|
||||
`protocolBinding`은 transport별 closed union이다.
|
||||
|
||||
```text
|
||||
REST
|
||||
method
|
||||
relativePathTemplate
|
||||
requestProjection
|
||||
requestMediaProfile
|
||||
responseMediaProfile
|
||||
conditionalProfile
|
||||
|
||||
GRAPHQL_HTTP
|
||||
endpointId
|
||||
graphqlHttpProfileRevision
|
||||
persistedEnvelopeProfileId
|
||||
responseStatusMediaProfileId
|
||||
persistedOperationId
|
||||
persistedOperationSha256
|
||||
operationType
|
||||
partialDataPolicy
|
||||
|
||||
GRPC_WEB
|
||||
endpointId
|
||||
clientRuntimeId
|
||||
grpcWebWireSpecRevision
|
||||
transportProfile
|
||||
responseHttpStatusProfileId
|
||||
fullyQualifiedService
|
||||
method
|
||||
rpcKind = UNARY | SERVER_STREAM
|
||||
requestMessageId
|
||||
responseMessageId
|
||||
|
||||
CONNECT_HTTP
|
||||
endpointId
|
||||
clientRuntimeId
|
||||
connectProtocolRevision
|
||||
encoding = PROTO_JSON | PROTO_BINARY
|
||||
requestMethod = POST | GET
|
||||
fullyQualifiedService
|
||||
method
|
||||
rpcKind = UNARY | SERVER_STREAM
|
||||
descriptorArtifactId
|
||||
descriptorDigest
|
||||
```
|
||||
|
||||
registry validation은 다음을 build/boot 전에 거절한다.
|
||||
|
||||
- 중복 operation/mapper/profile ID
|
||||
- protocol과 맞지 않는 binding field
|
||||
- 등록되지 않은 schema, mapper, auth, deadline, retry와 cache profile
|
||||
- `QUERY`인데 replay policy가 `SAFE | IDEMPOTENT`가 아님
|
||||
- `KEYED_COMMAND`인데 idempotency key policy가 `REQUIRED`가 아니거나 backend
|
||||
dedupe/reconcile profile이 없음
|
||||
- `NON_REPLAYABLE`인데 network/401 replay가 enabled
|
||||
- `COMMAND`인데 cache profile이 query data owner로 지정됨
|
||||
- `SERVER_STREAM`인데 ordinary query cache profile을 사용
|
||||
- opening retry가 enabled인데 replay policy가 `SAFE | IDEMPOTENT`가 아님
|
||||
- `KEYED_COMMAND | NON_REPLAYABLE` server stream인데 opening retry가 enabled거나
|
||||
explicit resume/reconcile/dedupe profile이 없음
|
||||
- `SERVER_STREAM`인데 `protocol=CONNECT_HTTP | GRPC_WEB`과
|
||||
`rpcKind=SERVER_STREAM` 조합이 아님. REST SSE와 GraphQL subscription을 이
|
||||
registry 의미로 암묵 등록하지 않음
|
||||
- unsafe REST method 또는 GraphQL mutation인데 CSRF/replay/key 결정이 없음
|
||||
- gRPC-Web client/bidi method
|
||||
- Connect browser client/bidi method 또는 descriptor의 `NO_SIDE_EFFECTS`가 없는
|
||||
Connect GET
|
||||
- absolute URL, runtime GraphQL document 또는 caller-provided service/method
|
||||
- implementation hard ceiling보다 큰 timeout, byte, frame, page와 retry 값
|
||||
- GraphQL operation↔provider의 HTTP revision/envelope/status-media profile 또는
|
||||
Connect/gRPC-Web operation↔provider의 runtime/wire/status/capability tuple
|
||||
mismatch
|
||||
|
||||
현재 installed reference operation은 REST v2 metadata를 사용한다. operation,
|
||||
runtime schema codec과 mapper contribution은 object spread가 아니라 각각의
|
||||
collision-aware composer로 설치되며 duplicate ID를 덮어쓰기 전에 거절한다.
|
||||
boot-time binding 검증은 path placeholder와 codec key, provider/auth/CSRF profile,
|
||||
path/request/response schema와 mapper input schema를 exact resolve한 뒤 immutable
|
||||
registry를 발행한다. GraphQL/Connect/gRPC-Web discriminant와 protocol-specific
|
||||
binding은 해당 reference adapter가 아직 없으므로 source에 구현됐다고 표현하지
|
||||
않는다.
|
||||
|
||||
현재 `API_CONTRACT_VERSION`은 runtime config와 release manifest의 문자열 일치
|
||||
gate다. 목표 contract set은 REST/OpenAPI artifact, GraphQL schema/persisted
|
||||
manifest, protobuf descriptor, runtime schema/mapper registry digest를 포함한
|
||||
bounded manifest를 만들고 global compatibility version과 함께 release tuple에
|
||||
binding한다. 문자열 일치만 actual backend compatibility 증거로 사용하지 않는다.
|
||||
|
||||
## 6. 공통 실행 lifecycle
|
||||
|
||||
```text
|
||||
lookup exact operation
|
||||
-> freeze session/account/runtime generation
|
||||
-> validate and canonicalize application input
|
||||
-> derive exact query/command identity
|
||||
-> allocate total operation deadline
|
||||
-> encode transport request from registry binding
|
||||
-> attach credential/CSRF through approved owner
|
||||
-> execute bounded attempt
|
||||
-> bounded response/frame decode
|
||||
-> validate transport envelope/status
|
||||
-> validate operation DTO/message semantics
|
||||
-> map to immutable application projection
|
||||
-> re-check scope/generation
|
||||
-> return Result
|
||||
-> query adapter may admit mapped value to memory cache
|
||||
```
|
||||
|
||||
현재 v2 auth owner는 `Request`를 반환하지 않고 transport가 만든 immutable
|
||||
request binding에 대해 allowlisted credential patch만 제공한다. 최소한
|
||||
transport는 attach 뒤에도 URL, origin, method, body digest, content headers,
|
||||
idempotency와 conditional binding이 바뀌지 않았음을 다시 검증한다.
|
||||
auth-required operation은 session state가 unauthenticated/integration-failed이거나
|
||||
credential attachment가 실패하면 **fetch 0회**로 닫는다. 동시 401 recovery는
|
||||
session owner의 single-flight 한 번만 공유하며 replay-safe operation만 동일
|
||||
logical deadline/idempotency binding으로 한 번 재실행한다.
|
||||
|
||||
모든 async boundary와 terminal cache write 전에 captured generation을 확인한다.
|
||||
logout/account switch 뒤 끝난 response, mapper와 stream frame은 old runtime
|
||||
결과로 폐기한다.
|
||||
|
||||
deadline은 attempt마다 새로 시작하지 않는다.
|
||||
|
||||
```text
|
||||
total budget
|
||||
= credential attach
|
||||
+ network attempts
|
||||
+ retry delay
|
||||
+ body/frame read
|
||||
+ schema validation
|
||||
+ mapper
|
||||
```
|
||||
|
||||
각 phase에 별도 하위 ceiling을 둘 수 있지만 전체 deadline을 늘릴 수 없다.
|
||||
caller abort, runtime teardown, timeout과 provider cancellation은 서로 다른 safe
|
||||
failure로 정규화한다.
|
||||
|
||||
## 7. Transport 선택 기준
|
||||
|
||||
| 요구 | 기본 선택 | 이유 |
|
||||
| --- | --- | --- |
|
||||
| resource/command, HTTP cache/conditional semantics, 파일 handoff | REST | Web/BFF·CDN·운영 도구와 자연스럽고 failure/status가 명확함 |
|
||||
| 여러 aggregate를 한 화면 shape로 읽고 client별 selection이 유의미 | persisted GraphQL query | allowlisted operation으로 over/under-fetch를 줄일 수 있음 |
|
||||
| Protobuf-first backend의 내부 web UI, unary 또는 bounded server stream | Connect-Web/Connect 우선 평가 | generated descriptor와 Fetch 기반 browser RPC를 재사용 |
|
||||
| 기존 gRPC-Web proxy/conformance 자산 | selected gRPC-Web runtime | runtime별 binary/text/stream capability를 exact profile로 고정 |
|
||||
| ProtoJSON/HttpRule 자체가 승인된 public HTTP contract | generated REST Gateway 검토 | envelope/status/cache/idempotency를 별도 증명 |
|
||||
| 현재 REST envelope·ETag·Range·제품 DTO가 중요 | curated REST BFF 유지 | generated transcoder가 제품 HTTP 의미를 자동 제공하지 않음 |
|
||||
| browser client/bidi streaming | Connect/gRPC-Web 사용 금지 | protocol 자체 기능과 browser 공통 지원을 혼동하지 않음 |
|
||||
| arbitrary ad-hoc query | GraphQL 사용 금지 | cost, authorization, cache identity와 operation governance를 우회 |
|
||||
| 단순 CRUD인데 GraphQL/gRPC dependency만 추가 | REST 유지 | 복잡도와 bundle/proxy 비용을 정당화하지 못함 |
|
||||
|
||||
한 feature가 여러 protocol을 사용할 수 있지만 한 `operationId`는 한 protocol에만
|
||||
binding한다. query read를 shadow 비교하는 경우에도 secondary 결과는 사용자와
|
||||
cache에 반영하지 않는다. command는 protocol 장애를 이유로 자동 failover/replay
|
||||
하지 않는다.
|
||||
|
||||
## 8. REST 설계 요약
|
||||
|
||||
REST 상세 결정은 VD-23이 소유한다. 공통 baseline은 다음과 같다.
|
||||
|
||||
- base origin과 relative path template은 composition/registry가 소유한다.
|
||||
- provider base URL은 HTTPS와 exact origin/path-prefix를 고정하고 userinfo,
|
||||
query와 fragment를 금지한다. path join은 선택한 base prefix를 보존하며
|
||||
leading slash가 prefix를 조용히 제거하지 않는다.
|
||||
- method는 closed union이며 path/search/header/body는 각 runtime schema를 지난다.
|
||||
- replay semantics는 `SAFE`, `IDEMPOTENT`, `KEYED_COMMAND`,
|
||||
`NON_REPLAYABLE`로 method와 교차 검증한다.
|
||||
- caller-provided URL, header, `credentials`, redirect와 cache option을 금지한다.
|
||||
- cookie session이면 unsafe method에 approved CSRF owner가 필요하다.
|
||||
- `SAFE | IDEMPOTENT | KEYED_COMMAND` 중 exact retry profile과 실제 provider
|
||||
replay evidence가 있는 operation만 network retry한다.
|
||||
- keyed retry와 401 recovery는 같은 logical idempotency key를 유지한다.
|
||||
- total deadline, attempts, backoff와 `Retry-After`는 implementation ceiling 안이다.
|
||||
- JSON/error body는 present/valid `Content-Length` advisory preflight와 actual
|
||||
decoded-byte bounded stream reader 뒤 parse한다. encoded transfer cap은
|
||||
BFF/proxy/CDN가 집행한다.
|
||||
- status, content type, response media profile과 envelope 조합을 exact하게 검증한다.
|
||||
- 204, 304, 412, 422와 problem/envelope profile은 operation이 명시한 경우만 허용한다.
|
||||
- strong ETag는 cache identity가 아니라 exact representation revalidation
|
||||
metadata다. validator를 diagnostics에 기록하지 않는다. `Last-Modified`는
|
||||
별도 weak/time validator profile이 승인되기 전 이번 target에 포함하지 않는다.
|
||||
- cursor는 opaque하며 filter/sort/scope와 binding한다. arbitrary URL을
|
||||
`next` link로 따라가지 않는다.
|
||||
- browser HTTP cache와 application ETag/TanStack revalidation owner 중 하나를
|
||||
operation별로 선택한다. 두 cache의 freshness를 서로 추측해 합치지 않는다.
|
||||
- request/response body, URL query, authorization, CSRF, idempotency key와 raw
|
||||
backend copy를 log/telemetry에 넣지 않는다.
|
||||
|
||||
## 9. GraphQL 설계 요약
|
||||
|
||||
GraphQL 상세 결정은 VD-26이 소유한다. reference target은
|
||||
**persisted operation only**다.
|
||||
|
||||
- production bundle은 arbitrary GraphQL document string을 runtime에 받지 않는다.
|
||||
- build artifact가 operation name, stable ID, SHA-256, variables/result schema,
|
||||
schema digest와 owner를 manifest로 만든다.
|
||||
- BFF/router는 allowlist에 없는 ID/hash와 cost/depth limit 초과를 거절한다.
|
||||
- endpoint는 fixed HTTPS registry ID이고 POST가 기본이다.
|
||||
- selected provider가 지원하는 GraphQL-over-HTTP revision과 persisted-envelope
|
||||
extension을 profile에 고정한다. ID/hash-only request를 generic 표준 envelope로
|
||||
가장하지 않는다.
|
||||
- `Accept: application/graphql-response+json`을 우선하고 final URL/media/body cap
|
||||
뒤에는 허용 HTTP status의 GraphQL envelope를 bounded decode한 다음 status/body
|
||||
matrix를 교차 검증한다. legacy `application/json`은 별도 profile이다.
|
||||
- public cacheable query의 GET은 별도 threat/cache review 뒤에만 허용한다.
|
||||
- variables는 request schema와 byte/depth/node ceiling을 통과한다.
|
||||
- HTTP status와 GraphQL `data/errors/extensions`를 두 단계로 검증한다.
|
||||
- default `partialDataPolicy=REJECT`; 승인 operation만 typed completeness metadata와
|
||||
함께 partial을 application으로 투영할 수 있다.
|
||||
- error message, path value와 arbitrary extensions를 노출하지 않고 registered
|
||||
safe code/category만 `AppFailure`로 mapping한다.
|
||||
- APQ miss에서 full document를 자동 전송하지 않는다. manifest/version mismatch로
|
||||
fail-closed하고 coherent frontend/router artifact를 복구한다.
|
||||
- batching은 auth, deadline, cancel, observation과 partial failure owner가
|
||||
별도 승인되기 전 `NOT_SELECTED`다.
|
||||
- subscription, `@defer`, `@stream`은 ordinary query adapter에 암묵적으로 넣지
|
||||
않는다.
|
||||
- Apollo/urql normalized cache는 기본 dependency가 아니다. mapped operation
|
||||
result의 memory owner는 TanStack Query다.
|
||||
|
||||
## 10. Browser Protobuf RPC와 REST Gateway 설계 요약
|
||||
|
||||
축과 선택 기준의 상세 계약은
|
||||
[Protobuf browser transport와 REST Gateway](./protobuf-browser-transport-and-rest-gateway.md)가
|
||||
소유한다. Protobuf는 IDL/serialization, Connect와 gRPC-Web은 browser wire
|
||||
protocol, Connect-Web/official grpc-web은 client runtime, REST Gateway는 HTTP
|
||||
노출 방식이다. 네 이름을 하나의 대안 목록이나 하나의 auto-negotiating
|
||||
executor로 합치지 않는다.
|
||||
|
||||
### 10.1 gRPC-Web
|
||||
|
||||
gRPC-Web 상세 결정은 VD-27이 소유한다. reference target은 unary와 bounded
|
||||
server-stream만 다룬다.
|
||||
|
||||
- checked-in/generated artifact는 pinned proto descriptor/module digest에 묶는다.
|
||||
- vendor generated client/message는 feature adapter 내부에만 존재한다.
|
||||
- fully-qualified service/method와 endpoint는 registry가 고정한다.
|
||||
- client runtime과 gRPC-Web wire-spec revision을 operation/provider에 고정한다.
|
||||
official grpc-web runtime 기준 binary profile은 unary에만 사용하고 server
|
||||
stream은 `grpcwebtext`에 binding한다.
|
||||
- Connect-Web의 `createGrpcWebTransport()`는 Fetch 기반 binary/JSON unary와
|
||||
server stream profile이며 `grpcwebtext` profile이 아니다. official XHR runtime의
|
||||
capability matrix를 이 runtime에 적용하지 않는다. platform-authored custom
|
||||
binary streaming도 세 browser와 actual proxy의 incremental evidence가 있는
|
||||
별도 runtime profile만 허용한다.
|
||||
- frame header, message length, compression flag, total bytes와 frame count를
|
||||
bounded decoder가 검증한다.
|
||||
- HTTP status와 terminal status source를 함께 검사한다. terminal status source는
|
||||
body trailer frame 또는 zero-body trailers-only response header 중 정확히
|
||||
하나이며 중복/충돌을 거절한다.
|
||||
- `grpc-message`, binary error details와 metadata는 allowlist projection 없이
|
||||
application에 반환하지 않는다.
|
||||
- deadline은 `grpc-timeout`과 local total deadline의 더 짧은 값이며
|
||||
AbortSignal이 fetch/stream reader를 cancel한다.
|
||||
- unary `SAFE | IDEMPOTENT | KEYED_COMMAND` 중 provider evidence가 있는
|
||||
operation만 retry한다. stream reconnect는 retry가 아니라
|
||||
server-owned sequence/resume-token을 가진 별도 application protocol이다.
|
||||
- idle deadline, total deadline, max frame/message/count/buffer를 모두 둔다.
|
||||
- client-streaming/bidi는 지원한다고 가장하지 않는다.
|
||||
- Envoy/BFF/Connect/gRPC-Web proxy의 CORS, exposed trailers, content type,
|
||||
auth와 maximum message 설정을 actual provider conformance로 검증한다.
|
||||
- int64/uint64는 JavaScript number로 변환하지 않고 safe integer 범위를
|
||||
증명하거나 decimal string/adapter-private bigint로 mapping한다.
|
||||
|
||||
### 10.2 Connect-Web과 Connect protocol
|
||||
|
||||
Connect 상세 결정은 VD-29가 소유한다.
|
||||
|
||||
- `createConnectTransport()`와 `createGrpcWebTransport()`는 같은 package의 서로
|
||||
다른 wire protocol이다. decoder/status/terminal profile을 공유하지 않는다.
|
||||
- Connect unary의 JSON/binary와 POST를 operation row에 고정한다.
|
||||
- GET은 unary + `NO_SIDE_EFFECTS` descriptor + non-sensitive bounded input +
|
||||
exact URL/cache/CORS profile에서만 별도 승인한다.
|
||||
- Connect server stream은 final EndStream envelope를 확인하기 전 성공이 아니다.
|
||||
- stock runtime의 whole-body decode와 streaming compression 한계를 exact package
|
||||
version evidence로 확인한다. proxy cap이나 custom bounded transport가 없으면
|
||||
production raw-byte ceiling을 완료로 표시하지 않는다.
|
||||
- interceptor의 resolved onion order, auth 이후 final invariant, total deadline,
|
||||
exactly-one retry owner와 cancel handle을 manifest에 고정한다.
|
||||
- `@connectrpc/connect-query`를 기본 도입하지 않는다. generated service/message는
|
||||
adapter-private이고 mapped application value만 기존 TanStack Query에 들어간다.
|
||||
|
||||
### 10.3 Protobuf contract와 REST Gateway
|
||||
|
||||
Protobuf/REST Gateway 상세 결정은 VD-30이 소유한다.
|
||||
|
||||
- authenticated proto/Buf source, descriptor, generator/runtime/plugin version과
|
||||
generated digest를 coherent release artifact로 고정한다.
|
||||
- JSON 노출은 최소 `WIRE_JSON` compatibility를 요구하고 canonical HttpRule
|
||||
route manifest와 generated OpenAPI를 별도 semantic diff한다.
|
||||
- current reference REST envelope에는 curated BFF를 유지한다.
|
||||
- direct grpc-gateway/Envoy transcoder는 ProtoJSON, method/path/query/body,
|
||||
status/error/CORS/cache contract가 그대로 제품 API로 승인된 unary operation에만
|
||||
적용한다.
|
||||
- gateway는 idempotency store, CursorPage snapshot, ETag/304/412, file Range나
|
||||
안전한 domain error vocabulary를 자동 제공하지 않는다.
|
||||
- REST server streaming은 generated gateway의 부수 동작으로 활성화하지 않고
|
||||
framing/terminal/cache를 소유하는 별도 ADR 없이는 `NOT_SELECTED`다.
|
||||
|
||||
## 11. Schema trust boundary
|
||||
|
||||
TypeScript type과 generated code는 compile-time convenience이지 runtime proof가
|
||||
아니다. trust transition은 다음 순서를 지킨다.
|
||||
|
||||
```text
|
||||
untrusted bytes/frames
|
||||
-> bounded transport decoder
|
||||
-> transport envelope/status proof
|
||||
-> operation DTO/message runtime or semantic proof
|
||||
-> ValidatedWireValue (adapter-private)
|
||||
-> boundary mapper
|
||||
-> immutable application projection
|
||||
```
|
||||
|
||||
schema profile은 최소 다음을 고정한다. browser가 직접 집행하는 decoded ceiling과
|
||||
provider/BFF/proxy가 집행하는 wire/encoded ceiling의 owner를 분리한다.
|
||||
|
||||
```text
|
||||
schemaId
|
||||
schemaVersion
|
||||
boundary
|
||||
protocol
|
||||
sourceArtifactId + sourceDigest
|
||||
unknownFieldPolicy
|
||||
providerMaxEncodedBytes
|
||||
maxDecodedBytes
|
||||
maxDepth
|
||||
maxNodes
|
||||
maxStringBytes
|
||||
maxCollectionItems
|
||||
compatibilityPolicy
|
||||
owner
|
||||
```
|
||||
|
||||
unknown-field 기본 정책:
|
||||
|
||||
| 경계 | 정책 |
|
||||
| --- | --- |
|
||||
| request, config, capability, control envelope | `REJECT_UNKNOWN` |
|
||||
| evolvable ordinary response DTO | `STRIP_UNKNOWN` 후 mapper에 전달 |
|
||||
| discriminant/security/authorization 의미를 가진 union | unknown variant 거절 |
|
||||
| unknown data 보존 | adapter 내부 forward proxy가 아닌 한 금지 |
|
||||
|
||||
현재 reference request/path DTO는 `.strict()`로 unknown field를 거절하고,
|
||||
ordinary response DTO는 `.strip()` projection으로 additive server field를
|
||||
cache/domain 경계 밖에 버린다. discriminant/security union을 포함한 다른
|
||||
operation은 각 compatibility profile에 따라 별도로 결정한다.
|
||||
|
||||
## 12. Mapper 경계
|
||||
|
||||
mapper는 transport가 아니라 feature contract가 소유한다.
|
||||
|
||||
```text
|
||||
MapperDefinition
|
||||
mapperId
|
||||
inputSchemaId
|
||||
outputContractId
|
||||
mapperVersion
|
||||
collectionPolicy
|
||||
temporalPolicy
|
||||
numericPolicy
|
||||
nullabilityPolicy
|
||||
owner
|
||||
```
|
||||
|
||||
mapper는 pure, deterministic, side-effect-free이며 다음 union을 반환한다.
|
||||
|
||||
```text
|
||||
MappingResult<T>
|
||||
= { ok: true, value: T }
|
||||
| { ok: false, error: MAPPING_CONTRACT_VIOLATION }
|
||||
```
|
||||
|
||||
현재 reference mapper는 예상 가능한 drift를 throw하지 않고 closed
|
||||
`MappingResult` failure로 반환한다. registry 실행 경계는 예상하지 못한 mapper
|
||||
throw도 fail-closed mapping failure로 바꾸며 raw DTO, value, path와 backend
|
||||
message를 버린다.
|
||||
|
||||
공통 scalar 규칙:
|
||||
|
||||
- opaque ID는 trim/재해석하지 않는 bounded branded string이다.
|
||||
- ISO timestamp는 offset/precision 정책을 검증한 뒤 application instant로
|
||||
변환한다. locale date string과 invalid date normalization을 금지한다.
|
||||
- `int64`, decimal money와 high-precision value는 JSON number로 받지 않는다.
|
||||
- `null`, absent와 empty string/list는 schema와 domain에서 별도 의미로 결정한다.
|
||||
- unknown enum은 domain이 explicit `UNKNOWN`을 소유한 경우만 mapping한다.
|
||||
- collection mapping은 count/byte ceiling 안에서 fail-fast하고 partial array를
|
||||
cache하지 않는다.
|
||||
- mapper는 network, clock, storage, QueryClient, locale formatter와 telemetry를
|
||||
호출하지 않는다.
|
||||
|
||||
## 13. Server State Cache 경계
|
||||
|
||||
TanStack Query는 transport response cache가 아니라 mapped application projection의
|
||||
memory lifecycle owner다.
|
||||
|
||||
cache에 허용:
|
||||
|
||||
- immutable plain application projection
|
||||
- registered query identity로 찾을 수 있는 bounded collection/page
|
||||
- UI가 stale/refresh 상태를 계산하는 library metadata
|
||||
|
||||
cache에 금지:
|
||||
|
||||
- `Response`, raw JSON/GraphQL envelope, generated protobuf message
|
||||
- auth/CSRF/idempotency token, request header와 arbitrary URL
|
||||
- raw ETag, trace/span, backend error/details
|
||||
- File/Blob/stream/native handle
|
||||
- domain service, class instance, function, Promise와 `AbortSignal`
|
||||
|
||||
query profile은 caller가 raw option을 전달하는 대신 registry에서 선택한다.
|
||||
|
||||
```text
|
||||
ServerStateProfileV1
|
||||
profileId
|
||||
queryKeyCodecId
|
||||
scopePersistencePolicyId
|
||||
staleTimeMs
|
||||
gcTimeMs
|
||||
refetchOnFocus
|
||||
refetchOnReconnect
|
||||
networkMode
|
||||
maxResultBytes
|
||||
maxCollectionItems
|
||||
paginationProfileId | null
|
||||
revalidationProfileId | null
|
||||
invalidationTopicRefs[] = { topicId, topicVersion }
|
||||
placeholderPolicy
|
||||
owner
|
||||
```
|
||||
|
||||
query key는 validated/canonical application input과 scope projection으로 만든다.
|
||||
REST URL, GraphQL document/hash, protobuf bytes와 generated message serialization은
|
||||
query key가 아니다. transport 교체가 use-case identity를 바꾸지 않으면 같은
|
||||
application query family를 유지할 수 있지만, old/new representation을 한 cache
|
||||
entry에 shadow write하지 않는다.
|
||||
|
||||
network retry는 transport adapter가 소유하고 Query retry는 기본 `false`다.
|
||||
refresh failure에서 유효한 previous data는 stale-degraded로 유지한다. schema,
|
||||
mapper, scope, authorization와 contract mismatch는 stale data를 계속 노출해도
|
||||
되는지 query profile이 명시해야 하며 기본은 security-sensitive scope에서
|
||||
즉시 숨김/clear다.
|
||||
|
||||
VD-13이 scope/persistence profile, normative key layout, account/session
|
||||
generation과 late-result fence를 소유한다. 이 문서는 그 profile을 exact join하고
|
||||
operation/cache policy, pagination, revalidation과 mutation coherence를 소유한다.
|
||||
|
||||
## 14. Pagination과 conditional revalidation
|
||||
|
||||
cursor pagination은 다음 binding을 갖는다.
|
||||
|
||||
```text
|
||||
PaginationBinding
|
||||
query family fingerprint
|
||||
canonical filters/sort
|
||||
scope fingerprint
|
||||
server snapshot/revision policy
|
||||
page size ceiling
|
||||
opaque next cursor
|
||||
```
|
||||
|
||||
- cursor를 decode하거나 URL로 취급하지 않는다.
|
||||
- `hasMore === (nextCursor !== null)`을 codec에서 강제한다.
|
||||
- single-page cache는 runtime-scoped cursor fingerprint를 semantic key에 포함하고,
|
||||
infinite query만 cursor를 root key에서 제외해 bounded `pageParam`으로 둔다.
|
||||
- max pages/items/estimated bytes를 넘으면 더 불러오지 않는다.
|
||||
- 동일 cursor 반복, loop와 non-progress page를 contract failure로 닫는다.
|
||||
- offset pagination의 insert/delete drift를 자동 deduplicate로 숨기지 않는다.
|
||||
- page merge는 mapper가 보장한 stable identity가 있을 때만 deterministic하다.
|
||||
- previous filters의 page를 새 filter key에 재사용하지 않는다.
|
||||
|
||||
REST `304`는 cached data가 있다는 뜻이 아니라 representation이 바뀌지 않았다는
|
||||
transport 결과다. exact query fingerprint, scope/generation과 cached mapped
|
||||
value에 binding된 validator record가 모두 있을 때만 freshness를 갱신한다.
|
||||
cached value가 없거나 binding이 다르면 unconditional request를 한 번 수행하거나
|
||||
closed failure로 끝낸다.
|
||||
|
||||
GraphQL persisted operation과 gRPC-Web unary는 기본적으로 application-level
|
||||
validator가 없다. backend가 revision을 제공하면 response schema/mapper가 opaque
|
||||
revision을 application revalidation policy로 투영해야 하며 HTTP/gRPC metadata를
|
||||
임의로 ETag처럼 해석하지 않는다.
|
||||
|
||||
## 15. Mutation coherence
|
||||
|
||||
mutation과 query cache는 server commit authority가 아니다.
|
||||
|
||||
```text
|
||||
validate command
|
||||
-> derive logical key + exact command equality/opaque identity token
|
||||
-> runtime/scope coordinator applies concurrency + duplicate admission
|
||||
-> separately acquire invalidation-topic hint-coalescing lease
|
||||
-> cancel exact affected query reads
|
||||
-> capture bounded base cache revision and inverse patch
|
||||
-> install own ordered optimistic layer with revision CAS
|
||||
-> transport derives backend idempotency binding from operation policy
|
||||
-> execute command once with transport-owned retry policy
|
||||
-> success: registered exact seed/compare-and-apply + list/aggregate invalidation
|
||||
-> rejection: remove or invert only own layer with revision CAS
|
||||
-> uncertainty/CAS miss: preserve other commits + invalidate/authoritative refetch
|
||||
-> release invalidation lease + concurrency admission
|
||||
```
|
||||
|
||||
- duplicate submit 정책은 `JOIN_IDENTICAL`, `REJECT_DUPLICATE`,
|
||||
`ALLOW_INDEPENDENT` 중 operation별로 고정하고, 동일성은 전체 validated semantic
|
||||
input의 runtime-private exact equality guard와 opaque identity token으로
|
||||
판정한다.
|
||||
- 같은 hook instance의 Promise dedupe를 server idempotency로 간주하지 않는다.
|
||||
- logical key/identity admission은 local ordering이고 invalidation-topic lease는
|
||||
remote hint coalescing일 뿐이다. 둘 다 backend idempotency authority가 아니다.
|
||||
- optimistic patch는 raw DTO나 generated message를 만들지 않는다.
|
||||
- snapshot item/byte ceiling을 넘으면 optimistic update를 하지 않고 pending UX만
|
||||
제공한다.
|
||||
- 409/GraphQL conflict code/gRPC `ABORTED`는 동일한 safe conflict vocabulary로
|
||||
mapping하되 server revision과 merge policy는 feature use case가 소유한다.
|
||||
- server가 commit한 뒤 local invalidation 실패를 command 실패로 되돌리지 않는다.
|
||||
cache health를 degraded로 기록하고 bounded refetch/recovery를 예약한다.
|
||||
- command의 자동 protocol failover는 중복 side effect 위험 때문에 금지한다.
|
||||
|
||||
## 16. Server streaming과 cache
|
||||
|
||||
gRPC-Web server stream, GraphQL subscription과 incremental delivery는 ordinary
|
||||
queryFn과 다르다.
|
||||
|
||||
현재 installed API operation registry는 terminal REST operation만 소유한다.
|
||||
GraphQL HTTP와 gRPC-Web unary/server-stream은 각 reference adapter가 구현될 때
|
||||
protocol discriminant와 전용 binding으로 확장한다. SSE, WebSocket과 Web Push는
|
||||
VD-28 realtime registry가 소유하며 bounded polling은 registered terminal REST
|
||||
`QUERY`의 scheduling policy이지 새 protocol이나 automatic failover가 아니다.
|
||||
GraphQL `@defer`/`@stream`은 선택될 경우에도 한 HTTP operation의 finite
|
||||
incremental response이며 subscription과 같은 장기 realtime stream이 아니다.
|
||||
|
||||
```text
|
||||
ServerStreamPort
|
||||
open(frozen request, signal)
|
||||
-> AsyncIterable<Result<MappedEvent>>
|
||||
-> close()
|
||||
```
|
||||
|
||||
- frame/event마다 schema, mapper, scope와 generation을 재검증한다.
|
||||
- sequence, duplicate, gap과 resume token은 backend application protocol이다.
|
||||
- bounded queue, high-water mark, overflow, idle/total deadline을 선언한다.
|
||||
- stream event는 registered reducer로 immutable snapshot을 만들거나 query
|
||||
invalidation hint만 발행한다.
|
||||
- partial event를 ordinary query success로 cache하지 않는다.
|
||||
- stream 종료/재connect를 TanStack Query retry로 처리하지 않는다.
|
||||
|
||||
GraphQL subscription은 현재 `NOT_SELECTED`이고, gRPC-Web server-stream은
|
||||
`DESIGNED_NOT_IMPLEMENTED`다.
|
||||
|
||||
gRPC-Web `ServerStreamPort`는 operation-bound outbound stream result일 수 있다.
|
||||
API adapter가 protobuf frame/message decode, semantic schema와 boundary mapper를
|
||||
끝낸 뒤 제품이 runtime-wide notification projection을 명시적으로 선택한
|
||||
branch에서만 mapped event를 VD-28 common coordinator/`FeatureEventInput`에
|
||||
전달한다. protobuf를 `REALTIME_EVENT_V1` JSON으로 감싸지 않고, 첫 event 전
|
||||
opening replay와 이후 resume 규칙은 VD-27이 계속 소유한다.
|
||||
|
||||
## 17. Backend/provider 계약
|
||||
|
||||
구현 owner, 권장 topology, 현재 reference endpoint/envelope, idempotency store,
|
||||
Cursor/ETag/revision과 protocol별 handoff checklist는
|
||||
[Backend API와 Server State contract](./backend-api-and-server-state-contract.md)가
|
||||
소유한다. 아래 표는 frontend 설계가 요구하는 경계 요약이다.
|
||||
|
||||
| 경계 | backend/provider가 제공할 계약 |
|
||||
| --- | --- |
|
||||
| 공통 | authorization, contract version/artifact compatibility, encoded transfer와 decoded payload의 bounded owner, stable error code, correlation/trace projection, idempotency와 rate-limit semantics |
|
||||
| REST | exact method/path/media/status/envelope, CSRF strategy, idempotency retention, cursor binding, ETag/If-None-Match 또는 revision, retry-safe status와 CORS/cache policy |
|
||||
| GraphQL | schema registry, persisted-operation manifest, allowlist/cost/depth enforcement, safe error extension vocabulary, operation retirement과 N/N-1 router rollout |
|
||||
| Connect | proto/descriptor/codegen source, exact Connect-Web runtime/encoding/method, EndStream/status/error, timeout/cancel/compression/CORS와 browser/server conformance |
|
||||
| gRPC-Web | proto/descriptor source, Buf/protoc compatibility policy, gRPC-Web proxy, exact service/method, message/frame ceiling, status/trailer/CORS exposure와 stream resume protocol |
|
||||
| Protobuf REST Gateway | selected gateway kind/version, HttpRule/ProtoJSON/OpenAPI artifact, path/query/body/status/error/header mapping, edge→upstream cancellation과 N/N-1 conformance |
|
||||
| Schema | authenticated source artifact, additive/breaking classification, deprecation window, fixtures and source digest |
|
||||
| Mapper | domain meaning, temporal/numeric/null/enum semantics와 stable identity |
|
||||
| Cache | revision/conflict/idempotency/invalidation semantics; frontend TTL은 authorization 대체가 아님 |
|
||||
|
||||
frontend가 제공하는 Zod schema, generated type과 cache invalidation은 backend
|
||||
authorization, validation, idempotency와 conflict resolution을 대체하지 않는다.
|
||||
|
||||
## 18. Security와 privacy
|
||||
|
||||
- API base/GraphQL/Connect/gRPC-Web endpoint는 HTTPS registry ID로 고정한다.
|
||||
- caller가 URL, header, GraphQL document, service/method와 metadata를 제출하지
|
||||
못한다.
|
||||
- auth owner가 credential을 붙이고 application/query/cache에는 token을 노출하지
|
||||
않는다.
|
||||
- credential attachment 뒤 URL/method/origin/body digest와 registry-owned header
|
||||
binding을 재검증한다. auth integration unavailable 상태에서 request를 보내지
|
||||
않는다.
|
||||
- cookie session의 unsafe request는 CSRF token/header 또는 same-site BFF 정책을
|
||||
operation profile과 provider conformance로 증명한다.
|
||||
- GET/GraphQL variables에 sensitive filter를 넣는 operation은 별도 review 없이
|
||||
만들지 않는다.
|
||||
- response byte/depth/node/string/collection/frame cap으로 resource exhaustion을
|
||||
막는다.
|
||||
- GraphQL cost/depth와 gRPC message cap은 server/proxy에서도 강제한다.
|
||||
- mapper와 cache는 prototype/accessor/class/native object를 받아들이지 않는다.
|
||||
- PII/business ID를 query key, diagnostics label, persisted cache physical key에
|
||||
직접 넣지 않는다. 필요한 identity는 opaque partition/token policy를 쓴다.
|
||||
- command identity token/logical mutation key는 runtime-local control data이며
|
||||
diagnostics, cross-context wire와 persistence에 넣지 않는다.
|
||||
- raw request/response, GraphQL variables/errors, protobuf bytes/metadata, ETag,
|
||||
cursor, idempotency key와 validation value를 관측 데이터에 넣지 않는다.
|
||||
|
||||
## 19. Observability
|
||||
|
||||
허용된 bounded aggregate:
|
||||
|
||||
- operation registry ID, protocol, semantics
|
||||
- outcome/error kind, HTTP status group 또는 gRPC status code allowlist
|
||||
- GraphQL full/partial/rejected outcome
|
||||
- attempt/deadline/duration/encoded-byte/result-item/frame bucket
|
||||
- schema/mapper profile ID와 compatibility outcome
|
||||
- query hit/miss/stale/refetch/eviction bucket
|
||||
- mutation optimistic/rollback/conflict/invalidation outcome
|
||||
- provider/browser/runtime version의 low-cardinality bucket
|
||||
|
||||
금지:
|
||||
|
||||
- URL/path parameter/search/body/header
|
||||
- GraphQL document, variables, response path와 raw error message
|
||||
- protobuf message/metadata/trailer raw value
|
||||
- resource/account/tenant ID, cursor, validator, digest와 cache value
|
||||
|
||||
하나의 logical operation은 terminal observation 하나를 만든다. attempt span은
|
||||
sampling된 내부 detail로만 남기며 terminal success/failure count를 중복시키지
|
||||
않는다.
|
||||
|
||||
## 20. Failure와 fallback
|
||||
|
||||
| 실패 | 기본 결과 |
|
||||
| --- | --- |
|
||||
| registry/schema/mapper 누락 | network 전 fail-closed, operation traffic disable |
|
||||
| incompatible contract artifact | product mount 또는 해당 capability admission 차단 |
|
||||
| response cap/decode/schema mismatch | body/reader cancel, cache write 금지, provider incompatibility |
|
||||
| mapper violation | cache write 금지, safe contract failure |
|
||||
| REST retry exhaustion | stale 허용 profile만 previous data 유지 |
|
||||
| GraphQL persisted operation missing | full document fallback 금지, coherent artifact rollback |
|
||||
| GraphQL partial data | default reject; explicit profile만 completeness와 함께 사용 |
|
||||
| Connect missing/duplicate EndStream 또는 oversize whole body | call cancel, cache write 금지, provider/runtime incompatible |
|
||||
| gRPC-Web proxy/trailer mismatch | reader cancel, provider unavailable/incompatible |
|
||||
| REST Gateway HttpRule/OpenAPI/runtime drift | affected route admission 차단, coherent gateway artifact rollback |
|
||||
| server stream gap/overflow | snapshot 폐기 또는 authoritative refetch |
|
||||
| account/generation mismatch | late result 폐기, old-scope cache write 금지 |
|
||||
| invalidation failure after commit | command 성공 유지, cache degraded + recovery refetch |
|
||||
|
||||
GraphQL, Connect 또는 gRPC-Web failure를 REST로 자동 전환하지 않는다. 사전에 등록된
|
||||
read-only shadow/fallback operation이 있고 동일 authorization/mapper/result
|
||||
contract를 conformance suite로 증명한 경우만 selector가 새 logical query를
|
||||
시작할 수 있다.
|
||||
|
||||
## 21. Rollout과 removal
|
||||
|
||||
```text
|
||||
ADR + registry schema accepted
|
||||
-> deterministic codec/schema/mapper fixture
|
||||
-> provider-neutral adapter and fake
|
||||
-> negative boundary/removal gate
|
||||
-> AVAILABLE_NOT_COMPOSED
|
||||
-> product/provider/operation selection
|
||||
-> bootstrap composition behind TrafficAdmission=DISABLED
|
||||
-> COMPOSED
|
||||
-> shadow/read-only conformance
|
||||
-> browser/provider/operations evidence
|
||||
-> PromotionEvidence=COMPLETE
|
||||
-> CANARY
|
||||
-> ENABLED
|
||||
```
|
||||
|
||||
REST v2 local baseline은 installed reference operation에 연결됐다. 실제 provider
|
||||
traffic은 operation/profile 단위의 conformance와 canary를 거쳐야 하며,
|
||||
conditional/pagination은 backend 계약 없이 enabled하지 않는다.
|
||||
GraphQL/Connect/gRPC-Web/codegen/gateway dependency는 실제 selected operation이
|
||||
없으면 production inventory에 없어야 한다.
|
||||
|
||||
removal:
|
||||
|
||||
1. 신규 operation admission을 닫는다.
|
||||
2. query는 cancel하고 command/stream은 bounded drain 또는 explicit abort한다.
|
||||
3. 해당 invalidation listener, auth attachment와 provider를 close한다.
|
||||
4. current scope의 mapped memory cache를 clear한다.
|
||||
5. operation/schema/mapper/query profile과 generated artifact를 제거한다.
|
||||
6. dependency, config, proxy route, test fixture와 production module inventory가
|
||||
함께 제거됐음을 증명한다.
|
||||
7. backend persisted-operation/method retirement은 N/N-1 client window 뒤에 한다.
|
||||
|
||||
## 22. Test와 promotion evidence
|
||||
|
||||
### Deterministic
|
||||
|
||||
- operation registry closed union/reference/orphan/duplicate
|
||||
- request canonicalization과 query-key identity
|
||||
- timeout/total deadline/retry/idempotency/auth recovery
|
||||
- response byte/depth/node/collection cap
|
||||
- schema unknown-field, scalar, null/enum/numeric/date matrix
|
||||
- mapper success/failure/no raw value leakage
|
||||
- query stale/gc/refetch/pagination/mutation/rollback/generation fence
|
||||
|
||||
### Contract
|
||||
|
||||
- REST OpenAPI/envelope/status/media/cursor/conditional/idempotency
|
||||
- GraphQL schema + persisted manifest + variables/result/error/partial policy
|
||||
- proto descriptor + breaking check + gRPC status/trailer/frame fixture
|
||||
- Connect unary/stream JSON/binary/GET/EndStream/CORS/deadline fixture
|
||||
- HttpRule route manifest + ProtoJSON/OpenAPI/status/error/header mapping fixture
|
||||
- 같은 fixture를 fake, emulator/staging과 actual provider에 실행
|
||||
|
||||
### Browser/integration
|
||||
|
||||
- bootstrap → feature → transport → schema → mapper → Query → UI
|
||||
- AbortSignal/navigation/logout/account switch
|
||||
- CORS/cookie/CSRF/redirect/content-encoding
|
||||
- HTTP/2/proxy/CDN/Connect EndStream/gRPC-Web trailer behavior
|
||||
- offline/reconnect/focus와 stale-degraded UI
|
||||
|
||||
### Fault
|
||||
|
||||
- truncated/oversize/malformed response
|
||||
- slow credential/network/body/schema/mapper phase
|
||||
- 401 recovery, 429, retry exhaustion과 total deadline
|
||||
- GraphQL partial/error/persisted-operation drift
|
||||
- Connect missing/early/duplicate EndStream, whole-body overflow와 compression mismatch
|
||||
- gRPC missing/conflicting terminal status source, corrupt/compressed/oversize
|
||||
frame와 stream gap
|
||||
- REST Gateway route/OpenAPI/runtime rewrite drift와 abort propagation loss
|
||||
- late response, duplicate mutation, optimistic rollback과 invalidation failure
|
||||
|
||||
### Operations
|
||||
|
||||
- operation kill switch
|
||||
- contract artifact N/N-1 rollout과 rollback
|
||||
- provider incompatibility containment
|
||||
- cache scope reset와 stale-data decision
|
||||
- generated client/GraphQL/Connect/gRPC-Web/REST Gateway removal drill
|
||||
|
||||
fake와 generated compile success만으로 actual provider, browser나 operations
|
||||
evidence를 `COMPLETE`로 표시하지 않는다.
|
||||
|
||||
## 23. 설계 우선 work package
|
||||
|
||||
| package | 목표 |
|
||||
| --- | --- |
|
||||
| API-01 | REST v2 operation registry, total deadline, bounded decoder와 conditional/pagination contract |
|
||||
| API-02 | multi-protocol schema artifact/digest governance와 typed Mapper result |
|
||||
| API-03 | strict ServerStateProfile, query-key codec, pagination/revalidation와 mutation policy |
|
||||
| API-04 | persisted-operation-only GraphQL reference adapter와 conformance harness |
|
||||
| API-05 | gRPC-Web unary/server-stream reference adapter와 proxy harness |
|
||||
| API-06 | Connect-Web unary/server-stream reference adapter와 provider harness |
|
||||
| API-07 | Protobuf governance와 selected REST Gateway conformance |
|
||||
| API-08 | atomic composition, readiness, kill switch, runbook와 provider/browser evidence |
|
||||
|
||||
권장 순서는 API-01 → API-02 → API-03이다. API-04~07은 제품 선택과
|
||||
backend/provider 계약이 생긴 branch만 독립적으로 시작한다. GraphQL, Connect,
|
||||
gRPC-Web과 REST Gateway를 “미래 대비” 목적으로 모두 기본 bundle에 설치하지
|
||||
않는다.
|
||||
|
||||
## 24. 완료 기준
|
||||
|
||||
- [ ] 모든 installed operation은 protocol/schema/mapper/cache/error/deadline owner가 있다.
|
||||
- [ ] application/presentation public type에 DTO, GraphQL SDK와 generated protobuf가 없다.
|
||||
- [ ] untrusted byte부터 mapped projection까지 모든 ceiling과 trust transition이 닫혀 있다.
|
||||
- [ ] query key와 실제 request input이 동일 canonical source에서 파생된다.
|
||||
- [ ] transport retry와 Query retry가 중복되지 않는다.
|
||||
- [ ] command idempotency, optimistic patch와 conflict/invalidation owner가 명시돼 있다.
|
||||
- [ ] account/logout/release generation 뒤 late result가 cache에 들어가지 않는다.
|
||||
- [ ] actual REST/GraphQL/Connect/gRPC-Web/Gateway provider에 같은 semantic
|
||||
conformance fixture를 실행한다.
|
||||
- [ ] contract drift, kill switch, rollback과 optional dependency removal drill이 통과한다.
|
||||
- [ ] raw payload/URL/document/message/metadata/validator가 log와 cache에 없다.
|
||||
- [ ] `COMPOSED`와 production-ready/provider-conformant를 같은 의미로 쓰지 않는다.
|
||||
|
||||
## 25. 관련 문서
|
||||
|
||||
- [Frontend ports, adapters, and boundaries](./frontend-ports-adapters-and-boundaries.md)
|
||||
- [Client cache and browser storage](./client-cache-and-storage.md)
|
||||
- [VD-13 Client cache scope와 persistence](./decisions/VD-13-client-cache-scope-and-persistence.md)
|
||||
- [Protobuf browser transport와 REST Gateway](./protobuf-browser-transport-and-rest-gateway.md)
|
||||
- [VD-29 Connect-Web와 browser Protobuf runtime](./decisions/VD-29-connect-web-and-browser-protobuf-runtime.md)
|
||||
- [VD-30 Protobuf contract와 REST Gateway](./decisions/VD-30-protobuf-contract-and-rest-gateway.md)
|
||||
- [Contract compatibility](../contracts/compatibility.md)
|
||||
- [Frontend platform testing strategy](../testing/frontend-platform-testing-strategy.md)
|
||||
Reference in New Issue
Block a user