Files
clean-architecture-frontend…/docs/architecture/decisions/VD-16-browser-transfer-composition-and-image-delivery.md

639 lines
26 KiB
Markdown

# VD-16: Browser transfer composition과 Image delivery
- 상태: Accepted — design complete, implementation pending
- 결정일: 2026-07-28
- 이 ADR이 선택한 common delta의 current status:
`DESIGNED_NOT_IMPLEMENTED`
- common delta의 목표 reference status:
`AVAILABLE_NOT_COMPOSED`
- 관련 결정: VD-10, VD-11, VD-12, VD-13, VD-14, VD-15
- current status ledger:
`docs/architecture/browser-data-capability-completion-ledger.md`
- 재검토: 첫 product upload/download/Image CDN capability를 조합하기 전
## 배경
현재 저장소에는 File runtime, presigned capability provider/vault/executor,
multipart/resumable upload, one-shot streaming download와 Image CDN verification
runtime의 개별 factory가 있다. 이 구현들은 production bootstrap에서 제거돼
있고 각각의 local `close()` 또는 `dispose()`만 제공한다.
제품에서 이들을 직접 조합하면 다음 문제가 생긴다.
- account/session이 바뀌어도 이전 capability, checkpoint, refresh 또는 async
completion이 살아남을 수 있다.
- 서로 다른 config가 같은 byte/resource/preset을 다르게 해석할 수 있다.
- 일부 provider만 생성된 partial runtime이 요청을 받기 시작할 수 있다.
- upload, download와 image에 retry, kill switch, deadline과 observation owner가
중복될 수 있다.
- Image engine은 이미 decode된 server descriptor를 받으므로 BFF transport,
expiry refresh와 presentation handoff의 책임이 비어 있다.
- 실제 provider가 frontend mock과 같은 계약을 지키는지 재사용 가능한
conformance harness가 없다.
이 결정은 도메인별 upload 화면이나 cloud vendor를 공통 플랫폼에 넣지 않는다.
선택된 capability를 안전하게 조립·폐기하는 composition protocol과 Image
descriptor acquisition 경계를 정한다.
## 현재 상태
| 항목 | 상태 | 설명 |
| --- | --- | --- |
| 개별 presigned/upload/image runtime | `AVAILABLE_NOT_COMPOSED` | factory와 deterministic test가 있으나 제품 graph에는 없음 |
| one-shot streaming download | `AVAILABLE_NOT_COMPOSED` | Range resume가 아닌 전체 객체 스트림 |
| top-level transfer runtime | `DESIGNED_NOT_IMPLEMENTED` | export index만 있고 atomic factory/readiness/lifecycle 없음 |
| Image descriptor HTTP provider | `DESIGNED_NOT_IMPLEMENTED` | caller가 `BackendIssuedImageAsset`을 직접 전달 |
| safe image DOM projection | `DESIGNED_NOT_IMPLEMENTED` | presentation descriptor는 있으나 renderer boundary 없음 |
| app-managed background download | `NOT_SELECTED` | VD-14의 별도 optional capability |
| cross-browser background-download guarantee | `PLATFORM_LIMITED` | browser-managed handoff가 기본 fallback |
| app-managed background upload | `NOT_SELECTED` | durable source staging/worker auth protocol이 별도로 필요 |
| cross-browser background-upload guarantee | `PLATFORM_LIMITED` | worker lifetime/local source permission을 공통 보장할 수 없음 |
이 ADR을 추가해도 위 상태는 자동으로 바뀌지 않는다. port, runtime, test와
removal evidence가 구현된 뒤에만 reference 상태를 올린다.
## 결정
### 1. 하나의 account-scoped composition owner
선택된 file/transfer/image capability는
`BrowserTransferRuntimeComposition` 역할의 단일 owner가 다음 순서로 생성한다.
```text
parse immutable config
-> validate implementation ceilings
-> obtain immutable session/account scope
-> create policy registries
-> create provider transports
-> create capability vaults
-> create checkpoint/lock/channel owners
-> create file/upload/download/image runtimes
-> run required compatibility probes
-> publish READY facade atomically
```
factory가 중간에 실패하면 생성된 owner를 역순으로 닫고 facade를 반환하지 않는다.
partial runtime, degraded provider 또는 mutable config를 application에 노출하지
않는다.
composition은 다음 두 종류를 반환하는 union이어야 한다.
```text
READY {
generation,
capabilities,
application facades,
readiness,
close()
}
UNAVAILABLE {
safe reason,
retryability,
fallback capability,
disposePartial()
}
```
`UNAVAILABLE`에 provider URL, raw browser exception, account/tenant ID 또는
credential을 넣지 않는다.
### 2. Runtime config는 closed schema다
config는 composition root만 읽고 깊은 snapshot/freeze한다. 최소한 다음
registry-owned reference를 갖는다.
- config schema version과 runtime compatibility version
- opaque session/account scope와 generation
- application origin과 fixed BFF endpoint IDs
- exact `PRESIGNED_TRANSFER_V1`, `PRESIGNED_MULTIPART_V1`,
`RANGE_RESUMABLE_DOWNLOAD_V1`, `IMAGE_CDN_DESCRIPTOR_V1` 중 선택한 protocol
registry와 fixed endpoint map
- upload purpose/profile, part/concurrency/retry/deadline hard ceiling
- download profile, size/integrity/strategy와 Range capability selection
- checkpoint namespace, retention, inventory와 maintenance budget
- lock/cancel transport selection과 unsupported outcome
- Image issuer/origin/preset/key/probe/decode policy
- descriptor refresh lead time, request/decode deadline와 concurrency
- capability별 traffic admission과 kill switch
- observation sink와 redaction policy
- active-operation drain deadline
caller는 raw endpoint, URL, header, object key, transform, retry count, byte ceiling,
cache policy 또는 account partition을 request마다 override할 수 없다.
config는 구현 절대 상한을 높일 수 없다. 구현 상한보다 큰 값, 중복 registry ID,
same-origin private Image CDN, 모순되는 fallback 또는 provider 누락은 startup에서
fail-closed한다.
### 3. Lifecycle state machine
top-level runtime은 다음 상태만 가진다.
```text
CREATING
-> PROBING
-> READY
-> DRAINING
-> CLOSED
CREATING | PROBING
-> FAILED
-> CLOSED
```
- `READY`만 새 operation을 받는다.
- `DRAINING`은 새 operation을 거절하고 진행 중 operation에 bounded deadline을
제공한다.
- deadline 뒤 남은 operation은 runtime lifetime signal로 abort한다.
- `close()`는 terminal/idempotent이며 `DRAINING/CLOSED`에서 반복 호출해도
새 side effect를 만들지 않는다.
- 닫힌 runtime은 reopen하지 않는다. 새 config/scope에는 새 generation을 만든다.
- operation은 시작할 때 runtime generation과 account scope snapshot을 얻고
모든 async boundary와 terminal commit 전에 다시 확인한다.
- 늦게 끝난 fetch, hash, IndexedDB transaction, image verification 또는 decode가
old generation이면 결과를 폐기하고 native resource를 닫는다.
### 4. Logout과 account/tenant switch
session owner notification이 authority다. BroadcastChannel, storage event,
capability expiry 또는 page unload를 logout authority로 사용하지 않는다.
```text
session owner announces local revoke
-> traffic admission CLOSED
-> runtime generation FENCED
-> reject new operations
-> signal active reads/fetch/backoff/probes
-> bounded drain
-> close capability and image vaults
-> close cancel channels and release locks/connections
-> apply checkpoint retention/purge policy with exact old scope
-> dispose observations
-> CLOSED
-> construct new scope/runtime independently
```
- old-scope purge와 new-scope open을 같은 transaction이나 facade에 섞지 않는다.
- checkpoint가 crash 때문에 남아도 exact scope/policy binding이 다르면 새
runtime이 읽지 못해야 한다.
- presigned URL, signed headers와 Image private URL은 어떤 teardown record에도
저장하지 않는다.
- old account의 descriptor refresh, part completion과 download destination
commit은 generation fence 뒤 성공으로 보고하지 않는다.
- logout이 backend capability의 즉시 revoke를 보장하지 않는다. 강한 회수가
필요하면 BFF가 revoke authority 또는 proxy/relay를 제공해야 한다.
### 5. Capability별 facade
application에는 top-level runtime 자체나 native adapter를 반환하지 않는다.
composition은 설치된 feature에 필요한 좁은 facade만 주입한다.
```text
FeatureUploadFacade
-> select local file profile
-> create/resume/pause/abort approved purpose
FeatureDownloadFacade
-> request approved resource
-> receive policy-selected handoff/save outcome
FeatureImageFacade
-> request opaque asset/preset
-> receive safe presentation descriptor
```
feature는 `File`, `Blob`, `Response`, `ReadableStream`, `FileSystemHandle`,
presigned URL, Image signature DTO, checkpoint store 또는 QueryClient를 받지 않는다.
progress UI를 위한 observation도 bounded aggregate snapshot이며 transfer
authority가 아니다.
## Upload lifecycle integration
### 6. Pause와 abort는 다르다
top-level upload facade가 향후 제공할 상태는 다음과 같다.
```text
ACTIVE
-> PAUSE_REQUESTED
-> PAUSED
-> RESUMING
-> ACTIVE
ACTIVE | PAUSED
-> ABORT_REQUESTED
-> ABORT_PENDING
-> ABORTED
ACTIVE
-> COMPLETING
-> QUARANTINED
```
- pause는 새 part와 retry를 중지하고 현재 bounded native operation을 abort한 뒤
non-authorizing checkpoint를 유지한다.
- cross-context pause wire literal은 `RESUMABLE_UPLOAD_PAUSE_V1`이며
`uploadKey`, exact scope/generation과 bounded message metadata만 운반한다.
- durable `PAUSED`를 추가하는 checkpoint는 `schemaVersion: 2`다. v1
`ACTIVE | ABORT_PENDING` reader/writer와 섞지 않고 old-writer drain,
historical migration과 N-1 fail-closed를 증명한다.
- abort는 server session authority와 reconcile한 뒤 terminal checkpoint를
제거한다.
- pause signal은 authority가 아니라 best-effort same-scope hint다. 수신자는
exact upload key/scope/generation을 검증한다.
- checkpoint inventory는 presigned URL이나 raw file path 없이 opaque upload
key, safe state, age/byte/part bucket과 expiry만 반환한다.
- inventory/list와 retention sweep은 count, cursor, deadline을 갖는다.
- abandoned/expired checkpoint는 server status 또는 expiry policy와 CAS를
확인한 뒤 bounded batch로 제거한다.
- file 재선택 뒤 source fingerprint/size/media/part layout이 exact하게 맞지
않으면 resume하지 않는다.
app-managed background upload는 이 state machine을 재사용할 수 있지만 page
runtime의 pause/resume를 background 보장으로 표현하지 않는다.
## Download integration
### 7. Strategy selector
download strategy는 application caller가 지정하지 않고 immutable profile,
resource delivery class, expected bytes, browser capability와 user activation을
입력으로 하는 headless selector가 결정한다.
| 조건 | 결과 |
| --- | --- |
| save picker 지원, user activation 있음, large stream | `WHOLE_OBJECT_PICKER_STREAM` |
| server-managed resource, picker 없음 또는 handoff가 제품 정책 | `BROWSER_MANAGED_HANDOFF` |
| generated artifact가 approved Blob cap 이하 | `BOUNDED_OBJECT_URL` |
| large generated artifact, picker 없음 | `SERVER_GENERATION_REQUIRED` 또는 `UNSUPPORTED` |
| Range profile + seekable destination + provider contract | `RANGE_RESUMABLE_FOREGROUND` |
| background download가 선택되고 지원되는 browser + owned staging | `APP_MANAGED_BACKGROUND_DOWNLOAD` |
selector는 capability probe와 actual invocation failure를 구분한다.
`WHOLE_OBJECT_PICKER_STREAM`인데 picker가 없으면 request validation 오류가 아니라
`UNSUPPORTED` 또는 승인 fallback이어야 한다. Safari/WebView/private mode의
fallback도 동일 표에서 결정하며 user agent 문자열만으로 기능을 가정하지 않는다.
selector의 위 값은 application-level `DownloadExecutionPlan`이다. 현재
file-delivery primitive와의 mapping은 다음처럼 닫는다.
| execution plan | 현재 adapter mapping |
| --- | --- |
| `WHOLE_OBJECT_PICKER_STREAM` | `DownloadStrategy=PROMPT_AND_STREAM` |
| `BROWSER_MANAGED_HANDOFF` | source kind `BROWSER_MANAGED_RESOURCE` + `DownloadStrategy=BROWSER_MANAGED` |
| `BOUNDED_OBJECT_URL` | `DownloadStrategy=BOUNDED_OBJECT_URL` |
| `RANGE_RESUMABLE_FOREGROUND` | VD-14의 별도 future port; 현재 adapter에 mapping 금지 |
| `APP_MANAGED_BACKGROUND_DOWNLOAD` | 별도 optional worker/staging port |
| `SERVER_GENERATION_REQUIRED` / `UNSUPPORTED` | browser delivery를 시작하지 않는 closed outcome |
Range resume의 checkpoint, validator와 seek/truncate 결정은 VD-14를 따른다.
app-managed background download는 기본 selector 결과가 아니다.
## Image descriptor acquisition과 delivery
### 8. Wire protocol
private Image descriptor BFF 계약은 다음 literal을 사용한다.
```text
IMAGE_CDN_DESCRIPTOR_V1
```
request는 composition-owned fixed HTTPS endpoint를 호출하며 최소한 다음
application-safe 입력만 허용한다.
- protocol
- opaque asset reference
- named preset reference 또는 preset family
- intended presentation class
- current runtime generation에 묶인 CSRF/session transport
caller는 CDN URL, source URL, origin, object key, width, height, DPR, quality, fit,
format, cache header, signing key ID나 expiry를 제출하지 않는다.
response decoder는 content type, status, header/body byte cap, total deadline와
closed JSON shape를 검증한다. response에는 최소한 다음이 binding된다.
- exact protocol과 issuer
- opaque asset ID와 immutable revision
- origin/preset binding IDs
- static raster media와 intrinsic dimensions
- allowed preset binding ID set
- issued/expiry time
- signature algorithm, key ID, canonical binding digest와 signature
unknown field 정책은 protocol version에서 고정한다. credential, backend stack,
raw provider key 또는 arbitrary transform은 descriptor에 포함하지 않는다.
HTTP `200`만 descriptor success다. `401/403/404`의 외부 mapping은 existence
hiding 정책에 따라 closed failure로 정규화하며 raw backend message를 버린다.
redirect, opaque response, wrong content type, oversize, timeout와 malformed
descriptor는 capability를 생성하지 않는다.
### 9. Provider와 verifier 경계
- BFF는 authorization, asset existence, quarantine/promotion state와 descriptor
발급 authority를 소유한다.
- verifier registry는 composition이 승인한 bounded old/new public key set만
가진다.
- client signature 검증은 BFF authorization의 대체가 아니라 response tamper와
registry mismatch를 fail-closed하는 보조 경계다.
- CDN은 asset revision과 preset binding ID로 exact transform candidate를
재계산한다. signed query나 client 계산 width가 authority가 아니다.
- private asset의 emergency revocation은 backend/CDN/BFF가 소유한다. client는
runtime close와 short expiry로 exposure를 줄인다.
### 10. Refresh state machine
descriptor provider는 asset/preset/scope/generation별 bounded single-flight만
허용한다.
```text
ABSENT
-> FETCHING
-> VERIFIED
-> FRESH
-> REFRESH_DUE
-> REFRESHING
-> FRESH
FETCHING | REFRESHING
-> TERMINAL_POLICY_FAILURE
-> PLACEHOLDER
FETCHING | REFRESHING
-> RETRYABLE_FAILURE
-> EXISTING_FRESH_UNTIL_EXPIRY | PLACEHOLDER
any state + scope/runtime revoke
-> REVOKED
```
- refresh lead time은 config가 정하되 expiry hard ceiling을 넘지 않는다.
- private descriptor를 generic Query cache, Web Storage 또는 IndexedDB에
persistence하지 않는다.
- concurrent callers는 같은 verified result를 받을 수 있지만 URL/string을
application state에 장기 복사하지 않는다.
- 기존 descriptor가 아직 fresh하고 refresh가 일시 실패하면 expiry까지만
사용할 수 있다. expiry 뒤 stale-while-error를 금지한다.
- `lazy` load로 실제 fetch가 expiry 뒤 시작될 가능성이 있으면 eager/priority로
바꾸거나 load 직전에 새 descriptor를 발급한다.
- logout, key registry replacement와 runtime generation 변경은 in-flight
transport, verification과 probe를 abort하고 늦은 결과를 폐기한다.
### 11. Safe presentation projection
공통 presentation primitive는 검증된 `ImagePresentationDescriptor`를 다음
정적 속성으로만 투영한다.
- fallback `src`
- ordered `<source type srcset>`
- registry-owned `sizes`
- intrinsic `width``height`
- `loading`, `decoding`, `fetchpriority`
- `referrerpolicy`
- `crossorigin="anonymous"`
primitive는 URL을 parse·조립·append하거나 transform query를 생성하지 않는다.
descriptor가 가진 string을 React property로 전달하기 전에 closed allowed
protocol/origin과 runtime generation을 다시 확인한다. raw HTML 주입과 CSS URL
조립을 금지한다.
다음은 제품 presentation owner가 결정한다.
- 의미 있는 `alt`
- placeholder와 오류 copy
- skeleton/aspect-ratio UX
- above-the-fold preload/priority
- route/SSR preload hint
- click/open/download behavior
descriptor refresh 실패를 native broken-image UI에만 맡기지 않고 제품이 승인한
placeholder outcome으로 매핑한다.
## Provider contract harness
### 12. 재사용 가능한 suite
frontend는 transport 구현과 분리된 provider contract harness를 제공한다. 같은
case set을 deterministic fake, local emulator와 실제 BFF/object storage/CDN에
실행한다.
Presigned/download case:
- exact protocol/status/content type/body cap
- method/origin/path/query/header binding
- redirect와 credential omission
- expiry/revocation/replay
- truncation/overrun/content encoding
- CORS exposed receipt/checksum
Multipart case:
- create/status/part/complete/abort idempotency
- part layout/checksum/receipt reconciliation
- 404/410/expiry와 orphan cleanup
- quarantine/promotion
- retry-after와 ambiguous completion
Image case:
- protocol/issuer/key/preset exact match
- old/new signing key overlap과 removal
- immutable revision/cache key
- private no-store/CORS/CSP
- pixel/decode/encoded byte ceiling
- malformed/animated/active content
- expiry refresh, revocation과 placeholder
actual provider test는 bearer URL, signature, account/asset/session ID를 artifact에
기록하지 않는다. fixture는 synthetic opaque values와 disposable storage를 쓴다.
## Failure, readiness와 fallback
### 13. Readiness report
readiness는 application-safe capability별 결과다.
| 상태 | 의미 |
| --- | --- |
| `READY` | 필수 provider/config/browser probe가 모두 유효 |
| `DEGRADED` | 승인된 좁은 fallback만 가능 |
| `UNAVAILABLE` | 기능을 노출하지 않음 |
| `DRAINING` | 기존 작업만 정리 중 |
| `CLOSED` | terminal |
`DEGRADED`는 byte/pixel/security ceiling을 낮출 수는 있지만 높이지 않는다.
예를 들어 enhanced picker off → native input, multipart concurrency off →
sequential, private Image CDN off → approved placeholder는 가능하다.
integrity off, arbitrary URL 허용, private response cache 또는 unbounded Blob은
fallback이 아니다.
### 14. Kill switch
최소한 다음 switch를 독립적으로 둔다.
- new presigned issuance
- direct object-storage data plane
- new upload session
- upload resume
- upload complete
- Range resume
- picker streaming save
- private image descriptor issuance
- advanced image format
- responsive candidates
switch 변경은 active operation의 의미를 소급 변경하지 않는다. 신규 진입을
닫은 뒤 reconcile/drain한다. remote runtime config를 사용한다면 config의
authenticity, release compatibility와 last-known-safe 정책을 별도 hosting 계약으로
검증한다.
## 관측성과 개인정보
허용:
- operation kind와 safe outcome
- runtime/readiness state
- byte/part/candidate/retry/age/deadline bucket
- policy rejection, abort, reconcile와 drain bucket
- aggregate active count, checkpoint count와 orphan age
금지:
- URL, query, signed/request/response header
- capability, bearer token, signature와 key material
- resource/session/asset/upload/account/tenant ID
- file name, local path, object key와 storage physical key
- digest, ETag, receipt와 checkpoint payload
- raw backend/browser exception message와 stack
runtime generation과 registry ID도 외부 telemetry에 그대로 보내지 않고 bounded
compatibility bucket으로 변환한다.
## Rollout과 rollback
### 15. Rollout
1. ledger와 관련 ADR을 accepted로 고정한다.
2. closed config/port와 provider contract harness를 먼저 구현한다.
3. fake와 negative fixture에서 partial composition/late result를 거절한다.
4. top-level runtime을 `AVAILABLE_NOT_COMPOSED`로 유지하고 removal gate를 만든다.
5. product owner, exact scope와 provider config를 선택하고 bootstrap에 kill
switch `DISABLED` 상태로 조합한다. 이 시점 primary status는 `COMPOSED`다.
6. 실제 BFF/storage/CDN conformance와 readiness probe를 통과한다.
7. Chromium/Firefox/WebKit과 실제 device에서 account switch, expiry와 crash를
검증한다.
8. runbook/rollback drill 뒤 internal cohort의 read-only/image public 또는 upload
shadow flow부터 연다.
9. private/image upload/download capability를 독립 canary와 kill switch로 확대한다.
### 16. Rollback
1. 신규 issuance/session/descriptor를 중지한다.
2. runtime을 `DRAINING`으로 바꾸고 bounded operation을 마무리한다.
3. ambiguous upload는 server reconcile하고 Range partial은 checkpoint 정책대로
보존 또는 삭제한다.
4. private capability를 backend에서 revoke하고 client runtime을 close한다.
5. old compatible composition을 새 generation으로 다시 생성하거나 기능을
unavailable로 유지한다.
6. optional source, config와 facade를 제거하고 production module inventory와
removal gate를 재검증한다.
schema version을 내리거나 checkpoint를 무조건 삭제해 rollback하지 않는다.
## 검증과 완료 기준
### 17. Deterministic
- partial factory failure의 reverse-order cleanup
- close idempotency와 closed-runtime rejection
- account switch 중 late fetch/hash/transaction/decode drop
- config duplicate/ceiling/missing provider fail-closed
- selector의 picker/size/resource matrix
- upload pause/abort/reconcile race와 bounded inventory
- descriptor refresh single-flight, expiry와 generation fence
- picture projection의 arbitrary URL/query 생성 0건
- diagnostics forbidden-value negative fixtures
### 18. Native browser
- Chromium/Firefox/WebKit의 native input/save picker fallback
- multi-tab upload pause/cancel과 unsupported Web Locks path
- page reload/account switch 중 active transfer drain
- public/private Image fetch, actual CORS/no-store와 bitmap decode
- offline/timeout/abort/late completion cleanup
- Safari/WebView/private mode의 approved selector result
### 19. Provider와 operations
- fake/emulator/실제 BFF·object storage·CDN 동일 contract suite
- signing key rotation과 emergency revoke drill
- checkpoint retention/orphan cleanup drill
- capability별 kill switch와 N-1 rollback
- runtime removal 후 source-module inventory 0건
다음 조건 전에는 목표 상태를 `AVAILABLE_NOT_COMPOSED`로 올리지 않는다.
- [ ] top-level closed config와 atomic factory가 구현됐다.
- [ ] generation-bound lifecycle과 account teardown이 구현됐다.
- [ ] `RESUMABLE_UPLOAD_PAUSE_V1`, checkpoint schema v2 old-writer
migration과 bounded upload inventory/retention owner가 구현됐다.
- [ ] `PRESIGNED_TRANSFER_V1`과 선택 protocol의 strict codec, unknown-version
rejection 및 reusable provider contract harness가 구현됐다.
- [ ] strategy selector가 browser fallback을 fail-closed한다.
- [ ] Image descriptor provider/refresh와 safe projection이 구현됐다.
- [ ] deterministic fault, boundary, removal test가 통과한다.
다음 조건 전에는 제품 상태를 `COMPOSED`로 올리지 않는다.
- [ ] product owner와 opaque account scope가 정해졌다.
- [ ] strict config를 사용하는 top-level runtime이 production bootstrap에서
생성되고 실제 feature facade consumer까지 연결됐다.
- [ ] traffic 기본값이 `DISABLED`이며 local teardown/close 경로가 연결됐다.
다음 조건 전에는 product-local production traffic을 승인하지 않는다.
- [ ] actual BFF/storage/CDN contract harness가 통과한다.
- [ ] browser/device evidence와 runbook drill이 보존됐다.
- [ ] kill switch와 rollback owner가 운영 승인됐다.
## 관련 문서
- [Browser data capability completion ledger](../browser-data-capability-completion-ledger.md)
- [Presigned transfer and Image CDN](../presigned-transfer-and-image-cdn.md)
- [VD-14 Resumable download와 background download](./VD-14-resumable-download-and-background-transfer.md)
- [Server file capability infrastructure](../server-file-capability-infrastructure.md)
- [Browser transfer recovery](../../operations/browser-transfer-recovery.md)
## 선택하지 않은 대안
- feature가 개별 transfer adapter factory를 직접 조합
- singleton runtime을 여러 account/tenant가 공유
- raw presigned/Image URL을 Query cache나 persistence에 저장
- Image descriptor endpoint/transform을 request마다 caller가 지정
- page abort를 upload pause 또는 server abort 완료로 간주
- browser-managed handoff를 저장 완료로 간주
- user-agent 문자열만으로 Safari fallback 결정
- Service Worker를 설치하면 background upload/download가 보장된다고 가정
- capability 일부만 준비된 partial runtime을 degraded success로 반환
## 결과
장점:
- account 전환과 teardown의 한 owner가 생긴다.
- 개별 runtime의 안전한 메커니즘을 제품별 facade로 좁혀 조합할 수 있다.
- provider mock과 실제 인프라 사이의 계약 차이를 같은 suite로 찾을 수 있다.
- Image URL과 transform이 application/presentation에서 재조립되지 않는다.
- capability별 rollout, kill switch와 제거가 독립적이다.
비용:
- config, lifecycle, provider harness와 browser evidence가 늘어난다.
- 제품이 선택하지 않은 capability는 여전히 조합할 수 없으며 이것이 의도된
결과다.
- actual provider와 운영 증거 없이는 reference runtime 구현만으로 production
완료를 주장할 수 없다.