Arda 팀 프로젝트 · 4인
면접이 끝나도 카메라가 안 꺼지던 것 — 끝나는 길이 셋이었다
놓는 코드를 dispose()에만 뒀는데, IndexedStack이 탭을 살려 두니 그 화면은 앱이 살아 있는 한 dispose되지 않았다.
지원자가 「면접 종료」를 누르고 「면접이 완료되었습니다」가 떠도
카메라 표시등이 계속 켜져 있었다. 다른 탭으로 옮겨도 그대로고,
앱을 죽여야 꺼졌다. 근거 커밋은 36bb7c5.
dispose()는 오지 않는다
놓는 코드를 dispose() 에만 뒀던 게 원인이다.
지원자 셸이 IndexedStack 으로 탭을 살려 둔다. 탭을 옮겨도 위젯이
버려지지 않고 상태가 유지되는 구조다 — 그게 이 셸을 그렇게 짠 이유이기도 하다.
그런데 그 말은 이 화면이 앱이 살아 있는 한 dispose 되지 않는다는 뜻이었다.
눈에 안 보이는 쪽이 더 나빴다
카메라 표시등은 눈에라도 보였다. 진짜 문제는 그 뒤에 있었다.
lie-detection 소켓도 안 닫혀서 서버가 끝난 세션을 계속 판정하고 있었다.
실기기로 면접을 끝낸 뒤에 /ai/health 를 봤더니 live.scored 와 no_face 가
10초에 하나씩 나란히 올라가고 있었다. 아무도 앞에 없는 화면을
서버가 계속 채점하고 있던 것이다.
끝나는 길이 셋이다
세어 보니 면접이 끝나는 경로가 셋이었고, 어느 것도 자원을 안 놓고 있었다.
- 「면접 종료」 버튼 →
_finish()— 서버에 알리기만 했다 - 서버가
InterviewDone을 보냄 — 질문을 다 답하면 이 길이다. 사실 더 흔하다 _load()가 done·expired 를 받음
셋 다 _releaseCall() 을 부르게 했다. 두 번 불러도 안전하게 만들었다.
순서에도 함정이 있었다. _finish() 는 서버가 끝을 확인한 뒤에 놓는다 —
먼저 놓으면 finish 가 실패했을 때 카메라만 꺼지고 면접은 살아 있는,
더 이상한 자리가 된다.
카메라 정리는 _dropCall() 로 갈라 동기로 뒀다.
dispose() 가 렌더러를 닫는 것과 엇갈리면 죽은 렌더러의 srcObject 를 건드린다.
테스트를 못 붙였다
이건 솔직히 적어둔다. 이 화면에는 테스트를 한 줄도 못 붙였다.
MicService 와 소켓을 하드코딩해서 만들고 있어서 주입구가 WebRTC 하나뿐이다.
위젯 테스트를 돌리면 플랫폼 채널에 걸려 죽는다.
주입구를 내는 건 별도 작업으로 남겼다.
그리고 그게 이 버그가 살아남은 까닭이다. 화면에 테스트가 없으면 “끝나는 길이 셋”같은 건 아무도 세어보지 않는다.
남는 교훈
dispose() 를 정리 지점으로 믿으려면 그 위젯이 정말 버려지는지부터 봐야 한다.
탭을 살려 두는 셸, 캐시, 라우트 스택 — 위젯을 살려 두는 구조는 흔하다.
그리고 자원을 쥐는 화면은 끝나는 길을 세어야 한다. 버튼 하나만 보고 있으면 나머지 둘은 조용히 빠져나간다.