아동004 29회기 수업 중단 — 아동 단말 인터넷 단절 원인 분석 ☠️ 해골 이슈 원인 확정

마지막 업데이트 2026-07-22

로그: 6-019ed9ba-…-d4dbc7706a57 (LogRocket, ppi-prod) 세션: 2026-06-18 17:00 KST 시작 (08:00 UTC) 관련 파일: apps/web/shared/lib/use-network-quality.ts
☠️ 해골 이슈 — 서버·앱 코드에 결함이 없고 아동 단말의 로컬 인터넷이 전면 단절된 케이스. 우리 쪽에서 직접 고칠 수 있는 버그가 아니라, 감지·안내·복구 UX 보강만 가능한 외부 요인 장애로 분류한다.

증상 VOC

수업(29회기) 진행 도중 아동(아동004) 측 접속이 끊김.
대기실 입장·디바이스 준비까지 모두 정상이었고, 수업 시작(08:00 UTC) 약 2분 51초 뒤 갑자기 연결이 두절됨. 콘솔에는 네트워크 진단 경고와 함께 React DOM 해체 에러(removeChild NotFoundError)가 동반 출력됨.

아동: 아동004 (userId 4d5630b7-…-51368c11177a) · 환경: prod · 단말: Realtek 오디오 + Samsung HD Camera (데스크탑/노트북 추정)

타임라인 (UTC, 괄호는 KST)

07:55:35.403 [USE ICE SERVERS] ICE 서버 6개 정상 수신 ← 셋업 정상 07:55:38.419 DeviceManager 권한 허용(audio+video), 디바이스 자동선택 완료 07:55:40.409 GuestEntry 대기실 입장 (plannedStart 08:00:00) 08:00:00 수업 시작 (17:00 KST) — ppi-socket 다수 룸 동시 시작 08:02:51.343 [NETWORK_QUALITY] 외부·서버 모두 도달 실패 — 사용자 인터넷 전체 다운으로 진단 externalLatency: null / serverLatency: null (issue = user-network) 08:02:51± Uncaught NotFoundError: removeChild — node already removed ← 화면 해체 부수 에러 ~08:03:30 [ppi-socket] disconnect 카운트 첫 틱업 (engine.io keepalive 만료로 서버가 끊김 인지)

useNetworkQuality 는 10초 주기로 돌고 issue 전이 시점에만 로그를 남긴다. 따라서 실제 망 단절은 08:02:51 직전 10초 구간 안에서 시작됐고, 프로브가 도는 시점까지 복구되지 않은 것이다.

핵심 결론 ROOT CAUSE

아동 단말의 로컬 인터넷이 수업 중 전면 단절된 것이 직접 원인. 서버(ppi-socket)·앱 코드에는 이상이 없었다. removeChild 에러는 끊김으로 인한 화면 해체 중 발생한 부수 증상이지 원인이 아니다.

근거 ① — NETWORK_QUALITY 3-프로브 동시 실패

user-network 판정(use-network-quality.ts:124)은 서로 다른 망/IDC의 3곳이 전부 3초 타임아웃으로 실패해야만 찍히도록 의도적으로 설계돼 있다. 우리 서버만 죽으면 issue: "server" 로 분기하므로, 3곳 동시 불통은 단말이 인터넷 자체를 잃었다는 강한 신호다.

프로브대상성격결과
외부 #1gstatic.com/generate_204Google anycast 연결성 표준실패
외부 #21.1.1.1/cdn-cgi/traceCloudflare anycast 폴백실패
서버/api/health우리 서버 (serverLatency=null)실패
이 조합은 Wi-Fi 끊김, 공유기/AP 문제, ISP 순간 단절, 절전·슬립, 셀룰러 핸드오프 등 단말 측 네트워크 단절로만 설명된다. 같은 시각 우리 서버는 다른 룸들의 트래픽을 정상 처리하고 있었다(아래 ②).

근거 ② — 서버측 교차검증 (ppi-socket 프로세스 메트릭)

Grafana PPI Socket 상세 대시보드를 끊김 윈도우(08:00–08:05 UTC / 17:00–17:05 KST)로 맞춰 확인. 끊김 순간 서버는 어떤 부하·블로킹도 없었다.

지표값 (08:00–08:05 UTC)판정
이벤트 루프 지연30~48 µs (마이크로초), 08:02:51 전후 스파이크 없음정상
Mediasoup 워커 CPU (PID 19/20)0 → 0.003% (사실상 유휴)정상
Node.js 메모리 RSS~140 MiB → 08:02:30경 ~128 MiB 하강 (세션 정리 회수)정상
접속/해제 비율해제(disconnect)가 ~08:03:30 첫 틱업끊김 인지
로그 레벨전부 INFO, WARN/ERROR 없음정상

서버에 남은 유일한 흔적은 ① 08:03:30의 정상 disconnect 카운트 틱, ② 세션 정리에 따른 RSS 소폭 회수뿐. 클라이언트가 인터넷을 잃어도 서버는 engine.io keepalive 만료까지 수십 초 지연되어 끊김을 인지하므로, 08:02:51 단절 → 08:03:30 서버 인지의 시차는 정상이다.

removeChild 에러는 부수 증상

Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node':
The node to be removed is not a child of this node.

망이 끊기며 컴포넌트·비디오 엘리먼트가 언마운트되는 과정의 React DOM teardown 레이스(이미 제거된 노드를 다시 제거)다. 코드 내 실제 removeChild 호출은 전부 다운로드/iframe 정리용이라 세션 끊김과 무관하다. 외부 확장(번역 등)이 DOM을 건드릴 때도 동일 에러가 나며, 어느 경우든 이 에러가 접속을 끊은 것이 아니라 끊김으로 인한 화면 해체 중 발생한 것이다.

영향 범위

대응 방향 제안

우리 코드 결함이 아니므로 "수정"이 아니라 감지·안내·복구 UX 보강이 핵심이다.
  • issue === "user-network" 진입 시 아동/진행자 양측에 "인터넷 연결을 확인해 주세요" 안내와 자동 재연결 진행 표시
  • 망 복구(issue → "none" 전이) 시 세션 자동 복귀 경로 점검 — 끊김 윈도우의 socket transport state/reconnect 로그로 실제 복구 여부 확정
  • 반복 발생 단말이면 사용 환경(Wi-Fi/유선·공유기) 점검을 운영 측에 공유

관련 문서