Uppdaterad för 1 månad sedan
1984년, 존 네이글은 초기 인터넷이 작은 패킷들로 넘쳐나는 것을 지켜봤다. Telnet 세션에서 타이핑하는 사용자는 키 입력마다 패킷 하나를 생성했다—단 1바이트의 데이터가 58바이트의 헤더에 감싸져 전송되었다. 네트워크는 전체 노력의 98%를 봉투에, 2%만을 편지 내용에 쓰고 있었다.
네이글의 해법은 우아했다: 수신 측이 이전 데이터를 확인 응답하거나, 새로운 데이터가 충분히 쌓여 제대로 된 패킷 하나를 채울 때까지 작은 쓰기 작업들을 버퍼에 모아두는 것이다. 이 알고리즘은 애플리케이션 코드를 한 줄도 바꾸지 않고 패킷을 쏟아내던 애플리케이션들을 효율적으로 탈바꿈시켰다. 패킷 발생률을 최대 90%까지 줄였다.
그것은 사십 년 전의 일이다. 이제 네트워크는 대역폭이 충분하지만, 사용자들은 지연에 훨씬 민감해졌다. 초기 인터넷을 구한 그 최적화가 이제는 현대 애플리케이션이 필요로 하는 것과 정면으로 충돌한다. 네이글 알고리즘이 언제 도움이 되고 언제 해가 되는지를 이해하는 것이, 반응성 좋은 애플리케이션과 이유를 알 수 없이 느릿느릿한 애플리케이션의 차이를 만든다.
네이글 알고리즘의 작동 방식
모든 TCP 패킷에는 오버헤드가 따른다: IP 헤더 20바이트, TCP 헤더 20바이트, 이더넷 프레이밍 18바이트. 애플리케이션이 단 1바이트를 전송할 때, 그 1바이트는 58바이트짜리 패킷에 담겨 이동한다—5,700%의 오버헤드 비율이다.
네이글 알고리즘은 단순한 규칙을 구현한다:
- 데이터가 최대 세그먼트 크기(MSS) 패킷을 채울 만큼 크다면, 즉시 전송
- 전송 중인 미확인 응답 데이터가 없다면, 즉시 전송
- 그렇지 않으면, ACK가 도착하거나 전체 패킷이 쌓일 때까지 데이터를 버퍼에 유지
첫 번째 쓰기 작업은 항상 즉시 전송된다. 이후의 작은 쓰기 작업들은 수신 측이 이미 전송 중인 데이터를 확인 응답할 때까지 기다린다. ACK가 도착하면, 버퍼에 쌓인 모든 데이터가 함께 전송된다.
이 방식은 네트워크 상황에 맞게 스스로 조절된다. ACK가 빠른 네트워크에서는 버퍼링이 최소화된다. ACK가 지연되는 느린 네트워크에서는 더 적극적인 일괄 처리가 일어난다—바로 대역폭이 가장 중요한 상황에서.
네이글 알고리즘이 해결한 문제
1980년대 인터넷의 대역폭은 오늘날과 비교하면 턱없이 부족했다. Telnet 같은 애플리케이션은 그 사실을 모른 채 엄청난 낭비를 유발하고 있었다. 개발자들은 애플리케이션 관점에서 합리적인 코드를 작성했다—데이터가 준비되면 전송하라—하지만 네트워크 수준의 파급 효과는 이해하지 못했다.
원격 터미널 세션에서 키를 누를 때마다 각자의 패킷이 생성되었다. 사용자가 "ls -la"를 입력하면 하나로 충분할 것을 여덟 개의 패킷이 만들어질 수 있었다. 수천 명의 사용자를 곱하면, 네트워크는 대부분의 용량을 헤더에 써버렸다.
네이글 알고리즘은 이를 투명하게 해결했다. 애플리케이션 변경이 필요 없었다. TCP 스택이 그저 작은 쓰기 작업들을 자동으로 일괄 처리하기 시작했고, 패킷 발생률은 극적으로 떨어졌다. 대역폭이 제한된 네트워크에서 이는 혁신적인 변화였다.
TCP_NODELAY로 비활성화하기
TCP_NODELAY 소켓 옵션은 TCP에게 네이글 알고리즘을 버리고 크기에 상관없이 모든 쓰기 작업을 즉시 전송하라고 지시한다. 패킷은 더 많아지고 오버헤드도 늘어나지만, 즉각적인 전달이 보장된다.
한번 설정되면, TCP_NODELAY는 소켓의 수명이 다할 때까지 활성 상태를 유지한다. 자동으로 다시 활성화되는 일도, 조건부 동작도 없다. 모든 쓰기 작업이 하나의 패킷이 된다.
비활성화해야 할 때
실시간 게임이 전형적인 사례다. 플레이어의 입력은 지금 당장 서버에 도달해야 한다. 왕복 지연을 기다릴 여유가 없다. 플레이어는 30~50ms 이상의 지연을 체감한다. 다음 입력을 보내기 전에 ACK를 기다리면 게임이 느리고 반응 없는 것처럼 느껴진다.
원격 데스크톱 프로토콜은 마우스 위치를 즉시 업데이트해야 한다. 일괄 처리 지연이 발생하면 커서가 사용자의 손 움직임보다 뒤처지게 되고—이 어색한 괴리감이 원격 시스템을 고장난 것처럼 느끼게 만든다.
금융 거래는 마이크로초 단위로 움직인다. 네이글의 지연은 단순한 불편함이 아니라, 금전으로 환산되는 경쟁상의 불이익이다.
SSH와 터미널 에뮬레이터는 대화형 세션에서 TCP_NODELAY의 혜택을 받는다. 현대 네트워크는 대역폭이 충분하므로 효율성 향상은 미미하지만, 지연 감소 덕분에 원격 셸이 로컬처럼 느껴진다.
HTTP 서버는 동적 콘텐츠에 대해 TCP_NODELAY를 자주 활성화한다. 응답을 점진적으로 스트리밍할 때, 사용자 경험 면에서 즉각적인 전송이 일괄 처리보다 낫다.
대용량 데이터 전송은 예외다. 파일 다운로드, 백업, 동기화는 네이글 알고리즘을 유지해야 한다. 메가바이트 단위로 데이터를 이동할 때는 꽉 찬 패킷이 중요하고, 지연은 문제가 되지 않는다.
지연된 ACK 문제
네이글 알고리즘은 또 다른 TCP 최적화인 지연된 확인 응답(delayed ACK)과 불운한 상호작용을 일으킨다. 수신 측은 모든 패킷에 즉시 ACK를 보내지 않는다—발신 데이터에 ACK를 편승시키거나 여러 패킷을 한꺼번에 확인 응답하기를 기대하며 최대 200ms까지 기다린다.
두 최적화가 모두 활성화되면, 서로 눈치만 보는 상황이 만들어진다. 발신 측은 다음 작은 패킷을 보내기 전에 ACK를 기다린다. 수신 측은 응답 데이터에 편승시키려고 ACK 전송을 미룬다. 둘 다 서로를 배려하고 있다. 둘 다 상황을 악화시키고 있다.
이 문제는 요청-응답 프로토콜에 특히 심각한 영향을 미친다. 클라이언트가 작은 요청을 보낸다. 네이글 알고리즘은 ACK를 기다리며 후속 데이터를 붙잡아 둔다. 수신 측은 응답에 편승시키려고 ACK를 미룬다. 그런데 요청이 완료되기 전까지는 응답을 생성할 수 없다. 모두가 기다린다.
해결책은 클라이언트 측에서 TCP_NODELAY를 사용하는 것이다. 일부 시스템은 지연된 확인 응답을 일시적으로 비활성화하는 TCP_QUICKACK을 제공하지만, TCP_NODELAY가 더 널리 사용되고 이해되어 있다.
현대의 기본값
1984년 이후로 판도가 완전히 뒤바뀌었다. 대역폭은 저렴하다. 지연은 비싸다. 사용자의 기대치는 "작동한다"에서 "즉시 작동한다"로 이동했다.
대화형 작업이라면 무엇이든, 네이글 알고리즘을 비활성화하라. 대역폭 오버헤드는 현대 네트워크에서 사실상 무시할 수 있는 수준이다. 지연 개선은 누구나 체감할 수 있다.
대용량 전송이라면, 활성화를 유지하라. 대용량 데이터를 이동할 때는 효율성이 여전히 중요하고, 개별 패킷을 기다리는 사용자가 없다.
HTTP/2와 HTTP/3의 경우, 이 문제는 해당 사항이 없다. 이 프로토콜들은 자체적인 멀티플렉싱과 우선순위 지정을 구현하므로, TCP 수준의 일괄 처리는 불필요하다.
새로운 프로토콜을 설계할 때, 패킷 효율성을 애플리케이션 계층에 녹여내라. 현대 프로토콜은 의미론적 경계를 이해하는 곳에서 메시지를 일괄 처리한다—TCP의 맹목적인 바이트 스트림 병합보다 훨씬 스마트한 접근이다.
현실적인 지연 환경에서 테스트하라. 네이글의 동작은 왕복 시간에 크게 의존한다. 로컬호스트에서 잘 작동하는 것이 인터넷을 경유하면 다르게 동작할 수 있다.
네이글 알고리즘에 관한 자주 묻는 질문
모두가 TCP_NODELAY를 사용하면 왜 네트워크 혼잡이 생기지 않나요?
TCP의 혼잡 제어는 네이글 알고리즘과 독립적으로 동작한다. TCP_NODELAY가 활성화되어 있어도, TCP는 패킷 손실과 네트워크 피드백에 따라 전송 속도를 조절한다. 더 많은 패킷을 보내겠지만, TCP가 네트워크를 과부하 상태로 내버려두지 않는다—혼잡이 감지되면 여전히 속도를 줄인다.
웹 애플리케이션에는 항상 TCP_NODELAY를 활성화해야 하나요?
동적 콘텐츠를 제공하는 HTTP/1.1 서버의 경우, TCP_NODELAY가 도움이 되는 경우가 많다. HTTP/2와 HTTP/3의 경우, 이 프로토콜들이 자체적으로 일괄 처리를 담당하므로 차이가 크지 않다. 주로 대용량 응답(이미지, 파일)을 전송하는 서버라면 네이글 알고리즘을 유지해도 괜찮다. 확신이 없다면, 현실적인 네트워크 조건에서 벤치마크를 실행해 보라.
일부 쓰기 작업에는 네이글 알고리즘을 활성화하고 다른 작업에는 비활성화할 수 있나요?
직접적으로는 불가능하다—TCP_NODELAY는 소켓 수준의 설정이다. 하지만 전송 전에 애플리케이션 계층에서 직접 쓰기 작업을 일괄 처리하거나, 서로 다른 트래픽 유형에 대해 다른 설정의 별도 연결을 사용하는 방식으로 유사한 효과를 낼 수 있다.
네이글 알고리즘이 UDP에서도 사용되나요?
아니다. UDP는 확인 응답이나 연결 상태의 개념이 없으므로, 네이글 알고리즘이 적용되지 않는다. UDP의 모든 전송은 즉시 패킷이 된다. UDP 애플리케이션에서 일괄 처리가 필요하다면, 직접 구현해야 한다.
Var den här sidan till hjälp?