[FE-39] 일괄 재평가 진입점 · 결과 요약
작업 내용 (설계 의도)
근거: 지원 관리 확장 FE 웹 설계 S-23 이력서 프로필 검토·확정·API 연동 > 2단계·방안 8(재평가 대기 UI)·Single Writer per File 검증 > 단계 2 wave 3·BE 역제안 4건 > C-5·2차 검수 결과 — C-7 수용 + 부속 확정 4건·400 에러 3분류 (A 확정)·Testing Plan > 반드시 커버할 실패·엣지 경로(33·34·43-e·43-e1), 지원 관리 확장 TDD 2단계 — 지원 추천도 API 계약(POST /api/recommendations/re-evaluations).
변경 사항
- 이력서 프로필 화면(S-23)에 일괄 재평가 진입점과 결과 요약 영역을 덧붙입니다. 프로필을 확정한 뒤 “그래서 관심 공고가 어떻게 됐나”에 답하지 못하면 확정 행동이 허공에 뜹니다.
- 결과 카드는
evaluatedCount·skippedCount·notEvaluableCount를 각각 구분해서 보여 줍니다. 셋을 합산하면 “평가했는데 결과가 없는” 경우와 “기준 버전이 같아 건너뛴” 경우가 뒤섞여 사용자가 재실행 여부를 판단할 수 없습니다. failures[]가 비어 있지 않으면 실패 건수와 각 항목의jobPostingId·reason을 목록으로 노출합니다. 부분 실패를 성공으로 뭉뚱그리면 조용히 누락된 공고가 생깁니다.failures[]가 0건이면 목록 영역 자체를 렌더하지 않습니다.409 RESUME_PROFILE_NOT_CONFIRMED는 “프로필을 먼저 확정해 주세요” + 확정 CTA로 처리합니다 — 사용자가 지금 이 화면에서 해결할 수 있는 문제이므로 안내에서 끝내지 않고 행동 경로를 붙입니다.- 프록시 타임아웃은 해소됐습니다 (C-5 확정). BE가
web/nginx.conf의/api/location에proxy_read_timeout 600s·proxy_send_timeout 600s·proxy_connect_timeout 5s를 적용합니다. 이 파일은 BE-56 단독 소유이며 이 티켓은 건드리지 않습니다(Single Writer per File). - 비동기 잡 큐는 미채택입니다 — 관심 공고 200건의 규칙 기반 계산은 외부 호출이 0회라 초 단위로 끝나고, NFR-13의 5분은 상한이지 기대값이 아닙니다. 잡 상태 테이블·진행 조회 엔드포인트·FE 폴링 로직 비용이 정당화되지 않으며 NFR-8(브로커·워커 미도입)의 취지와도 어긋납니다.
- 따라서 진행 표시 UI는 동기 대기 기준으로 설계합니다 — “관심 공고 N건을 다시 평가하고 있어요 · 최대 5분 걸릴 수 있어요 · 화면을 닫아도 계속 진행돼요”를 유지하고 중복 실행을 막습니다(NFR-13의 200건 5분 기준). 화면 이탈을 막지 않으며, 이탈 시 결과는 재진입 시 목록으로 확인합니다.
- 그래도 게이트웨이 오류(504 등)·네트워크 단절은 남는 경로이므로, 실패로 표시하되 “서버에서 계속 진행 중일 수 있어요 · 잠시 후 보관함에서 확인해 주세요” 로 안내해 무한 재시도를 막습니다.
- 서버 측 방어를
RECOMMENDATION_TARGET_LIMIT_EXCEEDED(400)로 확정 처리합니다(A 확정 — C-7 수용으로 코드가 신설돼 블로커가 해제됐습니다). 재평가 대상이 1,000건을 넘으면 이 코드로 거부됩니다(상한 없이 열어 두면 타임아웃을 아무리 늘려도 언젠가 넘습니다). 응답의actualCount·limit으로 문구를 조립합니다: “관심 공고 {actualCount}건이 상한 {limit}건을 넘었어요 / 제외로 정리한 뒤 다시 시도해 주세요” +[보관함 열기]. 수치를 넣는 이유는 몇 건을 정리해야 하는지가 안내에 있어야 사용자가 행동을 결정하기 때문입니다. - 문구 조립은
utils/recommendation의 순수 함수가 담당합니다(no-logic-in-component). 화면은code와ApiError를 넘기고 문구를 받기만 하며, 분기 기준은 항상code입니다 — 메시지 문자열 매칭 분기는 만들지 않습니다.ApiError의actualCount·limit은 FE-21이 소유하므로 이 티켓은 읽기만 하고api/client.ts를 수정하지 않습니다. elapsedMillis(밀리초)를 결과 카드에 실측 소요(예:4.2초)로 표시합니다(E 확정). 응답 스키마에 확정된 필수 필드이므로 “없으면 미표시” 분기를 두지 않습니다 — 동기 대기 안내(“최대 5분”)를 실제 값으로 닫아 주는 정보입니다. 밀리초 원값을 그대로 노출하지 않고 초 단위로 변환해 보여 줍니다.- 재평가 성공 시
['resume-profiles']와['job-postings']접두사 전체를 무효화합니다 — 추천도가 전부 바뀌므로 보관함 등급 칩과 공고 상세 섹션이 새 값으로 갱신되어야 합니다. - 이 티켓은
pages/resume/하위와 재평가 훅만 건드립니다. 같은 wave의 FE-38(보관함·공고 상세·서류)과 파일 교집합이 없습니다.
롤백: 재평가는 서버 데이터를 갱신하므로 FE 롤백으로 되돌릴 수 없습니다. FE를 이전 빌드로 되돌리면 진입점이 사라질 뿐이며, 잘못 실행된 평가는 재평가 재실행으로 정정합니다.
의존
- FE-30 — 재평가 응답 타입(필수
elapsedMillis), MSW 목(failures[]포함 200 · 409 ·400 RECOMMENDATION_TARGET_LIMIT_EXCEEDED(actualCount·limit포함) 시나리오). - FE-21 —
ApiError의actualCount·limit. 이 티켓은 읽기만 합니다. - FE-35 —
ResumeProfilePage본체와 확정 흐름. 이 티켓이 결과 영역을 덧붙입니다. - FE-36 — 등급 표시 컴포넌트(재평가 후 갱신 대상).
- BE-65 · BE-67 — 재평가 API와 기준 버전 계약.
다이어그램
처리 흐름
sequenceDiagram participant User as 사용자 participant Page as ResumeProfilePage participant Hook as useReevaluateRecommendations participant Api as POST re-evaluations User->>Page: 재평가 실행 Page->>Hook: mutate Hook->>Api: POST Api-->>Hook: 200 결과 또는 409/타임아웃 Hook-->>Page: 카운트 + failures[] Page-->>User: 결과 카드 또는 안내
컴포넌트 의존
flowchart LR Page[ResumeProfilePage] --> Progress[ReevaluationProgressCard] Page --> Hook[useReevaluateRecommendations] Hook --> Api[api/recommendation reevaluate] Progress --> Counts[평가·건너뜀·평가불가 카운트] Progress --> Failures[failures 목록] Hook --> Invalidate[job-postings 접두사 무효화] Invalidate --> Grade[RecommendationGradeChip 갱신]
테스트 케이스
- 재평가가 성공하면
evaluatedCount·skippedCount·notEvaluableCount가 각각 구분되어 결과 카드에 보인다. - 응답에
failures[]가 2건 있으면 실패 건수와 각 항목의 공고 식별자·사유가 목록으로 보인다. failures[]가 0건이면 실패 목록 영역이 렌더되지 않는다.409 RESUME_PROFILE_NOT_CONFIRMED를 받으면 “프로필을 먼저 확정해 주세요”와 확정 CTA가 보인다.- 재평가 진행 중에는 “최대 5분 걸릴 수 있어요 · 화면을 닫아도 계속 진행돼요” 안내가 보이고 실행 버튼이 비활성된다.
- 재평가 진행 중 실행 버튼을 다시 눌러도 중복 요청이 발생하지 않는다.
- 게이트웨이 타임아웃(504) 응답을 받으면 실패 표시와 함께 “서버에서 계속 진행 중일 수 있어요 · 잠시 후 보관함에서 확인해 주세요” 문구가 보인다.
- 네트워크 오류로 요청이 끊겨도 화면이 크래시하지 않고 같은 안내 문구로 처리된다.
- 재평가 성공 시
['resume-profiles']와['job-postings']접두사 쿼리가 무효화된다. evaluatedCount가 0이고skippedCount만 양수인 경우 “새로 평가한 건이 없어요” 취지로 구분되어 보이고 실패로 표시되지 않는다.400 RECOMMENDATION_TARGET_LIMIT_EXCEEDED(actualCount: 1842·limit: 1000)를 받으면 두 수치가 들어간 안내(“관심 공고 1,842건이 상한 1,000건을 넘었어요”)와[보관함 열기]가 보인다.- 400 안내 분기가
code기준으로utils/recommendation에서 이뤄지고, 컴포넌트가 응답 메시지 문자열을 매칭하거나 문구를 직접 조립하지 않는다. - 재평가 결과 카드에
elapsedMillis가 초 단위 소요(4.2초형태)로 표시되고 밀리초 원값이 그대로 노출되지 않는다. - 결과 카드와 실패 목록이
.dark클래스 환경에서 렌더되고 하드코딩 색을 0건 사용한다.