[FE-04] MSW 목 핸들러 · 픽스처 — BE 계약 선구현

작업 내용 (설계 의도)

근거 설계: 20260722-공고알림앱-design-fe-web.md — “방안 4 BE API 미완성과의 병행 개발”

변경 사항

BE TDD “API 계약(P0)” 표를 MSW(Mock Service Worker) 핸들러로 그대로 구현합니다. 목적은 세 가지입니다.

  1. FE 화면 티켓 12건이 BE-12~19 완료를 기다리지 않고 병렬 진행됩니다. 이게 없으면 FE 전체가 BE 마지막 wave에 직렬로 매달립니다.
  2. 목 핸들러가 계약의 실행 가능한 명세가 됩니다 — 계약 불일치가 통합 시점이 아니라 작성 시점에 드러납니다.
  3. 화면 테스트가 네트워크·BE 기동에 의존하지 않아 안정적입니다.

포함 범위 (src/mocks/**):

  • 핸들러 — 계약 표의 20개 엔드포인트 전량. 성공 응답 + 요청 파라미터(postingStatus·sort·companyId·days·dispatchStatus)에 따른 결과 분기를 구현합니다.
  • 실패 시나리오 핸들러 — 테스트가 4상태를 검증할 수 있도록 시나리오별 오버라이드를 제공합니다: 500, 404, 409(회사명 중복 / 지원 중복 / 전이 불가 / 면접 회차 중복 / 종료 지원), 400(검증 실패), 빈 목록, 지연 응답(로딩 검증용).
  • 픽스처 — 실제 대상 회사(당근·배민·우리은행·수협은행)를 반영한 데이터셋. 경계 케이스를 반드시 포함합니다:
    • 확신도 4종(CONFIRMED/LIKELY/UNKNOWN, INFERRED는 타입만)
    • 마감일 null(상시채용) / D-1 / 먼 미래 / 경과
    • postingStatus OPEN·CLOSED(마감 사유 2종: 미발견 2회·마감일 경과)
    • accessRestricted=true 공고
    • 매칭된 공고 / 매칭 안 된 공고(제외어 사유 포함)
    • 수동 등록 공고(origin=MANUAL)
    • 지원 상태 8종 전부 + 종료 상태
    • 수집 회차 성공 / 실패 / 0건(비정상) / 소스 가드 적용
    • 알림 발송 실패 이력(시도 3회·429)
  • 계약 요청 반영 — 설계 “BE에 요청할 계약 변경 목록” #1~#9의 요청안대로 구현하고, 각 핸들러에 요청 번호와 “BE 확정 시 갱신” 주석을 남깁니다. 실 계약이 확정되면 FE-17이 목과 타입을 동기화합니다.
  • 부트스트랩 — 테스트용 setupServer(FE-01이 만든 src/test/setup.ts에 핸들러를 연결) + 개발용 setupWorker(브라우저에서 목 모드 실행).

하드코딩된 임시 응답을 컴포넌트에 넣지 않는 근거: 하드코딩은 지워지지 않고 남으며 loading/error 상태 테스트를 작성할 수 없습니다(설계 “방안 4-c 미채택”).

의존

  • FE-01

다이어그램

처리 흐름

sequenceDiagram
    participant T as 화면 테스트
    participant S as setupServer
    participant H as 핸들러
    participant F as 픽스처
    participant Q as Query 훅
    T->>S: server.use(실패 시나리오 오버라이드)
    Q->>H: GET /api/companies/1/job-postings
    H->>F: 확신도·마감일 경계 데이터 조회
    F-->>H: 공고 목록
    H-->>Q: 200 (또는 오버라이드된 500)
    Q-->>T: success 또는 error 상태

클래스 의존

flowchart LR
    subgraph Mocks["mocks"]
        Handlers[handlers]
        Scenarios[errorScenarios]
        Fixtures[fixtures]
        Server[server setupServer]
        Worker[browser setupWorker]
    end
    subgraph Types["types"]
        Api[api.ts 계약 타입]
    end
    Handlers --> Fixtures
    Handlers --> Api
    Scenarios --> Handlers
    Server --> Handlers
    Worker --> Handlers

테스트 케이스

  • 계약 표의 20개 엔드포인트 전량에 핸들러가 등록되어 있다
  • 모든 핸들러 응답이 types/api.ts 타입을 만족한다 (타입체크로 검증)
  • 공고 목록 핸들러가 postingStatus=CLOSED에 마감 공고만 반환한다
  • 공고 목록 핸들러가 sort=WORK_ARRANGEMENTCONFIRMEDLIKELY → 나머지 순으로 반환한다
  • 픽스처에 확신도 CONFIRMED·LIKELY·UNKNOWN 공고가 각각 1건 이상 존재한다
  • 픽스처에 마감일 null 공고와 D-1 공고가 각각 존재한다
  • 픽스처에 accessRestricted=true 공고가 존재한다
  • 픽스처에 매칭 안 된 공고가 제외 사유와 함께 존재한다
  • 픽스처에 수동 등록 공고(origin=MANUAL)가 존재한다
  • 픽스처에 지원 상태 8종이 모두 등장한다
  • 픽스처에 수집 회차 성공·실패·0건이 모두 존재한다
  • 회사명 중복 시나리오가 409와 기존 회사 ID를 반환한다
  • 지원 전이 불가 시나리오가 409를 반환한다
  • 면접 회차 중복 시나리오가 409를 반환한다
  • 빈 목록 시나리오가 200과 빈 배열을 반환한다 (empty 상태 테스트용)
  • 지연 응답 시나리오가 loading 상태를 관찰 가능하게 한다

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

BE 계약 응답 + 애그리게이터 편입으로 목 핸들러·픽스처를 확장합니다. BE 완료를 기다리지 않는 병행 개발 범위를 유지합니다.

신규·수정 핸들러

  • GET /api/matching/criteria 신설 (계약 응답 #1)
  • DELETE /api/matching/exclusion-keywords/{id}·/work-arrangement-keywords/{id} 신설 (계약 응답 #2, 소프트 삭제 — 과거 평가 결과 참조 유지)
  • GET /api/companies?companyOrigin=WATCHED|DISCOVERED&page=&size= 필터·페이지네이션(totalCount·page), companyOrigin·brokenSourceCount 필드
  • POST /api/companies/{id}/promotion 신설 (DISCOVERED → WATCHED)
  • POST /api/aggregator-sources 신설 ({platform, searchCategoryCode, searchKeyword?})
  • 공고 목록: applied 제거, applicationId | null·sourceType·alternateSourceCount 추가
  • 공고 상세: alternateSources[] 추가
  • re-evaluations: 응답에 skippedCount 추가
  • collection-runs: guardApplied 제거, abnormal 단일 필드

픽스처 경계 케이스 추가

  • 관심 회사(WATCHED) 소수 + 발견 회사(DISCOVERED) 수십 건(페이지네이션 검증용)
  • brokenSourceCount가 0인 회사와 1 이상인 회사
  • 대표 공고(alternateSourceCount > 0) + 그 상세의 alternateSources[](대표 1 + 애그리게이터 N)
  • sourceType이 COMPANY_BOUND·AGGREGATOR인 공고 각각
  • 승격 성공/실패(500) 시나리오
  • 애그리게이터 소스 등록 성공/실패 시나리오
  • 재평가 응답에서 skippedCount > 0인 케이스

추가 테스트 케이스

  • GET /api/companies?companyOrigin=DISCOVERED가 발견 회사만 페이지네이션과 함께 반환한다
  • GET /api/companies?companyOrigin=WATCHED가 관심 회사만 반환한다
  • promotion 핸들러가 회사의 companyOrigin을 WATCHED로 바꿔 반환한다
  • aggregator-sources 핸들러가 {jobSourceId, platform}을 반환한다
  • 공고 목록 픽스처에 sourceType·alternateSourceCount가 존재한다
  • 공고 상세 픽스처에 alternateSources[]가 대표 표시와 함께 존재한다
  • 매칭 기준 조회 핸들러가 그룹·제외어·근무형태·criteriaRevision을 반환한다

3차 갱신 반영 (2026-07-22, BE 최종 확정)

신규 핸들러 2개

  • GET /api/aggregator-sources/categories?platform={categories:[{code, label}]}. 플랫폼별로 다른 목록(사람인·점핏)을 반환하고, 지연 응답·조회 실패(500) 시나리오도 제공(카테고리 Select 로딩·에러 검증용).
  • GET /api/aggregator-sources → 활성·비활성 소스 함께. DELETE /api/aggregator-sources/{id} → 소프트 삭제(해당 소스 disabled=true로 전환).

픽스처 추가

  • 애그리게이터 소스 목록: 활성 2건 + 비활성(disabled=true) 1건(관리 화면 위계 검증)
  • 카테고리 픽스처: 사람인·점핏 각각 다른 목록, 그리고 빈 목록(카테고리 0건) 케이스
  • 공고 상세 alternateSources[]: 대표 자신 포함 그룹(isRepresentative=true 1건 + 나머지), 그리고 단독(빈 배열) 케이스

추가 테스트 케이스

  • 카테고리 핸들러가 platform=SARAMIN과 JUMPIT에 서로 다른 목록을 반환한다
  • 애그리게이터 소스 목록 핸들러가 활성·비활성을 함께 반환한다
  • DELETE 핸들러 호출 후 해당 소스가 disabled=true로 조회된다
  • alternateSources[] 픽스처가 대표(isRepresentative=true)를 포함한다

4차 갱신 반영 (2026-07-22, 회색지대 애그리게이터 P0 편입)

  • 카테고리 핸들러(GET .../categories?platform=)가 6종 플랫폼(사람인·점핏·원티드·리멤버·잡코리아·서핏) 각각에 목록을 반환하도록 픽스처 확장. 카테고리 응답 shape는 불변({categories:[{code,label}]}).
  • 애그리게이터 소스 목록·등록 픽스처에 회색지대 플랫폼(예: 원티드) 소스 1건 이상 포함.

추가 테스트 케이스

  • 카테고리 핸들러가 원티드·리멤버·잡코리아·서핏 각각에 목록을 반환한다
  • 회색지대 플랫폼으로 등록한 애그리게이터 소스가 목록에 조회된다