본 내용은 "HTTP 완벽 가이드" 내용을 참고하여 기록한 정리본입니다.
사전지식: 커넥션 헤더의 오해
커넥션 헤더의 내용
- 두 개의 인접한 HTTP 애플리케이션 사이에서, “현재 맺고 있는 커넥션”에만 적용할 옵션을 지정하고자 할 때 사용하는 헤더.
- 예를 들어, 특정한 임시 헤더(
meter
)나 비표준 옵션(bill-my-credit-card
), 연결 종료(close
) 등에 대한 정보를 전달한다.
meter
같은 헤더는 다른 커넥션으로 전달되면 안 된다.bill-my-credit-card
와 같은 옵션을 선택할 수 있다.- 트랜잭션이 끝나면 커넥션을 끊겠다는 의사를
close
로 밝힐 수 있다.
커넥션 헤더의 동작 방식
- HTTP 헤더 필드 명
- “이 커넥션에만” 해당되는 헤더 이름을 지정한다. 예:
Connection: meter
- “이 커넥션에만” 해당되는 헤더 이름을 지정한다. 예:
- 임시적인 토큰 값
- 특정 비표준 옵션을 적용하기 위한 임시 토큰을 담는다. 예:
Connection: bill-my-credit-card
- 특정 비표준 옵션을 적용하기 위한 임시 토큰을 담는다. 예:
- close
- 작업 완료 후 커넥션을 종료할 것임을 알린다.
- 커넥션 헤더에 지정된 모든 헤더들은 홉 바이 홉(hop-by-hop) 성질을 띠며, 프록시나 게이트웨이를 통해 다음 단계로 전송되어서는 안 된다.
커넥션 헤더에 기술되어 있지 않더라도, hop-by-hop 헤더인 것들
Proxy-Authenticate
Proxy-Connection
Transfer-Encoding
Upgrade
이러한 헤더들은 이름 그대로 프록시나 특정 중간 노드와의 연결(hop)에서만 의미가 있는 헤더다.
HTTP 커넥션 관리: HTTP는 어떻게 커넥션을 관리하는가?
순차 트랜잭션 처리의 문제점
- HTTP 요청마다 커넥션을 맺었다 끊었다 반복한다면, 불필요한 지연이 늘어난다.
- TCP 연결 시 발생하는 3-way 핸드셰이크, slow start 지연 등은 전체 성능을 떨어뜨린다.
- 사용자 입장에서는 여러 개의 리소스(예: 이미지)가 동시에 로드되지 않으면 느리게 느낄 수 있다.
커넥션 관리 최적화 기법
- 병렬 커넥션
- 여러 개의 TCP 커넥션을 통해 동시에 HTTP 요청을 보내는 방식.
- 지속 커넥션
- 한 번 맺은 TCP 커넥션을 여러 HTTP 트랜잭션에 재사용해 연결/해제 오버헤드를 줄이는 방식.
- 파이프라인 커넥션
- 하나의 지속 커넥션 위에서 연속된 HTTP 요청들을 순서대로 겹쳐 보내는 방식.
- 멀티플렉스 커넥션
- HTTP/2.0부터 도입된 기술로, 단일 커넥션을 여러 스트림으로 나눠 동시에 요청과 응답을 주고받는다.
병렬 커넥션: 병렬로 받기
여러 개의 리소스를 각각의 TCP 커넥션으로 병렬 내려받기
- 여러 리소스를 각각 독립된 TCP 커넥션으로 동시에 요청함으로써, 병렬 다운로드를 구현한다.
병렬 커넥션의 이점
- 대역폭이 남아있다면, 나머지 대역폭을 다른 객체 다운로드에 활용할 수 있다.
- 나중에 설명할, 하나의 커넥션으로 여러 데이터를 조회하는 파이프라인 커넥션과는 다르다.
- 대역폭에 여유가 있어야 확실한 이점을 누릴 수 있다.
병렬 커넥션의 허점
- 대역폭이 부족하면, 병렬 연결을 열어도 성능 이점이 사라진다.
- 커넥션을 여러 개 열면 그만큼 서버·클라이언트 리소스(메모리, CPU) 소모량이 늘어난다.
- 불필요하게 많아진 커넥션은 오히려 성능 문제를 초래할 수 있다.
서버는 다양한 클라이언트와 커넥션을 맺어야 함
- 대부분 브라우저는 특정 서버 또는 프록시당 최대 6개 정도의 병렬 커넥션을 사용한다(HTTP/1.1 기준).
- 서버 과부하를 줄이기 위함이다.
- 서버는 특정 클라이언트가 과도한 커넥션을 생성하면, 임의로 커넥션을 끊을 수 있다.
사용자의 체감
- 여러 다운로드가 동시에 일어나는 것을 눈으로 확인하면, “더 빠르다”는 인상을 준다.
- 이는 실제로 더 빠르지 않더라도, "사용자가 그렇게 느낀다"는게 중요한 관점이다.
- 따라서 보통 다중 객체를 병렬 로드하는 방식을 선호하게 된다.
지속 커넥션: 커넥션 연결 과정 단축 1
커넥션을 새로 열면 생기는 문제
- 사이트 지역성(locality) 개념을 활용하기 어려워진다.
- 일반적으로 리소스는 한 서버에서 여러개를 받아올 일이 많다.
- 한 번 연결한 서버에 재접속할 때, 이미 준비된 TCP 상태를 활용하면 빠른 응답이 가능하지만, 매번 새 커넥션을 맺으면 이점을 살리지 못한다.
- 각 트랜잭션마다 커넥션을 맺고 끊으면, 연결/해제에 드는 시간과 대역폭이 소모된다.
- 새로운 커넥션은 무조건 TCP slow start로 시작하여, 초기에 성능 저하가 발생한다.
- 병렬로 열 수 있는 커넥션 수도 제한되어 있다.
튜닝된 커넥션
- 이미 slow start 단계를 지나 상대적으로 빠른 전송 속도를 낼 수 있는 커넥션을 말한다.
- “웜(warm) 상태”*의 TCP 커넥션이라 할 수 있으며, 이를 재활용하면 성능상의 이점이 크다.
HTTP/1.0+ Keep-Alive 커넥션
특징
- 원래 HTTP/1.0에는 명시되지 않은, 비표준 확장 방식으로 시작되었다.
Connection: keep-alive
헤더를 사용해, 여러 트랜잭션을 연속 처리할 수 있게 한다.- HTTP/1.1의 명세와는 조금 다르며, 구조적 결함이 있어 명확히 표준으로 자리 잡지는 않았다.
- 예를 들어
Connection: keep-alive, timeout=120, max=5
처럼 쉼표로 구분된 옵션을 지정하기도 했다.- 5번의 추가 트랜잭션까지 유지
- 2분 동안 연결 유지
제한과 규칙
- 중간에 거치는 모든 노드가
Connection: Keep-Alive
헤더를 이해하고 유지해야 한다.- 이를 이해하지 못하는 프록시 서버가 끼어 있으면 정상 동작이 어렵다.
- 클라이언트는 서버가
Connection: Keep-Alive
를 보내지 않을 경우, 연결이 끊어질 것임을 인지한다. - 커넥션이 끊어지기 전에, 메시지 본문의 끝(길이)을 정확히 알아야 한다.
- 새 요청이 어디서 시작되는지 파악할 수 있어야, 동일 커넥션을 안전하게 재활용할 수 있다.
- “정식” HTTP/1.0 기기(비확장)에는
Connection
헤더가 무시될 수 있다.- 멍청한(Misbehaving) 프록시가 존재할 가능성.
- 문제는 클라이언트가 그 기기가 확장 버전(HTTP/1.0+)인지, 순수 HTTP/1.0인지 구분할 방법이 없다는 점.
- 클라이언트는 응답을 다 못 받은 상황에서 커넥션이 끊어지면, 멱등한 요청이라면 재시도할 수 있어야 한다.
한계 - 멍청한 프록시
- 특징
Connection
헤더를 이해하지 못하여, Keep-Alive가 선언돼도 커넥션을 끊어버리는 문제.
- 발생 과정
- 클라이언트 → 프록시:
keep-alive
요청 - 프록시 → 서버:
keep-alive
요청 - 서버 → 프록시: 커넥션 유지 의사
- 클라이언트 → 프록시: 같은 커넥션에 새 요청
- 프록시는 같은 커넥션에 두 번째 요청이 오는 상황을 인지하지 못한다(혹은 기대치 못한다).
- 결과적으로 응답이 없거나, 커넥션이 끊긴다.
미봉책: 영리한 프록시와 확장 헤더
Proxy-Authenticate
등의 특별한 확장 헤더 또는Proxy-Connection: Keep-Alive
와 같은 필드로 의사소통할 수 있도록 만든다.- 영리한(진보된) 프록시는 이를 보고 멀티 요청 처리를 지원하지만, 여전히 모든 프록시가 이를 지원하는 것은 아니다.
- 영리한 프록시는 Proxy-Authenticate 등의 특별한 확장 헤더를 인식하고 적절히 처리하지만, 멍청한 프록시는 이를 무시하게 된다.
한계
- 중간에 멍청한 프록시가 하나라도 끼어 있다면, 다시 문제가 생길 수 있다.
HTTP/1.1 지속 커넥션
특징
- Keep-Alive가 기본으로 활성화되어 있다.
- 커넥션을 끊으려면
Connection: close
를 명시적으로 보내야 한다.
- 커넥션을 끊으려면
- 하지만, HTTP/1.1에서는 언제든지 클라이언트나 서버가 커넥션을 끊을 수 있으며, 무조건 연결을 영구 유지하는 것은 아니다.
제한과 규칙
- 커넥션에 있는 모든 메시지는 그 길이를 정확히 알 수 있어야 한다.
- HTTP/1.1 프록시는 클라이언트와 서버 각각에 대해 별도의 지속 커넥션을 맺고, 이를 관리해야 한다(홉 바이 홉).
- 어느 한쪽에서든 커넥션은 예고 없이 끊길 수 있다.
- 클라이언트는 멱등한 요청이면 다시 보낼 준비가 되어 있어야 한다.
- 하나의 클라이언트는, 한 서버에 대해 일반적으로 최대 2개의 지속 커넥션만 유지해야 한다(서버 과부하 방지 차원).
파이프라인 커넥션: 커넥션 연결 과정 단축 2
참고) 파이프라인은 현재 HoL(Head of Line) 블로킹 문제때문에, 기본으로 비활성화되어 있다.
제약과 규칙
- 클라이언트는 해당 커넥션이 지속 커넥션인지 확인된 이후에만 파이프라인을 사용할 수 있다.
- 응답 순서는 요청 순서와 동일해야 한다.
- 순서가 뒤섞이면 클라이언트가 요청-응답 매칭을 할 수 없다.
- 커넥션이 언제 끊어져도, 아직 완료되지 않은 요청이 있다면 재시도할 준비가 되어 있어야 한다.
- 비멱등(non-idempotent) 요청은 파이프라인으로 보내면 안 된다.
- 끊어졌을 때 재요청 가능 여부가 명확하지 않기 때문이다.
- 예:
POST
가 대표적인 비멱등 요청.
참고) HoL 블로킹 (Head of Line Blocking)이란?
- 파이프라인은 응답 순서가 요청 순서와 동일해야 하는데, 그렇게 되면 반드시 첫번째 요청부터 먼저 처리해야 한다.
- 그런데 첫번째 요청이 매우 오래걸리면, 금방 끝나는 뒷 요청까이 오랫동안 기다리다가 타임아웃이 발생하는 사태가 일어날 수 있다.
- 이를 HOL 블로킹이라고 하며, HTTP/2.0에서는 HTTP 멀티플렉싱으로 파이프라인이 해결하려던 문제를 해결하였다.
HTTP/1.1 "커넥션 끊기"에 대한 관련 주제
막 끊어도 됨
- HTTP/1.1 표준상, 특정 시점에 커넥션을 끊으면 안 된다는 강제 규정은 없다.
- 다만 요청 또는 응답 중간에 끊으면, 해당 트랜잭션은 실패하고 재시도가 필요할 수 있다.
Content-Length, Truncation
- 지속 커넥션 환경에서 정확한 본문 길이(Content-Length)를 모르면, 다음 요청·응답이 어디서 시작되는지 구분하기 어렵다.
- 트렁킹(Chunked Transfer-Encoding)처럼 크기를 알 수 없는 방식이라면, 각 청크를 명확히 구분해야 한다.
- 안전을 위해, 수신자는 데이터 길이가 의심스러울 경우 업스트림에 길이를 다시 요청해 확인해야 한다.
커넥션 끊기 방식 - 우아한 커넥션 끊기에 대하여
- 전체 끊기
- 입력·출력 스트림을 모두 닫아버리는 방식.
- 절반 끊기(half-close)
- 입력 채널과 출력 채널 중 한쪽만 닫고, 반대편이 닫히길 기다리는 것을 의미한다.
- 입력을 먼저 닫고, 출력이 닫히길 기다리거나, 출력을 먼저 닫고, 입력이 닫히길 기다리는 것이 가능하다.
- 주의
- 서버가 입력을 닫은 상태에서 클라이언트가 데이터를 보내면 서버의 운영체제 차원에서
connection reset by peer
에러를 메시지로 전달한다. - 이 오류를 받은 클라이언트 운영체제의 동작은, 이미 도착한 데이터가 있더라도 함께 폐기하기 때문에, 아직 수신 버퍼에 남은 데이터도 폐기될 수 있다.
- 파이프라인 커넥션이 있을 때 이 문제가 함께 발생하면 심각해질 수 있기에, "입력 끊기"는 잘 사용되지 않는다.
- 서버가 입력을 닫은 상태에서 클라이언트가 데이터를 보내면 서버의 운영체제 차원에서
- 실제로는, 출력을 먼저 닫고, 수신만 마저 받는 것이 비교적 안전한 방법으로 여겨진다.
- 이를 “우아한 커넥션 끊기” 라고 한다.
참고) 멱등성
- 연산이나 요청을 여러 번 수행해도 결과가 처음 한 번 수행한 것과 동일하게 유지되는 성질.
- 멱등성 보장 방법
- 멱등한 설계
- 로직 자체를 여러 번 요청해도 서버 상태가 변하지 않도록 만드는 방법.
- 멱등 키
- UUID 같은 고유 식별자 키를 생성하고, 해당 키로 중복 요청을 방어한다.
- 한 번 처리된 멱등 키에 대해서는 일정 기간 후에만 다시 허용하거나, 아예 차단하는 구조를 만든다.
- 멱등한 설계
HTTP에서는 이러한 멱등성을 고려해, 지속 커넥션에서 요청이 끊겼을 때 다시 보낼 수 있는지 여부를 판단한다. 이 원리가 잘 지켜지면, 의도치 않은 중복 처리나 오류를 줄일 수 있다.
'CS Repo > HTTP 완벽 가이드' 카테고리의 다른 글
프록시 (0) | 2025.04.08 |
---|---|
웹 서버 (0) | 2025.04.04 |
HTTP에서 바라본 TCP 커넥션 관리 방식 (0) | 2025.03.27 |
HTTP 메시지 (0) | 2025.03.25 |
URL과 리소스 (0) | 2025.03.24 |