UDP 프로토콜 실전 활용 | 저지연·DNS·게임·스트리밍과 QUIC 연결
이 글의 핵심
UDP는 연결 없이 데이터그램을 보내 지연을 최소화하지만, 신뢰성·순서는 애플리케이션이 설계하고 QUIC은 그 위에 현대적 스택을 얹습니다.
들어가며
UDP(User Datagram Protocol)는 연결 설정 없이 데이터그램을 보내는 경량 전송 프로토콜입니다. TCP가 신뢰성·순서·혼잡 제어를 대신 책임지는 대신, UDP는 “보내기만” 하고 손실·중복·순서 뒤집힘은 애플리케이션(또는 그 위의 QUIC, RTP 등)이 처리합니다. 그래서 DNS, 실시간 게임, VoIP/영상, 모니터링 에이전트처럼 지연 민감이거나 멀티캐스트가 필요한 영역에서 자주 선택됩니다.
이 글은 헤더·포트·체크섬 같은 기본을 밟고, 재전송·순서·MTU를 실무에서 어떻게 다루는지, 그리고 HTTP/3·QUIC이 UDP를 어떻게 쓰는지까지 연결합니다.
이 글을 읽으면
- UDP 헤더 구조와 IPv4/IPv6에서의 체크섬 차이를 설명할 수 있습니다
- C++/Python/JavaScript로 에코·타임아웃이 있는 기본 데이터그램 패턴을 구현할 수 있습니다
- 게임·스트리밍·DNS에서 UDP가 선택되는 이유를 지연·처리량 관점으로 말할 수 있습니다
- QUIC/WebRTC와의 관계를 스택 다이어그램 수준으로 구분할 수 있습니다
목차
프로토콜 개요
역사 및 개발 배경
UDP는 RFC 768(1980)으로 표준화되었고, IP 위의 멀티플렉싱(포트)과 최소한의 무결성(체크섬)을 제공하는 단순함이 철학입니다. “인터넷은 원래 불안정하다”는 전제 위에서, 실시간성이나 브로드캐스트/멀티캐스트 같은 요구는 가벼운 UDP에 맞기 쉽습니다.
OSI 7계층에서의 위치
UDP는 4계층(전송 계층)에 속합니다. IP가 호스트까지 배달하면, UDP 포트가 소켓(프로세스)으로 연결됩니다. 연결 상태는 없고, 각 데이터그램은 독립적입니다.
핵심 특징
| 특징 | 설명 |
|---|---|
| 비연결형 | 송신 전 핸드셰이크가 없음(앱이 필요하면 직접 구현). |
| 비신뢰성 | 네트워크가 드롭·순서 변경을 일으켜도 UDP는 복구하지 않습니다. |
| 데이터그램 경계 | 수신 단위가 전송 단위와 대응되는 경우가 많음(단, OS 버퍼에 따라 여러 번 recv로 나뉠 수 있음). |
| 멀티캐스트 가능성 | TCP와 달리 일대다 전송에 활용하기 쉬운 모델입니다. |
| 오버헤드 작음 | 8바이트 헤더로 TCP보다 가볍습니다. |
동작 원리
헤더 구조(IPv4)
UDP 헤더는 8바이트입니다.
| 필드 | 길이 | 설명 |
|---|---|---|
| Source Port | 16비트 | 송신 포트(선택). |
| Destination Port | 16비트 | 수신 포트. |
| Length | 16비트 | UDP 헤더+페이로드 길이. |
| Checksum | 16비트 | IPv4에서는 선택(0이면 미사용), IPv6에서는 의무. |
체크섬
IPv4에서는 체크섬이 선택이지만, IPv6 UDP에서는 의무입니다. 체크섬은 의사 헤더 + UDP + 페이로드를 포함해 계산됩니다. 애플리케이션 무결성이 더 중요하면 추가로 암호화·MAC을 씁니다.
포트
0~65535 범위의 포트로 호스트 내 프로세스를 구분합니다. 잘 알려진 포트(예: DNS 53)는 IANA 레지스트리에 등록되어 있습니다.
QUIC과의 관계(2026년 기준)
HTTP/3는 QUIC(UDP 위)을 사용합니다. QUIC은 암호화·혼잡 제어·다중 스트림·연결 마이그레이션을 UDP 데이터그램으로 캡슐화해, “UDP의 단순함”과 “TCP에 가까운 신뢰성” 사이의 현대적 절충을 제공합니다.
flowchart TB
subgraph app [애플리케이션]
H3[HTTP/3]
RTP[RTP/실시간 미디어]
DNS[DNS 클라이언트]
end
subgraph mid [프로토콜 스택 예]
QUIC[QUIC + TLS]
UDPR[UDP 소켓]
end
subgraph l3 [네트워크]
IP[IP]
end
H3 --> QUIC
QUIC --> UDPR
RTP --> UDPR
DNS --> UDPR
UDPR --> IP
실전 프로그래밍
C++ (UDP echo 클라이언트 스케치)
// g++ -std=c++17 -O2 udp_client.cpp -o udp_client
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
const char* host = argc > 1 ? argv[1] : "127.0.0.1";
const uint16_t port = argc > 2 ? static_cast<uint16_t>(std::stoi(argv[2])) : 5353;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) { perror("socket"); return 1; }
sockaddr_in peer{};
peer.sin_family = AF_INET;
peer.sin_port = htons(port);
if (inet_pton(AF_INET, host, &peer.sin_addr) != 1) return 1;
const std::string msg = "ping";
if (sendto(fd, msg.data(), msg.size(), 0,
reinterpret_cast<sockaddr*>(&peer), sizeof(peer)) < 0) {
perror("sendto");
close(fd);
return 1;
}
char buf[2048];
socklen_t plen = sizeof(peer);
ssize_t n = recvfrom(fd, buf, sizeof(buf), 0,
reinterpret_cast<sockaddr*>(&peer), &plen);
if (n < 0) { perror("recvfrom"); close(fd); return 1; }
std::cout.write(buf, n);
std::cout << '\n';
close(fd);
return 0;
}
Python 3
#!/usr/bin/env python3
import socket
import sys
def main() -> None:
host = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
port = int(sys.argv[2]) if len(sys.argv) > 2 else 5353
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(3.0)
try:
sock.sendto(b"ping", (host, port))
data, addr = sock.recvfrom(4096)
print(f"from {addr}: {data!r}")
except socket.timeout:
print("timeout: no response", file=sys.stderr)
raise SystemExit(1)
finally:
sock.close()
if __name__ == "__main__":
main()
settimeout: UDP는 응답이 없을 수 있음이 정상 시나리오라 타임아웃 필수입니다.
JavaScript (Node.js dgram)
// node udp_client.mjs
import dgram from "node:dgram";
const host = process.argv[2] ?? "127.0.0.1";
const port = Number(process.argv[3] ?? 5353);
const socket = dgram.createSocket("udp4");
const onMessage = (msg, rinfo) => {
console.log(`from ${rinfo.address}:${rinfo.port}: ${msg.toString()}`);
socket.close();
};
socket.on("message", onMessage);
socket.on("error", (err) => {
console.error(err.message);
socket.close();
process.exitCode = 1;
});
socket.send(Buffer.from("ping"), port, host, (err) => {
if (err) {
console.error(err.message);
process.exitCode = 1;
socket.close();
}
});
setTimeout(() => {
socket.close();
console.error("timeout");
process.exitCode = 1;
}, 3000).unref();
에러 처리·재전송 로직(애플리케이션 레벨)
UDP는 ACK가 없으므로, 요청-응답 패턴이면 일정 시간 내 미수신 시 재전송, 요청 ID로 중복 제거, 지수 백오프를 앱에서 구현합니다. QUIC/RTP는 이런 역할을 프로토콜이 대신합니다.
성능 특성
지연 시간(Latency)
핸드셰이크가 없어 첫 데이터그램을 즉시 보낼 수 있습니다(방화벽·NAT 제외). 실시간 게임·음성에서 “첫 패킷까지의 시간”이 중요할 때 유리합니다.
처리량(Throughput)
UDP 자체는 윈도 기반 혼잡 제어가 없어 무제한으로 쏠 수 있지만, 그렇게 하면 네트워크와 다른 사용자에게 피해를 줍니다. 대용량 전송은 애플리케이션 혼잡 제어 또는 QUIC을 고려해야 합니다.
오버헤드
8바이트 UDP 헤더로 TCP보다 작습니다. 다만 애플리케이션 헤더·암호화가 붙으면 차이는 줄어듭니다.
벤치마크 참고
로컬 루프백에서는 초당 수십만 패킷도 가능하지만, 인터넷에서는 MTU·손실·QoS에 의해 처리량보다 패킷 손실률이 서비스 품질을 가릅니다. 게임/음성은 pps(초당 패킷 수)와 지터가 중요합니다.
실무 활용 사례
DNS
대부분의 DNS 쿼리는 UDP로 전송됩니다(응답이 크면 TCP 폴백). 짧은 요청-응답에 연결 부담이 적은 전형적 사례입니다.
게임
실시간 멀티플레이어는 최신 상태만 중요하고 과거 패킷 재전송이 오히려 방해가 될 수 있어, UDP + 앱 레벨 상태 동기화가 흔합니다.
실시간 스트리밍
RTP는 보통 UDP 위에서 동작하며, 손실은 인코딩·FEC·재전송 전략으로 흡수합니다. WebRTC는 UDP를 사용하며, 상세는 WebRTC 가이드를 참고하세요.
최적화 팁
패킷 크기(MTU)
이더넷 MTU 1500 기준으로 IP+UDP+페이로드가 경로 MTU를 넘지 않게 설계합니다. IP 단편화는 손실 시 전체 재전송 비용이 커지기 쉬워 피하는 편이 좋습니다.
재전송·순서
- 재전송: RTT 측정, 지터 버퍼, 중복 시퀀스 무시를 함께 설계합니다.
- 순서: 시퀀스 번호를 두고, 늦게 도착한 패킷은 버리거나 버퍼링할지 정책을 명확히 합니다.
대역폭 공정성
UDP로 무제한 flood하면 ISP·데이터센터에서 스로틀되거나 인접 TCP 세션을 기아 상태로 만들 수 있습니다. 레이트 리밋과 혼잡 제어는 시민 의식 수준을 넘어 실제 서비스 안정성 문제입니다.
흔한 문제와 해결
흔한 실수와 해결
| 실수 | 결과 | 해결 |
|---|---|---|
| UDP에 TCP처럼 재전송이 있다고 가정 | 앱이 손실을 복구하지 않음 | 요청-응답이면 타임아웃·재시도·ID 중복 제거 설계 |
타임아웃 없이 recv 대기 | 스레드·이벤트 루프가 영구 대기 | settimeout 등으로 상한 설정 |
| 큰 페이로드를 한 번에 전송 | 경로 MTU·단편화 이슈 | 앱 단위 분할과 수신 쪽 재조립 |
| UDP만 열어두고 HTTP/3가 안 됨 | 방화벽이 UDP 443 차단 | 인프라 정책과 폴백(HTTP/2) 확인 |
패킷 손실
Wi‑Fi·모바일·혼잡 링크에서 흔합니다. FEC, 낮은 비트레이트, I-frame 주기 조정, NACK/재전송(지연 허용 시) 등을 선택합니다.
순서 뒤바뀜
멀티패스·라우팅 변화로 발생할 수 있습니다. 시퀀스 번호와 재정렬 버퍼로 해결하거나, 순서 무관 프로토콜로 설계합니다.
NAT·방화벽
UDP는 연결 상태가 없어 NAT가 타임아웃으로 매핑을 지우면 외부에서 역방향 패킷이 실패합니다. 주기적 keepalive, STUN/TURN(WebRTC), QUIC의 연결 유지가 관련됩니다.
마무리
핵심 요약
- UDP는 저지연·단순·멀티캐스트 친화에 강하고, 신뢰성은 앱 또는 상위 프로토콜이 책임집니다.
- DNS·게임·실시간 미디어는 UDP가 주류이며, HTTP/3·QUIC은 UDP 위에 현대적 신뢰성을 올린 예입니다.
- MTU·타임아웃·재전송은 실전에서 반드시 짚고 가야 합니다.
추천 사용 시나리오
- 짧은 요청·실시간·브로드캐스트에는 UDP(또는 QUIC/RTP)가 적합합니다. 신뢰성·순서가 최우선이면 TCP 완전 가이드를 먼저 검토하고, 브라우저 P2P 실시간은 WebRTC 가이드로 이어지세요.