[FE-44] 소스 상태 레지스트리 · 매칭 범위 재평가 패널

작업 내용 (설계 의도)

근거: 지원 관리 확장 FE 웹 설계 S-11 확장 (3단계) — 소스 상태 레지스트리 · 상태 표기 규칙 · 신규 순수 유틸 · Query 규약(무효화 매핑·operations staleTime 0) · API 연동 > 3단계 · Testing Plan > 반드시 커버할 실패·엣지 경로 #44~#46, 지원 관리 확장 TDD 3단계 — 소스 상태 레지스트리 (FR-88) · 3단계 — 소급 재평가 백필 (FR-87).

변경 사항

  • 기존 운영 화면(S-11)에 세그먼트 1개를 추가합니다(수집 이력 / 알림 실패 / 웹훅 수신 / 서류 실패 / 소스 상태). 새 라우트를 만들지 않고 운영 탭 하위에 둡니다 — 이상 감지는 운영 화면의 기존 목적입니다.
  • 운영 세그먼트 라벨 축약을 이 티켓이 담당합니다(방안 15). 이 티켓이 소스 상태를 추가하면 세그먼트가 5개가 되는데, 운영 화면 컨테이너는 max-w-md(448px) + px-4 = 가용 416px이고 5자 라벨 5개는 약 478px라 넘칩니다(4개까지는 약 384px로 안전). Segment(components/ui/Segment.tsx:15)는 주석부터 “2~3개 전환”용이고 inline-flex라 넘치면 잘립니다.
  • 따라서 5개 라벨을 전부 2자로 축약합니다 — 수집 · 알림 · 웹훅 · 서류 · 소스(약 268px). 운영 화면 안에서는 축약해도 의미가 유지됩니다. 공용 Segment 프리미티브는 개조하지 않습니다 — 가로 스크롤로 바꾸면 이 프리미티브를 쓰는 기존 5개 화면에 파급되고, 회귀 검증 비용이 라벨 축약보다 큽니다.
  • 소스 목록은 { sources: [] } 전량 응답(30건 미만)이라 페이지네이션 없이 한 번에 받고, 상태별 집계를 클라이언트에서 합산합니다. 합산 로직은 컴포넌트가 아니라 utils/sourceRegistry.ts(FE-40 소유 순수 유틸)를 호출합니다.
  • 정렬은 문제 있는 상태 우선입니다 — 접근 제한 → 일시 실패 → 미지원 → 발견됨 → 활성 → 비활성. 정상 소스가 위에 쌓이면 이상 소스를 스크롤로 찾아야 해서 화면 목적이 무너집니다. 정렬 기준도 순수 유틸에 고정하고 컴포넌트에서 재정의하지 않습니다.
  • registryStatus === null(레지스트리 백필 전)은 “상태 미확인” 중립 칩으로 표시합니다. 오류나 비활성으로 표현하면 배포 순서상 정상인 대기 구간이 장애로 보입니다.
  • 상태 6종 칩은 기존 토큰 매핑만 씁니다 — 활성 filled positive / 발견됨 outlined neutral / 일시 실패 filled warning / 접근 제한 filled danger / 미지원 filled neutral / 비활성 none. 신규 색 토큰 0건입니다.
  • 각 카드는 계약 필드만 소비합니다 — platform·sourceType·searchCategoryCode ?? sourceSlug·consecutiveAbnormalDays·lastNormalAt. FE가 lastCollectedAt으로 이상 여부를 추론하지 않고 서버의 registryStatus를 그대로 신뢰합니다.
  • 세그먼트 하단에 매칭 범위 재평가 패널을 둡니다 — dryRun 체크박스 + 실행 버튼 + 결과 4수치(processedCount·newlyMatchedCount·notificationSuppressedCount·unmatchedNowCount). 배포 절차 3-5/3-7에서 쓰는 운영 도구입니다.
  • dryRun=true일 때는 “적용됐어요” 문구를 렌더하지 않습니다. 시뮬레이션 결과를 적용 완료로 읽으면 운영자가 실제 실행을 건너뜁니다. 실행 결과 카드에 dryRun 여부를 명시합니다.
  • 실행은 동기이며 수 초~수십 초가 걸리므로 진행 중 버튼을 비활성화하고 대기 UI를 유지합니다. 성공 시 ['job-postings'] 접두사 전체를 무효화해 보관함·공고 상세의 매칭 표시가 새 기준으로 갱신되게 합니다.
  • 롤백: 재평가 실행은 되돌릴 수 없으므로 UI가 dryRun 선실행을 권장 문구로 안내하고, 실제 실행 전 확인 단계를 둡니다. 잘못 실행된 결과의 원복은 BE 재평가 재실행으로만 가능합니다.
  • pages/operations/OperationsPage.tsx는 3단계에서 이 티켓만 수정합니다(FE-28은 단계 1, FE-50은 단계 2). 라벨 축약도 같은 파일 수정이므로 이 티켓 범위 안에서 끝납니다.

의존

  • FE-40 — SourceRegistryResponse·JobSourceRegistryStatus·FieldScopeBackfillResponse 타입, ['operations','source-registry'] queryKey, 6종 상태 + null fixture, dryRun 반영 목, utils/sourceRegistry.ts를 사용합니다.
  • BE-78 — GET /api/operations/source-registry 구현.
  • BE-74 — POST /api/matching/field-scope-backfills 구현.
  • 라우트 신설이 없어 FE-41에 의존하지 않습니다.

다이어그램

처리 흐름

sequenceDiagram
    participant User as 운영자
    participant Page as OperationsPage
    participant Hook as useSourceRegistry
    participant Backfill as useFieldScopeBackfill
    participant API as 소스·백필 API
    User->>Page: 운영 > 소스 상태 열기
    Page->>Hook: 전체 소스 조회
    Hook->>API: GET source-registry
    API-->>Hook: sources[]
    Hook-->>Page: 집계 + 문제 우선 정렬
    User->>Backfill: dryRun 체크 후 재평가 실행
    Backfill->>API: POST field-scope-backfills
    API-->>Backfill: 통계 4수치
    Backfill-->>User: dryRun 표기된 결과 카드

컴포넌트 의존

flowchart LR
    Ops[OperationsPage] --> Section[SourceRegistrySection]
    Section --> Hook[useSourceRegistry]
    Hook --> Api[fetchSourceRegistry]
    Section --> Util[utils/sourceRegistry]
    Section --> Chip[Chip 기존 UI]
    Section --> Panel[FieldScopeBackfillPanel]
    Panel --> Mutation[useFieldScopeBackfill]
    Mutation --> BackfillApi[postFieldScopeBackfill]

테스트 케이스

  • 운영 세그먼트가 5개(수집·알림·웹훅·서류·소스) 축약 라벨로 렌더되고 잘리지 않는다.
  • 소스 15건을 받으면 상태별 집계(활성 12 · 일시 실패 2 · 접근 제한 1)와 카드 15장이 보인다.
  • 소스 목록이 접근 제한 → 일시 실패 → 미지원 → 발견됨 → 활성 → 비활성 순으로 정렬돼 렌더된다.
  • registryStatus === null인 소스에는 “상태 미확인” 중립 칩이 보이고 비활성·오류 표기로 렌더되지 않는다.
  • 6종 상태가 각각 다른 칩 라벨로 표시되고 consecutiveAbnormalDays·lastNormalAt이 카드에 함께 보인다.
  • 소스 목록 조회가 실패하면 ErrorState[다시 시도]가 보이고, 재시도하면 다시 조회한다.
  • 등록된 소스가 0건이면 “등록된 소스가 없어요” 빈 상태와 [회사 등록]이 보인다.
  • dryRun=true로 재평가를 실행하면 통계 4수치가 결과 카드에 보이고 “적용됐어요” 문구가 보이지 않는다.
  • dryRun=false로 재평가를 실행하면 적용 완료 표기와 함께 통계 4수치가 보인다.
  • 재평가 실행 중에는 실행 버튼이 비활성이고 중복 요청이 발생하지 않는다.
  • 재평가 실행이 실패하면 에러 토스트가 보이고 이전 결과 카드가 성공으로 바뀌지 않는다.
  • 재평가 성공 후 공고 관련 쿼리가 무효화돼 보관함·공고 상세가 새 기준으로 재조회된다.
  • 소스 상태 세그먼트를 다크 모드로 렌더하면 시맨틱 토큰 class만 사용하고 하드코딩 색이 0건이다.