[FE-36] 추천도 표시 컴포넌트
작업 내용 (설계 의도)
근거: 지원 관리 확장 FE 웹 설계 S-24 지원 추천도·상태 표기 규칙·컴포넌트 트리·Query 규약·접근성·Testing Plan > 반드시 커버할 실패·엣지 경로(27·28·29), 지원 관리 확장 TDD 2단계 — 지원 추천도 API 계약(GET /api/job-postings/{id}/recommendation).
변경 사항
- 추천도 표시 컴포넌트 4종을
components/recommendation/에 신설합니다 —RecommendationGradeChip(등급 칩),RecommendationScoreCard(점수·게이지),RecommendationAxisList(축별 근거),RecommendationCapNotice(59점 상한 안내). - 4종 모두 props만 받는 순수 프레젠테이션입니다. 데이터 조회는 소비 화면(보관함 카드·공고 상세 섹션)이 하고, 컴포넌트는 서버 상태를 스스로 가져오지 않습니다. 같은 컴포넌트를 목록과 상세 두 문맥에서 쓰기 위한 전제입니다.
- 조회 훅
useRecommendation과 API 함수는 이 티켓이 소유합니다.staleTime: 5 * 60_000을 적용합니다 — 재평가는 사용자 명시 액션(프로필 확정·일괄 재평가)으로만 발생하므로 30초 기본값으로 재조회할 이유가 없습니다. - 등급 4종을 기존
Chip의fill × tone조합으로만 표현합니다. 신규 색 토큰 0건,--accent원색 미사용입니다 —STRONGLY_RECOMMENDED는 filled positive,RECOMMENDED는 outlined positive,REVIEW는 filled neutral,NOT_RECOMMENDED는 none(text-tertiary)입니다. 점수를 크게 쓰되 색으로 겁주지 않는 것이 원칙이고, accent는 화면당 1곳(주 CTA)에만 씁니다. judgeable === false인 축은 점수·게이지를 렌더하지 않습니다. 대신 행 전체를surface-muted+text-tertiary로 낮추고 “판단 불가”와 정규화 사실을 문장으로 설명합니다(“공고에 근무 조건 정보가 없어 나머지 축으로 나눠 계산했어요”). 이 설명이 없으면 가중치 합이 안 맞는 점수가 마술처럼 보입니다.configuredWeight → normalizedWeight를 나란히 노출합니다. 사용자가 45%로 설정한 축이 51%로 계산된 사실을 숨기지 않아야 점수를 신뢰할 수 있습니다.capReleased === true여도capReason을 계속 노출합니다(FR-97 명시 요구). 표현은 중립 톤(“상한 해제됨 · 원래 사유: …”)으로 낮추되 사유 자체를 지우지 않습니다.capApplied && !capReleased일 때만 warning 톤 배너입니다.notEvaluableReason2종은 원인이 달라 문구와 액션이 다릅니다 —JD_UNAVAILABLE은 사용자가 할 일이 없으므로 안내만 하고,PROFILE_NOT_CONFIRMED는[프로필 만들기]CTA를 붙여 S-23으로 보냅니다. 둘을 같은 “평가 불가”로 합치면 해결 가능한 상황에서 사용자가 막힙니다.- 축 충족 3종(
FULL/PARTIAL/NONE)은 형태(채움/테두리/없음)로 구분하되 텍스트 라벨(“충족”/“일부”/“없음”)을 항상 병기합니다. 색·형태만으로 정보를 전달하지 않습니다. - 게이지는
role="meter"+aria-valuenow/aria-valuemin/aria-valuemax/aria-label을 갖고 숫자 텍스트를 병기합니다. 트랙은--border, 채움은--positive기존 토큰입니다. - 라벨·비율 계산은
utils/recommendation.ts(FE-30 소유)에 위임합니다 — 컴포넌트는 렌더만 합니다. - 공고 상세·보관함 카드에 이 컴포넌트를 배선하는 작업은 wave 3의 FE-38이 수행합니다. 이 티켓은 컴포넌트와 조회 훅까지만 소유해 같은 wave의 파일 충돌을 피합니다.
의존
- FE-30 —
RecommendationResponse·RecommendationAxis·RecommendationGrade·RecommendationAxisKey타입, 추천도 queryKey,utils/recommendation.ts, 분기 픽스처(judgeable false·cap 2종·notEvaluable 2종). - BE-63 — 추천도 평가·조회 API 구현.
- BE-65 — 재평가·기준 버전 계약(등급·상한 값의 산출 근거).
다이어그램
처리 흐름
sequenceDiagram participant Page as 소비 화면 participant Hook as useRecommendation participant Api as GET recommendation participant Card as RecommendationScoreCard participant Axis as RecommendationAxisList Page->>Hook: jobPostingId로 조회(staleTime 5분) Hook->>Api: GET Api-->>Page: RecommendationResponse Page->>Card: 점수·등급·상한 props Page->>Axis: axes props
컴포넌트 의존
flowchart LR Hook[useRecommendation] --> Api[fetchRecommendation] Api --> Types[RecommendationResponse] Card[RecommendationScoreCard] --> Grade[RecommendationGradeChip] Card --> Meter[role=meter 게이지] Card --> Cap[RecommendationCapNotice] Axis[RecommendationAxisList] --> Util[utils/recommendation] Grade --> Chip[기존 Chip fill x tone] Card --> Util
테스트 케이스
totalScore: 92·grade: 'STRONGLY_RECOMMENDED'이면 점수 숫자와 “강력 추천” 칩이 함께 보인다.- 등급 4종이 각각 filled positive·outlined positive·filled neutral·none 형태로 렌더되고
--accent원색 클래스를 사용하지 않는다. judgeable: false인 축은 게이지가 렌더되지 않고 “판단 불가”와 정규화 사실 문장이 보인다.judgeable: true인 축은configuredWeight와normalizedWeight가 나란히 보이고 충족률 게이지가 렌더된다.- 축 항목의
FULL·PARTIAL·NONE이 각각 “충족”·“일부”·“없음” 텍스트 라벨과 함께 보이고PARTIAL은reason이 함께 보인다. capApplied: true·capReleased: false이면 warning 톤 상한 배너와capReason이 보인다.capReleased: true이면 “상한 해제됨”과 원래 상한 사유가 함께 중립 톤으로 보인다.notEvaluableReason: 'JD_UNAVAILABLE'이면 안내 문구만 보이고[프로필 만들기]CTA가 보이지 않는다.notEvaluableReason: 'PROFILE_NOT_CONFIRMED'이면 다른 문구와 함께[프로필 만들기]CTA가 보인다.totalScore: null이면 점수 숫자와 게이지가 렌더되지 않고 0으로 대체되지 않는다.- 게이지가
role="meter"와aria-valuenow·aria-valuemin·aria-valuemax를 가지고 숫자 텍스트를 병기한다. useRecommendation의staleTime이 5분으로 설정되어 재마운트 시 즉시 재조회가 발생하지 않는다.- 컴포넌트 4종이 조회 훅 없이 props만으로 렌더된다(서버 요청이 발생하지 않는다).
- 컴포넌트 4종이
.dark클래스 환경에서 렌더되고 하드코딩 색을 0건 사용한다.