롱 폴링
실시간 통신2 / 4
- 1.주기 폴링
- 2.롱 폴링
- 3.Server Sent Event
- 4.WebSocket 핸드셰이크와 프레임
폴링부터 WebRTC까지 실시간 통신을 하나씩 정리하는 두 번째 게시글입니다. 1편에서는 주기 폴링을 다뤘습니다. 이 글은 학습하며 정리한 내용입니다. 롱 폴링을 직접 서비스에 적용해본 것은 아니며, 자료를 찾아보며 동작 방식과 장단점을 정리했습니다.
1편에서는 주기 폴링을 살펴봤습니다. 5초마다 요청하면 최대 5초의 지연이 생기고, 이 지연을 줄이기 위해 주기를 1초로 줄이면 요청을 수가 5배 증가합니다.
즉, 지연 시간과 요청 수는 반비례합니다.
그렇다면 완전 새로운 요청을 계속 보내는 대신, 서버 상태가 변할 때 까지 요청에 대한 응답을 보류하는 건 어떨까? 롱 폴링(Long Polling)은 이 아이디어에서 시작됐습니다.
하지만 롱 폴링은 요청을 줄이는 대신 서버에 요청을 붙잡고 있어야 하니 새로운 문제가 발생했습니다.
응답을 기다리는 서버
주기 폴링은 요청을 보내면 서버가 바로 응답합니다. 새로운 데이터가 없다면 그냥 없다고 응답하고, 클라이언트는 정해진 시간이 지난 뒤 다시 요청합니다.
롱 폴링은 이 부분에서 차이가 있습니다. 새로운 데이터가 없다면 서버는 바로 응답하지 않고 일정 시간동안 요청을 유지합니다. 이후 새로운 데이터가 생기면 그 때 클라이언트로 응답을 보냅니다.
async function subscribe(lastId: string) {
const response = await fetch(`/api/events?after=${lastId}`);
const events = await response.json();
if (events.length > 0) {
apply(events);
lastId = events[events.length - 1].id; // 마지막으로 처리한 데이터의 위치
}
subscribe(lastId);
}주기 폴링과 비교하면 클라이언트 코드도 크게 복잡하지 않습니다.
setInterval로 요청 시점을 관리하는 대신, 응답을 받은 뒤 바로 다음 요청을 보내는 구조입니다.
중요한 지점은 연결이 끊겼다가 다시 연결 될 수 있는 실시간 통신에서는 연결이 끊긴 동안 발생한 데이터를 복구하는 방법입니다.
롱 폴링은 보통 마지막으로 처리한 데이터의 위치를 클라이언트와 서버가 함께 관리합니다. 위 코드에서는 after=${lastId}가 그 역할을 합니다.
롱 폴링의 요청 흐름은 다음과 같습니다.