Skip to content

fix(web): 권한 대기 중 언마운트 시 카메라·마이크 트랙 누수 제거 - #232

Open
i3months wants to merge 1 commit into
devfrom
fix/media-hook-pending-unmount-leak
Open

fix(web): 권한 대기 중 언마운트 시 카메라·마이크 트랙 누수 제거#232
i3months wants to merge 1 commit into
devfrom
fix/media-hook-pending-unmount-leak

Conversation

@i3months

@i3months i3months commented Sep 7, 2026

Copy link
Copy Markdown
Member

배경 — 코드 감사에서 발견

라이브 면접 미디어 훅(features/interview/lib/media/) 정독 중 발견한 리소스 누수. 같은 버그 클래스가 두 훅에 있었다.

useWebcamPreview·useVoiceRecordergetUserMediaawait 한다. 그 사이(권한 프롬프트가 열려 있는 수 초 동안) 컴포넌트가 언마운트되면:

  1. 언마운트 cleanup 이 먼저 실행되는데, streamRef.current 는 아직 null 이라 아무 트랙도 정지하지 못한다.
  2. 이후 사용자가 권한을 허용하면 getUserMedia 가 resolve 되어 스트림이 이미 언마운트된 훅에 할당된다.
  3. 그 스트림을 정지할 소유자가 더는 없다 → 카메라 LED / 마이크 표시등이 페이지 새로고침 전까지 켜진 채로 남는다.

useWebcamPreview필수 주석("카메라 LED 가 계속 켜지는 것 방지")이 정확히 이 레이스로 무력화되고 있었다.

재현 동선

면접 중 카메라 켜기(또는 음성 답변) 클릭 → 브라우저 권한 프롬프트가 뜬 상태에서 면접 종료·이탈·연결 끊김 리다이렉트로 InterviewStage 언마운트 → 뒤늦게 권한 허용 → 장치 표시등 잔존.

변경

  • 두 훅에 requestId 세대값 도입. stop()언마운트 cleanup 이 이를 올려 pending 인 getUserMedia 를 무효화한다.
  • getUserMedia resolve 후 requestId 가 바뀌었으면 방금 열린 트랙을 즉시 stop().
  • 늦게 도착한 거부(catch)는 requestId 가 일치할 때만 denied 로 반영(무효화 후 상태 되돌림 방지).

useVoiceRecorder 는 기존에 cancel()requestId 를 올려 언마운트 경로가 누락돼 있었고, useWebcamPreview 는 세대값 자체가 없었다.

테스트

  • pending 중 언마운트 회귀 테스트 2건 추가 — 수정을 되돌리면 둘 다 실패하고, 적용하면 통과함을 확인.
  • 변경 파일 typecheck·eslint 통과. 관련 미디어 테스트(웹캠·음성·AnswerComposer) 통과.

참고(범위 밖): 전체 vitest 스위트에 사전 존재하는 플레이키가 있다(깨끗한 dev 에서도 FeedbackAiReport 등 lazy 마크다운 테스트가 실행마다 0~2건 오락가락). 본 PR 과 무관하며 별도 처리 필요.

🤖 Generated with Claude Code

useWebcamPreview·useVoiceRecorder 가 getUserMedia 를 await 하는 동안
언마운트되면, 언마운트 cleanup 은 아직 null 인 streamRef 만 읽어 아무것도
정지하지 못한다. 그 뒤 권한이 뒤늦게 허용되면 스트림이 소유자 없는 훅에
할당되어 카메라 LED·마이크 표시등이 페이지 새로고침 전까지 켜진 채 남는다.
프롬프트가 열려 있는 창(수 초)이라 흔히 재현된다.

- 두 훅에 requestId 세대값 도입. stop()/언마운트가 이를 올려 pending 을 무효화.
- getUserMedia resolve 후 requestId 가 바뀌었으면 방금 열린 트랙을 즉시 정지.
- 늦게 도착한 거부(catch)는 requestId 일치할 때만 denied 로 반영.
- pending 중 언마운트 회귀 테스트 2건 추가 (수정 없으면 실패, 있으면 통과 확인).

useVoiceRecorder 는 기존 cancel() 만 requestId 를 올려 언마운트 경로가 빠져
있었고, useWebcamPreview 는 세대값 자체가 없었다.
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