"Learning HTTP/2" 책을 보고 정리한 내용입니다.
우리가 매일 사용하는 웹(World Wide Web)은 어떻게 시작되었고, 그 핵심 통신 규약인 HTTP는 어떻게 발전해 왔을까요? 이번 글에서는 웹의 탄생 배경부터 시작하여, 초기 버전의 한계를 극복하고 현재의 웹을 가능하게 한 HTTP 프로토콜의 진화 과정을 따라가 보겠습니다.
시작: 팀 버너스 리의 비전과 HTTP/0.9
1989년, 유럽 입자 물리 연구소(CERN)에서 일하던 팀 버ner스 리(Tim Berners-Lee)는 방대한 연구 정보를 효율적으로 관리하고 공유하기 위한 새로운 시스템을 제안했습니다. 그는 테드 넬슨(Ted Nelson)이 제안했던 두 가지 중요한 개념을 채택했습니다.
- 하이퍼텍스트 (Hypertext): 정보들이 제약 없이 서로 연결되어 사용자가 쉽게 탐색할 수 있는 텍스트.
- 하이퍼미디어 (Hypermedia): 정보가 반드시 텍스트일 필요는 없으며, 이미지, 오디오 등 다양한 형태가 될 수 있다는 개념.
버너스 리는 이 개념들을 바탕으로, 전 세계의 수많은 컴퓨터에서 서버와 브라우저(클라이언트)를 구동하여 누구나 정보에 접근하고 공유할 수 있는 보편적인 시스템(Universal System), 즉 월드 와이드 웹(WWW)을 구상했습니다. 그리고 이 시스템의 핵심 통신 규약으로 HTTP(HyperText Transfer Protocol)의 초기 버전을 설계했습니다.
- HTTP/0.9 (1991년경): 극도의 단순함
- 단 하나의 메서드: GET 메서드만 존재했습니다. 클라이언트는 서버에게 특정 문서를 달라고 요청하는 것 외에는 할 수 없었습니다.
- 헤더(Headers) 없음: 요청이나 응답에 부가적인 정보(메타데이터)를 담을 방법이 없었습니다.
- HTML 전용: 오직 HTML 문서만을 가져오도록 설계되었습니다.
- 상태 코드 없음: 요청 성공 여부를 알 수 있는 방법이 없었습니다. 오류 발생 시 특정 HTML 오류 메시지를 보내는 방식이었습니다.
# HTTP/0.9 요청 예시 (매우 단순) GET /mypage.html
[Q&A]
- Q: "HTTP/0.9는 왜 이렇게 단순하게 만들어졌나요?"
- A: 초기 웹은 주로 연구자들 간의 학술 정보(주로 텍스트 기반 문서) 공유를 목적으로 했습니다. 따라서 복잡한 기능보다는 특정 문서를 가져오는 가장 기본적인 기능만으로도 충분했습니다. 또한, 초기 네트워크 환경과 컴퓨팅 자원을 고려한 단순한 설계이기도 했습니다.
HTTP/1.0 (1996년, RFC 1945): 기능의 추가와 남겨진 숙제
웹이 점차 대중화되면서 HTTP/0.9의 단순함은 한계에 부딪혔습니다. 이미지, 다양한 문서 형식 등 더 풍부한 콘텐츠를 다루고, 통신 상태를 명확히 알 필요성이 커졌습니다. 1995년에는 전 세계적으로 18,000대가 넘는 서버가 HTTP 트래픽을 처리할 정도로 웹은 빠르게 성장했습니다.
이에 따라 HTTP/0.9를 대폭 개선한 HTTP/1.0이 1996년 RFC 1945로 발표되었습니다.
- HTTP/1.0의 주요 개선점:
- 버전 정보: 요청/응답 메시지에 버전 정보(예: HTTP/1.0)가 포함되기 시작했습니다.
- 상태 코드 (Status Code): 서버가 요청 처리 결과를 클라이언트에게 명확하게 알려주는 숫자 코드(예: 200 OK, 404 Not Found)가 도입되었습니다.
- 헤더 (Headers): 요청과 응답에 대한 부가 정보(메타데이터)를 전달할 수 있게 되었습니다. (예: Content-Type, User-Agent)
- 다양한 HTTP 메서드: GET 외에도 POST, HEAD 등의 메서드가 추가되어 더 다양한 동작(데이터 전송, 헤더만 요청 등)이 가능해졌습니다.
- 다양한 콘텐츠 타입: Content-Type 헤더와 MIME 타입을 이용해 HTML 외에 이미지, 파일 등 다양한 종류의 데이터를 전송할 수 있게 되었습니다.
- 조건부 요청, 콘텐츠 인코딩, 리다이렉션, 오류 처리 등 다양한 기능이 추가되었습니다.
- 하지만 여전히 남은 문제점:
- 비연결성 (Connectionless): 기본적으로 요청 하나당 TCP 연결 하나를 맺고 응답 후 바로 끊는 방식이었습니다. 웹 페이지 로딩 시 여러 리소스를 받으려면 매번 TCP 3-Way Handshake를 반복해야 해 비효율적이었습니다. (일부 구현에서 Connection: Keep-Alive 헤더로 개선 시도)
- Host 헤더가 필수가 아니었음: 하나의 IP 주소로 여러 웹사이트를 운영하는 가상 호스팅(Virtual Hosting)을 지원하기 어려웠습니다. 서버는 클라이언트가 어떤 도메인으로 접속했는지 알기 어려웠습니다.
- 빈약한 캐싱(Caching) 옵션: 브라우저나 프록시 서버가 콘텐츠를 효율적으로 캐싱하기 위한 헤더 기능이 부족했습니다.
HTTP/1.0은 웹을 훨씬 풍부하게 만들었지만, 비효율적인 연결 관리와 가상 호스팅 지원 미비는 웹의 폭발적인 성장을 가로막는 큰 문제였습니다.
[Q&A]
- Q: "HTTP/1.0에서 상태 코드와 헤더는 왜 필요했나요?"
- A: 상태 코드는 클라이언트가 요청 결과를 명확히 알 수 있게 해줍니다. 성공인지(2xx), 실패인지(4xx, 5xx), 아니면 다른 곳으로 가야 하는지(3xx) 등을 숫자로 알려주어 클라이언트가 후속 조치를 취할 수 있게 합니다. 헤더는 요청/응답에 대한 중요한 부가 정보를 전달합니다. 예를 들어, Content-Type 헤더는 본문 데이터가 HTML인지, 이미지인지 등을 알려주고, Content-Length는 데이터 크기를 알려주어 메시지 끝을 파악하게 합니다. 이러한 정보 없이는 복잡한 웹 콘텐츠를 제대로 처리하기 어렵습니다.
- Q: "HTTP/1.0의 비연결성(Connectionless) 방식이 왜 비효율적인가요?"
- A: 현대 웹 페이지는 수십, 수백 개의 리소스(HTML, CSS, JS, 이미지 등)로 구성됩니다. 각 리소스를 요청할 때마다 TCP 연결(3-Way Handshake)과 해제(4-Way Handshake)를 새로 수행하면, 실제 데이터 전송 시간보다 연결 설정/해제에 드는 시간과 네트워크 자원(서버 소켓 등) 소모가 훨씬 커집니다. 이는 웹 페이지 로딩 속도를 현저히 느리게 만드는 주요 원인이었습니다.
HTTP/1.1 (1997년~, RFC 2616 -> 7230~7235): 현대 웹의 표준
HTTP/1.0의 문제점을 해결하기 위해 불과 1년여 만에 HTTP/1.1 표준이 발표되었습니다. HTTP/1.1은 매우 성공적인 버전으로, 오랫동안 웹 통신의 표준으로 자리 잡았으며 현재도 널리 사용되고 있습니다.
- HTTP/1.1의 핵심 개선점:
- 지속 연결 (Persistent Connection) 기본 지원: 기본적으로 Connection: keep-alive 동작을 지원합니다. 한 번 맺은 TCP 연결을 명시적으로 닫기 전까지 유지하며 여러 요청/응답을 처리합니다. 이를 통해 TCP Handshake 오버헤드를 크게 줄여 성능과 효율을 대폭 개선했습니다.
- Host 헤더 필수화: 요청 메시지에 Host 헤더를 반드시 포함하도록 규정했습니다. 이를 통해 서버는 클라이언트가 접속하려는 도메인 이름을 알 수 있게 되어, 하나의 IP 주소로 여러 웹사이트를 운영하는 가상 호스팅(Virtual Hosting)이 완벽하게 가능해졌습니다. (마치 아파트 동 호수를 알려주는 것과 같습니다. Nginx의 server 블록이나 Apache의 VirtualHost가 이 헤더를 이용해 요청을 구분합니다.)
- 그 외 주요 추가/개선 기능:
- 캐싱 제어 헤더 확장: Cache-Control, ETag 등 더 정교한 캐싱 제어 메커니즘을 제공하여 불필요한 데이터 전송을 줄였습니다.
- OPTIONS 메서드: 서버가 지원하는 HTTP 메서드 등의 통신 옵션을 클라이언트가 미리 확인할 수 있게 합니다.
- Upgrade 헤더: 현재 연결을 다른 프로토콜(예: WebSocket)로 전환할 것을 제안할 수 있습니다.
- Range 요청: 전체 리소스 중 특정 범위(부분)만 요청하여 받을 수 있게 합니다. (예: 동영상 스트리밍, 파일 이어받기)
- Transfer-Encoding: chunked: 응답 본문의 전체 크기를 미리 알 수 없을 때, 데이터를 덩어리(chunk) 단위로 나누어 보내는 방식. 동적으로 생성되는 콘텐츠 전송에 유용합니다.
- 파이프라이닝 (Pipelining): 하나의 TCP 연결에서 이전 요청의 응답을 기다리지 않고 여러 요청을 연속해서 보내는 기능. 이론적으로는 효율적이지만...
- 파이프라이닝의 문제점:
- HOL(Head-of-Line) Blocking: 여러 요청을 보내도 응답은 반드시 요청 순서대로 받아야 합니다. 만약 첫 번째 요청 처리가 오래 걸리면, 후속 요청들의 응답도 모두 지연됩니다.
- 구현 미비: 인터넷 상의 많은 서버와 프록시가 파이프라이닝을 제대로 지원하지 않거나 버그가 있어, 실제로는 거의 사용되지 못했습니다.
- 파이프라이닝의 문제점:
- 관련 RFC: HTTP/1.1은 RFC 2068에 처음 정의되었고, RFC 2616으로 개정되었으며, 이후 의미/구문/메시징/조건부요청/범위요청/캐싱/인증 등 여러 부분으로 나뉘어 RFC 7230부터 7235까지로 최종 개정되었습니다.
HTTP/1.1은 현대 웹의 기반을 다진 매우 중요한 표준이지만, 웹 페이지가 점점 더 복잡해지고 리소스 수가 늘어나면서 파이프라이닝의 한계 등으로 인해 성능 개선의 필요성이 다시 대두되었습니다.
[Q&A]
- Q: "Host 헤더가 필수화되면서 어떻게 가상 호스팅이 가능해졌나요?"
- A: 이전에는 웹 서버가 IP 주소만으로 요청을 받았습니다. 만약 한 IP 주소에 여러 웹사이트(a.com, b.com)가 있다면, 서버는 클라이언트가 어떤 사이트를 원하는지 알 수 없었습니다. Host 헤더가 필수가 되면서, 클라이언트는 요청 시 Host: a.com 또는 Host: b.com과 같이 원하는 도메인 정보를 명시적으로 보내게 됩니다. 웹 서버는 이 Host 헤더 값을 보고 해당 도메인에 맞는 웹사이트 콘텐츠를 응답으로 보내줄 수 있게 된 것입니다.
- Q: "지속 연결(Persistent Connection)은 어떻게 성능을 개선하나요?"
- A: 웹 페이지 하나를 로딩하는 데 필요한 수십 개의 리소스 요청을 하나의 TCP 연결 위에서 처리할 수 있게 합니다. 각 요청마다 TCP 연결을 새로 맺고 끊는 과정을 생략하므로, 연결 설정/해제에 드는 시간과 자원 낭비를 크게 줄일 수 있습니다. 이는 웹 페이지 로딩 속도를 눈에 띄게 향상시키는 효과를 가져왔습니다.
- Q: "파이프라이닝은 좋은 기능 같은데 왜 잘 안 쓰였나요?"
- A: 가장 큰 이유는 HOL(Head-of-Line) Blocking 문제입니다. 여러 요청을 미리 보내도 응답은 순서대로 받아야 하는데, 첫 응답이 느리면 뒤따르는 모든 응답이 막혀버립니다. 또한, 중간의 프록시 서버들이 파이프라이닝을 제대로 지원하지 않거나 잘못 구현한 경우가 많아 안정성 문제가 있었습니다. 결국 이론적인 장점에도 불구하고 현실적인 문제들로 인해 널리 사용되지 못했습니다.
HTTP/1.1 이후: 성능 개선을 위한 노력
HTTP/1.1은 오랫동안 웹을 지탱해왔지만, 웹 페이지는 점점 더 복잡해졌습니다. 수많은 이미지, 스크립트, 스타일시트 등 하나의 페이지를 구성하는 개체 수가 급증하면서, 한 번에 하나의 요청-응답(또는 제한적인 파이프라이닝)만 처리하는 HTTP/1.1 방식은 점점 더 많은 지연 시간을 유발했습니다. 브라우저들은 이를 극복하기 위해 서버당 여러 개의 TCP 연결(보통 6개)을 동시에 맺는 방식을 사용했지만, 이는 서버와 네트워크에 부담을 주는 임시방편이었습니다.
SPDY: HTTP/2를 위한 발판 (2009년)
2009년, 구글은 HTTP/1.1의 성능 한계를 극복하기 위한 실험적인 프로토콜 SPDY(스피디)를 발표했습니다. SPDY는 HTTP를 대체하는 것이 아니라, HTTP 메시지를 더 효율적으로 전송하는 새로운 방법을 제안했습니다.
- SPDY의 핵심 아이디어 (HTTP/2에 계승됨):
- 다중화 (Multiplexing): 하나의 TCP 연결 위에서 여러 개의 요청과 응답 메시지를 동시에, 순서에 상관없이 주고받을 수 있게 합니다. 이를 통해 HTTP/1.1의 HOL Blocking 문제를 해결했습니다.
- 프레이밍 (Framing): 통신 단위를 텍스트 기반이 아닌, 더 작고 파싱하기 쉬운 바이너리(Binary) 프레임으로 정의했습니다.
- 헤더 압축 (Header Compression): 반복적이고 큰 HTTP 헤더 정보를 압축하여 전송 데이터 양을 줄였습니다.
- (부가 기능) 서버 푸시(Server Push), 요청 우선순위 지정 등
SPDY는 크롬, 파이어폭스 등 주요 브라우저와 구글, 페이스북 등 주요 웹사이트에서 빠르게 채택되며 그 효과를 입증했습니다. 이는 차세대 HTTP 표준 개발의 중요한 기폭제가 되었습니다.
HTTP/2 (2015년, RFC 7540): 성능 혁신
SPDY의 성공에 힘입어, IETF(국제 인터넷 표준화 기구)는 2012년 차세대 HTTP 표준 개발을 위한 워킹 그룹을 재구성했습니다. 이들의 목표는 명확했습니다.
- HTTP/2의 개발 목표:
- 최종 사용자의 체감 지연 시간(Latency)을 실질적으로 개선할 것 (HTTP/1.1 대비).
- HTTP/1.1의 HOL(Head-of-Line) Blocking 문제를 해결할 것.
- 서버에 여러 개의 TCP 연결을 맺을 필요성을 없앨 것.
- TCP 사용 효율성을 높이고 혼잡 제어 동작을 개선할 것.
- HTTP/1.1의 의미 체계(메서드, 상태 코드, URI, 헤더 등)를 유지하여 기존 웹과의 호환성을 확보할 것.
- HTTP/1.1과의 상호 운용 및 전환 방법을 명확히 정의할 것.
- 확장성을 고려할 것.
워킹 그룹은 여러 제안을 검토한 끝에, SPDY를 기반으로 HTTP/2 표준 개발을 시작하기로 결정했습니다. 그리고 2015년 5월, 마침내 RFC 7540으로 HTTP/2가 공식 발표되었습니다.
- HTTP/2의 핵심 특징 (SPDY 계승 및 발전):
- 바이너리 프레이밍 (Binary Framing): 메시지를 바이너리 프레임 단위로 나누어 처리. 파싱 효율 증가, 오류 발생 가능성 감소.
- 다중화 (Multiplexing): 하나의 TCP 연결 내에서 여러 개의 요청/응답 스트림(Stream)을 동시에 인터리빙(Interleaving)하여 처리. HOL Blocking 해결.
- 헤더 압축 (Header Compression - HPACK): 중복되는 헤더 필드를 제거하고, 자주 사용되는 헤더는 정적/동적 테이블을 이용해 인덱스로 표현하여 압축률을 높임 (SPDY의 압축 방식 개선).
- 서버 푸시 (Server Push): 클라이언트가 요청하지 않은 리소스(예: CSS, JS 파일)도 서버가 필요하다고 판단하면 미리 보내줄 수 있는 기능.
HTTP/2는 HTTP/1.1의 의미 체계를 유지하면서 전송 메커니즘을 혁신하여 웹 성능을 크게 향상시켰고, 현재 많은 웹사이트에서 사용되고 있습니다. (참고: HTTP/2는 일반적으로 TLS(HTTPS) 위에서 동작합니다.)
[Q&A]
- Q: "HTTP/2의 다중화(Multiplexing)는 어떻게 HOL Blocking을 해결하나요?"
- A: HTTP/1.1 파이프라이닝은 하나의 연결에서 응답이 순서대로 와야 했기 때문에 앞선 응답이 느리면 뒤가 막혔습니다. HTTP/2 다중화는 하나의 TCP 연결 안에 여러 개의 독립적인 스트림(Stream)을 만듭니다. 각 요청과 응답은 특정 스트림 ID를 가지고 여러 개의 작은 프레임(Frame)으로 나뉘어 전송됩니다. 이 프레임들은 순서에 상관없이 인터리빙되어 전송될 수 있으며, 수신 측에서는 스트림 ID를 보고 각 스트림별로 프레임을 재조립합니다. 따라서 특정 스트림의 응답 처리가 지연되더라도 다른 스트림의 응답 전송에는 영향을 주지 않아 HOL Blocking이 발생하지 않습니다.
- Q: "HTTP/2에서 TCP 연결을 여러 개 맺을 필요가 없는 이유는 무엇인가요? 그게 왜 좋은 건가요?"
- A: HTTP/1.1 환경에서 브라우저가 서버당 여러 연결(보통 6개)을 맺었던 주된 이유는 병렬로 리소스를 다운로드하여 HOL Blocking의 영향을 줄이기 위함이었습니다. HTTP/2는 다중화를 통해 하나의 연결만으로도 여러 요청/응답을 동시에 병렬 처리할 수 있으므로 더 이상 여러 연결을 맺을 필요가 없어졌습니다. 연결 수를 줄이면 ① 서버와 클라이언트의 소켓 자원 소모를 줄이고, ② TCP 연결 설정(Handshake) 오버헤드를 없애며, ③ TCP 혼잡 제어가 더 효율적으로 동작하여 네트워크 자원을 더 잘 활용할 수 있게 됩니다.
[마무리하며]
HTTP 프로토콜은 단순한 문서 교환 규약에서 시작하여, 웹의 발전과 함께 성능 및 효율성 개선을 거듭하며 진화해왔습니다. HTTP/1.0에서 기본적인 기능들을 갖추고, HTTP/1.1에서 지속 연결과 가상 호스팅으로 현대 웹의 기틀을 마련했으며, 마침내 HTTP/2에서는 다중화와 헤더 압축 등 혁신적인 기술을 통해 성능의 한계를 극복했습니다. 이러한 진화의 역사를 이해하는 것은 현재 우리가 사용하는 웹 기술의 동작 원리를 더 깊이 파악하는 데 큰 도움이 될 것입니다. (그리고 이 여정은 UDP 기반의 QUIC을 사용하는 HTTP/3로 계속 이어지고 있습니다!)
'네트워크 > HTTP' 카테고리의 다른 글
HTTP/2 전환 준비: 성능 향상을 위한 체크리스트 (0) | 2025.04.11 |
---|---|
웹 성능의 어제와 오늘: HTTP/1.1의 한계와 극복을 위한 노력들 (0) | 2025.04.11 |
집에서 WSL로 HTTPS(HTTP/2) 서버 운영하기: 단계별 가이드 (0) | 2025.04.10 |