네트워크 핵심 정리

네트워크는 결국 서로 다른 컴퓨터나 프로그램이 데이터를 주고받기 위한 구조다.

게임을 실행하고 서버에 접속하면 클라이언트와 서버가 계속 데이터를 주고받는다.

Client -> Network -> Server

실제로는 이 과정이 이렇게 단순하지 않다.

데이터가 어떤 형식으로 만들어지는지, 누구에게 전달해야 하는지, 어떤 경로를 통해 이동하는지, 전달 과정에서 문제가 생겼을 때 어떻게 처리하는지까지 여러 계층에서 관리한다.

네트워크를 공부할 때는 각각의 프로토콜을 외우기보다 데이터가 애플리케이션에서 출발해서 상대방에게 도착하기까지 어떤 과정을 거치는가를 보는 편이 이해하기 좋다.


네트워크의 기본 구조

가장 단순하게 보면 네트워크 통신은 다음과 같다.

Application -> Network Stack -> Network -> Network Stack -> Application

게임 클라이언트가 서버에 플레이어의 이동 정보를 보낸다고 하면,

Game Client -> 이동 데이터 생성 -> Network Protocol -> 인터넷 -> Server -> 이동 데이터 처리

같은 흐름이 된다.

여기서 IP, TCP, UDP, HTTP 같은 개념이 각각 다른 역할을 담당한다.


네트워크 계층

네트워크는 하나의 거대한 기능으로 구현되어 있지 않다.

서로 다른 역할을 여러 계층으로 나누어 관리한다.

대표적으로 OSI 7계층이 있다.

7. Application
6. Presentation
5. Session
4. Transport
3. Network
2. Data Link
1. Physical

실제 인터넷 프로토콜을 이해할 때는 TCP/IP 모델처럼 조금 단순화해서 보는 경우도 많다.

Application -> Transport -> Internet -> Network Access

계층을 나누는 이유는 각 계층의 책임을 분리하기 위해서다.

애플리케이션은 데이터를 어떻게 사용할지에 집중하고, 하위 계층은 그 데이터를 실제 네트워크를 통해 전달하는 역할을 담당한다.


IP

IP(Internet Protocol)는 네트워크에서 목적지를 식별하고 데이터를 전달하기 위한 프로토콜이다.

인터넷에 연결된 장치는 IP 주소를 사용할 수 있다.

192.168.0.10

같은 형태가 IPv4 주소다.

IPv6에서는

2001:db8::1

처럼 표현한다.

IP의 역할을 단순화하면

어디로 보낼 것인가?

를 결정하는 데 가깝다.

다만 IP 자체는 패킷이 반드시 도착한다고 보장하지 않는다.

패킷이 유실되거나 순서가 바뀔 수 있다.

이런 문제를 어떻게 처리할지는 상위 계층의 프로토콜이 담당한다.


MAC Address

IP가 논리적인 네트워크 주소라면 MAC Address는 네트워크 인터페이스를 식별하기 위한 주소다.

같은 로컬 네트워크 안에서는 이더넷이나 Wi-Fi 같은 데이터 링크 계층에서 MAC 주소가 사용된다.

개념적으로 보면

IP
-> 논리적인 목적지 식별

MAC
-> 로컬 네트워크에서 인터페이스 식별

정도로 구분할 수 있다.

실제 통신에서는 IP만 알고 있다고 바로 이더넷 프레임을 보낼 수 있는 것은 아니다.

로컬 네트워크에서 목적지 MAC 주소를 알아내기 위해 ARP 같은 프로토콜이 사용된다.


Router

서로 다른 네트워크 사이에서 패킷을 전달하는 장치가 Router다.

예를 들어

Client -> Router -> Internet -> Router -> Server

처럼 패킷이 여러 네트워크를 거쳐 목적지까지 이동한다.

라우터는 목적지 IP를 기준으로 어떤 경로로 패킷을 전달할지 결정한다.

인터넷 전체가 하나의 네트워크로 연결된 것이 아니라 여러 네트워크가 라우터 등을 통해 연결되어 있는 구조다.


Port

하나의 컴퓨터에서 여러 프로그램이 동시에 네트워크 통신을 할 수 있다.

예를 들어 한 서버에서

Game Server
Web Server
Database

가 모두 실행될 수 있다.

이때 Port를 사용해 어떤 프로그램의 네트워크 서비스인지 구분한다.

IP Address + Port

예를 들어

192.168.0.10:8080

처럼 표현할 수 있다.

IP가 어떤 호스트인지 식별한다면 Port는 그 호스트 안에서 어떤 네트워크 서비스와 통신할지를 구분하는 데 사용된다.


TCP

TCP(Transmission Control Protocol)는 신뢰성 있는 연결 지향 통신을 제공한다.

TCP는 데이터를 전송할 때

  • 순서 보장
  • 재전송
  • 중복 데이터 처리
  • 흐름 제어
  • 혼잡 제어

등을 제공한다.

그래서 애플리케이션이 데이터 전달의 세부적인 문제를 직접 처리해야 하는 부담을 줄일 수 있다.

개념적으로는

Client -> TCP Connection -> Server

형태로 연결을 유지하면서 통신한다.


TCP 연결

TCP 연결은 일반적으로 3-way handshake를 통해 수립된다.

Client                  Server

  SYN ----------------->
      <--------------- SYN + ACK
  ACK ----------------->

이 과정을 통해 양쪽이 통신을 시작할 준비가 되었음을 확인한다.

연결이 만들어진 이후 데이터를 주고받는다.

TCP는 연결 상태를 관리하기 때문에 상대적으로 높은 신뢰성을 제공하지만 그만큼 상태 관리와 처리 비용이 발생한다.


UDP

UDP(User Datagram Protocol)는 TCP보다 단순한 방식으로 데이터를 전달한다.

연결 상태를 유지하거나 TCP처럼 데이터의 순서와 재전송을 보장하지 않는다.

Client -> UDP Datagram -> Server

그래서 UDP에서는 패킷이

유실될 수 있음
순서가 바뀔 수 있음
중복될 수 있음

을 고려해야 한다.

그 대신 프로토콜 자체가 단순하고 지연에 민감한 통신에서 유리할 수 있다.


TCP와 UDP

둘 중 어떤 것이 더 좋은지는 목적에 따라 달라진다.

구분TCPUDP
연결연결 지향비연결
순서 보장OX
재전송OX
신뢰성높음직접 구현 필요
오버헤드상대적으로 큼상대적으로 작음
활용웹, 파일 전송 등실시간 통신, 게임 등

다만 “게임은 UDP, 웹은 TCP”처럼 단순하게 외우면 부족하다.

게임에서도 TCP를 사용할 수 있고, UDP 기반 통신에서도 필요한 신뢰성 기능을 애플리케이션에서 직접 구현할 수 있다.

실제로 중요한 것은 데이터의 특성과 요구되는 지연 시간, 신뢰성, 순서 보장 등을 기준으로 선택하는 것이다.


Packet

네트워크에서 데이터는 한 번에 거대한 덩어리로 이동하는 것이 아니라 작은 단위로 나뉘어 전달된다.

이를 일반적으로 Packet이라고 부른다.

Large Data -> Packet 1, Packet 2, Packet 3, Packet 4 -> Network -> Server

각 계층에서 사용하는 데이터 단위의 이름은 조금씩 다르다.

Application -> Message / Data
Transport -> Segment / Datagram
Network -> Packet
Data Link -> Frame

엄밀한 용어는 프로토콜에 따라 달라질 수 있지만, 계층별로 데이터에 헤더를 붙이며 전달한다는 구조를 이해하는 것이 중요하다.


Encapsulation

애플리케이션에서 만들어진 데이터는 네트워크 계층을 내려가면서 각 계층의 정보가 추가된다.

Application Data -> TCP Header + Data -> IP Header + TCP Header + Data -> Ethernet Header + IP Header + TCP Header + Data

이 과정을 Encapsulation이라고 한다.

반대로 수신 측에서는 각 계층이 자신의 헤더를 처리하면서 상위 계층으로 데이터를 전달한다.

Frame -> Packet -> TCP Data -> Application Data

네트워크 계층 구조를 이해하는 데 상당히 중요한 개념이다.


DNS

사람은

google.com

같은 도메인을 사용하기 편하다.

하지만 네트워크 통신에서는 IP 주소가 필요하다.

DNS(Domain Name System)는 도메인 이름을 IP 주소 등 관련 정보로 해석하는 시스템이다.

google.com -> DNS -> IP Address -> Server

DNS도 하나의 서버가 모든 도메인을 관리하는 구조가 아니다.

계층적인 구조를 통해 도메인 정보를 관리한다.


HTTP

웹에서 가장 많이 사용하는 프로토콜 중 하나가 HTTP다.

HTTP는 클라이언트와 서버 사이에서 요청과 응답을 주고받는 프로토콜이다.

Client -> HTTP Request -> Server
Client <- HTTP Response <- Server

예를 들어

GET /players/10

같은 요청을 보낼 수 있다.

서버는

200 OK

와 함께 데이터를 반환할 수 있다.


HTTP Method

HTTP에는 요청의 목적을 나타내는 Method가 있다.

대표적으로

GET
POST
PUT
PATCH
DELETE

등을 사용한다.

일반적인 의미는

GET
-> 데이터 조회

POST
-> 새로운 데이터 생성 또는 처리 요청

PUT
-> 리소스 전체 수정

PATCH
-> 리소스 일부 수정

DELETE
-> 삭제

정도로 볼 수 있다.

실제 API 설계에서는 서비스의 의미에 맞게 사용해야 한다.


HTTP Status Code

HTTP 응답에는 상태 코드가 포함된다.

대표적으로

2xx -> 성공
3xx -> 리다이렉션
4xx -> 클라이언트 측 요청 문제
5xx -> 서버 측 오류

예를 들어

200 OK
201 Created
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
500 Internal Server Error

등이 있다.

401403은 특히 구분할 필요가 있다.

일반적으로 401은 인증이 필요한 상태를 나타내고, 403은 요청한 주체가 해당 작업을 수행할 권한이 없음을 나타낸다.


REST API

웹 API를 설계할 때 자주 사용하는 방식이 REST 스타일이다.

예를 들어

GET /players/10
POST /players
PATCH /players/10
DELETE /players/10

처럼 리소스를 URL로 표현하고 HTTP Method로 작업의 의미를 나타낼 수 있다.

REST는 특정 구현 라이브러리나 프로토콜이 아니다.

HTTP의 특성을 이용해 리소스와 상태를 일관된 방식으로 표현하기 위한 아키텍처 스타일에 가깝다.


Socket

네트워크 프로그래밍에서 애플리케이션이 통신하기 위한 인터페이스로 Socket을 사용할 수 있다.

개념적으로는

Application -> Socket API -> TCP / UDP -> Network

구조다.

게임 서버를 직접 구현한다면 Socket을 통해 TCP나 UDP 통신을 구성할 수 있다.

Client Socket <-> Server Socket

상위 게임 로직은 네트워크의 세부적인 처리와 분리하고, 네트워크 계층에서 패킷 송수신을 담당하도록 설계하는 것이 일반적이다.


Latency와 Bandwidth

네트워크 성능을 볼 때 LatencyBandwidth를 구분해야 한다.

Latency는 데이터가 목적지까지 도달하는 데 걸리는 시간에 가깝다.

Client -> Request -> Server -> Response

이 왕복 시간이 길면 게임에서는 입력 반응이 늦게 느껴질 수 있다.

Bandwidth는 일정 시간 동안 전송할 수 있는 데이터의 양이다.

Bandwidth -> 초당 전송 가능한 데이터량

대역폭이 크다고 해서 지연 시간이 반드시 짧은 것은 아니다.

온라인 게임에서는 둘을 별도로 고려해야 한다.


게임 네트워크에서의 문제

실시간 게임에서는 네트워크가 완벽하지 않다는 것을 전제로 해야 한다.

대표적인 문제가

Latency
Packet Loss
Jitter
Out of Order
Duplication

이다.

예를 들어 플레이어가 이동했다고 하자.

Client -> Move -> Move -> Move

서버에 패킷이 늦게 도착하거나 일부가 유실될 수 있다.

그래서 실시간 게임에서는 단순히 모든 상태를 TCP로 보내는 것만으로 해결되지 않는 문제가 있다.

게임의 성격에 따라

  • Client Prediction
  • Server Reconciliation
  • Interpolation
  • Extrapolation
  • Snapshot
  • Lag Compensation

같은 기법을 사용할 수 있다.


Client Prediction

온라인 게임에서 입력 반응을 빠르게 보이게 하기 위해 클라이언트가 자신의 입력 결과를 먼저 예측할 수 있다.

Client -> 입력 -> 즉시 이동 예측
Server -> 입력 검증 -> 실제 상태 계산

서버의 결과가 클라이언트의 예측과 다르면 클라이언트가 서버 상태에 맞춰 다시 보정한다.

이것이 Server Reconciliation과 연결된다.

이런 구조를 사용하면 네트워크 지연이 있더라도 자신의 캐릭터가 입력할 때마다 멈춰 있는 것처럼 보이는 문제를 줄일 수 있다.


Interpolation

다른 플레이어의 움직임을 네트워크 패킷이 도착할 때마다 바로 화면에 표시하면 움직임이 끊겨 보일 수 있다.

그래서 이미 받은 상태 사이를 보간해서 부드럽게 표시할 수 있다.

Snapshot A -> Interpolation -> Snapshot B

서버에서

Position A
Position B

를 받았다면 클라이언트가 그 사이의 위치를 계산해 부드럽게 움직이는 것처럼 보여줄 수 있다.


Serialization

게임 객체나 구조체를 그대로 네트워크로 보낼 수는 없다.

메모리 구조를 네트워크에서 전달할 수 있는 바이트 형태로 변환해야 한다.

이 과정을 Serialization이라고 한다.

Game Object -> Serialization -> Bytes -> Network -> Deserialization -> Game Object

예를 들어

struct PlayerState
{
    int id;
    float x;
    float y;
};

같은 데이터를 네트워크 패킷으로 변환할 수 있다.

실제 게임에서는 데이터 크기와 호환성, 보안, 성능 등을 고려해 자체 바이너리 프로토콜이나 Protocol Buffers 같은 직렬화 방식을 사용할 수 있다.


신뢰성과 순서

UDP를 사용하면 패킷의 도착과 순서를 보장받을 수 없다.

필요하다면 애플리케이션에서 직접 처리할 수 있다.

예를 들어 패킷에 sequence number를 넣는다.

Packet -> Sequence = 101 -> Data = ...

다음 패킷은

Sequence = 102

이런 식이다.

수신 측은 sequence를 보고 패킷의 순서를 판단할 수 있다.

특정 패킷이 반드시 도착해야 한다면 ACK, 재전송, timeout 등의 로직을 추가할 수도 있다.

결국 UDP를 선택했다고 해서 신뢰성을 포기해야 하는 것은 아니다.

필요한 신뢰성 기능을 애플리케이션이 직접 구현할 수 있다는 것에 가깝다.


네트워크 보안

네트워크로 전송되는 데이터는 기본적으로 외부에 노출될 가능성을 고려해야 한다.

웹에서는 TLS를 이용한 HTTPS가 일반적이다.

게임에서도 중요한 인증 정보나 통신 데이터를 적절히 보호해야 한다.

그리고 더 중요한 것은 클라이언트가 보내는 데이터를 무조건 신뢰하지 않는 것이다.

Client -> Request -> Server Validation -> Game Logic

클라이언트는 변조될 수 있다.

따라서 재화, 데미지, 아이템 획득, 이동 가능 여부 같은 중요한 상태를 서버에서 검증하는 구조가 필요하다.


네트워크를 하나의 흐름으로 보기

지금까지의 개념을 하나로 연결하면 대략 다음과 같은 구조가 된다.

Game Client -> Application Data -> Serialization -> TCP / UDP -> IP -> Router -> Internet -> Router -> IP -> TCP / UDP -> Deserialization -> Game Server

웹 서비스라면 조금 다른 형태가 된다.

Browser -> HTTP -> TLS -> TCP -> IP -> Internet -> Server

각 프로토콜이 서로 다른 문제를 담당하고 있다.

HTTP
-> 애플리케이션 수준의 요청 / 응답

TCP / UDP
-> 전송 방식

IP
-> 네트워크에서 목적지까지 전달

Ethernet / Wi-Fi
-> 로컬 네트워크에서 실제 데이터 전달

이 관계를 이해하면 네트워크 용어가 따로따로 외워야 하는 개념처럼 보이지 않는다.


네트워크 문제를 볼 때

네트워크에서 문제가 발생했을 때 무작정 코드를 수정하기보다 어느 계층에서 문제가 발생했는지부터 확인하는 것이 좋다.

애플리케이션 문제인가? -> 직렬화 문제인가? -> TCP / UDP 문제인가? -> IP / Routing 문제인가? -> DNS 문제인가? -> 서버 자체의 문제인가?

예를 들어

도메인으로 접속되지 않음
-> DNS 확인

서버 IP에는 도달하지만 연결되지 않음
-> Port / Firewall / Server 확인

연결은 되지만 데이터가 깨짐
-> Serialization / Protocol 확인

게임이 순간적으로 튐
-> Latency / Packet Loss / Jitter 확인

이런 식으로 원인을 좁혀갈 수 있다.


네트워크를 공부하면서 보는 것

네트워크를 공부할 때 결국 반복해서 등장하는 개념은 다음과 같다.

IP
-> 어디로 보낼 것인가

Port
-> 어떤 서비스와 통신할 것인가

TCP
-> 신뢰성 있는 전송

UDP
-> 빠르고 단순한 데이터그램 전송

DNS
-> 도메인을 네트워크 주소로 해석

HTTP
-> 웹에서 요청과 응답을 주고받는 방식

Socket
-> 애플리케이션의 네트워크 통신 인터페이스

TLS
-> 통신 보호

Router
-> 네트워크 사이에서 패킷 전달

게임 개발에서는 여기에

Latency
Packet Loss
Jitter
Prediction
Reconciliation
Interpolation
Serialization
Server Authority

같은 개념이 추가된다.

결국 네트워크에서 중요한 것은 프로토콜 이름을 많이 외우는 것이 아니다.

데이터가 어디에서 만들어지고, 어떤 형식으로 변환되고, 어떤 경로를 거쳐 전달되며, 전달 과정에서 발생할 수 있는 문제를 어떻게 처리하는지를 이해하는 것이다.

네트워크는 내가 작성한 코드만으로 통제할 수 없는 영역이 많다.

패킷은 늦을 수 있고, 유실될 수 있고, 순서가 바뀔 수 있다. 상대방의 클라이언트는 변조될 수도 있다.

그래서 네트워크 프로그래밍에서는 정상적인 상황만 구현하는 것보다 실패와 지연을 기본적인 조건으로 보고 시스템을 설계하는 것이 중요하다.