본문 바로가기
C.W.K.
Stream
Lesson 07 of 08 · published

재연결 전략

~14 min · browser, reconnect, backoff

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

WebSocket 은 자동으로 재연결하지 않아

EventSource 와 달리 브라우저 기본 WebSocket 은 연결이 끊겨도 스스로 다시 연결하지 않아. 애플리케이션마다 정책이 달라서 의도적으로 빠진 기능이야. 채팅은 적극적으로 재연결해야 하지만 한 번만 받는 내보내기 스트림은 그럴 필요가 없지. 프로토콜이 이 선택에 끼어들지 않는 거야.

지터를 더한 지수 백오프

널리 쓰는 방식은 지터를 더한 지수 백오프야. 실패 뒤 min(base * 2^retry, cap) 만큼 기다리고 여기에 ±25% 의 무작위 지터 를 더해. 클라이언트 10,000개가 동시에 재연결할 때 회복 중인 서버로 같은 순간에 몰려드는 ‘thundering herd’를 막아 주지. open 마다 재시도 횟수를 초기화해야 해.

1000 종료는 건너뛰어

코드 1000 으로 정상 종료했다면 애플리케이션이 요청한 일이므로 재연결하지 마. 1001, 1006, 1011 과 대부분의 4xxx 코드에는 재연결해도 돼. 다만 인증이 만료됐다는 4001 에는 무작정 재연결하지 말고 다시 인증하라는 안내를 띄워.

Code

작지만 올바른 ReconnectingWebSocket·javascript
class ReconnectingWebSocket extends EventTarget {
  constructor(url, { maxRetries = Infinity, baseDelay = 1_000, capDelay = 30_000 } = {}) {
    super();
    this.url = url;
    this.maxRetries = maxRetries;
    this.baseDelay = baseDelay;
    this.capDelay = capDelay;
    this.retry = 0;
    this.shouldReconnect = true;
    this._connect();
  }

  _connect() {
    this.ws = new WebSocket(this.url);

    this.ws.addEventListener('open', () => {
      this.retry = 0;
      this.dispatchEvent(new Event('connected'));
    });

    this.ws.addEventListener('message', (e) => {
      this.dispatchEvent(new MessageEvent('message', { data: e.data }));
    });

    this.ws.addEventListener('close', (e) => {
      this.dispatchEvent(new CloseEvent('disconnected', { code: e.code, reason: e.reason }));

      const isCleanShutdown = e.code === 1000 || e.code === 4001;
      if (!this.shouldReconnect || isCleanShutdown) return;
      if (this.retry >= this.maxRetries) return;

      const base = Math.min(this.baseDelay * (2 ** this.retry), this.capDelay);
      const jitter = base * 0.25 * (Math.random() * 2 - 1);
      const delay = Math.max(0, base + jitter);

      this.retry += 1;
      setTimeout(() => this._connect(), delay);
    });
  }

  send(data) {
    if (this.ws?.readyState === WebSocket.OPEN) this.ws.send(data);
  }

  close(code = 1000, reason = '') {
    this.shouldReconnect = false;
    this.ws?.close(code, reason);
  }
}
지터가 몰림 현상을 막는 이유·text
  Server crashes at t=0.
  10,000 clients all set their next reconnect for t = 1s, 2s, 4s, 8s, ...
  Without jitter, all 10,000 hit the recovering server at exactly t=1s.
  -> Server immediately overwhelmed and crashes again.

  With ±25% jitter:
  Reconnects spread across the 0.75-1.25s window, then the 1.5-2.5s window.
  Server sees a smooth ramp instead of a wall of traffic.

External links

Exercise

위 ReconnectingWebSocket 클래스를 만들고 서버가 5초마다 연결을 끊게 해. 개발자 도구에서 재시도가 지터와 함께 약 1, 2, 4, 8, 16초 간격으로 일어나는지, 재연결에 성공하면 횟수가 초기화되는지 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.