마지막 업데이트 2026-07-22
GUEST_SOCKET] Notifying video ended 로그가 없으면 guest-video-end emit 함수 자체가 호출되지 않은 것으로 판정했다.
Video play event 및 guest-video-play-started는 대부분 정상onEnd 호출해야 함DECODE_OVERLOAD, step change, force kick, navigation reset으로 onEnd 누락guest-video-end 미발송으로 모니터 진행자 자동전환 불가| 세션 | 핵심 판정 | Watchdog 미발화 이유 | 대표 로그 신호 |
|---|---|---|---|
| 길주한 21회기 2026-06-19 11:30Z |
부분 누락video_tiki20_movie_05 종료 emit 없음 |
마지막 확인 시 26.006 / 29.653s. 끝 3.6초 전이라 watchdog 종료 조건 밖. 곧바로 host step change로 mediaType=""가 되어 watchdog 해제. |
Video waiting [DECODE_OVERLOAD]Step changed by host |
| 김성운 25회기 2026-06-19 11:00Z |
최종 영상 누락video_dubutiki_bye_2 |
49.877초 영상이 media clock 기준 끝에 도달하기 전 complete force kick이 먼저 들어와 socket disconnect. |
Video waiting [DECODE_OVERLOAD]Guest force kicked complete |
| 이희성 15회기 2026-06-20 00:45Z |
중간 영상 누락 2개 영상 emit 없음 |
video_taeyang_explanation2...는 42.690 / 48.874s에서 다음 영상으로 소스 변경. video_chulmin_win_0은 끝 0.8초 전이었지만 near-end stall 3초 조건 전에 다음 prompt로 전환. |
Resetting video element after URL changeGUEST_STEP_UTILS step-update |
| 임주안 38회기_1 2026-06-17 11:30Z |
영상 종료 문제 아님 재입장/강제퇴장 흐름 |
140초 첫 영상이 23~28초만 재생된 상태에서 reason="kick" force kick. 종료 후 자동전환 실패가 아니라 외부 세션 제어 이벤트. |
Guest force kicked reason=kick |
| 임주안 38회기_2 2026-06-17 11:32Z |
종료 감지 루틴 소실video_newpp02_gacha_01 |
완전 버퍼링 상태로 재생 시작 후 waiting, pause, ended, watchdog 로그가 모두 없음. 이후 다른 auto-finish/step 진행으로 이전 watchdog이 reset된 패턴. |
Video play event 이후 장시간 영상 로그 침묵 |
| 최연우 21회기 2026-06-19 05:30Z |
소켓/미디어 파이프라인 끊김 최종 영상 emit 없음 |
17.4초 영상 4.4초 지점에서 탭 hidden + websocket transport close. 30초 stall 백스톱 전에 session navigation/reinitialize 발생. |
Unexpected pausereplaceTrack ... peer connection is closedtrack ended |
| 최유리 11회기 2026-06-19 08:32Z |
최종 영상 누락video_dubutiki_bye_2 |
49.877초 영상인데 마지막 waiting 시점 32.318s. 끝 17.6초 전이라 watchdog 종료 조건 밖. 직후 complete force kick으로 socket 종료. |
Video waiting [DECODE_OVERLOAD]Guest force kicked complete |
VideoEndWatchdog은 currentTime, duration, isPlaying, shouldStop 조건을 읽고, media clock이 끝에 도달했거나 완전히 stall된 경우에만 onEnd를 호출한다.
duration - currentTime <= 0.3sprops.isPlaying이 true이고 videoSrc가 유지되어야 함currentTime이 느리게 증가해 30초 완전 stall로 보이지 않음watchdog.reset()으로 이전 감지 상태 소멸// apps/web/shared/lib/video-end-watchdog.ts
const firstDelayMs = hasDuration
? Math.max(0, (duration - currentTime) * 1000)
: pollIntervalMs;
if (remaining <= 0.3) fire();
else if (remaining <= 2.0 && stalledMs >= 3000) fire();
else if (stalledMs >= 30000) fire();
video_tiki20_movie_05 play. duration 29.653s.Video waiting [DECODE_OVERLOAD] 반복. 마지막 확인 currentTime=26.006, 아직 3.647초 남음.Step changed by host, mediaType="". 게스트 isPlaying=false가 되어 watchdog disarm/reset.Video onEnd, Notifying video ended 모두 없음.video_dubutiki_bye_2 play. duration 49.877s.currentTime=32.318, 아직 17.559초 남음. watchdog 종료 조건 밖.Guest force kicked - disconnecting socket, reason complete. socket disconnect.guest-video-end 대신 세션 종료/퇴장 흐름이 선행.video_bye_jiwoo_kidscafe_new_0 play. duration 17.408s.transport close, unexpected pause.replaceTrack 실패, track ended. 이후 session navigation/reinitialize.아래 비율은 마지막으로 확인된 currentTime / duration이다. 100%에 가까울수록 watchdog/near-end fallback이 발화할 수 있다.
// apps/web/components/pages/guest.tsx
const handleVideoEnded = () => {
guestVideoActivity.notifyVideoEnd();
trackEvent("Guest:VideoEnded", ...);
};
// apps/web/entities/guest-socket/model/use-guest-socket.ts
const notifyVideoEnd = useCallback(() => {
logger.info("Notifying video ended");
socket.emit("guest-video-end", { roomId: props.roomId });
}, [props.roomId]);
Notifying video ended가 없으면 guest-video-end emit이 호출되지 않은 것으로 볼 수 있다.
// apps/web/shared/ui/meet-video.tsx
useEffect(() => {
isPlayingRef.current = props.isPlaying;
if (props.isPlaying) watchdog.arm();
else watchdog.disarm();
}, [props.isPlaying, watchdog]);
useEffect(() => {
watchdog.reset(); // videoUrl 변경 시 이전 영상 감지 상태 초기화
...
}, [videoUrl, watchdog]);
// apps/web/components/pages/guest.tsx
isPlaying={mediaType?.startsWith("resource:video_") || false}
따라서 host step change, auto-finish에 의한 새 step 진입, force kick, lesson-ended 화면 전환이 발생하면 기존 영상의 watchdog은 더 이상 종료 이벤트를 보장하지 않는다.
| 질문 | 판단 | 근거 |
|---|---|---|
| 소켓 emit 실패인가? | 대부분 아님 | Notifying video ended 로그가 없어 emit 함수 자체가 호출되지 않았다. |
| Watchdog이 배포/동작하지 않았나? | 아님 | 같은 로그군에서 Video onEnd triggered via watchdog backstop 정상 사례가 다수 존재한다. |
| Watchdog 설계가 이 장애를 커버하나? | 커버하지 못함 | 현재는 media clock 기반. wall-clock duration 초과, step 변경 전, force kick 전 강제 emit 보장은 없다. |
| 공통 개선 방향은? | 별도 wall-clock 안전장치 | 영상 시작 시각 + duration + grace 기준으로, 아직 같은 video step이면 guest-video-end를 한 번 더 보장하는 레이어가 필요하다. |
MeetVideo 또는 게스트 세션 레벨에 “wall-clock based video end guard”를 별도 추가한다.
조건은 videoPlayStartedAt + duration + grace를 넘고, 같은 activityIndex/stepIndex/videoSrc가 유지 중이며, 아직 onEndCalled가 아니면 notifyVideoEnd와 goToNextStep을 idempotent하게 호출하는 방식이 적합하다.