🌍 네트워크

UDP

User Datagram Protocol

비연결형 전송 프로토콜. 빠르지만 신뢰성 없음. 스트리밍, 게임.

📖 상세 설명

UDP(User Datagram Protocol)는 TCP/IP 모델의 전송 계층에서 동작하는 비연결형 프로토콜입니다. TCP와 달리 연결 설정(3-way handshake) 없이 데이터를 즉시 전송하며, 패킷의 순서 보장이나 재전송 메커니즘이 없습니다. 이러한 특성 덕분에 오버헤드가 매우 낮고, 실시간 데이터 전송에 최적화되어 있습니다.

UDP 헤더는 단 8바이트로 구성됩니다(출발지 포트, 목적지 포트, 길이, 체크섬). TCP 헤더가 최소 20바이트인 것과 비교하면 매우 가볍습니다. 패킷당 오버헤드가 적기 때문에 대량의 작은 패킷을 빠르게 전송해야 하는 상황에서 효율적입니다. DNS 쿼리가 UDP를 사용하는 이유도 이 때문입니다.

실시간 스트리밍, 온라인 게임, VoIP(Voice over IP), 화상 회의 등에서 UDP가 선호됩니다. 이러한 애플리케이션에서는 패킷 하나가 유실되더라도 재전송을 기다리는 것보다 다음 데이터를 빨리 받는 것이 더 중요합니다. 예를 들어 화상 통화에서 0.5초 전 음성 패킷을 재전송받는 것보다 현재 음성을 듣는 게 더 자연스럽습니다.

현대 웹에서는 QUIC 프로토콜이 UDP 위에서 동작합니다. HTTP/3는 QUIC을 기반으로 하며, UDP의 속도와 TCP의 신뢰성을 결합했습니다. Google, Cloudflare, Facebook 등 대형 서비스들이 이미 HTTP/3를 적용하고 있으며, 특히 모바일 환경에서 네트워크 전환 시 연결 유지가 더 안정적입니다.

💻 코드 예제

import socket

# ============ UDP 서버 ============
def udp_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server.bind(('0.0.0.0', 9999))
    print("UDP 서버 시작: 포트 9999")

    while True:
        data, client_addr = server.recvfrom(1024)
        print(f"수신: {data.decode()} from {client_addr}")
        server.sendto(b"ACK: " + data, client_addr)

# ============ UDP 클라이언트 ============
def udp_client():
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # UDP는 connect() 없이 바로 전송 가능

    message = "Hello, UDP!"
    client.sendto(message.encode(), ('127.0.0.1', 9999))

    # 타임아웃 설정 (UDP는 응답 보장 없음)
    client.settimeout(2.0)
    try:
        response, _ = client.recvfrom(1024)
        print(f"응답: {response.decode()}")
    except socket.timeout:
        print("응답 없음 - UDP는 신뢰성 보장 안됨!")

    client.close()

# ============ 브로드캐스트 전송 ============
def broadcast_message():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

    # 로컬 네트워크 전체에 메시지 전송
    sock.sendto(b"DISCOVER", ('255.255.255.255', 9999))
    print("브로드캐스트 전송 완료")

# 실행: python server.py (서버) | python client.py (클라이언트)
# netcat으로 UDP 테스트
# UDP 서버 시작 (-u: UDP 모드)
nc -u -l 9999

# UDP 클라이언트로 메시지 전송 (다른 터미널)
echo "Hello UDP" | nc -u 127.0.0.1 9999

# UDP 포트 스캔 (열린 UDP 포트 확인)
nmap -sU -p 53,123,161 192.168.1.1

# UDP 패킷 캡처 (tcpdump)
sudo tcpdump -i eth0 udp port 53

# DNS 쿼리 (UDP 53 포트 사용)
dig @8.8.8.8 google.com +short

# UDP 연결 상태 확인
netstat -anu | grep 9999
ss -anu | grep 9999

# iperf3로 UDP 대역폭 테스트
# 서버
iperf3 -s -u -p 5201

# 클라이언트 (100Mbps로 테스트)
iperf3 -c 192.168.1.100 -u -b 100M -p 5201

# socat으로 UDP 릴레이
socat UDP-LISTEN:9999,fork UDP:192.168.1.100:9999

# 특정 UDP 포트로 데이터 전송 (bash)
exec 3<>/dev/udp/127.0.0.1/9999
echo "Test message" >&3
const dgram = require('dgram');

// ============ UDP 서버 ============
const server = dgram.createSocket('udp4');

server.on('message', (msg, rinfo) => {
    console.log(`수신: ${msg} from ${rinfo.address}:${rinfo.port}`);

    // 응답 전송
    const response = Buffer.from(`ACK: ${msg}`);
    server.send(response, rinfo.port, rinfo.address);
});

server.on('listening', () => {
    const addr = server.address();
    console.log(`UDP 서버 시작: ${addr.address}:${addr.port}`);
});

server.bind(9999);

// ============ UDP 클라이언트 ============
const client = dgram.createSocket('udp4');

const message = Buffer.from('Hello, UDP!');
client.send(message, 9999, '127.0.0.1', (err) => {
    if (err) console.error('전송 실패:', err);
    else console.log('메시지 전송 완료');
});

// 응답 대기 (타임아웃 처리)
const timeout = setTimeout(() => {
    console.log('응답 없음 - 타임아웃');
    client.close();
}, 2000);

client.on('message', (msg) => {
    clearTimeout(timeout);
    console.log(`응답: ${msg}`);
    client.close();
});

// ============ 멀티캐스트 예제 ============
const multicast = dgram.createSocket({ type: 'udp4', reuseAddr: true });

multicast.bind(5000, () => {
    multicast.addMembership('239.255.255.250'); // 멀티캐스트 그룹 참가
    console.log('멀티캐스트 그룹 참가 완료');
});

multicast.on('message', (msg, rinfo) => {
    console.log(`멀티캐스트 수신: ${msg}`);
});

🗣️ 실무에서 이렇게 말하세요

💬 게임 서버 아키텍처 논의에서
"실시간 위치 동기화는 UDP로 처리하고, 채팅이나 결제 같은 중요 데이터는 TCP로 분리해야 합니다. UDP에서 패킷 손실이 발생해도 다음 프레임 위치 정보가 바로 오니까 괜찮아요. 대신 TCP는 재전송 때문에 head-of-line blocking이 생길 수 있으니 별도 연결로 가져가죠."
💬 면접에서
"TCP와 UDP의 가장 큰 차이는 연결 지향성과 신뢰성입니다. TCP는 3-way handshake로 연결을 설정하고 패킷 순서와 재전송을 보장하지만, UDP는 이런 과정 없이 바로 전송합니다. DNS가 UDP를 쓰는 이유는 쿼리가 보통 512바이트 이하로 작고, 응답이 없으면 클라이언트가 재시도하면 되기 때문입니다."
💬 VoIP 서비스 품질 회의에서
"음성 통화에서 200ms 이상 지연되면 사용자가 끊김을 느낍니다. UDP + RTP 조합으로 가면서 지터 버퍼를 50ms 정도 두면 적절할 것 같아요. 패킷 손실률이 1% 이하면 사용자가 거의 인지 못하니까, 품질 모니터링에서 그 기준으로 알림 설정해주세요."

⚠️ 흔한 실수 & 주의사항

중요 데이터를 UDP로만 전송

결제 정보, 로그인 토큰, 저장 데이터 등 유실되면 안 되는 데이터를 UDP로 보내면 안 됩니다. UDP는 패킷 손실을 감지하지 않으므로 데이터가 사라져도 모릅니다. 중요 데이터는 반드시 TCP를 사용하세요.

UDP 패킷 크기 제한 무시

UDP의 최대 데이터그램 크기는 65,507바이트지만, MTU(보통 1500바이트)를 초과하면 IP 단편화가 발생합니다. 단편화된 패킷 중 하나라도 손실되면 전체가 버려지므로, 실무에서는 패킷을 1472바이트(1500-28) 이하로 유지하세요.

방화벽에서 UDP 포트 차단 미고려

많은 기업 방화벽이 TCP는 허용하지만 UDP는 차단합니다. WebRTC나 게임 서버 개발 시 UDP가 막혀있을 경우를 대비해 TCP 폴백 또는 TURN 서버를 준비해야 합니다. STUN 테스트로 UDP 연결 가능 여부를 먼저 확인하세요.

올바른 UDP 사용 패턴

실시간성이 중요한 데이터(게임 위치, 음성, 영상)는 UDP로, 신뢰성이 중요한 데이터(로그인, 결제, 채팅)는 TCP로 분리. 애플리케이션 레벨에서 시퀀스 번호와 타임스탬프를 추가해 순서와 지연을 관리하세요.

🔗 관련 용어

📚 더 배우기