Search

HTTP 프로토콜에 대해 알아보자.

시작하기 앞서

HTTP 프로토콜의 구조에 대해서 알아보려고 한다. (주로 HTTP/1.1을 다루고자 한다)

HTTP는 클라이언트와 서버 간에 통신을 한다.

텍스트와 이미지 등과 같은 리소스를 필요하다고 요청하는 쪽이 클라이언트이다.
요구하는 리소스를 제공하는 쪽이 서버가 된다.
즉, 반드시 클라이언트 측에서 리퀘스트를 보내면, 서버 측에서 리스폰스가 돌아온다. (서버 측은 리퀘스트를 받지 않고서는 리스폰스를 송신하는 일은 없다)

리스폰스 구조

HTTP /1.1 200 OK Date: Tue, 10 Jul 2020 06:50:20 GMT Content-Length: 362 Content-Type: text/html <html> ...
Plain Text
첫 번째 줄은 HTTP 버전과 리퀘스트의 처리 결과를 나타내는 상태 코드와 설명이 있다.
다음 줄은 리스폰스가 발생한 일시를 나타내고 있는데 이를 헤더 필드라고 불리는 것 중에 하나이다.
빈 줄로 구분하는데 아래에 있는 부분이 바디라고 불리며 리소스 본체를 의미한다.

HTTP는 상태를 유지하지 않는다.

HTTP는 상태를 유지하지 않는 stateless 프로토콜이다.
새로운 리퀘스트가 보내질 때 마다 새로운 리스폰스가 생성된다.
과거의 리퀘스트나 리스폰스 정보를 가지지 않는다. 왜냐하면 많은 데이터를 매우 빠르고 확실하게 처리하는 범위성을 확보하기 위해 간단하게 설계되어 있다.
로그인 상태를 유지할 필요가 있는데 이를 해결하기 위해 쿠키가 등장했다.
쿠키(Cookie)? HTTP의 stateless 성질로 인해 이전 상태를 관리하지 않는다. 이 문제를 해결하기 위해 쿠키가 등장했다. 쿠키는 리퀘스트와 리스폰스에 쿠키 정보를 추가해서 클라이언트의 상태를 파악하기 위한 시스템이다. 먼저 서버에서 리스폰스로 보내진 Set-Cookie라는 헤더 필드에 의해 쿠키를 클라이언트에 보존한다. 그 다음에는 클라이언트가 같은 서버로 리퀘스트를 보낼 때, 쿠키 값을 넣어서 송신한다. 그러면 서버에서 쿠키를 확인하고 어느 클라이언트가 접속했는지 식별할 수 있게 된다.

리퀘스트 URI로 리소스를 식별하자.

URI를 사용하여 인터넷 상의 리소스를 지정한다. 따라서 인터넷 상의 어떤 장소에 있는 리소스도 호출이 가능하다.
서버 자신에게 리퀘스트를 송신하는 경우에는 아래와 같이 URI에 '*'을 지정할 수 있다.
OPTIONS * HTTP/1.1

HTTP 메소드로 서버에 임무를 부여하자.

메소드는 대문자와 소문자를 구별하기 때문에 대문자로 기재할 필요가 있다. 괄호 안에는 제공하고 있는 HTTP 버전을 의미한다. LINK, UNLINK라는 메소드도 존재하는데 이는 1.1버전에서 폐기처리 되었음을 알고 있자.

GET : 리소스 획득 (1.0, 1.1)

리퀘스트 URI로 식별된 리소스를 가져올 수 있도록 요구하는 용도로 사용한다.
클라이언트: "너가 이거 가지고 있으면 나한테 줘!" → 서버: "여기있어!" 혹은 "없어!"

POST : 엔티티 바디 전송 (1.0, 1.1)

엔티티를 전송하기 위해 사용된다.
클라이언트: "이 정보를 너한테 줄게!" → 서버: "알았어! 알아보고 응답해줄게"

PUT : 파일 전송 (1.0, 1.1)

파일은 전송하기 위해 사용된다. 리퀘스트 URI로 지정한 곳에 보존하도록 요구한다.
클라이언트: "이 파일 너한테 줄게!" → 서버: "너가 말한 곳에 저장할게!"
그러나 HTTP/1.1 PUT 자체에 인증 기능이 없기 때문에 누구든지 파일을 업로드 가능하다는 보안 상의 문제로 일반적인 웹 사이트에서는 사용되지 않고 있다.

HEAD : 메서드 헤더 취득 (1.0, 1.1)

GET과 같은 기능인데 메시지 바디는 돌려주지 않는다. 주로 URI 유효성과 리소스 갱신 시간을 확인하는 목적 등으로 사용된다.
클라이언트: "그 정보를 알려줘" → 서버: "알았어!"

DELETE : 리소스 삭제 (1.0, 1.1)

리퀘스트 URI로 지정된 리소스 삭제를 요구한다.
클라이언트: "이거 지워줘!" → 서버: "알았어!"

OPTIONS : 제공하고 있는 메서드 문의 (1.1)

리퀘스트 URI로 지정한 리소스가 제공하고 있는 메소드가 뭔지 확인하는 용도로 사용한다.
클라이언트: "어떤 명령이면 들어줄 수 있어?" → 서버: "GET과 HEAD라면 들어줄 수 있어!"

TRACE : 경로 조사 (1.1)

Web 서버에서 접속해서 자신에게 통신을 되돌려 받는 루프 백을 발생시킨다.
클라이언트: "어떤 과정을 거칠까?" → 프록시 서버: Max-Forwards(2)
→ 프록시 서버: Max-Forwards(1) → 오리진 서버: "응답해줄게!" Max-Forwards(0)
Max-Forwards라는 헤더 필드에 수치를 포함시킨다.
서버를 통과할 때마다 수치를 줄여간다.
수치가 0이 된 곳을 끝으로 르퀘스트를 마지막으로 수신한 곳에서 상태 코드 200 OK를 되돌려 준다.
프록시 등을 중계해서 오리진 서버에 접속할 때 동작을 확인하기 위한 용도로 사용된다.
그러나 크로스 사이트 트레이싱(XST)과 같은 공격을 일으키는 보안 상의 문제로 보통 사용되고 있지 않다.

CONNECT : 프록시에 터널링 요구 (1.1)

프록시에 터널 접속 확립을 요구하며, TCP 통신을 터널링 시키기 위해 사용된다. 주로 SSL이랑 TLS 등의 프로토콜로 암호화된 것을 터널링 시키기 윟 ㅐ사용된다.
클라이언트: "잠깐 지나갈게!" → 프록시 서버: "OK!" → 오리진 서버

지속 연결로 접속량을 절약

HTTP 초기 버전에서는 HTTP 통신을 한 번 할 때마다 TCP에 의해 연결을 종료할 필요가 있었다. 초기에는 작은 사이즈의 텍스트를 보내는 정도였기 때문에 문제는 없었다. 그러나 HTTP가 널리 보급되고 하나의 페이지에서 여러 이미지 등을 요구하면서 다량의 통신이 발생하는 문제가 생겼다. 즉, 리퀘스트를 보낼 때마다 매번 TCP 연결과 종료하면서 통신량이 늘어나는 문제가 생기는 것이다.

지속 연결로 해결해보자!

지속 연결은 무엇을 해결할 수 있을까?

TCP 연결 문제를 해결하기 위해 지속 연결이라는 방법이 고안되었다.
지속 연결은 한 쪽이 명시적으로 연결을 종료하지 않는 이상 TCP 연결을 계속 유지하는 것이다.

어떤 이점이 있을까?

TCP 커넥션의 연결과 종료를 반복되는 오버헤드를 줄여줄 수 있는 효과가 있다. 이로 인해 서버에 대한 부하가 감소할 수 있다. 따라서 오버헤드를 줄인 만큼 HTTP 리퀘스트, 리스폰스가 빠르게 완료되기 때문에 웹 페이지를 빨리 표시할 수 있는 효과가 있다.
여러 리퀘스트를 보낼 수 있도록 파잎프라인화를 가능하게 한다. 이로 인해 리퀘스트 후에 리스폰스를 기다리지 않고, 바로 다음 리퀘스트를 보낼 수 있다. (여러 리퀘스트를 병행해서 보낼 수 있다)

참고 자료