[FE-06] 운영 화면 — 수집 실행 이력 · 알림 발송 실패 이력

작업 내용 (설계 의도)

근거 설계: 20260722-공고알림앱-design-fe-web.md — “S-11 운영” 근거 PRD: Operations · 시나리오 4(소스 고장) · 시나리오 7(알림 발송 실패)

변경 사항

알림 자체가 실패하는 상황을 알림으로 알릴 수 없으므로, 알림에 의존하지 않는 확인 수단이 필요합니다(시나리오 7). 이 화면이 그 수단입니다.

과하게 만들지 않는 것이 설계 의도입니다 — 차트·대시보드·기간 커스텀 필터를 만들지 않고 탭 2개 + 요약 수치 1개 + 목록으로 끝냅니다(토스 Minimum Features). BE가 Prometheus·Grafana를 도입하지 않고 DB 테이블을 관측 대상으로 삼은 판단과 같은 방향입니다.

핵심 설계 의도:

  • 0건 회차를 성공과 구분해 표기하는 것이 이 화면의 핵심입니다. BE 설계상 “예외 없이 0건 반환”은 silent failure이며 비정상 회차로 분류됩니다. UI가 [성공]으로 표기하면 관측 목적 자체가 무너지므로 [0건] 비정상으로 분류됨 warning 칩으로 별도 표기합니다.
  • 소스 가드 상태를 함께 노출합니다 — “3일 연속 비정상 · 마감 판정 제외 중”(Operations 요구). 사용자가 “왜 이 소스 공고가 마감 처리되지 않는가”를 이 화면에서 답할 수 있어야 합니다.
  • 알림 실패 이력에는 재발송 정책을 문구로 명시합니다 — “다음 발송 시각에 다시 시도해요”. 사용자가 수동 조치를 찾아 헤매지 않게 합니다(BE가 다음 09:00 배치에서 자동 재발송).
  • 알림 실패 0건 empty는 긍정 톤으로 렌더합니다(“실패한 알림이 없어요”) — 에러 화면처럼 보이면 안 됩니다.
  • 30일 롤링 성공률은 서버 집계값을 표시합니다(계약 요청 #9). 클라이언트에서 재계산하지 않습니다.
  • 삭제·정리 UI를 만들지 않습니다 — 모든 데이터를 보존합니다(NFR-5).

범위: src/pages/operations/OperationsPage.tsx(FE-01 스텁 대체), 전용 컴포넌트(CollectionRunItem·NotificationDispatchItem), src/api/operations.ts, src/hooks/operations/**.

의존

  • FE-02, FE-03, FE-04
  • BE 의존: BE-19 (운영 조회 API) — 실 연동은 BE-19 머지 후, 개발·테스트는 MSW 목으로 선행

다이어그램

처리 흐름

sequenceDiagram
    participant U as 사용자
    participant P as OperationsPage
    participant H as useCollectionRuns
    participant A as api/operations
    participant S as 서버
    U->>P: /operations 진입
    P->>H: days=30
    H->>A: GET /api/operations/collection-runs
    A->>S: 요청
    S-->>A: 이력 + successRate
    A-->>H: 정규화 응답
    alt 회차가 abnormal
        P-->>U: [0건] 또는 [실패] + 가드 상태 표기
    else 정상
        P-->>U: [성공] + 수집 건수
    end
    U->>P: 알림 실패 탭 전환
    P->>H: dispatchStatus=FAILED

클래스 의존

flowchart LR
    subgraph Page["pages/operations"]
        Ops[OperationsPage]
        RunItem[CollectionRunItem]
        DispatchItem[NotificationDispatchItem]
    end
    subgraph Hooks["hooks/operations"]
        H1[useCollectionRuns]
        H2[useNotificationDispatches]
    end
    subgraph Api["api"]
        Client[operations.ts]
    end
    subgraph Ui["components/ui"]
        Tabs[Segment]
        Empty[EmptyState]
        Err[ErrorState]
        Skel[Skeleton]
    end
    Ops --> H1
    Ops --> H2
    Ops --> Tabs
    Ops --> RunItem
    Ops --> DispatchItem
    Ops --> Empty
    Ops --> Err
    Ops --> Skel
    H1 --> Client
    H2 --> Client

테스트 케이스

  • 수집 이력 탭에 소스별 실행 결과가 최신순으로 렌더된다
  • 30일 롤링 성공률이 서버 응답값 그대로 표시된다
  • 성공 회차가 [성공]과 수집 건수로 렌더된다
  • 0건 회차가 [성공]이 아니라 [0건] 비정상으로 렌더된다
  • 실패 회차가 [실패]와 오류 메시지로 렌더된다
  • 소스 가드가 적용된 소스에 “마감 판정 제외 중” 상태가 함께 표시된다
  • 알림 실패 탭 전환 시 발송 실패 이력이 시도 횟수·마지막 오류와 함께 렌더된다
  • 알림 실패 항목에 “다음 발송 시각에 다시 시도해요” 재발송 안내가 표시된다
  • 같은 대상의 실패 이력이 여러 건이면 모두 렌더된다
  • 수집 이력이 0건이면 “첫 수집은 오늘 자정” 안내 empty가 렌더된다
  • 알림 실패가 0건이면 긍정 톤 empty(“실패한 알림이 없어요”)가 렌더된다
  • 조회 실패(500) 시 에러 안내와 [다시 시도]가 렌더되고, 재시도 클릭이 재요청을 발생시킨다
  • 로딩 중 스켈레톤이 렌더되고 탭은 즉시 조작 가능하다
  • 탭 전환 시 다른 탭의 에러가 현재 탭에 표시되지 않는다
  • 이력 삭제 버튼이 존재하지 않는다

2차 갱신 반영 (2026-07-22)

guardApplied 제거 (계약 응답 #9 부분 수용)

  • BE가 collection-runs 응답에서 guardApplied를 제거하고 abnormal 단일 필드로 확정했습니다. “마감 판정 제외 중” 표기를 guardApplied가 아니라 **abnormal === true**를 근거로 렌더합니다 — 비정상 회차이면 소스 가드가 걸린 것이므로 두 개념이 이 화면에서는 동치입니다.
  • guardApplied를 참조하는 코드·타입·목을 두지 않습니다.

수정 테스트 케이스 (기존 케이스 대체)

  • 수집 회차가 abnormal이면 “마감 판정 제외 중” 상태가 함께 표기된다 (guardApplied 참조 없이)
  • 응답에 guardApplied 필드를 기대하지 않는다 (abnormal 단일 필드로 판정)