[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단일 필드로 판정)