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

long polling: 응답을 늦추는 묘수

~12 min · foundations, long-polling

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

바로 답하지 않고 기다려

long polling 은 요청 모양은 그대로 두고 응답 시점만 바꿔. 서버는 ‘새 소식 없어’라고 곧바로 답하지 않아. 알려줄 일이 생기거나 정해둔 시간 제한이 끝날 때까지 연결을 붙잡고 있지. 응답을 받은 클라이언트가 곧바로 다음 요청을 열면, 겉으로는 서버가 밀어준 것처럼 보여.

2010년대를 버틴 이유

기업 방화벽과 프록시가 HTTP 말고는 모조리 막던 시절에도 long polling 은 통과했어. Gmail 과 Facebook chat 도 한동안 이 방식을 썼지. WebSocket 이 막히면 Socket.IO 가 내려가는 안전망도 여전히 long polling 이야.

그래도 묘수일 뿐이야

응답할 때마다 연결이 닫히므로 다시 TCP 핸드셰이크를 치러야 해. 프록시가 1~2분 동안 조용한 요청을 끊는다면 서버 시간 제한을 그보다 짧게 잡아야 하고, 기다리는 요청마다 서버 자원도 차지해. 서버가 알려주는 방향은 잘 흉내 내지만 클라이언트가 보낼 때는 별도 요청이 필요하니 양방향도 아니야.

Code

FastAPI long-poll 엔드포인트·python
from fastapi import FastAPI
import asyncio

app = FastAPI()
queue: asyncio.Queue = asyncio.Queue()

@app.get('/api/messages')
async def long_poll():
    try:
        # Wait up to 25s for something — under most proxy idle limits.
        msg = await asyncio.wait_for(queue.get(), timeout=25.0)
        return {'messages': [msg]}
    except asyncio.TimeoutError:
        # Empty response triggers the client to reconnect immediately.
        return {'messages': []}
브라우저 long-poll 반복문·javascript
async function longPoll() {
  while (true) {
    try {
      const res = await fetch('/api/messages');
      const { messages } = await res.json();
      if (messages.length) renderMessages(messages);
    } catch (err) {
      console.warn('long poll failed, backing off', err);
      await new Promise(r => setTimeout(r, 2_000));
    }
  }
}

longPoll();

External links

Exercise

위 FastAPI long-poll 엔드포인트를 uvicorn main:app 으로 띄워. 한 터미널에서는 /admin/push 같은 엔드포인트를 만들어 대기열에 메시지를 넣고, 다른 터미널에서는 GET /api/messages 를 열어둬. 메시지를 넣을 때 응답이 도착하고, 넣지 않으면 25초 뒤 빈 응답이 오는지 확인해.

Progress

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

댓글 0

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

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