최대한 TCP, UDP와 친해지기
- 네트워크
- 2025. 10. 15. 20:09
* 초안입니다.
배경
TCP는 뭐고 UDP는 뭘까
우리가 알기로는 TCP는 느리지만 신뢰성이 좋다고 하고, 그리고 UDP는 느리지만 신뢰성이 좋지 않는다고 한다.
왜 그럴까?
어떤 이유 때문에 TCP는 느릴까?
TCP는 UDP보다 하는일이 굉장히 많다. 예를 들어, 신뢰성과 안정성을 보장하기 위해서는 기능이 많이 필요하다.
다음은 TCP,UDP가 하는일은 다음과 같다고 한다.
📡 TCP (이중 확인, 안정형)
- [연결 준비] → 3-way handshake
- [데이터 전송] → 패킷 전송 후 ACK 수신
- [손실 발생] → 재전송
- [혼잡 발생] → 전송 속도 감소
- [종료] → 4-way handshake로 세션 정리
➡️ 신뢰성 높음, 하지만 왕복 지연(RTT)마다 제어 수행 → 상대적으로 느림
⚡ UDP (던지고 잊기, 불안정형)
- [연결 준비] → 없음
- [데이터 전송] → 그냥 패킷 던짐
- [손실 발생] → 복구 없음
- [혼잡 발생] → 감지 없음
- [종료] → 없음
➡️ 빠르지만, 손실 시 재전송 로직을 직접 구현해야 함 (예: 게임, 스트리밍 등)
이렇게 5가지 과정만 봤는데도 TCP의 과정이 복잡하다고 할 수 있다. 그러면 본격적으로 하나씩 알아보자.
TCP or UDP는 어떻게 연결을 시도 할까?
TCP는 신뢰성을 기반으로 동작한다. 그렇기 때문에 연결을 할때도 엄청나게 신중하게 데이터를 전송을 해야 한다. 그냥 보내면 그 데이터를 어떻게 증명할 수 있을까? 사실 증명할 방법이 없다. (HTTP3같은 경우는 TCP대신 UDP를 사용한다고 한다.)
장치1에서 장치2로 메시지를 전송했다고 해보자. 장치1은 장치2로 메시지를 전송했지만, 그 메시지가 정상적으로 전송이 되었는지 알 수 없다. 그렇기 때문에 장치2는 메시지를 잘 받았다고 ACK를 발송하게 된다.
*ACK는 응답 플레그라고 한다., 요청 플레그는 SIN이라고 부른다.
장치1은 장치2가 메시지가 전송이 잘 되었다는 사실은 알게 되었지만, 장치2는 메시지가 잘 전달이 되었는지 모른다.
그렇기 때문에 장치1은 메시지를 잘 받았다고 응답을 보내줘야 한다.
이것이 3-way-handshake입니다. 이와 마찬가지로 TCP를 연결할때도 이 과정이 들어갑니다. 이걸 다시 그려보면 장비1이 클라이언트고
장비2가 서버입니다.
그렇다면 UDP는 어떨까요? 그냥 메시지를 던집니다.
이제 구조에 대해 알아보자.
구조
TCP는 모든 내용들을 모두 기록을 합니다. 어디에서 전송이 되었고 받았는지에 대해 상세하게 기록 합니다. 그래야 신뢰성을 보장할 수 있기 때문입니다.
잘 보시면 시작 포트, 도착 포트, 순번등등에 대한 정보들을 기록하게 된다.
이걸 통해 TCP의 신뢰성을 높일 수 있습니다. 왜냐하면 정보들이 많다는건, 신뢰성이 높다고 생각할 수 있기 때문입니다.
그렇다면 UDP의 구조는 어떨까요?
요렇게 생겼다고 한다. 그래도 출발지와 도착지는 알려주는거 같군요(당연한 얘긴가)
맞는 비유인지는 잘 모르겠지만 단순히 생각했을때, TCP는 동기와 비슷한거 같구 UDP는 비동기와 비슷한거 같다. 왜냐하면 동기 방식이 느리지만 정확하고, 비동기 방식은 빠르고 정확하지 않는다는 인식이 있다.(물론, 인식만 그럴뿐 실제로는 그렇지 않다.)
TCP와 UDP는 OSI 7계층으로 보면 전송계층에 속합니다. 전송계층은 패킷이라는 단위를 사용하게 되어집니다. 그러니까 TCP/UDP의 목표는 패킷을 목적지에 잘 전달하는게 목적일거라 생각합니다. 그렇다면 어떻게 패킷을 전송할까요?
패킷은 어떻게 전송할까?
당연한말이겠지만 TCP와 UDP의 전송 방법은 다릅니다.
TCP부터 얘기를 해보자면 '가상 회선 패킷 방식'으로 패킷을 전송합니다. 가상 회선? 패킷 방식? 가상 회선이라는건 물리적인 회선이 아니라는 뜻입니다. 모든 프로토콜을 사용이 되어질때 물리적인 회선을 깔 수는 없습니다. 이와 마찬가지로 TCP도 물리적인 회선을 깔지 못하는 프로토콜로 느껴집니다. 그러면, 논리적인 회선이라는 뜻입니다. 우리가 생각을 해야 하는 문제가 TCP는 신뢰성이 높다고 합니다. TCP는 각 패킷에 시퀀스 번호, ACK 번호, 상태 플래그(SYN, ACK, FIN) 등을 넣어서 수신 측이 “이 연결이 어떤 상태인지”를 인식할 수 있습니다.
뭐 위와 같이 가상 회선 패킷 방식이 동작한다는데 솔직히 말하면 잘 모르겠다. 패킷을 가상 회선으로 전송하는 방식을 말하는 정도만 기억하면 되지 않을까 싶다. 그렇다면 UDP는 어떨까? UDP는 비 신뢰성 프로토콜이다. 그렇다는건 가상 회선이 존재하지 않을 수도 있다는 뜻이 된다. UDP는 가상 회선 방식이 아닌 '데이터그램 패킷 교환 방식'으로 사용이 되어집니다. 이름부터 심상치않습니다. 데이터그램은 뭘까요?
이 방식은 각 패킷이 독립적으로 처리되어 목적지까지 도달하는 방식을 말합니다. 결국, 각자 알아서 목적지까지 전송이 되어진다는걸로 이해할 수 있을거 같습니다. 순서 또한 보장하지 못해 특정 데이터를 전송한다고 가정했을때 온전한 데이터로 전송이 되어진다고 장담할 수 없을 거 같습니다. 다음처럼 동작이 된다고 합니다.
동작 흐름은 이런식으로 된다는건 이해하겠는데, TCP건 UDP건 오류가 발생하지 않는리가 없다. 만약에 데이터 전송 도중에 오류가 발생한다면 어떻게 처리를 할까?
오류는 어떻게 처리할까?
프로토콜은 '약속,규약'이라는 뜻을 내포하고 있습니다. 그렇다면 프로토콜을 항상 성공 할까요? 실 생활에 이것을 비유를 해보면 바로 이해할 수 있는데 A라는 사람과 B라는 사람이 서로 계약을 맺었다고 해보자. 그러면 그 계약이 100% 성공할 수 있을까요? 일단 채결까지 갔다고 해도 어디서 문제가 발생할 수 도 있습니다. 계약자의 실수라던지 천지지변과 같은 결과로 갑자기 채결이 실패할 수 있습니다. 이와 마찬가지로 프로토콜도 충분히 오류가 발생할 수 있습니다. 그렇다면 어떻게 처리 할 수 있을까요?
위에서 TCP는 신뢰성을 기반한 프로토콜이라고 했습니다. 그렇다면, 오류를 오류상태로 두면 신뢰성이 떨어지지 않을까요?
제어에는 총 3가지가 존재하였습니다.
흐름제어, 오류제어, 혼합제어입니다.
흐름제어
먼저 흐름제어를 생각해보면 흐름을 제어한다? 이게 무슨 상황일까요? 범람 하거나 흐름이 끊기는 경우를 말하는 걸까요?
송신 버퍼에서 수신 버퍼로 200mb 데이터를 보낸다고 가정해봅시다. (버퍼는 임시 메모리 공간이다.) 하지만 수신 버퍼는 100mb만 수용할 수 있다고 해봅시다. 그러면 100mb는 사용하지 못하는데 애초에 신뢰애가 넘치는 TCP로써는 이게 별로 달갑지는 않을겁니다. 그래서 TCP는 몇 가지 방안을 제시 합니다.
Stop-and-Wait 방식
직역하자면 멈추고 기다려라.. 이걸 풀이하면 패킷1을 전송하고 기다리고 또 전송하는 식의 방법이다.
자바로 예시로 들면
for(int i=0; i<수신장치버퍼.size();i++) {
send(i);
wait();
}
코드로 보인것처럼 하나씩 읽고 전송하는 반복문과 비슷한 느낌을 받을 수 있습니다. 이렇게 되면 송신장치에서 무수히 많은
slide window 방식
이것은 window를 옮기면서 처리하는 방식을 뜻합니다. 어떻게 보면 위 방식의 상위 버전이라고 할 수 있습니다. 위에서는 무식하게(?) 1개씩 보냈다면 이 방식은 N개씩 패킷을 묶어서 전송하는 방식입니다. 뭐 중간에 오류가 터지면 어쩌냐 그럴 수 도 있긴한데 지금은 흐름제어를 설명하는 구간이기 때문에 넘어가도록 하겠습니다.

이걸 비유하면, 양동이에 물을 담아서 옮기는건데 양동이로 물을 옮기게 되면 일일이 수돗꼭지에서 물을 가져오지 않아도 빠른 시기내에 처리할 수 있습니다. 물론 양동이를 옮기는 도중에 발이 걸려 넘어지는건 실수이기때문에 고려하지는 않았습니다. 그렇다면 발이 걸려 물이 쏟아지는 상황에서는 어떻게 처리할 수 있을까요? (어쩌면 우리가 알고 있는 버퍼의 의미가 이거라고 생각할 수 도 있을지도 모르곘다.)
오류제어
발이 걸려 양동이에 있는 물을 쏟아지는 행위는 흐름제어를 통해 막을 수는 없습니다. 왜냐하면 흐름제어는 흐름을 제어하는건데 지금과 같은 상황은 예외적인 상황이기 때문에 제어가 불가능합니다. 그래서 TCP에서는 흐름제어가 아닌 오류제어를 통해 이를 해결을 해야 합니다.
일단 생각을 할 수 있는게 1. 패킷이 손상을 입는 경우 2. 순서가 잘못된 경우
이렇게 2가지를 생각할 수 있을거 같습니다.
일단 첫 번째같은 경우는 패킷자체가 손상을 입은 경우이기 때문에 TCP입장에서는 얘를 어떻게 처리할까요?
TCP헤더에는 체크섬을 통해 데이터의 무결성을 검사합니다. 체크섬은 수신자와 송신자간의 전자싸인정도로 이해하면 될거 같습니다.
만약에 체크썸의 결과가 상이하다면 손상된 패킷이라 판단하여 수신자측에서는 ACK를 발송하지 않게 됩니다. 그렇게 되면 송신자에서는 타임아웃이 발생하게 되어집니다. 타임아웃이 발생했기 때문에 재전송을 수행하여 신뢰성을 보장하게 되어집니다.
그 다음 순서가 잘못된 경우는 어떻게 해야 할까요? 어떻게 보면 TCP입장에서는 첫번째 방법과 크게 다르지 않다고 생각할 수 있습니다. 왜냐하면 결과로만 봐서는 순서가 잘못된 경우도 재전송이 필요하기 때문입니다. 하지만 본질적으로 두개는 상이합니다. 가장 큰 문제는 이번에는 손상된 패킷이 아니라는 점입니다. 이번에는 체크썸이 아니라 시퀀스 번호를 통해 결정이 되어집니다. 만약에 시퀀스 번호가 1,2,3,4번이 존재한다고 해봅시다. 그런데 3번이 누락이 되었다면, 일단 1,2,4번은 전송하게 되어지고 누락된 3번은 재 전송이 되어집니다. 그러면 최종적으로 1,2,4,3이렇게 들어오는게 아닌가요? 라고 할 수 있다. 다행스럽게도 재조합을 통해 순서가 보장이 되어진다고 합니다.
이 방식은 stop-and-wait방식, slide window방식 모두 유효하게 동작합니다.
혼합제어
시간상 제외
그렇다면 UDP는 어떨까?
UDP는 흐름제어나 오류제어같은걸 전혀 하지 않습니다. 일단 보내고 생각하죠.
왜냐하면 신뢰성을 포기하고 속도를 쟁취했기 때문입니다.
이제 데이터 전송까지 맞췄다. 이제 연결을 해제를 시켜야한다. 일반적으로 생각할때, 연결을 할때랑 똑같이 해제도 똑같다고 생각할 수 있다. 하지만 TCP만큼은 다릅니다. TCP의 연결 해제 하는 방법에 대해 알아봅시다.
TCP의 연결해제 하는 방법은 뭘까?
연결은 3-way-handshake이지만 해제는 4-way-handshake입니다. 일단 시간상 GPT에 있는것을 복사해 사용하겠습니다.
- FIN 전송 (송신 측 → 수신 측)
- 송신 측이 “더 이상 보낼 데이터가 없다”라고 알림
- FIN 패킷에는 종료 플래그(FIN)가 세팅되어 있음
- ACK 수신 (수신 측 → 송신 측)
- 수신 측이 FIN을 받았음을 ACK로 확인
- 이때 송신 측은 FIN 발송 완료 상태에 들어감
- FIN 전송 (수신 측 → 송신 측)
- 수신 측도 “나도 더 이상 데이터 없다”라고 FIN 전송
- 이 시점에서 송신 측은 수신 측의 FIN을 기다림
- ACK 수신 (송신 측 → 수신 측)
- 송신 측이 수신 측 FIN을 확인하면 완전히 연결 종료
- 이때 송신 측은 TIME-WAIT 상태에 들어가 잠시 기다림
(혹시 마지막 ACK가 손실될 경우를 대비)
그렇다면 왜 연결과 해제는 서로 상이하게 설계했을까요?
4-way handshake (연결 해제)
- 핵심 목표: 양쪽이 독립적으로 “나는 더 이상 보낼 데이터가 없다”라는 의사를 확인
- 절차:
- 한쪽(FIN 보내는 쪽) → 상대방: FIN (종료 의사)
- 상대방 → FIN 보내는 쪽: ACK (확인)
- 상대방 → FIN 보내는 쪽: FIN (자신도 종료)
- FIN 보내는 쪽 → 상대방: ACK (최종 확인)
- 이유:
- TCP는 양방향 스트림 → 한쪽이 종료해도 다른 쪽은 아직 데이터 전송 가능
- 따라서 양쪽 종료를 독립적으로 확인해야 안전하게 연결 종료 가능
- 이 과정이 바로 4단계(4-way handshake)
마무리
이 정도면 TCP랑 UDP 한 40%정도는 친해진거 같다는 생각이 듭니다. 다만 아쉬운점은 혼합제어를 작성하지 못한점 연결해제를 제 언어로 바꿔서 적지 못한점이 좀 아쉬운거 같습니다. 머릿말에 작성한것처럼 이 글은 초안입니다. 결국 변경이 있을 예정이라는 뜻이죠. 일단 여기서 마무리를 짓을예정입니다. 좀 쉴려구여..
원래 HTTP는 TCP를 채택하였습니다. 왜냐하면 HTTP도 신뢰성이 중요하기 때문입니다. 하지만 HTTP3부터는 UDP를 선정하였습니다. 이 부분도 다뤘으면 좋았을거 같은데 아쉽습니다.