HTTP vs Socket

mongBrown·2026년 4월 27일

HTTP는 쓰는 이유

게임 서버를 만든다고 해보자. 캐릭터가 움직일 때마다 서버에 위치를 전송해야 한다. 1초에 60번.

HTTP로 구현하면 매 전송마다 이런 것들이 딸려간다.

POST /position HTTP/1.1
Host: game.server.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...
Cookie: session=abc123; theme=dark
User-Agent: GameClient/1.0

{"x": 123.4, "y": 56.7}

실제 데이터는 {"x": 123.4, "y": 56.7} 두 숫자인데, 수백 바이트짜리 헤더가 1초에 60번 반복된다. 게임에 로그인한 이상 인증 정보는 바뀌지 않는데도.

그렇다면 소켓으로 직접 통신하면 되지 않을까. 어차피 HTTP도 TCP 위에서 동작하는데.

소켓을 쓰지 않는 이유가 뭘까

소켓으로 직접 통신하는 건 기술적으로 불가능한 게 아니다. 실제로 동작한다. 문제는 현실에서 만나는 장벽들이다.

첫 번째는 방화벽이다. 회사 내부 네트워크에서 외부 서버의 9000번 포트에 소켓으로 붙으려 하면 방화벽에서 막힌다. 기업 방화벽은 80, 443 같이 잘 알려진 포트만 열어두고 나머지는 차단하는 게 기본이다. HTTP는 80/443을 쓰기 때문에 방화벽을 자연스럽게 통과한다.

두 번째는 표준화다. 소켓으로 직접 통신하면 데이터를 어떤 형식으로 주고받을지 양쪽이 직접 설계해야 한다. HTTP는 이미 method, status code, header, body의 규칙이 정해져 있어서 아무 클라이언트나 아무 서버나 HTTP를 알면 통신이 된다.

세 번째는 인프라다. 로드밸런서, CDN, 리버스 프록시가 전부 HTTP를 기반으로 동작한다. 커스텀 소켓 프로토콜을 쓰면 이 인프라들이 그 프로토콜을 이해하지 못하기 때문에, 각각을 커스텀 프로토콜에 맞게 다시 맞춰야 하는 비용이 생긴다.

그러면 소켓을 직접 쓰는 경우는 언제일까

HTTP의 이점을 포기해야 할 만큼 소켓이 필요한 상황이 있다.

지속 연결이 필요하고, 데이터를 고빈도로 주고받으며, 오래된 데이터보다 지금 이 순간의 데이터가 더 중요한 경우다. 게임 서버의 위치 동기화가 전형적인 예다. 이전 프레임의 좌표를 뒤늦게 받아봐야 의미가 없으니, 패킷이 손실되더라도 최신 데이터를 빠르게 받는 게 낫다.

이런 케이스에서는 소켓 중에서도 UDP 소켓을 선택한다. 소켓은 TCP로도, UDP로도 쓸 수 있다. 게임 서버도 채팅이나 아이템 획득처럼 유실되면 안 되는 데이터는 TCP 소켓으로, 위치 동기화처럼 실시간성이 중요한 데이터는 UDP 소켓으로 분리해서 쓴다.

HTTP의 비효율이 구체적으로 뭔가

HTTP가 비효율적이라고 하는데, 무엇이 비효율인지 짚어두는 게 중요하다.

헤더 반복 전송. 매 요청마다 인증 토큰, 쿠키, User-Agent 같은 헤더가 붙는다. 고빈도 통신에서는 실제 데이터보다 헤더가 더 클 수도 있고, 이 헤더들이 요청 사이에 바뀌지 않아도 매번 전송된다.

텍스트 기반 프로토콜. HTTP/1.1은 사람이 읽을 수 있는 텍스트 형태로 데이터를 주고받는다. "Content-Type: application/json\r\n" 같은 문자열을 매번 파싱해야 한다. 바이너리 프로토콜이라면 정해진 바이트 위치를 읽으면 끝인데, 문자열 파싱은 그보다 무겁다. 데이터 크기도 텍스트가 더 크다. 숫자 123.4를 텍스트로 보내면 5바이트지만 float 바이너리로 보내면 4바이트다.

HOL Blocking. HTTP/1.1은 keep-alive로 TCP 연결을 재사용하더라도, 하나의 연결에서 한 번에 하나의 요청만 처리한다. 요청A가 응답을 기다리는 동안 요청B는 묶인다.

WebSocket은 어떻게 이 사이를 채웠나

HTTP의 표준화 이점은 챙기면서 소켓처럼 양방향 통신이 필요한 경우가 있다. 실시간 채팅이 대표적이다. 서버가 새 메시지를 받았을 때 클라이언트에 먼저 알려야 하는데, HTTP의 요청→응답 패턴으로는 서버가 먼저 데이터를 보낼 수 없다.

WebSocket은 HTTP로 시작해서 프로토콜을 업그레이드하는 방식으로 이 문제를 푼다.

클라이언트: GET /chat HTTP/1.1
            Upgrade: websocket
            Connection: Upgrade

서버:       HTTP/1.1 101 Switching Protocols
            Upgrade: websocket

101 응답 이후 프로토콜이 바뀐다. TCP는 원래 양방향 동시 전송(full-duplex)이 가능한데, HTTP 프로토콜이 항상 클라이언트 요청 선행을 강제해왔던 것이다. WebSocket은 그 규칙을 없애고 TCP의 양방향성을 그대로 쓴다. 이후 서버도 클라이언트도 언제든 먼저 데이터를 보낼 수 있다. 포트도, 연결도 하나로.

HTTP/2는 비효율을 어떻게 줄였나

HTTP/3(QUIC)가 UDP 기반으로 3-way handshake를 없앤 건 이전에 다뤘다. HTTP/2는 그보다 먼저 나온 버전으로, TCP는 유지하면서 HTTP 고유의 비효율 세 가지를 각각 해결했다.

헤더 반복 전송은 HPACK 압축으로 해결했다. 한 번 보낸 헤더는 인덱스로 참조해서 반복 전송을 줄인다.

텍스트 기반 파싱 비용은 바이너리 프로토콜로 전환해서 해결했다. 텍스트를 쪼개고 해석하는 과정 없이 정해진 바이트 위치를 읽으면 된다.

HOL Blocking은 멀티플렉싱으로 해결했다. 하나의 TCP 연결 안에 스트림이라는 논리 채널을 여러 개 만들어, 요청A, B, C를 동시에 보내고 각자 완료되는 대로 응답을 받는다. HTTP/1.1은 A 끝나야 B를 보낼 수 있었는데, HTTP/2는 세 요청을 동시에 띄워두고 가장 느린 것 하나가 끝날 때 모두 완료된다.

어떤 상황에 무엇을 쓸까

HTTP는 대부분의 API 통신에 적합하다. 방화벽, 표준화, 인프라 호환성을 다 챙길 수 있고, HTTP/2를 쓰면 고빈도 요청도 멀티플렉싱으로 소화된다.

WebSocket은 서버가 먼저 데이터를 푸시해야 하거나, 양방향 실시간 통신이 필요한 경우다. 채팅, 알림, 실시간 대시보드가 해당한다.

TCP 소켓을 직접 쓰는 경우는 HTTP 인프라 전체를 제어할 수 있는 환경에서 고빈도 바이너리 통신이 필요할 때다. UDP 소켓은 패킷 손실보다 속도가 중요하고 오래된 데이터는 버려도 되는 경우다. 게임의 위치 동기화, 음성/영상 스트리밍이 여기에 해당한다.

profile
화이팅!

0개의 댓글