HTTP의 request - response 메시지의 헤더와 친해지기
들어가며
- HTTP 프로토콜의 통신 과정은 request와 response의 교환 과정이다. 클라이언트는 서버에 request를 통해 요구사항을 보내고, 서버는 클라이언트에게 response를 통해 응답한다. 정확한 통신을 위하여 HTTP 에는 통신 규칙이 있다.
- HTTP 메시지의 구조와 각각의 구성 요소의 역할을 이해하는 것을 목표로 한다.
HTTP 메시지의 기본 형태
시작줄
헤더
줄바꿈(CRLF)
본문(entity)
- request의 기본적인 형태는 아래와 같다.
- 시작줄은 요청메소드 - 요청URL - http의 버전으로 이뤄져 있다.
- 이하 헤더와 바디 등으로 이뤄진다. 아래는 HOST 헤더가 있다.
GET /read/001.html HTTP/1.1
HOST: naver.com
- reponse의 기본적인 형태는 아래와 같다.
- 시작줄은 http의 버전 / 상태코드 / 사유구절로 이뤄져 있다. 사유구절은 사람이 이해할 수 있은 형식으로 표현을 했을 뿐, 중요한 부분은 앞의 두 개다.
- 헤더에는 Content-Type과 Content-length가 있다. 본문(Entity)의 데이타 타입과 길이를 의미한다. 데이터 타입은 MIME의 규칙에 따라 작성한다.
HTTP/1.1 200 OK
Content-Type : text/html
Content-length : 600
<html><body><div>http란 Hypertext Transfer Protocol의 준말로 TCP/IP의 통신 방식 중 하나로서..... (후략)
헤더 필드의 분류
- 헤더는 필드와 필드값으로 이뤄져 있다. 필드는 4가지로 분류된다.
- 일반헤더 : 일반적인 내용을 다룬다.
- 요청헤더 : 클라이언트가 서버에 요청 메시지를 위한 내용
- 응답헤더 : 서버가 클라이언트에 응답 메시지를 위한 내용
- 엔티티헤더 : 본문에 관련한 내용. 엔티티는 html의 body에 국한된 표현이라 다양한 타입을 수용할 수 있도록 표현-표현헤더란 표현으로 자주 사용한다.
- 현재의 블로깅으로는 간단하게 정리하고자 하며, 구체적인 내용은 링크 혹은 다른 자료를 참고하자.
요청헤더
- Host
- http 1.1 규약에 따라 request 헤더가 반드시 가져야 할 필드.
- 아이피에 여러 인터넷 호스트가 존재할 경우 host 필드 값을 명확하게 해야 함.
- Accept
- 서버에 요청하는 데이타의 타입
- HTTP는 MIME의 방식으로 인코딩/디코딩 및 데이타 통신을 함. 그러므로 필드값은 MINE 문법에 따라 작성
Accept : */*
: 모든 데이타 타입을 받는다. -Accept : text/html, application/json
: html과 json을 값으로 받는다.Accept : image/jpg, image/*;p=0.5
: p는 0과 1 사이의 정수이며 우선순위를 의미한다. 기본 값은 1이다. 예의 경우, jpg를 최우선으로 받되, 해당 포맷이 아닐 경우 그 이외의 숫자를 0.5의 우선순위로서 받는다는 의미이다.
- Authorization : 서버가 요구에 따른 인증값을 서버에 제출한다. 이를 통해 request 의 인증, 인가를 검토한다. BASIC, DIGEST 등등 다양한 형태의 보안방식이 존재한다.
- Expect : 서버에 특정 동작을 요구함. 원하는 HttpStatus(100, 200, 404 등)을 입력
조건부 리퀘스트 (if)
- 클라이언트가 소유한 엔티티가 서버의 엔티티와 같은지 여부를 판단할 때 사용.
- 클라이언트의 사본과 서버의 원본 엔티티의 고유번호(태그)가 같은지(it-match), 사본과 원본의 수정일이 같은지(if-modified-since)를 확인.
- 한편, range라는 기능을 통해, 용량이 큰 엔티티를 분할해서 받을 수 있는데, 아직 다운로드 받지 못한 부분의 엔티티의 태그의 값이 서버가 가진 값과 다르면, 무조건 새로운 엔티티를 서버가 클라이언트에 전달함.
응답헤더
- age : 클라이언트가 받은 엔티티의 사본이 얼마나 오래 되었는지를 보여줌. 서버는 사본의 max-age, expired(엔티티헤더) 등을 통해 엔티티의 유효기간을 정하며, 그 기간 동안 서버로부터 데이터를 가져오지 않고, 캐쉬로 응답한다. 이를 통해 서버의 자원을 아낀다.
- ETag : 엔티티의 고유번호를 부여하여 캐싱처리한다. 엔티티 리소스가 갱신될 경우 ETag도 변경되며 이전의 ETag를 가진 경우 요청을 보내고 그렇지 않으면 캐싱처리 한다.
- WWW-Authenticate 등 서버의 보안 관련 헤더
엔티티 헤더 필드
- Allow : request가 요청한 리소스에서 가능한 통신 매서드를 답변. Get, Post 등.
- Content-Encoding : 데이타를 전달할 때 압축 형식을 지정
- Content-Length : 리소스의 용량
- Content-Location : 리소스의 위치(URI)
- Content-MD5 : 데이타의 유효성 검사를 위하여 MD5알고리즘에 의해 생성된 값을 전달(그러나 MD5 역시 변조가 가능하므로 완전하게 신뢰할 수 없음)
- Content-Type : 엔티티 바디가 포함하는 미디어 타입과 문자 인코딩 타입을 전달.
Content-Type : text/html; charset=utf-8
그 외
- set-cookie : 쿠키 생성
- 사용자의 정의 아래에 필드와 그 값을 설정할 수 있음.
심화1. 헤더와 캐싱처리 - 조건부 리퀘스트
- 특정 자원을 받을 때 해당 자원이 변경되었는지를 확인하기 위하여 조건부 리퀘스트 헤더를 사용한다. 변경사항이 있을 경우 데이터를 다시 받아 브라우저에서 렌더링한다.
-
그 과정은 아래와 같다.
- 클라이언트, 최초 요청
GET /example.html HTTP/1.1
- 서버, 최초 응답. 해당 자원에 대한 eTag(파일의 해시값)와 마지막 변경 일자를 제공한다.
HTTP/1.1 200 OK
ETag: "12345"
Last-Modified: Wed, 21 Oct 2021 07:28:00 GMT
- 클라이언트, 새로고침 등 해당 자원을 활용할 때, 그것이 최신 값인지를 확인
GET /example.html HTTP/1.1
If-None-Match: "12345"
If-Modified-Since: Wed, 21 Oct 2021 07:28:00 GMT
- 서버, 응답1. 변경이 없을 경우
- 서버가 304 응답을 반환하면, 브라우저는 로컬에 저장된 캐시 데이터를 그대로 사용.
HTTP/1.1 304 Not Modified
- 서버, 응답2. 변경이 있을 경우.
HTTP/1.1 200 OK
Content-Type: text/html
ETag: "67890"
Last-Modified: Fri, 22 Oct 2021 12:30:00 GMT
심화2. 헤더와 캐싱처리 - age
- 조건부 리퀘스트는 명확한 태그와 변경일자를 기반으로 처리하나 age는 그것의 기한을 기반으로 처리한다.
-
해당 내용은 아래와 같다.
- 서버, 클라이언트가 캐싱서버에 자원을 요청하고 캐싱서버의 요청에 서버는 응답한다. max-age로 자원의 수명을 정의한다.
HTTP/1.1 200 OK
Cache-Control: max-age=3600
- 캐싱서버, 클라이언트에게 age 값을 제공한다. 이후 max-age가 넘기 전까지 캐싱된 자원을 제공한다.
HTTP/1.1 200 OK
Cache-Control: max-age=3600
Age: 100
- age 헤더는 사용에 있어서 수동적이다. age 값은 캐싱 서버에서만 관리되며, 클라이언트가 age를 변경하거나 서버로 보낸다고 해도 실제 캐시 데이터의 유효성 판단에는 영향을 미치지 않는다. 이는 age가 캐싱 서버의 응답 시간과 유효성을 나타내는 데 초점이 맞춰져 있기 때문이다. age 값은 보통 클라이언트가 자원의 캐싱 정보를 확인하거나 디버깅, 네트워크 성능 비교 정도로 활용한다. 특히 age가 낮다면 캐싱이 자주되어 성능 문제가 발생할 수 있다는 예상을 해볼 수 있다.
- age 또한 유연하게 처리 가능한데, 변경된 데이터가 필요한 경우 자원의 이름을 변경하여 새로운 데이터를 받을 수 있다.
- 조건부 리퀘스트는 ETag나 Last-Modified와 같은 메타데이터를 관리해야 하므로 구현에 복잡하다. 예를 들어, ETag는 데이터 변경 시마다 고유 해시 값을 생성하고 이를 클라이언트 요청과 비교해야 하며, 데이터 변경 관리 로직과의 통합이 필요하다.
- age의 경우 단순하며 변경이 별로 없는 정적 데이터에 자주 사용한다. age와 대비하여 조건부 리퀘스트는 매우 유연하고 세세하게 자원을 관리할 수 있다. 최신 데이터의 동기화처리가 중요할 때 자주 사용한다.
- restful api에서도 활용된다는데 이 부분은 차후 확인해보자
- Cache Control 등에 대한 이해를 바탕으로 해당 부분은 분리하는 것이 나을 것 같다.