비기능 요구사항 · 용량 · 비용 (NFR-*)
이 문서는 플랫폼이 어느 수준으로 동작해야 하는지를 측정 가능한 수치로 고정한다. techspec의 모든 목표(추정) 플레이스홀더(p95 "수백 ms 이내" 등)를 방어 가능한 구체 임계로 확정하고, 성능·가용성·신뢰성·보안 레이트리밋·관측성·유지보수성 NFR과 함께 진입 단계의 용량·부하 모델·스케일 레버·비용 추정·가드레일을 정의한다. 여기 모든 수치는 운영 데이터가 없는 진입 단계의 초기 확정 목표(설계)이며, 운영 개시 후 베이스라인으로 분기별 조정한다(달성치 날조 금지). SLI/SLO 카탈로그의 측정 윈도·쿼리·에러버짓 공식은 slo-catalog가 소유하고 이 페이지는 요약·링크한다.
읽는 법. 라벨 의미: 확인 외부·공식 출처로 검증된 사실, 추정 공개정보 추론, 설계 우리가 정한 결정·목표 수치. 모든 SLO/임계는 초기 확정치이며 운영 베이스라인으로 조정한다 — 달성치가 아니다. 구현 상태는 DONE(구현+테스트)·PARTIAL·STUB(껍데기/501/ack-only)·ABSENT(미존재)로 표기한다.
1. 요약 메트릭
핵심 NFR 임계를 한눈에. 모두 진입 단계 초기 확정 목표(설계)이며 출처는 §4 정규화 수치표다. 측정 인프라(METRICS.writeDataPoint)는 현재 미배선이라 현 시점 SLI 미측정이다(아래 NFR-OBS-001).
2. 성능 (NFR-PERF-*)
성능 임계는 techspec 흐름 1의 비동기 분리 설계에서 직접 도출된다: 클라이언트 응답(인제스트 accept)은 빨라야 하고, 무거운 처리(지오펜스 판정·정규화·ETA)는 Queues로 분리되어 별도 lag 예산을 갖는다. p95/p99 값은 §4.1 정규화 수치표를 글자 그대로 인용한다.
요구. POST /v1/ingest/positions 요청 수신부터 202 ack 응답까지의 accept 지연이 반드시(MUST) p95 ≤ 200ms, p99 ≤ 400ms를 만족해야 한다(초기 확정 목표, 운영 베이스라인으로 조정). DO push·queue enqueue까지만 동기 경로에 포함하고, 지오펜스 평가는 비동기로 분리한다.
수용기준
- Given 28일 롤링 윈도의 인제스트 요청 모집단, When Analytics Engine으로 accept 지연을 집계, Then p95 ≤ 200ms AND p99 ≤ 400ms.
- Given 배치 요청이 서버 하드캡 500 samples/req(FR-ACQ-API-001)에 근접해도, When 처리, Then 무거운 작업이 동기 경로에 끼어들지 않아(흐름 1) 임계 유지.
요구. 위치 enqueue부터 enter/exit 판정 및 event INSERT 완료까지의 평가 lag이 반드시(MUST) p95 ≤ 5s, p99 ≤ 15s를 만족해야 한다. 임계는 Queue 설정 max_batch_timeout=5s와 정합한다(배치 타임아웃이 p95 lag의 지배 항).
수용기준
- Given 큐 소비 경로(consumer), When enqueue→INSERT lag을 측정, Then p95 ≤ 5s AND p99 ≤ 15s.
- Given 큐 백로그 발생, When lag이 p95 임계를 초과, Then HIGH 알림(소비자 스케일·핫스팟 점검)으로 전환(pipeline-ops 인시던트).
요구. 위치 push부터 단일 shipment 뷰 대시보드 broadcast까지의 실시간 반영 지연이 p95 ≤ 1.5s, p99 ≤ 3s를 만족하도록 권장한다(SHOULD). 적용 경로는 Durable Object WebSocket fan-out으로 연결된 단일-shipment 뷰(Flutter)다. 플릿 개요는 폴링 경로(아래 NFR-PERF-003b)로 분리한다(ADR-0010).
수용기준
- Given DO에 WS로 구독한 대시보드, When 위치 push 발생, Then broadcast까지 p95 ≤ 1.5s AND p99 ≤ 3s.
요구. console 플릿 개요(FleetMap, 6s 폴링 경로)의 핀 staleness 예산은 핀 >60s = stale 표시, >300s = drop으로 반드시(MUST) 적용한다. 단일-shipment의 WS 실시간성(NFR-PERF-003)과 달리, 플릿 다수 핀은 6s 폴링으로 staleness를 명시적으로 노출한다.
수용기준
- Given FleetMap 6s 폴링 스냅샷, When 핀의 last_seen이 60s 초과, Then UI에 stale 표시. When 300s 초과, Then 해당 핀 drop.
3. 가용성 (NFR-AVAIL-001)
요구. 대시보드/API의 가용성(성공 응답 비율)이 반드시(MUST) ≥ 99.9% / 역월이어야 한다. 이는 월 에러버짓 0.1%/월 ≈ 43분을 의미한다. 엣지 자동 확장·무중단·리전 SPOF 회피(ADR-0001)를 전제로 한 초기 확정 목표이며, 운영 베이스라인으로 조정한다.
수용기준
- Given 역월 윈도의 API+Web 응답 모집단, When 성공 비율을 합성 점검+Web Analytics로 측정, Then ≥ 99.9%.
- Given 월 에러버짓 ≈ 43분, When 버짓이 소진, Then prod 승격 차단(릴리스 게이트) — 신규 기능보다 안정화·회귀 수정 우선(pipeline-ops 에러버짓 릴리스 게이트).
에러버짓 산식. 99.9% / 역월 → 버짓 = 1−0.999 = 0.1%. 30일 기준 0.1% × 43,200분/월 ≈ 43분/월의 다운타임 허용. 진입 단계엔 SLO·버짓을 느슨하게 시작해 베이스라인을 모은 뒤 조인다(거짓 경보 방지) 설계.
4. 신뢰성 (NFR-RELY-001)
요구. 웹훅 전달 성공률(첫 시도 또는 재시도 내 2xx 수신 비율)이 반드시(MUST) ≥ 99.0% / 28일이어야 한다. 발신 타임아웃은 5s hard로 둔다. 재시도 래더와 auto-disable 정책은 FR-DLV-WH-001(8회 래더, 24h 수명, 8연속 실패 시 auto-disable)이 소유하며 여기서 참조한다.
수용기준
- Given 28일 롤링 윈도의 웹훅 시도 모집단, When 첫 시도 또는 재시도 내 2xx 비율을 측정, Then ≥ 99.0%.
- Given 발신, When 엔드포인트 응답이 5s 초과, Then hard timeout으로 실패 처리·재시도 래더 진입(FR-DLV-WH-001).
5. 보안 — 레이트리밋 (NFR-SEC-RL-001)
요구. 시스템은 API Key별로 레이트리밋을 반드시(MUST) 적용해야 한다: API Key당 600 req/min 지속, 버스트 100/10s; 리포트 10 req/min; 인제스트 1200 req/min. 초과 시 429 + Retry-After 헤더를 반환한다. 인제스트 한도(1200/min)는 §6 용량 모델의 핑 유입을 흡수하도록 일반 API(600/min)보다 높게 설정한다.
수용기준
- Given API Key가 600 req/min 지속 한도를 초과, When 추가 요청, Then 429 + Retry-After.
- Given 10초 내 100건 버스트 초과 또는 리포트 10 req/min 초과 또는 인제스트 1200 req/min 초과, When 요청, Then 각 한도에서 429.
6. 관측성 (NFR-OBS-001)
요구. 모든 로그/메트릭에 request_id·shipment_id·tenant_id(개인정보 아닌 식별자) 상관 키를 반드시(MUST) 부착해 요청을 엣지→Queue→DO→D1로 추적할 수 있어야 한다. SLI(인제스트 지연·지오펜스 lag·웹훅 결과 등)는 Analytics Engine writeDataPoint로 차원(테넌트·이벤트타입·결과)을 부착해 반드시 방출해야 한다. 로그는 개인정보 최소화(좌표·실명 대신 식별자)한다.
수용기준
- Given 임의 인제스트 요청, When 엣지→Queue→DO→D1를 통과, Then 동일
request_id로 전 구간 상관 추적 가능. - Given SLI 대상 이벤트, When 코드 계측 실행, Then
METRICS.writeDataPoint가 차원 부착 데이터포인트를 방출(현재 TODO → SLI 미측정 갭 해소가 수용 조건).
7. 유지보수성 (NFR-MAINT-001)
요구. 배포는 무중단(zero-downtime)이어야 하며(SHOULD), 스키마 변경은 전진전용(forward-only) expand-first 마이그레이션으로 한정한다. 위험 기능은 feature flag로 게이트해 즉시 OFF(롤백 대체)할 수 있어야 한다. dev/staging/prod 3환경은 각각 독립 바인딩(별도 D1/KV/R2/Queues)을 갖는다. 거버넌스·CI 게이트·롤백·cron 락 상세는 pipeline-ops(OPS-*)가 소유한다.
수용기준
- Given 신규 배포, When
wrangler배포 진행, Then 진행 중 요청이 끊기지 않는다(무중단). - Given 스키마 변경, When 마이그레이션 작성, Then 파괴적 DROP/RENAME 없이 expand-first(추가→이행→정리 2단계)로만 진행.
- Given 위험 기능, When 인시던트, Then feature flag OFF로 즉시 비활성(근본 수정은 그다음).
8. 용량·부하 모델
운영 데이터가 없으므로 부하는 제1원리 계산으로 추정한다. 모든 값은 추정이며, 진입 후 실측 베이스라인으로 대체한다(OD-006 용량 베이스라인 post-launch).
계산 전제. 차량 N대 × 핑 주기 → 핑/일. 핑 주기는 적응형(FR-ACQ-GPS-001): 이동 중 60s(지오펜스 2km 내 30s), 정지 >10min 180s. 가동시간·주기 혼합을 보수적으로 평균 유효 60s/핑, 일 가동 10h로 가정 → 차량당 ≈ 600핑/일. 클라이언트는 100건/3분 배치(FR-ACQ-API-001)로 보내므로 요청 수는 핑/100 수준.
핵심: 핑은 D1 쓰기가 아니다. 원시 GPS는 DO push + R2 아카이브로 흐르고(흐름 1), D1 쓰기는 의미 이벤트(지오펜스 enter/exit·canonical event)에만 발생한다(ADR-0008). 1운행당 의미 이벤트는 대략 픽업·배달·경유 등 ≈ 10건으로 가정. 무료 티어(추정): D1 쓰기 10만 행/일, R2 egress 0(확인).
| 스케일 | 차량 N | 핑/sec (피크) | shipments/day | 동시 활성 DO | D1 쓰기/일 (이벤트만) | D1 쓰기 예산 헤드룸 |
|---|---|---|---|---|---|---|
| 런치 (제휴 1곳) | ~50 | ≈ 0.8/s (50×600핑 ÷ 36,000s) |
~50 | ~50 | ~500 (50×10) |
10만 대비 ≈ 0.5% (여유 큼) |
| 100거점 | ~100 | ≈ 1.7/s | ~100 | ~100 | ~1,000 | 10만 대비 ≈ 1% |
| 200거점 | ~200 | ≈ 3.3/s | ~200 | ~200 | ~2,000 | 10만 대비 ≈ 2% (헤드룸 충분) |
해석. 200거점 스케일에서도 D1 쓰기는 일 예산의 ≈2%로, 원시 핑을 D1에 넣지 않는 설계 덕분에 헤드룸이 크다. 만약 핑을 D1에 직접 적재했다면 200대×600 = 12만 행/일로 즉시 무료 티어 초과했을 것이다 — 이것이 ADR-0008 "D1엔 이벤트만"의 정량적 근거다. 동시 활성 DO는 운행 중 화물 수에 비례하며 200 수준은 NFR-SCALE-001 분할 트리거(>5,000)에 한참 못 미친다. 위 모든 값은 진입 후 OD-006 베이스라인으로 교정한다.
9. 확장 레버 (NFR-SCALE-001/002)
스케일이 무료 한도·DO 비용을 압박하기 전에 미리 완화 레버를 정의한다. 레버는 정량 트리거를 갖고, 트리거 도달 시 아키텍처를 조정한다(지금 필요 없는 복잡도는 사지 않는다 — 린 원칙).
요구. 동시 활성 DO가 > 5,000 OR DO active-wall-time 월 임계를 초과하면, 반드시(MUST) 종료(완료) 화물을 DO에서 내리고 D1 60s 폴링으로 전환한다(활성 화물만 DO 유지). Hibernation으로 유휴 DO는 이미 비용 0이므로, 이 레버는 동시 활성 수가 임계를 넘을 때만 발동한다.
수용기준
- Given 동시 활성 DO 수 모니터, When > 5,000 또는 active-time 월 cap 도달, Then 종료 화물 D1 60s 폴링 전환 가동.
요구. D1 쓰기/이벤트량이 한계에 근접하면 단계적 레버를 권장 적용한다(SHOULD): ① 원시 GPS는 D1 제외·R2 아카이브 + 의미 이벤트만(이미 적용) → ② 그래도 부족하면 이벤트 샤딩 / Worker 분할 / 분석 레이어 분리 → ③ Queues 처리량이 Kafka급으로 커지면 외부 스트리밍으로 이행(천장). 천장(③)은 진입 단계엔 적용하지 않으며 이행 경로만 보유한다.
수용기준
- Given D1 쓰기 추세, When 일 예산 근접 알람(NFR-COST-001), Then 샤딩/분석 레이어 분리 검토 착수.
- Given Queues 처리량이 Kafka급 규모로 성장, When 이행 결정, Then 외부 스트리밍 경로로 이행(설계 보유).
10. 비용 추정·가드레일 (NFR-COST-001)
techspec 비용 모델은 정성적·추정(달러 미산정)이다. 여기서 정성→정량 1차 시도를 한다: 위 §8 부하 추정을 무료 티어 한도에 대입해 진입 단계가 무료 티어 + 소액에 수렴함을 보인다. 정확한 단가·한도는 변동하므로 착수 시점에 Cloudflare 공식 가격으로 재확인한다(OD-005 리소스 프로비저닝).
| 프리미티브 | 무료 티어(추정) | 진입 단계 사용(추정) | 판정 |
|---|---|---|---|
| Workers | ~10만 req/일 | 인제스트는 100건/배치 → 200대 부하에서 수천 req/일 | 무료 내 |
| D1 쓰기 | 10만 행/일 | ≈ 2,000 행/일(이벤트만, §8) | ≈ 2% 사용 |
| D1 읽기 | 500만 행/일 | 대시보드·폴링·리포트 집계 | 무료 내 |
| KV 읽기 | 10만/일 | H3 역인덱스(읽기 多·쓰기 少) | 무료 내 |
| R2 | 10GB·egress 0 확인 | 증빙 사진·규제 PDF·GPS 아카이브; 반복 다운로드 전송비 0 | 초기 무료 내 |
| Durable Objects | 활성 시간·요청 기준 | 활성 화물만(Hibernation)·~200 동시 | 저스케일 미미 |
R2 egress 0의 구조적 우위 확인. 規制 출력(実運送体制管理簿·荷待ち 기록)을 화주가 매월 받아 감독당국 제출용으로 반복 다운로드해도 전송비 0이고, 원시 GPS 아카이브를 ML 학습에 반복 읽어도 egress 0이다. 이는 "약간 싸다"가 아니라 데이터를 적극 쌓고 반복 활용하는 트래킹 모델과 구조적으로 정합한다. 외부 비용 주의: Google Maps Platform 호출은 Cloudflare 외부 비용이라 캐싱·国土地理院 타일 병용으로 별도 관리한다.
요구. 비용·한도 근접을 사전 감지하기 위해 가드레일 알람을 반드시(MUST) 둔다: D1 쓰기 ≥ 70% 일 예산, R2 스토리지 ≥ 80% 티어, DO active-time 월 cap 도달 시 비용/한도근접 대시보드 알람. 임계 도달은 NFR-SCALE-001/002 레버 검토의 선행 신호다.
수용기준
- Given D1 일 쓰기량 모니터, When ≥ 70% 일 예산 도달, Then 대시보드 알람(아카이브·샤딩 레버 검토).
- Given R2 스토리지, When ≥ 80% 티어, Then 알람. Given DO active-time, When 월 cap 도달, Then 알람 + 폴링 분할 검토(NFR-SCALE-001).
법무 서명 플래그. 데이터 보존 기간(RET-* — D1 의미 이벤트 5년, R2 규제 산출물 7년 등)과 規制 산출물의 공식 포맷은 법무 확인이 필요한 미확정 사항이다 추정. 보존 정책은 security-privacy(SR-APPI-RETENTION-001), 공식 포맷 법무 확인은 regulatory(RR-LEGAL-001)가 소유한다. 이 페이지의 R2 스토리지 비용 추정은 이 보존 기간에 의존하므로, 보존 정책 확정 시 재계산한다.
요약(정직성). 이 페이지의 모든 임계(p95/p99·99.9%·43분·≥99%·600 req/min·>5,000·70%/80%)는 운영 데이터가 없는 진입 단계의 초기 확정 목표(설계)이며 달성치가 아니다. 현재 writeDataPoint 미배선으로 SLI는 미측정 상태이고, 레이트리밋·비용 알람·합성 점검은 미구현(ABSENT)이다. 모든 목표는 운영 개시 후 베이스라인을 모아 분기별 조정하며 명시적 개정 트리거를 보유한다(OD-006).
근거·상호참조
- techspec: 01-architecture/overview.md — 흐름 1(인제스트 비동기 분리)·NFR 표·3환경 토폴로지·Queues→외부 스트리밍 이행 경로
- techspec: cost-model.md — 무료 티어 추정·R2 egress 0(확인)·스케일 비용 레버·H3 지오스페이셜 비용
- techspec: 08-operations/observability-slo.md — SLI/SLO S1~S8·에러버짓·알림·request_id 상관·writeDataPoint
- techspec: ADR-0008(D1엔 이벤트만)·ADR-0001(0대 운영·DO 폴링 분할)·ADR-0009(H3)
- TRD 상호참조 — SLO 카탈로그: 03-quality/slo-catalog.html (S1·S2·S3·S5·S6·OBS-METRICS-001 소유) · 파이프라인: 04-delivery/pipeline-ops.html (무중단 배포·에러버짓 릴리스 게이트·OPS-*)
- TRD 상호참조 — 기능: 02-requirements/functional.html (FR-ACQ-GPS-001·FR-ACQ-API-001·FR-DLV-WH-001) · 보안: 03-quality/security-privacy.html (RET-*·인가) · 규제: 03-quality/regulatory.html (RR-LEGAL-001) · 미해결: 04-delivery/open-decisions-rfc.html (OD-005·OD-006)
- 구현(as-built): server —
ingest/positions.ts·consumers/queue.ts·lib/geofence-engine.ts·do/shipment.tsDONE;handleNotifyBatch/레이트리밋/비용 알람/메트릭 방출 미구현