웹소켓 연결 끊김 감지와 재연결: 실시간 시세 화면에서 틱 누락 없이 복구하기

해외선물 HTS 웹 화면을 만들면서 가장 오래 붙잡고 있던 문제는 웹소켓이 에러 없이 조용히 끊기는 경우였습니다. 화면은 멀쩡해 보이는데 시세가 30초째 멈춰 있고, 사용자는 멈춘 가격을 보고 주문을 넣습니다. 실시간 시세 화면에서 연결 끊김을 감지하고, 재연결하고, 끊긴 동안 놓친 데이터를 복구한 방법을 정리합니다.

왜 onclose가 안 오는가

웹소켓은 TCP 위에서 돕니다. 서버가 정상적으로 연결을 닫으면 onclose가 오지만, 다음 경우에는 브라우저가 한참 동안 끊긴 줄 모릅니다.

  • 노트북 절전 후 복귀, 와이파이에서 LTE로 전환
  • 중간 프록시나 로드밸런서가 유휴 연결을 조용히 정리
  • 모바일 브라우저가 백그라운드 탭의 연결을 멈춤

이때 readyState는 여전히 OPEN입니다. 그래서 이벤트를 기다리지 말고 직접 확인해야 합니다.

1. 끊김 감지: 마지막 수신 시각 감시

가장 단순하고 확실한 방법은 마지막으로 메시지를 받은 시각을 기록해 두고, 일정 시간 아무것도 안 오면 끊긴 것으로 보는 것입니다. 시세가 뜸한 종목도 있으니 서버가 몇 초마다 가벼운 하트비트를 보내게 합니다.

let lastMsgAt = Date.now();
ws.onmessage = (e) => {
  lastMsgAt = Date.now();
  handle(JSON.parse(e.data));
};

// 서버 하트비트 5초 간격 기준, 15초 무소식이면 끊긴 것으로 판단
setInterval(() => {
  if (Date.now() - lastMsgAt > 15000) ws.close(4000, 'stale');
}, 3000);

close()를 직접 호출해 onclose 경로로 합류시키면 재연결 로직을 한 곳에서만 관리할 수 있습니다.

브라우저 이벤트도 같이 씁니다. online 이벤트가 오거나 탭이 다시 보이면(visibilitychange) 즉시 상태를 확인합니다. 절전 복귀 직후가 가장 흔한 끊김 시점입니다.

2. 재연결: 지수 백오프 + 지터

끊기자마자 무한 재시도하면 서버가 재시작될 때 모든 클라이언트가 동시에 몰려 서버를 다시 쓰러뜨립니다. 대기 시간을 두 배씩 늘리고, 무작위 값을 섞어 접속 시점을 흩어 놓습니다.

let retry = 0;
function connect() {
  ws = new WebSocket(URL);
  ws.onopen = () => { retry = 0; resubscribe(); resync(); };
  ws.onclose = () => {
    const base = Math.min(30000, 1000 * 2 ** retry++);
    setTimeout(connect, base / 2 + Math.random() * base / 2);
  };
}
  • 상한(여기서는 30초)을 둡니다. 없으면 몇 분씩 기다리게 됩니다.
  • 연결에 성공하면 retry를 0으로 되돌립니다.
  • 사용자가 탭을 다시 보거나 네트워크가 돌아오면 대기 중인 타이머를 취소하고 바로 연결합니다.

3. 구독 복원

웹소켓은 연결이 새로 생기면 서버 쪽 구독 상태도 새로 시작합니다. 재연결 후 보고 있던 종목, 호가창, 체결 목록 구독을 다시 보내야 합니다. 클라이언트가 현재 구독 목록을 항상 들고 있게 하고, onopen에서 그 목록을 통째로 다시 보내는 방식이 가장 단순합니다.

4. 끊긴 동안 놓친 데이터 채우기

재연결만 하면 끊긴 사이의 체결과 가격 변화가 빠집니다. 차트에 구멍이 나고 호가창이 틀린 상태로 남습니다.

  • 호가창, 현재가: 증분 업데이트를 이어 붙이지 말고, 재연결 직후 REST로 현재 스냅샷을 새로 받아 덮어씁니다. 이후 들어오는 증분만 적용합니다.
  • 차트 캔들: 마지막으로 받은 캔들 시각 이후 구간을 REST로 받아 채웁니다.
  • 순서 보장: 메시지에 서버 시퀀스 번호를 넣어 두면, 번호가 건너뛰는 순간을 감지해 재연결 없이도 스냅샷 재동기화를 할 수 있습니다.

스냅샷을 받는 동안 들어오는 증분은 버퍼에 쌓아 두었다가, 스냅샷 시퀀스보다 뒤의 것만 적용해야 순서가 꼬이지 않습니다.

5. 사용자에게 상태 보여주기

실시간 금융 화면에서는 이게 제일 중요합니다. 끊겨 있는 동안에는 멈춘 가격이 실시간인 것처럼 보이면 안 됩니다.

  • 연결 상태 점(초록, 노랑, 빨강)을 화면 구석에 둡니다.
  • 15초 이상 끊기면 가격을 흐리게 처리하고 "재연결 중" 문구를 띄웁니다.
  • 끊긴 상태에서는 주문 버튼을 잠그거나, 최소한 확인 창을 한 번 더 띄웁니다.

정리

  • onclose만 믿지 말고 마지막 수신 시각과 하트비트로 끊김을 직접 감지합니다.
  • 재연결은 지수 백오프에 무작위 지터를 섞고 상한을 둡니다.
  • 재연결 후에는 구독을 복원하고, 호가와 현재가는 스냅샷으로 덮어써서 놓친 데이터를 채웁니다.
  • 끊긴 상태를 화면에 분명히 표시하고 주문을 막습니다.
profile
거래소 및 AI 자동화 시스템 개발 노트

0개의 댓글