웹소켓(WebSocket) 재연결 전략과 절전모드 감지: 하트비트 vs 핑퐁

송연지·2025년 6월 26일
0

트러블슈팅

목록 보기
29/32

웹소켓 기반 애플리케이션을 운영하다 보면 가장 많이 마주치는 이슈가 바로

👉 "연결이 끊겼는데도 UI는 멀쩡해 보이는 문제"입니다.

특히 절전모드(슬립)노트북 덮기, 탭 전환 후 장시간 방치 같은 상황에서는

브라우저가 웹소켓을 끊었음에도 readyState === OPEN 으로 남아있는 경우도 있습니다.

이 글에서는 다음 내용을 다룹니다:


1. ❌ 왜 웹소켓이 끊겼는지 모르고 방치될까?

WebSocket은 브라우저/OS/네트워크 상태에 따라 "조용히 끊길 수 있습니다".

  • 절전모드 → 브라우저 프로세스 일시 정지됨
  • 네트워크 재연결 → underlying TCP 소켓 끊김
  • VPN 변경/라우팅 오류 → 서버 도달 실패
  • 방화벽/프록시 → 일정시간 이후 소켓 정리

📌 문제는 webSocket.readyState항상 믿을 수 있는 값은 아니라는 것입니다.


2. 🌙 절전모드 감지: 하트비트 방식

가장 많이 쓰는 방식 중 하나는 바로 하트비트(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이거나 이상하면 재연결

하트비트(heartbeat)란?

하트비트는 "앱 레벨에서 주기적으로 연결 상태를 감지하는 로직"입니다.

  • setInterval로 5초마다 체크
  • 연결 상태를 확인하거나, ping 요청을 날려 응답 확인
  • 절전모드 감지, 이벤트 루프 delay 감지, 앱의 주기적 상태확인 등에 쓰임

3. 🛰️ WebSocket Ping/Pong 프레임

WebSocket은 RFC 6455 표준에 따라

프로토콜 레벨에서 ping/pong 프레임을 지원합니다.

Ping/Pong 특징

항목내용
Ping 전송 주체서버 또는 클라이언트
Pong 응답상대방이 자동 응답하거나 앱에서 응답
장점TCP 단에서 직접 응답 → 실제 연결 생존 여부 확인 가능
단점브라우저에서 WebSocket.ping() 직접 호출은 불가 (Node.js는 가능)

⚠️ 일반 브라우저 JS에서는 ping 프레임을 직접 보낼 수 없습니다.
→ 서버가 ping, 클라이언트가 pong 응답


4. 🏭 실무에서는 어떻게?

- onclose만 쓰면 안 되는 이유

웹소켓을 감시할 때, 많은 분들이 처음엔 이렇게 처리합니다:

const ws = new WebSocket('wss://...');
ws.onclose = () => {
  console.warn('웹소켓 닫힘 → 재연결 시도');
  reconnect();
};

하지만 실무에서는 매우 불안정합니다:

- 문제점들

  • 절전모드에서 onclose 발동되지 않음
  • 라우팅 장애, VPN 전환 등에서도 readyState는 OPEN 상태 유지
  • 서버가 죽어도 클라이언트는 알 수 없음

- 참고: onclose란?

  • WebSocket 연결이 정상적으로 종료되었을 때 실행되는 콜백 함수입니다.
  • 예를 들어 ws.close()를 호출했을 때나 서버가 명시적으로 연결을 닫을 때 발생합니다.
  • 하지만 네트워크 끊김, 절전모드 등은 비정상 종료이기 때문에 onclose가 호출되지 않을 수 있습니다.

✅ 그래서 실무에서는 이렇게 대응합니다

감지 방법역할왜 필요한가
onclose명시적 연결 종료 감지유저가 logout하거나 서버가 정상적으로 연결을 종료했을 때
heartbeat절전모드 / 지연 / 렌더러 이슈 감지타이머 기반 감지, OS의 방해를 받지 않음
ping/pong실제 TCP 연결 생존 확인서버 ↔ 클라이언트 네트워크 상태 감시

💡 결론: onclose는 일부 상황에서만 유효

  • onclose정상 종료에만 잘 작동합니다
  • 절전모드, 네트워크 오류, VPN, 방화벽 등 실무에서 자주 발생하는 이슈에는 아예 작동 안 하는 경우도 많습니다
  • 그래서 반드시 heartbeat 또는 ping/pong을 보완적으로 사용해야 합니다

실무에서는 "둘 다" 병행합니다.

목적방식비고
실제 네트워크 생존 여부 확인ping/pong서버 중심
앱 자체의 이상 상황 감지heartbeat프론트 중심
브라우저 환경 대응heartbeat절전모드, 탭 전환 감지
방화벽/프록시 대응ping/pongkeep-alive 용도

💡 실무에서는 서버가 ping, 클라이언트가 pong 응답하고

프론트는 30초마다 setIntervalreadyState를 감시하는 구조가 일반적입니다.


5. Electron + React + Vite 환경에서의 적용 예시

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      | 연결 종료됨                |
  • 절전모드, 탭 전환 등은 하트비트로 감지
  • 서버는 ping/pong 설정을 적극 활용하자
  • Electron 환경에서도 렌더러에 heartbeat는 유용하게 작동

📚 참고자료

  • RFC 6455 - The WebSocket Protocol
  • MDN - WebSocket
  • Socket.IO Heartbeat 참고
profile
프론트엔드 개발쟈!!

0개의 댓글