1094 lines
42 KiB
Markdown
1094 lines
42 KiB
Markdown
# VD-30: Protobuf contract와 REST transcoding gateway
|
|
|
|
- 상태: Accepted design — schema/codegen과 provider runtime implementation pending
|
|
- 결정일: 2026-07-28
|
|
- Protobuf schema/codegen reference governance:
|
|
`DESIGNED_NOT_IMPLEMENTED`
|
|
- provider-neutral REST transcoding reference:
|
|
`DESIGNED_NOT_IMPLEMENTED`
|
|
- product REST Gateway composition: `NOT_SELECTED`
|
|
- 현재 installed REST reference의 기본 public adapter: curated BFF
|
|
- direct REST Gateway default RPC kind: unary only
|
|
- REST server-stream/client-stream/bidirectional-stream composition:
|
|
`NOT_SELECTED`
|
|
- 관련 결정: VD-12, VD-13, VD-14, VD-23, VD-24, VD-25, VD-27, VD-29
|
|
- 상세 설계:
|
|
[Protobuf browser transport와 REST Gateway](../protobuf-browser-transport-and-rest-gateway.md)
|
|
- 상위 API 설계:
|
|
[API contract, Schema, Mapper와 Server State](../api-contract-schema-mapper-and-server-state.md)
|
|
- backend handoff:
|
|
[Backend API와 Server State contract](../backend-api-and-server-state-contract.md)
|
|
|
|
## 배경
|
|
|
|
현재 repository에는 authenticated `.proto` source, Buf module, `buf.yaml`,
|
|
`buf.lock`, `buf.gen.yaml`, immutable descriptor set, generated gateway,
|
|
generated OpenAPI, protobuf runtime dependency와 actual REST transcoding provider
|
|
evidence가 없다. 따라서 이 ADR은 production contract와 구현 순서를 정의하지만
|
|
capability 구현 완료 증거는 아니다.
|
|
|
|
VD-27의 gRPC-Web과 이 ADR의 REST Gateway는 같은 protobuf source를 사용할 수
|
|
있어도 같은 browser protocol이 아니다.
|
|
|
|
```text
|
|
gRPC-Web
|
|
browser generated protobuf client
|
|
-> gRPC-Web binary/text frame
|
|
-> gRPC-Web proxy
|
|
|
|
REST transcoding
|
|
browser ordinary HTTP/JSON client
|
|
-> google.api.http route + ProtoJSON
|
|
-> REST/gRPC transcoding gateway
|
|
```
|
|
|
|
gRPC-Web의 frame, terminal trailer와 client runtime을 REST Gateway에 재사용하지
|
|
않는다. REST Gateway의 route, ProtoJSON, HTTP status/header와 OpenAPI contract를
|
|
gRPC-Web descriptor compatibility로 대신 증명하지도 않는다.
|
|
|
|
현재 installed REST reference는 다음 public 의미를 가진다.
|
|
|
|
- curated `{ success, data, error, meta }` envelope
|
|
- stable frontend error vocabulary와 redaction
|
|
- create의 승인된 `200 | 201`
|
|
- optional HTTP `ETag`, `304`, `If-Match`, `412`
|
|
- `Idempotency-Key`와 effect reconciliation
|
|
- browser-facing auth/CORS/CSRF, body ceiling과 mapper
|
|
|
|
일반 transcoding gateway의 기본 ProtoJSON/status/error 출력은 이 contract와
|
|
동일하지 않다. 따라서 현재 reference operation은 curated BFF를 기본 선택으로
|
|
유지한다. 제품이 direct gateway를 선택할 때는 이 ADR의 별도 artifact와 public
|
|
contract compatibility를 먼저 승인한다.
|
|
|
|
## 결정
|
|
|
|
### 1. Public protocol과 adapter를 혼용하지 않는다
|
|
|
|
지원 가능한 adapter profile은 다음처럼 분리한다.
|
|
|
|
```text
|
|
CURATED_REST_BFF
|
|
PROTOBUF_REST_JSON_GATEWAY
|
|
GRPC_WEB_BINARY
|
|
GRPC_WEB_TEXT
|
|
CONNECT_PROTOCOL
|
|
```
|
|
|
|
한 semantic operation의 한 public endpoint는 하나의 exact profile에만
|
|
binding한다.
|
|
|
|
- HTTP media를 보고 runtime이 BFF와 gateway를 자동 선택하지 않는다.
|
|
- provider 장애를 이유로 command를 다른 protocol로 자동 replay하지 않는다.
|
|
- 같은 path에 BFF envelope와 direct ProtoJSON을 content negotiation으로
|
|
섞지 않는다.
|
|
- gRPC-Web proxy route를 HTTP/JSON REST route로 간주하지 않는다.
|
|
- 내부 gRPC service address, fully-qualified method와 metadata를 browser input으로
|
|
받지 않는다.
|
|
- shadow read는 secondary 결과를 사용자나 cache에 반영하지 않는다.
|
|
|
|
public adapter 변경은 동일 backend method를 사용하더라도 API contract migration
|
|
이다. N/N-1 route, payload, status, error와 cache evidence 없이 in-place 전환하지
|
|
않는다.
|
|
|
|
### 2. Topology와 현재 기본 선택
|
|
|
|
현재 기본:
|
|
|
|
```text
|
|
Browser
|
|
-> CDN / reverse proxy / WAF
|
|
-> same-origin or approved-origin curated BFF
|
|
-> public REST contract enforcement
|
|
-> application service or internal gRPC service
|
|
```
|
|
|
|
제품 승인 뒤 가능한 direct topology:
|
|
|
|
```text
|
|
Browser
|
|
-> CDN / reverse proxy / WAF
|
|
-> authentication / authorization edge
|
|
-> pinned REST transcoding gateway
|
|
-> exact generated route allowlist
|
|
-> ProtoJSON codec
|
|
-> internal gRPC application service
|
|
```
|
|
|
|
gateway deployable이 독립 process인지 application과 같은 process인지는 이 ADR의
|
|
핵심이 아니다. 다음 owner가 논리적으로 분리돼야 한다.
|
|
|
|
- edge: TLS, origin, request admission, encoded byte/rate ceiling
|
|
- gateway: HTTP route, path/query/body mapping, ProtoJSON, status/header projection
|
|
- application service: authorization 재검사, validation, transaction,
|
|
idempotency, pagination, revision과 domain invariant
|
|
- persistence: durable dedupe, cursor/snapshot과 concurrency authority
|
|
|
|
### 3. Protobuf source of truth와 artifact
|
|
|
|
`.proto` source와 dependency는 authenticated immutable input이어야 한다.
|
|
|
|
```text
|
|
ProtoContractArtifactV1
|
|
artifactId
|
|
contractVersion
|
|
sourceRepository
|
|
sourceCommit
|
|
sourceTreeDigest
|
|
bufModuleRef
|
|
bufModuleCommit
|
|
bufModuleDigest
|
|
bufLockDigest
|
|
bufImageId
|
|
bufImageDigest
|
|
fileDescriptorSetId
|
|
fileDescriptorSetDigest
|
|
includeImports
|
|
includeSourceInfo
|
|
httpRuleSource = PROTO_ANNOTATION | SERVICE_CONFIG
|
|
httpRuleArtifactId
|
|
httpRuleArtifactDigest
|
|
productionBaselineArtifactId
|
|
productionBaselineDigest
|
|
protoJsonProfileId
|
|
generatedGatewayArtifactId
|
|
generatedGatewayDigest
|
|
generatedOpenApiArtifactId
|
|
generatedOpenApiDigest
|
|
minimumBackendVersion
|
|
minimumGatewayVersion
|
|
retirementEpoch | null
|
|
owner
|
|
```
|
|
|
|
불변조건:
|
|
|
|
- moving branch, tag, label이나 `latest`를 production generation input으로 사용하지
|
|
않는다.
|
|
- Buf Schema Registry를 사용하면 exact module commit/digest를 release에 고정한다.
|
|
- `buf.lock`의 direct/transitive dependency commit과 digest를 check-in한다.
|
|
- `google/api/annotations.proto`, `http.proto`, well-known type dependency가 실제
|
|
descriptor input과 일치해야 한다.
|
|
- Buf image와 native `google.protobuf.FileDescriptorSet`을 구분해 저장한다.
|
|
- descriptor digest는 exact artifact byte의 integrity/provenance 식별자다.
|
|
digest가 다르다는 사실만으로 semantic breaking을 추측하지 않고 compatibility
|
|
gate를 별도로 실행한다.
|
|
- production server reflection을 source/codegen 공급망으로 사용하지 않는다.
|
|
reflection은 승인된 diagnostics profile에서만 사용할 수 있다.
|
|
- artifact download는 authenticated channel, integrity verification과 audit
|
|
evidence를 가져야 한다.
|
|
|
|
### 4. Schema evolution과 breaking policy
|
|
|
|
공통 protobuf governance:
|
|
|
|
- field number 재사용 금지
|
|
- 제거한 field number와 name을 모두 reserve
|
|
- package/service/method full name 안정성
|
|
- 삭제 대신 deprecate와 retirement window
|
|
- proto3 optional/presence와 implicit default 의미 명시
|
|
- enum zero value와 unknown numeric value 처리
|
|
- 제거한 enum name/number reserve
|
|
- oneof absence, 새 case와 unknown case 처리
|
|
- map iteration order를 identity, signature와 pagination order로 사용하지 않음
|
|
- repeated/map/message depth와 decoded byte ceiling
|
|
- Timestamp/Duration valid range, nanos와 timezone projection
|
|
- int64/uint64를 JavaScript `number`로 무조건 변환하지 않음
|
|
- bytes base64와 maximum decoded length
|
|
- `Any`, `Struct`, `Value`는 type allowlist와 semantic validator 없이는 public
|
|
contract에 넣지 않음
|
|
|
|
breaking baseline은 mutable main branch가 아니라 마지막으로 production에
|
|
promote된 immutable artifact다.
|
|
|
|
Buf category:
|
|
|
|
- generated gateway, generated server/client 또는 여러 언어 source compatibility를
|
|
보호하는 기본은 `FILE`이다.
|
|
- source compatibility를 의도적으로 별도 관리해도 HTTP/JSON을 제공하는 module은
|
|
최소 `WIRE_JSON`을 통과해야 한다.
|
|
- JSON을 사용하는 module에서 `WIRE`만 통과한 것을 REST compatibility 증거로
|
|
사용하지 않는다.
|
|
|
|
`google.api.http`와 다른 custom option의 의미는 generic Buf breaking rule만으로
|
|
닫히지 않는다. 따라서 다음을 별도 gate로 둔다.
|
|
|
|
- canonical route manifest semantic diff
|
|
- generated OpenAPI compatibility diff
|
|
- HTTP method/path/body/query/response body 변경 분류
|
|
- additional binding 추가/삭제/충돌 검사
|
|
- path escaping/unescaping profile diff
|
|
- auth, idempotency, pagination, conditional과 status profile diff
|
|
|
|
schema wire-safe 변경도 제품 의미, validation ceiling, pagination이나 auth scope를
|
|
바꾸면 semantic breaking일 수 있다. Buf success를 mapper/domain compatibility
|
|
승인으로 사용하지 않는다.
|
|
|
|
### 5. Code generation provenance와 reproducibility
|
|
|
|
generation toolchain:
|
|
|
|
```text
|
|
CodegenToolchainV1
|
|
bufCliVersion
|
|
protocVersion
|
|
plugins[]:
|
|
pluginId
|
|
pluginVersion
|
|
pluginRevision
|
|
binaryDigest
|
|
optionsDigest
|
|
invocationStrategy
|
|
protobufRuntimeId
|
|
protobufRuntimeVersion
|
|
grpcRuntimeVersion
|
|
gatewayRuntimeVersion
|
|
languageRuntimeVersion
|
|
osImageDigest
|
|
```
|
|
|
|
- Buf CLI, protoc, every local/remote plugin과 runtime을 exact version으로 pin한다.
|
|
- BSR remote plugin은 upstream version뿐 아니라 repackaging revision도 고정한다.
|
|
- version을 생략해 latest plugin을 선택하지 않는다.
|
|
- local plugin은 package lock과 executable digest를 함께 기록한다.
|
|
- plugin option, managed-mode override, include/exclude path/type와 invocation
|
|
strategy를 manifest에 포함한다.
|
|
- generated code보다 오래된 incompatible protobuf runtime을 조립하지 않는다.
|
|
- `buf.gen.yaml`의 output 경계는 생성 전에 clean되며 generated directory에
|
|
hand-written file을 두지 않는다.
|
|
|
|
CI pipeline:
|
|
|
|
```text
|
|
authenticated immutable source
|
|
-> dependency lock verification
|
|
-> buf format/lint
|
|
-> descriptor/image build
|
|
-> breaking check against production baseline
|
|
-> HttpRule route manifest generation/diff
|
|
-> pinned gateway/OpenAPI/runtime code generation
|
|
-> clean regenerate
|
|
-> worktree diff == 0
|
|
-> generated compile/typecheck/test
|
|
-> N/N-1 binary + ProtoJSON fixtures
|
|
-> provider conformance
|
|
-> artifact sign/attest/SBOM
|
|
-> release contract set
|
|
```
|
|
|
|
“Buf를 사용했다”는 이유만으로 deterministic generation이라 부르지 않는다.
|
|
동일 input/toolchain에서 clean regenerate diff가 0이고 artifact digest가
|
|
일치하는 증거를 남긴다. timestamp, absolute local path와 host-dependent output을
|
|
생성물에서 금지하거나 normalize한다.
|
|
|
|
generated gateway/server/message/OpenAPI는 application/domain model이 아니다.
|
|
frontend가 generated OpenAPI client를 선택하더라도 transport adapter 밖으로
|
|
generated DTO를 노출하지 않고 VD-24 runtime schema와 mapper를 유지한다.
|
|
|
|
### 6. ProtoJSON profile
|
|
|
|
모든 gateway operation은 immutable `ProtoJsonProfile`에 binding한다.
|
|
|
|
```text
|
|
ProtoJsonProfileV1
|
|
profileId
|
|
specificationRevision
|
|
useProtoNames
|
|
discardUnknownOnRequest
|
|
emitUnpopulated
|
|
useEnumNumbers
|
|
allowPartial
|
|
int64Projection
|
|
bytesProjection
|
|
nullPresencePolicy
|
|
duplicateFieldPolicy
|
|
timestampPolicy
|
|
durationPolicy
|
|
fieldMaskPolicy
|
|
anyTypeAllowlist[]
|
|
maxDecodedMessageBytes
|
|
maxDepth
|
|
maxNodes
|
|
maxStringBytes
|
|
maxRepeatedItems
|
|
maxMapEntries
|
|
```
|
|
|
|
reference 기본:
|
|
|
|
```text
|
|
useProtoNames = false
|
|
discardUnknownOnRequest = false
|
|
emitUnpopulated = false
|
|
useEnumNumbers = false
|
|
allowPartial = false
|
|
int64Projection = DECIMAL_STRING
|
|
bytesProjection = STANDARD_BASE64
|
|
nullPresencePolicy = PROTOJSON_UNSET
|
|
duplicateFieldPolicy = REJECT_OR_PROVIDER_PINNED
|
|
```
|
|
|
|
wire 의미:
|
|
|
|
- JSON field name은 lowerCamelCase가 기본이며 proto field name 입력 수용 여부와
|
|
출력 option을 fixture로 고정한다.
|
|
- int64/uint64 출력은 decimal string이다. frontend mapper가 safe integer 범위를
|
|
증명하지 않는 한 `number`로 변환하지 않는다.
|
|
- bytes는 base64를 decode하기 전 encoded/decoded ceiling을 모두 확인한다.
|
|
- enum output은 name이며 unknown numeric value를 application enum으로 추측하지
|
|
않는다.
|
|
- serializer는 presence가 없는 default field를 기본 생략한다.
|
|
- `null`은 일반 field의 명시적 domain null이 아니라 protobuf unset으로
|
|
해석될 수 있다. nullable domain 의미는 별도 message/oneof/mapper가 소유한다.
|
|
- unknown request field를 production에서 조용히 버리지 않는다.
|
|
- response additive rollout은 old frontend가 unknown field를 받는 N/N-1 fixture를
|
|
통과한 뒤 writer를 활성화한다.
|
|
- duplicate field를 사용하는 client 의미에 의존하지 않는다. selected runtime이
|
|
reject하지 않으면 deterministic last-value behavior와 WAF/parser differential
|
|
test를 별도 증명한다.
|
|
- Timestamp/Duration/FieldMask/wrapper well-known type의 special JSON mapping을
|
|
ordinary object로 추측하지 않는다.
|
|
|
|
gateway decoder success는 semantic validation proof가 아니다. application
|
|
service와 frontend boundary는 range, presence, enum/oneof, collection ceiling과
|
|
domain invariant를 각각 다시 검증한다.
|
|
|
|
### 7. HttpRule과 route manifest
|
|
|
|
기본 source of truth는 RPC의 `google.api.http` annotation이다.
|
|
|
|
외부 gRPC API service-config YAML은 proto를 직접 변경할 수 없거나 동일 service를
|
|
서로 다른 public API로 투영해야 하는 승인된 경우에만 사용한다.
|
|
|
|
- annotation과 service-config를 같은 RPC에 중복 정의하지 않는 것이 기본이다.
|
|
- service-config가 matching annotation을 override할 수 있으므로 artifact digest,
|
|
selector, precedence와 generated route 결과를 release에 포함한다.
|
|
- config rule의 last-one-wins에 의존해 중복 selector를 숨기지 않는다.
|
|
- `generate_unbound_methods`는 기본 false다.
|
|
|
|
```text
|
|
RestGatewayRouteManifestV1
|
|
manifestId
|
|
protoContractArtifactId
|
|
httpRuleArtifactDigest
|
|
routes[]:
|
|
semanticOperationId
|
|
fullyQualifiedService
|
|
method
|
|
rpcKind
|
|
httpMethod
|
|
pathTemplate
|
|
additionalBindingIndex | null
|
|
pathFieldBindings
|
|
queryFieldBindings
|
|
requestBodyBinding | null
|
|
responseBodyBinding | null
|
|
protoJsonProfileId
|
|
operationProfileId
|
|
routeManifestDigest
|
|
```
|
|
|
|
HttpRule 불변조건:
|
|
|
|
- GET/DELETE operation은 request body를 갖지 않는다.
|
|
- path variable은 허용된 non-repeated primitive field에만 binding한다.
|
|
- body field는 top-level이며 path field와 중복되지 않는다.
|
|
- `body: "*"`이면 path에 포함되지 않은 모든 field가 body로 가고 query
|
|
parameter가 없음을 계약한다.
|
|
- body를 생략하면 request body가 없고 나머지 field가 query로 가는 것을
|
|
계약한다.
|
|
- repeated primitive query의 repeated-key 의미와 maximum item을 고정한다.
|
|
- nested message query flattening은 approved field set과 depth ceiling을 가진다.
|
|
- additional binding 안에 additional binding을 중첩하지 않는다.
|
|
- 한 RPC의 additional binding이 다른 RPC route와 충돌하지 않는다.
|
|
- reserved path character와 multi-segment parameter는 selected gateway의
|
|
`AllExceptReserved` 동등 unescaping profile과 actual fixture를 통과한다.
|
|
- caller-provided custom HTTP verb와 arbitrary route를 허용하지 않는다.
|
|
|
|
PATCH가 FieldMask를 사용하면 gateway 자동-population 여부와 `body: "*"` 예외를
|
|
profile에 고정한다. partial update의 최종 authorization, writable field allowlist와
|
|
mask validation은 application service가 수행한다.
|
|
|
|
### 8. OpenAPI contract
|
|
|
|
generated OpenAPI는 documentation 부산물이 아니라 browser-facing compatibility
|
|
artifact다.
|
|
|
|
```text
|
|
GatewayOpenApiArtifactV1
|
|
generatorId
|
|
generatorVersion
|
|
generatorRevision
|
|
generatorOptionsDigest
|
|
routeManifestDigest
|
|
protoJsonProfileId
|
|
errorProfileId
|
|
statusProfileId
|
|
documentDigest
|
|
```
|
|
|
|
- production-stable pipeline은 selected grpc-gateway
|
|
`protoc-gen-openapiv2` version을 pin한다.
|
|
- 공식 상태가 alpha인 OpenAPI v3 generator는 별도 승인과 toolchain conformance
|
|
전까지 production source of truth로 승격하지 않는다.
|
|
- generated document를 authenticated immutable artifact로 배포한다.
|
|
- generated path, parameter, request/response schema, status와 error가 actual
|
|
gateway fixture와 일치해야 한다.
|
|
- runtime response rewriter처럼 OpenAPI에 표현되지 않는 변환은 direct gateway의
|
|
기본 public contract에서 금지한다.
|
|
- custom transformation이 필요하면 curated BFF를 선택하거나 별도 hand-written
|
|
contract와 bidirectional conformance를 소유한다.
|
|
- OpenAPI client 생성 여부와 무관하게 frontend runtime boundary validation을
|
|
제거하지 않는다.
|
|
|
|
`google.api.HttpBody`는 raw/binary body를 전달할 수 있지만 일반 application
|
|
REST에 암묵적으로 사용하지 않는다. 파일 upload/download, Range, presigned URL,
|
|
background transfer와 CDN은 VD-12/VD-14의 transfer 계약을 유지한다.
|
|
|
|
### 9. Gateway provider와 operation definition
|
|
|
|
```text
|
|
RestTranscodingProviderProfileV1
|
|
providerId
|
|
gatewayImplementation
|
|
gatewayVersion
|
|
fixedHttpsOrigin
|
|
basePathPrefix
|
|
routeManifestId
|
|
routeManifestDigest
|
|
protoContractArtifactId
|
|
protoJsonProfileId
|
|
openApiArtifactId
|
|
authProfileId
|
|
credentialsMode
|
|
corsProfileId
|
|
incomingHeaderProfileId
|
|
outgoingHeaderProfileId
|
|
pathUnescapingProfileId
|
|
errorProfileId
|
|
statusProfileId
|
|
requestAdmissionProfileId
|
|
redirect = ERROR
|
|
referrerPolicy
|
|
owner
|
|
```
|
|
|
|
```text
|
|
ProtobufRestOperationV1
|
|
protocol = PROTOBUF_REST_JSON_V1
|
|
semanticOperationId
|
|
providerId
|
|
fullyQualifiedService
|
|
method
|
|
rpcKind = UNARY
|
|
requestMessageId
|
|
responseMessageId
|
|
protoContractArtifactId
|
|
descriptorDigest
|
|
routeManifestDigest
|
|
httpMethod
|
|
pathTemplate
|
|
requestBodyBinding | null
|
|
responseBodyBinding | null
|
|
requestSemanticSchemaId
|
|
responseSemanticSchemaId
|
|
mapperId
|
|
successStatusMediaProfileId
|
|
errorProfileId
|
|
authProfileId
|
|
csrfProfileId
|
|
replayPolicy
|
|
idempotencyProfileId | null
|
|
deadlineProfileId
|
|
retryProfileId
|
|
paginationProfileId | null
|
|
conditionalProfileId | null
|
|
serverStateProfileId | null
|
|
maxUrlBytes
|
|
maxRequestHeaderBytes
|
|
maxRequestBodyBytes
|
|
maxDecodedRequestBytes
|
|
maxResponseHeaderBytes
|
|
maxEncodedResponseBytes
|
|
maxDecodedResponseBytes
|
|
maxResponseItems
|
|
owner
|
|
```
|
|
|
|
composition이 거절:
|
|
|
|
- unknown service/method/message/descriptor
|
|
- descriptor, route manifest, ProtoJSON과 OpenAPI digest mismatch
|
|
- operation과 provider profile mismatch
|
|
- client-streaming 또는 bidirectional RPC
|
|
- 별도 streaming ADR 없는 server-streaming RPC
|
|
- caller-provided URL, service, method, metadata와 header
|
|
- body/query/path/status가 generated route와 다름
|
|
- ceiling, deadline, auth, error 또는 owner가 없음
|
|
- non-replayable command에 retry
|
|
- keyed command에 durable idempotency evidence가 없음
|
|
|
|
### 10. Unary-only default와 streaming 경계
|
|
|
|
direct REST Gateway reference는 unary RPC만 허용한다.
|
|
|
|
- unary request 한 개와 unary response 한 개
|
|
- exact terminal HTTP status/media/body
|
|
- bounded response decode
|
|
|
|
grpc-gateway의 server stream은 newline-separated JSON chunk envelope와 terminal
|
|
error body를 사용할 수 있다. 이것은 ordinary REST JSON response도, VD-27의
|
|
gRPC-Web framing도 아니다.
|
|
|
|
따라서 다음은 `NOT_SELECTED`다.
|
|
|
|
- REST server stream
|
|
- REST client stream
|
|
- REST bidirectional stream
|
|
- streaming `HttpBody` file delivery를 ordinary API adapter로 사용
|
|
|
|
제품이 server stream을 요구하면 chunk media type, message delimiter,
|
|
partial-delivery meaning, terminal error, backpressure, sequence/gap/resume,
|
|
idle/total deadline과 browser buffering을 별도 ADR과 actual provider evidence로
|
|
설계한다. SSE, gRPC-Web, WebSocket 또는 bounded polling과도 요구를 다시
|
|
비교한다.
|
|
|
|
### 11. Direct gateway와 curated BFF 선택
|
|
|
|
| 요구 | 기본 선택 |
|
|
| --- | --- |
|
|
| `google.api.http`와 canonical ProtoJSON이 그대로 public contract | direct gateway 가능 |
|
|
| simple unary resource operation, 별도 envelope/aggregation 없음 | direct gateway 가능 |
|
|
| 현재 `{ success, data, error, meta }` envelope 유지 | curated BFF |
|
|
| 제품별 DTO projection, aggregation 또는 여러 backend orchestration | curated BFF |
|
|
| cookie session, CSRF와 same-origin session lifecycle | curated BFF |
|
|
| stable safe error vocabulary와 PII redaction | curated BFF 권장 |
|
|
| custom `201/204`, HTTP `304/412`와 conditional cache transaction | curated BFF 또는 별도 custom gateway |
|
|
| idempotency receipt/status/reconcile UX | curated BFF 권장 |
|
|
| file transfer, Range, presigned URL, background download와 Image CDN | transfer/BFF 경계 |
|
|
| server streaming | 별도 protocol ADR |
|
|
|
|
direct gateway는 다음을 모두 증명할 때만 선택한다.
|
|
|
|
- public DTO가 canonical ProtoJSON과 일치
|
|
- no custom success envelope
|
|
- exact google.api.http route가 product REST route와 일치
|
|
- safe error/status mapping이 frontend failure vocabulary와 호환
|
|
- edge/gateway/upstream auth owner가 명확
|
|
- operation이 unary이고 bounded
|
|
- custom header/status/cache 의미가 제한적이고 artifact에 표현 가능
|
|
- actual browser/gateway/backend conformance 통과
|
|
|
|
BFF 뒤에서 internal gRPC를 호출하는 것은 direct gateway가 아니다. public
|
|
contract owner는 계속 BFF다.
|
|
|
|
### 12. Request admission과 semantic validation
|
|
|
|
request 처리:
|
|
|
|
```text
|
|
TLS/WAF encoded admission
|
|
-> fixed route/method
|
|
-> origin/auth/CSRF/rate admission
|
|
-> header/path/query/body byte ceiling
|
|
-> HttpRule binding
|
|
-> strict ProtoJSON decode
|
|
-> generated request message
|
|
-> application semantic validation
|
|
-> authorization
|
|
-> application service
|
|
```
|
|
|
|
- edge는 encoded request/body/header와 request rate ceiling을 소유한다.
|
|
- gateway는 URL/path/query/body mapping과 decoded protobuf message ceiling을
|
|
소유한다.
|
|
- application service는 generated message를 신뢰하지 않고 domain validation과
|
|
authorization을 다시 수행한다.
|
|
- path와 body에 같은 field가 중복되거나 충돌하면 fail-closed한다.
|
|
- unknown query/body field, malformed percent encoding과 parser differential을
|
|
거절한다.
|
|
- decompression ratio, nested depth, repeated/map count와 string/bytes length에
|
|
별도 ceiling을 둔다.
|
|
- error body도 bounded writer와 redaction을 통과한다.
|
|
|
|
### 13. Success status, body와 media
|
|
|
|
operation은 exact success matrix를 가진다.
|
|
|
|
```text
|
|
GatewaySuccessProfileV1
|
|
allowedHttpStatuses[]
|
|
responseMediaTypes[]
|
|
bodyPolicy = REQUIRED | EMPTY | PROTOJSON_MESSAGE
|
|
responseHeaderAllowlist[]
|
|
responseBodyMessageId | null
|
|
```
|
|
|
|
- default transcoded unary success를 임의로 `201` 또는 `204`로 추측하지 않는다.
|
|
- `google.protobuf.Empty`의 default `200 {}`와 `204`는 같은 contract가 아니다.
|
|
- `201 Created`, `Location`, `204 No Content`가 필요하면 generated OpenAPI,
|
|
gateway hook, actual fixture와 rollback을 함께 승인한다.
|
|
- upstream metadata로 status를 바꾸는 기능은 registered response type/method와
|
|
allowlisted value만 허용한다.
|
|
- caller가 `x-http-code` 같은 internal control metadata를 제출하지 못한다.
|
|
- status hook 뒤 body가 이미 write된 상태에서 status를 변경하지 않는다.
|
|
- response body rewrite가 OpenAPI에 반영되지 않으면 direct public gateway에서
|
|
사용하지 않는다.
|
|
- HTML proxy error, redirect login page와 unexpected media를 ProtoJSON으로
|
|
해석하지 않는다.
|
|
|
|
현재 curated REST envelope를 direct ProtoJSON으로 조용히 바꾸지 않는다. direct
|
|
gateway adoption에는 새 operation/version 또는 증명된 compatibility facade가
|
|
필요하다.
|
|
|
|
### 14. Error contract와 redaction
|
|
|
|
gateway error profile:
|
|
|
|
```text
|
|
GatewayErrorProfileV1
|
|
grpcToHttpStatusMapId
|
|
publicEnvelope = GOOGLE_RPC_STATUS | CURATED_APP_FAILURE
|
|
safeReasonDomainAllowlist[]
|
|
safeDetailTypeAllowlist[]
|
|
exposeGrpcMessage = false
|
|
exposeUnknownDetails = false
|
|
maximumErrorBytes
|
|
maximumDetails
|
|
```
|
|
|
|
- gRPC canonical status와 HTTP status mapping을 exact gateway revision에 pin한다.
|
|
- routing `404/405/400`과 upstream application error를 구분한다.
|
|
- `Status.message`는 machine branch key가 아니다.
|
|
- arbitrary `Status.message`, stack, SQL/vendor copy, internal address와 raw
|
|
`Any.details`를 frontend에 노출하지 않는다.
|
|
- frontend branch는 registered stable reason/code/category만 사용한다.
|
|
- `ErrorInfo`를 사용하면 `(reason, domain)`과 approved metadata key를 versioned
|
|
vocabulary로 관리한다.
|
|
- authorization detail은 resource 존재 여부나 policy reason을 누출하지 않는다.
|
|
- malformed/oversize upstream error는 safe generic provider failure로 닫는다.
|
|
- unary custom error handler와 routing error handler가 actual OpenAPI/fixture와
|
|
일치해야 한다.
|
|
- stream error handler는 unary handler와 다르므로 streaming이 선택되기 전
|
|
production guarantee로 표시하지 않는다.
|
|
|
|
partial success/error는 기본 거절한다. bulk partial semantics가 필요하면
|
|
long-running operation이나 별도 result contract를 선택한다.
|
|
|
|
### 15. Authentication, authorization, CORS와 CSRF
|
|
|
|
gateway는 authorization authority를 자동 제공하지 않는다.
|
|
|
|
- edge 또는 gateway가 credential을 검증해도 application service가 operation과
|
|
resource authorization을 다시 수행한다.
|
|
- bearer profile은 issuer, audience, signature algorithm, time claims, revocation과
|
|
tenant binding을 검증한다.
|
|
- cookie profile은 `Secure`, `HttpOnly`, explicit `SameSite`, host/path scope와
|
|
unsafe method CSRF를 함께 승인한다.
|
|
- credentialed CORS에 wildcard origin을 사용하지 않는다.
|
|
- exact origin/method/header/expose 목록과 bounded preflight cache를 fixture로
|
|
검증한다.
|
|
|
|
grpc-gateway의 incoming `Authorization`은 upstream gRPC metadata로 전달될 수
|
|
있다. 이 동작을 다음처럼 다룬다.
|
|
|
|
- public bearer를 upstream service가 검증하는 profile인지 명시
|
|
- edge가 identity를 변환하면 original credential 전달/제거 owner를 별도 설계
|
|
- 외부 caller가 내부 principal, tenant, role, trace와 policy header를 spoof하지
|
|
못하도록 edge에서 제거
|
|
- incoming/outgoing header matcher는 closed allowlist
|
|
- hop-by-hop, cookie, raw credential, internal debug와 arbitrary `grpc-*`
|
|
metadata를 forwarding하지 않음
|
|
- auth attach 뒤 final origin/path/method/body digest와 registry binding 재검증
|
|
|
|
current cookie/same-origin이나 curated auth/error/meta 요구가 크면 BFF를 선택한다.
|
|
|
|
### 16. Deadline과 cancellation
|
|
|
|
```text
|
|
browser total deadline
|
|
-> edge admission
|
|
-> gateway request context
|
|
-> upstream gRPC deadline
|
|
-> application/downstream deadline propagation
|
|
```
|
|
|
|
- gRPC는 deadline이 기본으로 자동 설정된다고 가정하지 않는다.
|
|
- frontend total deadline 안에서 edge/gateway/upstream phase가 더 짧은 ceiling을
|
|
가진다.
|
|
- retry/backoff가 total deadline을 늘리지 않는다.
|
|
- gateway는 remaining budget을 upstream deadline으로 전달하고 queue/connection
|
|
time을 제외하지 않는다.
|
|
- browser AbortSignal/navigation/logout와 connection close를 구분된 safe
|
|
cancellation reason으로 정규화한다.
|
|
- HTTP disconnect가 upstream gRPC cancel과 application work stop으로 이어지는지
|
|
actual provider에서 검증한다.
|
|
- server handler와 spawned/downstream work는 cancellation을 주기적으로 확인하고
|
|
불필요한 계산/I/O를 중단한다.
|
|
- deadline/cancel 뒤 timer, request body, response writer와 upstream call을
|
|
정리한다.
|
|
|
|
cancellation은 이미 commit된 effect를 rollback하지 않는다. client와 server의
|
|
success 판단이 다를 수 있으므로 command는 cancellation 결과만으로 effect
|
|
없음을 선언하지 않는다.
|
|
|
|
### 17. Retry와 idempotency
|
|
|
|
semantic replay policy:
|
|
|
|
```text
|
|
SAFE
|
|
IDEMPOTENT
|
|
KEYED_COMMAND
|
|
NON_REPLAYABLE
|
|
```
|
|
|
|
- gateway의 모든 upstream gRPC call이 HTTP POST라는 이유로 retry-safe라
|
|
간주하지 않는다.
|
|
- frontend, CDN/proxy, gateway, gRPC client와 service retry 중 한 owner만 physical
|
|
retry를 수행한다.
|
|
- automatic retry 기본 대상은 bounded unary safe read다.
|
|
- transactional sequence, `ABORTED`, validation, auth와 non-replayable command를
|
|
transport가 자동 retry하지 않는다.
|
|
- response header/body가 시작된 뒤 retry하지 않는다.
|
|
- attempt count, backoff, sleep, elapsed와 total deadline ceiling을 둔다.
|
|
|
|
idempotency transport는 operation마다 하나를 선택한다.
|
|
|
|
```text
|
|
HTTP_IDEMPOTENCY_KEY
|
|
PROTO_REQUEST_ID
|
|
NONE
|
|
```
|
|
|
|
- 현재 curated REST의 `Idempotency-Key`를 direct gateway가 사용하면 exact
|
|
incoming metadata mapping과 upstream binding을 정의한다.
|
|
- protobuf `request_id` field를 사용하면 HttpRule body/query 위치, UUID format,
|
|
payload fingerprint와 replay window를 정의한다.
|
|
- 두 identity를 동시에 받아 어느 값을 authority로 쓸지 추측하지 않는다.
|
|
- gateway process-local map/lock을 idempotency store로 사용하지 않는다.
|
|
- principal/tenant + semantic operation/version + key + canonical request
|
|
fingerprint를 durable store에 binding한다.
|
|
- same key/same fingerprint replay는 authoritative prior result/receipt를
|
|
반환한다.
|
|
- same key/different fingerprint는 stable conflict다.
|
|
- commit 뒤 response loss는 새 effect를 만들지 않는다.
|
|
- `EFFECT_UNKNOWN`은 새 key나 다른 protocol retry가 아니라 status/reconcile
|
|
endpoint로 확인한다.
|
|
- retention은 frontend recovery window보다 길고 quota/abuse policy를 가진다.
|
|
|
|
### 18. Pagination
|
|
|
|
HttpRule/gateway는 protobuf field를 HTTP query/body로 옮길 뿐 pagination 의미를
|
|
구현하지 않는다.
|
|
|
|
paginated list는 첫 public version부터 다음을 포함한다.
|
|
|
|
```text
|
|
ListRequest
|
|
pageSize
|
|
pageToken
|
|
|
|
ListResponse
|
|
items[]
|
|
nextPageToken
|
|
```
|
|
|
|
application/backend 책임:
|
|
|
|
- finite default와 maximum page size
|
|
- negative size rejection과 oversized coercion/approved policy
|
|
- opaque URL-safe page token
|
|
- principal/tenant/filter/sort/contract/snapshot binding
|
|
- token integrity, expiry와 key rotation
|
|
- stable total ordering과 immutable unique tie-breaker
|
|
- insertion/deletion 중 snapshot or documented consistency
|
|
- duplicate/gap/loop 방지
|
|
- next token empty만 end-of-collection 의미
|
|
- page token이 authorization을 대체하지 않음
|
|
|
|
unpaginated RPC에 field만 추가하고 default response를 일부로 줄이는 변경은
|
|
behavioral breaking이다. 현재 reference array response를 page object로 바꾸려면
|
|
새 operation/schema와 N/N-1 migration을 사용한다.
|
|
|
|
frontend는 arbitrary next URL을 따라가지 않고 opaque token만 registered query
|
|
input에 투영한다. raw token을 log, analytics와 persistent cache에 넣지 않는다.
|
|
|
|
### 19. ETag, revision과 HTTP conditional
|
|
|
|
다음 두 profile을 구분한다.
|
|
|
|
```text
|
|
PROTO_RESOURCE_ETAG
|
|
HTTP_REPRESENTATION_CONDITIONAL
|
|
```
|
|
|
|
`PROTO_RESOURCE_ETAG`:
|
|
|
|
- resource/request message의 `string etag` field
|
|
- server-owned output
|
|
- strong/weak meaning 명시
|
|
- mismatch는 selected gRPC status, 보통 `ABORTED`
|
|
- ProtoJSON body/query field로 왕복
|
|
|
|
`HTTP_REPRESENTATION_CONDITIONAL`:
|
|
|
|
- HTTP response `ETag`
|
|
- request `If-None-Match` 또는 `If-Match`
|
|
- unchanged read의 `304` empty body
|
|
- failed write precondition의 approved `412 | 409`
|
|
- exact principal/tenant-visible representation과 encoding variant binding
|
|
- `Cache-Control`, `Vary`, CORS expose/preflight와 frontend cache transaction
|
|
|
|
proto `etag` field가 존재한다고 gateway가 HTTP `ETag`, `304` 또는 `If-Match`를
|
|
자동 제공한다고 간주하지 않는다. body ETag를 HTTP validator로 투영하려면
|
|
allowlisted response header hook, request header mapping, status conversion,
|
|
generated/curated OpenAPI와 actual cache fixture가 필요하다.
|
|
|
|
현재 REST reference의 TanStack/application conditional transaction은
|
|
`HTTP_REPRESENTATION_CONDITIONAL`이다. direct gateway가 이 의미를 정확히
|
|
구현하지 않으면 curated BFF를 유지한다.
|
|
|
|
frontend는 validator와 mapped cached value의 scope/query identity/representation
|
|
version/cache revision이 모두 일치할 때만 304를 success로 처리한다. raw validator를
|
|
log, diagnostics와 metric label에 넣지 않는다.
|
|
|
|
### 20. Cache와 server state
|
|
|
|
gateway adoption이 frontend query identity를 바꾸지 않게 semantic operation
|
|
input을 canonical source로 유지한다.
|
|
|
|
- generated path/query serializer와 query-key codec가 같은 validated application
|
|
input에서 파생
|
|
- ProtoJSON DTO와 generated message를 TanStack Query cache에 저장하지 않음
|
|
- schema/semantic validation과 mapper가 끝난 immutable application projection만
|
|
저장
|
|
- auth/session/tenant generation이 cache scope를 소유
|
|
- late response와 retry result는 generation fence 뒤 cache에 들어가지 않음
|
|
- protocol migration 중 BFF와 gateway 결과를 같은 key에 섞지 않음
|
|
- pagination token과 ETag는 domain resource data와 별도 bounded sidecar owner
|
|
- mutation commit 뒤 exact invalidation/reconcile owner 필요
|
|
|
|
### 21. Observability, privacy와 abuse control
|
|
|
|
허용 dimension:
|
|
|
|
```text
|
|
semantic operation ID
|
|
gateway/provider profile ID
|
|
proto contract artifact version
|
|
route manifest version
|
|
ProtoJSON profile ID
|
|
status group / safe error code
|
|
attempt bucket
|
|
duration and size bucket
|
|
admission/cancellation stage
|
|
```
|
|
|
|
금지:
|
|
|
|
- raw Authorization/cookie/CSRF/idempotency key
|
|
- raw path/query/body와 protobuf message
|
|
- cursor/page token/ETag/revision
|
|
- `Status.message`, unknown details와 validation value
|
|
- principal, tenant, resource ID와 IP의 unbounded label
|
|
- descriptor/proto source URL credential
|
|
|
|
gateway는 operation별 request rate, concurrent upstream call, body/response byte,
|
|
CPU-heavy JSON decode, error volume와 upstream saturation ceiling을 가진다.
|
|
|
|
최소 metric:
|
|
|
|
- route admission/rejection
|
|
- ProtoJSON malformed/unknown/oversize
|
|
- descriptor/route/OpenAPI artifact mismatch
|
|
- gRPC→HTTP status/error category
|
|
- deadline/cancel propagation stage
|
|
- retry attempts/amplification
|
|
- idempotency replay/conflict/unknown effect
|
|
- page token invalid/expired
|
|
- conditional hit/miss/precondition failure
|
|
- provider/browser conformance version
|
|
|
|
### 22. Provider conformance와 promotion evidence
|
|
|
|
#### Deterministic
|
|
|
|
- Buf format/lint/breaking
|
|
- exact production baseline
|
|
- source/dependency/image/descriptor/toolchain/generated digest
|
|
- clean regenerate diff 0
|
|
- gencode/runtime compatibility
|
|
- generated import boundary와 SBOM
|
|
- HttpRule custom-option semantic diff
|
|
|
|
#### Contract
|
|
|
|
- descriptor N/N-1 binary fixture
|
|
- ProtoJSON scalar/presence/null/unknown/enum/int64/bytes/time matrix
|
|
- method/path/body/query/additional binding
|
|
- path percent-encoding and `AllExceptReserved` behavior
|
|
- generated OpenAPI vs actual request/response
|
|
- exact success/media/status/body
|
|
- safe unary/routing error mapping
|
|
- auth/header allowlist
|
|
- idempotency/pagination/conditional fixtures
|
|
|
|
#### Integration/browser
|
|
|
|
- actual gateway version + actual gRPC backend
|
|
- Chromium/Firefox/WebKit fetch
|
|
- CORS/preflight, bearer 또는 cookie+CSRF
|
|
- reverse proxy/CDN/WAF body and header ceiling
|
|
- AbortSignal/disconnect/deadline propagation
|
|
- navigation/logout/account switch
|
|
- HTTP/1.1, HTTP/2와 intermediary error behavior
|
|
|
|
#### Fault/security
|
|
|
|
- malformed/oversize/truncated JSON
|
|
- unknown/duplicate field와 parser differential
|
|
- route collision, encoded slash와 path traversal
|
|
- spoofed identity/internal control header
|
|
- upstream unavailable/deadline/status mismatch
|
|
- error detail/stack/PII redaction
|
|
- response started 뒤 failure/retry
|
|
- commit 뒤 response loss와 idempotency reconciliation
|
|
- invalid/expired/wrong-scope page token
|
|
- body ETag와 HTTP conditional 혼동 방지
|
|
|
|
#### Performance/operations
|
|
|
|
- bounded JSON transcoding CPU/memory
|
|
- maximum request/response와 concurrent load
|
|
- slow client/upstream and queue saturation
|
|
- canary, kill switch와 traffic admission
|
|
- coherent proto/gateway/OpenAPI/frontend/backend rollback
|
|
- generated/proxy/dependency removal drill
|
|
|
|
fake gateway, generated compile과 OpenAPI 생성 success만으로 actual provider
|
|
conformance를 완료 처리하지 않는다.
|
|
|
|
### 23. Rollout
|
|
|
|
1. backend/product owner가 direct gateway가 필요한 operation과 BFF 대비 이점을
|
|
승인한다.
|
|
2. authenticated proto source, exact production baseline과 artifact owner를
|
|
확정한다.
|
|
3. Buf/codegen toolchain, breaking category와 HttpRule source를 pin한다.
|
|
4. descriptor, route manifest, ProtoJSON profile와 OpenAPI artifact를 생성한다.
|
|
5. provider/gateway version, auth/error/status/deadline ceiling을 승인한다.
|
|
6. unary safe read 한 개로 reference conformance harness를 만든다.
|
|
7. frontend adapter를 `AVAILABLE_NOT_COMPOSED`로 판정한다.
|
|
8. product composition 뒤 `TrafficAdmission=DISABLED`로 배포한다.
|
|
9. internal/staging shadow read는 cache/UI에 반영하지 않고 BFF result와 semantic
|
|
diff를 관찰한다.
|
|
10. actual browser canary를 낮은 비율로 시작한다.
|
|
11. error/latency/schema/cache SLO와 artifact digest가 정상일 때 단계적으로
|
|
확대한다.
|
|
12. keyed command는 durable dedupe/reconcile fault evidence 뒤 별도 canary한다.
|
|
13. server stream은 계속 `NOT_SELECTED`다.
|
|
|
|
한 release에서 proto writer, gateway route, error envelope와 frontend consumer를
|
|
동시에 breaking 전환하지 않는다. additive reader-first/writer-later와 N/N-1
|
|
window를 사용한다.
|
|
|
|
### 24. Rollback과 removal
|
|
|
|
rollback:
|
|
|
|
- 신규 gateway operation traffic admission 중지
|
|
- in-flight safe read cancel
|
|
- command는 cancel 뒤 authoritative receipt/status로 effect reconcile
|
|
- gateway-scoped query/validator/pagination sidecar clear 또는 invalidate
|
|
- last-known-good proto/descriptor/route/OpenAPI/gateway/backend/frontend artifact를
|
|
coherent하게 복구
|
|
- approved BFF read fallback은 새 logical request와 독립 total deadline으로만
|
|
실행
|
|
- command를 BFF나 gRPC-Web로 자동 replay하지 않음
|
|
|
|
removal:
|
|
|
|
1. operation traffic과 route retirement window 시작
|
|
2. N/N-1 frontend/backend/gateway 소비자를 확인
|
|
3. route manifest와 traffic admission에서 제거
|
|
4. frontend operation/schema/mapper/cache profile 제거
|
|
5. generated gateway/OpenAPI/message output 제거
|
|
6. gateway route, proxy/CORS/auth/config 제거
|
|
7. plugin/runtime/package와 SBOM entry 제거
|
|
8. proto RPC는 다른 소비자가 없고 deprecation window가 끝난 뒤 제거
|
|
9. message field/enum number와 name은 제거 시 reserve
|
|
10. descriptor/baseline artifact는 audit/retention 정책에 따라 보존
|
|
11. build, bundle, dependency, provider probe와 removal drill 통과
|
|
|
|
### 25. Backend/provider handoff checklist
|
|
|
|
- [ ] proto source/module owner와 immutable production baseline이 있다.
|
|
- [ ] `buf.lock`, Buf image, native descriptor와 digest가 release에 binding됐다.
|
|
- [ ] Buf/protoc/plugin version/revision/options/runtime이 pin됐다.
|
|
- [ ] `FILE` 또는 승인된 최소 `WIRE_JSON` breaking gate가 있다.
|
|
- [ ] HttpRule custom-option와 generated OpenAPI semantic diff gate가 있다.
|
|
- [ ] ProtoJSON profile과 N/N-1 fixture가 있다.
|
|
- [ ] direct gateway와 curated BFF 중 operation별 public owner가 하나다.
|
|
- [ ] exact unary service/method/route/status/media/error가 allowlist됐다.
|
|
- [ ] bearer 또는 cookie+CSRF/CORS provider evidence가 있다.
|
|
- [ ] incoming/outgoing header와 internal identity spoofing 방지가 검증됐다.
|
|
- [ ] edge/gateway/upstream byte/rate/depth/count ceiling이 있다.
|
|
- [ ] total deadline, browser abort와 downstream cancellation evidence가 있다.
|
|
- [ ] retry owner가 하나이고 command idempotency/reconcile가 durable하다.
|
|
- [ ] pagination을 선택했다면 token/snapshot/authorization contract가 있다.
|
|
- [ ] conditional을 선택했다면 body ETag와 HTTP validator profile이 분리됐다.
|
|
- [ ] generated OpenAPI와 actual gateway wire fixture가 일치한다.
|
|
- [ ] actual browser/gateway/backend canary, kill switch와 rollback drill이 통과했다.
|
|
- [ ] generated code, proxy route와 dependency removal drill이 통과했다.
|
|
|
|
## 규범 기준
|
|
|
|
- [Protocol Buffers ProtoJSON format](https://protobuf.dev/programming-guides/json/)
|
|
- [Protocol Buffers proto3 language guide](https://protobuf.dev/programming-guides/proto3/)
|
|
- [Protocol Buffers cross-version runtime guarantee](https://protobuf.dev/support/cross-version-runtime-guarantee/)
|
|
- [Buf breaking-change detection](https://buf.build/docs/breaking/)
|
|
- [Buf breaking rules and categories](https://buf.build/docs/breaking/rules/)
|
|
- [Buf code generation](https://buf.build/docs/generate/)
|
|
- [Buf `buf.gen.yaml` v2](https://buf.build/docs/configuration/v2/buf-gen-yaml/)
|
|
- [Buf dependency management](https://buf.build/docs/bsr/module/dependency-management/)
|
|
- [Buf FileDescriptorSet](https://buf.build/docs/bsr/module/descriptor/)
|
|
- [Google API HTTP and gRPC transcoding](https://google.aip.dev/127)
|
|
- [Google API errors](https://google.aip.dev/193)
|
|
- [Google API request identification](https://google.aip.dev/155)
|
|
- [Google API pagination](https://google.aip.dev/158)
|
|
- [Google API resource freshness validation](https://google.aip.dev/154)
|
|
- [Google API `HttpRule`](https://docs.cloud.google.com/endpoints/docs/grpc-service-config/reference/rpc/google.api)
|
|
- [gRPC-Gateway introduction](https://grpc-ecosystem.github.io/grpc-gateway/docs/tutorials/introduction/)
|
|
- [gRPC-Gateway FAQ](https://grpc-ecosystem.github.io/grpc-gateway/docs/faq/)
|
|
- [gRPC-Gateway customization](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/customizing_your_gateway/)
|
|
- [gRPC-Gateway PATCH](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/patch_feature/)
|
|
- [gRPC-Gateway HttpBody](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/httpbody_messages/)
|
|
- [gRPC-Gateway OpenAPI v3 status](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/openapi_v3/)
|
|
- [gRPC deadlines](https://grpc.io/docs/guides/deadlines/)
|
|
- [gRPC cancellation](https://grpc.io/docs/guides/cancellation/)
|
|
- [gRPC status codes](https://grpc.io/docs/guides/status-codes/)
|
|
|
|
## 완료 기준
|
|
|
|
- Protobuf source/dependency/descriptor/toolchain/generated artifact의 provenance와
|
|
digest가 production baseline에 binding된다.
|
|
- Buf binary/source compatibility와 HttpRule/OpenAPI semantic compatibility를
|
|
별도 gate로 검증한다.
|
|
- ProtoJSON field/int64/bytes/enum/null/presence/unknown 의미가 exact profile로
|
|
닫힌다.
|
|
- direct REST Gateway와 gRPC-Web/curated BFF가 서로 다른 protocol artifact와
|
|
state machine을 가진다.
|
|
- 현재 installed REST reference는 별도 product 승인 전 curated BFF를 유지한다.
|
|
- direct gateway operation은 unary, fixed route/service/method와 bounded
|
|
request/response에만 compose된다.
|
|
- status/body/error/auth/header/deadline/cancel/retry가 actual provider fixture와
|
|
일치한다.
|
|
- gateway가 idempotency, pagination, revision과 authorization authority를
|
|
대신한다고 표시하지 않는다.
|
|
- body ETag와 HTTP conditional semantics를 혼동하지 않는다.
|
|
- generated OpenAPI와 actual wire contract drift가 promotion을 차단한다.
|
|
- actual browser/gateway/backend conformance, canary, kill switch, coherent rollback과
|
|
removal drill이 통과한다.
|
|
- schema/codegen implementation 전에는 `DESIGNED_NOT_IMPLEMENTED`, product 선택
|
|
전에는 REST Gateway를 `NOT_SELECTED`로 유지한다.
|