Skip to content

feat: 약점 집중 재도전 (B-3) - #177

Merged
i3months merged 5 commits into
devfrom
feature/weakness-focused-retry
Aug 19, 2026
Merged

feat: 약점 집중 재도전 (B-3)#177
i3months merged 5 commits into
devfrom
feature/weakness-focused-retry

Conversation

@i3months

Copy link
Copy Markdown
Member

변경 사항

감사 백로그 B-3. 피드백에서 낮았던 평가 축을 다음 면접의 집중 영역으로 새겨, 질문 생성이 그 영역을 검증하는 질문을 우선 배치한다.

직전 PR(#176)의 재도전 위에 얹었다 — POST /api/sessions/{id}/retry{"focusOnWeakness": true} 를 주는 형태다.

자동이 아니라 opt-in 으로 한 이유

"이전 면접이 약했으니 알아서 그쪽을 더 묻는다"를 기본값으로 넣을 수도 있었지만, 그러면 사용자 모르게 질문 성격이 바뀐다. 왜 이런 질문이 나왔는지 설명할 수 없는 AI 동작은 신뢰를 깎는다. 그래서 버튼으로 노출하고, 면접 화면 헤더에 "논리력·전달력 집중" 을 표시하고, 시작할 때 토스트로도 알린다.

약점 선정 규칙

  • 임계값(interview.weakness-focus.score-threshold, 기본 70) 미만인 축을 낮은 순 최대 2개
  • 전부 기준 이상이어도 가장 낮은 하나는 고른다 — 눌렀는데 아무것도 안 바뀌면 버튼이 고장난 것처럼 보인다
  • 피드백이 없으면(중단 세션 등) 빈 목록 → 일반 재도전과 동일

작업 중 걸린 함정 하나: @Value 필드는 Spring 밖(단위 테스트)에서 주입되지 않아 0.0 이 되고, 그러면 모든 축이 '기준 이상'으로 판정돼 조용히 다른 동작을 한다. 자바 초기값(= 70)을 함께 두는 것으로 막았다.

전달 경로

retry(focusOnWeakness=true)
  → 원본 피드백에서 약한 축 선정
  → interview_sessions.focus_areas (V25, JSONB)
  → SessionQuestionsRequester 가 세션에서 읽어 generate.questions.focusAreas 로 적재
  → AI 프롬프트의 집중 영역 블록

이벤트 레코드를 늘리지 않고 이미 갖고 있던 sessionRepository 로 조회한다.

AI 프롬프트에서 주의한 것

  • _FOCUS_GUIDE — 축별 질문 설계 관점을 명시했다. 라벨만 넘기면 LLM 이 제각각 해석한다(LOGIC 을 "논리 퀴즈"로 받는 식). 기술 정확도는 동작 원리·트레이드오프, 논리력은 판단 근거와 대안 폐기 이유, 전달력은 눈높이 설명으로 각각 다른 질문이 나오게 했다
  • 근거 없는 질문 생성 금지는 그대로다. 집중 영역은 무엇을 묻는가를 바꾸는 것이지 target_evidence 필수 조건을 면제하지 않는다 — 자료가 부족하면 집중 비중을 줄이도록 명시했다. 이게 없으면 "논리력 집중"이 자료에 없는 가상 질문을 지어내는 구실이 된다

테스트

레이어 신규/변경 내용
backend SessionServiceTest +3 기준 미만 축 2개 선정, 전부 강할 때 최저 1개 폴백, 피드백 없을 때 일반 재도전 폴백
ai test_question_focus_areas.py (11) 미지정 시 쏠림 지시가 새지 않는지, 축별 관점 문구 적재, 모르는 축도 안 깨짐, 프롬프트 변수 항상 채움(비면 렌더가 KeyError)
frontend SessionEndedPanel.test.tsx +2, focusArea.test.ts (2) 완료 세션의 약점 집중 호출 인자, 중단 세션엔 미노출, 라벨 폴백

backend test 통과(ArchUnit 포함) · ai flake8/black/pytest 362 · frontend vitest 90/90 · eslint 0 · tsc · build.

영향 범위

  • DB 마이그레이션: 있음 (V25 — interview_sessions.focus_areas 컬럼 추가만. 기존 행은 NULL = 일반 면접)
  • API contract 변경: POST /api/sessions/{id}/retry 에 optional body 추가, SessionResponse.focusAreas 추가 (둘 다 하위호환)
  • 환경변수: 없음 (interview.weakness-focus.score-threshold 는 기본값 있음)

리뷰어 체크포인트

  • 임계값 70이 적절한지. 점수 분포를 보고 조정할 수 있게 설정으로 뺐다
  • 최대 2개로 제한한 것 — 3개 다 집중하면 집중이 아니게 된다고 봤다
  • 축별 관점 문구(_FOCUS_GUIDE)가 실제 면접관 관점으로 맞는지. 여기가 질문 품질을 좌우한다

`POST /api/sessions/{id}/retry` 에 `{"focusOnWeakness": true}` 를 주면, 원본 세션 피드백에서
낮았던 평가 축을 새 세션의 집중 영역으로 새긴다. 질문 생성이 그 영역을 검증하는 질문을
우선 배치한다.

## 약점 선정

- 임계값(`interview.weakness-focus.score-threshold`, 기본 70) 미만인 축을 낮은 순 최대 2개
- **전부 기준 이상이어도 가장 낮은 하나는 고른다** — 눌렀는데 아무것도 안 바뀌면 버튼이
  고장난 것처럼 보인다
- 피드백이 없으면(중단 세션 등) 빈 목록 → 일반 재도전과 동일하게 동작

`@Value` 필드에 자바 초기값(`= 70`)도 함께 뒀다. Spring 밖(단위 테스트)에서는 주입이 안 돼
0.0 이 되고, 그러면 모든 축이 '기준 이상'으로 판정돼 **조용히 다른 동작**을 한다.
실제로 이 테스트를 쓰다가 걸렸다.

## 전달 경로

V25 로 `interview_sessions.focus_areas`(JSONB, `SessionFocusArea` name 배열) 추가.
`SessionQuestionsRequester` 가 세션에서 읽어 `generate.questions.focusAreas` 로 싣는다 —
이벤트 레코드를 늘리지 않고 이미 갖고 있던 `sessionRepository` 로 조회한다.
`SessionResponse.focusAreas` 로도 노출해 화면이 "무엇을 겨냥하는 면접인지" 보여줄 수 있게 했다.

## 테스트

`SessionServiceTest` +3 — 기준 미만 축 2개 선정, 전부 강할 때 최저 1개 폴백, 피드백 없을 때
일반 재도전 폴백. 기존 retry 테스트는 새 시그니처에 맞춰 갱신.
`generate.questions` 의 `focusAreas`(TECHNICAL|LOGIC|COMMUNICATION)를 받아, 그 영역을
검증하는 질문이 과반이 되도록 프롬프트에 지시한다.

- `_FOCUS_GUIDE` — 축별 **질문 설계 관점**을 명시. 라벨만 넘기면 LLM 이 제각각 해석한다
  (LOGIC 을 "논리 퀴즈"로 받는 식). 기술 정확도는 동작 원리·트레이드오프, 논리력은 판단 근거와
  대안 폐기 이유, 전달력은 눈높이 설명으로 각각 다른 질문이 나오게 했다
- **근거 없는 질문 생성 금지는 그대로**다. 집중 영역은 *무엇을 묻는가*를 바꾸는 것이지
  target_evidence 필수 조건을 면제하지 않는다 — 자료가 부족하면 집중 비중을 줄이도록 명시
- 지정이 없으면 "(지정 없음 — 치우치지 말 것)" 이 들어가 일반 면접과 동일하게 동작

테스트 `test_question_focus_areas.py` 신설 (11) — 미지정 시 쏠림 지시가 새지 않는지,
축별 관점 문구가 실리는지, 모르는 축이 와도 깨지지 않는지(Core 가 축을 늘릴 수 있다),
생성기가 프롬프트 변수를 항상 채우는지(비면 템플릿 렌더가 KeyError). pytest 362건 통과.
- 피드백 화면: "약점 집중해서 다시" 를 primary 로. 점수가 나온 직후라 여기서는 그게
  기본 제안이고, "같은 설정으로 다시" 는 secondary 로 내렸다
- 종료 패널: 완료 세션에만 약점 집중을 함께 제안한다. 약점은 피드백 점수가 근거라
  피드백이 없는 중단 세션에는 뜻이 없다
- 면접 화면 헤더에 "논리력·전달력 집중" 표시 + 시작 시 토스트. 질문 성격이 왜 달라졌는지
  보이지 않으면 사용자는 이유를 알 수 없다
- `focusAreaLabel` — 모르는 축은 원본을 그대로 쓴다(서버가 축을 늘려도 빈칸이 되지 않게)

테스트: `SessionEndedPanel.test.tsx` +2(완료 세션의 약점 집중 호출 인자, 중단 세션엔 미노출),
`focusArea.test.ts` 신설(2). vitest 90/90 · eslint 0 · tsc · build 통과.
- `docs/database.md` — interview_sessions.focus_areas 반영
- `docs/messaging.md` — generate.questions 페이로드에 focusAreas 추가
- `backend/CLAUDE.md` — 약점 선정 규칙(임계값·최저 1개 폴백)과 @value 초기값 함정 기록
- `ai/CLAUDE.md` — 축별 질문 설계 관점(_FOCUS_GUIDE)과 근거 필수 조건 유지 명시
@i3months
i3months merged commit ada75e0 into dev Aug 19, 2026
5 checks passed
@i3months
i3months deleted the feature/weakness-focused-retry branch August 19, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant