[FE-07] 매칭 설정 화면 — 직무 키워드 그룹 · 제외어 · 근무형태 키워드 · 재평가
작업 내용 (설계 의도)
근거 설계: 20260722-공고알림앱-design-fe-web.md — “S-10 매칭 설정”, “낙관적 업데이트 적용 지점”
근거 요구: FR-22(동의어 그룹) · FR-23(제외어) · FR-26·33(근무형태 = 라벨·정렬 전용) · FR-25(재매칭)
변경 사항
매칭 기준을 등록·삭제하고, 기준이 바뀌면 저장된 공고를 다시 확인(재평가)하는 화면입니다.
핵심 설계 의도:
- 내부 용어를 그대로 노출하지 않습니다(토스 Casual Concept). “동의어 그룹” → “같은 뜻으로 볼 단어”, “criteriaRevision 증가” → “조건이 바뀌었어요 / 저장된 공고를 다시 확인해 보세요”. 사용자는 revision이라는 개념을 몰라도 됩니다.
- 근무형태가 필터가 아님을 화면에서 명시합니다(FR-33) — “목록에서 라벨과 정렬에만 쓰고, 공고를 걸러내지는 않아요”. 이 문구가 없으면 사용자는 근무형태 키워드를 등록하면 그 공고만 보인다고 오해합니다.
- 재평가 배너는 revision이 바뀌었을 때만 노출합니다. 항상 떠 있으면 무시됩니다.
- 낙관적 업데이트를 키워드 칩 추가·삭제에만 적용합니다. 근거: 조작이 가볍고 결과가 단순(칩 하나)이라 즉시 피드백이 자연스럽습니다. 롤백 흐름:
onMutate에서 이전 캐시 스냅샷 → 실패 시onError에서 복원 + 토스트(“추가하지 못했어요”) →onSettled에서 무효화. 자동 재시도는 하지 않습니다(사용자가 다시 누르는 편이 명확). - 재평가에는 낙관적 업데이트를 적용하지 않습니다 — 수 초가 걸리고 결과가 목록 전체에 영향을 미치므로 실제 완료를 기다려 결과 건수를 보여줍니다.
- 삭제 확인 다이얼로그를 두지 않습니다 — 키워드는 재등록 비용이 낮고, 확인 단계가 잦은 조작을 방해합니다.
계약 리스크 (blocking): 매칭 기준 조회 API가 계약에 없습니다(POST·DELETE만 존재) → 계약 요청 #1. 제외어·근무형태 키워드 삭제 API도 없습니다 → 계약 요청 #2. 이 티켓은 MSW 목으로 개발·테스트를 완주하되, 실 연동은 두 계약 수용이 전제입니다. 미수용 시 이 화면은 등록 전용으로 축소되며 그 경우 사용자가 등록된 기준을 확인할 수 없다는 문제가 남습니다.
범위: src/pages/matching/MatchingSettingsPage.tsx(스텁 대체), 전용 컴포넌트(ReEvaluationBanner·KeywordGroupSection·KeywordChipSection), src/api/matching.ts, src/hooks/matching/**.
의존
- FE-02, FE-03, FE-04
- BE 의존: BE-12 (매칭 기준 CRUD), BE-13 (재평가 API)
다이어그램
처리 흐름
sequenceDiagram participant U as 사용자 participant P as MatchingSettingsPage participant M as useRegisterExclusionKeyword participant C as Query 캐시 participant S as 서버 U->>P: 제외어 "인턴" 추가 P->>M: mutate(keyword) M->>C: onMutate — 스냅샷 후 칩 낙관적 추가 M->>S: POST /api/matching/exclusion-keywords alt 실패 S-->>M: 500 M->>C: onError — 스냅샷 복원 (칩 제거) M-->>U: 토스트 "추가하지 못했어요" else 성공 S-->>M: {id, criteriaRevision} M->>C: onSettled — 무효화 P-->>U: 재평가 배너 노출 end
클래스 의존
flowchart LR subgraph Page["pages/matching"] Main[MatchingSettingsPage] Banner[ReEvaluationBanner] Group[KeywordGroupSection] ChipSec[KeywordChipSection] end subgraph Hooks["hooks/matching"] Read[useMatchCriteria] Write[키워드 mutation 4종] Re[useReEvaluate] end subgraph Api["api"] Client[matching.ts] end subgraph Ui["components/ui"] Chip Sheet[BottomSheet] Empty[EmptyState] end Main --> Read Main --> Banner Main --> Group Main --> ChipSec Banner --> Re Group --> Write ChipSec --> Write ChipSec --> Chip Group --> Sheet Group --> Empty Read --> Client Write --> Client Re --> Client
테스트 케이스
- 등록된 직무 키워드 그룹이 동의어와 함께 렌더된다
- 제외어·근무형태 키워드가 각각 칩으로 렌더된다
- 근무형태 섹션에 “공고를 걸러내지는 않아요” 안내 문구가 렌더된다
- 동의어 4개를 가진 직무 그룹을 추가하면 목록에 반영되고 재평가 배너가 노출된다
- 제외어를 추가하면 칩이 즉시(서버 응답 전) 추가된다 (낙관적 업데이트)
- 제외어 추가가 실패하면 낙관적으로 추가된 칩이 사라지고 토스트가 표시된다
- 키워드 삭제가 실패하면 삭제된 칩이 복원된다
- 재평가 배너는
criteriaRevision이 변하기 전에는 렌더되지 않는다 - “다시 확인하기” 클릭 시 CTA에 로딩이 표시되고 완료 후 평가 건수 토스트가 뜬다
- 재평가 실패 시 배너가 유지되고 에러 토스트가 표시된다
- 빈 문자열·공백만 있는 키워드는 제출 버튼이 비활성이다
- 각 섹션이 비어 있으면 섹션별 empty 안내가 렌더된다
- 세 섹션이 모두 비어도 화면 자체는 정상 렌더된다 (설정 화면)
- 기준 조회 실패 시 전체 에러 대체와
[다시 시도]가 렌더된다 - 로딩 중 섹션별 스켈레톤이 렌더된다
2차 갱신 반영 (2026-07-22)
blocking 해소 (계약 응답 #1·#2)
GET /api/matching/criteria신설 → 이 화면은 더 이상 blocking이 아니며 실 API로 완주합니다.- 제외어·근무형태 키워드
DELETE신설(소프트 삭제 — 과거 평가 결과 참조 유지) → 삭제 흐름도 실 API 완주.
재평가 skip 표기 (계약 응답 #6)
- “다시 확인하기”는
{force?}를 미전송(기본 false)해 revision이 바뀐 공고만 재평가합니다. - 응답
{evaluatedCount, skippedCount}를 토스트로 표기: “42건 재평가 · 1,200건은 이미 최신이라 건너뜀”.skippedCount가 화면 변화 적음을 설명해 “왜 아무 일도 안 일어났지” 오해를 막습니다.
추가 테스트 케이스
- 매칭 기준 조회가 실 API 엔드포인트(
GET /api/matching/criteria)를 사용한다 - 제외어·근무형태 키워드 삭제가 각각의 DELETE 엔드포인트를 호출한다
- 재평가 응답의
skippedCount가 토스트에 “이미 최신이라 건너뜀”으로 표기된다 - 재평가 요청에
force가 기본 미전송된다