연결 단계에서 발견된 간헐적 연결 문제
WebRTC는 RTCPeerConnection
과 ICE 프로토콜을 통해 두 클라이언트 간 최적 경로를 탐색한다.
const peerConnection = new RTCPeerConnection({
iceServers: [{ urls: "example.google.com:19302" }],
});
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
socket.emit("ice-candidate", event.candidate);
}
};
peerConnection
.createOffer()
.then((offer) => peerConnection.setLocalDescription(offer))
.then(() => {
socket.emit("offer", peerConnection.localDescription);
});
해외 네트워크 환경을 사용하는 사용자들의 연결 기능에서 다음과 같은 현상이 간헐적으로 발생했다:
- 영상이 연결되지 않거나 상대가 보이지 않음
- 상태 동기화나 메시지 수신이 실패함
- 네트워크 문제인지 브라우저 문제인지 식별하기 어려움
- 사용자 입장에서는 연결 상태에 대한 명확한 피드백이 없어 혼란을 느낌
📌 VPN 사용 환경에서의 연결 실패
또한 일부 사용자들이 VPN을 활성화한 상태에서 접속하는 경우, WebRTC 연결이 실패하는 이슈가 존재했습니다.
이 경우 STUN 서버가 공인 IP를 제대로 파악하지 못하거나,
VPN이 UDP 및 P2P 트래픽을 제한하는 경우가 많아 ICE candidate 수집 자체가 실패할 수 있다.
// ICE candidate 로그 분석으로 VPN 의심 환경 확인
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
const cand = event.candidate.candidate;
if (cand.includes("relay")) {
console.log(
"VPN 또는 제한된 네트워크일 경우 설정에서 VPN 기능을 끄고 사용하시길 권장합니다.",
);
} else if (cand.includes("host")) {
console.log("직접 연결(host candidate)");
}
} else {
console.log("ICE candidate 수집 종료");
}
};
프론트엔드에서는 이를 직접 감지할 수는 없지만,
연결 실패 시 “VPN이 켜져 있다면 확인해 주세요"라는 안내 메시지를 추가함으로써
사용자 혼란을 줄이는 데 도움이 되었다.
이 문제를 계기로, 연결 상태에 대한 진단 및 복원 전략을 설계하게 되었다.
전략: 테스트 영상 연결 시스템 구축
영상 연결 시작 전, 네트워크 상태를 점검할 수 있는 별도의 테스트 연결 시스템을 구축했다.
목표는 단순했다:
- 사용자 측의 네트워크가 WebRTC 연결에 적합한지 사전 진단
- 연결이 정상인지 아닌지를 눈으로 확인할 수 있는 구조
- 실패 시, 관리자가 사전 대응(재접속 유도, 전화 연결 등)을 할 수 있도록
Socket.IO로 연결 상태 추적
WebRTC의 연결 여부 자체는 API만으로 감지하기 어렵기 때문에,
우리는 WebRTC 연결과 함께 Socket.IO
를 통해 연결 상태를 판단했다.
// useSocket.ts
const socket = io(SOCKET_URL, {
reconnection: true,
reconnectionAttempts: 5, // 5회까지 재시도
reconnectionDelay: 3000, // 3초 간격
timeout: 8000, // 연결 시도 제한 시간 8초
});
useEffect(() => {
socket.on("connect", () => {
setStatus("connected");
});
socket.on("disconnect", () => {
setStatus("disconnected");
});
socket.on("reconnecting", () => {
setStatus("reconnecting");
});
socket.on("connect_error", () => {
setStatus("error");
});
}, []);
이 이벤트 흐름을 통해 연결이 끊겼는지, 재시도 중인지, 정상 연결되었는지를 클라이언트에서 실시간으로 인지할 수 있었다.
연결 상태를 UI로 시각화
우리는 단순한 문자열 표시가 아닌, 직관적인 색상 원형 표시를 사용해 네트워크 상태를 표현했다.
// Indicator.tsx
import React from "react";
import styled from "styled-components";
type Status = "connected" | "reconnecting" | "disconnected" | "error";
interface IIndicatorTypeProps {
status: Status;
}
const statusColorMap: Record<Status, string> = {
connected: "#4CAF50", // green
reconnecting: "#FFEB3B", // yellow
disconnected: "#F44336", // red
error: "#9E9E9E", // gray
};
const Dot = styled.div<{ color: string }>`
width: 10px;
height: 10px;
border-radius: 50%;
background-color: ${({ color }) => color};
margin-right: 8px;
`;
const Indicator: React.FC<IIndicatorTypeProps> = ({ status }) => {
return <Dot color={statusColorMap[status]} />;
};
export default Indicator;
이러한 UI는 사용자 또는 관리자가 한눈에 상황을 파악할 수 있도록 해주었고,
사용자에게도 “지금 연결이 안 되고 있다"는 명확한 피드백을 제공해 UX 혼란을 줄일 수 있었다.
재연결 로직 및 수동 재시도
Socket.IO는 자동 reconnect 기능을 제공하지만,
간헐적인 실패에 대비하여 수동 재시도 버튼도 함께 제공했다.
{
status === "disconnected" && (
<button onClick={manualReconnect}>다시 연결 시도</button>
);
}
이때 manualReconnect 함수는 내부적으로 소켓과 WebRTC 스트림을 초기화한 뒤, 화면을 다시 렌더링하며 재연결을 시도했다.
시스템 적용 이후의 변화
이 테스트 시스템 도입 후, 다음과 같은 개선이 있었다:
- 연결 실패율이 감소했고, 실패 시 사전 감지가 가능해짐
- 연결 문제에 대한 피드백이 시각적으로 제공되어 UX 혼란이 줄어듦
- 담당자와 사용자 모두가 상태 인지 → 대처 흐름을 이해하기 쉬워짐
마무리하며
WebRTC는 네트워크 품질과 연결 상태에 큰 영향을 받는다.
특히 해외 사용자 대상의 시스템에서는 환경이 예측 불가했기 때문에,
연결 실패 자체보다도 실패했는지 조차 모르는 상황이 더 큰 문제였다.
Socket.IO와 간단한 연결 UI를 기반으로 한 이 테스트 시스템은
WebRTC 인프라를 보완하지 않고도 문제를 조기에 감지하고 대응할 수 있는 프론트엔드 측 해결책이었다.