본문으로 건너뛰기
Previous
Next
TCP vs UDP 전송 프로토콜 비교 | 신뢰성·지연·QUIC까지 선택 가이드

TCP vs UDP 전송 프로토콜 비교 | 신뢰성·지연·QUIC까지 선택 가이드

TCP vs UDP 전송 프로토콜 비교 | 신뢰성·지연·QUIC까지 선택 가이드

이 글의 핵심

TCP와 UDP의 연결·신뢰성·순서 보장 차이를 비교합니다. DNS·스트리밍·게임·QUIC(HTTP/3) 실무 관점에서 프로토콜 선택 기준을 정리했습니다.

개인적으로는 이론보다 Wireshark 쪽이야. TCP랑 UDP를 표로 외우다가 “아 맞다 RTO” 같은 말이 입에 붙는 순간, 현장에선 대부분 이미 캡처 떠 있거든. 책 3장 읽는 것보다, 느린 API 한 번 잡는 게 스택이 더 잘 잡힌다. 그래서 이 글도 처음부터 RFC 번호로 시작 안 할 거다. 캡처 창 열고 같이 떠올리는 쪽으로 갈게.

옛날에 있었던 일. 스테이징에서 REST 호출이 간헐적으로 8초씩 걸리는 팀이 있었는데, 애플리케이션 로그엔 “그냥 느림”만 있었어. curl으로 찍으면 가끔 재현되고, DB는 가볍고, 뭐가 느린지는 안 나옴. 누가 Wireshark 켰더니(필터는 tcp port 443 정도) 같은 요청이 올라갈 때 TCP Spurious Retransmission이랑 Dup ACK이 같이 끼어드는 구간이 있었지. 뒤에 보니 VPN 경로 쪽이 깨끗한 공유기랑 MTU·경로가 안 맞아서 중간에서 조각/재전송이 터지는 패턴. 이론으론 “손실 있으면 TCP가 느려진다” 한 줄이면 끝인데, 캡처에선 어느 방향이, 몇 번째 재전산이, RTT가 얼마나 튀는지가 한눈에 잡힌다. 그날의 결론은 “앱이 아니라 경로”였다. 표로 ‘TCP=신뢰성’만 외웠으면 그날 밤엔 sleep 넣는 쪽으로 갔을 거다.

TCP는 말이 길다. 3-way handshake, 시퀀스/ACK, 윈도우, 혼잡 제어, 재전송… 책이 두꺼운 이유다. 옆 동료한테는 “전화 같다”고 말하고 끝내도 되고, “등기다”로 굴려도 됨. 중요한 건 “순서 맞추고, 빠뜨린 거 다시 보내고, 그 대신 지연이랑 지터를 살 수 있다”는 감각. UDP는 가볍다. “도착했는지, 순서가 맞는지”는 앱이 알아서. “전단 던지기” “짧은 질문 하나” 느낌. DNS가 UDP가 많다는 이야기는 여기서 오고, VoIP/게임 쪽이 UDP를 쓰는 이유도 대충 “다음 틱이 오면 이전 틱은 스킵해도 된다” 쪽. 그런데 “UDP가 항상 더 빠르다”는 말은 손실이 없을 때나 성립한다. 캡처에서 UDP가 뚫리면 그냥 없다. TCP는 늦게 오지만(재전송) 끝까지 온다는 쪽.

QUIC(HTTP/3)은 그 사이에 끼인 제3의 현실이다. “TCP vs UDP”만 고르면 2020년대 웹은 설명이 안 된다. UDP 위에 TLS랑 다중 스트림을 합쳐 놓은 축. TCP+TLS 여러 번 오가던 걸 겹쳐서 줄이려는 쪽이니, 이론은 ‘UDP 기반’이 맞는데, 체감은 ‘또 다른 신뢰 전송’에 가깝다. curl --http3 한 번 켜고, 같은 사이트를 2로도 3으로도 뜯어 보면(지원만 된다면) 핸드셰이크 줄어든 게 체감될 때가 있다. 안 되면 그게 네가 아니라 망/방화벽/UDP 443 쪽.

코드는 그냥 감 잡는 용. TCP(스트림)로 HTTPS 한 줄 때려 보면, 앱이 재전송을 몰라도 되는 게 체감으로 온다.

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(("example.com", 443))
# TLS는 ssl.wrap_socket 등으로 별도 적용
sock.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
print(sock.recv(4096))
sock.close()

UDP는 “보내고 끝”이 먼저다. DNS 53으로 한 번.

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(b"query", ("8.8.8.8", 53))
data, addr = sock.recvfrom(512)
print(data)
sock.close()

캡처 잡힐 때 TCPFollow → TCP Stream 해보고, UDP는 한 방으로 끊겨서 “어? 뒤에 응답이 없는데?”가 나오는지 보면 됨. 이론은 그 다음에 맞출 수 있다. RTO, Fast Retransmit, SACK… 용어는 캡처에 붙이면서 외우라고, 처음부터 암기하지 말라고, 나는 본다.

현장에서 자주 뜨는 느낌만 말해볼게. 느린데 “우리 쪽 CPU는 멀쩡하다”면 ss나 Wireshark로 케이스를 쪼갠다. TCP면 재전송·Zero Window·SACK, UDP/QUIC이면 손실·MTU/조각·방화벽. mtr이랑 iperf3“망이 나쁘다 vs 앱이 나쁘다”를 갈라 주는 쪽. 내 의견으론 같은 심볼(타임아웃, 8초)이어도 한 번은 캡처해 보는 게 이득다. “신뢰 vs 속도” 표 백 줄보다, 흰색/검은색 바이트 스트림 한 줄이 설득력 있을 때가 많다.

실무 선택을 한 줄로 못 박으면 — 끝까지 맞는 바이트가 시급하면 TCP(우리는 보통 TLS 얹는다) 쪽, 늦은 건 버려도 되는 프레임이면 UDP 계열(또는 그 위에 QUIC/RTP가 알아서). “HTTP/3 켤까?”는 LB·CDN·UDP 443까지 같이 본다. UDP를 골랐다가 앱이 재전송·순서를 못 맞추는 케이스도 Wireshark엔 “그냥 잘렸다”로만 남는다. 그때 이론서가 말해 주는 뒷문장이 “그래서 QUIC이 있지” 쪽.

나머지는 TCP 쪽, UDP 쪽, HTTP 글이 더 잘 쪼갠다. 요약: 외울 땐 TCP=스트림, UDP=데이터그램, 요즘 웹은 QUIC도 책상에 같이 둬라. 다만 “아는지”는 표가 아니라 캡처로 증명하는 편이 난 편하다. Wireshark 켤 사람 손.