네트워크 2편 - TCP 핸드셰이크, 연결의 비용
작성일: 2026년 4월 13일
티켓팅하는 순간을 생각해보자. 수만 명이 동시에 클릭하는 그 순간, 서버는 데이터를 처리하기도 전에 이미 무너져 있다. 이상한 일이다. 실제로 주고받을 데이터는 아직 한 글자도 오가지 않았는데, 무엇이 서버를 그토록 지치게 만들었을까?
데이터가 오가기 전에 앞서 끝내야 하는 일이 있다. 그 일이 마무리되지 않으면, 거래는 시작조차 못한다.
세상의 모든 거래에는 시작 비용이 있다. 상대를 신뢰해도 되는지 확인하고, 조건을 맞추고, 서로의 준비 상태를 점검하는 시간. 거래의 본질이 신뢰일수록 이 시작 비용은 더 비싸진다.
네트워크도 다르지 않다. 데이터를 안전하게 주고받으려면, 양쪽이 먼저 서로를 확인해야 한다. 패킷이 제대로 도착했는지, 순서가 맞는지, 누락된 게 없는지. 이 모든 신뢰의 전제 조건을 먼저 합의해야 비로소 한 글자라도 보낼 수 있다.
이 합의는 공짜가 아니다. 시간이라는 비용을 반드시 지불해야 한다. 그리고 그 비용은 우리가 흔히 생각하는 것보다 훨씬 비싸다.
TCP의 선택 — 데이터보다 계약이 먼저
섹션 제목: “TCP의 선택 — 데이터보다 계약이 먼저”TCP는 데이터를 보내기 전에 반드시 계약을 먼저 맺는다.
양쪽이 신호를 주고받으며 서로의 준비를 확인하는 절차. 이 절차가 완전히 끝나기 전까지, 실제로 보내려는 데이터는 단 1바이트도 전송되지 않는다. 신뢰를 얻기 위해 속도를 포기한 선택이다.
문제는 이 계약이 “한 번”으로 끝나지 않는다는 점이다. 새 연결이 필요할 때마다, 매번 처음부터 다시 계약을 맺어야 한다. 한 명의 사용자에게 한 번이면 작은 비용이지만, 수만 명이 동시에 접속하는 순간 이 비용은 서버를 무너뜨릴 만큼 거대해진다.
Client Server | | | ———————————— SYN ——————————> | "연결할 수 있어?" | | | <————————— SYN-ACK ————————— | "가능해, 너도 준비됐어?" | | | ———————————— ACK ———————————> | "응, 시작하자" | | | [ 데이터 전송 시작 ] |세 번의 신호가 오가야 비로소 데이터를 주고받을 수 있다. 중요한 점은 데이터를 주고받기 전에 반드시 계약을 맺어야 한다. SYN과 SYN-ACK는 조건 협상이고, ACK는 최종 서명이다.
실제 거래, 즉 데이터 전송은 이 세 단계가 모두 끝난 뒤에야 시작된다.
두 번 청구되는 비용
섹션 제목: “두 번 청구되는 비용”연결을 열 때 — RTT
섹션 제목: “연결을 열 때 — RTT”계약을 맺는 데 걸리는 시간은 물리적 거리에 묶여 있다. 서울에서 미국 서버까지 신호가 한 번 왕복하는 데 약 150ms가 소모된다. 이를 RTT(Round Trip Time)라 부른다.
TCP는 데이터가 서버에 도달하기까지 최소 1.5번의 왕복이 필요하다. 사용자가 클릭하는 순간부터 서버가 그 데이터를 인지하기까지, 이미 225ms(150ms × 1.5)가 증발하는 셈이다.
요청 하나에 225ms. 동시 접속자가 1만 명이면 이 비용이 1만 번 발생한다. 서버 성능을 아무리 높여도 줄일 수 없다. 연결을 맺는 시간은 물리적 거리에 묶인 고정 비용이기 때문이다.
연결을 닫은 뒤 — TIME_WAIT
섹션 제목: “연결을 닫은 뒤 — TIME_WAIT”비용은 계약이 끝난 뒤에도 남는다. TCP는 연결이 종료된 후 포트를 즉시 반환하지 않는다. 최대 2분간 TIME_WAIT 상태로 붙잡아 둔다. 뒤늦게 도착하는 패킷과 새로운 연결이 충돌하는 것을 막기 위해서다.
네트워크 1편에서 사용 가능한 포트가 약 28,000개라고 했다. 초당 500개의 연결이 맺어지고 끊어지는 서버를 생각해보자. 2분이 지나기 전에 TIME_WAIT 상태의 포트가 60,000(500/초 x 120초)개 쌓인다. 한계를 두 배 넘게 초과하는 순간, 서버는 새로운 연결을 받을 수 없는 상태에 빠진다.
연결 종료 후:
Port 5001 [TIME_WAIT ——————————— 2분 ———————————]Port 5002 [TIME_WAIT ——————————— 2분 ———————————]Port 5003 [TIME_WAIT ——————————— 2분 ———————————] ...Port 5028 [TIME_WAIT ——————————— 2분 ———————————]
→ 28,000개 소진. 신규 연결 불가.RTT는 연결을 시작하는 비용이고, TIME_WAIT는 연결을 끝내는 비용이다. TCP는 시작과 끝, 양쪽 모두에서 값을 청구한다.
Reference: Cloudflare Learning: What is round-trip time?
Reference: Cloudflare Learning: What is TCP/IP?
거래 비용 이론과 Keep-Alive
섹션 제목: “거래 비용 이론과 Keep-Alive”1937년, 경제학자 로널드 코스는 이런 질문을 던졌다.
시장에서 거래는 왜 항상 마찰 없이 일어나지 않는가.
모든 거래에는 준비 비용이 따른다. 상대를 찾고, 조건을 협상하고, 계약을 체결하는 과정 자체가 비용이다. 올리버 윌리엄슨은 이를 거래 비용 이론(Transaction Cost Theory)으로 발전시켰다.
Reference: UBS Nobel Perspectives: Oliver Williamson — Transaction Cost Theory
TCP 핸드셰이크가 정확히 그렇다. 모든 연결마다 협상 수수료가 청구되는 구조다. 그리고 거래 비용 이론은 이 구조에 해법을 제시한다.
거래 비용을 줄이는 가장 효과적인 방법은 거래 횟수 자체를 줄이는 것이다.
연결을 빠르게 맺는 게 아니라, 연결을 적게 맺는 것. 이것이 HTTP Keep-Alive의 핵심 논리다. 요청마다 연결을 새로 맺고 끊는 대신, 한 번 맺은 연결을 여러 요청에 걸쳐 재사용한다. 핸드셰이크 비용을 연결 하나가 아닌, 수십 개의 요청으로 분산시키는 것이다.
하지만 Keep-Alive만으로는 부족했다. 한 번 맺은 계약 안에서도 “먼저 들어온 요청이 처리될 때까지 뒤의 요청들이 줄을 서서 기다려야 하는” HTTP 고유의 구조적 한계가 있었기 때문이다.
정리하며
섹션 제목: “정리하며”TCP 핸드셰이크는 ‘신뢰’라는 상품을 얻기 위해 지불해야 하는 가격(Price)이다. RTT는 연결을 열 때 내는 비용이고, TIME_WAIT는 연결을 닫은 뒤에 치르는 대가다. 거래 비용 이론의 언어로 말하자면, TCP는 모든 연결마다 ‘협상 수수료’를 청구하는 프로토콜이다.
2편의 핵심은 연결을 빠르게 맺는 것이 아니라, 연결 횟수 자체를 최소화하는 데 있다.
다음 편에서는 HTTP/1.1이 Keep-Alive로 이 문제를 어떻게 완화했는지, HTTP/2가 멀티플렉싱으로 한 단계 더 나아갔는지, 그리고 왜 HTTP/3가 TCP 자체를 버리는 선택을 했는지를 살펴본다.