Files
clean-architecture-frontend…/docs/architecture/backend-api-and-server-state-contract.md
T

29 KiB

Backend API와 Server State handoff contract

1. 문서의 경계

이 문서는 이 frontend template이 실제 제품 backend와 연결될 때 backend가 제공해야 하는 구조, wire contract, 상태 의미와 운영 증거를 정의한다. 특정 언어·framework·cloud 제품을 강제하지 않는다. Spring, Nest/Fastify, Go, .NET 또는 다른 stack을 사용해도 아래 불변조건은 동일하다.

이 repository에는 backend source, database migration, identity provider, GraphQL schema/router, protobuf descriptor, Connect/gRPC-Web runtime/proxy, REST transcoder와 실제 provider evidence가 없다. 따라서 이 문서는 backend 구현 완료 증거가 아니다.

파일 업로드·다운로드, object storage, presigned URL, multipart와 Image CDN은 별도 server file 문서가 소유한다. 이 문서는 ordinary REST/GraphQL/Connect/ gRPC-Web application API, Protobuf REST Gateway와 frontend Server State 계약만 소유한다.

2. 권장 논리 구조

Browser
  -> CDN / reverse proxy / WAF
       -> Browser-facing Web API or BFF
            -> authentication + authorization
            -> exact operation registry
            -> request schema / byte / rate limit
            -> REST controller
            -> optional persisted GraphQL router
            -> optional Connect browser RPC gateway
            -> optional gRPC-Web gateway
            -> optional Protobuf REST transcoder
                 -> application service
                      -> command transaction
                      -> query/read-model service
                      -> idempotency coordinator
                      -> revision/validator owner
                      -> outbox/event owner
                           -> primary database
                           -> idempotency store
                           -> read replica/read model
                           -> event broker when selected

Browser-facing contract owner와 내부 service contract owner를 분리한다.

  • Browser API/BFF는 CORS, cookie/CSRF 또는 bearer, public DTO, envelope, body ceiling, status/media와 redaction을 소유한다.
  • Application service는 authorization 재검사, transaction, idempotency, conflict, revision과 domain invariant를 소유한다.
  • Persistence adapter는 SQL/NoSQL/Redis vendor type, row version과 cursor implementation을 외부 DTO에 노출하지 않는다.
  • GraphQL router, Connect/gRPC-Web gateway와 REST transcoder는 선택 adapter다. 다른 protocol로 임의 fallback하거나 frontend에 내부 service address를 노출하지 않는다.

작은 제품은 이 논리 모듈을 하나의 deployable로 구현할 수 있다. deployable을 나누는 것보다 transaction/idempotency/authorization owner가 하나로 명확한지가 우선이다.

3. 공통 contract artifact

Backend와 frontend release는 다음 bounded contract set을 공유한다.

ApiContractSetV1
  globalApiContractVersion
  restArtifactId + digest
  runtimeSchemaManifestId + digest
  mapperSemanticManifestId + digest
  errorVocabularyVersion
  minimumFrontendVersion
  minimumBackendVersion
  effectiveAt
  retirementEpoch | null

  optional:
    graphqlSchemaId + digest
    persistedGraphqlManifestId + digest
    protobufDescriptorId + digest
    protobufSourceOrModuleId + digest
    protobufCodegenProfileId
    connectProviderProfileId
    grpcWebProviderProfileId
    protobufRestGatewayProfileId
    httpRuleArtifactId + digest
    protoJsonProfileId
    gatewayOpenApiArtifactId + digest

최소 산출물:

  • authenticated OpenAPI 또는 동등한 REST schema source
  • exact status/media/envelope fixture
  • stable error code vocabulary
  • request/response byte와 collection ceiling
  • scalar/date/null/enum 의미
  • N/N-1 compatibility 결과
  • backend build와 frontend release가 참조하는 immutable digest

runtime config의 version 문자열 일치만 compatibility 증거로 사용하지 않는다. artifact digest가 없는 동안에는 실제 staging conformance fixture와 수동 승인 evidence가 필요하다.

4. 현재 reference REST 계약

현재 frontend에 실제 조립된 operation은 다음 세 개다.

operation request success
LIST_REFERENCE_RESOURCES GET /api/reference-resources?cursor&limit&tags 200 application/json
GET_REFERENCE_RESOURCE GET /api/reference-resources/{resourceId} 200 application/json
CREATE_REFERENCE_RESOURCE POST /api/reference-resources 200 또는 201 application/json

현재 list payload는 ReferenceResource[]다. cursor 입력이 존재하더라도 CursorPage 출력 계약은 아직 아니다. backend가 같은 operation에서 배열을 page object로 조용히 바꾸면 schema mismatch로 실패한다.

resource DTO:

ReferenceResourceDtoV1
  id: non-empty string, maximum 120 characters
  name: non-empty string, maximum 240 characters
  createdAt?: RFC 3339 date-time

create command:

CreateReferenceResourceCommandV1
  name: trimmed string, 1..120
  note?: trimmed string, 0..500

Backend는 frontend validation을 신뢰하지 않고 동일하거나 더 좁은 validation과 authorization을 다시 수행한다.

4.1 JSON envelope

모든 현재 JSON success/failure는 다음 envelope를 사용한다.

{
  "success": true,
  "data": {},
  "meta": {
    "requestId": "server-request-id",
    "traceId": "server-trace-id",
    "correlationId": "client-correlation-id"
  }
}
{
  "success": false,
  "error": {
    "code": "STABLE_MACHINE_CODE",
    "category": "optional-safe-category",
    "message": "optional non-sensitive copy",
    "retryable": false,
    "details": {}
  },
  "meta": {
    "requestId": "server-request-id",
    "traceId": "server-trace-id",
    "correlationId": "client-correlation-id"
  }
}

Envelope 최상위 unknown field는 현재 거절된다. ordinary resource DTO의 unknown field는 frontend schema에서 strip되지만 additive compatibility는 contract review와 fixture를 먼저 통과해야 한다.

requestId, traceId, correlationId는 각각 1..128 범위의 안전한 opaque identifier다. credential, user data, cursor, validator와 database key를 identifier에 encode하지 않는다.

4.2 Status와 error

HTTP status 의미
400 malformed request 또는 closed request contract 위반
401 인증 없음/만료. 이미 적용된 command를 401로 반환하지 않음
403 authenticated principal에게 권한 없음
404 authorization 정책상 공개 가능한 not-found
409 idempotency fingerprint, domain revision 또는 semantic conflict
412 selected conditional write의 If-Match precondition 실패
422 field validation. bounded details.issues[]만 허용
429 rate limit. 유효한 Retry-After와 operation 정책 제공
500/502/503/504 server/provider failure. command effect certainty 별도

현재 frontend의 ordinary status mapper는 412 전용 처리를 아직 연결하지 않았다. conditional mutation을 선택할 때 frontend failure vocabulary와 transaction을 함께 승격해야 한다.

Backend error.code는 machine-readable stable code다. stack, SQL/vendor error, raw validation value, authorization reason과 내부 service address를 반환하지 않는다.

5. 인증, CSRF와 CORS

현재 reference operation은 다음 profile로 조립돼 있다.

auth = external bearer
Authorization: Bearer <credential>
fetch credentials = omit
CSRF profile = none
redirect = error
referrer policy = no-referrer

Backend/BFF는 bearer의 issuer, audience, signature algorithm, time claims와 revocation/session policy를 검증하고 operation별 authorization을 적용한다. 401과 403을 구분하며 frontend cache를 authorization authority로 사용하지 않는다.

쿠키 session으로 전환할 경우 같은 profile로 간주하지 않는다. 별도 SAME_ORIGIN_COOKIE profile에 다음을 함께 승인한다.

  • Secure, HttpOnly, 명시적 SameSite와 host/path scope
  • unsafe method의 CSRF token/header와 Origin/Sec-Fetch-Site 검증
  • credentialed CORS에서 wildcard origin 금지
  • login/logout/session rotation과 cache generation 전환
  • session fixation, token rotation과 concurrent tab 동작

Cross-origin bearer provider 최소 CORS:

  • exact allow-origin 목록과 bounded preflight cache
  • Authorization, Content-Type, Idempotency-Key, X-Correlation-ID, 향후 If-None-Match, If-Match 허용
  • 필요한 경우 ETag, Retry-After, request/trace header만 expose
  • redirect login page, HTML error body와 wildcard credential 금지

6. Command와 idempotency

CREATE_REFERENCE_RESOURCE는 keyed command다. frontend memory single-flight는 backend idempotency를 대체하지 않는다.

idempotency identity:

principal/tenant
  + semantic operation ID and contract version
  + Idempotency-Key
  + canonical request fingerprint

권장 record:

IdempotencyRecord
  principalFingerprint
  operationId
  contractVersion
  idempotencyKeyHash
  requestFingerprint
  state = IN_PROGRESS | COMMITTED | FAILED_SAFE | EFFECT_UNKNOWN
  responseStatus
  responseEnvelopeReference
  resourceRevision | null
  leaseOwner + leaseExpiry
  retentionExpiry
  createdAt + completedAt

불변조건:

  • claim과 command transaction의 관계가 원자적이거나 crash reconciliation 가능해야 한다.
  • 같은 key와 같은 fingerprint replay는 같은 authoritative receipt를 반환한다.
  • 같은 key와 다른 fingerprint는 409 IDEMPOTENCY_KEY_REUSED다.
  • concurrent replay는 하나만 실행하고 나머지는 같은 result를 기다리거나 bounded IN_PROGRESS 결과를 받는다.
  • commit 뒤 response 유실은 새 resource를 만들지 않는다.
  • EFFECT_UNKNOWN은 새 key로 자동 재시도하지 않고 status/reconcile endpoint로 확인한다.
  • retention은 frontend retry/recovery 최대 window보다 길고 quota/abuse limit이 있다.
  • key 원문과 request body를 log/metric label에 넣지 않는다.

Database unique constraint 또는 durable compare-and-set이 최종 중복 방지 authority여야 한다. process-local map/lock만 사용하지 않는다.

7. Cursor pagination과 snapshot

Backend가 pagination을 선택할 때 새 response schema/operation version으로 다음 contract를 제공한다.

CursorPage<T>
  items: T[]
  nextCursor: opaque string | null
  hasMore: boolean
  snapshotToken: opaque string | null

필수 불변조건:

  • hasMore === (nextCursor !== null)
  • 동일 chain의 snapshotToken은 모든 page에서 동일
  • cursor는 principal/tenant, filter, sort, contract version과 snapshot에 binding
  • cursor는 opaque, 무결성 보호, 만료와 key rotation 정책 보유
  • offset이 아니라 stable keyset ordering 사용
  • total order의 마지막 tie-breaker는 immutable unique ID
  • deleted/inserted row가 duplicate/gap을 만드는 의미를 snapshot 정책으로 결정
  • empty page인데 hasMore=true인 sparse page 허용 여부를 operation profile에 고정
  • cursor 최대 encoded byte, page size와 total scan/cost ceiling을 server도 강제
  • invalid, expired, wrong-principal, wrong-filter cursor의 safe error code를 고정

권장 query ordering 예:

ORDER BY created_at DESC, resource_id DESC
cursor payload = version + snapshot watermark + last(created_at, resource_id)
                 + filter digest + principal/tenant binding + expiry

Cursor 원문은 log, trace, analytics와 frontend persistent storage에 넣지 않는다.

7.1 배열에서 page로의 migration

  1. ReferenceResourceListPagePayloadV2 schema와 새 operation/version을 추가한다.
  2. backend가 N/N-1 동안 기존 배열과 page contract를 동시에 제공한다.
  3. frontend가 cursor runtime을 새 bound query/infinite query에 연결한다.
  4. loop/snapshot/ceiling/abort conformance를 staging에서 검증한다.
  5. 새 operation을 canary한 뒤 기존 배열 operation을 retirement한다.

동일 media/status에서 payload shape만 바꾸는 in-place migration은 금지한다.

8. Conditional read와 revision/CAS

8.1 Read validator

Backend가 application-managed revalidation을 선택하면 exact mapped representation마다 ETag를 제공한다.

GET without validator
  -> 200 + JSON envelope + ETag

GET with If-None-Match
  -> representation unchanged: 304 + empty body
  -> changed: 200 + JSON envelope + new ETag

불변조건:

  • validator는 principal/tenant, authorization-visible representation, response schema/mapper semantics와 encoding variant에 binding
  • weak/strong 선택을 operation profile에 고정
  • user-private response를 shared CDN/public cache에 저장하지 않음
  • cross-origin이면 ETag를 expose하고 If-None-Match를 preflight 허용
  • 304에는 JSON success envelope를 넣지 않음
  • validator 원문을 log/metric/diagnostics에 넣지 않음
  • VaryCache-Control owner를 명확히 하고 browser HTTP cache와 TanStack/application revalidation이 서로 다른 value owner가 되지 않게 함

Frontend는 validator와 mapped cache value의 scope, query identity, representation version과 cache revision이 모두 일치할 때만 304를 success로 받는다. cache value가 없으면 unconditional refetch 또는 safe failure로 닫는다.

8.2 Conditional command

수정/삭제 command가 선택되면 DTO에 opaque domain revision을 추가하고:

If-Match: "<revision validator>"

를 요구한다. 일치하지 않으면 412 또는 승인된 409 contract 하나만 사용한다. frontend optimistic layer의 commit/rollback은 backend revision authority를 대체하지 않는다.

9. Optimistic mutation을 위한 backend 의미

Frontend ordered optimistic layer runtime은 구현돼 있지만 제품 operation에 연결하려면 backend가 다음을 결정해야 한다.

  • resource/list membership을 결정하는 canonical filter와 sort
  • command가 생성/수정/삭제하는 stable identity
  • server-assigned ID와 client correlation의 reconcile 방법
  • authoritative resource/list revision
  • conflict status와 stable error code
  • commit response가 complete resource인지 receipt인지
  • effect certainty와 idempotency status/reconcile endpoint
  • event/outbox가 있을 때 sequence/gap/snapshot reset 의미

Create가 server-assigned ID를 사용하는 경우 temporary UI ID를 backend ID로 원자적으로 교체하고 관련 detail/list key를 reconcile하는 정책이 필요하다. 이 의미 없이 generic optimistic append를 기본 활성화하지 않는다.

10. Database와 application service baseline

구현 예시는 다음 논리 table/constraint를 만족해야 한다.

reference_resource
  tenant_id
  resource_id
  display_name
  note
  revision
  created_at
  updated_at
  deleted_at | null
  unique(tenant_id, resource_id)

idempotency_record
  principal/tenant fingerprint
  operation + contract version
  key hash
  request fingerprint
  state + receipt
  lease/retention timestamps
  unique(principal/tenant, operation, contract version, key hash)

outbox_event when selected
  aggregate identity + revision
  event type/version
  sequence
  payload reference or bounded safe projection
  publication state

Application service transaction은 authorization scope와 tenant predicate를 모든 read/write에 적용하고, resource mutation과 revision/outbox 기록을 같은 transaction boundary에 둔다. cache/replica lag를 고려해 command 직후 read consistency와 invalidation owner를 선언한다.

11. GraphQL 선택 시 추가 구조

GraphQL은 제품 operation이 REST보다 aggregation 이점을 실제로 가질 때만 선택한다.

Browser
  -> persisted-operation endpoint
       -> manifest allowlist
       -> auth/CSRF/rate/cost/depth/alias enforcement
       -> GraphQL router
            -> application services/loaders

Backend handoff:

  • authenticated immutable schema artifact와 digest
  • named operation source와 persisted ID/hash manifest
  • variables/result runtime fixtures
  • selected GraphQL-over-HTTP revision과 exact media/status profile
  • partial data policy와 safe error extension vocabulary
  • field/row authorization, cost/depth/alias/list ceiling
  • N/N-1 router/frontend manifest rollout과 retirement

Production endpoint는 arbitrary document와 persisted miss 후 full-document fallback을 받지 않는다. normalized frontend entity cache는 별도 제품 선택이다.

12. gRPC-Web 선택 시 추가 구조

gRPC-Web은 browser-facing gateway/proxy가 실제 선택된 unary 또는 bounded server-stream operation에만 사용한다.

Browser
  -> same-origin BFF/Envoy/gRPC-Web gateway
       -> exact service/method allowlist
       -> frame/message/deadline/status/trailer enforcement
            -> internal gRPC application service

Backend handoff:

  • authenticated proto source와 immutable descriptor digest
  • Buf/protoc lint/breaking 및 deterministic generation evidence
  • exact service/method/rpc-kind allowlist
  • selected gRPC-Web runtime kind, client API와 binary/JSON/text/wire revision; official XHR와 Connect-Web Fetch profile을 분리
  • proxy CORS, content-type, terminal status/trailer behavior
  • Envoy를 선택하면 exact version/config digest, filter order, upstream HTTP/2, route/idle/max-stream timeout, timeout offset와 buffering/flush
  • message/frame/count/queue/idle/total budget
  • server-stream sequence, gap, resume와 snapshot reset protocol
  • actual browser/proxy conformance

client streaming과 bidirectional streaming은 common gRPC-Web browser contract로 간주하지 않는다. upload는 REST transfer, duplex는 별도 protocol을 선택한다.

13. Connect-Web/Connect 선택 시 추가 구조

Connect는 Protobuf-first backend의 selected unary 또는 bounded server-stream operation에만 사용한다. Connect protocol과 Connect-Web의 gRPC-Web transport는 서로 다른 provider row다.

Browser Connect-Web adapter
  -> same-origin BFF 또는 exact cross-origin Connect endpoint
       -> auth/CSRF/CORS + service/method allowlist
       -> Connect protocol handler
            -> application service

Backend handoff:

  • authenticated proto/Buf source, descriptor와 generated-service digest
  • exact Connect-Web/client runtime과 server/gateway version
  • protocol revision, JSON/binary encoding, POST 또는 approved GET
  • unary HTTP/error profile 또는 stream EndStream terminal profile
  • request/response/envelope/message/count/queue byte ceiling
  • unary/stream compression capability; stock browser stream은 identity-only
  • total/idle timeout, browser abort→server context→downstream cancellation 전파
  • exact CORS allow/expose/preflight와 auth/CSRF profile
  • actual Chromium/Firefox/WebKit와 selected proxy/server conformance

GET은 descriptor NO_SIDE_EFFECTS, non-sensitive bounded input, URL/cache key, Vary와 credential policy가 모두 승인된 unary에만 허용한다. browser client-streaming/bidi는 Connect protocol 자체 기능과 별개로 PLATFORM_LIMITED다.

14. Protobuf REST Gateway 선택 시 추가 구조

한 route는 CURATED_BFF | GRPC_GATEWAY | ENVOY_TRANSCODER 중 하나만 소유한다. 현재 reference REST의 envelope와 200|201, 향후 204/304/412 의미를 유지하는 기본 선택은 curated BFF다.

Direct gateway는 ProtoJSON/HttpRule/status/error 자체를 새 public contract로 승인한 unary operation에서만 선택한다. Backend handoff:

  • .proto annotation 또는 precedence가 고정된 service config의 immutable source
  • descriptor/Buf image, canonical HttpRule route manifest와 digest
  • pinned gateway/runtime/generator/plugin과 generated OpenAPI artifact
  • ProtoJSON name/default/enum/int64/bytes/null/presence/unknown-field profile
  • exact method/path/query/body/response-body/additional-binding와 path escaping
  • safe status/error/header mapping과 raw google.rpc.Status detail redaction
  • CORS/auth/CSRF, body/header/query ceiling와 rate limit
  • browser abort/deadline의 upstream gRPC/application work 전파
  • N/N-1 route/OpenAPI/runtime conformance와 coherent rollback

Gateway는 durable idempotency, pagination snapshot, ETag/HTTP conditional, product envelope, authorization와 file transfer semantics를 자동 구현하지 않는다. 필요한 operation은 application service와 BFF가 계속 소유한다. generated REST streaming은 별도 framing/terminal/cache ADR 없이는 NOT_SELECTED다.

15. Invalidation과 realtime

현재 frontend cross-tab invalidation은 같은 browser origin 안의 opaque invalidate-only hint다. backend event delivery를 의미하지 않는다.

Backend-driven invalidation/realtime을 선택하면:

  • transactional outbox 또는 동등한 durable publication
  • principal/tenant authorization을 통과한 event projection
  • event type/version, aggregate revision, sequence와 dedupe identity
  • reconnect cursor, gap detection과 snapshot reset
  • retention, replay ceiling과 slow-consumer policy

를 제공해야 한다. event payload를 authoritative resource snapshot으로 쓸지 query invalidate hint로만 쓸지 operation별 reducer contract가 필요하다.

16. Rate limit, deadline와 retry

  • backend deadline은 frontend total deadline보다 짧거나 cancellation을 전파할 수 있어야 한다.
  • disconnect/cancel 뒤 불필요한 query 작업은 중단한다.
  • keyed command는 disconnect가 transaction rollback을 보장하지 않으므로 idempotency receipt로 effect를 판정한다.
  • Retry-After는 selected status에서만 bounded delta/date 형식으로 제공한다.
  • retry-safe read와 keyed command를 구분한다.
  • proxy, BFF와 service retry가 겹쳐 retry amplification을 만들지 않게 한 owner만 재시도한다.
  • rate limit key는 principal/tenant/operation과 abuse policy에 binding하며 raw credential/IP를 metric label에 넣지 않는다.

17. Observability와 privacy

허용되는 공통 dimension:

operation ID
contract/profile version
status group / safe error code
attempt bucket
duration bucket
provider/runtime health
traffic admission stage

금지:

  • Authorization, cookie, CSRF와 idempotency key
  • request/response body와 validation value
  • URL query, cursor, snapshot, ETag/revision
  • GraphQL variables/path/raw error/extensions
  • protobuf bytes, metadata와 trailer 원문
  • user ID/email/file name을 metric label이나 trace attribute로 사용

Request ID와 trace ID는 browser에 반환할 수 있지만 credential 역할을 하지 않으며 추측 가능한 database primary key를 포함하지 않는다.

필수 SLO/alert 후보:

  • operation availability와 latency
  • 401/403/409/412/422/429 및 5xx rate
  • schema/mapper/contract mismatch
  • idempotency in-progress age, collision과 unknown effect
  • cursor invalid/expired/loop-equivalent server detection
  • conditional hit/miss와 invalid 304
  • GraphQL persisted miss/cost reject
  • Connect missing/duplicate EndStream, whole-body/queue overflow와 compression mismatch
  • gRPC-Web missing terminal status, frame/idle/queue overflow
  • REST Gateway route/OpenAPI/runtime rewrite drift와 cancel propagation loss

18. 배포, compatibility와 rollback

권장 순서:

  1. contract artifact와 compatibility diff를 생성한다.
  2. backend가 N/N-1 fixture를 통과한 상태로 먼저 배포한다.
  3. frontend operation은 traffic disabled 상태에서 staging conformance를 실행한다.
  4. read-only shadow/canary 뒤 query traffic을 올린다.
  5. keyed command는 idempotency/reconcile fault injection 뒤 별도 canary한다.
  6. pagination, conditional, optimistic, GraphQL, Connect, gRPC-Web과 REST Gateway는 각각 독립 gate로 승격한다.
  7. provider/browser/operations evidence가 완료된 operation만 enabled한다.

Rollback은 frontend/backend/contract artifact를 coherent set으로 되돌린다. unknown-effect command를 다른 protocol이나 새 idempotency key로 replay하지 않는다. Backend가 old contract를 제거하는 시점은 실제 frontend support window와 cache/CDN retention 뒤다.

19. Conformance와 fault-injection matrix

Backend 완료 판정에는 unit test 외에 actual staging provider evidence가 필요하다.

범위 필수 증거
REST exact path/query/body, media/status/envelope, max body, malformed/truncated JSON
Auth missing/expired credential, 401/403, rotation, cross-origin preflight
Command concurrent same-key replay, fingerprint mismatch, commit 뒤 response loss
Cursor filter/sort binding, expiry, snapshot stability, loop/gap/duplicate 방지
Conditional 200→304, cache-missing 304 방지, representation change, 412
Schema additive/breaking/null/enum/time/number fixtures와 N/N-1
GraphQL persisted hit/miss/hash mismatch, partial, cost/depth, router rollout
Connect JSON/binary unary, GET restriction, EndStream, body/message cap, compression, cancel/deadline와 CORS
gRPC-Web proxy media/status/trailer, oversized frame, cancel, idle, gap/resume
REST Gateway HttpRule path/query/body, ProtoJSON, OpenAPI/status/error rewrite, abort propagation과 N/N-1
Operations deadline/retry amplification, rate limit, kill switch, coherent rollback

20. Backend handoff checklist

  • Browser-facing API/BFF owner와 on-call이 정해졌다.
  • reference REST exact endpoint/envelope/status/media fixture가 있다.
  • bearer 또는 cookie+CSRF 중 하나의 실제 profile과 CORS evidence가 있다.
  • stable error vocabulary와 redaction contract가 있다.
  • keyed command idempotency store, TTL, receipt와 reconcile이 있다.
  • CursorPage를 선택했다면 opaque cursor/snapshot contract가 있다.
  • conditional을 선택했다면 ETag/304/412와 cache owner가 있다.
  • optimistic을 선택했다면 identity/membership/revision/conflict 의미가 있다.
  • OpenAPI/runtime schema/mapper semantic artifact와 digest가 release에 binding됐다.
  • GraphQL을 선택했다면 schema/persisted manifest/router evidence가 있다.
  • Connect를 선택했다면 descriptor/runtime/server/browser evidence가 있다.
  • gRPC-Web을 선택했다면 descriptor/proxy/browser evidence가 있다.
  • REST Gateway를 선택했다면 kind/HttpRule/ProtoJSON/OpenAPI와 runtime conformance evidence가 있다.
  • staging conformance, fault injection, canary, kill switch와 rollback drill이 통과했다.

21. Frontend 완료 경계

Backend 구현과 별개로 현재 frontend 상태를 다음처럼 해석한다.

범위 현재 상태 남은 owner
REST path/provider/auth/deadline/bounded JSON COMPOSED actual provider conformance는 backend/operations
runtime schema와 mapper registry COMPOSED artifact digest/source provenance는 backend contract source + frontend/platform
session generation과 query identity COMPOSED account identity projection은 identity integration + frontend
Cursor runtime AVAILABLE_NOT_COMPOSED CursorPage backend 계약 후 frontend query binding
conditional validator store AVAILABLE_NOT_COMPOSED ETag/304/412 backend 계약 후 frontend HTTP/cache transaction
ordered optimistic layer AVAILABLE_NOT_COMPOSED product membership/revision 승인 후 frontend mutation definition
GraphQL adapter DESIGNED_NOT_IMPLEMENTED product/backend 선택 뒤 frontend adapter/codegen
Browser RPC V3 공통 계약/coordinator AVAILABLE_NOT_COMPOSED selected descriptor/generated client와 protocol transport 확정 뒤 frontend provider adapter
Protobuf schema/codegen DESIGNED_NOT_IMPLEMENTED authenticated backend contract source 선택 뒤 pinned frontend generation
Connect-Web adapter DESIGNED_NOT_IMPLEMENTED product/backend/server 선택 뒤 frontend adapter/codegen
gRPC-Web adapter DESIGNED_NOT_IMPLEMENTED product/backend/proxy 선택 뒤 frontend adapter/codegen
Protobuf REST Gateway NOT_SELECTED gateway kind와 public HTTP contract 승인 뒤 REST adapter binding
persisted query/offline command NOT_SELECTED 별도 product ADR와 backend durability 계약

따라서 “backend만 구현하면 frontend가 아무 변경 없이 모든 capability를 자동 사용한다”는 의미는 아니다. 현재 선택된 REST reference vertical의 공통 frontend 기반은 완료됐지만, backend contract가 확정되면 Cursor/conditional/optimistic의 마지막 composition과 schema/mapper 변경이 frontend에 남는다. GraphQL, Connect/gRPC-Web과 Protobuf REST Gateway는 제품이 선택되지 않았다. 공통 Browser RPC operation/profile registry, application port와 lifecycle coordinator는 구현했지만, wire별 generated client/decoder/provider binding은 아직 구현하지 않았다. 따라서 backend contract가 정해져도 frontend provider adapter와 composition 작업은 명시적으로 남는다.