콘텐츠로 이동

네트워크 2편 - TCP 핸드셰이크, 연결의 비용

작성일: 2026년 4월 13일

TCP가 신뢰를 보장하는 방식은 단순하다. 먼저 계약을 맺는 것. 그런데 그 계약이 얼마나 비싼지 생각해본 적 있는가?

티켓팅하는 순간을 생각해보자. 서버는 요청을 처리하기 전에 먼저 해야 할 일이 있다. 클라이언트와 계약을 맺는 것이다. 신호를 주고받고, 준비를 확인해야 한다. 그리고 시간이라는 비용을 지불해야 계약이 성립되고, 실제 데이터가 오간다. 이 계약 비용은 한 번에 최대 수백 밀리초가 소모된다. 만약 수만 명이 동시에 클릭한다면, 서버는 실제 거래(데이터)를 시작하기도 전에 이미 ‘계약서(SYN)’ 더미에 파묻혀 버린다.



이것이 TCP가 신뢰를 보장하는 방식이다. 그리고 그 신뢰는 반드시 값을 지불해야 보장된다.


TCP는 패킷이 제대로 전달됐는지 확인하고, 순서가 뒤바뀌면 재조립하며, 유실되면 재전송한다. 이 모든 신뢰성의 전제가 바로 연결이다. 데이터를 보내기 전에 양쪽이 서로를 확인해야 한다.

Client Server
| |
| ———————————— SYN ——————————> | "연결할 수 있어?"
| |
| <————————— SYN-ACK ————————— | "가능해, 너도 준비됐어?"
| |
| ———————————— ACK ———————————> | "응, 시작하자"
| |
| [ 데이터 전송 시작 ] |

세 번의 신호가 오가야 비로소 데이터를 주고받을 수 있다. 중요한 점은 이 계약 체결 과정이 완료되기 전까지, 우리가 진짜 보내려던 ‘알맹이 데이터(Payload)‘는 단 1바이트도 전송되지 않았다는 것이다. 오직 연결을 확인하기 위한 ‘절차적 비용’에만 소중한 시간이 소모된 셈이다.


세 번 서명하는 사이에 벌어지는 일

섹션 제목: “세 번 서명하는 사이에 벌어지는 일”

RTT — 거리가 비용이 된다

SYN을 보내고 SYN-ACK를 받는 데 걸리는 시간을 RTT(Round Trip Time)라고 한다. 예를 들어 서울에서 미국 서버까지 통신할 때 RTT는 약 150ms다.

TCP 핸드셰이크는 실제 데이터를 실어 보내기까지 최소 한 번의 왕복(1 RTT)이 선행되어야 하며, 서버가 그 데이터를 수신하기까지는 총 1.5번의 왕복(1.5 RTT(SYN(0.5) + SYN-ACK(0.5) + ACK with Data(0.5))이 필요하다. 즉, 사용자가 클릭한 후 서버가 “아, 이 데이터를 처리해야겠구나”라고 인지하는 데만 이미 225ms(150ms × 1.5) 가 증발하는 셈이다.

요청 하나에 225ms. 동시 접속자가 1만 명이면 이 비용이 1만 번 발생한다. 서버 성능을 아무리 높여도 줄일 수 없다. 연결을 맺는 시간은 물리적 거리에 묶인 고정 비용이기 때문이다.

TIME_WAIT — 끝난 연결이 자리를 차지한다

연결이 종료된 뒤에도 포트는 즉시 반환되지 않는다. TCP는 종료된 연결을 최대 2분간 TIME_WAIT 상태로 유지한다. 뒤늦게 도착하는 패킷과 새로운 연결이 충돌하는 것을 막기 위해서다.

네트워크 1편에서 사용 가능한 포트가 약 28,000개라고 했다. 초당 500개의 연결이 맺어지고 끊어지는 서버를 생각해보자. 2분이 지나기 전에 TIME_WAIT 상태의 포트가 60,000(500/초 x 120초)개 쌓인다. 28,000개 한계를 두 배 넘게 초과한 시점에서, 결국 서버는 ‘포트 고갈’ 상태에 빠진다.

연결 종료 후:
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는 시작과 끝, 양쪽 모두에서 값을 치른다.


1937년, 경제학자 로널드 코스는 이런 질문을 던졌다. 시장에서 거래는 왜 항상 마찰 없이 일어나지 않는가. 그의 답은 단순했다. 모든 거래에는 준비 비용이 따른다. 상대를 찾고, 조건을 협상하고, 계약을 체결하는 과정 자체가 비용이다. 올리버 윌리엄슨은 이를 거래 비용 이론(Transaction Cost Theory)으로 발전시켰다.

TCP 핸드셰이크는 이 이론의 네트워크 버전이다. 데이터를 주고받기 전에 반드시 계약을 맺어야 한다. SYN과 SYN-ACK는 조건 협상이고, ACK는 최종 서명이다. 실제 거래, 즉 데이터 전송은 이 세 단계가 모두 끝난 뒤에야 시작된다.

거래 비용 이론이 제시하는 해법은 명확하다. 거래 비용을 줄이는 가장 효과적인 방법은 거래 횟수 자체를 줄이는 것이다. 매번 새 계약을 맺는 대신, 한 번 맺은 계약을 최대한 오래 유지하면 된다.

이것이 바로 HTTP Keep-Alive의 핵심 논리다. 요청마다 연결을 새로 맺고 끊는 대신, 한 번 맺은 연결을 여러 요청에 걸쳐 재사용한다. 핸드셰이크 비용을 연결 하나가 아닌, 수십 개의 요청으로 분산시키는 것이다.

그런데 Keep-Alive만으로는 부족했다. 한 번 맺은 계약 안에서도 “먼저 들어온 요청이 처리될 때까지 뒤의 요청들이 줄을 서서 기다려야 하는” HTTP 고유의 구조적 한계가 있었기 때문이다. 다음 편에서는 이 한계를 해결하기 위해 HTTP가 어떻게 진화해왔는지를 살펴본다.


TCP 핸드셰이크는 ‘신뢰’라는 상품을 얻기 위해 지불해야 하는 가격(Price)이다. RTT는 연결을 열 때 내는 비용이고, TIME_WAIT는 연결을 닫은 뒤에 치르는 대가다. 거래 비용 이론의 언어로 말하자면, TCP는 모든 연결마다 ‘협상 수수료’를 청구하는 프로토콜이다.

결국 최적화의 핵심은 연결을 빠르게 맺는 것이 아니라, 연결 횟수 자체를 최소화하는 데 있다. HTTP는 그 방향으로 진화해왔다.

다음 편에서는 HTTP/1.1이 Keep-Alive로 이 문제를 어떻게 완화했는지, HTTP/2가 멀티플렉싱으로 한 단계 더 나아갔는지, 그리고 왜 HTTP/3가 TCP 자체를 버리는 선택을 했는지를 살펴본다.