기능 요구사항 (FR-*)
이 페이지는 LogiNippon이 무엇을 해야 하는가를 테스트 가능한 단위로 고정한다. 인증·데이터 수집·인제스트·지오펜싱·트래킹 엔진(ETA·예외)·전달 계층·규제 리포트·프로비저닝의 모든 정규(normative) 기능 요구사항을 고유 ID 카드로 열거하고, 각 카드에 진술(반드시/권장)·우선순위(P0/P1/P2)·단계(Phase)·근거·수용기준(Given/When/Then)·구현상태(DONE/STUB/ABSENT) 6요소를 부여한다. 모든 임계 수치는 마스터 정규화 수치표(§4)에서 글자 그대로 인용했으며, 전부 운영 데이터가 없는 진입 단계의 초기 확정 목표(설계)이고 운영 베이스라인으로 분기별 조정·명시적 개정 트리거를 가진다 — 달성치가 아니다. 규제 산출물의 컬럼·포맷 등 상세 명세는 regulatory.html이 소유하며, 여기 FR-RPT 카드는 고위(高位) 수준에서 무엇을 만들지만 고정하고 링크한다.
읽는 법. 우선순위 P0·Must=MVP 출시에 반드시(MUST), P1·Should=권장(SHOULD), P2·Could=선택(MAY). 단계 배지 Phase 0(제휴·하드게이트) / Phase 1(MVP) / Phase 2(확장). 검증 배지 T 테스트·D 데모·A 분석·I 점검. 구현상태는 마스터 §7 as-built 사실에 따라 DONE(구현+테스트) / STUB(껍데기·ack-only·501) / ABSENT(미존재)로 표기한다.
FR 그룹 요약
본 페이지가 소유·정의하는 FR 그룹과 카드 수, 대표 임계·구현 성숙도다. 타 페이지가 소유한 ID(NFR/DM/IR/SR/RR/SLO)는 정의하지 않고 링크만 한다.
| 그룹 | 범위 | 카드 | 대표 임계·계약(§4 인용) | 구현 성숙도(§7) |
|---|---|---|---|---|
| FR-AUTH | 인증·토큰·권한·자격 프로비저닝 | 001–005 | access 3600s / refresh 2592000s / ingest 604800s, revocation ≤60s, PBKDF2 ≥100k | 대체로 DONE |
| FR-ACQ | 스마트폰 GPS 수집·동의·오프라인·案件 자동공유 | GPS/API/CONSENT/OFFLINE/AUTOSHARE | 샘플링 30/60/180s, 배치 500/100, 동의 하드게이트 | ABSENT(app stub) |
| FR-INGEST | 인제스트 수신·검증·멱등 | 001–003 | 202 ACK, 하드캡 500/req(413), at-least-once | DONE |
| FR-GEO | H3 지오펜싱·히스테리시스·dwell·CRUD | 001–003/CRUD/DEBOUNCE/H3RES | gridDisk(+1), dwell, res8/9/10/11, ENTER 단일핑 | 엔진 DONE / CRUD ABSENT |
| FR-ENG-ETA | 룰 ETA·ML 전환 트리거 | 001–002 | 시드 80/30/50 km/h·dwell 30min, 전환 ≥3,000 & MAPE>20% | STUB(ack-only) |
| FR-ENG-EXC | 예외 감지(지연·荷待ち·추적손실·이탈·정차·픽업) | 001–006 | +30min, >120min, 45min→TRACKING_LOST, 2km, 15min | 부분(cron sweep STUB) |
| FR-DLV | 웹훅·WebSocket·알림·추적링크 | WH/WS/NOTIFY/TRACK | 재시도 8회 래더 24h·auto-disable, HMAC-SHA256 | STUB(ack-only/501) |
| FR-RPT | 규제 리포트·범위·스코어카드(고위) | NIMACHI/JITSUUNSO/KOSOKU/RANGE/ANALYTICS | ≤92일·서명URL 300s — 상세는 regulatory.html | NIMACHI DONE / 나머지 501·ABSENT |
| FR-PROV | 운영자 프로비저닝(테넌트·캐리어·드라이버·지오펜스 UI) | 001–002 | console Provisioning, admin-provisioned 자격 | 서버 라우트 ABSENT |
FR-AUTH — 인증·인가·자격
API Key·토큰·권한 매트릭스·운영자 프로비저닝 자격을 고정한다. 토큰 TTL·로테이션·PBKDF2·revocation 전파 수치는 마스터 §4.5(SR/RET) 표의 FR-AUTH-001 행에서 인용한다. 권한 매트릭스(scope→endpoint→role) 자체는 SR-AUTHZ-001이 소유하며 여기서는 "강제(enforcement)" 요구만 정의한다. 토큰 저장 하드닝은 SR-TOKEN-STORE-001·OD-003으로 연결된다.
요구. 시스템은 세 종류의 토큰을 명시된 TTL로 반드시 발급해야 한다(MUST): access 3600s, refresh 2592000s, ingest 604800s(스코프 positions:write 한정). 비밀번호 해시는 PBKDF2 ≥100k 반복을 반드시 사용한다. 값은 진입 단계 초기 확정치이며 운영 베이스라인으로 조정한다(설계).
수용기준
- Given 유효 자격, When
POST /v1/auth/token, Thenexpires_in=3600의 access 토큰과 refresh 토큰을 반환한다. - Given access 토큰 발급 3601초 후, When 보호 엔드포인트 호출, Then
401 UNAUTHENTICATED. - Given ingest 토큰, When
shipments:read가 필요한 엔드포인트 호출, Then403 PERMISSION_DENIED(positions:write 한정 강제).
요구. refresh 토큰은 로테이션-only여야 하며(매 갱신 시 새 refresh 발급·기존 무효화), 재사용 탐지 시 해당 토큰 패밀리를 반드시 폐기한다(MUST). 키/토큰 revocation은 KV를 통해 ≤60s 내 전파되어야 한다.
수용기준
- Given 한 번 사용된(rotate된) refresh 토큰, When 그 토큰 재제출, Then 거부 + 토큰 패밀리 전체 무효화.
- Given revocation 등록, When ≤60s 경과 후 동일 토큰으로 호출, Then
401(KV 전파 확인). - Given 정상 갱신, When refresh 사용, Then 새 access+새 refresh 발급, 직전 refresh는 무효.
요구. 비밀번호는 PBKDF2 ≥100k 반복으로 솔트 해시 저장하고, 평문·약한 해시 저장을 반드시 금지한다(MUST). 자격은 admin-provisioned이며(가입 셀프서비스 없음 — FR-AUTH-005), 시드 비밀번호는 로컬 dev 외 사용 금지를 권장(SHOULD) 강제한다.
수용기준
- Given 신규 자격 등록, When DB 검사, Then 비밀번호는 PBKDF2(≥100k)+salt 형태로만 저장.
- Given 동일 평문 두 계정, When 해시 비교, Then 솔트로 서로 다른 해시.
요구. 모든 엔드포인트는 토큰의 scope와 tenant_id로 반드시 인가·필터링해야 한다(MUST). 타 테넌트 리소스는 존재를 비노출하기 위해 404로 응답한다(403 아님). 受取人(최종 고객) 스코프는 차량 위치를 노출하지 않는다(FR-DLV-TRACK-001). 매트릭스 정의는 SR-AUTHZ-001 소유, 본 카드는 강제만 요구한다.
수용기준
- Given 화주 A 토큰, When 화주 B의 shipment 조회, Then
404(존재 비노출). - Given
shipments:read만 가진 토큰, WhenPOST /v1/shipments, Then403 PERMISSION_DENIED. - Given 임의 보호 엔드포인트, When 토큰 없이 호출, Then
401 UNAUTHENTICATED.
요구. 사용자 자격(드라이버·운영자)은 운영자 프로비저닝으로만 생성되어야 하며(FR-PROV-001와 정합), 공개 셀프서비스 가입을 반드시 제공하지 않는다(MUST NOT). B2B 운송사 계약 단위 도입 모델과 정합한다.
수용기준
- Given 미등록 사용자, When 로그인 시도, Then 인증 실패 — 가입 경로 부재.
- Given 운영자가 드라이버 자격 생성, When 드라이버 로그인, Then 발급된 자격으로만 성공.
FR-ACQ — 데이터 수집 (스마트폰 GPS 1차 소스)
스마트폰 앱 GPS를 1차 데이터 소스로 확정한 acquisition.md의 수집 전략을 요구로 고정한다. "버튼 의존 제거"(案件 자동 공유 + 지오펜스 자동 이벤트)와 개인정보 동의 하드게이트가 핵심. 본 그룹의 구현 주체는 Flutter app이며, 마스터 §7에 따라 핵심 GPS 서비스가 전부 주석 처리된 scaffold stub 상태다 — 카드는 계약을 동결해 unblock하는 역할이다.
as-built 갭(§7). Flutter app의 백그라운드 GPS(TransistorLocationService)는 100% 주석 처리, hasConsent→false/token→null 하드코딩, 권한·동의 온보딩·증빙사진·푸시·l10n 배선 부재. 또한 서버 consentGate()는 lib에 존재하나 ingest 경로에서 미호출(FR-ACQ-CONSENT-001의 APPI 갭). 이 카드들은 계약을 정의해 클라이언트·서버 구현을 잠금 해제한다.
요구. 드라이버 앱은 컨텍스트별 샘플링 주기를 반드시 적용해야 한다(MUST): 이동 중 & 다음 지오펜스 2km 내 30s, 주행 중 60s, 정지 >10min 180s. distanceFilter 40m, heartbeat 120s, stopTimeout 5min, stationaryRadius 30m. 자원 예산은 배터리 ≤6%/h, 데이터 ≤10MB/일을 목표로 한다(초기 확정치, 운영 조정).
수용기준
- Given 차량이 다음 지오펜스 2km 내 주행, When 샘플링, Then 30s 주기로 좌표 생성.
- Given 차량 정지 10분 초과, When 샘플링, Then 180s 주기로 감속.
- Given 8시간 운행, When 자원 측정, Then 배터리 소모 ≤6%/h·데이터 ≤10MB/일 예산 내.
요구. 클라이언트는 좌표를 배치로 묶어 전송해야 한다(MUST): 목표 배치 100건(100건 또는 3min flush). 서버 하드캡은 500 samples/req이며 초과 시 413으로 거부한다. 오프라인 버퍼는 ≤100 청크(FR-ACQ-OFFLINE-001).
수용기준
- Given 100건 누적 또는 3분 경과, When flush, Then 단일
POST /v1/ingest/positions배치 1건. - Given 501 samples 요청, When 인제스트, Then
413거부. - Given 정상 배치, When 수신, Then
202ACK(FR-INGEST-001).
요구. 드라이버 위치 수집·전송은 유효한 동의(driver.consent_at)가 없으면 반드시 거부되어야 한다(MUST). 동의는 수집·이용 목적·기간을 명시한 하드게이트로, 클라이언트 전송 전과 서버 인제스트 경로 양쪽에서 강제한다. 個人情報保護法(APPI) 준거.
수용기준
- Given 동의 미획득 드라이버, When 위치 전송 시도, Then 클라이언트가 전송하지 않고 서버도 거부.
- Given 동의 철회, When 이후 핑, Then 수집 중단 + 운송사 운영자 알림.
- Given 동의 있음, When 인제스트, Then
consentGate()통과 후 처리.
요구. 통신 두절 구간의 좌표를 로컬 큐에 ≤100 청크로 버퍼링하고, 네트워크 복구 시 일괄 전송해야 한다(MUST). occurred_at(발생)과 received_at(수신)을 분리 저장해 늦게 도착한 핑도 정확한 시각으로 처리한다(DM-TS-001).
수용기준
- Given 30분 통신 두절, When 복구, Then 버퍼된 좌표가 occurred_at 보존된 채 일괄 전송.
- Given 버퍼 100 청크 초과, When 신규 청크, Then 가장 오래된 것부터 정책에 따라 처리(드롭/병합) 후에도 데이터 손상 없음.
요구. 드라이버가 案件을 수락하면 그 案件의 운송 구간 동안 위치 공유가 자동 활성화되어야 하고(MUST), 配達 완료 또는 案件 종료 시 자동 비활성화되어야 한다. "공유 시작" 수동 버튼에 의존하지 않는다. 공유는 案件 단위·기간 한정으로 個人情報保護法상 목적·기간을 명확히 한다(PickGo·project44 데이터 거버넌스 사상 확인).
수용기준
- Given 드라이버가 案件 수락, When 수락 직후, Then 백그라운드 위치 공유 ON(수동 조작 없이).
- Given 配達 완료/案件 종료, When 종료 이벤트, Then 위치 공유 자동 OFF.
- Given 배정된 案件 없음, When 평상시, Then 위치 미공유(목적·기간 외 수집 없음).
FR-INGEST — 인제스트 (수신·검증·멱등)
Ingest Worker는 인증·기본 검증 후 라이브(DO)와 비동기(Queues)로 분기하고 클라이언트에 빠르게 응답한다(acquisition.md·engine.md). 마스터 §7 기준 이 경로는 DONE(ingest→DO→queue→R2).
요구. POST /v1/ingest/positions는 인증·기본 검증을 통과한 배치에 반드시 202로 빠르게 ACK하고(MUST), 라이브 반영을 위해 Shipment DO에 push하며 비동기 처리를 위해 Queues에 enqueue해야 한다. accept 지연 목표는 NFR-PERF-001 p95 ≤ 200ms / p99 ≤ 400ms(SLI S1, 초기 확정치).
수용기준
- Given 유효 배치, When 인제스트, Then
202+ DO 라이브 상태 갱신 + geofence-eval 큐 enqueue. - Given 배치 수신, When 측정, Then accept 지연 p95 ≤ 200ms(SLI S1) 목표 추적.
요구. 인제스트는 좌표 범위(위/경도)·속도·단위·타임스탬프 형식을 반드시 검증해야 한다(MUST). 타임스탬프는 ISO 8601 +09:00 오프셋 명시를 요구하고 잘못된 형식은 거부한다(DM-TS-001 — 荷待ち 법정 계산 의존). 검증 실패는 422 VALIDATION_FAILED.
수용기준
- Given 위도 100 같은 범위 외 좌표, When 인제스트, Then
422거부. - Given
'Z'(UTC)만 있고 오프셋 모순 타임스탬프, When 인제스트, Then 거부(+09:00 규칙). - Given 비정상 속도 단위, When 검증, Then 거부 또는 정규화.
요구. Queues는 at-least-once 전달이므로 같은 이벤트가 두 번 처리될 수 있다 — 시스템은 dedup_key로 반드시 멱등 처리해야 한다(MUST). 키는 sha256(source_type | shipment_id | canonical_code | floor(occurred_at→분) [| stop_id])이며 D1 UNIQUE로 중복을 흡수한다(DM-DEDUP-001).
수용기준
- Given 동일 이벤트 2회 enqueue, When 소비, Then D1에 1건만 INSERT(UNIQUE 충돌 흡수).
- Given 멀티소스 중복(예: GEOFENCE + APP_GPS 동분), When 정규화, Then 동일 dedup_key로 1건.
FR-GEO — 지오펜싱 (H3 셀 멤버십)
지오펜스 판정을 폴리곤 PIP 대신 H3 셀 멤버십 + KV 역인덱스 O(1)로 치환한다(engine.md·ADR-0009). 히스테리시스는 gridDisk(+1) 링, 경계는 interior 확정 + boundary만 PIP 1회. 마스터 §7 기준 enter/exit/dwell 히스테리시스 엔진은 풀 구현 DONE이나, 온라인 지오펜스 CRUD 라우트는 ABSENT다.
요구. 지오펜스 등록·수정 시 폴리곤을 polygonToCells로 덮는 H3 셀 집합을 만들고 역인덱스 cell → geofence_id[]를 KV에 반드시 기록해야 한다(MUST). 핑마다 latLngToCell 1회 + KV 조회 1회로 멤버십을 O(1)에 판정한다(핑당 PIP 제거). 셀은 D1 geofence_cell에 kind(interior/boundary)와 함께 저장한다.
수용기준
- Given 지오펜스 등록, When 사전계산, Then KV에
h3:<cell>→geofence_id[]역인덱스 생성. - Given 거점 내부 핑, When 판정, Then KV O(1) 조회로 멤버십 확정(PIP 없이).
- Given 거점 밖 핑, When 판정, Then 히트 없음 = 상태 변화 없음.
요구. 경계 핑퐁(spurious ENTER/EXIT)을 막기 위해 진입은 정확한 덮개 셀에서, 이탈은 덮개 셀 + 1링(gridDisk(+1)) 밖으로 나가야만 EXITED를 발행해야 한다(MUST). 진입 반경 < 이탈 반경(50m<100m의 H3판). 경계 걸친 boundary 셀은 그 핑 하나에만 PIP 1회로 확정한다(하이브리드).
수용기준
- Given INSIDE 상태, When 핑이 덮개+1링 안에 머무름, Then EXIT 발행 없음(플래핑 0).
- Given INSIDE 상태, When 핑이 gridDisk(+1) 밖으로, Then EXITED 발행 + dwell 계산.
- Given boundary 셀 적중, When 판정, Then 그 핑만 PIP 1회로 내/외부 확정.
요구. EXITED 이벤트 시 dwell_minutes = actual_departure_at − actual_arrival_at(= EXITED ts − ENTERED ts)을 반드시 계산해 stop.dwell_minutes에 저장해야 한다(MUST). 이 값이 荷待ち 규제 리포트(FR-RPT-NIMACHI-001)와 過다체류 예외(FR-ENG-EXC-002)의 원천이다. 타임스탬프는 +09:00 명시(DM-TS-001).
수용기준
- Given ENTER 09:12·EXIT 11:35, When dwell 계산, Then
dwell_minutes=143저장. - Given dwell > 임계(120), When 측정, Then 荷待ち 초과 예외 발행(FR-ENG-EXC-002).
요구. 운영자는 지오펜스를 온라인으로 생성·수정·삭제할 수 있어야 하며(MUST), 변경 시 H3 덮개 셀과 KV 역인덱스·D1 geofence_cell을 원자적으로 재기록해야 한다(부분 갱신으로 KV/D1 불일치 금지). 핫 경로가 아닌 드문 쓰기로 설계한다(읽기 多·쓰기 少).
수용기준
- Given 폴리곤 수정, When 저장, Then KV 역인덱스와 D1 geofence_cell이 동시에 새 덮개로 일관 갱신.
- Given 재기록 중 실패, When 복구, Then KV/D1가 이전 또는 신규로 일관(부분 상태 없음).
- Given 지오펜스 삭제, When 저장, Then 관련 KV 엔트리·셀 제거.
요구. ENTER는 단일 핑으로 즉시 발행해야 한다(MUST) — 법정 荷待ち 도착 시계를 지연시키지 않기 위함. 단, stop 스코프 + PIP로 오탐을 제거하고, EXIT는 gridDisk(+1) 이탈 링 통과를 요구한다(비대칭 디바운스). 경계 플래핑의 spurious ENTER/EXIT는 0이어야 한다.
수용기준
- Given 차량이 거점 덮개 셀 첫 진입, When 단일 핑, Then 즉시 ENTERED(
actual_arrival_at기록) — 荷待ち 시계 시작. - Given 경계 근처 핑 흔들림, When 연속 판정, Then spurious ENTER/EXIT 0.
요구. 거점 폴리곤 면적별로 cover 해상도를 반드시 선택해야 한다(MUST): >50,000㎡→res8, 5,000–50,000㎡→res9, <5,000㎡→res10/11. 권위 변길이: res8≈0.53km, res9≈0.20km, res10≈76m, res11≈29m. 분석 컬럼은 h3_r10 고정. boundary 셀 비율은 <30%, ping-res와 cover-res는 다해상도 lookupCell로 정합한다.
수용기준
- Given 60,000㎡ DC, When cover 생성, Then res8 셀 집합 + boundary 비율 <30%.
- Given 3,000㎡ 도크, When cover 생성, Then res10/11 셀 집합.
- Given 분석 GROUP BY, When 집계, Then
h3_r10고정 컬럼 사용.
FR-ENG-ETA — ETA 예측 (룰 → ML)
콜드 스타트로 운행 데이터가 없으므로 룰 기반으로 시작해 정직한 "추정치"로 표기(eta_is_estimate=1)하고, 데이터 누적 후 ML로 전환한다(engine.md·ADR-0007). MAPE 목표는 룰 ≤25%·ML 후 ≤15%(KPI-ETA-001, 하드 SLO 아님). 마스터 §7 기준 handleEtaBatch는 ack-only STUB(룰 미구현).
요구. 룰 ETA는 ETA = now + Σ(잔여거리/구간속도) + Σ(잔여 dwell)로 반드시 계산해야 한다(MUST). 시드 속도: 고속 80·도심 30·기본 50 km/h; 기본 정차 dwell 30min; 속도/임계는 KV CONFIG에서 런타임 read(현재 미read)하고, 월 1회 구간 median 갱신; 거리/교통은 Google Maps로 보정. ETA는 eta_is_estimate=1로 추정 표기한다.
수용기준
- Given 고속 잔여 160km, When 룰 계산, Then 80km/h로 2h + 잔여 dwell 합산.
- Given ETA 산출, When 응답, Then
eta_is_estimate=true표기. - Given KV CONFIG 속도 변경, When 재계산, Then 런타임 read한 새 값 반영(현재 갭).
요구. 레인-클래스별 누적 ≥ 3,000 완료 운행 AND 룰-MAPE > 20%를 모두 만족할 때 그 레인-클래스의 ETA를 ML로 전환해야 한다(SHOULD). 피처: 시간대·요일·공휴일·실시간 위치/속도·구간 교통·날씨(JMA 무료 API)·과거 분포. 추론은 Workers AI(미래).
수용기준
- Given 레인-클래스 누적 2,999건, When 평가, Then 룰 유지(전환 안 함).
- Given 누적 ≥3,000 AND 룰-MAPE>20%, When 평가, Then 해당 레인-클래스 ML 전환 자격.
- Given 누적 ≥3,000 AND 룰-MAPE 18%, When 평가, Then 룰 유지(두 조건 AND).
FR-ENG-EXC — 예외 감지 (룰 엔진)
예외는 이벤트 도착 시(Queue Consumer)와 시간 경과 시(Cron)에서 평가한다(engine.md). "안 일어남"(추적 손실·픽업 미발생)은 Cron 5분 sweep으로 감지한다. 마스터 §7 기준 Cron sweepActive는 빈 구현 STUB이며, 예외 룰의 알림 발행은 웹훅 STUB(FR-DLV-WH-001)에 의존한다.
| ID | 예외 | 임계(§4.3) | 감지 위치 | canonical/코드 |
|---|---|---|---|---|
| EXC-001 | 배달 지연 | 약속 ETA +30min | Consumer/Cron | LATE_DELIVERY_PREDICTED |
| EXC-002 | 荷待ち 초과 | dwell > 120min | Consumer | DWELL_THRESHOLD_EXCEEDED |
| EXC-003 | 추적 손실 | 45min → TRACKING_LOST | Cron 5min | TRACKING_LOST |
| EXC-004 | 경로 이탈 | 2km | Consumer | ROUTE_DEVIATION |
| EXC-005 | 비정상 정차 | 계획 외 15min | Consumer | ABNORMAL_STOP |
| EXC-006 | 픽업 실패 | planned_pickup +30min | Cron | PICKUP_FAILED |
요구. 현재 ETA가 약속 ETA의 +30min 버퍼를 초과하면 배달 지연 예외(HIGH)를 반드시 발행해야 한다(MUST). 웹훅 SHIPMENT_EXCEPTION(canonical_code: LATE_DELIVERY_PREDICTED, delay_minutes)로 전파.
수용기준
- Given 약속 17:00, When 예측 ETA 17:35(+35min), Then 지연 예외 발행.
- Given 약속 17:00, When 예측 ETA 17:20(+20min), Then 예외 없음(버퍼 내).
요구. dwell이 > 120min(법정 2시간; geofence.dwell_threshold_min, 테넌트 override 가능)을 초과하면 荷待ち 초과 예외를 반드시 발행해야 한다(MUST). 이 임계는 物流効率化法 2시간 목표와 정합하며, 측정 원천은 FR-GEO-003 dwell이다.
수용기준
- Given dwell 143분, When EXITED 처리, Then 荷待ち 초과 예외(
DWELL_THRESHOLD_EXCEEDED) 발행. - Given 테넌트 override 90분, When dwell 100분, Then 초과 예외(override 임계 적용).
요구. 마지막 유효 핑 후 45min이 경과하면 Cron 5분 sweep이 TRACKING_LOST 예외를 반드시 발행해야 한다(MUST). 일본 도심 터널이 많아 너무 짧으면 허위 경보이므로 오경보율 <5%를 목표로 한다(초기 확정치, 운영 조정).
수용기준
- Given 마지막 핑 후 46분, When Cron 5min sweep, Then
TRACKING_LOST발행. - Given 마지막 핑 후 30분, When sweep, Then 예외 없음(임계 미달).
- Given 28일 운영, When 측정, Then 추적 손실 오경보율 <5% 목표 추적.
요구. 위치가 계획 경로에서 2km 벗어나면 경로 이탈 예외(MEDIUM~HIGH)를 발행해야 한다(SHOULD). Queue Consumer에서 이벤트 도착 시 평가한다.
수용기준
- Given 계획 경로, When 위치가 2.3km 이탈, Then 경로 이탈 예외.
- Given 계획 경로, When 위치가 1.5km 이내, Then 예외 없음.
요구. 계획 외 지점에서 15min 이상 정지하면 비정상 정차 예외(LOW~MEDIUM)를 발행할 수 있다(MAY). Queue Consumer 평가.
수용기준
- Given 계획 외 지점, When 16분 정지, Then 비정상 정차 예외.
- Given 지오펜스 내 정지, When 임계 경과, Then 비정상 정차 아님(荷待ち로 처리).
요구. planned_pickup_at 후 +30min이 경과해도 PICKUP 이벤트가 없으면 픽업 실패 예외(HIGH)를 반드시 발행해야 한다(MUST). "안 일어남" 감지이므로 Cron으로 평가한다.
수용기준
- Given planned_pickup 09:00, When 09:31에도 PICKUP 없음, Then 픽업 실패 예외.
- Given planned_pickup 09:00, When 09:12 PICKUP_COMPLETED, Then 예외 없음.
FR-DLV — 전달 계층 (웹훅·WebSocket·알림·추적링크)
처리된 데이터를 외부·사용자에게 전달한다(delivery.md). 원칙: REST+웹훅은 외부 연동, WebSocket은 UI 전용. ADR-0010에 따라 실시간 전송을 분리한다 — Flutter 단일-shipment 뷰=WebSocket(NFR-PERF-003 ≤1.5s), console 플릿 개요=6s 폴링(NFR-PERF-003b staleness 예산). 웹훅 카탈로그/계약은 IR-WH-001·IR-WS-001이 소유한다. 마스터 §7 기준 웹훅 전달(handleNotifyBatch)은 ack-only STUB, 웹훅 CRUD는 501이다.
요구. 화주는 이벤트 타입별 웹훅을 구독할 수 있어야 하고(MUST), 발송은 HMAC-SHA256({timestamp}.{raw_body}, endpoint_secret) 서명·replay ±300s 검증을 포함하며, 재시도는 래더 [0, 60, 300, 1800, 7200, 21600, 43200, 86400]s(8회, max_retries=8, 최대 전달수명 24h)를 따른다. 발신 타임아웃은 5s hard. 8연속 실패 시 구독을 auto-disable하고 대시보드에서 재활성화한다. 전달 성공률 목표 NFR-RELY-001 ≥99.0%/28일(초기 확정치).
수용기준
- Given 구독 + 이벤트 발생, When 전달, Then
X-LogiNippon-Signature(HMAC-SHA256) 헤더 + 멱등Event-Id포함 POST. - Given 수신자 5xx 반복, When 재시도, Then 0→60→300→1800…86400s 래더로 최대 24h.
- Given 8연속 실패, When 한도 소진, Then 구독
disabled+ 운영 알림. - Given 서명 timestamp가 ±300s 밖, When 수신자 검증, Then replay로 거부.
요구. 실시간 전송은 ADR-0010에 따라 반드시 분리되어야 한다(MUST): Flutter 단일-shipment 뷰는 Durable Object WebSocket(NFR-PERF-003 p95 ≤1.5s/p99 ≤3s), console 플릿 개요는 6s 폴링(NFR-PERF-003b >60s stale·>300s drop). WebSocket은 UI 전용이며 외부 연동 채널로 노출하지 않는다(외부는 웹훅/REST).
수용기준
- Given Flutter 단일 shipment 뷰, When 위치 push, Then WebSocket으로 p95 ≤1.5s 반영(목표).
- Given console FleetMap, When 폴링, Then
GET /v1/shipments/tracking6s 주기, 핀 >60s stale 표시. - Given 외부 시스템, When WS 연결 시도, Then 연동 채널로 노출 안 함(웹훅/REST로 유도).
요구. 운영팀 실시간 예외 알림은 LINE / LINE Works를 1순위 채널로 해야 한다(SHOULD) — 일본 B2B 현장 지배적(이메일·SMS보다 즉각 반응 확인). 화주가 커스텀 알림 규칙("지연 30분 초과 시 이메일")을 설정할 수 있어야 한다. 예외 큐는 심각도·시간순으로 정렬한다.
수용기준
- Given HIGH 예외, When 발생, Then LINE 즉시 알림 + 웹훅(채널 우선순위 적용).
- Given 화주 규칙 "지연>30분→이메일", When 조건 충족, Then 규칙대로 이메일 발송.
요구. 최종 고객(受取人) 추적 링크는 ETA만 노출하고 차량 실시간 위치를 반드시 비노출해야 한다(MUST, 個人情報保護法). GET /v1/shipments/{id}/tracking의 position 객체는 화주/운송사 스코프에서만 채워지고 受取人 스코프에서는 생략되며 ETA·status만 반환한다. staleness_seconds·tracking_rate는 정직하게 노출한다.
수용기준
- Given 受取人 스코프 토큰, When tracking 조회, Then
position생략, ETA/status만. - Given 화주 스코프, When tracking 조회, Then
position(lat/lon/h3_r10) +staleness_seconds노출. - Given 어느 스코프든, When tracking 응답, Then
tracking_rate정직 노출.
FR-RPT — 규제 리포트·분석 (고위)
규제 출력이 킬러 피처다(delivery.md). 여기서는 "무엇을 만들지"만 고위 수준으로 고정하고, 컬럼·인코딩·집계식·라이프사이클 등 상세 명세는 regulatory.html(RR-*)이 소유한다. 법정 공식 포맷이 미확정인 항목은 RR-LEGAL-001 법무 서명 플래그를 단다.
RR-LEGAL 플래그. 実運送体制管理簿·荷待ち·구속시간의 법정 공식 출력 포맷·필수 컬럼·CSV 인코딩(Shift_JIS 여부)은 법무 확인 전이다 추정. 본 페이지의 FR-RPT 카드는 "이 데이터를 생성·집계·보존한다"는 기능 요구만 고정하고, 포맷 준수 책임은 RR-LEGAL-001·OD-004로 이관한다.
요구. 시스템은 stop.dwell_minutes(FR-GEO-003)를 거점별·운송별·기간별로 집계해 荷待ち 기록 리포트를 반드시 생성해야 한다(MUST, 物流効率化法 2026.4 荷待ち 2시간 목표 확인). 임계 2시간 초과 건을 표시한다. 집계식·컬럼은 RR-NIMACHI-001 소유.
수용기준
- Given
GET /v1/reports/nimachi?from=&to=, When 호출, Then 메타 + R2 다운로드 URL(CSV). - Given dwell >120 건 포함 기간, When 생성, Then 초과 건 플래그된 집계.
요구. 시스템은 carrier.tier(元請/下請/孫請)·parent_carrier_id 계층에서 청부 체인을 구성하고 실시간 위치로 검증된 실운송 차량을 결합해 実運送体制管理簿를 반드시 자동 생성해야 한다(MUST, 改正物流法 2025.4 元請 의무화 확인). 컬럼·provenance는 RR-JITSUUNSO-001 소유.
수용기준
- Given
GET /v1/reports/jitsuunso?from=&to=, When 호출, Then 청부 체인 + 실운송 차량 결합 메타 + 다운로드 URL. - Given 다단계 하청 화물, When 생성, Then
actual_carrier(실시간 검증)가 관리부에 반영.
요구. 운행·체류 타임스탬프를 구속시간 산정의 객관적 보조 데이터로 제공해야 한다(SHOULD). 전용 노무관리 대체가 아니라 데이터 제공이다(2024년 문제 — 연 구속 3,400h·시간외 960h 상한 확인). 필드·집계는 RR-KOSOKU-001 소유.
수용기준
- Given 운행·체류 타임스탬프, When 집계, Then 구속시간 산정용 일/기간별 데이터 산출.
- Given 산출물, When 검토, Then "보조 데이터" 명시(노무관리 대체 아님).
요구. 리포트 범위는 from < to AND ≤ 92일이어야 하며(초과 422), 기본은 최근 30일 JST다. R2 산출물 다운로드 서명 URL TTL은 300s로 반드시 제한한다(MUST). R2 egress 무료라 반복 다운로드 비용은 0이다.
수용기준
- Given
from~to93일, When 리포트 요청, Then422. - Given from/to 생략, When 요청, Then 최근 30일 JST 기본 적용.
- Given 다운로드 서명 URL 발급 301초 후, When 접근, Then 만료(TTL 300s).
요구. 캐리어별·레인별 OTD·dwell(荷待ち)·tracking rate·예외율·CO₂ 추정 스코어카드를 제공해야 한다(SHOULD) — 대형 화주(특정 제1종 荷主) 도입 유도 핵심 도구. 지오 분석은 h3_r10 셀 GROUP BY로 공간 DB 없이 산출한다. KPI 공식은 north-star-kpi.html 소유.
수용기준
- Given
GET /v1/analytics/carriers/{id}, When 호출, Then OTD·dwell·tracking rate·예외율 지표 반환. - Given h3_r10 채워진 stop/event, When 집계, Then 체류 밀도 히트맵을 공간 DB 없이 산출.
FR-PROV — 운영자 프로비저닝
ADR-0010에 따라 console(Astro+Svelte)이 운영자/운송사 컨트롤 타워로 공식 채택되며, 프로비저닝(테넌트·캐리어·드라이버·차량·지오펜스 등록)을 담당한다. 마스터 §7 기준 console Provisioning 컴포넌트는 존재하나 서버 측 POST /v1/admin/drivers 등 라우트는 ABSENT다.
요구. 운영자는 console에서 테넌트·캐리어(tier·parent)·드라이버·차량을 프로비저닝할 수 있어야 한다(MUST). 생성된 드라이버 자격은 admin-provisioned(FR-AUTH-005)이며, 캐리어 계층은 FR-RPT-JITSUUNSO-001(実運送体制管理簿)의 청부 체인 원천이다.
수용기준
- Given 운영자 console, When 드라이버 생성, Then 자격 발급 + 드라이버 로그인 가능(FR-AUTH-005).
- Given 캐리어 등록, When tier/parent 지정, Then 청부 계층이 実運送体制管理簿에 반영.
- Given 차량 등록, When 드라이버 배정, Then shipment-vehicle-driver 연결 가능.
요구. 운영자는 console에서 거점 지오펜스 폴리곤을 등록·수정할 수 있어야 한다(MUST). 저장 시 서버는 H3 cover·KV/D1 재기록을 원자적으로 수행하고(FR-GEO-CRUD-001), 면적별 해상도 정책(FR-GEO-H3RES-001)을 적용한다.
수용기준
- Given 운영자가 폴리곤 그림, When 저장, Then 지오펜스 + H3 cover + KV 역인덱스 생성(FR-GEO-CRUD-001).
- Given 폴리곤 면적, When 저장, Then 면적별 해상도(res8/9/10/11) 자동 적용.
가칭·미프로비저닝 주의. 'LogiNippon'은 전 레포 가칭이며 모든 wrangler D1/KV/R2 ID는 placeholder(0000…000a)로 미프로비저닝 상태다. 사전 출시 확정/리네임은 OD-002(정식명)·OD-005(리소스 ID)에서 다룬다. 전 그룹의 구현 성숙도 종합과 검증 매핑은 acceptance-traceability.html 추적성 매트릭스를 참조.
근거·상호참조
- techspec: 03-data-acquisition/acquisition.md (FR-ACQ — 스마트폰 GPS·案件 자동공유·동의·오프라인·인제스트 파이프라인)
- techspec: 04-tracking-engine/engine.md (FR-GEO·FR-ENG-ETA·FR-ENG-EXC — H3 지오펜싱·히스테리시스·dwell·룰 ETA·예외 룰)
- techspec: 05-delivery-layer/delivery.md (FR-DLV·FR-RPT — 웹훅·WebSocket·LINE·추적링크·규제 출력·스코어카드)
- techspec ADR: ADR-0006(스마트폰 GPS) · ADR-0007(ETA 룰→ML) · ADR-0009(H3)
- code: server/README.md —
routes/{auth,jobs,shipments,reports,webhooks}.ts,ingest/positions.ts,lib/{auth,geofence,geofence-engine,h3,reports,webhook,db}.ts,consumers/{queue,cron}.ts,do/shipment.ts,migrations/0001..0003 - Research(근거): implications-for-us(案件 자동공유·연속성 한계)·tracking-engine(예외 유형·히스테리시스)·delivery-layer(채널·스코어카드)
- TRD 내부: data-model(DM-TS/DEDUP) · api-contracts(IR-API/WS/WH) · nonfunctional(NFR-PERF/RELY·RET) · security-privacy(SR-AUTHZ/CRYPTO/APPI/PII) · regulatory(RR-* 상세) · acceptance-traceability(추적성) · open-decisions-rfc(OD-002/003/004/005/007) · 마스터 §6 ADR-0010