발전하는 춘배

[Backend] 서버와 클라이언트는어떻게 실시간으로 통신할까? - Polling과 WebSocket 본문

backend

[Backend] 서버와 클라이언트는어떻게 실시간으로 통신할까? - Polling과 WebSocket

춘배0 2026. 10. 20. 12:41

서버와 클라이언트의 실시간 통신 방식에 대해 Polling과 WebSocket을 중심으로 알아보자.

(Short) Polling의 한계를 극복하기 위한 Long Polling 방식과 SSE 기술도 간략하게 알아보고,

실시간성이 더욱 중요한 경우 사용하는 WebRTC와, 웹 생태계의 발전에 따라 등장한 기술인 HTTP/3 기반의 WebTransport도 짧게 알아본다.

추가로 WebSocket이 내부적으로 어떻게 작동하는지 매우 간략하게 알아본다.

 

이 글은 GDGoC 파트 세션에서 직접 발제를 준비하며 공부한 내용을 재정리한 내용이며, 예제 코드와 원본 발제 자료는 다음과 같다.

예제 코드

발제 자료


1. HTTP

빠르게 HTTP에 관해 간단히 알아보자.

HTTP는 HyperText Transfer Protocol의 약자다.

 

HTTP를 한 문장으로 요약해 보면 클라이언트- 서버로 구성된 요청-응답 구조라 정리할 수 있을 것 같다.

 

오늘 주제와 관련된 주요 성질 두 가지는 다음과 같다.

  • Stateless(무상태성): “클라이언트의 이전 상태를 기억하지 않는다.” 필요한 상태에 대한 정보를 클라이언트가 가지고 오기 때문에 클라이언트의 요청에 어느 서버가 응답해도 상관이 없다. 따라서 클라이언트의 요청이 대폭 증가하면 서버를 증설해 해결할 수 있다.
  • Connectionless(비연결성): “클라이언트가 요청하고 서버가 이에 대해 응답하면 TCP/IP 연결을 끊어 연결을 유지하지 않는다.” 서버의 자원을 효율적으로 관리하고 수많은 클라이언트의 요청에 대응할 수 있게 된다.

2. 실시간의 구현

카카오톡이나 웹게임처럼 실시간으로 무언가를 처리해야 되는 경우 앞서 언급한 HTTP의 무상태성, 비연결성은 약간 문제가 된다.

 

Player B가 아직 요청하지 않았는데, 서버는 A의 이동을 어떻게 알릴까?

HTTP상에서는 알릴 수 없다.

HTTP는 클라이언트-서버의 요청-응답 구조라 했다.

즉 클라이언트가 요청하지 않으면 서버는 응답할 방법이 없다. 누구한테 무슨 데이터를 줘야 하는지 모르기 때문이다.

 

그럼 이 상황에서 가장 쉽게 실시간을 구현할 방법에 대해 생각해 보면 다음과 같은 해법을 찾을 수 있다.

Player B가 서버에 “A의 위치 변화가 어떻게 되나요?”를 계속해서 요청하면 되겠군


3. HTTP Polling

그렇게 클라이언트가 일정한 주기로 서버에 HTTP 요청을 보내 새로운 데이터가 있는지 확인하는 통신 방식을 HTTP Polling이라 한다.

 

Vanilla JS를 사용하는 클라이언트와 FastAPI 서버로 간단히 구현해 보면 핵심 코드는 이렇다. 예제는 단순히 클라이언트들이 실시간으로 움직임을 공유하는 게임(?) 서버다.

예제

클라이언트:

pollState라는 async 함수를 POLL_INTERVAL_MS의 주기로 반복해서 호출한다.

pollState함수는 서버에게 현재 게임 보드의 상태를 요청해서 랜더링 함수를 호출한다.

 

서버:

서버 측 코드는 정말 별 게 없다.

metrics는 실험용으로 메트릭을 측정하는 거고 그냥 요청에 대해 게임 보드 상태를 그대로 리턴해준다.

데이터 변경 여부는 체크하지 않는다.

위 예제 코드에서는 데이터를 무조건 주게 했지만, 빈 데이터는 204 No Content를 응답하는 경우도 있다.

 

Polling 방식의 장점

1. 구현이 쉽다.

위 코드를 보면 알겠지만 정말 간단하다.

 

2. 브라우저나 인프라에 상관없이 모든 웹 환경에서 완벽하게 호환된다.

왜냐하면 별도의 기술을 도입한 게 아니라, 단순한 HTTP 요청을 '주기적'으로 할 뿐이기 때문이다.

 

3. 장애 복구가 쉽다.

장애 복구라고 할 것도 없다. 한번 뭔가 문제가 생겼다고 해도, 바로 다음 폴링 사이클에 응답이 정상적으로 이루어진다면 바로 복구가 된다.

 

Polling 방식의 한계

한계도 명확하다. 일단 딱 봐도 뭔가 비효율적인 것처럼 느껴진다.

 

1. 불필요한 트래픽

클라이언트는 다른 클라이언트가 '움직였는지 안 움직였는지' 확인이 불가하다.

확인하려면 요청해야 하는데, 따라서 아무도 움직이지 않았어도 서버에 주기적으로 데이터를 요청해야 한다.

즉, 폴링 방식에서는 새(혹은 변경된) 데이터가 없어도 요청이 계속 발생하기 때문에 대역폭이 낭비된다.

심지어 유저가 나 혼자여도 미친듯이 요청을 날린다. (물론 구현의 문제이긴 하다)

2. 서버 부하

위 문제의 연장선에서 클라이언트가 많아질수록 서버가 감당해야 할 요청 수가 선형적으로 증가한다.

동접자가 200명이고 폴링 주기가 100ms면 초당 2000회의 요청이 서버에 쏟아져 들어온다. 물론 이 중 대부분은 의미 없는 요청이다.

 

3. 실시간성의 한계

서버 부하를 줄이려면 폴링 주기를 길게 잡으면 될 것이다. 그런데 이 시점에서 생각해 볼 점이 있다.

애초에 우리는 '실시간' 서비스를 구현하기 위해 폴링을 도입하려고 했는데, 특히 웹게임이라고 생각해 보자.

폴링 주기가 100ms라는 건, 무조건 발생되는 최소한의 핑이 100ms라는 것이다. 롤에서 핑이 30~50만 되어도 체감이 많이 되는데 이게 100ms라는 건 사실 더 이상 '실시간'이라 하기 어렵게 되어 버린다. 실제로 폴링 서버 예제에서 두 개의 클라이언트를 띄워 놓고, 한쪽 클라이언트에서 오른쪽 화살표를 꾹 눌러보면, 다른 클라이언트에서는 정말 뚝뚝 끊기는 것을 볼 수 있다.

심지어 100ms면 굉장히 짧은 polling 주기이다. 앞서 살펴봤듯 서버 부하가 굉장히 심하다.

 

 

그래서 사실 웹 게임이나 실시간 채팅 서비스의 핵심 로직 구현은 폴링으로 하지 않는다.


3-1. Long Polling

우선 앞서 소개한 (Short) Polling의 1번, 2번 한계를 극복하는 데 좋은 대안이 하나 있다.

숏 폴링 방식에서 대부분의 응답은 의미 없는 빈 데이터라고 했다.

그래서 서버 상태(데이터)에 변화가 생기면 그때 서버가 응답을 보내는 Long Polling 방식이 있다.

그러면 데이터가 바뀔 때까지 클라이언트는 요청을 마구잡이로 보내는 게 아니라 기다리기 때문에 불필요한 트래픽을 감소시킬 수 있다는 장점이 있다.

실제로 Long Polling 방식으로 동작하는 꽤 많은 서비스가 있다.

대신 당연히 실시간성이 그다지 중요하지 않은 서비스에서만 사용할 수 있다.


4. WebSocket

Polling 방식의 한계를 극복해 보자.

HTTP는 요청-응답 후에 연결을 끊어버린다고 했다.(비연결성) 이것은 최소한의 자원으로 서버를 유지하는 데 도움이 된다. 그런데 폴링 방식은 이 장점을 덮어버리는 너무 많은 요청-응답이 발생하는 게 문제다.

그렇다면 이 연결을 끊지 않으면 굳이 요청-응답을 반복하지 않아도 되는 게 아닌가?

 

그 기술이 WebSocket이다.

클라이언트와 서버가 한번 연결을 열고 나면, 해당 TCP 커넥션을 유지하면서, 양방향으로 데이터를 주고받는 방식이다.

폴링과 웹소켓의 차이를 GPT에게 이미지로 만들어달라고 했다.

웹소켓은 이미 서버와 클라이언트 사이의 연결이 수립되어 있기 때문에 클라이언트가 요청하지 않아도 서버가 필요할 때 클라이언트에게 데이터를 쏴 줄 수 있다.

 

그렇다고 웹소켓이 HTTP와 전혀 다른 방식이냐 하면 그렇지 않다.

웹소켓은 HTTP Upgrade 요청에 대해 서버가 101 Switching Protocols 응답을 돌려주면서 시작된다.

Upgrade 후에는 TCP 연결을 끊지 않고, 이 연결 위에서 frame을 주고받는다.

 

그래서 WebSocket 방식의 장점은 다음과 같다.

WebSocket 장점

1. 높은 실시간성

얼마나 높은지는 직접 확인할 수 있다.

클라이언트2의 요청: 왼쪽부터 데이터, 길이, 시간이다.
클라이언트1에게 온 데이터

클라이언트 2가 움직임 요청을 보낸 후 36ms 만에 클라이언트 1에게 움직임이 전달되었다.

36ms면 긴 거 아닌가? 싶어서 AI한테 물어봤다.

사실 물리적으로 데이터가 클라1의 집 -> 서버 -> 클라2의 집까지로 이어진다고 생각해 보면 36ms라는 지연 시간은 정말 짧은 편이라는 걸 느낄 수 있다. 그래도 와닿지 않는다면 폴링 방식은 서버에 부담이 매우 심한데도 100ms였단 걸 생각해 볼 필요가 있다.

폴링 서버에서 실험해봤던 것처럼 Websocket 서버에 접속한 두 개의 클라이언트를 띄워 놓고, 한쪽 클라이언트에서 오른쪽 화살표를 꾹 눌러보면, 다른 클라이언트에서는 그래도 훨씬 덜 끊겨 보이는 걸 볼 수 있다. (실제 멀티플레이어 게임이었다면 36ms의 끊김도 체감이 크게 느껴질 수 있다. 그래서 보간법이나 외삽법 같은 여러 기법을 통해 끊기는 느낌이 들지 않도록 하기도 한다.)

 

2.가벼운 연결, 적은 부하

WebSocket 연결이 한번 이루어지면 그 이후엔 무거운 HTTP 헤더 없이 프레임만을 주고받을 수 있으며, 아무도 움직이지 않으면 데이터 교환이 없다는 걸 생각해 보면 된다.

하나의 ws 연결 위에서 춘배2가 move 요청을 보냈을 때만 춘배1에게 메시지가 온다.

Websocket 간단한 원리

문득 이런 생각이 들었다.

웹소켓이라는 게 내부적으로 폴링으로 작동하는 걸 그냥 사용하기 쉽게 추상화시켜놓은 기술이라면 똑같은 게 아닐까?

아니다.

웹소켓은 아예 다른 방식이다. 위에서 언급했듯 Switching Protocols 이후에는 TCP 연결을 끊지 않고 해당 연결에서 frame만을 주고받는다고 했다.

그러면 서버 컴퓨터는 어떻게 클라이언트의 프레임을 '기다리고' 있는 걸까?

서버측 코드다.

while True 안에서 receive_json()을 무한히 호출하는 것처럼 생겼다. 그럼 이걸 서버 CPU가 버틸 수 있나 싶다.

당연히 그렇지 않다. 사실 위 코드는 데이터 receive_json()이라는 함수를 미친 듯이 호출해서 데이터가 있나 없나 그런 걸 체크하는 코드가 아니다.

await에 의해 receive_json()이 무언가를 return하기 전까지는 이 라인에서 함수 진행이 block된다.

클라이언트로부터 보내진 데이터가 NIC를 통해 도착하면, OS는 해당 데이터를 소켓 버퍼에 넘긴다. 그러면 이벤트 루프가 이 데이터가 읽기 가능 상태임을 감지하고, 그러면 중단되어 있던 함수가 다시 실행되어 데이터를 읽고 receive_json()이 리턴된다.

즉 클라이언트로부터 네트워크 데이터가 오기 전까지 CPU는 이 함수 진행을 더 이상 하지 않기 때문에 다른 일을 할 수 있다.


5. SSE; Server-Sent Events

Polling과 Websocket 말고 다른 방식도 있는데 SSE 방식이다.

쉽게 SSE는 서버에서 클라이언트로 단방향으로 푸시해 주는 방식이다.

즉, 실시간성이 중요하다고 판단될 때, 클라이언트도 서버로 뭔가 보내고 서버도 클라이언트로 뭔가 보내는 양방향 전송이 필요하면 웹소켓, 클라이언트는 서버로 뭔가 보낼 필요가 없고 서버만 클라이언트로 단방향으로 푸시해주기만 하면 되는 경우엔 SSE를 사용하면 된다.


6. 다른 기술들 (WebRTC, WebTransport)

사실 정말 실시간성이 강조되는 서비스들은 웹소켓으로 구현하지 않는다.

예를 들어 디스코드나 구글 미트의 화면 공유는 어떻게 구현된 것일까?

웹소켓은 어쨌든 TCP 기반 통신 방식이다. TCP의 특성에 의해 데이터의 신뢰성과 순서가 보장되는 대신 UDP 방식보다는 느릴 수밖에 없다.

실시간 음성채팅, 화면공유 같은 경우는 오래된 데이터를 정확히 전송하는 것보다, 최신의 데이터를 제때 전송하는 것이 중요한 서비스이므로 UDP를 선택한다.

이러한 UDP 기반 통신을 이용하는 기술로는 WebRTC나 WebTransport가 있다.

WebRTC는 실시간 음성·영상 통신을 위한 기술 모음으로, UDP 기반 미디어 전송을 비롯한 여러 기능을 제공한다.

WebTransport는 최신의 HTTP/3에서 TCP 대신 채택한 QUIC(UDP 기반 프로토콜)을 활용하여 신뢰성 있는 스트림과 신뢰성을 보장하지 않는 데이터그램을 모두 제공한다.

점차 지원 범위가 확장되고 있기 때문에 선택에 있어 고려할 만한 기술이다.


 

[참고하면 좋은 자료]

 

HTTP

SSE

WebRTC

WebSocket vs WebTransport

반응형

'backend' 카테고리의 다른 글

회원 로그인 구현 시 인증 방식(쿠키, 세션, 토큰)  (0) 2024.08.30