Long Polling vs WebSockets vs Server-Sent Events: AI 에이전트 루프를 위한 재검토
요약
AI 에이전트 환경에서는 기존 브라우저 중심의 실시간 통신 방식(WebSockets, SSE)이 한계를 보입니다. 에이전트의 빈번한 재시작, 네트워크 제약, JSON 중심의 데이터 구조를 고려할 때 롱 폴링(Long Polling)의 유효성을 재검토해야 합니다.
핵심 포인트
- AI 에이전트는 브라우저와 달리 빈번한 재시작과 상태 유실이 발생함
- NAT나 방화벽 뒤의 에이전트는 서버의 푸시를 받을 수 없는 아웃바운드 전용 환경임
- 에이전트 루프에는 구조화된 JSON 처리가 필수적임
- 연결 복구 및 네트워크 비대칭성 측면에서 롱 폴링이 더 안정적일 수 있음
당신이 읽어본 모든 실시간 웹 비교 글들은 동일한 세 가지 옵션인 long polling, WebSockets, 그리고 Server-Sent Events (SSE)를 나란히 놓고 비교합니다. 지연 시간(latency), 오버헤드(overhead), 브라우저 지원, 단방향(unidirectional) 대 양방향(bidirectional) 등의 트레이드오프(trade-offs)는 이미 확립되어 있습니다. 하지만 그러한 기사들은 브라우저 탭에 사람이 있다는 것을 전제로 합니다. 클라이언트가 AI 에이전트 (AI agent) — 즉, 충돌하거나, 재시작되거나, NAT 뒤에서 실행되며 자체적인 일정에 따라 이벤트를 처리하는 무언가 — 일 때, 이 세 가지 옵션은 다르게 보입니다. 고전적인 세 가지 방식의 비교는 대부분의 문서가 제시하는 방식으로는 유효하지 않습니다.
에이전트 빌더들에게 중요한 사례, 즉 클라이언트가 계속 사라지고 당신이 해당 클라이언트가 실행되는 네트워크를 제어할 수 없는 상황에 맞춰 이를 재검토해 보겠습니다.
고전적인 비교 (틀린 것은 아니지만, 불완전한 이유)
long polling vs WebSockets vs Server-Sent Events에 대한 비교를 한 번이라도 보았다면, 그 형태를 알고 있을 것입니다:
| 메커니즘 (Mechanism) | 방향 (Direction) | 지연 시간 (Latency) | 재연결 부담 (Reconnect burden) |
|---|---|---|---|
| Long polling | 클라이언트 풀 (Client pull) | 높음 (최소 RTT) | 에러 발생 시 클라이언트가 재폴링 (re-polls) |
| ... |
이러한 트레이드오프는 안정적인 TCP 연결을 유지하고, 메모리에 세션 상태를 유지하며, 공인 IP를 가졌거나 최소한 아웃바운드 전용 연결성을 가진 웹 브라우저를 위해 도출되었습니다. AI 에이전트 루프는 이러한 모든 가정을 위반합니다.
클라이언트가 에이전트 루프일 때 변하는 것들
자율 에이전트(autonomous agent)는 브라우저에 앉아 있지 않습니다. 에이전트는 재활용되는 컨테이너, 콜드 스타트(cold-start) 지연 시간 문제가 있는 서버리스 함수(serverless function), 또는 기업 방화벽 뒤의 머신에서 실행됩니다. 세 가지 요소가 기존의 가정을 깨뜨립니다:
1. 클라이언트가 자주 재시작됩니다. 모든 충돌, 배포, 또는 클라우드 마이그레이션은 메모리 내의 연결 상태를 파괴합니다. 이전 PID가 사라지면 "우아하게 재연결(reconnect gracefully)"하는 것은 불가능합니다. 새로운 인스턴스는 모든 것을 처음부터 다시 구축해야 합니다. Long polling은 이를 자연스럽게 처리합니다 (어차피 모든 폴링은 새로운 연결이기 때문입니다). 하지만 WebSocket과 SSE의 복구(recovery)를 위해서는 에이전트가 재개 상태(resume state)를 어딘가 영구적인 곳에 보관해야 하는데, 대부분의 에이전트 루프는 그렇게 하지 않습니다.
2. 수신 가능한 공용 주소가 없음. WebSocket과 SSE는 모두 서버가 도달 가능한 엔드포인트(endpoint)로 데이터를 푸시(push)해야 합니다. 만약 에이전트가 NAT, CGNAT, 또는 개방된 포트가 없는 기업 방화벽 뒤에 있다면, 서버는 연결을 시작할 수 없습니다. 에이전트는 오직 '외부로(out)'만 연결할 수 있습니다. 이것이 근본적인 비대칭성입니다. 에이전트의 환경이 양방향 푸시를 불가능하게 만들 수 있으며, 고전적인 세 가지 방식 중 아웃바운드 전용(outbound-only) 위치에서 실제로 작동하는 유일한 옵션은 롱 폴링 (Long Polling)입니다.
3. 페이로드(Payload)는 DOM 이벤트가 아닌 JSON임. SSE는 브라우저가 점진적으로 렌더링하는 텍스트 스트림(text streams)을 위해 설계되었습니다. 에이전트는 구조화된 JSON을 처리합니다. 에이전트가 동작하기 전에 전체 페이로드를 역직렬화(deserialize)해야 한다면, 점진적 스트림의 이점은 낭비됩니다. 또한, 에이전트가 통상적으로 처리하는 메시지 크기(수 KB)에 대해서는 WebSocket의 프레이밍(framing)이 HTTP 응답 경계(response boundaries)보다 유의미하게 더 낫지도 않습니다.
롱 폴링이 에이전트에게 생각보다 실제로 더 안 좋은 이유
롱 폴링은 어떤 네트워크에서도 작동하기 때문에 유혹적입니다. 하지만 에이전트 루프(agent loop)에서는 모든 빈 폴링(empty poll)이 연산 비용(compute)을 발생시킵니다. 에이전트는 깨어나서 HTTP 요청을 보내고, 데이터가 없는 200 응답을 받은 뒤, 다음에 무엇을 할지 결정해야 합니다. 각 결정 주기마다 아무런 작업도 하지 않는 상태(no-op)에서 토큰, 지연 시간(latency), 그리고 컨텍스트 윈도우(context-window) 예산이 소모됩니다. 실시간 서버 푸시 옵션의 지연 시간이 5~50ms에 달하는 상황에서, 5초 간격의 롱 폴링은 모든 이벤트에 대해 강제적인 5초의 지연을 도입하며, 간격이 짧아질수록 낭비되는 폴링 비용은 복리로 증가합니다.
더 나쁜 점은, 에이전트가 타임아웃(timeout) 없이는 "아직 이벤트가 없음"과 "서버가 사라짐"을 구분할 수 없다는 것입니다. 이는 단순히 "다음 이벤트를 기다림"이어야 할 경로에 에러 핸들링(error-handling) 분기를 추가하게 만듭니다.
에이전트를 위한 WebSocket: 재연결 비용
WebSocket은 저지연 푸시를 제공하지만, 에이전트가 재연결(reconnection)을 직접 관리해야 합니다. 즉, 다음을 의미합니다:
- 어딘가에 영구적인 상태 (Persistent state) (파일, DB 행, 또는 에이전트의 사고 내 플래그)를 유지하여 WebSocket이 _기대되었음_을 알아야 합니다.
- 재연결 시 재인증 (Re-auth on reconnect). 모든 새로운 WebSocket 업그레이드는 새로운 인증 핸드셰이크 (authentication handshake)를 수반합니다. 만약 인증 서버가 다운되어 있다면, 재연결을 시도하는 에이전트는 멈추게 됩니다.
- 서버는 에이전트의 위치를 알아야 합니다. WebSocket 서버는 클라이언트별 연결 상태 (per-client connection state)를 유지합니다. 에이전트가 새로운 IP나 새로운 NAT 매핑 (NAT mapping) 뒤에서 재시작되면, 서버는 여전히 오래된 연결을 유효하지 않은 상태 (stale)로 보유하게 되며, 기존 매핑은 끊어집니다. 에이전트가 서버에 도달하기 전까지 서버는 푸시 (push)를 할 수 없습니다.
결과적으로: WebSocket은 재시작하지 않고 안정적인 네트워크에서 작동하는 에이전트에게는 매우 훌륭합니다. 하지만 그 외의 경우에는 지연 시간 (latency)의 이점보다 유지 관리 부담이 더 큽니다.
에이전트를 위한 SSE: 방향성 문제
Last-Event-ID를 통한 SSE의 자동 재연결 (auto-reconnect) 기능은 브라우저가 대신 처리해주므로 정말 편리합니다. 하지만 SSE는 서버에서 클라이언트로만 (server-to-client only) 전송됩니다. 만약 에이전트가 데이터를 다시 보내야 한다면 (작업 확인, 상태 업데이트 등), 두 번째 채널 — 즉, 다른 HTTP 엔드포인트 (endpoint)나 별도의 POST 요청이 필요합니다. 두 개의 채널은 관리해야 할 두 개의 스택, 두 개의 타임아웃 (timeout), 두 세트의 에러 상태를 의미합니다.
또한 SSE는 WebSocket의 도달 가능성 (reachability) 문제도 그대로 물려받습니다. 서버가 에이전트의 엔드포인트로 푸시를 하는데, 만약 그 엔드포인트에 도달할 수 없다면 SSE는 도움이 되지 않습니다.
실제로 작동하는 방식: 터널 기반 푸시를 이용한 영구적인 가상 주소
전통적인 세 가지 방식 중 어느 것도 핵심적인 문제를 해결하지 못합니다. 바로 에이전트의 _주소 (address)_가 일시적 (ephemeral)이라는 점입니다. 에이전트가 재시작하거나 이동할 때마다 IP와 NAT 매핑이 변경됩니다. 에이전트가 먼저 연결을 시도하고 그 연결이 유지되지 않는 한, 서버는 에이전트에게 도달할 방법이 없습니다.
문제의 형태를 바꾸는 대안: 머신이 재시작되거나, 컨테이너가 재활용되거나, 클라우드 리전(Cloud Region)이 전환되어도 바뀌지 않는 **영구적인 가상 주소 (Permanent Virtual Address)**를 에이전트에게 부여하는 것입니다. 에이전트는 오버레이 네트워크 (Overlay Network)에 암호화된 UDP 연관 관계 (Association)를 유지하는 작은 유저스페이스 터널 데몬 (Userspace Tunnel Daemon)을 실행합니다. 서버는 다음 NAT 타임아웃 시 만료되는 IP:포트(IP:port)가 아니라, 해당 **주소 (Address)**로 메시지를 푸시합니다.
이 모델 하에서는:
- 에이전트는 여전히 아웃바운드 (Outbound) 연결을 수행하지만 (열린 포트가 필요 없음), 주소가 TCP 소켓 (TCP Socket)이 아닌 키 쌍 (Keypair)에 결합되어 있으므로 재시작 시에도 터널이 유지됩니다.
- 서버는 휘발성 연결 (Ephemeral Connections)을 추적하지 않습니다. 서버는 에이전트의 주소로 메시지를 보내고, 오버레이 네트워크가 이를 전달합니다.
- 에이전트는 WebSocket 상태 머신 (State Machine)을 관리하거나 빈 폴링 (Empty Polls)에 사이클을 낭비하지 않고도 푸시를 수신합니다.
- 재연결은 에이전트가 처리해야 하는 이벤트가 아닙니다. 터널 데몬이 이를 투명하게 처리하며, 반대편의 주소는 동일하게 유지됩니다.
이것은 브라우저 패턴이 아닙니다. 오버레이 네트워크에서 빌려와 에이전트의 생명 주기 (Lifetime)에 맞게 조정된 네트워킹 패턴입니다. 트레이드오프 (Trade-off)는 기존의 세 가지 방식이 요구하지 않는 인프라 — 즉, 터널 데몬과 레지스트리 (Registry) — 가 필요하다는 점입니다. 하지만 에이전트 루프 (Agent Loop)에 이미 신뢰할 수 있는 메시징, 상태 지속성 (State Persistence), 그리고 크로스 클라우드 모빌리티 (Cross-cloud Mobility)가 필요하다면, 이 인프라는 폴링 비용과 연결 끊김 버그를 제거함으로써 그 가치를 충분히 증명할 것입니다.
요약
| 메커니즘 | NAT 뒤에 있는 에이전트에서 작동하는가? | 에이전트 재시작 시 유지되는가? | 토큰 효율적 (낭비 없음)? |
|---|---|---|---|
| Long polling | 예 | 예 (stateless) | 아니요 — 모든 빈 폴링이 연산 자원을 소모함 |
| ... |
Long polling vs WebSockets vs Server-Sent Events의 고전적인 비교는 브라우저의 경우에는 적절합니다. 하지만 에이전트 루프 (agent loops)의 경우, 제약 조건이 충분히 다르기 때문에 기본 설정이 달라집니다. 만약 당신의 에이전트가 항상 도달 가능하고 절대 재시작되지 않는다면, WebSocket을 사용해도 괜찮습니다. 하지만 에이전트가 위 두 가지를 보장할 수 없는 환경에서 실행된다면, 지속적인 가상 주소 (persistent virtual address)를 사용하는 것이 문제를 "어떻게 연결을 유지할 것인가"에서 "어떻게 메시지를 받을 것인가"로 변화시키며, 이는 훨씬 더 단순한 질문이 됩니다.
Pilot Protocol은 각 에이전트에 암호화된 UDP 터널과 아웃바운드 전용 NAT 트래버설 (NAT traversal)을 통해 영구적인 가상 주소를 부여하는 오픈 소스 오버레이 네트워크 (overlay network)입니다. 포트를 개방할 필요가 없습니다. 재시작 및 네트워크 변경 시에도 메시지를 안정적으로 수신해야 하는 에이전트 루프를 구축 중이라면, 문서(docs)를 참조하거나 데몬(daemon)을 설치하세요:
curl -fsSL https://pilotprotocol.network/install.sh | sh
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기