5.5MB 런타임을 사용하는 브라우저 기반 Python 플레이그라운드가 느린 연결을 사용하는 사용자들에게 조용히 실패(failing silently)하기 시작했습니다. 원인은 잘못 사용된 Network Information API와 문제를 잘못 분류한 에러 그룹화 대시보드였습니다. 이 버그는 몇 주 동안 숨어 있었고, 개발자의 시간을 낭비했으며, 일부 사용자가 코드를 실행할 수 없게 만들었습니다.
문제가 드러난 과정
플레이그라운드의 에러 트래커에는 **“undefined is not an object.”**라는 눈에 띄는 메시지 하나가 나타났습니다. 제목만 보고 팀은 단순한 JavaScript 오타라고 생각하여 존재하지 않는 코드 경로를 추적했습니다. 하지만 원시 메타데이터를 조사했을 때, 해당 인시던트의 89%가 실제로는 네트워크 타임아웃이라는 것을 발견했습니다. 대시보드가 도착한 첫 번째 에러를 가져와 전체 배치의 이름으로 사용함으로써 실제 실패 유형을 가려버린 것입니다.
교훈 1 – 대시보드 제목은 기만적일 수 있다
대시보드가 인시던트를 집계할 때는 그 집계 로직이 각 이벤트의 실제 원인을 반영해야만 도움이 됩니다. 여기서는 에러 원인이 아닌 위치별로 그룹화하여 클라이언트 측 버그라는 잘못된 인상을 주었습니다. 교훈: 대시보드 헤드라인만 보고 문제를 해결하려 하지 마세요. 리소스를 할당하기 전에 기반이 되는 이벤트 샘플을 추출하여 실제로 무슨 일이 일어나고 있는지 확인해야 합니다.
교훈 2 – 플레이스홀더 값은 측정값이 아니다
느린 연결을 사용하는 사용자들에게 무거운 런타임을 로드하지 않기 위해, 코드는 Network Information API를 참조하여 초당 메가비트(Mbps)를 보고하는 downlink 속성을 읽었습니다. Chrome은 첫 방문 시 실제 측정값 대신 플레이스홀더(placeholder)를 반환하는 경우가 많습니다. 로직은 이 플레이스홀더를 빠른 연결로 간주하여 최적화를 건너뛰었고, 결과적으로 도움을 주려 했던 바로 그 사용자들을 차단하는 결과를 초래했습니다.
모든 기본값(default)이나 센티널(sentinel) 값은 "데이터 없음"으로 취급하십시오. 플레이스홀더는 실제 속도 측정값으로 해석되는 것이 아니라, 폴백(fallback) 전략을 트리거해야 합니다.
교훈 3 – 네트워크 환경은 변하므로 단일 스냅샷은 신뢰할 수 없다
downlink 문제를 해결한 후, 팀은 연결을 “4g”, “3g” 등으로 분류하는 effectiveType을 확인하는 방식으로 전환했습니다. 간단한 실험실 테스트는 통과했지만, 잠시 후 동일한 테스트를 다시 실행했을 때는 실패했습니다. 모바일 연결은 변동성이 큽니다. 사용자는 한순간에는 빠른 4G 연결 상태였다가 다음 순간에는 느린 3G로 떨어질 수 있습니다. 페이지 로드 시점에만 연결을 확인하는 것은 도박과 같습니다.
올바른 접근 방식은 Network Information 객체의 change 이벤트에 **구독(subscribe)**하여, 일회성 결정을 내리는 대신 대역폭의 모든 변화에 반응하는 것입니다.
팀이 변경한 사항
- 2단계 다운로드 – 이제 런타임은 아주 작은 부트스트랩(bootstrap) 파일로 시작합니다. 연결이 느린 것으로 확인되면, 부트스트랩 파일이 나머지 런타임을 작은 청크(chunk) 단위로 가져와 전체 다운로드가 중단될 가능성을 줄입니다.
- 실시간 모니터링 – 단일
downlink읽기 대신, 이제 코드는change이벤트를 수신하여 대역폭의 변화에 따라 다운로드 전략을 즉시 조정합니다. - 안정적인 소스 선택 – 이전에는 더 빠른 엔드포인트가 나타나면 다운로드 도중에 CDN을 전환했습니다. 이는 느린 연결에서 다운로드를 처음부터 다시 시작하게 만들어 문제를 악화시켰습니다. 새로운 로직은 다운로드가 진행되는 동안 소스를 고정합니다.
- 지연된 캐시 쓰기 – 앱을 사용할 수 있게 되기 전에 실행되던 무거운 캐시 작업은 이제 런타임이 시작된 이후로 연기되어, 중요한 다운로드를 위한 대역폭을 확보합니다.
더 넓은 관점에서의 중요성
웹 기반 도구를 구축하는 개발자에게 네트워크 가변성은 매우 중요한 고려 사항입니다. 느린 연결에서의 조용한 실패는 사용자를 좌절시키고 텔레메트리 데이터를 왜곡하여, 팀이 잘못된 디버깅 경로로 빠지게 만듭니다. 이번 사례의 경우, 데이터를 잘못 해석한 탓에 몇 주간의 무익한 조사가 이어졌습니다.
다음에 주의 깊게 살펴볼 점
핵심 요약: 데이터가 너무 깔끔해 보인다면 플레이스홀더일 가능성이 높습니다. 대시보드 헤드라인이 단일 버그를 가리킨다면 더 깊이 파고드세요. 그리고 단 한 번의 네트워크 읽기에 기반해 결정을 내린다면, 그것은 움직이는 과녁을 겨냥하는 것과 같습니다. 이러한 현실을 반영하여 조정할 때, 조용한 실패는 예측 가능하고 복구 가능한 이벤트로 바뀝니다.
