Junglog.

14KB 규칙과 최신 웹 프로토콜

웹 성능 최적화의 핵심: 14KB

최정환·2025년 8월 20일·7분 읽기182 views

14KB는 아직도 유효할까: TCP 슬로우 스타트부터 HTTP/3까지

어느 날 유튜브를 보다 웹 페이지의 첫 응답이 약 14KB를 넘으면 로딩이 한 번 더 지연될 수 있다는 내용을 접했다. 새 TCP 연결에서는 서버가 수신 확인(ACK)을 받기 전 초기 혼잡 윈도우만큼만 데이터를 보낼 수 있고, 일반적인 환경에서는 그 크기가 약 14KB라는 설명이었다.

14KB만 넘으면 웹 페이지가 갑자기 느려질 수 있다는 말을 처음 봤을 때는 납득하기 어려웠다. 14KB는 이미지 한 장보다도 작은 크기다. 더구나 HTTP/2와 HTTP/3까지 보급된 지금도 이 숫자를 기준으로 삼아야 하는지 의문이 들었다.

처음에는 오래된 TCP 최적화 팁이라고 생각했다. 관련 RFC를 따라가 보니 반은 맞고 반은 틀렸다. 14KB는 넘으면 안 되는 절대 경계가 아니다. 다만 새 연결에서 서버가 처음 보낼 수 있는 데이터의 예산을 이해하는 데는 여전히 쓸 만한 기준이다.

이 글에서 확인할 질문은 하나다.

14KB라는 숫자는 어디에서 나왔고, HTTP/3에서도 여전히 의미가 있을까?

서버는 처음부터 전속력으로 보내지 않는다

TCP(Transmission Control Protocol)는 패킷의 도착 여부와 순서를 확인하고, 손실된 데이터는 다시 보내는 신뢰성 있는 전송 프로토콜이다. 이 과정에는 ACK(Acknowledgement), 즉 수신 확인이 사용된다.

문제는 새 연결이 시작되는 순간이다. 서버는 현재 네트워크가 얼마나 많은 데이터를 감당할 수 있는지 알지 못한다. 처음부터 대량의 데이터를 밀어 넣었다가 중간 구간의 처리 한도를 넘으면 패킷 손실과 재전송이 늘어난다.

그래서 TCP는 혼잡 윈도우(cwnd, congestion window)만큼만 먼저 보내고, ACK가 도착하면 전송량을 늘린다. 연결 초기에 ACK를 받을 때마다 윈도우를 빠르게 키우는 구간이 슬로우 스타트(Slow Start)다. 이름은 느려 보이지만, 실제로는 안전한 크기에서 시작해 전송량을 공격적으로 늘리는 과정에 가깝다.

택배 터미널에 처음 물건을 맡기는 상황과 비슷하다. 처리 능력을 모르는 터미널에 트럭 백 대를 한꺼번에 보내지 않는다. 먼저 몇 대를 보내 정상적으로 처리되는지 확인한 뒤 다음 물량을 늘린다.

14KB는 어떻게 계산됐을까

과거 TCP의 초기 윈도우는 2~4개의 세그먼트였다. RFC 6928은 이 상한을 10개 세그먼트로 높이는 실험적 제안을 담았다. 흔히 IW10(Initial Window 10)이라고 부르는 값이다.

일반적인 이더넷과 IPv4 환경을 단순화하면 다음과 같이 계산할 수 있다.

text
MTU                         1,500 bytes
- IPv4 기본 헤더               20 bytes
- TCP 기본 헤더                20 bytes
= MSS                         1,460 bytes

초기 윈도우 10 MSS
= 1,460 × 10
= 14,600 bytes
= 14.6 kB
≈ 14.3 KiB

이 값이 흔히 말하는 14KB의 출발점이다. 정확히는 14,600바이트이며, 1KB를 1,000바이트로 보느냐 1,024바이트로 보느냐에 따라 표기가 달라진다.

여기서 한 가지는 바로잡아야 한다. RFC 6928은 표준 트랙 문서가 아니라 Experimental RFC, 즉 실험적 RFC다. 초기 윈도우 10도 모든 연결에 강제되는 고정값이 아니다. 운영체제와 서버 설정, 경로의 최대 전송 단위, 이전 연결에서 학습한 정보에 따라 실제 값은 달라질 수 있다.

초기 윈도우 안에 응답이 들어오면 서버는 추가 ACK를 기다리지 않고 그 응답을 한 번에 보낼 수 있다. 반대로 경계를 넘는 부분은 ACK가 도착해 윈도우가 열릴 때까지 기다릴 수 있다. 따라서 차이가 몇백 바이트뿐이어도, 특정 조건에서는 데이터 왕복 한 번이 추가된다.

다만 이를 “14KB 이하면 페이지가 반드시 첫 RTT에 렌더링된다”라고 이해하면 안 된다. DNS 조회, TCP와 TLS 연결 설정, 서버 처리 시간, 브라우저의 파싱과 렌더링도 필요하다. 14KB가 줄여 주는 것은 이 가운데 초기 응답 전송에 추가될 수 있는 데이터 왕복이다.

중요한 것은 페이지 전체가 아니라 첫 응답의 내용이다

14KB 규칙을 처음 접하면 HTML, CSS, JavaScript, 이미지까지 페이지 전체를 14KB 아래로 줄여야 한다고 생각하기 쉽다. 가능하다면 훌륭하지만, 대부분의 서비스에서는 현실적인 목표가 아니다.

더 실용적인 질문은 이것이다.

처음 전송되는 데이터만으로 사용자가 의미 있는 화면을 볼 수 있는가?

이때 보는 크기는 소스 코드의 파일 크기가 아니라 gzip이나 Brotli로 압축되어 네트워크를 통과하는 크기다. 응답 헤더도 같은 연결의 전송 예산을 사용한다. 연결과 암호화 단계, 동시에 전송되는 다른 리소스까지 고려하면 본문에 온전히 14,600바이트를 쓸 수 있다고 보기도 어렵다.

그래서 초기 HTML에는 다음 내용이 먼저 오는 편이 좋다.

  • 문서의 기본 구조와 제목
  • 첫 화면의 핵심 콘텐츠
  • 첫 화면을 그리는 데 필요한 최소 CSS
  • 반드시 필요한 경우에만 작은 초기 스크립트

반대로 첫 화면 아래의 이미지, 분석 스크립트, 채팅 위젯, 무거운 상호작용 코드는 뒤로 미룰 수 있다. 핵심은 모든 것을 14KB 안에 욱여넣는 것이 아니라, 앞부분의 바이트를 렌더링 가능한 정보로 채우는 것이다.

RTT(Round Trip Time)가 30밀리초인 환경에서는 데이터 왕복 한 번이 추가되어도 체감이 작을 수 있다. RTT가 300밀리초라면 같은 한 번이 약 300밀리초의 대기로 이어질 수 있다. 모바일망처럼 지연과 손실이 큰 환경에서 초기 전송량이 더 중요해지는 이유다.

HTTP/2가 이 문제를 없애지는 못했다

HTTP/2는 하나의 TCP 연결에서 여러 요청과 응답을 스트림으로 나눠 동시에 처리한다. HTTP/1.1에서 여러 연결을 만들거나 요청을 순서대로 기다리던 문제를 크게 줄였다.

하지만 아래쪽 전송 계층은 여전히 하나의 TCP 바이트 스트림이다. TCP 패킷 하나가 손실되면 그 뒤의 데이터가 도착했더라도, 빠진 바이트가 재전송될 때까지 애플리케이션에 순서대로 전달할 수 없다. 서로 다른 HTTP/2 스트림도 같은 TCP 연결 위에 있으므로 함께 멈출 수 있다. 이를 전송 계층의 HOL(Head-of-Line) 블로킹이라고 한다.

HTTP/2의 멀티플렉싱은 요청을 효율적으로 섞어 보내는 방법을 바꿨지만, 새 TCP 연결의 혼잡 제어까지 없애지는 않았다. 따라서 초기 전송 예산이라는 관점도 그대로 남았다.

HTTP/3에서도 14KB의 직관은 남는다

HTTP/3는 QUIC을 전송 계층으로 사용한다. QUIC은 UDP 위에서 신뢰성, 암호화, 혼잡 제어와 여러 스트림을 구현한다.

가장 눈에 띄는 변화는 스트림 간 HOL 블로킹을 줄였다는 점이다. 어떤 패킷이 손실되면 그 데이터를 담고 있던 스트림은 재전송을 기다리지만, 관련 없는 다른 스트림은 계속 진행할 수 있다. 단, 한 QUIC 패킷에 여러 스트림의 데이터가 함께 들어 있었다면 그 스트림들은 모두 영향을 받을 수 있다.

그렇다고 혼잡 제어가 사라진 것은 아니다. RFC 9002는 QUIC도 새 연결을 슬로우 스타트로 시작하며, 초기 혼잡 윈도우로 최대 데이터그램 크기의 10배를 권장한다. 계산식은 TCP의 IW10과 같은 계보에 있고, 일반적인 최대 데이터그램 크기에 따라 초기 예산은 대략 12~14KB대가 된다.

즉, HTTP/3가 바꾼 것은 “손실 하나가 모든 스트림을 멈추는가”이지, “새 경로에서 처음부터 데이터를 무제한으로 보낼 수 있는가”가 아니다. 14KB라는 정확한 숫자보다 첫 전송량이 제한되어 있다는 원칙이 남는다.

0-RTT도 무제한 전송을 뜻하지 않는다

QUIC의 0-RTT는 연결한 적이 있는 서버에 클라이언트가 이전 세션 정보를 이용해 핸드셰이크가 끝나기 전부터 애플리케이션 데이터를 보낼 수 있게 한다.

여기에도 조건이 있다.

  • 첫 방문이 아니라 이전 연결 정보가 있는 재접속이어야 한다.
  • 클라이언트와 서버가 0-RTT 사용을 지원하고 서버가 이를 받아들여야 한다.
  • 재전송 공격 가능성이 있어 아무 요청이나 실어 보내면 안 된다.
  • 요청을 일찍 보낼 수 있다는 뜻이지 서버 응답이 혼잡 윈도우의 제한을 받지 않는다는 뜻은 아니다.

따라서 0-RTT는 연결 설정 지연을 줄이는 기능이고, 14KB 규칙은 초기 데이터 전송량을 바라보는 기준이다. 둘은 같은 문제를 해결하는 기능이 아니다.

실제 최적화에서는 14KB보다 측정이 먼저다

14KB는 목표를 세우기 좋은 숫자지만, 이를 맞추는 것 자체가 목적이 되면 안 된다. 실제 초기 윈도우가 더 클 수도 있고 작을 수도 있으며, 재사용된 연결에서는 상황이 달라진다. HTML을 억지로 줄이다가 유지보수성과 접근성을 해치는 것도 좋은 선택이 아니다.

실무에서는 다음 순서가 더 현실적이다.

  1. 새 연결과 느린 네트워크 조건에서 응답 흐름을 측정한다.
  2. 압축된 HTML과 응답 헤더가 초기 전송에서 어느 정도를 차지하는지 확인한다.
  3. 첫 화면에 필요하지 않은 CSS와 JavaScript의 실행·전송을 늦춘다.
  4. 첫 화면 아래의 이미지에는 지연 로딩을 적용하고, 이미지 크기와 포맷을 최적화한다.
  5. 캐시와 연결 재사용, CDN(Content Delivery Network), HTTP/3 지원을 함께 점검한다.
  6. 전송 크기만 보지 않고 실제 사용자 환경의 렌더링 시점도 확인한다.

SSR(Server-Side Rendering)은 브라우저가 사용할 HTML을 먼저 받을 수 있게 도와주지만, 그 자체로 응답 크기를 줄이지는 않는다. 지연 로딩 역시 첫 화면 밖의 리소스를 미루는 방법이지 초기 HTML을 자동으로 작게 만드는 기능은 아니다. 각각 해결하는 문제가 다르다.

정리

  1. 14KB는 일반적인 TCP 초기 윈도우 10 MSS에서 나온 약 14,600바이트의 경험칙이다.
  2. 절대적인 물리 법칙이나 모든 서버가 지켜야 하는 고정값은 아니다.
  3. 핵심은 페이지 전체 크기보다 초기 전송 데이터가 의미 있는 렌더링으로 이어지는지다.
  4. HTTP/2는 멀티플렉싱을 제공하지만 TCP의 혼잡 제어와 스트림 간 HOL 블로킹은 남는다.
  5. HTTP/3는 스트림 간 HOL 블로킹을 줄였지만 QUIC 역시 초기 혼잡 윈도우를 사용한다.
  6. 0-RTT는 재접속의 핸드셰이크 지연을 줄일 뿐, 혼잡 제어를 없애지 않는다.
  7. 14KB를 목표로 삼되 최종 판단은 실제 전송 흐름과 사용자 환경의 측정값으로 해야 한다.

14KB는 지켜야 할 마법의 숫자가 아니었다. 대신 브라우저가 첫 화면을 그리기 전에 어떤 바이트를 먼저 보내야 하는지 묻게 만드는 꽤 좋은 질문이었다.

참고 자료

이 글의 출발점이 된 자료를 소개한 ThePrimeTime에 감사한다.