30 KiB
Backend API와 Server State handoff contract
정본 안내 (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 저장소 정합형 구현 결정 폐쇄 상세 설계가 정본이다. 본 문서의 해당 서술이 정본과 충돌하면 정본을 따른다.
- 상태: frontend handoff design accepted, backend implementation/evidence pending
- 기준일: 2026-07-28
- 대상: Web API/BFF, application service, persistence, identity, GraphQL router, Connect/gRPC-Web gateway, Protobuf REST Gateway와 운영 owner
- frontend 기준: API contract, Schema, Mapper와 Server State
- browser Protobuf/gateway 기준: Protobuf browser transport와 REST Gateway
- 파일 전송 backend 기준: Server file capability infrastructure
- 복구 절차: API contract와 server-state recovery
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
ReferenceResourceListPagePayloadV2schema와 새 operation/version을 추가한다.- backend가 N/N-1 동안 기존 배열과 page contract를 동시에 제공한다.
- frontend가 cursor runtime을 새 bound query/infinite query에 연결한다.
- loop/snapshot/ceiling/abort conformance를 staging에서 검증한다.
- 새 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에 넣지 않음
Vary와Cache-Controlowner를 명확히 하고 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:
.protoannotation 또는 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.Statusdetail 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
권장 순서:
- contract artifact와 compatibility diff를 생성한다.
- backend가 N/N-1 fixture를 통과한 상태로 먼저 배포한다.
- frontend operation은 traffic disabled 상태에서 staging conformance를 실행한다.
- read-only shadow/canary 뒤 query traffic을 올린다.
- keyed command는 idempotency/reconcile fault injection 뒤 별도 canary한다.
- pagination, conditional, optimistic, GraphQL, Connect, gRPC-Web과 REST Gateway는 각각 독립 gate로 승격한다.
- 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 작업은 명시적으로 남는다.