HTTP와 관련하여, 그동안 기본적이지만 추상적으로 알고있던 HTTP 및 WEB 관련 내용을, 나도 누군가가 나에게 물으면 쉽게 설명해줄 수 있도록 내 방식대로 정리해보고 싶었다.
🚨 올바르지 않은 내용이 있을 경우 댓글로 남겨주시면 감사드리겠습니다.
만약 내가 미국에 있는 친구에게 메세지를 보내려고할 때 인터넷
이라는 연결망을 통해 보내게 된다. 바로 다이렉트로 친구에게 메세지를 보낼 수 있으면 좋겠지만, 인터넷 연결망은 복잡하기 때문에 수많은 중간 노드 서버들을 거쳐서 목적지까지 도달하게 된다.
그럼 어떻게 이렇게 수많은 노드들을 거쳐서 목적지까지 내가 전달하고자하는 정보를 안전하게 전달할 수 있는 것일까?
IP에 대한 정의는 이렇게나 길지만, 누군가가 물어보면 짧게 이렇게 대답할 것 같다.
클라이언트와 서버 간의 인터넷 통신(데이터 교환 등)을 위해 정해놓은 규약
IP 프로토콜은 크게 2가지 역할을 하게 된다.
사람이 마치 각자의 집주소가 있듯이(ex. 서울시 강남구 대치동 ~)
인터넷도 각자의 주소가 있는데 그것이 바로 IP주소이다.(ex. 190.123.45.678)
흐름은 아래와 같다
➔ IP 프로토콜은 출발지 IP주소, 목적지 IP주소, 그리고 기타 전송 데이터를 담은 IP 패킷(Package + bucket)을 만든다.
➔ 그 후 서버에게 요청을 보내기위해 해당 패킷을 인터넷 망에 투입 시킨다.
➔ 패킷은 목적지 IP주소에 도달하기까지 규약을 따르는 수많은 노드서버들을 거쳐 전달, 전달하게 되고 서버에까지 도달하게 된다.
➔ 서버에서도 마찬가지로 위와 같은 방식으로 다시 목적지에서부터 출발지까지 응답을 패킷을 보내게 된다.
IP 프로토콜의 한계
비연결성
IP 프로토콜은 받을 대상이 없거나, 대상이 서비스 불능 상태여도 패킷을 전송할 수 있다. 즉 클라이언트는 대상 서버가 패킷을 받을 수 있는 상태인지 알 수 없다.
비신뢰성
IP 프로토콜은 수많은 노드 서버들을 거쳐가는 중간에 패킷이 사라지거나, 패킷이 여러개를 보냈을 때 순서를 보장하지 않을 수 있다는 한계가 있다.
프로그램 구분의 문제
같은 IP에서도 사용하는 어플리케이션이 둘 이상(게임, 채팅 등)일 때 대상 서버는 어떤 프로그램에 대한 요청인지 구분하지 못한다.
위와 같은 문제를 해결하기 위해 TCP/IP 프로토콜이 존재한다.
TCP/IP 프로토콜은 기존 IP 패킷 정보(출발지 IP주소, 도착지 IP주소, 기타 데이터 전송 정보)
에 + 출발지 PORT, 목적지 PORT, 전송제어, 순서, 검증 정보 등
을 포함하여 패킷을 주고 받는다. 이를 통해 데이터 전달을 보증할 수 있고 순서까지 보장하게 된다.
TCP 3 way handshake
TCP는 각 장치간 연결을 지향하기 위해 3 way handshake를 사용한다.
TCP 3 Way Handshake는 TCP/IP프로토콜을 이용해서 통신을 하는 응용프로그램이 데이터를 전송하기 전에 먼저 정확한 전송을 보장하기 위해 상대방 컴퓨터와 사전에 연결을 수립하는 과정을 의미한다.
3 way handshake 연결 수립 과정은 아래와 같다.
SYN
라는 접속 요청을 보낸다.ACK
라는 접속 수락과 함께 SYN
이라는 접속 요청을 보낸다.ACK
라는 접속 수락을 보낸 후 서로가 연결을 통해 데이터를 주고 받을 수 있는 상태가 되어진다.3 way handshake는 양쪽 모두 데이터를 전송할 준비가 되었다는 것을 보장하고, 데이터 전달이 시작하기전에 한쪽이 다른 쪽이 준비되었다는 것을 알수 있도록 한다.
하지만 이것이 두 장치가 물리적으로 실제로 연결된 것이 아닌 개념적, 논리적으로만 연결되었다고 생각하면 된다고 한다. 즉, 중간에 거쳐가는 수많은 노드 서버까지 연결이 정상적으로 이루어졌다는 보장은 아니라는 것이다.
TCP/IP는 3 way handshake 연결 수립을 토대로, 클라이언트에서 데이터를 요청 혹은 전달하게 되면 최종 목적지인 서버는 해당 요청이 잘받아졌는지에 대한 응답을 하게 되어, 데이터 전달을 보증한다. 또한, IP 프로토콜만으로 해결하지 못했던 데이터 전달 순서 보장을 한다는 특징을 갖는다. 예를 들어, 패킷1
, 패킷2
, 패킷3
이 패킷1
, 패킷3
, 패킷2
순서로 도착한다면, 서버에서는 다시 패킷2
부터 다시 보내달라는 응답을 통해 순서를 보존할 수 있다는 것이다.
결론적으로 TCP 프로토콜은 신뢰할 수 있는 프로토콜이며 현재 대부분의 어플리케이션에서는 TCP를 사용하고 있다고 한다.
UDP는 TCP위와 같이 IP 바로 상위에 있는 프로토콜 계층에 속한다. UDP는 TCP의 3 way handshake도 없고, 데이터 전달을 보증하지도 않고, 순서를 보장하지도 않는다. 즉, IP와 거의 동일하지만, +PORT, +체크섬 정도만 추가가 되어있는 프로토콜이다.
하지만 연결 지향으로 인해 속도가 상대적으로 느린 TCP에 비해 UDP 그러한 과정이 없고 단순하고 빠르다. 또한 이미 연결 과정이 구축이 되어있는 TCP(이미 인터넷은 TCP기반으로 점유하고 있다)에 비해 UDP는 최적화에 대한 커스터마이징을 할 수 있는 장점을 갖는다.
HTTP3의 경우에는 웹 브라우저의 최적화를 위해 UDP를 사용하는 등 최근에는 UDP의 중요성이 부상하고 있다.
결론적으로, 신뢰성이 요구되는 애플리케이션에서는 TCP를 사용하고 간단한 데이터를 빠른 속도로 전송하고자 하는 애플리케이션에서는 UDP를 사용한다고 이해하면 되겠다.
PORT는 같은 IP 내에서 특정 프로세스를 구분&식별하기 위한 논리 단위이다.
예를 들어 같은 IP주소(ex. 190.123.45.678)내에서도 게임, 화상통화, 웹 브라우저 등 여러 어플리케이션이 존재하는데, 각각 어플리케이션에 PORT Number를 지정해주어(ex 8090, 21000, 100010 등) 클라이언트에서는 해당 프로세스를 처리해주는 서버의 포트넘버로 요청할 수 있게 되고, 서버에서도 마찬가지로 해당 어플레케이션에 맞는 클라이언트 포트넘버로 응답할 수 있게 된다.
도메인 네임 시스템(Domain Name System, DNS)은 호스트의 도메인 이름을 호스트의 네트워크 주소로 바꾸거나 그 반대의 변환을 수행할 수 있도록 하기 위해 개발되었다. 출처- 위키백과
Domain이란 쉽게 말해 외우기 어려운 IP주소(ex. 191.234.56.789)에 이름을 부여한 것이라고 생각하면 된다.(ex. 네이버의 IP 주소는 61.255.165.75이다.)
만약, 클라이언트에서 인터넷 주소창에 naver.com이라는 도메인 네임을 입력하게되면 바로 서버에 요청이 가지 않고 DNS 서버를 거쳐 61.255.165.75라는 IP주소를 응답받게 되고, 해당 원주소 서버에 요청을 보내게되는 것이다.
클라이언트 입장에서는 Domain으로 요청을 보내면 DNS가 해당하는 변경된 IP주소로 매핑하여 응답하기 때문에 만약 서버의 IP주소가 바뀌더라도 문제가 발생하지 않는다는 장점도 가지고 있다.
URI는 간단하게 말해서 자원을 식별할 수 있는 통일된 방법이라고 말할 수있다. URI는 URL(Locator)과 URN(Name)으로 분류될 수 있다.
URL은 리소스가 있는 위치를 지정하고, URN은 리소스 그 자체에 이름을 부여하는 방식이다. 실제로는 URN 이름만으로 실제 리소스를 찾을 수 있는 방법이 보편화 되지 않았기 때문에 URN은 거의 쓰이지않고 결국 URI과 URL이 혼용되어서 같은 의미로 많이 쓰이고 있다.
URL은 아래와 같이 구성되어있다.(userInfo와 fragement는 생략함)
https://www.google.com:443/search?q=hello&hl=ko
- 프로토콜(https)
프로토콜은 어떤 방식으로 자원에 접근할 것인가 하는 약속 및 규칙이다.(http, https, ftp 등)- 호스트명(www.google.com)
도메인명 또는 IP주소를 직접 사용가능- 포트번호(443)
접속 포트로서 일반적으로는 생략한다.(http는 80, https는 443)- Path(/search)
리소의 경로로서 계층적 구조를 갖는다.(ex. /members/3420)- query parameter(q=hello&hl=ko)
key, value 형태를 갖고 있으며 ?로 시작하며 &로 추가가 가능하다. 해당 쿼리를 통해 데이터를 주고받을 수 있다.
위의 내용들을 토대로 HTTP에 들어가기 앞서 먼저 간단히 웹브라우저(클라이언트)에서부터 서버까지의 데이터 흐름을 정리해보자.
- URL에 https://www.google.com:443/search?q=hello&hl=ko을 입력함으로써 도메인과 포트 정보를 찾는다.
- DNS 서버를 통해 도메인을 실제 IP주소로 변환하여 요청한다.
- HTTP 요청 메세지를 생성한다.
GET /search?q=hello&hl=ko HTTP/1.1 Host: www.google.com
- TCP 3 way handshake를 통해 서버와의 연결을 확인한다.
- TCP/IP 패킷(출발지 IP, PORT, 목적지 IP, PORT, 전송 데이터인 HTTP 메시지 등)을 생성하고 인터넷 망으로 전달한다.,
- 수많은 노드서버들을 거쳐 요청 패킷이 서버로 도착한다.
- 서버는 요청 패킷에 있는 HTTP 요청 메세지를 해석하고 요청 사항은 수행한다.(데이터 검색 등)
- 비즈니스가 완료가 되면 서버에서는 HTTP 응답 메시지를 생성한다.
HTTP/1.1 200 OK Content-Type: text/html;charset=UTF-8 Content-Length: 3423 Host: www.google.com <html> <body>...</body> </html>
- 마찬가지로 TCP/IP 패킷을 생성 후 클라이언트에게 응답을 전달한다.
- 패킷이 도착하면 클라이언트는 서버의 HTTP 응답 메세지를 해석하고 필요할 경우 데이터로 내려준 html 등을 웹 브라우저에 랜더링하는 등 추가 작업을 수행하며 마무리된다.
HTTP(Hyper Text Transfer Protocol)란 짧게 인터넷에서 데이터를 주고 받을 수 있도록 정해 놓은 통신 규약&규칙이다. 여기서 데이터란, HTML, TEXT, IMAGE, 음성, 영상, 파일 JSON, XML 등 거의 모든 형태의 데이터를 포함한다. 클라이언트에서 서버, 서버에서 클라이언트, 또 서버간에 데이터를 주고 받을 때 대부분이 이 HTTP라는 데이터 통신 규악을 사용한다. 정리하자면 우리 데이터를 서로 주고 받을 때는 규칙을 가지고 서로가 잘 이해할 수 있게 보내자!
라고 정했고 그 규칙이 바로 HTTP인 것이다.
현재는 HTTP/1.1(TCP 프로토콜 기반)이 주로 사용되고 HTTP/2(TCP 기반), HTTP/3(UDP 기반)도 점점 증가하는 추세이지만, 결정적으로 핵심 스펙은 HTTP/1.1에 담겨있고 2,3는 최적화를 위해 업그레이드된 버젼이다.
- 클라이언트-서버 구조
기본적으로 HTTP는 클라이언트-서버 구조로 각각의 역할이 명확하게 구분되어 있다. 클라이언트는 서버에 요청을 보내고, 서버는 해당 요청에 대한 결과를 응답하는 구조이다. 이렇게 구분하게되면 클라이언트는 단지 서버에서 내려주는 응답값에 대해 대응하고 랜더링하는 등의 UX, UI에 대해서만, 또 서버는 해당 요청에 대한 비즈니스 로직을 처리해주고 원하는 데이터를 내려주는 등 각각의 역할이 분리가 되어 기본적인 어플리케이션에 유지 보수가 용이해진다는 장점이 있다.- 무상태성(Stateless)
HTTP는 무상태성을 갖는다. 그 의미는 서버가 클라이언트에 상태, 맥락, 컨텍스트에 대해서 전혀 모르는 상태라는 것이다.
예를 들어, 손님 A가 점원 A에게노트북 사겠습니다
라고 말하고나서 10초 후에2개 현금으로 살게요
라고 다시 말한다면 점원 A는 손님 A의 맥락을 알고있기 때문에 문제없이 처리할 수 있고 이러한 경우를 Stateful이라고 표현한다. 그런데 손님 A가 점원 A에게노트북 사겠습니다
라고 말하고나서 점원 A가 잠시 화장실을 가고 대타로 온 점원 B에게 바로2개 현금으로 살게요
라고 말한다면 점원 B는 손님 A가 대체 뭘 산다는 것인지에 대한 정보가 없기 때문에 대응할 수가 없다. 하지만 만약, 손님이 점원A가 오든 B가 오든지 간에노트북 2개 현금으로 구매하겠습니다
라고 요청한다면, 어떠한 점원이 와도 요청에 대해서 응답해줄 수 있고, 이러한 방식을 Stateless라고 이해하면 된다.
이처럼 무상태성을 유지하기 위해서는 손님(client)이노트북 2개 현금으로 구매하겠습니다
라는 모든 상태, 즉 필요한 데이터 정보를 점원(server)에게 요청할 때마다 넘겨줘야한다는 것을 전제로한다.
무상태성의 장점은 서버는 클라이언트에 대한 어떤 상태도 보관하지도 않기 떄문에 어떠한 서버 인스턴스가 와서 이에 대해 대응할 수 있다는 것이다. 따라서 만약 현재 가동중인 서버1이 장애가 발생한다고 하더라도 중계서버에서 바로 서버2로 전환하여 해당 요청에 대한 응답이 정상적으로 이루어질 수 있도록 할 수 있다.
또한, 서버의 스케일아웃(수평확장)이 유리하기 때문에 만약 대형 이벤트 기간으로 인해 클라이언트에서 요청이 엄청나게 증가하는 상황에 대해서도 같은 기능을 하는 서버를 늘려주기만 해도 대응할 수있다는 장점을 가지고있다.
하지만 무상태성은 어떤 관점에서보면 서버에 요청할 때마다노트북 2개 현금으로 구매하겠습니다
라는 모든 정보를 보내야하기 때문에 전송되는 데이터량이 많다라는 단점을 가진다고 볼 수도 있다.
마지막으로 로그인과 같이 반드시 상태를 서버에서 유지해야하는 경우가 있듯이, 모든 것을 무상태로 설계하기란 쉽지 않다. 하지만 HTTP 설계에서 최대한 Stateless를 유지하되 상태 유지는 최소한만 사용하는 것으로 지향해야한다.- 비연결성(connectionless)
비연결성은 클라이언트와 서버가 계속해서 연결이 유지하지 않는다는 의미이다. 예를 들어, 클라이언트에서 서버에게 요청을 보낼 경우, 먼저 TCP/IP 연결 확인 후 서버에게 요청을 보내는데, 서버에서는 해당 요청에 대한 응답이 완료되면 즉시 TCP/IP 연결을 종료함으로써 서로에 대한 연결을 유지하지 않는 것이다. 이런 비연결성이 가능한 이유는 일반적으로 요청과 응답은 초 단위 이하의 빠른 속도로 이루어지고, 1시간 동안 수천명이 서비스를 사용해도 실제 서버에서 동시에 처리하는 요청은 수십개 이하로 매우 적기 때문이다. 이러한 비연결성 특징 덕분에 HTTP는 서버 자원을 매우 효율적으로 사용할 수 있게된다. 하지만 반대로 TCP/IP를 계속해서 새로 맺어야하기 때문에 3 way handshake에 대한 시간이 계속해서 추가되고, 웹 브라우저로 사이트를 요청하면 계속해서 HTML 뿐만 아니라 자바스크립트, css, 이미지 등 수많은 자원이 계속해서 함께 다운로드 된다는 한계가 있다. 이를 극복하기 위해 지금은 HTTP 지속 연결(Persistent Connctions)로 문제를 해결하고 있고, HTTP/2, HTTP/3에서는 더 많이 최적화되어있다고 한다.
- start-line
Request - HTTP 메서드(GET, POST, PUT, DELETE 등..) + 요청 대상(/search?q=hello&hl=ko) + HTTP Version
Response - HTTP 버전 + HTTP 상태 코드(200, 400, 500 등)- header
Content-Type, Content-Length 등 HTTP 전송에 필요한 모든 부가정보가 포함되어있다.
ex)메세지 바다의 내용, 메세지 바디의 크기, 압축, 인증, 요청 클라이언트(브라우저) 정보, 서버 어플리케이션 정보, 캐시 관리정보 등- empty line
- message body
실제 전송할 데이터(HTML 문서, 이미지, 영상, JSON 등등 byte로 표현할 수 있는 모든 데이터 전소 가능)를 포함한다.
사진 출처 - mozilla
클라이언트가 서버에게 회원 정보를 조회해줘!
라고 요청하였을 때 여기서 회원 정보
는 리소스(URI) 조회해줘!
는 행위라고 구분할 수 있다. 즉, URL는 리소스만 식별하고 행위는 HTTP 메서드로 구분지어 요청할 수 있다.
- GET
리소스를 조회할 때 사용되고, 서버에 전달하고자 하는 데이터는(ex. id)는 쿼리 파라미터나, 쿼리 스트링을 통해 전달한다.(메시지 바디를 사용해서 데이터를 전달할 수 있지만, 지원하지 않은 곳이 많아 권장되지 않는다)- POST
요청 데이터를 처리할 때 사용된다. 메시지 바디를 통해 서버로 요청 데이터를 전달한다. 서버는 전달된 데이터로 신규 리소스를 등록하거나, 프로세스를 처리한다.
여기서 중요한 것은 POST가 단지 신규등록 뿐만 아니라 여러가지 역할을 수행한다는 것이다.
- 서버가 아직 식별하지 않은 새 리소스를 생성하거나,
- 단순히 데이터를 생성하거나, 변경하는 것을 넘어서 요청 프로세스를 처리해야하는 경우(ex, 주문 ➔ 결제완료 ➔ 배달시작 ➔ 배달완료처럼 단순히 값 변경을 넘어 프로세스의 상태가 변경되는 경우, 그래서 POST의 결과로 새로운 리소스가 생성되지 않을 수도 있음.)
즉, 해당 리소스 URI에 POST 요청이 오면 요청 데이터를 어떻게 처리할지 리소스마다 따로 정해줘야한다!
ps. 리소스 만으로 URI를 설계해야하지만 어쩔 수 없이 그러하지 못하는 상황이 많이 발생한다. 이러한 경우에는 컨트롤 URI로 설계하면된다 . ex) POST /orders/{orderId}/start-delivery(컨트롤 URI)
- PUT
리소스가 있으면 완전히 대체하고, 없으면 생성하는 메소드이다. 즉, 덮어쓰기 기능과 비슷하다고 보면된다. POST와의 차이점은 클라이언트가 리소스 위치를 알고 식별된 URI를 지정해서 요청한다는 점이다.
(사실 프로젝트를 진행할 적에는 PUT메소드로 리소스를 완전히 대체하지는 않았는데, 이러한 부분은 확인이 좀 필요할 것 같다)- PATCH
리소스를 부분 변경할 때 사용된다.- DELETE
리소스를 삭제할 때 사용된다.
HTTP 상태코드는 클라이언트가 보낸 요청의 처리 상태를 서버에서 응답으로 알려주는 기능이다.
- 1xx(Information): 요청이 수신되어 처리중(거의 사용되지 않는다.)
- 2xx(Successful): 요청 정상 처리
- 3xx(Redirection): 요청을 완료하려면 추가 행동이 필요
- 4xx(Client Error): 클라이언트 오류. 잘못된 문법으로 서버가 요청을 수행할 수 없음
- 5xx(Server Error): 서버 오류, 서버가 정상 요청을 처리하지 못함
200 OK
클라이언트에서 보낸 요청이 서버에서 잘 처리가 되어 성공하였다는 상태코드
201 Created
클라이언트에서 보낸 요청이 서버에서 잘 처리가 되어 새로운 리소스가 생성되었다는 상태코드.
생성된 리소스는 응답의 Location 헤더 필드로 식별한다.(ex. Location /members/100)
202 Accepted
요청이 접수되었으나 처리가 완료되지 않았음.(ex. 요청 접수 후 1시간 뒤에 배치 프로세스가 요청을 처리함)
204 No Content
서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음.ex) 웹 문서 편집기의 save 버튼.
흔히 204를 반환하는 경우는 PUT 요청에 대한 응답으로, 사용자에게 보여지는 페이지를 바꾸지 않고 리소스를 업데이트할 때 쓰입니다. 리소스를 생성한 경우엔 201 Created를 대신 반환합니다. 새롭게 업데이트한 페이지를 보여줘야 할 경우 200을 사용해야 합니다. - 출저 mozilla
400 Bad Request
클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음.(ex. 요청 파라미터가 잘못되거나, API 스펙이 맞지 않을 때) 클라이언트는 요청 내용을 다시 검토하고 수정하여 보내야한다.
401 Unauthorized
인증되지 않음, 즉 클라이언트가 해당 리소스에 대한 인증이 필요하다는 상태 코드. 401 오류시 응답에 WWW-Authenticate 헤더와 함께 인증 방법을 설명해야한다.
403 Forbidden
서버가 요청을 이해했지만 승인을 거부함.(인증은 되었지만, 접근 권한이 불충분한 경우) 예를 들어 어드민 등급이 아닌 사용자가 로그인은 했지만, 어드민 등급의 리소스에 접근하는 경우에 사용된다.
404 Not Found
요청 리소스가 서버에 없거나, 클라이언트가 권한이 부족한 리소스에 접근할 때 (403의 정보도 보여주고 싶지 않고) 해당 리소스를 숨기고 싶을 떄 사용된다.
서버의 다양한 오류로 발생되어지고, 서버에 문제가 있기 때문에 재시도하면 성공할 수도 있다.
500 Internal Server Error
서버 내부 문제로 오류가 발생되었다는 상태코드로 애매할 경우 대부분 500오류로 응답한다.
503 Service Unavailable
서버가 일시적인 과부하 또는 예정된 작업으로 잠시 요청을 처리할 수 없다는 것을 알려주는 상태코드. Retry-After 헤더 필드로 얼마뒤에 복구되는지도 함께 보내줄 수도 있다.
HTTP 전송에 필요한 모든 부가정보는 HTTP 헤더에 삽입하여 보낸다.(ex 메시지 바디의 내용, 바디의 크기, 압축 여부, 인증, 요청 클라이언트, 서버 정보, 캐시 관리 정보 등).
HTTP 바디는 표현 데이터를 전달하는 역할을 맡고, 표현 헤더는 표현 데이터를 해석할 수 있는 정보를 제공한다(html인지 json인지 등의 데이터 유형, 데이터 길이, 압축 정보 등). 표현 헤더는 요청, 응답 둘다 사용 가능하다.
- Content-Type: 미디어 타입, 문자 인코딩 등 표현 데이터의 형식
ex)text/html; charset=utf-8, application/json, image/png- Content-Encoding: 표현 데이터의 압축 방식. 표현 데이터를 압축하기 위해 사용된다. 데이터를 전달하는 쪽에서는 압축 후 인코딩 헤더를 추가하고, 데이터를 받는 쪽에서는 인코딩 헤더의 정보로 압축을 해체한다.
ex) gzip, deflate, identity- Content-Language: 표현 데이터의 자연 언어 ex) ko, en, en-US
- Content-Length: 표현 데이터의 길이
클라이언트가 선호하는 표현을 요청하는 것이다. 따라서 협상 헤더는 요청시에만 사용한다. 서버가 무조건 요청하는 바에 의무는 없지만, 되도록이면 이렇게 응답을 주었으면 좋겠다라고 말그대로 협상을 하는 것이라고 이해하면 되겠다.
- Accept: 클라이언트가 선호하는 미디어 타입 전달.
- Accept-Charset: 클라이언트가 선호하는 문자 인코딩
- Accept-Encoding: 클라이언트가 선호하는 압축 인코딩
- Accpet-Language: 클라이언트가 선호하는 자연 언어
협상 헤더에도 우선순위가 있다.
1. Quality values(q)가 클수록 높은 우선순위를 갖는다.
ex) Accept-Language: ko-KR, ko;q=0.9, en-US;q=0.8;en;q=0.7
1.ko-KR;q=1 (q생략)
2. ko;q=0.9
3. en-US;q=0.8
4. en;q=0.7
2. 구체적인 것이 더 높은 우선순위를 갖는다.
ex) Accept: text/, text/plain, text/plain;format=flowed
1. text/plain;format=flowed
2. text/plain
3. text/
Referer
- 현재 요청된 페이지의 이전 웹 페이지 주소로써, 유입 경로 분석이 가능하다.
- A ➔ B로 이동하는 경우 B를 요청할 때 Refer: A를 포함해서 요청한다.
- 요청에서 사용된다.
User-Agent
- 클라이언트의 어플리케이션 정보(웹 브러우저 정보 등)로써 어떤 종류의 브라우저에서 장애가 발생하는지 파악 가능하여 통계 정보를 쓸 때 사용된다.
- 요청에서 사용된다.
Server
- 요청을 처리하는 ORIGIN 서버의 소프트웨어 정보.(여기서 ORIGIN 서버란, 데이터 교환시 중간에 거치게 되는 여러 Proxy 서버가 아닌 실제 응답을 해주는 원 서버를 의미한다)
- 응답에서 사용된다.
Date
- 메시지가 발생한 날짜와 시간
- 응답에서 사용된다.
Host
- 요청한 호스트 정보(도메인)
- 필수 헤더 정보이며, 요청에서 사용된다.
- 하나의 서버가 여러 도메인을 처리해야 할 때, 하나의 IP 주소에 여러 도메인이 적용되어 있을 때 사용된다.
Location
- 웹 브라우저는 3xx 응답 결과에 Location 헤더가 있으면, Location 위치로 리다이렉트 한다.
- 201(Created): Location 값은 요청에 의해 생선된 리소스 URI를 의미
- 3xx(Redirection): Location 값은 요청을 자동으로 리다이렉션하기 위한 대상 리소스를 가리킨다.
Allow
- 허용 가능한 HTTP 메서드로서 405(Method Not Allowed)에서 응답에 포함해야한다.
- ex) Allow: GET, HEAD, PUT
Retry-After
- 유저 에이전트가 다음 요청을 하기까지 기다려야 하는 시간을 의미한다.
- 503(Service Unavailable): 서비스가 언제까지 불능인지 알려줄 수 있음.
- ex) Retry-After: Fri, 31 Dec 1999 23:59:59 GMT(날짜 표기) or 120(초단위 표기)
Authorization: 클라이언트 인증 정보를 서버에 전달하는데 사용되어짐
WWW-Authenticate: 리소스 접근시 필요한 인증 방법을 정의한다. 401 Unauthorized 응답과 함께 사용되어진다. ex) WWW-Authenticate: Newauth realm="apps", type=1, title="Login to \"apps\"", Basic realm="simple"
Set-Cookie: 서버에서 클라이언트로 쿠키 전달(응답)
ex) set-cookie: sessionId=abcd1234; expires=Sat, 26-Dec-2020 00:00:00 GMT or max-age=3600(초); path=/; domain=.google.com; Secure
Cookie: 클라이언트가 서버에서 받은 쿠키를 쿠키저장소에 저장하고, HTTP 요청시 서버로 전달.
쿠키는 사용자 로그인 세션을 관리할 때 많이 사용되며, 광고 정보를 트래킹할 때도 종종 쓰인다.
쿠키 정보는 항상 서버에 전송되기 때문에 네트워크 트래픽이 추가 유발된다.
따라서 최소한의 정보만 쿠키에 사용할 수 있도록 하는 것이 좋다(세션 id, 인증 토큰 등).
서버에 전송하지 않고, 웹 브라우저 내부에 데이터를 저장하고 싶으면 웹 스토리지(localStorage, seesionStorage)를 사용하면 된다. 하지만 보안에 민감한 데이터는 절대 저장하면 안된다!
클라이언트에서 서버로 요청을 보낼 시 cache-control: max-age=60와 같은 조건부 요청을 함께보내면 처음 응답받은 데이터를 브라우저 캐시 저장소에 보관해놓았다가, 다시 똑같은 요청을 할 시 먼저 브라우저 캐시를 탐색한후 해당 캐시가 유효하다면, 따로 서버에 다시 요청을 보내지 않을 수 있다. 즉, 캐시 덕분에 캐시 가능 시간동안 네트워크를 사용하지 않아도 되고, 덕분에 비싼 네트워크 사용량을 줄일 수 있어, 브라우저 로딩 속도가 빨라진다는 장점이있다.
캐시 시간 초과
캐시 유효 시간이 초과해서 서버에 다시 요청하면 두 가지 상황이 나타난다.
1. 서버에서 기존 데이터를 변경함
2. 서버에서 기존 데이터를 변경하지 않음.(현재 캐시에 있는 데이터와 서버에서 응답보낼 데이터가 여전히 동일)
2번과 같은 상황일 경우 같은 데이터임에도 다시 요청을 보내서 응답을 받아야한다는 점에서 비효율을 야기할 수 있다. 그래서 클라이언트 데이터와 서버의 데이터가 아직 같다는 사실을 확인할 수 있는 방법이 필요하고, 이 방법이 검증헤더와 조건부 요청이다.
검증헤더
조건부 요청 헤더
Last-Modified, If-modified-since
첫 요청시 cache-control: max-age=60와 함께 Last-Modified: 2020년 11월 10일 10:00:00의 검증헤더도 함께 응답받아 캐시 저장소에 저장한다. 캐시 시간이 초과된 후에는 바로 데이터를 요청 보내지 않고, if-modified-since: 2020년 11월 10일 10:00:00와 같은 조건부 요청을 보낸다. 그럼 서버에서 해당 데이터의 수정일을 비교하고 검증하여, 날짜가 동일하다면 데이터가 수정되지 않았다는 것으로 판단하고, 응답을 보낼때에 304 Not Modified(캐시로 리다이렉션해서 그 데이터를 사용하세요) 상태코드를 보내게 된다.(이 때, 보낼 데이터가 없기 때문에 Body를 비워서 보낸다) 그러면 클라이언트 쪽에서는 응답 결과를 재사용하고, 헤더 데이터(cache-control 등)를 갱신한다. 그 후 갱신된 캐시 데이터를 조회하여 사용하게 된다. 결과적으로 이러한 검증헤더와 조건부 요청을 사용하게 된다면, 네트워크 응답이 이루어지지만 용량이 적은 헤더 정보만 다운로드 되기 때문에 효율적이다.
하지만 해당 방법은 1초 단위로 캐시 조정이 불가능하고, 수정날짜만 다르지만, 데이터 결과는 똑같은 경우에도 다시 동일한 데이터를 응답하게 된다든 단점이 있다. 그래서 서버에서 별도의 캐시 로직을 관리하고 싶은 경우에는 ETag를 사용하면 된다.
ETag, If-None-Match
- Cache-Control: max-age
캐시 유효시간, 초 단위- Cache-Control: no-cache
데이터는 캐시해도 되지만, 캐시 유효 시간과 상관없이 항상 원(origin) 서버에 검증하고 사용한다.- Cache-Control: no-store
데이터에 민감한 정보가 있으므로 저장하면 안됨.(메모리에서 사용하고 최대한 빨리 삭제)- Cache-Control: must-revalidate
캐시 유효 시간이라면 캐시를 사용한다. 캐시 만료후 최초 조회시 원 서버에 검증해야함. 원 서버 접근 실패시 반드시 오류가 발생(504 Gateway Timeout)해야한다는 것이 no-cache와의 차이점이다.(no-cache는 원서버와의 연결이 오류가 발생할 때, 일단 기존의 캐시를 사용한다고 함.)- Cache-Control: public
응답이 public 캐시에 저장되어도 됨.- Cache-Control: private
응답이 해당 사용자만을 위한 것임. private 캐시에 저장해야함.(기본값)- Cache-Control: s-maxage
프록시 캐시에만 적용되는 max-age- Age: 60(HTTP 헤더)
오리진 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)
만약 한국에서 미국에 있는 원서버에 요청을 보내게 되다면, 한국과 미국은 절대적 물리적 거리가 멀기 때문에 응답 속도가 느릴 수 밖에 없다. 이러한 점을 보완하기 위해서 첫 요청 후에 한국 어딘가에 존재하는 프록시 캐시 서버(이를 public 캐시라고 부른다, 개인 웹 브라우저의 캐시 저장소는 private 캐시)에 해당 응답 데이터를 저장하게 된다. 그 이후 만약 다른 한국사람들이 똑같은 요청을 하게되면, 미국에 있는 원서버에 요청하는 것이 아닌 한국 프록시 캐시 서버에 요청을 하게되어 응답 속도의 개선을 경험할 수 있게된다.
원하든 원치않든 브라우저는 자동적으로 캐시를 사용할 경우가 있는데, 만약 서버에서 의도적으로 캐시가 절대 안되는 상황을 만들어야하는 경우, 즉 확실한 캐시 무효화를 시키기 위해서는 아래의 항목을 모두가 포함시켜 응답을 보내주면 된다.
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
(HTTP 1.0의 하위 호환을 위해)
해당 블로그는 인프런 '모든 개발자를 위한 HTTP 웹 기본 지식' 강의를 기반으로 작성되어졌습니다.