Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
ffc5bfe
fix: 회원정보 수정 요청을 서버 계약에 맞춤
danjee2009 Sep 29, 2026
7fee5bc
fix: 포트폴리오 업로드 후 메타데이터 재조회
danjee2009 Sep 29, 2026
fbd96e2
fix: 마이페이지 직무 값을 FRONTEND, BACKEND로 통일
danjee2009 Sep 29, 2026
45c1757
Merge pull request #119 from Alpha-origin/bug/#118-profile-api-fix
danjee2009 Sep 29, 2026
9b46e8c
feat: check session on route change and tab focus
danjee2009 Sep 30, 2026
9cf5cac
fix: 회원정보 이메일을 읽기 전용으로 변경
danjee2009 Sep 30, 2026
89deb11
fix: 회원정보 저장 후 수정 응답으로 스토어 갱신
danjee2009 Sep 30, 2026
14e5183
fix: 마이페이지 직무 선택 제거
danjee2009 Sep 30, 2026
03c0fa2
chore: 사용하지 않는 포트폴리오 업로드 함수 제거
danjee2009 Sep 30, 2026
a8fe39c
Merge pull request #122 from Alpha-origin/enhancement/#121-main-my-pa…
danjee2009 Sep 30, 2026
5f9b5ea
Merge remote-tracking branch 'origin/dev' into bug/#118-profile-api-fix
danjee2009 Sep 30, 2026
c44ad18
Merge pull request #123 from Alpha-origin/bug/#118-profile-api-fix
danjee2009 Sep 30, 2026
8a2a7a2
feat: 면접 녹화·업로드와 세션 종료 흐름 통합
danjee2009 Oct 10, 2026
bd2d981
feat: 사전설정에서 음성·영상 및 텍스트 면접 선택
danjee2009 Oct 10, 2026
1e6cbec
test: 면접 녹화 회귀 검증 및 API 연동 문서 추가
danjee2009 Oct 10, 2026
09a86ee
feat: use male voice for pressure interviews
han0911 Sep 17, 2026
491858b
fix: use interviewer1 for solo pressure interviews
han0911 Sep 17, 2026
eb5c942
feat: restore solo interviewer selection UI
han0911 Sep 17, 2026
2ab50a1
style: match solo interview setup layout
han0911 Sep 17, 2026
e2d75fc
fix: fit solo interview setup on one screen
han0911 Sep 21, 2026
f1a0186
fix: remove interviewer card image spacing
han0911 Sep 21, 2026
80a756a
fix: keep interviewer card images aligned
han0911 Sep 21, 2026
f855cb3
fix: 인터뷰 설정 페이지 높이 맞춤
han0911 Sep 21, 2026
4adb1c3
feat: 자료 분석 상태 조회와 면접 시작 가능 여부 판단 추가
danjee2009 Oct 12, 2026
84d994f
feat: 마이페이지에 자료 분석 상태 카드와 자료 변경 확인 창 추가
danjee2009 Oct 12, 2026
78bac67
feat: 면접 설정 시작 차단과 면접 준비 대기·다시 시도 처리
danjee2009 Oct 12, 2026
bf3b3b7
test: 녹화 회귀 테스트의 면접 준비 응답에 preparationStatus 반영
danjee2009 Oct 12, 2026
ecffaed
fix: warn before unload only for pending recordings
danjee2009 Oct 12, 2026
060bd3d
Merge branch 'codex/fix-pr126-recording' into codex/fix-pr127-profile
danjee2009 Oct 12, 2026
763e5c1
fix: allow material saves after profile query failures
danjee2009 Oct 12, 2026
7bda09d
Merge pull request #127 from Alpha-origin/enhancement/#125-question-d…
danjee2009 Oct 12, 2026
25976ed
Merge pull request #126 from Alpha-origin/feature/#124-sound-video-an…
danjee2009 Oct 12, 2026
f0dfa7b
fix: 마이페이지 개인정보·면접 자료 카드 디자인 개선
han0911 Oct 13, 2026
4d04cfa
fix: 마이페이지 개발·비개발 직무 선택 UI 추가
han0911 Oct 13, 2026
df13a06
Merge pull request #130 from Alpha-origin/fix/#128-my-ui-update
han0911 Oct 14, 2026
0295560
fix: 종합 피드백 UI를 참고 디자인에 맞춰 개편
han0911 Oct 14, 2026
3f57736
fix: 종합 피드백에 이전 면접 점수 비교 연동
han0911 Oct 14, 2026
535e0d9
fix: 종합 피드백 전용 헤더와 페이지 간격 조정
han0911 Oct 14, 2026
27e5e00
feat: 면접관 음성 카탈로그와 voiceIndex 저장 연결 (#132)
danjee2009 Oct 14, 2026
6dcf121
feat: Chat 질문 오디오 재생과 답변 시작 기준 시간 적용 (#132)
danjee2009 Oct 14, 2026
16bc354
test: 질문 오디오와 녹화 브라우저 회귀 검증 (#132)
danjee2009 Oct 14, 2026
75ef748
fix: 피드백 페이지의 면접 답변 조회 API 연동
han0911 Oct 14, 2026
a3000ee
fix: 피드백 생성 요청 및 분석 상태 자동 갱신 처리
han0911 Oct 14, 2026
87db101
fix: 종합 및 상세 피드백 탭의 선택 상태 수정
han0911 Oct 14, 2026
15cdf27
fix: 상세 피드백 UI를 참고 디자인에 맞춰 개편
han0911 Oct 14, 2026
a9d17b1
fix: 피드백 PDF 저장 시 레이아웃 유지 및 내용 잘림 수정
han0911 Oct 14, 2026
19cbba1
docs: TTS PR과 후속 배포 단계 기록 (#132)
danjee2009 Oct 14, 2026
20373b4
Merge pull request #135 from Alpha-origin/codex/132-tts-audio
danjee2009 Oct 14, 2026
75bd721
Merge pull request #133 from Alpha-origin/codex/132-tts-voice-index
danjee2009 Oct 14, 2026
49448c9
feat: compose solo interview personas and use server-assigned majors
danjee2009 Oct 15, 2026
3ea4aa2
feat: add generated Han Minseok interviewer portrait
danjee2009 Oct 15, 2026
2aaa935
Merge pull request #137 from Alpha-origin/codex/solo-persona-composition
danjee2009 Oct 15, 2026
273e717
fix: 랜딩 페이지 목업 수정
kwontaeyeonn Oct 15, 2026
71cc5ec
Merge pull request #138 from Alpha-origin/fix/120-landing-mockup-only
kwontaeyeonn Oct 15, 2026
332ffa1
feat: 분석 완료 시각과 면접 진입 동작 추가 (#140)
danjee2009 Oct 15, 2026
c0e172c
fix: 면접 준비 복구와 중단 저장 실패 처리 (#141)
danjee2009 Oct 15, 2026
77d4bbd
fix: 면접 시작에 PDF와 GitHub 자료 모두 요구 (#142)
danjee2009 Oct 15, 2026
1eff6f1
fix: 분석 시각 전체 검증과 명시적 시간대 처리
danjee2009 Oct 15, 2026
7104b21
fix: 복구 세션과 저장 재시도 후 원래 이탈 동작 유지
danjee2009 Oct 15, 2026
a01bfcb
Merge pull request #144 from Alpha-origin/codex/140-analysis-complete…
danjee2009 Oct 15, 2026
bbd0e5b
Merge pull request #145 from Alpha-origin/codex/141-interview-session…
danjee2009 Oct 15, 2026
e019451
Merge pull request #146 from Alpha-origin/codex/142-interview-eligibi…
danjee2009 Oct 15, 2026
9a19676
fix: 일시적인 피드백 조회 오류 후 자동 갱신 재시도
han0911 Oct 15, 2026
422dbe7
Merge pull request #147 from Alpha-origin/fix/#131-feed-back-ui-upadte
han0911 Oct 15, 2026
669e0fe
fix: 1대1 면접 설정 화면 반응형 레이아웃 개선
han0911 Oct 15, 2026
94172d2
fix: update interview setting UI
han0911 Oct 15, 2026
3b5363d
fix: dev 병합 충돌 해결 및 면접 설정 UI 유지
han0911 Oct 15, 2026
addbf6c
Merge pull request #149 from Alpha-origin/fix/#148-interview-ui-update
han0911 Oct 15, 2026
73e3045
fix: 메인페이지 헤더 크기와 메뉴 간격 조정
han0911 Oct 15, 2026
6184b28
fix: 메인페이지 면접 선택 카드 디자인 개선
han0911 Oct 15, 2026
0159dcd
fix: 메인페이지 콘텐츠 너비 및 반응형 여백 조정
han0911 Oct 15, 2026
caafa59
fix: 메인페이지 서비스 소개 배너 추가 및 안내 링크 연결
han0911 Oct 15, 2026
97eb245
fix: 솔로 면접 사전설정 UI 정리
danjee2009 Oct 15, 2026
de8d276
Merge pull request #152 from Alpha-origin/codex/solo-preset-ui
danjee2009 Oct 15, 2026
bb60220
Merge pull request #151 from Alpha-origin/fix/#150-main-ui-update
danjee2009 Oct 15, 2026
95cf999
fix: FAILED 피드백 재채점 요청 연결 (#153)
danjee2009 Oct 17, 2026
96ae407
enhancement: 페이지 레이아웃 통일 및 헤더 요소 확대 (#156)
danjee2009 Oct 17, 2026
d2fc238
fix: 브라우저 탭 Re:pit 파비콘 적용
danjee2009 Oct 17, 2026
ad8728e
feat: 직무 명세 상수 도입 및 GitHub 규칙·직무 표시 정리 (#160)
danjee2009 Oct 18, 2026
e87b3f5
fix: 옛 전공 코드 표시 유지, 숨김 GitHub 주소 전송 방지 (#160)
danjee2009 Oct 18, 2026
a5618e1
Merge pull request #159 from Alpha-origin/fix/#158-browser-favicon
danjee2009 Oct 18, 2026
6f10536
Merge pull request #157 from Alpha-origin/enhancement/#156-page-layou…
danjee2009 Oct 18, 2026
ee66479
fix: 개발 직무 헬퍼의 코드 리터럴 타입 유지 (#160)
danjee2009 Oct 18, 2026
5b5b771
fix: 재채점 충돌 후 결과가 없으면 FAILED 유지 (#153)
danjee2009 Oct 18, 2026
d3218c1
Merge remote-tracking branch 'origin/dev' into origin/fix/#153-feedba…
danjee2009 Oct 18, 2026
198bb19
Merge remote-tracking branch 'origin/dev' into origin/enhancement/#16…
danjee2009 Oct 18, 2026
9f142a5
refactor: 1:1·N:1 면접 생성 로직을 submit 함수로 분리 (#162)
danjee2009 Oct 18, 2026
34ac21b
feat: 사전설정 다음 기업 선택 단계 화면 임시 추가 (#162)
danjee2009 Oct 18, 2026
fdcd6d9
feat: 마이페이지·면접 시작 판정을 저장된 직무·자료(materials) 기준으로 변경 (#160)
danjee2009 Oct 18, 2026
40ef024
fix: 저장된 자료 안내·직무 선택 반영·직무 안내 문구 정리 (#160)
danjee2009 Oct 18, 2026
40bb04a
feat: 마이페이지 직무·자료를 문서 리스트·보완 입력과 함께 한 번에 저장 (#160)
danjee2009 Oct 18, 2026
892635e
fix: 저장소 공개 실패는 다시 분석, 변경 없는 저장은 확인 창 생략, 직무 없는 사용자 안내 (#160)
danjee2009 Oct 18, 2026
fb5b305
test: 면접 시작 판정 fixture를 materials 엔드포인트로 변경 (#160)
danjee2009 Oct 18, 2026
072b4b6
Merge #164 fixture fix into enhancement/#160-materials-edit
danjee2009 Oct 18, 2026
0944fcb
feat: 기업 선택 API와 면접 초안·생성 복구 연결 (#166)
danjee2009 Oct 18, 2026
7131d0c
feat: 기업 피드백과 실제 문항·이력 배지 연결 (#166)
danjee2009 Oct 18, 2026
57c170c
test: 기업 선택·복구·피드백 브라우저 검증 추가 (#166)
danjee2009 Oct 18, 2026
ffcc7a1
fix: 자료 조회 실패 시 저장 차단, 저장 중 수정 내용 보존 (#160)
danjee2009 Oct 18, 2026
fe1634c
fix: 분석 완료 시각 함수를 import 없는 파일로 분리 (#160)
danjee2009 Oct 18, 2026
3971bd4
Merge enhancement/#160-job-spec-foundation into enhancement/#160-mate…
danjee2009 Oct 18, 2026
30fe46d
Merge enhancement/#160-materials-read into enhancement/#160-materials…
danjee2009 Oct 18, 2026
f30c663
test: 면접 생성 404 시나리오에 기업 선택 단계 추가 (#162)
danjee2009 Oct 18, 2026
db78be9
Merge pull request #154 from Alpha-origin/fix/#153-feedback-regrade
danjee2009 Oct 18, 2026
ae19e81
Merge pull request #161 from Alpha-origin/enhancement/#160-job-spec-f…
danjee2009 Oct 18, 2026
ff79915
Merge pull request #164 from Alpha-origin/enhancement/#160-materials-…
danjee2009 Oct 18, 2026
b3addd1
Merge pull request #163 from Alpha-origin/feature/#162-company-select…
danjee2009 Oct 18, 2026
b9d0da8
Merge origin/dev into feature/#166-company-dataset
danjee2009 Oct 19, 2026
d9d818a
fix: 기업 선택 단계에서 면접 준비 404 시 마이페이지 안내 복구 (#166)
danjee2009 Oct 19, 2026
92d6cea
fix: 피드백 상세 문항 링크 스크롤·주소 유지와 초안 직무 코드 검증 정리 (#166)
danjee2009 Oct 19, 2026
31da3a6
fix: 종합 피드백 평가 축과 면접별 섹션 표시 수정 (#168)
danjee2009 Oct 19, 2026
9e63d48
fix: 텍스트 면접 문항 수를 실제 질문 수로 표시
danjee2009 Oct 19, 2026
eccf570
fix: 면접 완료 후 해당 면접의 종합 피드백으로 이동
danjee2009 Oct 19, 2026
99f06f1
test: 문항 수 표시와 면접 완료 후 피드백 이동 브라우저 검사 추가
danjee2009 Oct 19, 2026
fc4b891
fix: 피드백 목록 ABANDONED 상태를 "중단됨"으로 표시
danjee2009 Oct 19, 2026
599db8e
fix: 피드백 목록 난이도를 level로 읽고 SOLO 면접관 이름 표시
danjee2009 Oct 19, 2026
d5f681e
feat: 피드백 목록 정렬 기준(날짜·종합 점수)과 방향 선택, 기본값 최신순
danjee2009 Oct 19, 2026
dd7e300
test: 피드백 목록 표시·정렬 브라우저 시나리오 추가
danjee2009 Oct 19, 2026
358b3db
Merge pull request #165 from Alpha-origin/enhancement/#160-materials-…
danjee2009 Oct 19, 2026
0d652ae
fix: 자료 저장과 면접 생성 회귀 fixture 통합 (#166)
danjee2009 Oct 19, 2026
afb1d46
Merge pull request #167 from Alpha-origin/feature/#166-company-dataset
danjee2009 Oct 19, 2026
d818b61
fix: 정렬 방향 버튼 접근성 이름에 현재 방향 포함
danjee2009 Oct 19, 2026
cbc4c24
Merge remote-tracking branch 'origin/dev' into fix/#168-feedback-metr…
danjee2009 Oct 19, 2026
3179d7b
Merge pull request #169 from Alpha-origin/fix/#168-feedback-metrics-v…
danjee2009 Oct 19, 2026
662ca93
Merge remote-tracking branch 'origin/dev' into HEAD
danjee2009 Oct 19, 2026
927a0a2
Merge pull request #170 from Alpha-origin/fix/interview-complete-flow
danjee2009 Oct 19, 2026
a28d815
Merge remote-tracking branch 'origin/dev' into HEAD
danjee2009 Oct 19, 2026
954692b
Merge pull request #171 from Alpha-origin/fix/feedback-display-sort
danjee2009 Oct 19, 2026
2c29900
fix: 생성된 면접 확인 시 N:1 면접관 순서 차이를 불일치로 보지 않음
danjee2009 Oct 19, 2026
262f250
fix: 질문 음성 PENDING 응답 후 재요청에 최소 1초 간격 적용
danjee2009 Oct 19, 2026
5a0e481
Merge pull request #173 from Alpha-origin/fix/created-interview-perso…
danjee2009 Oct 19, 2026
6573dd7
Merge pull request #174 from Alpha-origin/fix/question-audio-pending-…
danjee2009 Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions docs/company-interviews.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 기업 선택·생성 복구·피드백

기준: 2026-10-19 사용자 제공 「기업 데이터셋 구현 명세 — 00. 공통 계약」·「01. Web」.
기반 커밋: `34ac21b` (기업 선택 임시 화면, PR #163), 원격 `dev` 확인: `6f10536`.
이슈: #166. 의존 PR #163을 먼저 병합한다.

## 화면과 API

`/main/setting/interview` 또는 `/main/setting/interview/multi` → `/main/setting/interview/company?draftId=<uuid>` → `/main/interview/:id?draftId=<uuid>`.

사전 설정은 검증·초안 저장·다음 단계 이동만 한다. 최종 시작에서 기존 페르소나 저장 로직으로 생성 DTO를 만들고, 선택한 경우에만 `companyId`를 추가한다. 면접 생성은 `submitSetupDraft` 한 곳에서만 호출한다.

- 목록: `GET /api/companies`. 검색 300ms 디바운스·최대 30자, 1자는 q 생략, 전체 분류는 category 생략. 요청 취소와 취소 여부 검사로 이전 결과가 현재 결과를 덮지 않는다.
- 단건 복구: `GET /api/interviews/get?interviewId=…`. 사용자·면접 ID 확인 후 종료 상태를 처리한다.
- 준비: 기존 `GET /api/questions/tailor?interviewId=…`, `POST /api/interviews/:id`, `POST /api/interviews/:id/preparation/retry`. READY와 chatDelivered를 확인한 뒤 진행 화면에 진입한다. 준비 실패로 create를 다시 호출하지 않는다.
- 기업 배지: 단건 면접 API의 snapshot 배지를 진행 화면에 전달한다. 이력과 피드백도 API 배지만 사용한다.

## 초안과 제출

Zustand 메모리 + 현재 탭 sessionStorage의 `repit:interview-setup:<ownerKey>:<draftId>`에 저장한다. 초기화·복원·변경 시 설정 enum·면접관·역할·중복·safe integer·UUID·제출 본문을 검사하고 알려진 필드만 보관한다. 토큰·문서 바이트·답변 원문을 저장하지 않는다. 해당 사용자의 로그아웃·사용자 변경 시 이 기능의 초안만 제거한다.

미제출 초안은 2시간 후 만료한다. 생성 ID가 있는 초안과 결과 불명인 제출은 TTL로 제거하지 않는다. 불명인 요청을 만료시키고 새 키로 생성하면 중복 면접이 될 수 있기 때문이다. 완료 면접은 피드백으로, 중단된 면접은 명시적 새 면접 안내로 복구한다.

최종 본문과 UUID `Idempotency-Key`를 **POST 전에 저장**한다. 저장 실패 시 POST하지 않는다. 연타·같은 탭에서 재진입한 제출은 같은 promise를 공유한다. 타임아웃·네트워크 실패·409는 본문과 키를 보존하며 설정 편집을 잠근다. 새로고침의 SUBMITTING/UNKNOWN도 같은 본문·키로만 재전송한다. COMPANY_UNAVAILABLE/COMPANY_CONTEXT_UNAVAILABLE 또는 확정된 동기 검증 실패는 선택을 유지하고 편집으로 돌아간다.

기술 가정: 생성 요청 타임아웃은 30초, 조회는 15초, 준비 확인은 기존 런타임과 같은 최대 20분·기본 1초/질문 대기 3초 폴링을 재사용한다. 서버 retryAfterMs보다 빨리 조회하지 않으며, 대기 초과 시 수동 상태 확인을 제공한다. 새 면접은 draftId 없는 명시적 설정 진입에서 시작한다. 예전 완료 초안을 자동으로 쓰지 않는다.

## 기업 코멘트

기업 선택 없음은 섹션을 숨긴다. company가 있으면서 null·유효하지 않은 companyFit이면 ‘기업 관점 코멘트를 제공하지 못했습니다’를 표시한다. 점수·그래프·합격 예측을 추가하지 않는다.

공통 상한(총평 300자, 목록별 3항목, factId 32자, focus 20자, 근거/제안 150자)은 Unicode code point로 검사한다. 긴 결과를 잘라 이어 붙이지 않는다. 같은 목록의 factId는 첫 유효 항목을 사용한다. 문항 ID는 `String(feedbacks[].questionId)`와 불투명 문자열로 비교하고 표시 번호는 실제 피드백 배열 순서를 쓴다. 유효 문항이 없는 항목은 숨기며, 원래 있던 항목이 전부 제거되면 총평도 숨긴다. 처음부터 두 목록이 빈 정상 결과는 총평만 표시한다.

종합·상세 화면과 기존 PDF 캡처 영역에 같은 기업 섹션을 포함한다. 링크는 `questionId` query로 실제 문항·답변을 선택하며 꼬리질문도 동일하게 처리한다.

## 검증

```sh
npm run lint
npm run build
node --test tests/*.test.mjs
PLAYWRIGHT_MODULE_PATH=/absolute/path/to/playwright/index.mjs INTERVIEW_TEST_ORIGIN=http://127.0.0.1:5174 node tests/company-setup.browser.mjs
```

Playwright는 기존 브라우저 검사처럼 외부 설치 경로를 지정할 수 있으며 새 패키지는 추가하지 않는다. `COMPANY_SCREENSHOT_DIR`로 화면 저장 위치를 지정한다.

기업 검사는 API 응답 대역을 사용한다. 초안 훼손·만료·소유자 변경·저장 실패, SOLO/MULTI 기업 유무, 최종 연타, SUBMITTING/UNKNOWN 새로고침, 같은 본문·키 유지, 검색 응답 역전, 목록 0개/장애, 검색 밖/숨김 기업 유지, 모바일·키보드, 준비 실패 재시도·완료 복구, snapshot 이름·꼬리질문·PDF를 검사한다.

## 서버 선행 조건과 검증 한계

로컬 API 소스의 기업 목록·snapshot 배지·companyFit DTO와 기존 준비 endpoint를 확인했다. 다만 현재 확인한 API `InterviewController`에는 Idempotency-Key 처리 코드가 없다. 공통 계약의 **서버 중복 방지 구현과 계약 테스트를 먼저 적용해야 한다**. Web의 고정 키 재전송만으로 서버 중복 생성이 방지되는 것은 아니다.

공개·검수 완료 기업 필터링, 숨김·snapshot 불가 오류 코드, 배포된 API/Analysis/Chat의 수신·저장·실제 사용도 서버 계약 테스트와 실제 연동으로 확인해야 한다. 이번 Web 작업에서는 실제 서버에 면접을 생성하거나 배포하지 않았다. 검수 데이터 완성·실제 모델 정확도·서버 실연동을 완료한 것으로 보지 않는다.


## 확인 결과

- `npm run lint`: 오류 0, 기존 router.tsx의 사용하지 않는 eslint-disable 경고 1개.
- `npm run build`: 통과 (TypeScript project build 포함).
- `node --test tests/*.test.mjs`: 32개 통과.
- 기업 설정 브라우저: 18개 시나리오 통과.
- 기존 면접 복구: 10개, 피드백 폴링: 11개, 녹화·종료·업로드: 22개 시나리오 통과.
- 녹화 검사의 기존 설정 버튼명 2곳을 현재 화면의 ‘텍스트 답변’·‘영상/음성 답변’에 맞췄다.
- 자체 리뷰: 생성 호출 단일화, POST 이전 저장, 소유자 변경 시 정리, 서버 준비 상태 확인, 문자열 문항 연결을 점검했다.
46 changes: 46 additions & 0 deletions docs/feedback-regrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# 실패한 피드백 재채점

- 이슈: #153
- 기준: `dev` `bb60220` (#147의 재채점 수정 요청 후속)
- 범위: 종합·상세 피드백의 FAILED 재시도. 서버 계약 변경 없음.

## 사용자 흐름

1. 피드백 화면 진입 시 기존 결과를 조회하고, 결과가 없으면 생성을 요청한다.
2. FAILED의 "다시 시도"는 `POST /api/feedbacks?interviewId=…`로 재채점을 요청한다.
3. 접수 중에는 버튼을 비활성화하고 "요청 중…"을 표시한다.
4. PENDING으로 접수되면 기존 5초 폴링을 새 5분 기한으로 시작한다.
5. 완료되면 결과를 표시하고, 실패하면 다시 시도할 수 있다.

409 응답은 다른 요청이 먼저 접수됐거나 결과가 이미 완성된 경우로 보고 한 번 조회한다.
조회 결과가 PENDING이면 폴링하고, SUCCEEDED이면 결과를 표시한다.
조회 결과가 없으면(404) FAILED를 유지하고 상태 확인 실패 문구를 표시한다. 다시 누르면 재채점을 요청한다.
그 밖의 재요청 오류는 FAILED를 유지하면서 새 오류 메시지를 우선 표시한다.

## 요청과 화면 수명

- 최초 조회·생성의 중복 방지와 POST의 중복 방지를 각각 유지한다.
- 최초 생성과 명시적 재채점은 같은 POST 요청 함수를 사용한다.
- 같은 면접의 POST가 진행 중이면 기존 Promise를 공유한다.
- 빠른 연속 클릭은 훅에서도 즉시 차단한다.
- 면접 변경·재조회·화면 이탈로 무효가 된 응답은 화면에 반영하지 않는다.

면접 정보 조회 오류의 재시도와 피드백 목록 재조회는 기존 동작을 유지한다.
5분 지연 상태의 버튼도 우선 재조회하며, API가 FAILED로 정리한 뒤 다시 눌러야 재채점된다.

## 검증

```sh
npm run lint
npm run build
# 별도 터미널에서 npm run dev 실행 후
node tests/feedback-polling.browser.mjs
```

외부 Playwright는 `PLAYWRIGHT_MODULE_PATH`, 브라우저 실행 파일은
`BROWSER_EXECUTABLE_PATH`, 서버 주소는 `INTERVIEW_TEST_ORIGIN`으로 지정할 수 있다.
API는 모의 응답으로 검증하며 실제 분석 서버를 호출하지 않는다.

브라우저 검증 범위: 종합·상세 재채점 완료, 409 후 진행 중·완료·결과 없음 분기,
500 후 오류 표시·재요청, 중복 클릭·요청 중 버튼 잠금, 최초 생성,
접수 응답 FAILED, 면접 전환 뒤 늦은 응답 무시, 기존 폴링·기한·화면 이탈.
47 changes: 47 additions & 0 deletions docs/feedback-visibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 종합 피드백 점수·섹션 표시 (#168)

`scoreBreakdown.axes`를 축 이름으로 연결합니다. 순서는 의도 충족(INTENT), 깊이(DEPTH), 구체성(SPECIFICITY), 정확성(ACCURACY)이며, 종합 점수는 기존 `totalScore`를 그대로 씁니다.

- 0점은 0점입니다. `score:null, weight:null`이 명시된 축은 “해당 없음”, 누락·잘못된 값·중복 축은 “미제공”입니다.
- 옛 응답의 의도 점수만 `intentAlignmentScore`로 보완합니다. 해당 없음은 덮어쓰지 않습니다.
- 분석 완료 결과에 고정 “분석 대기”를 만들지 않습니다. 점수 없는 결과를 조회한다고 재채점하지 않습니다.

| 유형 | 답변 방식 | 면접관별 코멘트 | 음성 영역 |
|---|---|---|---|
| SOLO | TEXT | 숨김 | 숨김·음성 요청 없음 |
| SOLO | VOICE | 숨김 | 표시 |
| MULTI | TEXT | 표시 | 숨김·음성 요청 없음 |
| MULTI | VOICE | 표시 | 표시 |

면접 유형은 면접 조회의 mode 우선, 누락 시 피드백 mode를 씁니다. 둘 다 없으면 면접관별 섹션을 숨깁니다. 빈 personas 개수로 유형을 추측하지 않습니다.

답변 방식은 [API 이슈 #107](https://github.com/Alpha-origin/Repit-api-server/issues/107)의 `answerMode: TEXT | VOICE | null` 계약입니다. 새 제출에만 설정 값을 추가하며 저장된 UNKNOWN/SUBMITTING 본문·요청키에는 새 필드를 주입하지 않습니다. 생성·재진입에서도 서버 값을 보존합니다.

기존 null 기록은 `/api/interviews/{id}/audio-analysis`의 녹음 답변 목록을 보조 근거로 씁니다. 완료 응답의 빈 녹음 목록은 섹션을 숨기지만 기록 자체를 TEXT로 보정하지 않습니다. 업로드가 전부 실패한 옛 VOICE 기록도 구분할 수 없는 한계가 있습니다. 조회 오류는 텍스트로 단정하지 않고 재확인 버튼을 표시합니다.

음성 조회는 PENDING에만 5초 간격·최대 5분 진행하며 화면 이탈·ID 변경 시 취소합니다. 완료/부분/결과 없음/실패/조회 오류를 구분합니다. 원시 지표에서 임의 100점 점수를 만들지 않고 음성 점수·코멘트는 준비 중으로 안내합니다. 본문 채점은 음성 조회를 기다리지 않습니다. 키워드·신뢰성·PDF 기능은 TEXT에서도 유지합니다.

## 검증 (2026-10-19)

- `node --test tests/*.test.mjs`: 40개 통과.
- `npm run lint`: 오류 0, 기존 router.tsx unused eslint-disable 경고 1개.
- `npm run build`: 통과.
- 실제 브라우저 + 독립 HTTP fixture: SOLO/MULTI × TEXT/VOICE, 새로고침, null 기록의 녹음 있음/없음, 503 및 재확인, PENDING→READY 자동 갱신, PENDING 도중 이탈 확인.
- fixture 요청 기록: TEXT 4201·4203의 음성 API 0회, VOICE 4202·4204 각 1회, 오류 4207 재확인 포함 2회, PENDING→READY 4208은 2회, 화면 이탈한 4209는 1회에서 중단.
- SOLO TEXT PDF 다운로드 후 Poppler로 A4 1쪽 렌더링: 네 축과 키워드·신뢰성 유지, 면접관·음성 섹션 없음, 겹침·잘림 없음.
- 자체 리뷰: 축 null/0 구분, 필드 없는 구형 제출 본문 불변, effect 취소와 stale 응답 차단, PDF의 숨김 조건 확인.

fixture 실행:

```sh
node tests/feedback-visibility-fixture.mjs
# http://127.0.0.1:5183/__feedback-fixture
```

실제 서버·유료 모델을 사용하지 않는 테스트 데이터입니다. 실서버를 연결한 별도 포트에서 기존 56번 결과를 열었으나 로그인 화면으로 이동하여 **수정 후 실서버 재면접·실모델 연동은 미검증**입니다. 실행 중 localhost:5173과 DB를 변경하지 않았습니다.

선행 Web #167, API #106·#107 변경 통합 후 실제 환경에서 새 텍스트 면접을 확인해야 합니다. 점수 생성·가중치·Analysis·Chat 계약은 변경하지 않습니다.

![SOLO TEXT 화면](images/feedback-visibility/solo-text.png)

![내보낸 PDF 렌더링](images/feedback-visibility/solo-text-pdf.png)
Binary file added docs/images/company-interviews/desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/company-interviews/feedback.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/company-interviews/mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/feedback-visibility/solo-text-pdf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/feedback-visibility/solo-text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 85 additions & 0 deletions docs/interview-recording.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# 면접 녹화와 업로드

## 답변 방식

1:1·다중 면접 모두 사전설정에서 `음성/영상 면접` 또는 `단순 텍스트 면접`을 선택한다.
선택은 `PreparedInterviewData.answerMode`로 라우터 상태에 보관하며, 서버의 `SOLO`/`MULTI` 면접 구성과는 별개다.
면접 내부에서는 전환할 수 없다. 텍스트 면접은 카메라·마이크 권한을 요청하거나 녹화하지 않는다.
음성/영상 면접에서 권한을 거부하면 답변 시작이 비활성화된다. 종료 후 사전설정에서 텍스트 면접을 선택할 수 있다.

## 파일과 수명

- 면접 준비와 카메라·마이크 준비가 모두 완료되면 전체 영상·음성 녹화를 한 번 시작한다.
- 각 답변 시작 시 오디오 트랙만 복제하고 당시 질문 번호를 보관한다.
- 답변 완료 버튼은 음성 파일 확정 → 업로드 시작 → 전사 답변 제출 순서다.
- 210초 자동 완료도 동일 경로를 사용하며 `endReason=timeout`을 보낸다. 분석 서버의 기본 길이 제한은 240초다.
- 그만두기·헤더 메뉴·뒤로가기·로그아웃은 진행 중 답변을 `interrupted`로 확정하고 전체 영상을 마무리한다.
- HTTP·소켓 정상 완료는 같은 종료 Promise를 공유한다. 전체 녹화 업로드와 장치 해제는 한 번만 실행한다.
- 답변은 WebM/MP4(Ogg 대체), 전체 영상은 MP4/WebM 중 지원 형식으로 녹화한다. 파일의 MIME과 확장자는 실제 컨테이너를 따른다. 답변 음성의 MP4 컨테이너 확장자는 `.m4a`다.
- 원본 오디오는 재인코딩하지 않는다. 요청 비트레이트는 음성 128kbps, 영상 1Mbps다.
- 복제 트랙은 녹화 정지·오류 시 해제한다. 원본 트랙은 세션 종료·페이지 해제 시 해제한다.

`POST /api/interviews/{interviewId}/recordings`의 multipart 필드:

| kind | 필드 |
| --- | --- |
| `ANSWER` | `file`, `kind`, `questionId`, `endReason` |
| `FULL_INTERVIEW` | `file`, `kind` |

업로드는 기존 인증 axios 클라이언트를 사용한다. 전체 영상에는 `questionId`와 `endReason`을 보내지 않는다.

## 업로드 상태

앱 범위의 작업 큐가 이동 이후에도 진행·완료·실패를 표시한다. 성공하거나 사용자가 폐기한 파일은 메모리 참조를 해제한다.
실패한 파일은 메모리에 보관하고 사용자가 다시 업로드하거나 폐기할 수 있다. 자동 재시도는 하지 않는다.
로그아웃은 세션 종료와 진행 중 업로드를 기다린 뒤 토큰을 지운다. 실패 파일이 있으면 인증을 유지하고 복구·폐기를 안내한다.

녹화 중, 파일 확정 중 또는 저장되지 않은 파일이 있는 동안 새로고침·탭 닫기에 `beforeunload` 경고를 요청한다.
브라우저 강제 종료 이후의 파일 복구는 제공하지 않는다. 장시간 전체 영상도 업로드 전까지 메모리에 보관한다.
서버가 파일 업로드에 멱등 키를 제공하지 않으므로, 서버 저장 이후 응답만 유실된 요청을 사용자가 재시도하면 중복 파일이 생길 수 있다.
전체 영상의 분석·다시보기 활용 정책과 사용자 조회 API는 별도 작업이다.

## 음성 분석 피드백 연결 조건

현재 로컬 API의 `AudioAnalysisController`는 `/api/analyses/audio/callback` 저장만 제공한다.
사용자 조회 엔드포인트를 추측하거나 Analysis 서버에 브라우저가 직접 접근하지 않는다.
API 서버에서 면접 소유권을 검사하는 조회 API와 질문·답변·녹음 식별자 계약을 제공한 뒤 피드백 훅·위젯을 연결한다.

조회 계약은 다음 사항을 포함해야 한다:

- 전체 및 답변별 `PENDING`, `READY`, `PARTIAL`, `UNAVAILABLE`, `FAILED` 상태.
- 질문·답변·녹음 식별자와 최신 분석 요청 식별자.
- 말 속도, 침묵, 속도 변동, 간투사·반복 후보, 항목별 측정 불가 이유 및 오류.
- `PARTIAL`은 일부 결과가 제공된 **완료 상태**다. 폴링은 `PENDING`에만 적용한다.
- 음향적 말더듬기 검출은 현재 제공되지 않는다. 유창성 후보에 임의 점수를 붙이지 않는다.

## 검증

`npm run lint`, `npm run build`를 실행한다.
브라우저 회귀 검증은 `npm run dev` 실행 후, 사용 가능한 Playwright 런타임과 Chromium으로 아래 스크립트를 실행한다.
프로젝트 패키지는 추가하지 않았다. 외부 런타임을 사용할 때 `PLAYWRIGHT_MODULE_PATH`에 해당 `playwright/index.mjs` 절대 경로를 지정한다.

```sh
node tests/interview-recording.browser.mjs
node tests/interview-persona.browser.mjs
node tests/question-audio.browser.mjs
```

`INTERVIEW_TEST_ORIGIN`으로 개발 서버 주소를 변경할 수 있다(기본 `http://127.0.0.1:5173`).
서버·소켓·전사는 모의하며 Chromium의 실제 MediaRecorder와 가상 카메라·마이크로 파일을 생성한다.
1:1·다중 답변 완료, 자동 완료, 진행 중 종료, 헤더 이동, 뒤로가기, 로그아웃, HTTP·소켓 동시 완료,
업로드 실패·재시도·파일 폐기, 장치 권한 거부, 텍스트의 장치 미요청을 검증한다.
준비 실패·권한 거부·녹화 시작 실패에는 새로고침 경고가 없고, 녹화·파일 확정·미저장 업로드 중에는 경고가 유지되며 업로드 완료·폐기 후 해제되는 것도 확인한다.
실제 API/S3 및 실장치 호환성 검증은 별도로 필요하다.

질문 음성은 Chat 오디오 조회 API(long-poll, wait=20)로 받은 HTTPS URL을 재생한다.
음성 면접은 질문마다 한 번 자동 재생하고 텍스트 면접은 `질문 듣기`로 재생한다.
답변 녹음 시작·답변 제출 성공·종료 시 정지하며, 정지한 질문의 늦은 음성은 자동 재생하지 않는다.
자동 재생이 브라우저에 차단되면 버튼으로 재생할 수 있고, 서버/파일 실패는 같은 안내 문구를 표시한다.
응답 시간은 질문 도착이 아닌 녹음 시작 또는 텍스트 첫 입력부터 계산한다.

`interview-persona.browser.mjs`는 실제 설정 화면의 SOLO 면접관 5명·프리셋·직접 조합, MULTI 전공 없음, 저장 요청 번호·성별·슬롯 순서·저장 실패를 검증한다.
`question-audio.browser.mjs`는 HTTPS 가상 저장소의 무음 WAV를 실제 Audio로 재생한다.
자동/수동 재생, 입력 중 재생 유지, 응답 시간, PENDING/QUEUED/GENERATING, 실패·404·재시도,
180초 상한, 자동 재생 차단, 질문 전환·종료 취소, 늦은 응답·play Promise를 검증한다.
재시도·상한 시나리오의 타이머만 단축하며, 운영 서버·외부 합성 서비스에는 요청하지 않는다.
Loading