[FE-19] 공고 수집 cycle 운영 가시성

작업 내용 (설계 의도)

근거: 확장 안전 공고수집 배치 FE 웹 설계, BE TDD의 additive GET /api/operations/collection-runs 계약(BE-40).

변경 사항

  • 기존 CollectionRun/CollectionRunsResponse에 nullable additive cycle field, 서버 소유 closeGuarded·abnormal, summary 타입을 추가하고, API·TanStack Query 경로는 같은 GET URL과 queryKey를 유지한다.
  • 기존 CollectionRunsResponse에 full additive slaSnapshot 객체(snapshotDate, scheduledAt, captureStatus, capturedAt, missReason, lateBeforeNotificationCount, items)를 type화한다. lateBeforeNotificationCount만으로 snapshot 상태를 추론하지 않는다.
  • OperationsPage의 기존 수집 이력 탭에 summary가 있으면 읽기 전용 수집 운영 요약을 렌더하고, slaSnapshot.captureStatus로 09:00 KST SLA 영역을 렌더한다. PENDING(점검 예정), CAPTURED(완료 + 0/양수 count 및 immutable items), MISSED(누락 + missReason), NOT_APPLICABLE(적용 전), UNAVAILABLE(조회 불가)를 구별한다. FE는 시간 비교나 목록 합산을 하지 않는다.
  • SLA snapshot item은 jobSourceId, sourceLabel, snapshot 당시 cycleStatus, fetchedCount, abnormal, closeGuarded만 소비한다. listPageCountdetailRequestCount는 immutable snapshot에 요구하거나 current cycle에서 보충하지 않으며, live CollectionRun 행에서만 표시한다.
  • 순수 presentation 로직에서 v2 cycleStatus를 우선 해석하고 NOT_STARTED/PENDING/RUNNING/BUDGET_EXHAUSTED/SUCCESS/FAILED를 각각 neutral/neutral/accent/warning/positive/danger로 표현한다. NOT_STARTED는 시작 전아직 수집을 시작하지 않았어요를 보이며 실패/지연으로 표현하지 않는다. 새 필드가 없는 과거 이력은 현재 runStatus/abnormal 표현으로 폴백한다.
  • JSON에 v2 closeGuarded 키가 존재하면 true/false/null 서버 값을 직접 사용하고 isComplete/abnormal/cycleStatus로 가드를 추론하지 않는다. 키 자체가 없는 legacy 응답에서만 runStatus !== 'SUCCESS' || abnormal === true로 폴백한다. 따라서 서버가 closeGuarded=true로 준 완료된 0건 비정상 cycle도 가드한다. 예산 소진은 안전 상한으로 중단됨이라는 운영 경고로 보여 주며 소스 고장/비정상으로 취급하지 않는다.
  • MSW fixture/handler와 API·hook·페이지·presentation 테스트를 additive 계약으로 갱신한다. 새 라우트, 새 write API, 실행/재시도/예산 설정 버튼, 전역 store, 색 토큰은 추가하지 않는다.

롤백: FE를 이전 빌드로 되돌리면 additive JSON 필드는 무시된다. 새 FE도 필드 부재를 legacy UI로 처리한다.

의존

  • BE-40 — collection-runs additive API contract. FE 구현은 BE가 병합되기 전에도 MSW 계약으로 시작할 수 있으나, 실제 통합 검증은 BE-40 완료 후 수행한다.

다이어그램

처리 흐름

sequenceDiagram
    participant User as 사용자
    participant Page as OperationsPage
    participant Query as useCollectionRuns
    participant API as GET collection-runs
    User->>Page: 운영 > 수집 이력 열기
    Page->>Query: days=30 조회
    Query->>API: GET
    API-->>Query: items + additive summary
    Query-->>Page: Query cache data
    Page-->>User: summary + cycle 상태 행

클래스 의존

flowchart LR
    Page[OperationsPage] --> Hook[useCollectionRuns]
    Hook --> Api[fetchCollectionRuns]
    Api --> Types[CollectionRunsResponse]
    Page --> Summary[CollectionOperationsSummary]
    Page --> Item[CollectionRunItem]
    Item --> Present[describeCollectionRun]
    Present --> Types

테스트 케이스

  • Given slaSnapshot.captureStatus=CAPTURED, lateBeforeNotificationCount=3, immutable items가 있을 때 When 수집 이력 탭을 렌더하면 Then 09:00 KST 점검 완료, 09:00 KST 시점 미완료 3개와 당시 미완료 소스 목록이 보인다.
  • Given CAPTURED snapshot item과 별도 live cycle의 목록/상세 요청량이 있을 때 When SLA item 목록을 렌더하면 Then snapshot 당시 source/status만 보이고 live listPageCount/detailRequestCount는 섞여 표시되지 않는다.
  • Given slaSnapshot.captureStatus=PENDING과 count=0일 때 When 수집 이력 탭을 렌더하면 Then 09:00 KST 점검 예정이 보이고 0건 완료/위반 없음으로 표현되지 않으며 FE는 브라우저 시각을 비교하지 않는다.
  • Given slaSnapshot.captureStatus=MISSED, NOT_APPLICABLE, UNAVAILABLE일 때 When 수집 이력 탭을 렌더하면 Then 각각 점검 누락(+missReason), SLA 점검 적용 전, SLA snapshot 조회 불가가 구별되어 보이고 current cycle로 count/items가 대체되지 않는다.
  • Given cycleStatus=NOT_STARTED일 때 When 행을 렌더하면 Then neutral 시작 전, 아직 수집을 시작하지 않았어요가 보이고 실패/예산 소진 문구는 보이지 않는다.
  • Given cycleStatus=BUDGET_EXHAUSTED이고 isComplete=false일 때 When 행을 렌더하면 Then warning 예산 상한 도달, 안전 상한으로 중단됨, 마감 판정 제외 중이 보이고 소스 고장 문구는 보이지 않는다.
  • Given cycleStatus=RUNNING과 목록/상세 요청량이 있을 때 When 행을 렌더하면 Then accent 진행 중목록 Np · 상세 N건, 마감 제외 문구가 보인다.
  • Given cycleStatus=SUCCESS, isComplete=true, fetchedCount=0, abnormal=true, closeGuarded=true일 때 When 행을 렌더하면 Then 0건 비정상 표현과 마감 판정 제외 중이 함께 보여 서버 가드가 직접 소비된다.
  • Given v2 row에 closeGuarded=falseabnormal=true가 있을 때 When 행을 렌더하면 Then FE는 자체 추론으로 가드를 추가하지 않고 서버 값대로 마감 제외 문구를 숨긴다.
  • Given v2 row에 closeGuarded=nullabnormal=true가 있을 때 When 행을 렌더하면 Then FE는 legacy 폴백을 적용하지 않고 마감 제외 문구를 숨긴다.
  • Given 새 additive 필드가 없는 기존 SUCCESS/abnormal/FAILED fixture일 때 When 행을 렌더하면 Then 현재 성공·0건 비정상·실패 표현과 가드 동작이 그대로 유지된다.
  • Given summary가 없거나 모든 count가 0일 때 When legacy 또는 정상 목록을 렌더하면 Then summary card는 보이지 않고 기존 성공률·목록만 보인다.
  • Given collection-runs GET이 실패할 때 When 수집 탭을 렌더하면 Then 기존 ErrorState와 다시 시도로 재조회할 수 있다.
  • Given 다크 모드일 때 When summary와 각 신규 상태를 렌더하면 Then 시맨틱 토큰 class만 사용하고 원색 class/inline 색이 없다.