[HTTP 완벽 가이드 정리] 01. HTTP: 웹의 기초 - 04. 커넥션 관리 (2)
4장 커넥션 관리에서는 HTTP에서 관리하는 TCP 커넥션에 대한 일반적인 오해들과 잘못 작성된 규칙 및 동작 방식에 대해서 알아본다.
📝 핵심 요약
주제 | 요약 |
---|---|
HTTP 커넥션 관리 | 병렬 / 지속 / 파이프라인 / 다중 커넥션을 통해 성능 향상 가능 |
병렬 커넥션 | - 병렬 커넥션의 지연 시간이 겹쳐짐으로써 총 지연시간 단축 가능 - 다수의 커넥션은 자원 낭비와 혼잡 초래 가능 |
지속 커넥션 | - 커넥션을 재사용함으로써 1) 커넥션을 맺기 위한 설정 비용을 줄이고 2) 느린 시작 지연을 피하여 성능 향상 가능 - HTTP/1.1 에서는 기본적으로 지속 커넥션 활성화되어 있음 - 커넥션 끊는 경우 Connection: close 헤더를 명시 |
파이프라인 커넥션 | - HTTP/1.1은 지속 커넥션을 통해 요청 파이프라이닝 가능 - 요청 전송 대기 시간 단축으로 성능 향상 가능하지만 1) 지속 연결 필요 2) 응답 순서 보장 필수 3) 연결 조기 종료 대비 필요 4) 문제가 발생할 수 있는 비멱등 요청은 파이프라이닝 지양 등의 제약 사항 존재 |
커넥션 끊기에 대한 미스터리 | - 커넥션은 언제든 끊을 수 있음 - 종료 타이밍이 어긋날 경우 reset 오류 발생할 수 있음 - reset 메시지는 입력 버퍼에 저장되어 있는 아직 읽히지 않은 데이터를 모두 삭제함 - 내 출력 채널을 먼저 끊고 다른 쪽 출력 채널이 끊기길 기다리는 게 좋지만, 그렇게 되지 않으면 타임아웃으로 커넥션 강제 종료 |
3. HTTP 커넥션 관리
TCP 연결의 구조와 성능 이슈를 바탕으로, 이제 HTTP 자체가 연결을 어떻게 최적화하고 제어하는지 설명
HTTP 연결을 효과적으로 관리하기 위해 병렬 연결, 지속 연결, 파이프라이닝 같은 전략이 등장
❗ Connection 헤더
HTTP는 프록시, 캐시 등 중간 노드(hop)를 거쳐 메시지를 전달하는 hop-by-hop 통신 구조
Connection
헤더는 현재 연결 구간에서만 유효한 옵션이나 특정 헤더들을 명시하며, 이후 hop에서는 해당 정보가 전달되지 않아야 함을 나타냄
📌 Connection 헤더 값의 유형
헤더 이름: hop-by-hop 속성을 가지는 헤더를 명시해 다음 노드에 전달되지 않도록 보호
비표준 옵션 토큰: 특정 연결에서만 해석되는 임의의 연결 확장 값
“close” 값: 이 연결은 응답 후 종료됨을 의미 (
Connection: close
)
📌 처리 규칙
수신자는
Connection
헤더에 명시된 모든 헤더와Connection
헤더 자체를 전달 전에 삭제다음과 같은 헤더는
Connection
에 명시되지 않아도 절대 전달해서는 안 됨:
Proxy-Authenticate
,Proxy-Connection
,Transfer-Encoding
,Upgrade
Connection
헤더는 단순한 keep-alive 제어용이 아니라, 중간 프록시 간 연결 제어의 핵심 메커니즘
4. 병렬 커넥션
브라우저가 HTML과 포함된 이미지 등 리소스를 하나씩 순차적으로 처리하면 매우 느림
HTTP는 여러 개의 TCP 연결을 동시에 열어 리소스를 병렬 요청할 수 있도록 지원함
예) HTML 문서를 받은 후, 이미지 4개를 각기 다른 연결로 동시에 요청 가능
⚡ 병렬 커넥션이 성능을 높이는 이유
각 요청의 지연 시간이 겹치며(overlap) 전체 로딩 속도를 줄일 수 있음
하나의 연결이 클라이언트 대역폭을 다 쓰지 않는 경우, 남는 대역폭을 추가 리소스 로딩에 사용 가능
결과적으로 복합적인 웹 페이지(이미지, CSS, JS 포함) 로딩 속도 개선에 효과적
⚠️ 병렬 커넥션의 한계와 주의점
낮은 대역폭 환경(예: 느린 모뎀)에서는 병렬 연결이 오히려 전체 전송 속도를 나누게 되어 역효과
너무 많은 병렬 연결은:
클라이언트: 메모리/연결 관리 부담 증가
서버: 연결 수가 폭발적으로 증가하여 리소스 고갈과 성능 저하 초래 (예: 100명 × 100연결 = 10,000 연결)
실제 브라우저는 병렬 연결 수를 보통 6~8개 정도로 제한하며, 서버도 과도한 연결을 강제로 닫을 수 있음
🤔 체감 속도의 착시 효과
병렬 연결은 실제 로딩 시간을 단축하지 않아도, 사용자가 빠르게 느끼도록 도와줌
동시에 여러 요소가 화면에 나타나면 더 빠르게 내려받고 있는 느낌을 받아 긍정적 인식 유도
병렬 연결은 실제 성능 향상뿐 아니라 사용자 체감 속도 향상에도 효과적이지만,
과도한 연결은 오히려 성능 악화를 유발할 수 있어 제한적으로 사용
5. 지속 커넥션
HTTP 클라이언트는 동일한 서버에 여러 요청을 반복적으로 보내는 경향(사이트 지역성, site locality)이 있음 (예: HTML, 이미지 등 동일 출처)
이를 고려하여 HTTP/1.1과 일부 HTTP/1.0 확장에서는 TCP 연결을 재사용할 수 있도록 함
→ 이를 지속 커넥션(Persistent Connection)이라 부름반대로, 요청마다 연결을 새로 여는 방식은 비지속 커넥션(Nonpersistent)이라 함
💡 지속 커넥션을 사용하면:
연결 설정 및 종료 지연을 줄일 수 있음
슬로우 스타트 없이 빠르게 데이터 전송 가능
네트워크 및 서버 자원 사용 최적화
🔁 병렬 연결 vs 지속 연결
항목 | 병렬 연결 | 지속 연결 |
---|---|---|
연결 방식 | 요청마다 새로운 연결 | 하나의 연결을 여러 요청에 재사용 |
장점 | 동시에 여러 요청 가능 → 병목 완화 | 연결/해제 오버헤드 줄이고, 튜닝된 상태 유지 |
단점 | 자원 과다 사용, 연결 수 제한 | 연결 상태 관리 필요, 유휴 연결 증가 가능 |
🌐 HTTP/1.1에서의 변화
HTTP/1.0+:
Connection: keep-alive
로 지속 연결 요청HTTP/1.1: 지속 연결이 기본값
명시적으로 닫고 싶을 때만
Connection: close
사용클라이언트/서버는 항상 연결 유지가 보장되는 것은 아님
⚠️ 지속 연결 사용 시 주의사항
Connection: close
가 요청에 포함되면, 해당 연결에서는 더 이상 요청 금지엔터티 바디의 길이는 정확해야 함
→Content-Length
나Transfer-Encoding: chunked
필수프록시(중간 노드)는 각 방향(클라이언트-프록시 / 프록시-서버)의 연결을 독립적으로 관리
HTTP/1.1 프록시는 HTTP/1.0 클라이언트와의 지속 연결은 피해야 함
어떤 상황에서도 서버는 임의로 연결을 끊을 수 있음
- 단, 최소 한 개의 요청은 처리 후 끊는 것이 바람직
클라이언트는 연결이 중간에 끊겼을 때 재시도 가능해야 함 (단, 부작용 없는 요청만)
클라이언트는 서버당 2개의 연결만 유지하는 것이 권장됨 (프록시는 사용자 수 ×2)
지속 연결은 성능 최적화에 매우 효과적이지만, 그에 따른 연결 상태 관리와 오류 복구 전략도 함께 고려
6. 파이프라인 커넥션
HTTP/1.1은 지속 연결(persistent connection) 위에서 요청을 순차적으로 연달아 전송할 수 있도록 허용함
→ 이를 파이프라이닝(pipelining)이라 부름즉, 첫 번째 요청의 응답을 기다리지 않고 두 번째, 세 번째 요청을 미리 전송 가능
특히 지연이 큰 네트워크 환경(latency)에서 왕복 횟수를 줄여 성능 향상에 유리
🔁 작동 방식
하나의 TCP 연결을 유지한 상태에서 요청 1, 2, 3을 연속 전송
서버는 요청 순서대로 응답하며, 응답 순서를 반드시 유지해야 함
⚠️ 제약사항
항목 | 설명 |
---|---|
연결 조건 | 파이프라이닝은 지속 연결일 때만 사용 가능 |
응답 순서 | 응답은 요청 순서대로 반환되어야 함 → 식별자가 없기 때문에 순서 어긋나면 혼란 발생 |
연결 종료 | 클라이언트는 언제든 연결이 끊길 수 있다는 가정하에, 전송되지 않은 요청을 재시도할 수 있어야 함 |
멱등성 고려 | POST처럼 재시도 시 문제가 생기는 요청은 파이프라이닝에서 제외해야 함 (안전하지 않음) |
❗️ 적용 한계
서버나 프록시가 파이프라이닝을 제대로 지원하지 않거나 비활성화한 경우
앞 요청의 지연이 후속 응답 전체를 막는 Head-of-Line blocking 문제 발생
파이프라이닝은 HTTP/1.1에서 요청/응답 병목을 줄이기 위한 시도였지만,
응답 순서 제한과 낮은 호환성으로 인해 HTTP/2의 멀티플렉싱이 이를 대체함
7. 커넥션 종료의 미스터리
- 커넥션 관리에는 명확한 기준이 없음 (언제 어떻게 커넥션을 종료하는지 등)
🔌 마음대로 커넥션 끊기
어떤 HTTP 참여자(클라이언트, 서버, 프록시)라도 언제든지 TCP 연결을 끊을 수 있음
일반적으로는 메시지 끝에서 닫지만, 에러나 유휴 시간 후 임의로 끊길 수 있음
특히 파이프라이닝 연결에서 중간에 연결이 끊기면 요청이 전송되지 못하거나 응답을 잃을 수 있음
📏 Content-Length와 Truncation
응답의 정확한
Content-Length
명시는 필수명시되지 않거나 틀린 경우, 클라이언트는 연결 종료 시점을 메시지 끝으로 간주함
잘못된 길이로 인해 데이터가 중간에 잘리거나 손실될 수 있음
프록시는 잘린 응답을 캐싱해서는 안 되며, 메시지를 수정 없이 그대로 전달해야 함
🔁 커넥션 끊기의 허용, 재시도, 멱등성
연결은 정상 상태에서도 언제든 닫힐 수 있으므로, HTTP 클라이언트는 이에 대비해야 함
요청 중 연결이 끊기면 멱등한 요청만 재시도 가능 (GET, PUT 등)
POST 같은 비멱등 요청은 재전송하면 부작용(예: 주문 중복)이 발생할 수 있음
브라우저는 POST 재전송 시 사용자에게 확인을 요청하는 UI 제공
🌿 우아한 커넥션 끊기
TCP 연결은 양방향 스트림으로, 출력과 입력을 각각 따로 종료 가능
close()
→ 완전 종료 (입력 + 출력 모두 닫힘)shutdown()
→ 반쯤 종료 (입력 또는 출력만 닫기 가능)
안전한 방식은:
출력 채널을 먼저 닫음 (쓰기 중단)
상대방의 출력 채널(= 나의 입력)이 닫힐 때까지 대기
양방향 모두 닫히면 전체 연결 종료
입력 채널만 먼저 닫으면 위험: 상대방이 아직 데이터를 보내면 → TCP reset 발생, 버퍼된 데이터까지 날아감
특히 파이프라이닝된 응답이 아직 읽히지 않은 상태에서 reset 발생 시, 전체 응답 손실됨
출력부터 닫고 → 입력이 닫힐 때까지 기다리는 것이 이상적인 우아한 종료 방법
- HTTP 명세는 “우아하게 종료하라”고 하지만, 방법은 명확히 정의하지 않음
- 절반 끊기 후 상대의 상태 검사를 주기적으로 하고, 일정 시간 내 응답이 없으면 강제 종료