[FE-04] MSW 목 핸들러 · 픽스처 — BE 계약 선구현
작업 내용 (설계 의도)
근거 설계: 20260722-공고알림앱-design-fe-web.md — “방안 4 BE API 미완성과의 병행 개발”
변경 사항
BE TDD “API 계약(P0)” 표를 MSW(Mock Service Worker) 핸들러로 그대로 구현합니다. 목적은 세 가지입니다.
- FE 화면 티켓 12건이 BE-12~19 완료를 기다리지 않고 병렬 진행됩니다. 이게 없으면 FE 전체가 BE 마지막 wave에 직렬로 매달립니다.
- 목 핸들러가 계약의 실행 가능한 명세가 됩니다 — 계약 불일치가 통합 시점이 아니라 작성 시점에 드러납니다.
- 화면 테스트가 네트워크·BE 기동에 의존하지 않아 안정적입니다.
포함 범위 (src/mocks/**):
- 핸들러 — 계약 표의 20개 엔드포인트 전량. 성공 응답 + 요청 파라미터(
postingStatus·sort·companyId·days·dispatchStatus)에 따른 결과 분기를 구현합니다. - 실패 시나리오 핸들러 — 테스트가 4상태를 검증할 수 있도록 시나리오별 오버라이드를 제공합니다: 500, 404, 409(회사명 중복 / 지원 중복 / 전이 불가 / 면접 회차 중복 / 종료 지원), 400(검증 실패), 빈 목록, 지연 응답(로딩 검증용).
- 픽스처 — 실제 대상 회사(당근·배민·우리은행·수협은행)를 반영한 데이터셋. 경계 케이스를 반드시 포함합니다:
- 확신도 4종(
CONFIRMED/LIKELY/UNKNOWN,INFERRED는 타입만) - 마감일
null(상시채용) / D-1 / 먼 미래 / 경과 postingStatusOPEN·CLOSED(마감 사유 2종: 미발견 2회·마감일 경과)accessRestricted=true공고- 매칭된 공고 / 매칭 안 된 공고(제외어 사유 포함)
- 수동 등록 공고(
origin=MANUAL) - 지원 상태 8종 전부 + 종료 상태
- 수집 회차 성공 / 실패 / 0건(비정상) / 소스 가드 적용
- 알림 발송 실패 이력(시도 3회·429)
- 확신도 4종(
- 계약 요청 반영 — 설계 “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_ARRANGEMENT에CONFIRMED→LIKELY→ 나머지 순으로 반환한다 - 픽스처에 확신도
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=true1건 + 나머지), 그리고 단독(빈 배열) 케이스
추가 테스트 케이스
- 카테고리 핸들러가 platform=SARAMIN과 JUMPIT에 서로 다른 목록을 반환한다
- 애그리게이터 소스 목록 핸들러가 활성·비활성을 함께 반환한다
- DELETE 핸들러 호출 후 해당 소스가
disabled=true로 조회된다 alternateSources[]픽스처가 대표(isRepresentative=true)를 포함한다
4차 갱신 반영 (2026-07-22, 회색지대 애그리게이터 P0 편입)
- 카테고리 핸들러(
GET .../categories?platform=)가 6종 플랫폼(사람인·점핏·원티드·리멤버·잡코리아·서핏) 각각에 목록을 반환하도록 픽스처 확장. 카테고리 응답 shape는 불변({categories:[{code,label}]}). - 애그리게이터 소스 목록·등록 픽스처에 회색지대 플랫폼(예: 원티드) 소스 1건 이상 포함.
추가 테스트 케이스
- 카테고리 핸들러가 원티드·리멤버·잡코리아·서핏 각각에 목록을 반환한다
- 회색지대 플랫폼으로 등록한 애그리게이터 소스가 목록에 조회된다