Post

[HTTP 완벽 가이드 정리] 01. HTTP: 웹의 기초 - 04. 커넥션 관리 (2)

4장 커넥션 관리에서는 HTTP에서 관리하는 TCP 커넥션에 대한 일반적인 오해들과 잘못 작성된 규칙 및 동작 방식에 대해서 알아본다.

[HTTP 완벽 가이드 정리] 01. HTTP: 웹의 기초 - 04. 커넥션 관리 (2)

📝 핵심 요약

주제요약
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 헤더 값의 유형

  1. 헤더 이름: hop-by-hop 속성을 가지는 헤더를 명시해 다음 노드에 전달되지 않도록 보호

  2. 비표준 옵션 토큰: 특정 연결에서만 해석되는 임의의 연결 확장 값

  3. “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 사용

    • 클라이언트/서버는 항상 연결 유지가 보장되는 것은 아님


⚠️ 지속 연결 사용 시 주의사항

  1. Connection: close가 요청에 포함되면, 해당 연결에서는 더 이상 요청 금지

  2. 엔터티 바디의 길이는 정확해야 함
    Content-LengthTransfer-Encoding: chunked 필수

  3. 프록시(중간 노드)는 각 방향(클라이언트-프록시 / 프록시-서버)의 연결을 독립적으로 관리

  4. HTTP/1.1 프록시는 HTTP/1.0 클라이언트와의 지속 연결은 피해야 함

  5. 어떤 상황에서도 서버는 임의로 연결을 끊을 수 있음

    • 단, 최소 한 개의 요청은 처리 후 끊는 것이 바람직
  6. 클라이언트는 연결이 중간에 끊겼을 때 재시도 가능해야 함 (단, 부작용 없는 요청만)

  7. 클라이언트는 서버당 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() → 반쯤 종료 (입력 또는 출력만 닫기 가능)

  • 안전한 방식은:

    1. 출력 채널을 먼저 닫음 (쓰기 중단)

    2. 상대방의 출력 채널(= 나의 입력)이 닫힐 때까지 대기

    3. 양방향 모두 닫히면 전체 연결 종료

  • 입력 채널만 먼저 닫으면 위험: 상대방이 아직 데이터를 보내면 → TCP reset 발생, 버퍼된 데이터까지 날아감

  • 특히 파이프라이닝된 응답이 아직 읽히지 않은 상태에서 reset 발생 시, 전체 응답 손실됨


출력부터 닫고 → 입력이 닫힐 때까지 기다리는 것이 이상적인 우아한 종료 방법

  • HTTP 명세는 “우아하게 종료하라”고 하지만, 방법은 명확히 정의하지 않음
  • 절반 끊기 후 상대의 상태 검사를 주기적으로 하고, 일정 시간 내 응답이 없으면 강제 종료



This post is licensed under CC BY 4.0 by the author.