결론 — 범인부터 공개한다
이 이슈의 본질은 영상 컴포넌트 자체가 OFF를 무시하는 것이 아니라, 수업 시작 전 호스트 설정 모달의 OFF 값이 게스트가 사용하는 SFU room autoTransition 상태로 전파되지 않는 상태 소스 분리다. 서버 room 기본값이 ON이기 때문에, 게스트는 승인 시 ON을 받고 영상 종료 시 정상 코드 경로대로 자동 전환한다.
자동전환 OFF가 게스트 영상 종료 로컬 전환에 반영되지 않는 상태 분리 버그
호스트 설정 모달의 settings.autoTransitionEnabled와 SFU room의 autoTransitionEnabled가 별도 상태로 유지된다.
monitor-dashboard의 useRoomAutoTransition으로 OFF하면 SFU room에 반영되어 게스트로 브로드캐스트된다.
수사 일지 ① — 사건 발생: OFF로 껐는데 화면이 넘어갔다
진행자는 분명 설정 모달에서 자동전환을 OFF로 바꿨다. 그런데 수업 영상이 끝나자 아동 화면이 제멋대로 다음 스텝으로 넘어갔다. 진행자 UI에는 OFF라고 표시된 채로. 먼저 사건의 정확한 재현 경로부터 확보했다.
- 증상: 자동전환을 OFF로 바꿨는데 수업 영상이 끝나면 아동 화면이 다음 스텝으로 넘어간다.
- 발생 경로: 게스트가 먼저 대기 화면에 들어와 있고, 호스트가 이후 수업 화면에 들어와 자동전환 OFF를 설정한 뒤 수업을 시작한다.
- 영향 범위: 영상 스텝(
full:video/split:video)의MeetVideo.onEnded이후 게스트 로컬goToNextStep()경로. - 분석 기준: Slack 원문 재현경로와 현재 로컬 develop 코드 기준.
git pull origin develop은 GitHub 인증 문제로 실패하여 최신 원격 동기화는 미확인.
최종 재현 경로
/client-guest/session으로 진입하고 수업 대기(고양이 화면) 상태가 된다.autoTransitionEnabled: true를 기본값으로 둔다./app/main/meet/[roomId]에 들어와 설정 모달에서 자동전환을 OFF로 바꾼다.components/pages/host.tsx의 로컬 settings만 갱신한다. SFU room 상태에는 SET_AUTO_TRANSITION을 보내지 않는다.autoTransitionEnabled: true를 guest-approved2 payload로 보낸다.autoTransitionEnabled === true로 판단해 goToNextStep()을 호출한다.수사 일지 ② — 용의자 심문: 반증 루프
가장 단순한 용의자부터 심문했다 — "영상 컴포넌트가 OFF를 무시하고 무조건 넘긴다"는 가설. 그러나 코드는 결백했다. 게이트는 있었고, 문제는 게이트에 들어오는 값이었다. 심문을 거듭할수록 수사망은 "값이 어디서 갈라졌나"로 좁혀졌다.
| 가설 | 검증 결과 | 판정 |
|---|---|---|
영상 컴포넌트가 OFF를 무시하고 항상 goToNextStep() 한다. | GuestLayoutContent는 영상 종료에서 if (autoTransitionEnabled)로 게이트한다. | 기각 |
| 서버가 OFF 값을 게스트에게 전송하지 않는다. | SFU room 상태가 OFF라면 GUEST_APPROVED2에 OFF가 실려 간다. 다만 호스트 설정 모달 변경은 SFU room 상태를 바꾸지 않는다. | 부분 수정 |
| 호스트 UI의 OFF와 게스트가 보는 OFF가 서로 다른 상태이다. | host.tsx 로컬 settings와 routerManager.rooms[roomId].autoTransitionEnabled가 별도로 존재한다. | 유지 |
수사 일지 ③ — 범행 수법: 코드 레벨 원인
살아남은 가설은 하나 — "호스트가 본 OFF와 게스트가 받은 ON은 애초에 다른 상태다". 이제 그 두 상태가 어디서 태어나 어디서 갈라지는지, 코드로 범행 경로를 복원했다. 서버 기본값(A)에서 출발해 게스트로 전달되는 payload(B), 어디에도 닿지 않는 호스트 설정(C), 그리고 최종 범행 현장인 영상 종료 게이트(D)까지.
A. 서버 room 기본값은 ON
apps/socket/src/mediasoup/routerManager.ts:24-34
room = {
...,
autoTransitionEnabled: true, // default: true for new system
}
getAutoTransitionEnabled()도 room이 없으면 true를 반환한다(routerManager.ts:140-142). 따라서 명시적으로 SFU room 상태를 OFF로 바꾸지 않으면 게스트가 받는 값은 ON이다.
B. 게스트 입장/승인 시 서버 값이 전달된다
apps/socket/src/sfu-socket/handlers/room-handlers.ts:171-184
const autoTransitionEnabled = routerManager.getAutoTransitionEnabled(roomId);
socket.to(guestId).emit(SOCKET_EVENTS.GUEST_APPROVED2, {
resourceFileNames,
autoTransitionEnabled,
externalSttEnabled,
});
게스트는 이 값으로 pendingAutoTransitionEnabled와 세션 상태를 세팅한다.
apps/web/entities/guest-socket/model/use-guest-socket.ts:97-101
setPendingAutoTransitionEnabled(data.autoTransitionEnabled ?? true);
apps/web/entities/guest-page-session/model/use-guest-page-session.ts:625-635
setAutoTransitionEnabled(enabled);
aiSessionRef.current.updateAutoTransitionEnabled(enabled);
C. 호스트 설정 모달은 로컬 settings만 바꾼다
apps/web/components/pages/host.tsx:984-1012
const handleSettingsConfirm = (newSettings) => {
...
setSettings(newSettings);
...
}
여기에는 SOCKET_EVENTS.SET_AUTO_TRANSITION, sessionRelay.toggleAutoTransition(), 또는 SFU room 상태 갱신 호출이 없다. 즉 “수업 시작 전 자동전환 OFF”가 게스트가 참조하는 room 상태와 분리된다.
D. 영상 종료 시 게스트 로컬 전환은 게스트 상태를 기준으로 한다
apps/web/shared/ui/guest-layout-content.tsx:113-118
props.session.notifyVideoEnd();
if (autoTransitionEnabled) {
props.session.goToNextStep();
}
따라서 게스트가 승인 payload에서 true를 받으면, 호스트 로컬 설정이 OFF여도 영상 종료 순간 게스트는 다음 스텝으로 이동한다.
수사 일지 ④ — 결정적 대조: 정상/반례 경로
범행 수법을 확정하려면 "같은 OFF인데 정상 동작하는 경로"와 나란히 놓고 대조해야 한다. monitor-dashboard 경로와 세션 릴레이 토글은 모두 room 상태(또는 게스트 세션)에 실제로 닿는다 — 닿지 않는 것은 수업 시작 전 설정 모달뿐이었다.
- 정상 경로 1: monitor-dashboard 설정(
useRoomAutoTransition)에서 OFF하면SOCKET_EVENTS.SET_AUTO_TRANSITION을 emit하고 서버가 room 상태를 변경한다(use-room-auto-transition.ts:42-59,room-handlers.ts:282-304). - 정상 경로 2: 수업 시작 후 호스트의 세션 릴레이 토글을 사용하면
host-session-auto-transition이 게스트 세션 매니저로 전달되어 AI auto-finish 쪽은 갱신된다(use-host-session-relay.ts:293-300,use-guest-session-relay.ts:367-370). - 문제 경로: 수업 시작 전
components/pages/host.tsx설정 모달에서 OFF만 저장하면 room 상태가 ON으로 남는다.
증거물 목록 — 의심 파일/라인
apps/web/components/pages/host.tsx:984-1012— 설정 모달 확정 시 로컬settings만 갱신하고 SFU room autoTransition 상태를 갱신하지 않음.apps/socket/src/mediasoup/routerManager.ts:24-34, 140-142— room 기본값과 fallback이true.apps/socket/src/sfu-socket/handlers/room-handlers.ts:171-184— 게스트 승인 시 room의 현재 autoTransition 값을 payload에 포함.apps/web/entities/guest-socket/model/use-guest-socket.ts:97-101, 333-341— 승인 payload를 게스트 pending 상태로 저장 후 승인 확정 시 전달.apps/web/entities/guest-page-session/model/use-guest-page-session.ts:625-635, 656-661— 게스트 세션의autoTransitionEnabled상태 갱신 지점.apps/web/shared/ui/guest-layout-content.tsx:113-118— 영상 종료 시 게스트 로컬 자동 전환 게이트.
수사 일지 ⑤ — 검거 계획: 해결 방향
범인은 "두 개의 진실"을 허용한 상태 분리다. 검거는 호스트 설정 모달의 OFF가 게스트가 참조하는 단일 source of truth(room 상태)에 반드시 닿게 만드는 것이다.
- 권장:
handleSettingsConfirm에서autoTransitionChanged가 true면 SFU room에도SOCKET_EVENTS.SET_AUTO_TRANSITION을 emit한다. 그러면 대기 중인 게스트에게도AUTO_TRANSITION_UPDATED가 브로드캐스트된다. - 보완:
GUEST_APPROVED2payload 기준도 호스트 로컬 설정이 아니라 단일 source of truth인 room 상태로 유지하되, 호스트 설정 UI가 반드시 그 상태를 갱신하게 만든다. - 검증: 재현 경로에서 OFF 변경 직후 게스트 콘솔에
Auto-transition updated { enabled:false }가 찍히고, 영상 종료 후Notifying video ended는 찍히되Moving to next step이 없어야 한다.