네트워크 통신은 클라이언트와 서버가 데이터를 주고받는 과정이다. 게임 서버, 웹 서버, 실시간 멀티플레이 시스템을 개발할 때는 단순히 데이터를 전송하는 것뿐만 아니라 어떤 프로토콜을 사용하고, 통신이 어떤 방식으로 이루어지며, 네트워크 오류에 어떻게 대응할 것인지를 이해해야 한다.
이번 글에서는 네트워크 통신의 핵심 개념인 HTTP와 WebSocket, TCP와 UDP, 요청·응답 방식과 양방향 통신, 네트워크 지연과 패킷 손실 및 타임아웃을 정리한다.
HTTP와 WebSocket의 차이
HTTP와 WebSocket은 애플리케이션 계층에서 사용하는 통신 프로토콜이다. 두 프로토콜의 핵심적인 차이는 통신을 유지하고 데이터를 주고받는 방식에 있다.
HTTP란?
HTTP(Hypertext Transfer Protocol)는 클라이언트가 서버에 요청(Request)을 보내고 서버가 응답(Response)을 반환하는 프로토콜이다.
일반적인 웹 API에서는 클라이언트가 데이터를 요청하거나 변경할 때 HTTP를 사용한다.
예를 들어 게임 클라이언트에서 플레이어의 프로필을 조회한다고 가정해 보자.
GET /api/players/1001 HTTP/1.1
Host: game.example.com
Authorization: Bearer ACCESS_TOKEN
서버는 요청을 처리한 뒤 결과를 반환한다.
HTTP/1.1 200 OK
Content-Type: application/json
{
"playerId": 1001,
"nickname": "Player01",
"level": 25
}
이처럼 HTTP 통신은 일반적으로 클라이언트가 요청을 보내고 서버가 응답하는 구조로 동작한다.
HTTP/1.1에서는 하나의 TCP 연결을 여러 요청에 재사용할 수 있으며, HTTP/2와 HTTP/3에서는 요청을 효율적으로 다중화하는 기능도 제공한다. 따라서 HTTP를 사용할 때마다 반드시 새로운 연결을 생성하는 것은 아니다.
WebSocket이란?
WebSocket은 클라이언트와 서버가 하나의 연결을 통해 양방향으로 데이터를 주고받을 수 있도록 하는 프로토콜이다.
일반적인 WebSocket 연결은 HTTP 핸드셰이크로 시작한다. 연결이 업그레이드되면 이후에는 WebSocket 프로토콜을 통해 데이터를 주고받는다.
예를 들어 멀티플레이 게임에서 플레이어가 이동했다고 가정해 보자.
클라이언트는 자신의 위치를 서버에 전송한다.
{
"type": "PLAYER_MOVE",
"x": 120.5,
"y": 30.0,
"z": 80.2
}
서버는 해당 정보를 다른 플레이어에게 전달할 수 있다.
{
"type": "PLAYER_UPDATED",
"playerId": 1001,
"x": 120.5,
"y": 30.0,
"z": 80.2
}
클라이언트가 별도로 HTTP 요청을 보내지 않아도 서버가 먼저 데이터를 전송할 수 있다는 점이 핵심이다.
HTTP와 WebSocket 비교
| 구분 | HTTP | WebSocket |
|---|---|---|
| 기본 통신 방식 | 요청·응답 | 양방향 통신 |
| 서버의 자발적 전송 | 일반적인 응답 외에는 제한적 | 가능 |
| 연결 | 연결 재사용 가능 | 업그레이드 후 지속 연결 |
| 적합한 용도 | REST API, 로그인, 데이터 조회 | 채팅, 실시간 게임, 실시간 알림 |
| 통신 오버헤드 | 요청·응답마다 HTTP 헤더가 포함될 수 있음 | 연결 이후 프레임 단위로 데이터 전송 |
| 연결 관리 | 요청 단위로 처리 가능 | 연결 상태 및 종료 관리 필요 |
WebSocket이 항상 HTTP보다 빠른 것은 아니다. 단순한 데이터 조회에서는 HTTP가 더 적합할 수 있으며, 지속적으로 데이터를 교환하는 환경에서는 WebSocket이 효율적일 수 있다.
게임 서버에서의 활용
게임 서버는 기능에 따라 통신 방식을 분리할 수 있다.
- 로그인 및 계정 정보 조회: HTTP
- 인벤토리 조회 및 구매 요청: HTTP 또는 WebSocket
- 실시간 채팅: WebSocket
- 실시간 플레이어 상태 전달: WebSocket 또는 UDP 기반 프로토콜
- 서버 상태 및 운영 정보 조회: HTTP
단, WebSocket을 사용한다고 해서 게임 동기화 문제가 자동으로 해결되는 것은 아니다. 서버 권한 검증, 메시지 순서, 지연 보정, 상태 동기화 전략은 별도로 설계해야 한다.
TCP와 UDP의 차이
TCP와 UDP는 전송 계층에서 사용하는 프로토콜이다. 애플리케이션이 만든 데이터를 실제 네트워크를 통해 전달할 때 어떤 전달 방식을 사용할지 결정한다.
TCP란?
TCP(Transmission Control Protocol)는 연결 기반의 신뢰성 있는 데이터 전송을 제공한다.
TCP는 수신 확인, 재전송, 순서 정렬 등의 기능을 통해 애플리케이션에 데이터를 순서대로 전달한다.
예를 들어 클라이언트가 다음 데이터를 전송한다고 가정하자.
A → B → C → D → E
전송 도중 C에 해당하는 데이터가 손실되면 TCP는 손실을 감지하고 필요한 데이터를 재전송한다. 수신 애플리케이션에는 원칙적으로 데이터가 올바른 순서로 전달된다.
TCP의 주요 특징은 다음과 같다.
- 연결 설정 및 종료 절차가 존재한다.
- 손실된 데이터를 재전송한다.
- 수신 측에서 데이터를 순서대로 읽을 수 있도록 보장한다.
- 흐름 제어를 통해 수신 측의 처리 능력을 고려한다.
- 혼잡 제어를 통해 네트워크 혼잡에 대응한다.
다만 TCP가 보장하는 것은 TCP 연결을 통한 데이터 전달의 신뢰성이다. 서버 프로세스가 요청을 실제로 처리했는지, 데이터베이스에 저장했는지까지 보장하지는 않는다.
또한 TCP는 메시지 경계를 보존하지 않는 바이트 스트림 프로토콜이다. 따라서 애플리케이션에서 메시지 길이 헤더나 구분자 등을 사용해 메시지 경계를 정의해야 할 수 있다.
UDP란?
UDP(User Datagram Protocol)는 비연결형 데이터그램 전송 프로토콜이다.
UDP는 TCP와 달리 연결 설정, 수신 확인, 재전송, 순서 보장을 자체적으로 제공하지 않는다.
송신자 수신자
패킷 A ────────────→ A 수신
패킷 B ──────X B 손실
패킷 C ────────────→ C 수신
패킷 D ────────────→ D 수신
B가 손실되더라도 UDP 자체가 이를 재전송하지 않는다. 애플리케이션에서 별도로 구현하지 않는 한 수신자는 B를 복구할 수 없다.
UDP의 주요 특징은 다음과 같다.
- 연결 설정 없이 데이터그램을 전송할 수 있다.
- 프로토콜 자체의 헤더가 작다.
- 수신 확인과 재전송을 기본적으로 제공하지 않는다.
- 패킷 순서와 전달을 보장하지 않는다.
- 애플리케이션이 필요한 신뢰성 기능을 직접 설계할 수 있다.
UDP는 신뢰성이 없는 프로토콜이지만, 이를 사용한다고 반드시 데이터가 손실되는 것은 아니다. 반대로 TCP를 사용한다고 네트워크 지연이 사라지는 것도 아니다.
TCP와 UDP 비교
| 구분 | TCP | UDP |
|---|---|---|
| 통신 형태 | 연결 기반 | 비연결형 |
| 전달 보장 | 재전송 등을 통한 신뢰성 제공 | 자체적인 전달 보장 없음 |
| 순서 보장 | 바이트 스트림의 순서 보장 | 순서 보장 없음 |
| 재전송 | 프로토콜에서 처리 | 애플리케이션이 필요에 따라 구현 |
| 데이터 형태 | 바이트 스트림 | 데이터그램 |
| 주요 활용 | 웹 API, 파일 전송, 데이터베이스 연결 | 실시간 게임, 음성·영상, DNS 등 |
<LearningViz type_id=”TCP_VS_UDP” initial_values={{“protocol”:”tcp”,”lossMode”:”drop_packet_3″}} locale_override=”ko-KR” />
게임 서버에서 TCP와 UDP를 선택하는 기준
게임에서는 모든 데이터에 동일한 전송 방식을 적용할 필요가 없다.
TCP가 적합한 데이터
- 로그인 및 계정 정보
- 아이템 구매와 재화 변경 요청
- 게임 결과 및 보상 처리
- 순서와 전달 신뢰성이 중요한 데이터
UDP가 적합할 수 있는 데이터
- 플레이어의 위치와 방향
- 빈번하게 갱신되는 이동 상태
- 실시간 액션 게임의 일부 전투 정보
- 지연이 정확한 전달보다 중요한 상태 업데이트
예를 들어 100ms 전에 생성된 플레이어 위치보다 최신 위치가 중요하다면, 오래된 패킷을 기다리거나 재전송하는 것보다 최신 상태를 빠르게 반영하는 편이 유리할 수 있다.
반면 재화 지급 요청이 손실되거나 중복 처리되면 문제가 발생한다. 이러한 데이터는 신뢰성뿐 아니라 중복 요청 방지와 서버 측 검증도 필요하다.
중요한 점은 UDP를 선택하면 개발자가 신뢰성, 순서, 중복 제거, 혼잡 제어 등의 필요 여부를 판단해야 한다는 것이다. 모든 기능을 직접 구현해야 한다는 뜻은 아니며, QUIC이나 게임 네트워크 라이브러리처럼 이러한 기능을 제공하는 기술을 활용할 수도 있다.
요청·응답 방식과 양방향 통신
요청·응답과 양방향 통신은 데이터를 주고받는 방식에 관한 개념이다. HTTP와 WebSocket을 이해할 때 중요한 구분이지만, 두 개념이 특정 프로토콜에만 종속되는 것은 아니다.
요청·응답 방식
요청·응답(Request-Response)은 한쪽에서 요청을 보내고 상대방이 그 요청에 대한 결과를 반환하는 방식이다.
클라이언트 서버
│ │
│──── 로그인 요청 ─────→│
│ │ 인증 처리
│←── 로그인 결과 ───────│
│ │
예를 들어 로그인 API에서는 클라이언트가 아이디와 인증 정보를 전달하고 서버가 성공 여부와 필요한 정보를 반환한다.
이 방식의 장점은 명확한 처리 흐름이다.
- 요청과 응답의 관계가 명확하다.
- API 설계와 테스트가 쉽다.
- 서버에서 요청 단위로 인증 및 검증을 수행하기 편리하다.
단점은 서버가 새로운 정보를 전달해야 할 때 클라이언트가 다시 요청하거나 별도의 통신 채널을 사용해야 할 수 있다는 것이다.
양방향 통신
양방향 통신은 양쪽이 서로 데이터를 전송할 수 있는 방식이다.
클라이언트 A 서버 클라이언트 B
│ │ │
│──── 이동 정보 ──────→│ │
│ │──── 이동 정보 ──────→│
│ │ │
│ │←──── 채팅 메시지 ────│
│←──── 채팅 메시지 ────│ │
WebSocket은 연결이 성립된 이후 클라이언트와 서버가 각각 데이터를 전송할 수 있다. 서버는 특정 클라이언트의 요청에 대한 응답이 아니더라도 이벤트나 상태 변경 정보를 전송할 수 있다.
이 방식은 다음과 같은 시스템에 적합하다.
- 실시간 멀티플레이 게임
- 채팅 및 협업 도구
- 실시간 알림
- 서버 모니터링 대시보드
양방향 통신이라고 해서 두 클라이언트가 반드시 직접 연결되는 것은 아니다. 일반적인 게임 서버에서는 클라이언트들이 서버와 각각 연결하고, 서버가 전달 대상을 판단해 메시지를 중계한다.
또한 양방향 통신은 반드시 WebSocket만을 의미하지 않는다. TCP 소켓이나 UDP 기반 프로토콜에서도 양방향 데이터 교환을 구현할 수 있다.
네트워크 지연, 패킷 손실, 타임아웃
네트워크 통신은 데이터를 전송하는 것만으로 끝나지 않는다. 실제 운영 환경에서는 지연, 손실, 연결 단절 등이 발생할 수 있으므로 이를 고려해 시스템을 설계해야 한다.
네트워크 지연(Latency)
네트워크 지연은 데이터가 송신자에서 수신자까지 전달되는 데 걸리는 시간이다.
일반적으로 지연 시간은 밀리초(ms) 단위로 측정한다.
예를 들어 클라이언트가 서버에 요청을 보낸 후 응답을 받기까지 80ms가 걸렸다면 해당 요청의 왕복 시간(RTT, Round-Trip Time)은 약 80ms다.
지연 시간에는 여러 요인이 영향을 준다.
- 클라이언트와 서버 사이의 물리적 거리
- 네트워크 경로 및 라우팅
- 통신 장비와 회선의 혼잡
- 서버의 처리 시간
- 데이터베이스 조회 및 외부 서비스 호출
특히 RTT와 서버 처리 시간은 구분해야 한다. 응답이 늦게 도착하더라도 원인이 네트워크인지, 서버의 CPU 처리인지, 데이터베이스인지에 따라 해결 방법이 달라진다.
게임에서 지연이 커지면 다음과 같은 현상이 나타날 수 있다.
- 플레이어의 이동이 늦게 반영된다.
- 공격 판정 결과가 늦게 도착한다.
- 다른 플레이어의 움직임이 끊겨 보인다.
- 입력과 화면 반응 사이에 차이가 발생한다.
지연을 줄이려면 서버 위치, 패킷 크기, 처리 비용, 데이터베이스 접근 방식 등을 점검해야 한다. 실시간 게임에서는 보간(Interpolation), 예측(Prediction), 서버 권한 기반 판정 등의 기법도 활용할 수 있다.
패킷 손실(Packet Loss)
패킷 손실은 송신된 패킷이 수신 측에 도착하지 못하는 현상이다.
원인에는 네트워크 혼잡, 무선 연결 불안정, 장비 오류, 버퍼 부족 등이 있다.
패킷 손실은 전송 프로토콜에 따라 다른 결과를 만든다.
TCP의 경우
TCP는 손실을 감지하면 필요한 데이터를 재전송한다. 따라서 애플리케이션이 데이터 누락 없이 처리할 수 있도록 돕지만, 재전송을 기다리는 동안 지연이 커질 수 있다. 수신 측은 누락된 바이트가 도착할 때까지 뒤에 도착한 데이터의 전달을 기다리는 상황도 발생할 수 있다.
UDP의 경우
UDP는 자체적으로 손실된 패킷을 복구하지 않는다. 따라서 실시간 상태 데이터에서는 일부 정보가 누락될 수 있지만, 최신 패킷을 빠르게 반영하는 전략을 사용할 수 있다.
예를 들어 60Hz로 위치를 전송하는 게임에서 특정 위치 패킷 하나가 손실되더라도 다음 위치 업데이트가 도착하면 현재 상태를 다시 갱신할 수 있다.
다만 모든 UDP 데이터가 손실을 허용하는 것은 아니다. 중요한 명령이나 이벤트라면 별도의 확인 응답, 재전송, 시퀀스 번호 또는 신뢰성 전송 계층이 필요할 수 있다.
타임아웃(Timeout)
타임아웃은 정해진 시간 안에 기대한 응답이나 작업 완료가 이루어지지 않았을 때 해당 작업을 실패 또는 지연 상태로 처리하는 기준이다.
예를 들어 클라이언트가 로그인 요청을 보내고 5초 동안 응답을 받지 못했다면 요청 타임아웃을 발생시킬 수 있다.
async function login(): Promise<void> {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, 5000);
try {
const response = await fetch(
"https://game.example.com/api/login",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username: "player01",
password: "example-password",
}),
signal: controller.signal,
},
);
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
const result = await response.json();
console.log(result);
} catch (error) {
if (
error instanceof Error &&
error.name === "AbortError"
) {
console.error("로그인 요청 타임아웃");
} else {
console.error("로그인 요청 실패", error);
}
} finally {
clearTimeout(timeoutId);
}
}
위 코드는 요청이 5초 안에 완료되지 않으면 클라이언트 측에서 요청을 중단한다. 다만 AbortController로 요청을 중단했다고 해서 서버에서 이미 시작된 작업까지 취소되었다고 보장할 수는 없다.
특히 결제나 게임 재화 지급처럼 중요한 작업에서는 타임아웃 발생 후 무조건 재요청하면 안 된다. 서버가 작업을 완료했지만 응답만 유실되었을 수 있기 때문이다.
이러한 상황에서는 다음과 같은 전략을 사용할 수 있다.
- 요청마다 고유한 요청 ID를 부여한다.
- 동일 요청의 중복 처리를 방지한다.
- 작업 상태를 조회할 수 있는 API를 제공한다.
- 재시도 정책과 최대 재시도 횟수를 설정한다.
재시도와 타임아웃 설계
일시적인 네트워크 오류가 발생했을 때 재시도는 유용하지만, 무조건 반복하면 오히려 장애를 악화시킬 수 있다.
예를 들어 서버가 과부하 상태인데 모든 클라이언트가 1초마다 요청을 재시도한다면 요청량이 증가해 서버가 더 오랫동안 복구되지 않을 수 있다.
따라서 실무에서는 다음과 같은 전략을 고려한다.
Exponential Backoff
재시도 간격을 점차 늘리는 방식이다.
1차 재시도: 1초 후
2차 재시도: 2초 후
3차 재시도: 4초 후
4차 재시도: 8초 후
Jitter
여러 클라이언트가 동시에 재시도하는 현상을 줄이기 위해 대기 시간에 무작위 변동을 추가한다.
최대 재시도 횟수
무한 재시도를 방지하고, 일정 횟수 이후에는 실패로 처리하거나 사용자에게 재시도하도록 안내한다.
재시도는 모든 요청에 적용할 수 있는 전략이 아니다. 읽기 요청은 비교적 재시도하기 쉽지만, 결제나 재화 지급 같은 변경 요청은 멱등성(Idempotency)과 중복 처리 방지 설계가 필요하다.
네트워크 통신 설계 시 고려할 사항
실제 서버를 개발할 때는 프로토콜을 선택하는 것만큼 오류 처리와 데이터의 의미를 고려하는 것이 중요하다.
| 상황 | 고려할 설계 |
|---|---|
| 로그인 API 응답 지연 | 요청 타임아웃, 오류 응답, 제한된 재시도 |
| 실시간 플레이어 이동 | 최신 상태 반영, 시퀀스 번호, 보간 |
| 패킷 손실 | TCP 재전송 또는 애플리케이션 수준의 복구 전략 |
| WebSocket 연결 종료 | 재연결, 세션 복구, 중복 연결 처리 |
| 재화 지급 요청 중 응답 유실 | 요청 ID, 멱등성, 결과 조회 |
| 서버 과부하 | 요청 제한, 백오프, 부하 분산 |
이러한 설계는 네트워크 오류가 발생해도 시스템이 일관된 상태를 유지하도록 돕는다.