이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • LB idle timeout이 WebSocket, SSE, long polling, gRPC 연결을 어떻게 끊는가?
  • heartbeat, keepalive, reconnect backoff를 timeout 체인과 어떻게 맞춰야 하는가?
  • 장기 연결 장애를 사용자 네트워크 문제와 인프라 설정 문제로 어떻게 분리하는가?

개요

Idle timeout은 일정 시간 동안 데이터가 흐르지 않는 연결을 닫는 설정이다. REST API에서는 잘 드러나지 않지만 WebSocket, SSE, long polling, gRPC streaming에서는 사용자 경험을 직접 결정한다. 가장 짧은 timeout이 실제 연결 수명을 결정한다.

  • LB, reverse proxy, app server, client SDK가 각각 timeout을 가진다.
  • heartbeat가 idle timeout보다 길면 정상 연결도 끊긴다.
  • mobile network 전환과 NAT idle timeout은 장기 연결을 더 불안정하게 만든다.
  • 재연결 전략이 없으면 짧은 네트워크 흔들림이 사용자 기능 중단이 된다.

원리

장기 연결 경로는 다음 timeout chain을 가진다.

Client
  -> Mobile carrier / NAT timeout
  -> CDN / LB idle timeout
  -> Reverse proxy read timeout
  -> App server idle timeout
  -> Application heartbeat

연결이 60초마다 끊긴다면 어떤 계층의 기본 idle timeout이 60초인지 확인한다. 끊긴 간격이 일정하면 인프라 timeout, 불규칙하면 client network나 app backpressure 가능성이 커진다.

연결 유형별 판단

유형주의점
WebSocketupgrade header, idle timeout, ping/pong
SSEresponse buffering off, 주기적 comment heartbeat
Long Pollingrequest timeout과 client retry
gRPC StreamingHTTP/2, keepalive, LB 지원 여부
File Downloadslow client와 send timeout

설정 예시

WebSocket proxy 예시다.

location /ws/ {
    proxy_pass http://app_upstream;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 1h;
}

SSE는 buffering을 끄고 heartbeat를 보낸다.

location /events/ {
    proxy_pass http://app_upstream;
    proxy_buffering off;
    proxy_read_timeout 1h;
}

SSE heartbeat 예시다.

: heartbeat
 
data: {"type":"message","id":"m-1"}
 

코드와 명령으로 확인하기

SSE가 buffering 없이 주기적으로 오는지 확인한다.

curl -N https://api.example.com/events

WebSocket은 전용 client로 연결 시간을 본다.

npx wscat -c wss://api.example.com/ws

운영에서 Node 도구 사용이 어렵다면 브라우저 DevTools Network의 WS/SSE timing과 close code를 저장한다.

인프라 협업 포인트

  • LB idle timeout, proxy read timeout, app heartbeat 주기를 같은 표로 맞춘다.
  • 장기 연결 endpoint는 일반 REST target group과 분리할지 검토한다.
  • connection 수가 많아지면 LB, proxy, app의 file descriptor, memory, thread/event loop 사용량을 함께 본다.
  • 모바일 앱은 foreground/background 전환, 네트워크 전환, reconnect backoff 정책을 앱 팀과 맞춘다.

실전 팁

  • heartbeat 주기는 idle timeout보다 충분히 짧게 둔다. 예를 들어 timeout 60초라면 20~30초 heartbeat를 검토한다.
  • reconnect는 즉시 무한 재시도하지 말고 exponential backoff와 jitter를 둔다.
  • 장기 연결은 배포 draining에 취약하다. 배포 중 연결 종료와 재연결 UX를 설계한다.
  • gRPC는 HTTP/2 end-to-end 지원과 ALPN, proxy idle timeout을 같이 확인한다.
  • 개인 프로젝트에서는 WebSocket보다 SSE가 운영상 단순한 경우가 많다. 양방향이 꼭 필요한지 먼저 묻는다.

위험 신호!

  • SSE endpoint에 proxy buffering이 켜져 있다.
  • WebSocket close code와 끊긴 주기를 수집하지 않는다.
  • LB idle timeout보다 heartbeat 주기가 길다.
  • reconnect storm이 장애 중 origin을 더 압박한다.

확인 질문

확인 질문

  • 일정한 간격으로 장기 연결이 끊기면 무엇을 의심해야 하는가?
    • LB, proxy, app server, NAT 중 하나의 idle timeout 기본값을 의심해야 한다.
  • SSE에서 curl -N이 유용한 이유는 무엇인가?
    • buffering 없이 이벤트가 즉시 흘러오는지 확인할 수 있기 때문이다.
  • reconnect backoff가 필요한 이유는 무엇인가?
    • 장애나 배포 중 많은 client가 동시에 재연결해 서버를 더 압박하는 상황을 줄이기 위해서다.

참고 문서