본문 바로가기
공부/책

[HTTP 완벽 가이드] 1부 학습

by nahowo 2025. 5. 6.

2장: URL과 리소스

URI

  • URI(Uniform Resource Identifier)는 정보 리소스를 고유하게 식별하고 위치를 지정하는 자원 식별자이다. URI에는 URL, URN(Uniform Resource Name)의 두 가지 종류가 있다.

URL

정의

  • URL(Uniform Resource Locator)은 인터넷의 리소스를 가리키는 표준이름이다. URL은 브라우저가 정보를 찾는 데 필요한 리소스의 위치를 가리킨다.

URN과의 비교

  • URL은 리소스가 어디 있는지 설명해 리소스를 식별한다.
  • URN은 리소스가 어디에 존재하든 상관없이 그 이름만으로 리소스를 식별한다.

URL 구조

  • http://www.joes-hardware.com/seasonal/index-fall.html를 예시로 들어보자.
  • http는 URL 의 스킴(scheme) 부분이다.
    • 스킴은 클라이언트가 리소스에 어떻게 접근하는지를 알려준다.
  • www.joes-hardware.com은 서버의 위치이다.
    • 리소스가 어디에 호스팅되어 있는지를 알려준다.
  • /seasonal/index-fall.html은 리소스의 경로이다.
    • 서버에 존재하는 로컬 리소스들 중에서 요청받은 리소스가 무엇인지 알려준다.
  • 이렇게 URL을 사용하면 리소스를 일관된 방식으로 지칭할 수 있다. 대부분의 URL은 스킴://서버 위치/경로 구조로 이루어진다.

URL 문법

  • 대부분의 URL 스킴 문법은 일반적으로 9개 부분으로 나뉜다.
  • 스킴://사용자 이름:비밀번호@호스트:포트/경로;파라미터?질의#프래그먼트
  • 스킴: 어떤 프로토콜을 사용해 서버에 접근하는지
  • 사용자 이름: 몇몇 스킴은 리소스에 접근하기 위해 사용자 이름을 필요로 함(기본 annonymous)
  • 비밀번호: 사용자의 비밀번호
  • 호스트: 리소스를 호스팅하는 서버의 호스트명이나 IP 주소
  • 포트: 리소스를 호스팅하는 서버가 열어놓은 포트 번호(스킴마다 기본 포트 다름, HTTP의 기본 포트는 80)
  • 경로: 서버 내 리소스가 서버 어디에 있는지
  • 파라미터: 특정 스킴에서 입력 파라미터를 기술하는 용도, 여러 개를 가질 수 있고 이름: 값 형식의 쌍
  • 질의: 스킴에서 애플리케이션에 파라미터를 전달하는 데 사용
  • 프래그먼트: 리소스 조각/일부분을 가리키는 이름

스킴: 사용할 프로토콜

  • URL을 해석하는 애플리케이션이 어떤 프로토콜을 사용해 리소스를 요청해야 하는지 알려준다.
  • 스킴은 알파벳으로 시작해야 하고 : 문자로 구분한다. 대소문자를 가리지 않는다.

호스트와 포트

  • 호스트와 포트는 리소스를 호스팅하는 장비와 그 장비 내에서 리소스에 접근할 수 있는 서버가 어디에 있는지 알려준다.
  • 호스트는 접근하려는 리소스를 가지고 있는 인터넷상의 호스트 장비를 가리킨다. 호스트 명이나 IP 주소로 제공한다.
  • 포트는 서버가 열어놓은 네트워크 포트를 가리킨다.

사용자 이름과 비밀번호

  • 몇몇 서버는 본인 데이터에 접근을 허용하기 전에 사용자 이름과 비밀번호를 요구한다. FTP와 같이 사용자 이름과 비밀번호를 요구하는 URL 스킴을 사용하고 그 값들을 채우지 않으면 기본 사용자 이름과 기본 비밀번호가 들어간다. 브라우저마다 기본값이 다르다.

경로

  • 리소스가 서버의 어디에 있는지 알려준다.
  • 계층적 파일 시스템 경로와 유사한 구조이다.
  • / 문자를 기준으로 각 경로 조각으로 나뉜다.
  • 각 경로 조각은 자체적인 파라미터 컴포넌트를 가질 수 있다.

파라미터

  • 애플리케이션이 서버에 정확한 요청을 하기 위해 필요한 입력 파라미터를 받는 데 사용한다.
  • 이름-값 쌍의 리스트로 URL 나머지 부분들로부터 ; 문자로 구분하여 기술한다.
  • 각 경로 조각이 자체적으로 파라미터를 가질 수 있다.

질의 문자열

  • 요청받을 리소스 형식의 범위를 좁하기 위해 질문/질의를 받을 수 있다.
  • & 문자로 구분해 나열한다.

프래그먼트

  • HTML 등의 리소스 형식은 본래 수준보다 더 작게 나눌 수 있다.
  • 리소스 내의 특정 부분을 가리킬 수 있도록 프래그먼트를 제공한다.
  • 일반적으로 HTML 서버는 일부가 아닌 전체만 다루기 때문에 프래그먼트를 전달하지 않는다.

단축 URL

  • 웹 클라이언트는 몇몇 단축 URL을 인식하고 사용한다.

상대 URL

  • URL은 상대 URL과 절대 URL로 나뉜다.
  • 절대 URL은 리소스에 접근하는데 필요한 모든 정보를 가진다.
  • 상대 URL은 기저 URL을 사용해 URL을 짧게 표현하는 방식이다.
    • 상대 URL로 리소스에 접근하기 위해 필요한 모든 정보를 얻기 위해서는 기저 URL을 이용해야 한다.
  • 상대 URL은 프래그먼트이거나 URL의 일부이다.
  • URL을 처리하는 브라우저 등의 애플리케이션은 상대 URL과 절대 URL간 상호 변환을 할 수 있어야 한다.

3장: HTTP 메시지

메시지의 흐름

  • HTTP 메시지는 HTTP 애플리케이션 간에 주고받은 데이터의 블록들이다.
  • HTTP는 트랜잭션 방향을 표현하기 위해 인/아웃바운드라는 용어를 사용한다.
    • 메시지가 원 서버로 향하는 것은 인바운드로 이동하는 것이다.
    • 모든 처리가 끝난 뒤에 메시지가 사용자 에이전트로 돌아오는 것은 아웃바운드로 이동하는 것이다.
  • 모든 메시지(요청/응답)는 다운스트림으로 흐른다.

메시지의 각 부분

  • HTTP 메시지는 단순하고 데이터의 구조화된 블록이다.
  • 메시지는 시작줄, 헤더 블록, 본문의 세 부분으로 이루어진다.
    • 시작줄은 해당 메시지가 어떤 메시지인지 서술한다.
    • 헤더 블록은 속성을 서술한다.
    • 본문은 데이터를 담으며 없을 수도 있다.

부분 구성

  • 시작줄과 헤더는 줄 단위로 분리된 아스키 문자열이다.
  • 각 줄은 캐리지 리턴(Cariage Return, CR; 맨 처음 칸으로 이동)과 개행 문자(Line Feed, LF; 다음 줄로 이동)로 구성된 두 글자의 줄바꿈 문자열로 끝난다. 이 줄바꿈 문자열은 CRLF(CR + LF)라고 쓴다.
    • 견고한 애플리케이션이라면 그냥 개행 문자도 받아들일 수 있어야 한다.
  • 본문은 단순히 선택적인 데이터 덩어리이다.

메시지 문법

  • 모든 HTTP 메시지는 요청이거나 응답 메시지로 구분된다.
  • 요청 메시지는 웹 서버에 어떤 동작을 요구한다.
    • 메서드: 클라이언트 측에서 서버가 수행해주길 바라는 동작
    • 요청 URL: 요청 대상이 되는 리소스를 지칭하는 완전한 URL 혹은 URL의 경로 구성요소
    • 버전: 해당 메시지에서 사용 중인 HTTP 버전, HTTP/<메이저>.<마이너> 형식
    • 헤더: 이름: 값CRLF 형식의 0개 이상의 헤더들
    • 엔티티 본문: 임의의 데이터 블록 포함
  • <메서드> <요청 URL> <버전> <헤더> <엔티티 본문>
  • 응답 메시지는 요청의 결과를 클라이언트에게 돌려준다.
    • 상태 코드: 요청 중에 무엇이 일어났는지 설명하는 세 자리의 숫자
    • 사유 구절: 상태 코드 의미를 설명하는 짧은 문구
  • <버전> <상태 코드> <사유 구절> <헤더> <엔티티 본문>
  • 헤더나 엔티티 본문이 없더라도 HTTP 헤더 집합은 항상 빈 줄(CRLF)로 끝나야 한다.

시작줄

메서드

  • 많이 쓰이는 HTTP 메서드는 아래와 같다.
    • GET: 서버에서 어떤 문서를 가져옴
    • HEAD: 서버에서 어떤 문서에 대해 헤더만 가져옴
    • POST: 서버가 처리해야 할 데이터를 보냄
    • PUT: 서버에 요청 메시지의 본문을 저장
    • TRACE: 메시지가 프락시를 거쳐 서버에 도달하는 과정 추적
    • OPTIONS: 서버가 어떤 메시지를 수행할 수 있는지 확인
    • DELETE: 서버에서 문서 제거
  • 모든 서버가 위의 메서드를 모두 구현한 것은 아니다.
  • 메서드는 대부분 제한적으로 사용된다.
  • HTTP는 확장 가능하게 설계되었기 때문에 서버가 본인만의 메서드를 추가로 구현했을 수도 있다.

상태 코드

  • 상태 코드의 종류는 아래와 같다. 전체 범위(정의된 범위) 형식이다.
    • 100-199(100-101): 정보
    • 200-299(200-206): 성공
    • 300-399(300-305): 리다이렉션
    • 400-499(400-415): 클라이언트 에러
    • 500-599(500-505): 서버 에러
  • 상태 코드도 현재 프로토콜의 확장으로 정의된 범위를 벗어나는 코드를 정의할 수 있다.

버전 번호

  • 어떤 애플리케이션이 지원하는 가장 높은 HTTP 버전을 가리킨다.
  • 버전 번호는 분수로 다루어지지 않는다. HTTP/1.22는 HTTP/1.3보다 크다.

헤더

  • 일반 헤더: 요청, 응답에 모두 나타날 수 있음
  • 요청 헤더: 요청에 대한 부가정보 제공
  • 응답 헤더: 응답에 대한 부가정보 제공
  • Entity 헤더: 본문 크기와 콘텐츠, 리소스 그 자체를 서술
  • 확장 헤더: 명세에 정의되지 않은 새로운 헤더

메서드

안전한 메서드(safe method)

  • HTTP 요청의 결과로 서버에 어떤 작용도 없는 메서드(보장은 개발자가 해야 한다)

GET

  • 주로 서버에게 리소스를 달라고 요청할 때 쓰인다.
  • HTTP/1.1은 서버가 GET을 구현하도록 요구한다.

HEAD

  • GET과 유사하지만 헤더만 반환한다.

PUT

  • 서버에 문서를 쓴다.
  • 서버에 있는 리소스에 데이터를 입력하기 위해 사용한다.
  • 서버가 요청의 본문을 가지고 요청 URL의 이름대로 새 문서를 만들거나 이미 URL이 존재한다면 본문을 사용해 교체하는 것이다.

POST

  • 서버에 입력 데이터를 전송하기 위해 설계되었다.

TRACE

  • 클라이언트에게 자신의 요청이 서버에 도달했을 때 어떻게 보이게 되는지 알려준다.
  • 목적지 서버에서 루프백(loopback) 진단을 시작한다. 클라이언트는 자신과 목적지 서버 사이에 있는 모든 HTTP 애플리케이션의 요청/응답 연쇄를 따라가면서 자신이 보낸 메시지가 망가졌거나 수정되었는지, 어떻게 변경되었는지 확인할 수 있다.

OPTIONS

  • 웹 서버에게 지원 범위에 대해 물어본다.
  • 특정 리소스에 대해 어떤 메서드가 지원되는지 물어본다.

DELETE

  • 서버에게 요청 URL로 지정하는 리소스를 삭제할 것을 요청한다.
  • 삭제가 수행되는 것은 보장하지 못한다.
    • 서버는 클라이언트에게 알리지 않고 요청을 무시할 수 있다.

확장 메서드

  • HTTP는 확장 가능하도록 설계되어 있으므로 새로 기능을 추가해도 과거 구현된 소프트웨어들의 오작동을 유발하지 않는다.
  • 확장 메서드에 대해 관용적인 태도를 취한다.
    • 프락시는 종단간 행위를 망가뜨리지 않을 수 있다면 알려지지 않은 확장 메서드 메시지를 다운스트림 서버로 전달하려고 시도한다. 종단간 행위를 망가뜨리는 경우 프락시는 501 Not Implemented 상태 코드로 응답해야 한다. → 엄격하게 보내고 관대하게 받아들여라 - Postel 법칙

헤더

일반 헤더

  • 클라이언트와 서버 둘 다 사용한다.

요청 헤더

  • 요청 메시지를 위한 헤더이다.
  • 서버에게 클라이언트가 받고자 하는 데이터의 타입이 무엇인지와 같은 부가 정보를 제공한다.

응답 헤더

  • 클라이언트에게 정보를 제공하기 위해 응답 메시지가 가지는 헤더이다.

엔티티 헤더

  • 엔티티 본문에 대한 헤더이다.

확장 헤더

  • 애플리케이션 개발자에 의해 만들어진 비표준 헤더이다.
  • HTTP 프로그램은 확장 헤더의 의미를 모르더라도 용인하고 전달해야 할 필요가 있다.

4장: 커넥션 관리

TCP 커넥션

  • 모든 HTTP 통신은 TCP/IP를 통해 이루어진다.
  • 일단 커넥션이 맺어지면 클라이언트와 서버 컴퓨터 간 손실/손상/순서 뒤섞임 없이 메시지를 전달할 수 있다.
  • 일반적인 통신 과정은 아래와 같다.
    1. 브라우저가 호스트명을 추출한다.
    2. 브라우저가 호스트명에 대한 IP 주소를 찾는다.
    3. 브라우저가 포트번호를 얻는다.
    4. 브라우저가 IP 주소의 포트로 TCP 커넥션을 생성한다.
    5. 브라우저가 서버로 HTTP 요청 메시지를 보낸다.
    6. 브라우저가 서버에서 온 HTTP 응답 메시지를 읽는다.
    7. 브라우저가 커넥션을 끊는다.

신뢰할 수 있는 데이터 전송 통로인 TCP

  • HTTP 커넥션은 몇몇 사용 규칙을 제외하고는 TCP 커넥션에 불과하다.
  • TCP 커넥션의 한쪽에 있는 바이트들은 반대쪽으로 순서에 맞게 정확히 전달된다.
  • TCP 스트림은 세그먼트로 나뉘어 IP 패킷을 통해 전송된다.
    • HTTP는 IP, TCP, HTTP로 구성된 프로토콜 스택에서 최상위 계층이다.
    • HTTP에 보안 기능을 더한 HTTPS는 TLS 또는 SSL이라 불리기도 하며 HTTP와 TCP 사이에 있는 암호화 계층이다.
  • TCP는 세그먼트 단위로 데이터 스트림을 잘게 나누고, 세그먼트를 IP 패킷에 담아 인터넷을 통해 데이터를 전달한다. 이 과정은 TCP/IP 소프트웨어에 의해 처리되며 각 과정은 HTTP 프로그래머에게 보이지 않는다.
  • 각 TPC 세그먼트는 하나의 IP 주소에서 다른 IP 주소로 IP 패킷에 담겨 전달된다.
  • 각 IP 패킷은 다음과 같은 구성요소를 갖는다.
    • IP 패킷 헤더(보통 20바이트)
    • TCP 세그먼트 헤더(보통 20바이트)
    • TCP 데이터 조각(0 이나 그 이상의 바이트)

TCP 커넥션 유지하기

  • 컴퓨터는 항상 여러 개의 TCP 커넥션을 가지고 있다. TCP는 포트 번호를 통해 이 여러 개의 커넥션을 유지한다.
  • IP 주소는 해당 컴퓨터에 연결되고 포트번호는 해당 애플리케이션으로 연결된다.
  • TCP 주소는 네 가지 값으로 식별한다.
    • 발신지 IP주소, 발신지 포트, 수신지 IP 주소, 수신지 포트

TCP 소켓 프로그래밍

  • 운영체제는 TCP 커넥션 생성과 관련된 여러 기능을 제공한다. 소켓 API는 HTTP 프로그래머에게 TCP, IP 세부사항을 숨긴다.
    • socket(파라미터): 연결되지 않은 익명의 소켓 생성
    • bind(소켓, 로컬 주소): 소켓에 로컬 포트번호와 인터페이스 할당
    • connect(소켓, 원격 주소): 로컬 소켓과 원격 주소 사이에 TCP 커넥션 할당
    • listen(소켓): 커넥션을 받아들이기 위해 로컬 소켓에 허용함을 표시
    • acceps(소켓): 누군가 로컬 포트에 커넥션을 맺기를 기다림
    • read(소켓, 버퍼, n): 소켓으로부터 버퍼에 n바이트 읽기 시도
    • write(소켓, 버퍼, n): 소켓으로부터 버퍼에 n바이트 쓰기 시도
    • close(소켓): TCP 커넥션을 완전히 끊음
    • shutdown(소켓): TCP 커넥션의 입출력만 닫음
    • getsocket(소켓): 내부 소켓 설정 옵션값을 읽음
    • setsocket(소켓): 내부 소켓 설정 옵션값을 변경
  • 클라이언트와 서버 간 HTTP 트랜잭션을 수행하기 위한 소켓 API의 사용 방법은 아래와 같다. 서버는 파랑, 클라이언트는 초록으로 표기한다. 소켓 API는 마지막에 표기한다. 
    1. 새 소켓을 만든다. socket
    2. 80포트로 소켓을 묶는다.
    3. 소켓 커넥션을 허용한다. listen
    4. 커넥션을 기다린다. accept
    5. IP 주소와 포트를 얻는다.
    6. 새로운 소켓을 생성한다. socket
    7. 서버의 IP:포트로 연결한다. connect
    8. 애플리케이션 커넥션 통지
    9. 요청을 읽기 시작한다. read
    10. 성공적으로 연결
    11. HTTP 요청을 보낸다. write
    12. HTTP 응답을 기다린다. read
    13. HTTP 요청을 처리한다.
    14. HTTP 응답을 보낸다. write
    15. HTTP 응답을 처리한다.
    16. 커넥션을 닫는다. close
    17. 커넥션을 닫는다. close

TCP 성능

HTTP 트랜잭션 지연

  • HTTP 트랜잭션 지연 원인은 아래와 같다.
    1. 웹 서버의 IP 주소, 포트 번호를 알아내는 데에 걸리는 시간
    2. TCP 커넥션 설정 시간
    3. 요청 메시지를 전달하고 처리되는 데 걸리는 시간
    4. 응답 메시지를 보내는 데 걸리는 시간

TCP 커넥션 헨드셰이크 지연

  • 핸드셰이크 순서는 아래와 같다.
    1. 클라이언트가 새로운 TCP 커넥션을 생성하기 위해 TCP 패킷을 서버에게 보낸다. SYN 플래그를 가진다.
    2. 서버가 몇 가지 커넥션 매개변수를 산출하고 커넥션 요청을 받아들였다는 의미로 SYN, ACK 플래그를 포함한 TCP 패킷을 클라이언트에게 보낸다.
    3. 클라이언트가 커넥션이 잘 맺어졌음을 알리기 위해 서버에게 다시 확인응답 신호를 보낸다. 이때 데이터를 함께 보낼 수 있다.
  • 크기가 작은 HTTP 트랜잭션에서는 SYN/SYN+ACK 핸드셰이크에 50% 이상의 시간을 소요한다.

확인응답 지연

  • 각 TCP 세그먼트는 순번과 데이터 무결성 체크섬을 가진다. 각 세그먼트의 수신자는 세그먼트를 온전히 받으면 작은 확인응답 패킷을 송신자에게 반환한다.
  • 확인응답은 크기가 작기 때문에 TCP는 같은 방향으로 송출되는 데이터 패킷에 확인응답을 편승(piggyback)시킨다.
  • 편승을 늘리기 위해 많은 TCP 스택은 확인응답 지연 알고리즘을 구현한다.
    • 확인응답 지연은 송출할 확인응답을 특정 시간 동안 버퍼에 저장해 두고, 확인응답을 편승시키기 위한 송출 데이터(같은 방향) 패킷을 찾는다. 일정 시간 안에 해당 패킷을 찾지 못하면 별도 패킷을 만들어 전송시킨다.
  • 요청-응답 두 가지 형식으로만 이루어지는 HTTP 동작은 편승 기회를 감소시킨다. 따라서 확인응답 지연 알고리즘에 의한 지연이 자주 발생한다.

TCP 느린 시작(slow start)

  • TCP 데이터 전송 속도는 TCP 커넥션이 만들어진 지 얼마나 지났는지에 따라 달라질 수 있다.
  • TCP 느린 시작: TCP 커넥션은 시간이 지나면서 자체적으로 튜닝되어 처음에는 커넥션 최대 속도를 제한하다가 데이터가 성공적으로 전송됨에 따라 속도 제한을 높여나간다.
    • 인터넷의 급작스러운 부하와 혼잡을 방지하는 데 사용한다.
    • TCP가 한 번에 전송할 수 있는 패킷 수를 제한한다. 이를 혼잡 윈도를 연다고 표현한다.
    • 혼잡 제어 기능 때문에 새 커넥션은 튜닝된 커넥션보다 느리다.

네이글(Nagle) 알고리즘과 TCP_NODELAY

  • TCP 세그먼트는 40바이트 상당의 플래그, 헤더를 포함해 전송하기 때문에 작은 크기의 데이터를 포함한 많은 수의 패킷을 전송한다면 네트워크 성능이 떨어진다.
  • 네이글 알고리즘은 네트워크 효율을 위해 패킷 전송 이전에 많은 양의 TCP 데이터를 한 개의 덩어리로 합친다.
    • 세그먼트가 최대 크기가 되지 않으면 전송을 하지 않는다.
    • 네이글 알고리즘과 확인응답 지연을 함께 사용하면 효율이 많이 떨어진다.
  • HTTP 스택에 TCP_NODELAY 파라미터를 설정해 네이글 알고리즘을 비활성화하기도 한다. 이때에는 작은 크기의 패킷이 너무 많이 생기지 않도록 해야 한다.

TIME_WAIT 누적과 포트 고갈

  • TCP 커넥션 종단에서 커넥션을 끊으면 종단에서는 커넥션의 IP 주소와 포트 번호를 메모리의 작은 제어 영역(control block)에 기록해 놓는다. 이는 동일한 주소, 포트 번호의 새 TCP 커넥션이 일정 시간 동안 생성되지 않게 하기 위해서이다.
    • 이전 커넥션과 관련된 패킷이 그 커넥션과 같은 주소, 포트 번호를 가지는 새로운 커넥션에 삽입되는 문제(패킷이 중복되고 TCP 데이터가 충돌하게 된다)를 방지한다.
    • 동일한 패킷이 2분 이내에 생성되지 않도록 막는다.
  • 일반적으로 커넥션 종료 지연이 문제되지는 않지만 성능 테스트 시 부하를 발생시킬 컴퓨터 수는 적기 때문에 가능한 연결 조합(발신지 IP, 포트, 수신지 IP, 포트)이 제한된다. 이 때문에 순간순간 포트 재활용이 불가능해진다.

HTTP 커넥션 관리

흔히 잘못 이해하는 Connection 헤더

  • HTTP는 클라이언트-서버 사이에 프락시 서버, 캐시 서버 등의 중개 서버를 허용한다. 이때 HTTP 메시지는 중개 서버들을 전부 거치면서 전달된다.
  • HTTP Connection 헤더 필드는 커넥션 토큰을 쉼표로 구분해 가진다. 이 값들은 다른 커넥션에 전달되지 않는다.
  • Connections 헤더에는 다음 세 가지 종류의 토큰이 전달될 수 있다.
    1. HTTP 헤더 필드명: 현재 커넥션에만 해당되는 헤더를 나열한다. 현재 커넥션만을 위한 정보이므로 다음 커넥션에 전달하면 안 된다.
    2. 임시적인 토큰 값: 커넥션에 대한 비표준 옵션이다.
    3. close 값: 현재 커넥션은 작업 완료(메시지 전송 완료) 후 끊어져야 한다.

순차적인 트랜잭션 처리에 의한 지연

  • 각 트랜잭션을 순차적으로 처리하면 지연이 발생해 성능이 좋지 않다.
  • HTTP 커넥션 성능 향상을 위한 방법에는 아래가 있다.
    1. 병렬 커넥션
    2. 지속 커넥션
    3. 파이프라인 커넥션
    4. 다중 커넥션

병렬 커넥션Parallel

  • 클라이언트가 여러 개의 커넥션을 맺음으로써 여러 개의 HTTP 트랜잭션을 병렬로 처리할 수 있게 한다.
  • 클라이언트의 네트워크 대역폭이 좁을 때는 성능상 장점이 거의 없어지기 때문에 병렬 커넥션이 항상 빠른 것은 아니다.
  • 메모리를 더 많이 소모하고 자체적 성능 문제를 발생시킬 수 있다.
  • 병렬 커넥션에는 아래와 같은 단점이 있다.
    • 각 트랜잭션마다 새로운 커넥션을 끊고 맺는다. 이 과정에서 시간과 대역폭이 소요되고 느린 시작 때문에 성능이 떨어진다.
    • 실제 연결 가능한 병렬 커넥션 수에는 제한이 있다.
  • 브라우저는 대체로 6~8개의 병렬 커넥션만을 허용한다.

지속 커넥션Persistent

  • 웹 클라이언트는 보통 같은 사이트에 여러 개의 커넥션을 맺는다. 이를 사이트 지역성(site locality)이라고 한다.
  • HTTP/1.1을 지원하는 기기는 처리 완료 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용할 수 있다. 이를 지속 커넥션이라고 한다. 지속 커넥션은 클라이언트나 서버가 커넥션을 끊기 전까지는 트랜잭션 간에도 커넥션을 유지한다.
  • 병렬 커넥션에 비해 지속 커넥션은 아래와 같은 장점이 있다.
    • 커넥션 작업과 지연을 줄여준다.
    • 튜닝된 커넥션을 유지해 느린 시작으로 인한 지연이 적다.
    • 커넥션의 수를 줄여준다.
  • 하지만 계속 연결된 상태의 커넥션이 쌓이면 로컬 리소스, 원격 클라이언트와 서버의 리소스에 불필요한 소모를 발생시킨다.
  • 지속 커넥션과 병렬 커넥션을 함께 사용할 때 가장 효과적이다.
  • HTTP/1.0+의 Keep-Alive 커넥션
    • 하나의 지속 커넥션으로 요청을 처리한다.
    • HTTP/1.1에서는 명세에서 제외되었지만 아직 널리 사용되기 때문에 HTTP 애플리케이션은 keep-Alive를 처리할 수 있게 개발해야 한다.
    • 클라이언트는 커넥션을 유지하기 위해서 요청에 Connection: Keep-Alive 헤더를 포함시킨다. 이 요청을 받은 서버는 그 다음 요청도 해당 커넥션을 통해 받고자 하는 경우에만 응답에 동일한 헤더를 포함시킨다.
    • Keep-Alive는 커넥션을 유지하기를 바라는 요청이고 보장하지는 않는다.
  • 멍청한 프락시(dumb proxy)는 오래되거나 단순해 Connection 헤더를 처리하지 못하고(현재 홉에서 제거하지 못하고) 다음 홉에 요청을 그대로 전달할 수 있다. 이로 인해 Keep-Alive가 잘못 전달되면 브라우저나 서버가 타임아웃이 나서 커넥션이 끊길 때까지 기다릴 수 있다.
    • 위와 같은 잘못된 통신을 피하기 위해 프락시는 Connection 헤더와 Connection 헤더에 명시된 헤더들을 절대 전달하면 안 된다.
  • Proxy-Connection
    • 넷스케이프는 멍청한 프락시 문제를 해결하기 위해 Connection 헤더 대신 비표준인 Proxy-Connection 확장 헤더를 프락시에게 전달한다.
      • 프락시가 Proxy-Connection을 무조건 전달해도 웹 서버는 그것을 무시하기 때문에 문제가 발생하지 않는다.
      • 영리한 프락시는 의미 없는 Proxy-Connection을 Connection으로 바꿔 원하던 효과를 얻을 수 있다.
  • HTTP/1.1의 지속 커넥션
    • HTTP/1.1에서는 Keep-Alive를 지원하지 않는 대신 설계가 더 개선된 지속 커넥션을 지원한다.
    • 별도 설정이 없는 경우 모든 커넥션을 지속 커넥션으로 취급한다.
    • 트랜잭션이 끝난 다음 커넥션을 끊으려면 Connection: close 헤더를 명시해야 한다.

파이프라인 커넥션Pipelined

  • HTTP/1.1은 지속 커넥션을 통해 요청을 파이프라이닝할 수 있다.
  • 여러 개의 요청은 응답이 도착하기 전까지 큐에 쌓인다. 첫 요청이 서버로 전달되면 이후 요청이 전달될 수 있다.
  • 파이프라인의 제약 사항은 아래와 같다.
    • 커넥션이 지속 커넥션인지 확인하기 전에는 파이프라인을 이어서는 안 된다.
    • 응답은 요청 순서와 같게 와야 한다.
    • 클라이언트는 커넥션이 언제 끊어지더라도 완료되지 않는 요청이 파이프라인에 있으면 언제든 다시 요청을 보낼 준비가 되어 있어야 한다.
    • 클라이언트는 비멱등 요청은 파이프라인을 통해 보내면 안 된다.
  • 우아한 커넥션 끊기
    • 애플리케이션은 TCP 입력 채널과 출력 채널을 끊을 수 있다.
      • close()를 호출하면 입출력 채널을 둘 다 끊는다. (전체 끊기)
      • shutdown()을 호출하면 채널을 하나만 개별적으로 끊는다. (절반 끊기)
    • 단순한 HTTP 애플리케이션은 전체 끊기만을 사용할 수 있다.
    • 애플리케이션이 각기 다른 HTTP 클라이언트, 서버, 프락시와 통신하거나 파이프라인 지속 커넥션을 사용한다면 예상치 못한 쓰기 에러를 예방하기 위해 절반 끊기를 사용해야 한다.
      • 보통 커넥션의 출력 채널을 끊는 것이 안전하다. 반대편의 기기는 데이터 읽기가 끝남과 동시에 커넥션이 끝났다는 것을 알 수 있다.
      • 대부분 운영체제는 연결이 끊긴 입력 채널에 데이터가 도착하면 connection reset by peer 에러가 발생하고, 읽히지 않은 데이터를 모두 삭제한다. 이는 파이프라인 커넥션에서 더 취약하다.
    • 우아한 커넥션 끊기란 자신의 출력을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기는 것을 기다리는 것이다. 이를 사용하면 커넥션은 리셋 위험 없이 온전히 종료될 수 있다.

다중 커넥션Multiplexed

  • 요청과 응답들에 대한 중재

'공부 > ' 카테고리의 다른 글

[HTTP 완벽 가이드] 5부 학습  (0) 2025.05.29
[HTTP 완벽 가이드] 3부 학습  (0) 2025.05.23
[HTTP 완벽 가이드] 2부 학습  (0) 2025.05.23
[단위 테스트] 학습  (0) 2025.01.24