웹소켓 기반 애플리케이션을 운영하다 보면 가장 많이 마주치는 이슈가 바로
👉 "연결이 끊겼는데도 UI는 멀쩡해 보이는 문제"입니다.
특히 절전모드(슬립)나 노트북 덮기, 탭 전환 후 장시간 방치 같은 상황에서는
브라우저가 웹소켓을 끊었음에도 readyState === OPEN
으로 남아있는 경우도 있습니다.
이 글에서는 다음 내용을 다룹니다:
WebSocket은 브라우저/OS/네트워크 상태에 따라 "조용히 끊길 수 있습니다".
📌 문제는 webSocket.readyState
가 항상 믿을 수 있는 값은 아니라는 것입니다.
가장 많이 쓰는 방식 중 하나는 바로 하트비트(heartbeat).
setInterval(() => {
const now = Date.now();
const drift = now - lastTick;
if (drift > 10000) {
console.warn('절전모드 감지, 웹소켓 재연결 시도');
reconnect();
}
lastTick = now;
}, 5000);
performance.timing.navigationStart
또는 Date.now()
를 주기적으로 측정WebSocket.readyState !== OPEN
이거나 이상하면 재연결하트비트는 "앱 레벨에서 주기적으로 연결 상태를 감지하는 로직"입니다.
setInterval
로 5초마다 체크ping
요청을 날려 응답 확인WebSocket은 RFC 6455 표준에 따라
프로토콜 레벨에서 ping/pong 프레임을 지원합니다.
항목 | 내용 |
---|---|
Ping 전송 주체 | 서버 또는 클라이언트 |
Pong 응답 | 상대방이 자동 응답하거나 앱에서 응답 |
장점 | TCP 단에서 직접 응답 → 실제 연결 생존 여부 확인 가능 |
단점 | 브라우저에서 WebSocket.ping() 직접 호출은 불가 (Node.js는 가능) |
⚠️ 일반 브라우저 JS에서는 ping 프레임을 직접 보낼 수 없습니다.
→ 서버가 ping, 클라이언트가 pong 응답
웹소켓을 감시할 때, 많은 분들이 처음엔 이렇게 처리합니다:
const ws = new WebSocket('wss://...');
ws.onclose = () => {
console.warn('웹소켓 닫힘 → 재연결 시도');
reconnect();
};
하지만 실무에서는 매우 불안정합니다:
ws.close()
를 호출했을 때나 서버가 명시적으로 연결을 닫을 때 발생합니다.감지 방법 | 역할 | 왜 필요한가 |
---|---|---|
onclose | 명시적 연결 종료 감지 | 유저가 logout하거나 서버가 정상적으로 연결을 종료했을 때 |
heartbeat | 절전모드 / 지연 / 렌더러 이슈 감지 | 타이머 기반 감지, OS의 방해를 받지 않음 |
ping/pong | 실제 TCP 연결 생존 확인 | 서버 ↔ 클라이언트 네트워크 상태 감시 |
onclose
는 일부 상황에서만 유효onclose
는 정상 종료에만 잘 작동합니다실무에서는 "둘 다" 병행합니다.
목적 | 방식 | 비고 |
---|---|---|
실제 네트워크 생존 여부 확인 | ping/pong | 서버 중심 |
앱 자체의 이상 상황 감지 | heartbeat | 프론트 중심 |
브라우저 환경 대응 | heartbeat | 절전모드, 탭 전환 감지 |
방화벽/프록시 대응 | ping/pong | keep-alive 용도 |
💡 실무에서는 서버가 ping
, 클라이언트가 pong
응답하고
프론트는 30초마다 setInterval
로 readyState
를 감시하는 구조가 일반적입니다.
Electron 앱의 렌더러 프로세스에서는 다음처럼 heartbeat를 구현할 수 있습니다.
// heartbeat.ts
let lastCheck = Date.now();
setInterval(() => {
const now = Date.now();
const delay = now - lastCheck;
if (delay > 10000) {
console.warn('[하트비트] 절전모드 감지 → 재연결 시도');
webSocketManager.manualReconnect(); // 커스텀 재연결 로직
}
lastCheck = now;
}, 5000);
또는 WebSocket wrapper 클래스 내에서 다음과 같이 상태를 주기적으로 체크할 수 있습니다:
const ws = new WebSocket('wss://...');
setInterval(() => {
if (ws.readyState !== WebSocket.OPEN) {
console.warn('연결 끊김 → reconnect');
reconnect();
}
}, 10000);
WebSocket.readyState
는 믿되 너무 믿지 말자readyState
별 의미 요약 표
| 상태 코드 | 이름 | 의미 |
|-----------|-------------|----------------------------|
| 0 | CONNECTING | 연결 중 |
| 1 | OPEN | 연결 완료, 송수신 가능 |
| 2 | CLOSING | 연결 종료 중 |
| 3 | CLOSED | 연결 종료됨 |