Skip to content

fix: 중단된 면접의 문답을 다시 볼 수 없던 문제 + 답변 시퀀스 경합 - #175

Merged
i3months merged 1 commit into
devfrom
fix/terminal-session-transcript-access
Aug 18, 2026
Merged

fix: 중단된 면접의 문답을 다시 볼 수 없던 문제 + 답변 시퀀스 경합#175
i3months merged 1 commit into
devfrom
fix/terminal-session-transcript-access

Conversation

@i3months

Copy link
Copy Markdown
Member

변경 사항

감사 백로그 A-5, A-6.

A-5 — 중단 세션에 진입 경로 자체가 없었다

보고했던 것보다 나쁜 상태였다. SessionCardCOMPLETED 일 때만 <Link> 였다:

return completed ? <Link to={`/sessions/${id}/feedback`}>{body}</Link> : body

중단된 면접은 히스토리에서 눌리지도 않고, 피드백은 애초에 생성되지 않는다(INTERRUPTED 는 SessionEndedEvent 미발행). 즉 사용자가 말한 내용에 도달할 방법이 하나도 없었다. URL 을 직접 쳐서 /sessions/:id 로 가도 SessionEndedPanel 이 "면접이 중단되었습니다" 한 줄만 보여주고, /sessions/:id/feedback 은 폴링 40회(약 2분)를 다 쓴 뒤 INTERRUPTED 세션에는 통하지도 않는 "피드백 다시 생성" 버튼을 내민다.

진행 중이던 면접도 같은 이유로 눌리지 않아서, 탭을 닫거나 다른 화면으로 나가면 돌아갈 길이 없었다.

상태 이전 이후
COMPLETED 피드백 리포트 그대로
INTERRUPTED 진입 불가 세션 화면 → 문답 기록
IN_PROGRESS 진입 불가 세션 화면 → 이어서 진행
READY 진입 불가 로비
CANCELLED 진입 불가 그대로 (시작 전 취소라 볼 게 없다)

SessionEndedPanel 은 COMPLETED·INTERRUPTED 에서 InterviewTranscript 를 함께 렌더한다. 이 컴포넌트는 이미 있었고 피드백 화면에서만 쓰이고 있었다 — 즉 피드백이 있어야만 볼 수 있는 상태였다. InterviewTranscript 가 자체적으로 useSessionMessages(sessionId) 를 켜므로, useLiveInterview 가 종료 세션에서 메시지 조회를 끄고 있어도 기록은 정상적으로 뜬다.

A-6 — 동시 제출이 500 으로 나가던 문제

submitAnswer / createVoicePlaceholderlatest.getSequenceNumber() + 1 로 seq 를 계산한다. 같은 턴에 두 요청이 동시에 들어오면 둘 다 같은 값을 잡고, (session_id, sequence_number) UNIQUE 가 한쪽을 막아 정체불명의 500 이 됐다.

실제 의미는 '이 턴은 이미 답변됐다' 이므로 그렇게 알린다 — saveAndFlush 로 INSERT 를 메서드 안에서 터뜨리고(커밋까지 미루면 트랜잭션 밖에서 500 으로 샌다) DataIntegrityViolationExceptionSESSION_INVALID_STATE(422)로 변환했다.

사전 조회로 대체하지 않은 이유: 사전 체크도 같은 경합에 노출된다. UNIQUE 제약이 최종 방어선이고, 여기서는 그 결과를 사용자에게 정확한 의미로 옮기는 것만 한다.

테스트

  • SessionCard.test.tsx 신설 (5) — 상태별 목적지 4종 + 취소 세션은 링크 없음
  • InterviewMessageServiceTest · VoiceAnswerUploadServiceTest 에 시퀀스 충돌 → 422 변환 케이스 추가
  • 기존 스텁 savesaveAndFlush 정정. 꼬리질문 placeholder 경로는 save 그대로라 MessageSequenceAssignmentTest 는 건드리지 않았다

backend test 통과 (ArchUnit 포함), frontend vitest 81/81 · eslint 0 · tsc 통과.

영향 범위

  • DB 마이그레이션: 없음
  • API contract 변경: 없음 (동시 제출 시 응답 코드가 500 → 422 로 바뀌는 것이 전부)
  • 환경변수: 없음

리뷰어 체크포인트

  • CANCELLED 를 링크에서 제외한 판단 — 시작 전 취소라 자기소개 질문 하나뿐이라고 봤다. 취소 이력도 열람 대상이라면 알려주세요
  • 중단 세션 화면에 "같은 설정으로 다시" CTA 를 붙이면 자연스러운 자리다 (백로그 B-2)

## A-5 — 중단 세션에 진입 경로 자체가 없었다

히스토리 카드(`SessionCard`)가 **COMPLETED 일 때만** `<Link>` 였다. 중단된 면접은 눌리지도
않고, 피드백은 애초에 생성되지 않으므로(INTERRUPTED 는 `SessionEndedEvent` 미발행) 사용자가
말한 내용에 도달할 방법이 하나도 없었다. 진행 중이던 면접도 마찬가지로 눌리지 않아서, 탭을
닫거나 다른 화면으로 나가면 **돌아갈 길이 없었다**.

- `SessionCard` — 상태별 목적지를 표로 정리: COMPLETED→피드백, INTERRUPTED→세션 화면(기록),
  IN_PROGRESS→세션 화면(이어서), READY→로비. CANCELLED 는 시작 전 취소라 볼 게 없어 그대로 둔다
- `SessionEndedPanel` — COMPLETED·INTERRUPTED 면 `InterviewTranscript` 를 함께 렌더.
  이 컴포넌트는 이미 있었고 피드백 화면에서만 쓰이고 있었다(= 피드백이 있어야만 볼 수 있었다)

`InterviewTranscript` 는 자체적으로 `useSessionMessages(sessionId)` 를 켜므로,
`useLiveInterview` 가 종료 세션에서 메시지 조회를 끄고 있어도 기록은 정상적으로 뜬다.

## A-6 — 동시 제출이 500 으로 나가던 문제

`submitAnswer`/`createVoicePlaceholder` 가 `latest.getSequenceNumber() + 1` 로 seq 를
계산한다. 같은 턴에 두 요청이 동시에 들어오면 둘 다 같은 값을 잡고, `(session_id,
sequence_number)` UNIQUE 가 한쪽을 막아 정체불명의 500 이 됐다.

실제 의미는 '이 턴은 이미 답변됐다' 이므로 그렇게 알린다 — `saveAndFlush` 로 INSERT 를
메서드 안에서 터뜨리고 `DataIntegrityViolationException` 을 `SESSION_INVALID_STATE`(422)로
변환. 제약 자체가 최종 방어선이라 사전 체크로 대체하지 않았다.

## 테스트

- `SessionCard.test.tsx` 신설 (5) — 상태별 목적지 + 취소 세션은 링크 없음
- `InterviewMessageServiceTest` / `VoiceAnswerUploadServiceTest` 에 시퀀스 충돌 → 422
  변환 케이스 추가. 기존 스텁은 `save`→`saveAndFlush` 로 정정
  (꼬리질문 placeholder 경로는 `save` 그대로다 — `MessageSequenceAssignmentTest` 무변경)

backend `test` 통과, frontend vitest 81/81 · eslint 0 · tsc 통과.
@i3months
i3months merged commit 2f640a6 into dev Aug 18, 2026
5 checks passed
@i3months
i3months deleted the fix/terminal-session-transcript-access branch August 18, 2026 18:31
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