PPI Media Recovery Reference

업링크 stall 발생 시 웹 대처방안

마지막 업데이트 2026-09-29

아동 단말에서 SFU로 올라가는 실시간 미디어 업로드가 멈추거나 급격히 나빠졌을 때, 웹 클라이언트가 취할 수 있는 감지·완화·복구·운영 대응을 정리한다. 핵심은 “네트워크 자체를 고치는 것”이 아니라 오디오 우선 정책으로 수업 지속성을 확보하는 것이다.

작성일: 2026-09-29 대상: guest → mediasoup SFU uplink 우선순위: mic-audio > ai-audio relay > camera-video

1. 업링크 stall의 의미

여기서 업링크 stall은 transport가 반드시 끊긴 상태만 의미하지 않는다. transport는 connected이고 producer/track도 live인데 outbound bytes가 증가하지 않는 상태까지 포함한다.

품질 저하형lowBitrate / rttHigh / packetLoss

RTT, 송신 대역폭, 패킷 손실 지표가 임계치를 넘는다. 현재는 네트워크 팝업·LogRocket 이벤트·진행자 relay 중심으로 동작한다.

채널 끊김형cameraVideo / cameraAudio / aiAudio / socket

미디어 track 또는 socket이 7초 이상 live가 아니면 critical로 올라간다. 화면 멈춤, 아동/AI 소리 끊김, 재접속으로 체감된다.

connected-but-stalledbytesSentDelta = 0

producer가 살아 있는데 1초 샘플에서 bytesSent가 늘지 않는다. 현재 코드는 로그와 15초 재확인만 수행한다.

2. 현재 코드의 대응 범위

상황현재 대응근거 코드한계
RTT ≥ 400ms, bitrate < 150kbps, packet loss > 5%게스트 네트워크 경고, Guest:NetworkAlertTriggered, monitor relayapps/web/hooks/use-guest-network-alert.ts:148-257
apps/web/hooks/network-alert-config.ts:2-15
자동 복구 액션은 없음
send transport disconnected3초 후에도 disconnected이면 transport cleanup 후 재생성apps/web/hooks/mediasoup/use-mediasoup-producer.ts:356-369connected 상태의 실송신 stall은 못 잡음
send transport failed즉시 reconnect 시도, 최대 3회apps/web/hooks/mediasoup/use-mediasoup-producer.ts:370-374
:460-483
3회 실패 후 새로고침 안내
audio producer outbound stallAudio producer outbound stalled 로그, 15초 후 재확인apps/web/hooks/mediasoup/use-mediasoup-producer.ts:179-205producer 재생성·track 재획득·transport 재생성 없음

요약: transport 레벨 장애에는 자동 reconnect가 있지만, producer/track은 live인 connected-but-stalled 상태에는 진단만 있다.

3. 권장 복구 원칙

업링크 stall 대응의 기본 정책은 “영상 품질을 낮추거나 잠시 포기하더라도, 아동 마이크와 AI 대화 흐름을 우선 살린다”이다.
오디오 우선mic-audio를 최우선으로 유지

수업 진행자는 아동 음성이 들려야 개입과 수업 지속이 가능하다. uplink가 부족하면 camera-video를 먼저 낮추고, mic producer 복구를 우선한다.

단계적 복구producer → track → transport → socket 순서

큰 비용의 socket 재접속부터 하지 말고, producer 재생성·track 재획득·transport 재생성 순으로 blast radius를 키운다.

4. 웹에서 가능한 자동 대응 루프

감지

producer별 outbound stats를 2포인트 샘플링한다. bytesSentDelta === 0, track.readyState === "live", !producer.paused이면 stall 후보로 본다. camera-video에도 동일한 stats sampling을 확장한다.

즉시 완화

lowBitrate 또는 camera stall이면 camera-video bitrate/framerate를 낮추거나 producer를 일시 pause한다. mic-audio는 유지한다.

producer 재생성

해당 producer를 close하고 기존 live track을 clone해 produce()를 다시 수행한다. mic-audio는 동일 track clone 우선, 실패 시 getUserMedia({ audio: true }) 재획득으로 넘어간다.

track 재획득

track 자체가 mute/ended이거나 clone reproduce 후에도 stall이면 카메라/마이크를 재획득한다. 재획득 중에는 진행자에게 “복구 시도 중” 상태를 relay한다.

send transport 재생성

producer 복구 후에도 10~15초 이상 connected-but-stalled가 지속되면 기존 attemptReconnect() 경로를 재사용해 send transport를 cleanup/recreate한다.

fallback UX

복구 실패 시 아동에게는 재접속 안내, 진행자에게는 원인 channel과 추천 액션을 표시한다. 수업은 가능한 한 음성 우선 모드로 유지한다.

5. channel별 대응안

reason/channel1차 대응2차 대응진행자 표시
lowBitratecamera bitrate/framerate 강등camera producer pause, mic 유지아동 업로드 대역폭 부족 · 음성 우선 전환
packetLosscamera 강등, 복구 대기producer 재생성 후 transport 재생성패킷 손실로 음성/영상 끊김 가능
cameraVideocamera producer close/reproduce카메라 track 재획득, 저화질 produce아동 영상 업로드 복구 중
cameraAudio 또는 mic stallmic producer close/reproduce마이크 getUserMedia 재획득아동 마이크 업로드 복구 중
aiAudioai-audio producer reproduceremote track clone 대신 AudioContext destination track 사용AI 음성 relay 복구 중
socketsocket reconnect/rejoin 확인room rejoin 후 transport 재생성수업 신호 연결 재접속 중

6. 구현 스케치

// pseudo
if (producerStalled(type)) {
  log("Audio/Video producer outbound stalled", detail);

  if (type === "camera-video") {
    degradeCameraQuality();        // 360p/10fps 또는 pause
    reproduceCameraProducer();
  }

  if (type === "mic-audio") {
    reproduceMicProducerWithClonedTrack();
    if (stillStalled()) reacquireMicTrackAndProduce();
  }

  if (type === "ai-audio") {
    reproduceAiAudioProducer();
    if (stillStalled()) produceAudioContextDestinationTrack();
  }

  if (allRecoveryFailedFor(10_000)) {
    recreateSendTransport();       // existing attemptReconnect path 재사용
  }
}
주의: transport 재생성은 producer 재생성보다 비용이 크다. connected-but-stalled를 감지했다고 곧바로 socket disconnect나 강제퇴장을 유도하면 수업 맥락이 더 크게 흔들릴 수 있다.

7. 운영 판정 기준

로그 확인LogRocket/Grafana 키워드
  • Guest:NetworkAlertTriggered의 reasons
  • Audio producer outbound stalled
  • Send transport disconnected/failed
  • guest-transport-state의 RTT/bitrate/packetLoss
분류네트워크 vs 미디어 경로
  • RTT/bitrate/packetLoss 악화 동반: 네트워크성 uplink 문제
  • stats 정상인데 element/RMS만 0: 재생·출력 문제
  • producer live + bytes 0: connected-but-stalled 복구 후보

권장 결론: 현재 PPI 웹은 stall을 관찰할 수 있는 기반이 있으므로, 다음 개선은 “오디오 우선 + producer 재생성 + camera 품질 강등 + 필요 시 transport 재생성”의 자동 복구 루프를 추가하는 것이다.