마지막 업데이트 2026-09-29
진행자는 "아동 영상이 시작되지 않는다"고 보고 강제퇴장을 했습니다. 하지만 아동 화면의 영상은 재생되고 있었습니다. 멈춘 것은 진행자 화면에 띄우는 복제본이었습니다.
setIsVideoPlaying(false)를 updater 안에서 늦게 적용해 true를 덮어쓰고, 그래서 아동 영상은 정상 재생되는데 진행자 화면의 영상만 0초에 멈춥니다(use-host-socket.ts:770).
진행자 화면은 아동 화면을 녹화해서 받는 것이 아니라, 같은 영상 파일을 따로 재생합니다.
서버는 순서를 지키려고 ① 스텝 변경 → ② 재생 시작을 연달아 보냅니다.
① 쪽지 안의 "재생 끄기"는 봉투에 넣어 두었다가 화면을 다시 그릴 때 꺼내 적용합니다.
setState(updater). updater 함수는 React가 화면을 다시 그릴 때 실행될 수 있습니다. 그 안에서 부른 setIsVideoPlaying(false)가 먼저 들어온 true보다 뒤에 적용됩니다.canplay), 서버 시계로 재생 위치를 맞췄을 때입니다. 그래서 강제퇴장 후 재접속으로 상태를 새로 받을 때까지 멈춰 있었습니다.
파일·장치·브라우저는 모두 같았습니다. 달랐던 것은 쪽지 두 장의 도착 간격뿐입니다.
파일: 세션 로그 임새아_2회기.json (JSONL, 12,841줄). 아동과 진행자는 다른 PC라 시계가 어긋날 수 있어 간격은 진행자 시각끼리만 비교했습니다.
실패 — 모델링영상 L3413 09:09:02.904Z guest Moving to next step ← 아동: 모델링영상 스텝 L3438 09:09:02.943Z guest Video canplay event fired L3440 09:09:02.944Z guest Notifying video play started ← 아동: 재생 시작 전송 L3446 09:09:03.314Z monitor Guest step updated ┐ L3449 09:09:03.314Z monitor Guest step updated │ 1ms L3450 09:09:03.315Z monitor Guest video play started ┘ L3459 09:09:04.035Z monitor Video canplay event fired ← 이후 재생 시도 없음 성공 — 재접속 후 같은 영상 L3581 09:09:48.392Z guest Notifying video play started L3590 09:09:48.700Z monitor Guest step updated ┐ 43ms L3602 09:09:48.743Z monitor Guest video play started ┘ L3603 09:09:48.746Z monitor Attempting programmatic play from playback effect 성공 — 다음 영상(bridge) L6847 09:14:59.163Z guest Notifying video play started L6855 09:14:59.331Z monitor Guest step updated ┐ 148ms L6856 09:14:59.479Z monitor Guest video play started ┘ L6868 09:14:59.636Z monitor Attempting programmatic play from canplay handler
timeupdate가 5초 이상 끊기면 남김)가 0건이었고, 09:09:21.717Z에 isPlaying: true 상태로 화면이 내려갔습니다. 진행자도 "모델링영상 스텝에서 영상이 정지 상태였다"고 확인했습니다.
제품 코드는 건드리지 않았습니다. 프로젝트와 같은 React 19.2.6으로 임시 스크립트를 만들어 같은 호출 순서를 흉내 냈습니다.
| 케이스 | 최종 재생 상태 |
|---|---|
| A. L3446~L3450 순서를 다시 그리기 없이 연속 처리 | false — 실패 재현 |
| B. 스텝 변경을 그린 뒤 재생 시작 도착 | true |
| C. 별도 태스크로 연달아 도착 (중간에 그리기 가능) | true |
| 위치 | 하는 일 |
|---|---|
apps/web/entities/host-socket/model/use-host-socket.ts:769-779 | 스텝 변경 처리. setGuestCurrentStep(() => { if (stepChanged) setIsVideoPlaying(false); … }) ← 봉투 속 "재생 끄기" |
use-host-socket.ts:988-995 | 재생 시작 처리. setIsVideoPlaying(true) |
apps/socket/src/sfu-socket/handlers/session-relay-handlers.ts:934-1063, 541-559 | 서버가 재생 신호를 스텝 확정까지 보류한 뒤 ①→② 순서로 연달아 전송 (#1058) |
apps/web/shared/ui/meet-video.tsx:471, 719 | 진행자 영상을 재생하는 두 경로. 둘 다 isPlaying이 true일 때만 동작 |
apps/web/app/monitor-dashboard/[group]/[roomId]/page.tsx:2206 | requestVideoElapsed={isVideoPlaying ? … : undefined} — false면 서버 시계 기준 재생 위치 맞추기도 시작되지 않음 |
| 날짜 | 커밋 | 내용 |
|---|---|---|
| 2026-03-19 | 8fc6119d (#302, PPI-676) | 스텝 변경 updater 안에 setIsVideoPlaying(false) 추가 — 결함 시작 |
| 2026-07-28 | b03871e4 | 스텝이 바뀌었는지 판단하는 코드는 updater 밖으로 뺐지만, "재생 끄기"는 안에 남음 |
| 2026-09-02 | 3e9f13d8 (#1058) | 서버가 ①→② 순서를 보장. "② 먼저 와서 지워짐" 문제는 막았지만 모니터 쪽 봉투 문제는 그대로 남음 |
canplay 후 재생 시도 없음 + 두 쪽지 간격 수 ms 이내)이 몇 번 나오는지 세어 보면 확인할 수 있습니다.setIsVideoPlaying(false)를 updater 밖으로 빼서 쪽지를 받은 순서대로 적용합니다.canplay 후 일정 시간 멈춰 있으면 서버에 재생 여부를 다시 물어봅니다.| 의심 | 판정 |
|---|---|
영상 파일 video_episode_family_0_low | 아닙니다. 양쪽 모두 정상 로드(86.517초)됐고, 재접속 후 끝까지 재생됐습니다. |
| 네트워크·연결 (자동 분류기 1순위) | 아닙니다. 끊김 기록은 강제퇴장 때문에 생긴 결과입니다. |
setSinkId("communications") AbortError | 아닙니다. 아동 페이지에 들어올 때마다 났고, 영상이 정상 재생될 때도 똑같이 났습니다. |
| 오디오 장치 (SSL 2 USB · Voicemeeter) | 이 증상과는 무관합니다. 다만 "USB"가 들어간 장치를 헤드셋으로 자동 선택하는 규칙 때문에 소리 경로가 나뉘었을 수 있어, 18:05 "소리가 안 들려요" 신고와는 따로 확인이 필요합니다. |