본 내용은 RFC7230을 기반으로 작성되었습니다.
영문판을 기반으로 해석을 통해 작성하였으며, 틀린 내용 댓글로 알려주시면, 수정할 수 있도록 하겠습니다.
HTTP란?
HTTP는 HyperText Transfer Protocol의 약자로, 네트워크를 기반으로 작동하는 무상태 어플리케이션 레벨 요청/응답 프로토콜을 말한다. 이 응답 프로토콜이 사용하는 것으로는 네트워크 기반 하이퍼 텍스트 정보시스템과 유연한 상호작용을 위한 확장 가능한 메시지 페이로드를 포괄하고 있다.
처음 들으면, 무엇인지 이해하기 힘든데, 중요한 키워드 2가지 무상태와 어플리케이션 레벨만 정리하고 넘어가자
첫번째로, 무상태란 무엇일까?
무상태를 설명하기 위해서는, 그 전에 Connectionless(비연결성)에 대해서 설명해야된다. 비연결성이란, 서버와 클라이언트가 한번 연결을 맺고, 클라이언트가 요구한 자료를 서버가 다 보내고 나면 연결을 끊어버리는 성격을 의미한다. 연결성을 유지하면, 서버는 지금 요청한 클라이언트가 누군지 지속적으로 기억할 수 있고 이를 통해 작업의 맥락을 이어나갈 수 있다.(내가 지금 로그인하면 서버는 지금 통신하고 있는 클라이언트가 내가 로그인한 것을 기억하고 데이터를 유지한다) 하지만, 이렇게 하지 않는 것은 연결성을 유지하는 것이 너무나도 큰 비용이 들기 때문이다. 서버에 접속하는 모든 클라이언트에 대해서 연결성을 유지하면, 그 수만큼 오버헤드가 커지기 때문이다.
커넥션을 유지한다는 것은, 해당 클라이언트에 대해서 서버가 지속적으로 데이터를 가지고 통신을 연결해야하는 부담이 생기는데, 이것은 서버가 다수의 연결을 받아들이는 것이 큰 부담을 준다. 이런 문제를 해결하기 위해, HTTP는 비연결성을 지향하고 이 때문에 우리는 HTTP 연결을 할 때마다 연결을 복구하는 비용을 들여야한다. HTTP가 연결을 끊고 다시 연결되기 위해서는, HTTP을 지탱하고 있는 프로토콜인 IP, TCP 프로토콜을 매번 작동시켜야되는 것이다. 그리고 이런 비연결성의 특징은 무상태 프로토콜과 연관된다.
무상태란? Connectionless로 인해 서버가 클라이언트를 식별할 수 없는 것을 무상태라고 한다. 예를들어, 내가 서버에 로그인을 하더라도, 내가 다시 GET 요청으로 index.html을 가져오면 서버는 아까 로그인 한 '나'와 지금 index.html을 요청한 '나'를 별개의 존재로 인식한다. 요청할 때마다 새롭게 통신을 구축하기 때문에 이렇게 무상태성이 생기게 된다.(물론, KeepAlive와 쿠키를 통해서 위에 있었던 모든 예시와 다르게 작동시킬 수 있지만, 기본적인 것은 이렇게 작동한다)
두번째로, 어플리케이션 레벨이란 무엇일까?
CS를 공부하다가 보면, OSI 7계층이라는 것을 자주 보게 된다. 네트워크가 작동하는 계층을 7가지로 나눈 것인데, 이를 말로 간단하게 표현하면 이렇게 표현할 수 있겠다. 내가 사용하는 프로그램은 문으로 포트라는 것을 가지고 있고, 컴퓨터는 자신의 이름으로 IP를 가지고 있으며, 이 데이터는 물리 계층에서 LAN카드를 통해서 이루어진다. 모든 HTTP통신은 자신의 포트를 이용하여, TCP 통신을 통해 안전성을 확보하고, IP를 통해서 상대를 찾아 나서며, LAN카드를 통해서 실제 전기 신호를 밖으로 보낸다.
HTTP는 TCP, IP에 의존하고 있고, 이는 HTTP 통신을 할 때마다 IP와 TCP 프로토콜이 작동된다는 것을 의미한다. 우리가 브라우저에 있는 어떤 URI에 데이터를 요청하면(GET 요청) 이는 물리계층으로 갈 때까지 각각의 프로토콜에 의해서 Warp된다. 그리고 이를 작동시킬 때 꼭 필요한 것들이 포트라고 생각하면 된다.
HTTP 아키텍처
본 문 내용은 대부분 RFC7230에 의존하고 있습니다. 내용중 중요하다고 생각하는 해석을 작성하되, 설명이 필요한 내용만 추가적으로 설명할 예정입니다.
1. HTTP는 신뢰성 있는 전송 계층(TCP) 또는 세션 계층의 메시지 교환에 의해 작동되는 상태없는 요청/응답 프로토콜이다.
2. HTTP 클라이언트(브라우저 등)는 하나 이상의 HTTP 요청들을 전송하기 위한 목적으로 커넥션을 서버에 설립하는 프로그램이다. 반대로, HTTP 서버(Nginx 등)은 HTTP 응답을 전송함으로써 HTTP 요청을 처리하기 위해 커넥션을 허용하는 프로그램이다.
3. Origin Server(오리진 서버)라는 용어는 제시된 특정 대상 리소스에 대답할 수 있는 권한 있는 응답을 할 수 있는 서버를 의미한다. (그렇기 때문에 Proxy 서버는 오리진 서버가 아니다)
4. Sender(발신자)와 Recipient(수신자)는 각각 주어진 메시지를 발송하고 전달받기위해 구현된 것들을 의미한다.
5. HTTP는 로소스와 리소스들간의 관계에 있어서는 RFC3986에 의존한다.
6. 클라이언트는 HTTP요청을 할 때, 서버에 메시지 형식, Method, URI 그리고 프로토콜 버전을 토함하고 있는 REQUEST-LINE을 시작으로, request modifier, 클라이언트 정보, 표현 메타데이터 등을 통함하는 헤더필드로 이어지며, 헤더필드의 끝은 빈 줄로 나타낸다. 그 이후에는 Body를 포함한다.
해당 문장은 HTTP Request 문장이 어떻게 구성되어야하는지를 이야기하며, 모든 HTTP 요청은 위와 같은 형식을 갖추어 작성되어야한다. 반대로 서버에서 클라이언트에 응답을 할 때에도 비슷한 규칙이 있다.
7. 서버는 클라이언트에게 HTTP응답 메시지를 작성할 때, 각각의 프로토콜 버전, 성공 혹은 에러 코드, 원문 그 자체의 상태코드를 포함한 status-line(예: 200 OK) 그리고 가능하다면 서버정보, 리소스의 메타데이터와 표현 메타 데이터를 포괄하는 헤더 필드 그리고 헤더 필드 다음에는 끝을 의미하는 빈 줄을 포함해야한다. 또, 마지막으로는, 페이로드 바디를 포함한 메시지 본문으로 작성되어야한다.
서버와 클라이언트가 HTTP 통신을 할 때에는 위와 같은 방식으로 요청과 응답을 작성해야된다. 이런 정의된 형식을 바탕으로 Connection은 다수의 요청/응답 교환을 위해 사용될 수 있다.
HTTP 요청 예시
GET /objectserver/restapi/alerts/status HTTP/1.1
Accept: application/json
Authorization: Basic dGVzdHVzZXIwMTpuZXRjb29s
Host: localhost
Connection: keep-alive
HTTP 응답 예시
HTTP/1.1 200 OK
Cache-Control: no-cache
Server: libnhttpd
Date: Wed Jul 4 15:32:03 2010
Connection: Keep-Alive:
Content-Type: application/rdf+xml
Content-Length: 24860
// 이하 생략(BODY)
8. HTTP를 설계 할 때, 모든 사용자 에이전트들이 범용 브라우저들이고 모든 원 서버(Origin Server)들이 큰 규모의 공용 웹사이트라고 생각하기 쉽다. 하지만, 보통의 HTTP 사용자 에이전트(Client)들은 체중계, 음향기기들 같이 다양한 모양과 크기의 통신 기기들을 포함한다. 마찬가지로 대부분의 HTTP 원 서버들은 홈 자동화 장치, 사무용 기계, 뉴스 피드 등등을 포괄한다.
9. HTTP는 중개자을 이용하여 커넥션을 충족시킬 수 있다. 즉, 프록시(Proxy), 게이트웨이, 터널 등을 이용하여 통신을 연결할 수 있다.
10. 통신의 방향은 UPSTREAM과 DOWNSTREAM으로 구성되어있으며, 모든 메시지들은 UPSTREAM에서 DOWNSTREAM으로 흐른다. INBOUND와 OUTBOUND는 요청 라우트의 관계에서 방향을 설명하기 위해 사용한다. INBOUND는 원서버로 향하는 메시지를 의미하며, OUTBOUND는 사용자 에이전트로 향하는 것을 의미한다.
11. 프록시(PROXY)란? 클라이언트에의해 선택되고, 일반적으로는 로컬 구성 규칙들을 통해 일부 절대 URI의 유형을 처리하기 위해 만들어진 메시지 전송 에이전트다. 프록시는 HTTP와 HTTP 간의 중개자 역할을 하며, 다른 어플리케이션 레벨 프로토콜 간에 변화도 가능하다. 프록시는 주로, 보안적 이익 혹은 공유 캐시 등등을 위한 공통 중개자로써 집단의 HTTP요청들을 그룹화 하는 것에 자주 사용된다.
12. 게이트웨이(GATEWAY)란? 아웃바운드 커넥션을 위한 원서버의 역할을 하기도 하지만, 수신된 요청을 다른 서버로 변환하여 인바운드로 전송하는 중간 서버 역할도 한다. 게이트웨이는 오래되거나 신뢰할 수 없는 정보들을 캡슐화하는 것에 사용되며, 서버 성능 개선을 위한 캐싱 혹은 파티셔닝 등도 수행할 수 있다. 또, 여러 시스템 간의 HTTP 로드 밸런싱을 할 수 있게 해준다.
13. 터널(TUNNEL)란? 메시지 변경 없이 2개의 커넥션에 숨겨진 중개자로써 역할을 하는 존재이다. 터널이 활성화 되면, 터널은 HTTP 통신의 당사자로 고려되지 않는다. 또, 두 끝점에서 전달하는 커넥셙이 종료되면 터널은 더 이상 존재하지 않게 된다.
14. HTTP는 무상태 프로토콜로 정의되어있으며, 이는 각 요청 메시지는 개별적으로 이해해야된다는 것을 의미한다. 따라서, 서버는 (커넥션이 보안되어 있고, 해당 에이전트를 특정하지 않는 한) 동일한 커넥션에 대한 두개의 요청이 동일한 사용자 에이전트에서 온 것으로 가정해서는 안된다. 만약 이를 위반한다면, 이는 보안 및 상호작용에서 문제를 일으킬 것이다.
15. 캐시(CACHE)란? 이전 응답 메시지의 로컬 보관소이고, 메시지의 저장 검색 삭제를 관리하는 서브 시스템이다. 캐시는 캐시 가능한 응답을 저장하여 향후 동일한 요청에 대한 응답 시간과 네트워크 대역폭 사용을 줄일 수 있다. 어떤 서버도 캐시를 포함할 수는 있지만, 캐시는 서버가 터널 역할을 하는 동안에는 사용될 수 없다.
16. 캐시의 효과는 요청/응답 간에 적용할 수 있는 캐시가 있는 경우 전체적인 프로토콜 체인이 단축되는 효과를 준다. 만약, A-C간의 연결 내에 프록시가 4개 있는 경우, 첫번째 프록시에 캐시가 있다면 총 5번의 연결이 필요한 통신이 1번으로 단축되는 효과를 발생시킬 수 있다.
17. WWW와 여러 대규모 조직에 의해 배치된 캐시의 설계와 구성은 매우 다양한다. 대양(예컨데, 태평양)을 횡단하는 대역폭을 아끼기 위한 국가 계층의 프록시 등등 다양한 종류의 캐시가 존재할 수 있다.
18. HTTP 구현과 관련하여서, HTTP에 맞는 규칙들과 관련된 모든 요구사항을 충족한다면 해당 구현은 적합한 것으로 간주한다. 적합함에는 다음과 같은 예시들이 있을 수 있다. 첫째, 발신자는 발신자가 거짓이라는 의미를 전달하는 프로토콜 요소를 생성하면 안된다. 둘째, 발신자는 ABNF 규칙에 대응하는 문법과 맞지 않는 프로토콜을 생성하면 안된다. 셋째, 주어진 메시지에서, 발신자는 다른 약할의 참가자만 생성할 수 있는 프로토콜 요소나 구문 대안을 생성해서는 안된다.
19. HTTP는 구현의 문맥 배치와 목적에 따라서 적절하다고 판단되는 길이가 다양할 수 있기 때문에, 대부분의 프로토콜 요소에 있어서 특정 길이 제한을 두고 있지 않다. 따라서, 프로토콜 요소의 적절한 길이는 수/발신자 간의 공유된 예상에 의존한다.
20. 최소한, 수신자는 동일한 프로토콜의 다른 메시지에 대하여 프로토콜 요소 길이를 구문분석하고 처리할 수 있는 능력을 갖춰야한다.
21. HTTP는 <메이저 버전>.<마이너 버전> (예 : 1.1)으로 구성되어있고, 이는 프로토콜의 버전을 표시하기 위해 사용한다.(RFC7230은 HTTP 1.1을 정의한다)
22. HTTP1.1 메시지가 HTTP1.0 수신자 혹은 알 수 없는 수신자에게 전달될 경우, HTTP1.1은 유효한 HTTP1.0 버전으로 해석될 수 있도록 구성된다.
23. 새로운 헤더 필드는 프로토콜 버전을 변경하지 않고도 도입될 수 있다. 새로운 헤더 필드의 정의는 그것을 인식(알고있지) 못하는 수신자들에 의해 완전하게 무시될 수 있다.
24. HTTP 메시지를 처리하는 중개자(PROXY 등) 은 전달된 메시지로 자산의 HTTP 버전을 전송해야된다. 중개자는 메시지의 프로토콜 버전이 메시지 수신과 발신 모두에 적합한 버전인지 확인하지 않고, 메시지의 첫번째 줄을 맹목적으로 전달하는 것을 허용하지 않는다.
25. 클라이언트의 메이저 버전(HTTP 2 혹은 1)이 서버가 지원하는 가장 높은 버전보다 높지 않고, 클라이언트가 가장 높은 버전을 호환하는 것으로 확인된다면, 클라이언트는 가장 높은 버전의 방식으로 요청을 전송해야된다. 또한, 클라이언트는 적합하지 않은 버전의 요청을 보내서는 안된다.
26. 서버가 HTTP 규격을 잘못 구현한 것으로 확인되어도, 클라이언트는 하나의 정상적인 요청을 시도한 후, 서버가 잘못된 응답 상태 코드 혹은 헤더필드를 보내는 것을 확인 후에만, 클라이언트는 하위 버전으로 요청을 보낼 수 있다.
27. 반대로, 클라이언트가 HTTP 사양을 잘못 구현한 것으로 확인되거나, 의심되는 경우 서버는 요청에 대해 HTTP1.0으로 응답을 보낼 수 있다. 하지만, 이런 다운그레이드는 클라이언트의 요청 헤더에 있는 한 개 이상의 특정 값들이 에러가 있다고 확인되지 않으면 실행되어서는 안된다.
28. URI는 리소스를 식별하는 수단으로, HTTP 통신 전체에서 사용된다. URI는 타겟 지정, 리다이렉트 표시, 관계 정의에 사용된다.
29. 지정된 포트에서 TCP 커넥션을 청취하는(LISTEN) HTTP 원서버에 의해 관리되는 식별자를 해석하기 위한 목적으로, HTTP URI 문법이 정의되어 있다. ( http-URI = "http:" "//" authority path-abempty ["?" query] ["#" fragment]
30. 호스트 식별자가 IP주소로 제공되는 경우, 원 서버는 해당 IP주소의 지정된 TCP포트에서 청취하고 있는 대상이 된다. 포트 하위 구성 요소가 비어있거나 지정되지 않은 경우 TCP 포트 80이 기본값이다. (HTTPS는 443)
31. 물론, 주어진 권한 구송요소와 URI가 있다고 해서, 해당 호스트 및 포트가 항상 HTTP통신을 대기중인 것은 아니다.
32. HTTP는 전송 프로토콜과 독립적이지만, HTTP 문법은 이름 위임 프로세스가 TCP에 의존하기 때문에, TCP 기반 서비스에서만 사용된다.(1.1 기준)
33. HTTPS URI 스키마 또한, 계층적으로 관리된 네임 스페이스의 ㅇ녀계에 따라 식별자를 만들기 위한 목적으로 정의된다. HTTPS의 스키마는 HTTP의 스크마의 요구조건을 계승하며, 포트 443을 기본값으로 가진다. 사용자 에이전트(CLIENT)는 첫번째 HTTP 요청을 보내기 전에 강력한 암호화 사용을 통해 원 서버에 대한 커넥션이 안전한지 확인해야만 한다.
34. 포트가 스키마의 기본포트와 같다면(HTTP는 80, HTTPS는 443) 일반적인 형태는 포트 하위 구성요소를 생략한다. 예컨데, http://songseungwoon.com 와 http://songseungwoon.com:80은 동일하다.
'개발 공부 > WEB의 심연' 카테고리의 다른 글
끝까지 파보는 HTTPS (0) | 2022.12.05 |
---|---|
브라우저는 어떻게 작동하는가? (주소창에 엔터를 누르면 생기는 일) (0) | 2022.12.05 |
브라우저는 어떻게 작동하는가? (브라우저의 프로세스) (0) | 2022.12.05 |
브라우저는 어떻게 작동하는가? (0) | 2022.12.05 |