Skip to content

fortune-man/HTTP-Basic

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 

Repository files navigation

개발자는 평생 HTTP 기반 위에서 개발해야 합니다. 언젠가 한번은 HTTP에 대해 정리해야 할 필요가 있습니다. HTTP의 전체 흐름을 이해해보도록 하겠습니다.

Reference

🙇 모든 개발자를 위한 HTTP 웹 기본 지식


인터넷 네트워크

HTTP 프로토콜은 인터넷 망 위에서 동작합니다. 배경적으로 먼저 인터넷의 통신이 어떤식으로 일어나는지 알아보겠습니다.

인터넷에서 컴퓨터 둘은 어떻게 통신할까?

물리적으로 떨어져있는 두 대의 컴퓨터가 통신하기 위해선 중간에 "인터넷"이 필요합니다. 예를 들어 클라이언트 컴퓨터가 "Hello, world!"라는 메시지를 서버 컴퓨터에게 전달하려고 할 때, 인터넷의 동작방식은 단순하게 직접 바로 전달하는 것이 아니라 내부적으로 수 많은 노드들을 거쳐서 이동하는 복잡한 과정을 구성합니다. 인터넷 망을 통해 수 많은 노드들을 거쳐야 하기 때문에 내부적으로 상당히 복잡합니다. 이 복잡한 내부 과정들은 최소한의 "규칙"을 가지고 있습니다. 지금부터 하나씩 알아보도록 하겠습니다.

IP(인터넷 프로토콜)

IP 주소 부여

컴퓨터가 통신하기 위해 복잡한 인터넷망을 사용하는 것은 각 컴퓨터에 IP 주소를 부여하는 것을 통해 가능해집니다.

IP(Internet Protocol)

인터넷 프로토콜의 역할

  • 지정한 IP 주소(IP Adress)에 패킷(Packet)이라는 통신 단위로 데이터를 전달합니다.

인터넷 프로토콜은 서로 다른 컴퓨터에게 메시지를 전달할 수 있도록 정해진 규칙입니다. 규칙의 내용은 다음과 같습니다.

클라이언트 패킷 전달

  1. IP 패킷에 출발 IP 주소(클라이언트 주소)와 목적 IP 주소(서버 주소)를 저장합니다.
  2. 전달할 요청 데이터를 IP 패킷으로 감쌉니다.
  3. 전달 데이터가 포함되어 있는 IP 패킷을 인터넷 망에 전송합니다.
  4. IP 프로토콜의 규약에 따라 출발지부터 목적지에 도착할 때까지 노드들이 패킷을 순차적으로 전달하여 목적 IP 주소에 패킷이 도착하게 됩니다.

패킷을 전달받은 컴퓨터로 마찬가지로 응답할 수 있습니다.

서버 패킷 전달

  1. IP 패킷에 출발 IP 주소(서버 주소)와 목적 IP 주소(클라이언트 주소)를 저장합니다.
  2. 전달할 응답 데이터를 IP 패킷으로 감쌉니다.
  3. 전달 데이터가 포함되어 있는 IP 패킷을 인터넷 망에 전송합니다.
  4. IP 프로토콜의 규약에 따라 출발지부터 목적지에 도착할 때까지 노드들이 패킷을 순차적으로 전달하여 목적 IP 주소에 패킷이 도착하게 됩니다.
  • 참고 : 클라이언트와 서버가 각각 전송할 때 서로 다른 노드를 통해 전달될 수 있습니다.

하지만 IP 주소를 통한 위와 같은 방식에는 다음과 같은 한계가 있습니다.

IP 프로토콜의 한계

  • 비연결성

    • 패킷을 받을 대상이 없을 경우, 또는 서비스 불능 상태일 경우여도 패킷을 전송합니다.
  • 비신뢰성

    • 비연결성 때문에 패킷이 중간에 사라지거나 순서대로 도착하지 않을 수도 있어 신뢰할 수 없습니다.
  • 프로그램 구분

    • 같은 Ip를 사용하는 서버에서 통신하는 애플리케이션이 둘 이상일 경우 구분하는 것에 한계가 있습니다.

    IP Protocol doesnt make sense

    이러한 문제를 해결하기 위해 존재하는 TCP를 알아보겠습니다.

TCP, UDP

IP 프로토콜에서 발생된 문제들을 해결해 줍니다. 설명하기에 앞서 인터넷 프로토콜 스택의 4계층을 살펴보겠습니다.

아래 부터 순서대로 레이어가 쌓입니다.

애플리케이션 계층 전송 계층 인터넷 계층 네트워크 인터페이스 계층

TCP는 IP를 보완하기 위해 IP 스택 위에 쌓인다고 이해하면 된다고 합니다. protocol layer

좀 더 자세히 살펴보면 우리가 사용하는 애플리케이션(Web browser, 게임, 채팅 프로그램..) 운영체제(Window, Linux, Mac..) 네트워크 인터페이스(LAN 드라이버, LAN 카드에 관련된 장비들..) 의 모습을 살펴볼 수 있습니다.

layer2 동작 순서는 다음과 같습니다.

  1. 프로그램이 메시지 생성 (Hello, world!)
  2. SOCKET 라이브러리를 통해 OS 계층에 메시지 전달
  3. OS 계층에서 TCP 정보를 먼저 생성해서 메시지 데이터를 포함시킵니다.
  • IP 정보를 생성해서 데이터를 포함시킨 것과 같습니다.
  1. IP 패킷을 생성해서 TCP 데이터를 포함시킨 후 네트워크 인터페이스를 통해 LAN 카드를 이용해 Ethernet frame을 포함시켜 인터넷 망으로 전송을 출발합니다.
  • Ethernet frame : LAN 카드에 등록된 물리적인 주소, 데이터 패킷의 성공적인 전송을 담당

IP 패킷과 TCP 패킷이 정보를 생성해서 데이터를 포함시키는 과정은 다음과 같습니다.

ip tcp packets

TCP 데이터는 IP가 출발지와 목적지의 주소만 포함했던 것과 다르게 PORT, 전송 제어, 순서, 검증 정보 등을 포함시키는 것을 통해 IP 프로토콜의 여러 문제들이 해결됩니다.

TCP 특징

전송 제어 프로토콜(Transmission Control Protocol) : 출발지에서 목적지로 전송하는 것만을 담당했던 인터넷 프로토콜과 다르게 구체적으로 전송을 어떻게 할지 제어함으로써 문제를 해결하며 다음과 같은 특징을 가집니다.

  • 연결지향 - TCP 3 way handshake (가상 연결)
    • 클라이언트와 서버의 연결 여부를 검증 후 메시지를 전송합니다. 따라서 비연결성이 해결됩니다.
  • 데이터 전달 보증 : 메시지 전송 중 패킷 누락 여부를 알 수 있습니다.
  • 순서 보장

이러한 특징들 때문에 __신뢰할 수 있는 프로토콜__이 되어 대부분 애플리케이션에서 TCP를 사용__한다고 합니다.

TCP 3 way handshake

연결지향이 가능하게 되는 원리는 다음과 같습니다.

  1. 클라이언트가 서버에게 SYN(synchronized)라는 메시지를 먼저 보냅니다.
  2. 서버가 SYN을 전달받으면 ACK(요청 확인)이라는 메시지를 포함하여 응답합니다.
  3. 서버에게 SYN+ACK를 전달받은 클라이언트는 ACK(응답 확인)를 다시 응답하여 연결 여부를 검증합니다. 이런 과정을 통해 클라이언트와 서버는 연결 여부를 확인하며 동시에 서로를 신뢰할 수 있게 됩니다.
  4. 연결이 되고나면 서버가 응답해야할 데이터를 클라이언트에게 전달합니다. 연결 여부가 확인되지 않을 경우엔 데이터를 전송하지 않습니다.
  • 참고 : 요즘은 3에서 ACK와 함께 데이터를 포함시켜 한번에 전달하는 최적화가 되어있기도 합니다.

가상 연결 : 사실 이 연결은 실제로 연결된 것이 아닌 개념적으로만 연결된 것입니다. 물리적으로 선이 이어져있는게 아니라 논리적으로만 서로 연결되는 것을 확인하여 검증하는 것입니다. 즉, 전용 랜선이 보장 된 것은 아닙니다.

tcp 3way handshake

데이터 전달 보증, 순서 보장

  • 데이터 전달 보증
  • TCP에서는 클라이언트가 서버에게 데이터를 전송하면 데이터를 잘 받았는지 서버가 응답해줍니다.
  • 응답이 없을 경우 문제가 있는 것을 확인할 수 있습니다.
  • 따라서, 정확한 데이터의 전달이 보증됩니다.
  • 순서 보장
    • 클라이언트가 패킷을 전송한 순서와 서버에 도착한 순서가 서로 다를 경우, 기본적으로 서버는 잘못된 순서부터 재정렬후 재전송할 것을 클라이언트에게 요청합니다.
    • 따라서 데이터 전달의 순서가 보장됩니다. 데이터 전달 보증, 순서 보장

3 way handshake, 데이터 전달 보증, 순서 보장은 TCP 패킷에 전송제어 정보, 순서 정보, 검증 정보 등을 포함하기 때문에 가능합니다.

UDP

사용자 데이터그램 프로토콜(User Datafram Protocol)

IP 계층 바로 위에 TCP와 함께 같은 공간에 존재합니다. UDP는 기능이 거의 없습니다.

  • 하얀 도화지에 비유(기능이 거의 없음)

  • 연결지향 - TCP 3 way handshake X

  • 데이터 전달 보증 X

  • 순서 보장 X 즉, IP와 거의 같습니다. 다만, PORT가 추가됩니다.

  • PORT : 같은 IP 내 다른 프로그램들을 구분하기 위해 사용하는 체크섬(메시지 보증 데이터)

  • UDP를 사용하는 이유

    • 3 way handshake를 사용하는 TCP보다 "단순하고 빠르다"라는 장점때문입니다.
    • 이미 체계가 구축되어 있는 TCP에 비해 원하는 것을 애플리케이션에서 최적화를 하고 싶어서 추가 작업을 필요로 할 때 사용합니다.

PORT

예를 들어 클라이언트가 두 개 이상의 서버에 연결해야 한다면 한 번에 여러 컴퓨터와 통신하게 됩니다. 이럴 경우 패킷이 혼동될 수 있습니다. 따라서 구분해야할 필요가 있습니다.

IP에서 출발지 IP와 목적지 IP를 포함했듯이, TCP에서 출발지 PORT와 목적지 PORT를 IP 주소와 함께 포함합니다.

같은 IP 주소 내에서 애플리케이션의 프로세스를 구분하는 용도로 사용됩니다. 따라서 하나의 IP 주소를 가진 클라이언트에서 여러 요청을 받아도 서버는 각각 정확한 응답을 할 수 있게 됩니다.

port

PORT의 특징은 다음과 같습니다.

  • 0 ~ 65535까지 할당 가능합니다.
  • 0 ~ 1023 : 잘 알려진 포트, 사용하지 않은 것이 좋다고 합니다.
    • FTP - 20, 21
    • TELNET - 23
    • HTTP - 80 (대표적인 포트)
    • HTTPS - 443

DNS

IP 프로토콜은 또 다른 문제를 가지로 있었습니다.

  • IP는 주소가 길고 복잡해서 기억하기 어렵습니다. (예: 100.100.100.1)
  • IP는 변경될 수 있습니다.
    • 시간이 지남에 따라 기존 IP가 신규 IP로 변경될 수 있습니다.
    • IP가 변경되면 동일한 서버에 접근할 수 없습니다.

이를 해결하기 위해 도메인 네임 시스템이 DNS(Domain Name System) 등장하게 되었습니다.

전화번호를 저장하고 관리하는 전화번호부 같은 서버를 제공해서 도메인명을 등록하고 IP 주소로 변환할 수 있는 원리입니다.

예를 들어 "google.com" 이라는 도메인을 구매해서 등록하면 도메인명을 IP 주소로 변환해서 관리하는 DNS 서버를 이용할 수 있게 됩니다. DNS 서버에 저장되어 있는 도메인명을 통해 IP 주소를 모두 기억하지 않아도 되고, 변경되더라도 접근할 수 있게 되었습니다.

dns

인터넷 네트워크 정리

  • 인터넷 통신
    • 복잡한 인터넷 망에서 메시지를 통신하기 위해선 인터넷 프로토콜이 있어야 합니다.
  • IP (Internet Protocol)
    • 인터넷 프로토콜은 한계가 있습니다. (비신뢰성, 비연결성, 프로그램 구분..)
  • TCP, UDP
    • 이러한 IP의 문제를 TCP가 해결해줍니다.
    • IP의 문제를 해결해주는 TCP의 특징(3 way handshake, 데이터 전달 보증, 순서 보장)은 TCP 패킷에 전송제어 정보, 순서 정보, 검증 정보 등을 포함하기 때문에 가능합니다.
    • UDP도 해결에 도움이 됩니다. 거의 IP와 비슷하며 포트가 추가되며 필요에 따라 애플리케이션에서 기능을 확장할 수 있습니다.
  • PORT
    • 같은 IP 내에서 동작하는 애플리케이션을 구분하기 위해 사용됩니다.
  • DNS
    • IP는 기억하기 어렵고, 변경될 수 있다는 문제를 가지고 있었습니다.
    • 도메인명을 IP 주소로 변환해서 관리하는 DNS 서버에 접근하는 방식인 도메인 네임 시스템을 이용해서 문제를 해결할 수 있었습니다.

URI와 웹 브라우저 요청 흐름

URI(Uniform Resource Identifier)

  • Uniform : 리소스 식별하는 통일된 방식입니다.
  • Resource : 자원, URI로 식별할 수 있는 모든 것(제한 없음)입니다. html파일 뿐만 아니라 식별할 수 있는 모든 data 자원을 의미합니다.
  • Identifier : 다른 항목과 구분하는데 필요한 정보입니다.

URI? URL? URN? URI표준 스펙 "URI는 로케이터(locatior), 이름(name) 또는 둘 다 추가로 분류될 수 있다."

  • URI(Uniform Resource Identifier) : 리소스를 식별합니다.
  • URL(Uniform Resource Locator) : 리소스에 위치를 부여합니다. 변할 수 있습니다.
  • URN(Uniform Resource Name) : 리소스에 이름을 부여합니다. 변하지 않습니다.

uri

  • 참고 : URN 이름 만으로 실제 리소스를 찾을 수 있는 방법이 보편화 되어 있지 않아서 리소스 결과 맵핑이 어렵기 때문에 주로 URL을 많이 사용한다고 합니다.

URL 분석 예시

https://www.google.com/search?q=hello&hl=ko

URL 전체 문법

  • 프로토콜(https)
  • 호스트명(www.google.com)

  • 포트 번호(443)

  • 패스(/search)

  • 쿼리 파라미터(q=hello&hl=ko)

  • scheme : 주로 프로토콜에 사용됩니다.

    • 프로토콜 : 어떤 방식으로 자원에 접근할 것인가 하는 약속 규칙 (http, https, ftp..)
    • http는 80 포트 , https는 443 포트를 주로 사용하며 포트는 생략이 가능합니다.
    • https는 http에 강력한 보안이 추가된 것입니다.(HTTP Secure).
  • userinfo : URL에 사용자 정보를 포함해서 인증해야 할 때 사용합니다. (거의 안 쓰인다고 합니다.)

  • host : "호스트명"이라고 하며, 도메인명 또는 IP 주소를 직접 사용 가능합니다.

  • port : 접속 포트, 일반적으로 생략 가능하며 생략시 http는 80, https는 443을 사용합니다.

  • path : 리소스가 있는 경로입니다. 보통 계층적 구조로 되어있습니다. (ex. /home/file.jpg)

  • query : key - value 형태로 데이터가 들어갑니다.

    • ?로 시작, &로 추가 가능 ?keyA=valueA&keyB=valueB
    • 웹 서버에 제공하는 파라미터이고 문자의 형태이기 때문에 보통 query parameter, query string 등으로 불립니다.
  • fragment : html 내부에서 이동할 수 있는 북마크 등에 사용됩니다.

    • 서버에 전송되는 정보가 아닙니다.

웹 브라우저 요청 흐름

https://www.google.com/search?q=hello&hl=ko 메시지를 요청할 씨의 웹 브라우저의 흐름을 자세히 살펴보면 다음과 같습니다.

  1. 클라이언트는 DNS 서버 조회를 통해 접근합니다.

  2. 예시에선 443(https는 포트 생략)을 통해 IP와 포트 정보를 찾아냅니다. getDns

  3. HTTP 메시지를 3.1~3.3의 과정을 거쳐 생성하여 전송합니다. createHttpMessage

  4. 3.1~3.3을 통해 생성된 패킷의 요청을 서버에 전달합니다. requestPacket

  5. 요청 패킷이 도착하면 서버가 패킷을 버리고 내부 메시지(데이터)를 분석합니다.

  6. 서버는 분석 결과에 적절하게 응답할 메시지를 생성합니다.

  7. 클라이언트와 마찬가지로 응답 패킷을 포함하여 전달합니다.

  8. 클라이언트에게 응답 패킷이 도착하면 html 데이터를 렌더링하여 사용자의 화면에 보여줍니다. responsePacket


HTTP 기본

모든 것이 HTTP

HTTP(Hyper Text Transfer Protocol) : 하이퍼미디어 문서를 전송하기 위한 프로토콜입니다.

지금은 HTTP에서 다음과 같은 데이터를 전송할 수 있습니다.

  • HTML, TEXT
  • IMAGE, 음성, 영상, 파일
  • JSON, XML (API)
  • 거의 모든 형태의 데이터를 전송하는 것이 가능 합니다.
  • 서버간에 데이터를 주고 받을 때도 대부분 HTTP를 사용합니다. 지금은 html같은 하이퍼미디어 문서 뿐만 아니라 거의 모든 것을 다 전송할 수 있는 HTTP의 시대라고 합니다.

HTTP 역사

  • HTTP/0.9 1991년: GET 메서드만 지원, HTTP 헤더X
  • HTTP/1.0 1996년: 메서드, 헤더 추가
  • HTTP/1.1 1997년: 가장 많이 사용, 우리에게 가장 중요한 버전
    • HTTP/1.1 스펙에 기본 기능이 다 들어있기 때문에 가장 중요합니다.
    • RFC2068 (1997) -> RFC2616 (1999) -> RFC7230~7235 (2014)
  • HTTP/2 2015년: 성능 개선
  • HTTP/3 진행중: TCP 대신에 UDP 사용, 성능 개선

기반 프로토콜

  • TCP: HTTP/1.1, HTTP/2
    • HTTP/1.1, HTTP/2는 TCP 프로토콜 위에서 동작합니다.
  • UDP: HTTP/3
    • HTTP/3는 UDP 기반으로 개발되어있습니다. 3 way handshake등의 문제를 개선하여 애플리케이션 계층에서 성능을 최적화해서 속도가 굉장히 빠르도록 개발되었습니다.
  • 현재는 HTTP/1.1을 주로 사용한다고 합니다. HTTP/1.1을 기반으로 성능이 개선된 HTTP/2 HTTP/3도 사용도가 점점 증가하고 있다고 합니다.

HTTP 특징

HTTP는 다음과 같은 중요한 특징이 있습니다.

  1. 클라이언트 서버 구조로 동작하게 됩니다.
  • 클라이언트는 서버에 요청을 보내고 응답을 대기합니다.
  • 서버가 요청에 대한 결과를 만들어서 응답합니다.
  • 응답받은 결과를 클라이언트가 렌더링해서 동작합니다.
  • 즉, Request Response 구조입니다. 클라이언트와 서버로 구조를 분리하는 것은 중요합니다.
  • 서버는 비즈니스 로직과 데이터를 서버에서 관리하고, 클라이언트는 UI, 사용성 등에 집중할 수 있습니다. 즉, 구조를 분리하면 클라이언트와 서버가 각각 독립적으로 진화할 수 있습니다.
  • 예를 들어 클라이언트는 복잡한 데이터나 비즈니스 로직을 다룰 필요가 없이 UI /UX에 집중할 수 있고, 서버는 규모가 확장되거나 변경될 때마다 서버의 아키텍쳐나 대용량 트래픽을 고도화 시키는 것에만 집중할 수 있습니다. 구조를 분리하여 양 쪽이 독립되게 진행할 수 있다는 것이 중요합니다.
  1. 무상태 프로토콜(Stateless)
  • HTTP의 특징은 무상태 프로토콜(Stateless)을 지원합니다. 서버가 클라이언트의 이전 상태를 보존하지 않는 것을 의미합니다.
  • 서버가 클라이언트의 이전 상태를 유지하는 것은 Stateful이라고 하며 다음과 같은 차이가 있습니다.
  • Stateful : 항상 같은 서버가 유지되어야 합니다. 유지되던 서버에 변경이 생기면 서비스에 장애가 발생합니다. 또는 클라이언트의 이전 상태를 모두 기억해야 하는 단점이 있습니다.
  • Stateless : 클라이언트의 이전 상태를 보관하지 않기 때문에 아무 서버나 호출해도 됩니다.
    • 갑자기 클라이언트 요청이 증가해도 서버를 대거 투입할 수 있습니다. stateful_stateless
    • 또한 중간에 서버에 장애가 발생하더라도, 중계 서버(프록시 서버)가 다른 서버가 응답하도록 관리해주기 때문에 대응이 가능합니다.
    • 무상태는 응답서버를 쉽게 바꿀 수 있기 때문에 무한한 서버를 증설하는 것이 가능합니다.
    • 이처럼 중계 서버를 통해 수평 확장에 유리하도록 설계하는 것을 "스케일 아웃"이라고 합니다. stateless
    • Stateless에도 한계가 있습니다. 모든 것을 무상태로 설계할 수 있는 경우도 있고 없는 경우도 있습니다. 예를 들어 로그인한 사용자의 경우 로그인 상태를 서버에 유지해야하기 때문에 일반적으로 브라우저 쿠키와 서버의 세션을 조합해서 상태를 유지하는 기능을 필요한 경우에만 적절하게 최소한만 사용합니다.
    • Stateless는 요청 데이터를 훨씬 많이 다룬다는 단점도 있습니다.
    • 하지만 무상태의 장점이 더 크기 때문에 최대한 Stateless로 설계하고 정말 필요한 경우에만 상태를 유지해야한다고 기억하면 된다고 합니다.
  1. 비연결성(connectionless)
  • 기본적으로 TCP/IP는 연결을 유지하는 모델입니다.
  • 다수의 클라이언트가 서버와 연결할 경우, 다수의 연결을 유지한 상태에서 요청과 응답을 주고받기 때문에 서버는 불필요하게 자원을 소모하게 됩니다. connectionModel
  • 연결을 유지하지 않는 모델로 연결할 경우, 연결 - 요청 - 응답 - 연결 종료 순서로 필요한 동작만 함으로써 서버는 최소한의 자원만을 유지하게 됩니다. connectionlessModel
  • HTTP는 기본적으로 연결을 유지하지 않는 모델이기 때문에 일반적으로 초 단위 이하의 빠른 속도로 응답할 수 있습니다.
  • 아주 많은 사용자가 서비스를 사용해도 실제 서버에서 동시에 처리하는 요청은 매우 작습니다.
    • 예를 들어 웹 브라우저에서 동일한 시간에 다수의 사용자가 동일하게 검색의 기능을 요청하지는 않거나 매우 적습니다. 초 단위 이하로 응답을 확인하기 때문에 더욱 분명합니다.
  • 따라서 연결을 유지하지 않는 모델은 서버 입장에서 자원의 가용성을 훨씬 더 높일 수 있기 때문에 자원을 매우 효율적으로 사용할 수 있습니다.
  • 비 연결성도 단점과 한계가 있습니다.
    • TCP/IP 연결을 매번 새로 맺어야 하기 때문에 3 way handshake의 시간이 추가됩니다.
    • HTML 뿐만 아니라 자바스크립트, css, 추가 이미지 등이 추가될 수록 수 많은 자원이 함께 포함됩니다.
    • 그래서 지금은 HTTP 지속 연결(Persistent Connections)로 문제를 해결한다고 합니다.Persistent Connections
    • HTTP2 / HTTP3에선 훨씬 많은 최적화가 되어 있다고 합니다.
    • 참고 : 스테이트리스를 기억하자 (서버 개발자들이 어려워하는 업무)
      • 같은 시간에 딱 맞추어 발생하는 대용량 트래픽(선착순 이벤트, 명절 KTX 예약, 학과 수업 등록.. 등등 수만명이 동시에 요청하는 상황)
      • 비연결성이 의미없을 만큼 트래픽의 규모가 커서 패닉할수 있지만, 어떻게든 최대한 stateless하게 설계하는 것이 중요합니다. 대용량 트래픽과 요청에도 서버의 규모를 확장해서 대응할 수 있는 부분이 많습니다. 보통 이벤트 설계할 때 많이 사용되는 경우라고 합니다.(사용자가 이벤트 html을 어느정도 사용한 후에 이벤트 참여 버튼을 동작시키도록 하는 경우)
  1. HTTP 메시지 HTTP 메시지에 다음과 같은 상상할 수 있는 거의 모든 Binary 데이터를 전송할 수 있다는 것을 다시 한 번 상기해보겠습니다.
  • HTML, TEXT
  • IMAGE, 음성, 영상, 파일
  • JSON, XML (API)
  • 거의 모든 형태의 데이터를 전송하는 것이 가능 합니다.
  • 서버간에 데이터를 주고 받을 때도 대부분 HTTP를 사용합니다.

기본적으로 start line, header, 공백, body로 구성되어있는 HTTP 메시지는 요청 메시지와 응답 메시지로 분리되며 각각의 구조는 다음과 같은 차이가 있습니다. httpMessageStructure

  • 참고 : HTTP 요청 메시지도 본문에 필요한 바디를 가질 수 있습니다.

공식 스펙

자세히 살펴 보겠습니다. start-line (요청 메시지)

  • 요청의 시작 라인은 request-line라고 합니다.
    • method / request-target(요청 대상 절대경로 path가 들어갑니다.) / HTTP 버전
  • HTTP 메서드 : 서버가 수행해야할 동작을 지정합니다.
  • 요청 대상 : absolute-path[?query]. 절대경로("/" 로 시작하는 경로입니다.) startLineRequest - HTTP 버전 start-line (응답 메시지)
  • 응답의 시작라인은 status-line라고 합니다.
    • HTTP 버전 / HTTP 상태 코드(클라이언트의 요청 성공, 실패여부를 나타냅니다.) / 이유 문구(HTTP 상태 코드를 이해할 수 있도록 짧게 설명하는 글입니다.)
    • 참고 : HTTP status-code 200(성공), 400(클라이언트 요청 오류), 500(서버 내부 오류) startLineResponse header 헤더 필드는 [헤더 명: OWS(띄어쓰기 허용) 필드 값 OWS] 형태로 구성되어 있습니다.
    • 참고 : field-name은 대소문자 구분이 없습니다. HTTP 헤더의 용도는 HTTP 전송에 필요한 모든 부가정보를 포함시키는 것입니다.
    • 메시지 바디의 내용이 html인지, 메시지 바디의 크기나 압축의 여부, 인증 정보, 요청 클라이언트 정보, 응답하는 서버 애플리케이션의 정보, 캐시에 대한 정보 등이 들어있습니다.
    • 메시지 바디를 제외한 필요한 메타 데이터가 다 들어있다고 할 수 있습니다.
    • 표준 헤더 필더는 아주 방대합니다.
    • 필요에 따라 임의의 헤더를 추가 할 수 있습니다. 다만 약속한 클라이언트와 서버의 경우입니다. header body
  • HTTP 메시지 바디는 실제 전송할 데이터를 담는 용도입니다.
  • Byte로 표현할 수 있는 모든 데이터(HTMl 문서, 이미지, 영상, JSON..)를 전송할 수 있습니다. body
  1. 단순하고 확장이 가능합니다.
  • HTTP는 생각보다 단순하고 스펙도 읽어볼만 하다고 합니다.
  • HTTP 메시지도 매우 단순합니다.
  • 단순하지만 확장이 가능한 기술이기 때문에 크게 성공한 표준 기술이 된 것 같다고 합니다.

HTTP 정리

  • 지금은 HTTP의 시대인 이유를 다음과 같이 알아보았습니다.
  • HTTP 메시지에 모든 것을 전송할 수 있습니다.
  • HTTP/1.1 기준으로 학습하는 것이 중요합니다. HTTP/2나 HTTP/3은 성능 확장이 포함되어있으며 점점 자주 사용됩니다.
  • 클라이언트 서버의 구조로 분리된 형태입니다.
  • 무상태 프로토콜(Stateless) 위주로 설계하는 것이 좋으며 필요에 따라 최소한의 상태를 유지하는 것이 권장됩니다.
  • HTTP 메시지는 시작라인, 헤더, 공백, 바디로 단순하면서도 확장이 가능하도록 구성되어 있습니다.

HTTP 메서드

간단한 HTTP API 설계 예시를 통해 HTTP 메서드가 왜 필요한지에 대해 알아보겠습니다.

요구사항

"회원 정보 관리 API를 만들어라."

  • 회원 목록 조회
  • 회원 조회
  • 회원 등록
  • 회원 수정
  • 회원 삭제

위와 같은 요구사항에서 초보 개발자들은 다음과 같은 실수를 저지른다고 합니다. "이해하기 편하도록 이름을 잘 지어서 URI를 기준으로 기능을 구현해볼까?"

API URI 설계

URI(Uniform Resource Identifier)

  • 회원 목록 조회 /read-member-list
  • 회원 조회 /read-member-by-id
  • 회원 등록 /create-member
  • 회원 수정 /update-member
  • 회원 삭제 /delete-member

초보 개발자들은 이것을 보고 그럴듯하게 설계했다고 착각한다고 합니다. 이것이 정말 좋은 URI 설계인지 알아보겠습니다. URI 설계에서 가장 중요한 것은 "리소스 식별"입니다. 회원을 등록, 수정, 조회하는게 리소스가 아니라, "회원"이라는 개념 자체가 리소스입니다. 따라서 리소스를 어떻게 식별하는게 좋을지 고민하려면 회원을 등록, 수정, 조회하는 것을 모두 배제하고 회원이라는 리소스만 식별하도록 하면 됩니다. 즉, 회원 리소스를 URI에 매핑해야합니다. "회원" 개념만을 리소스를 식별하고, URI의 계층 구조를 활용하여 매핑한 결과는 다음과 같습니다.

API URI 설계 리소스 식별, URI 계층 구조 활용

  • 회원 목록 조회 /members
  • 회원 조회 /members/{id} -> 어떻게 구분하지?
  • 회원 등록 /members/{id} -> 어떻게 구분하지?
  • 회원 수정 /members/{id} -> 어떻게 구분하지?
  • 회원 삭제 /members/{id} -> 어떻게 구분하지?
  • 참고: 계층 구조상 상위를 컬렉션으로 보고 복수단어 사용 권장(member -> members)

여기까지 온 초보 개발자들은 회원이 리소스인걸 알겠으나, 구분할 방법을 몰라서 URI 설계에 막힌다고 합니다. 같은 리소스에 대해 식별하여 동작해야 하기 때문입니다.

가장 중요한 것은 리소스와 행위를 분리하는 것입니다. URI는 리소스만 식별하고, 리소스와 해당 리소스를 대상으로 하는 행위를 분리했습니다.

  • 리소스(명사) : 회원
  • 행위(동사) : 조회하라, 등록하라, 삭제하라, 변경하라 행위는 어떻게 구분하는지 HTTP 메서드를 통해 알아보겠습니다.

HTTP 메서드

HTTP 메서드는 클라이언트가 서버에게 무언가를 요청할 때 서버에게 기대하는 행동입니다. HTTP 주요 메서드의 종류는 다음과 같습니다.

  • GET: 리소스를 조회합니다.
  • POST: 클라이언트가 요청한 데이터를 서버가 처리하며, 주로 등록에 사용됩니다.
  • PUT: 리소스를 클라이언트가 서버에게 보내는 리소스로 대체합니다. 또는 해당 리소스가 없으면 생성합니다.(폴더에 파일이 있으면 덮어쓰고, 없으면 생성하는 것과 같다고 생각하면 됩니다.)
  • PATCH: 리소스를 부분적으로 변경합니다. 리소스의 일부 필드를 변경할 때 사용합니다.
  • DELETE: 리소스를 삭제합니다.
  • 참고 : 최근에는 리소스가 아닌 Representation으로 바뀌었습니다. 잠시 편의상 리소스로 표현하겠습니다.

기타 메서드

  • HEAD: GET과 동일하지만 메시지 부분을 제외하고, 상태 줄과 헤더만 반환
  • OPTIONS: 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명(주로 CORS에서 사용)
  • CONNECT: 대상 자원으로 식별되는 서버에 대한 터널을 설정
  • TRACE: 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행

HTTP 주요 메서드 : GET

  • 리소스를 조회합니다.
  • 서버에 전달하고 싶은 데이터를 query(쿼리 파라미터, 쿼리 스트링)를 통해서 전달합니다.
  • 메시지 바디를 이용해서 데이터를 전달할 수 있지만 지원하지 않는 곳이 많아서 권장하지 않는다고 합니다. 실제로 동작하는 과정은 다음과 같습니다.
  1. 클라이언트가 원하는 데이터를 메시지와 식별자를 포함한 메시지를 전달하여 서버에게 요청합니다.
  2. 서버에 메시지가 도착해서 메시지를 확인합니다.
  3. 서버가 메시지의 해당 데이터를 내부 데이터베이스에서 조회하여 응답결과가 포함된 메시지를 생성 후 클라이언트에게 전달하는 것으로 응답합니다. getMethod getResponse

HTTP 주요 메서드 : POST

POST는 클라이언트가 서버에게 데이터를 전달하면 해당 요청 데이터를 서버에서 처리해주는 것을 기대하는 메서드 입니다.

  • 클라이언트의 요청 데이터를 서버에서 처리합니다.
  • 메시지 바디를 통해 서버로 요청 데이터 전달하고
  • 서버는 요청 데이터를 처리하는 것이 핵심입니다.
    • 메시지 바디를 통해 들어온 데이터를 처리하는 모든 약속된 기능을 수행한다는 의미입니다.
  • 주로 전달된 데이터로 신규 리소스를 등록하거나, 변경된 프로세스를 처리하는데 사용됩니다.

동작 과정은 다음과 같습니다.

  1. 클라이언트가 전송하는 해당 데이터는 서버가 내부적으로 처리하도록 약속되어있습니다.
  2. 클라이언트가 요청 데이터(/members)를 POST로 전송합니다.
  3. 서버에서 해당 데이터를 전송받아 확인 후 스스로 내부로직을 처리(데이터베이스에 신규 데이터 등록, 신규 식별자 생성)하여 응답할 데이터를 생성합니다.

post

POST는 단순히 데이터를 등록하는 것만이 아닙니다. 요청 데이터를 어떻게 처리한다는 뜻인지 예시를 통해 알아보겠습니다.

스펙 : POST 메서드는 대상 리소스가 리소스의 고유한 의미 체계에 따라 요청에 포함 된 표현을 처리하도록 요청합니다. (구글 번역) 좀 더 쉽게 예를 들어 보면 다음과 같은 기능에 사용됩니다.

  • HTML 양식에 입력 된 필드와 같은 데이터 블록을 데이터 처리 프로세스에 제공합니다.
    • 예) HTML FORM에 입력한 정보로 회원 가입, 주문 등에서 사용되는 것과 같습니다.
  • 게시판, 뉴스 그룹, 메일링 리스트, 블로그 또는 유사한 기사 그룹에 메시지를 게시합니다.
    • 예) 게시판 글쓰기, 댓글 달기
  • 서버가 아직 식별하지 않은 새 리소스를 생성합니다.
    • 예) 신규 주문 생성
  • 기존 자원에 데이터를 추가합니다.
    • 예) 한 문서 끝에 내용 추가하기 중요한 것은 리소스 URI에 POST 요청이 오면 요청 데이터를 어떻게 처리할지 리소스마다 따로 정해야 한다는 것입니다. 따라서 정해진 것이 따로 없습니다.

POST 정리 POST는 여러가지 쓰임새가 있으며 다음과 같습니다.

  1. 주로 새 리소스를 생성하고 등록하는데 사용됩니다.
  2. 요청 데이터를 처리하는데도 사용됩니다.
  • 단순히 데이터를 생성하거나 변경하는 것을 넘어서, 프로세스를 처리해야 합니다.
  • 예) 주문에서 결제완료 -> 배달시작 -> 배달완료 처럼 단순히 값을 변경하는 것을 넘어 프로세스의 상태가 변경되는 경우 처리하는데 사용되는 것을 의미합니다.
  • 위와 같은 이유로 POST의 결과로 새로운 리소스가 생성되지 않을 수도 있습니다.
    • 이럴 경우 어쩔 수 없이 이미 분리된 행위도 리소스에 포함시켜서 동사같은 URI가 나올 수도 있습니다. 기본적으로 최대한 리소스로 URI를 설계하되 어쩔 수 없을 땐 최소한의 컨트롤 URI 설계가 필요하다고 합니다.
    • 예) POST /orders/{orderId}/start-delivery (컨트롤 URI)
  1. 다른 메서드로 처리하기 애매한 경우에도 사용됩니다.
  • 예) JSON으로 조회 데이터를 넘겨야 하는데, GET 메서드를 사용하기 어려운 경우 쿼리 파라미터를 쓰지 않거나 지원하는 서버가 없을 경우 조회이지만 POST로 조회용 데이터를 전달해서 처리하도록 사용될 수 있습니다.
  • POST는 모든 걸 할 수 있다고 이해하면 된다고 합니다. 하지만 조회할 때는 GET을 쓰는 것이 유리합니다. 약속된 메서드를 우선 사용하는 것이 복잡성을 최소화하기 때문입니다. 다만 그 외 데이터가 변경되거나 프로세스가 진행되거나 등등 어쩔 수 없는 경우에 선택할 수 있습니다.

HTTP 주요 메서드 : PUT

PUT은 리소스를 대체합니다. 비유하자면 파일을 복사하는 것과 비슷합니다. 기존 파일이 없으면 생성하고, 기존 파일이 있으면 덮어버립니다. 마찬가지로 PUT은

  • 리소스가 있으면 완전히 대체합니다.
  • 리소스가 없으면 새로 생성합니다. 중요한 것은 클라이언트가 구체적인 리소스 경로를 알고 있다는 것입니다. __"클라이언트가 리소스 위치를 알고 URI를 직접 지정한다"__라는 POST와의 차이점이 있습니다. (POST는 클라이언트가 리소스의 위치를 모릅니다. 서버에게 모두 전담합니다.) __클라이언트가 리소스를 식별한다는 차이점__이 중요하다고 합니다.

주의할점은 PUT은 리소스를 완전히 대체한다는 점입니다. PUT을 사용하게 되면 기존 데이터가 삭제되고 PUT에 포함되어 있는 요청 데이터로 완전히 대체됩니다.

put

HTTP 주요 메서드 : PATCH

PATCH는 PUT처럼 리소스를 완전히 대체하지 않고 부분 변경하는데 사용됩니다. 리소스의 일부 데이터를 부분적으로 변경하고 싶을 때 사용하면 된다고 합니다. 리소스의 위치를 직접 지정합니다. patch

  • 참고 : 리소스를 부분 변경해야 할 때 PATH를 사용하지만 지원하지 않는 서버도 있다고 합니다. 그런 경우에는 POST를 사용하면 됩니다. POST는 무적이라고 합니다.

HTTP 주요 메서드 : DELETE

리소스를 직접 제거하고 싶을 때 사용하면 됩니다. 리소스의 위치를 직접 지정합니다. delete

HTTP 메서드의 속성

HTTP 메서드는 다음과 같은 속성을 가지고 있습니다.

  • 안전(Safe Methods)

    • 호출해도 리소스를 변경하지 않는다.(GET, DELETE와 달리 POST, PUT, PATH와 같은 호출했을 때 변경이 일어나는 메서드는 안전하지 않습니다.)라는 개념입니다.
      • Q: 안전한 메서드여도 계속 호출한다면, 로그 같은게 쌓여서 장애가 발생하지 않을까요?
      • A: 안전은 해당 리소스만 고려합니다. 그런 부분까지 고려하지 않습니다.
  • 멱등(Idempotent Methods)

    • f(f(x)) = f(x)
    • 한 번 호출하든 두 번 호출하든 100번 호출하든 결과가 똑같아야 한다는 의미로 쉽게 이해하면 된다고 합니다.
    • 멱등 메서드
      • GET: 한 번 조회하든, 두 번 조회하든 같은 결과가 조회됩니다.
      • PUT: 결과를 대체합니다. 따라서 같은 요청을 여러번 해도 최종 결과는 같습니다.
      • DELETE: 결과를 삭제합니다. 같은 요청을 여러번 해도 삭제된 결과는 똑같습니다.
      • POST: 멱등이 아닙니다! 두 번 호출하면 같은 행위가 중복해서 발생할 수 있습니다.
    • 멱등이라는 개념은 자동 복구 메커니즘에 활용됩니다.
      • 서버가 정상적인 응답을 못주었을 때(예를 들어 TIMEOUT), 클라이언트가 같은 요청을 다시 해도 되는지의 판단 근거가 됩니다. 전반적으로 많이 활용된다고 합니다.
    • Q: 재요청 중간에 다른 곳에서 리소스를 변경해버리면 어떻게 되나요?
      • 예) 사용자1: GET -> username:A, age:20
      • 예) 사용자2: PUT -> username:A, age:30
      • 예) 사용자1: GET -> username:A, age:30 -> 사용자2의 영향으로 바뀐 데이터가 조회됩니다. 이 상황에서 GET은 멱등하지 않은 것인가요?
    • A: 멱등은 외부 요인으로 중간에 리소스가 변경되는 것 까지는 고려하지는 않습니다. 동일한 요청을 해도 괜찮은지의 판단 근거 개념을 충족해야 하기 때문입니다.
  • 캐시가능(Cacheable Methods)

    • "응답 결과 리소스를 캐시(이전에 가져온 리소스 재사용)해서 사용해도 되는가?"에 대한 개념입니다.
    • GET, HEAD, POST, PATCH 캐시가능합니다.
    • 실제로는 GET, HEAD 정도만 캐시로 사용(실무에서는 GET만 캐시로 사용한다고 합니다.)
    • 이유는 POST, PATCH는 본문 내용까지 캐시 키로 고려해야 하는데, 구현이 쉽지 않기 때문이라고 합니다.

httpMethodAttribute


HTTP 메서드 활용

클라이언트에서 서버로 데이터 전송

클라이언트에서 서버로 데이터를 전송할 때 HTTP 메서드가 어떻게 활용되는지 HTTP API 설계 예시를 통해 알아보겠습니다.

클라이언트에서 서버로 데이터를 전송하는 데이터 전달 방식은 크게 2가지 입니다.

  • URI 끝에 쿼리 파라미터를 포함해서 데이터 전송하는 방법
    • GET
    • 주로 정렬 필터(예) 검색어, 게시판 리스트에 정렬 조건 포함..)에 사용됩니다.
  • HTTP 메시지 바디를 통한 데이터 전송하는 방법
    • POST, PUT, PATCH
    • 주로 회원 가입, 상품 주문, 리소스 등록, 리소스 변경 할 때 사용됩니다.

크게 4가지 상황으로 예시를 통해 정리해보겠습니다.

  • 정적 데이터 조회

    • 주로 이미지, 정적 텍스트 문서 조회에 사용됩니다.
    • GET 사용
    • 정적 데이터는 일반적으로 쿼리 파라미터 없이 리소스 경로로 단순하게 조회가 가능합니다.
  • 동적 데이터 조회

    • 클라이언트가 쿼리 파라미터를 기반으로 데이터를 전달해서 동적 데이터 결과를 조회해야 할 때 사용됩니다.
    • 주로 검색, 게시판 목록에서 정렬 필터(검색어)에 사용됩니다.
    • 조회 조건을 줄여주는 필터, 조회 결과를 정렬하는 정렬 조건에 주로 사용됩니다.
    • GET을 사용하며 GET은 쿼리 파라미터를 사용해서 데이터를 전달합니다.
  • HTML Form을 통한 데이터 전송 html form 태그를 submit하면 웹 브라우저가 form 태그의 데이터를 읽어서 다음과 같은 HTTP 메시지를 생성해줍니다.

    • 주로 회원 가입, 상품 주문, 데이터 변경에 사용됩니다. formPOST
  • Content-Type : 쿼리 파라미터와 유사한 key - value 형식으로 데이터를 생성해서 HTTP 바디에 포함시켜줍니다. 서버에 데이터가 전송되면 Content-Type을 파싱하여 데이터를 저장합니다.

  • form 태그에서 GET 메서드를 사용할 수도 있습니다. 이 경우엔 웹 브라우저가 GET 메서드로 변경해서 메시지 바디를 사용하지 않고 쿼리 파라미터에 바로 포함시킵니다. 리소스 변경이 발생하는 곳에 사용하거나 form태그의 save에 조회를 사용하지 않도록 주의하고 조회에 사용해야 합니다. formGET

  • Binary 데이터를 포함해서 전송할 때는 multipart/form-data를 사용합니다.

    • 예를 들어 이미지같은 Binary 데이터를 포함해서 함께 전송해야 하는 경우 웹브라우저가 Content-Type에 boundary를 자동 추가합니다. boundary로 Content-Type을 구분해서 여러 형식의 form 데이터를 전송하는 원리를 통해 파일을 전송합니다. multipart/fromData
  • 정리

    • HTML Form submit시 POST 전송
      • 예) 회원 가입, 상품 주문, 데이터 변경할 때 주로 사용됩니다.
    • Content-Type: application/x-www-form-urlencoded 사용합니다.
      • form의 내용을 메시지 바디를 통해서 전송(key=value, 쿼리 파라미터 형식)합니다.
      • 전송 데이터를 url encoding 처리합니다.
        • 예) abc김 -> abc%EA%B9%80 (UTF-8)
    • HTML Form은 GET 전송도 가능
      • 주의 : GET은 저장하거나 리소스 변경이 발생하는 곳에 사용하면 안 됩니다.
      • 마찬가지로 쿼리 파라미터 형식이 사용됩니다.
    • Content-Type: multipart/form-data
      • 파일 업로드 같은 바이너리 데이터 전송시 사용됩니다.
      • 다른 종류의 여러 파일과 폼의 내용 함께 전송 가능(그래서 이름이 multipart)합니다.
    • 참고: HTML Form 전송은 GET, POST만 지원합니다.
  • HTTP API를 통한 데이터 전송

    • 서버 to 서버
      • 주로 백엔드 시스템 통신에 사용됩니다.(html이 전혀 없습니다.)
    • 앱 클라이언트
      • 아이폰, 안드로이드 같은 앱 클라이언트에서도 사용됩니다.
    • 웹 클라이언트
      • HTML에서 Form 전송 대신 자바 스크립트를 통한 통신에 사용(AJAX)됩니다.
      • 예) React, VueJs 같은 웹 클라이언트와 함께할 때 API 통신을 많이 사용한다고 합니다.
    • POST, PUT, PATCH: 메시지 바디를 통해 데이터 전송할 때 사용됩니다.
    • GET: 조회할 때 항상 쿼리 파라미터로 데이터 전달해서 사용합니다.
    • Content-Type: application/json을 주로 사용합니다. (사실상 표준)
      • 예전엔 XML을 표준으로 정말 많이 사용했지만 최근에는 이해하기 쉽고 단순하며 크기도 작은 데이터인 JSON을 많이 사용한다고 합니다.
      • TEXT, XML, JSON 등등

    클라이언트에서 서버로 데이터를 전송하는 데이터 전달 방식은 크게 2가지 입니다.

    • 쿼리 파라이터를 통한 데이터 전송
      • GET
      • 주로 정렬 필터(검색어)에 사용됩니다.
    • 메시지 바디를 통한 데이터 전송
      • POST, PUT, PATCH
      • 회원 가입, 상품 주문, 리소스 등록, 리소스 변경에 사용됩니다.
    • 4가지 상황
      • 정적 데이터 조회 : GET으로 URL path에 리소스 경로만 지정해주면 됩니다.
      • 동적 데이터 조회 : GET을 사용하지만 URL path에 리소스 경로를 지정하고 동적 결과를 위해 쿼리 파라미터를 전달합니다.
      • HTML Form을 통한 데이터 전송 : POST, GET 사용 가능합니다. 데이터를 변경할 땐 POST, 조회할 때는 GET을 사용합니다.
      • HTTP API를 통한 데이터 전송 : HTML Form을 사용하지 않는 거의 모든 상황에서 주로 서버 to 서버, 앱 클라이언트, 웹 클라이언트 AJAX 통신할 때 사용됩니다.

HTTP API 설계 예시

HTTP URI를 어떻게 설계하는지 설계 예시를 알아보겠습니다.

  1. 회원 관리 시스템 - 컬렉션 POST 기반 등록 API 설계
  • 회원 목록 /members -> GET(회원 데이터를 쿼리파라미터로 정렬, 조회)
  • 회원 등록 /members -> POST(신규 회원 데이터 추가)
  • 회원 조회 /members/{id} -> GET(특정 회원 데이터 조회)
  • 회원 수정 /members/{id} -> PATCH(회원 데이터 부분 수정), PUT(완전히 대체, 예) 게시글 수정), POST (애매할 땐 천하무적 POST)
  • 회원 삭제 /members/{id} -> DELETE(회원 데이터 삭제)

(중요)POST는 신규 자원을 등록할 때 다음과 같은 특징을 가집니다.

  • 클라이언트는 등록될 리소스의 URI를 모릅니다.
    • 회원 등록 /members -> POST
    • POST /members
  • 서버가 새로 등록될 리소스 URI를 생성해줍니다.
    • HTTP/1.1 201 Created Location: /members/100 이러한 __클라이언트가 서버에게 리소스 관리를 전담하는 형식의 저장소__를 "컬렉션(Collection)"이라고 합니다.
  • 컬렉션(Collection)
    • 서버가 관리하는 리소스 디렉토리를 뜻합니다.
    • 서버가 리소스의 URI를 생성하고 관리합니다.
    • 여기서 컬렉션의 리소스는 /members입니다.
  1. 파일 관리 시스템 - 스토어 이번엔 원격지의 파일을 관리하는 시스템 설계 예시를 알아보겠습니다. PUT 기반 등록 API 설계
  • 파일 목록 /files -> GET(파일 데이터 조회)

  • 파일 조회 /files/{filename} -> GET(특정 파일 조회)

  • 파일 등록 /files/{filename} -> PUT(신규 파일 생성, 기존 파일은 덮어쓰기)

    • POST보다 PUT이 적합한 점을 알 수 있습니다.
  • 파일 삭제 /files/{filename} -> DELETE (파일 삭제)

  • 파일 대량 등록 /files -> POST (PUT으로 파일 등록을 하기 때문에 POST의 의미를 임의로 지정할 수 있습니다.)

  • 클라이언트가 리소스의 URI를 알고 있어야 합니다.(POST가 아니라 PUT을 사용하는 것에 주목해야 합니다.)

    • 파일 등록 /files/{filename} -> PUT
    • PUT /files/star.jpg
  • 클라이언트가 직접 리소스의 URI를 지정합니다.

    • 클라이언트가 리소스의 URI를 직접 관리합니다. 이러한 __클라이언트가 서버에 의존하지 않고 직접 리소스를 관리하는 형식__의 저장소를 스토어라고 합니다.
  • 스토어(Store)

    • 클라이언트가 관리하는 리소스 저장소입니다.
    • 클라이언트가 리소스의 URI를 알고 관리합니다.
    • 여기서 스토어는 /files 입니다.
  • 참고: 대부분 컬렉션을 사용하고 스토어는 비중이 적다고 합니다. 파일 업로드와 비슷한 서버에 지원하는 경우는 사용한다고 합니다.

  1. HTML FORM 사용 HTML FORM은 GET, POST만 지원합니다. AJAX 같은 기술을 사용해서 해결 가능(회원 API참고)할 수 있지만, 순수 HTML, HTML FORM을 가정했을 땐 다음과 같은 제약이 있습니다. 따라서 이러한 __GET, POST만 지원하는 제약을 해결하기 위해 동사로된 리소스 경로를 활용하는 형식의 컨트롤 URI를 사용__해야 합니다.
  • 회원 목록 /members -> GET

  • 회원 등록 폼 /members/new -> GET(실제 등록 폼 조회)

  • 회원 등록 /members/new, /members -> POST(데이터 저장)

  • 회원 조회 /members/{id} -> GET

  • 회원 수정 폼 /members/{id}/edit -> GET(수정 폼 조회)

  • 회원 수정 /members/{id}/edit, /members/{id} -> POST(수정 폼 저장)

  • 회원 삭제 /members/{id}/delete -> POST (어쩔 수 없이 컨트롤 URI를 사용합니다.)

  • 사용된 컨트롤 URI : /new, /edit, delete

    • HTTP 메서드로 해결하기 애매한 경우에 사용합니다. (HTTP API 포함)
    • 이상적으로 HTTP 메서드를 사용하는 것이 좋겠지만 실제 실무에서는 어쩔 수 없이 정말 많이 사용된다고 합니다.
    • 컨트롤 URI를 남발하면 안됩니다.
      • 최대한 리소스 개념으로 URI를 설계하고 그 과정에서 어쩔 수 없을 때 컨트롤 URI를 대체제로써 사용해야 합니다.

정리

  • HTTP API - 컬렉션 스타일로 조회했습니다.
    • POST 기반으로 등록했습니다.
    • POST 기반이기 때문에 서버가 리소스 URI 결정했습니다. 이를 컬렉션 스타일이라고 합니다.
  • HTTP API - 스토어
    • PUT 기반으로 등록했습니다.
    • 클라이언트가 리소스 URI 결정했습니다. 이를 스토어라고 합니다.
  • HTML FORM 사용
    • 순수 HTML + HTML form 사용하는 경우를 알아보았습니다.
    • GET, POST만 지원한다는 제약이 있습니다.
      • 동사로된 리소스 경로를 활용한 컨트롤 URI를 사용했습니다. 컨트롤 URI는 대체제임을 주의해야하며 남발해선 안됩니다.

여러 사람들이 API를 설계하다보면 좋은 사례가 모이게 됩니다. 다음과 같은 좋은 사례가 정리된 모음이 있다고 합니다. 참고하면 좋은 URI 설계 개념

  • 문서(document)
    • 단일 개념(파일 하나, 객체 인스턴스, 데이터베이스 row)
    • 예) /members/100, /files/star.jpg
  • 컬렉션(collection)
    • 서버가 관리하는 리소스 디렉터리
    • 서버가 리소스의 URI를 생성하고 관리
    • 예) /members
  • 스토어(store)
    • 클라이언트가 관리하는 자원 저장소
    • 클라이언트가 리소스의 URI를 알고 관리
    • 예) /files
  • 컨트롤러(controller), 컨트롤 URI
    • 문서, 컬렉션, 스토어로 해결하기 어려운 추가 프로세스 실행
    • 동사를 직접 사용
    • 예) /members/{id}/delete

HTTP 상태 코드

상태 코드 : 클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능입니다.

  • 1xx (Informational): 요청이 수신되어 처리중을 의미합니다.
    • 거의 사용하지 않습니다.
  • 2xx (Successful): 클라이언트의 요청이 성공적으로 처리되었음을 의미합니다.
    • 200 OK : 요청 성공
    • 201 Created : 요청이 성공해서 서버가 클라이언트가 요청한 신규 자원을 생성하고 URI를 관리, 주로 POST 등록에 사용됩니다. 200,201
    • 202 Accepted : 요청이 접수되었으나 처리가 완료되지 않았음을 의미합니다.
      • 예 ) 요청 접수 후 1시간 뒤에 배치 프로세스가 요청을 처리해야 로직이 정상 작동할 때
    • 204 No Content : 서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음을 의미합니다.
      • 결과 내용이 없어도 204 메시지(2xx)만으로 성공을 인식할 수 있습니다.
      • 예) 웹 문서 편집기에서 save 버튼
      • save 버튼의 결과로 아무 내용이 없어도 됩니다.
      • save 버튼을 눌러도 같은 화면을 유지해야 합니다.
  • 3xx (Redirection): 요청을 완료하려면 유저 에이전트(클라이언트 프로그램, 주로 웹 브라우저)의 추가 조치가 필요함을 의미합니다.
    • 300 Multiple Choices (거의 사용하지 않아서 301~308이 중요하다고 합니다.)
    • 301 Moved Permanently
    • 302 Found
    • 303 See Other
    • 304 Not Modified
    • 307 Temporary Redirect
    • 308 Permanent Redirect

리다이렉션(Redirection) : 웹 브라우저는 3xx 응답의 결과에 Location 헤더가 있으면, Location 위치로 자동으로 이동합니다.(리다이렉트) 기존 event 페이지를 new-event로 바꾼 예시를 통해 흐름을 이해해보겠습니다.

PRG 사용자가 /event를 통해 웹 브라우저에 접근하면 서버가 /new-event로 변경되었음을 자동으로 알려줍니다. 이것을 "리다이렉트"라고 합니다. 사용자가 변경된 경로로 재접근하면 요청이 정상적으로 처리됩니다. 클라이언트의 입장에선 너무 빨라서 인식을 못한다고 하지만 리다이렉션에는 다음과 같은 종류가 있습니다.

리다이렉션 종류

  • 영구 리다이렉션 : 특정 리소스의 URI가 영구적으로 이동했음을 의미합니다.

    • 301, 308
    • 원래의 URL를 사용하지 않고, 검색 엔진 등에서도 변경을 인지합니다.
    • 301 Moved Permanently
      • 리다이렉트시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있습니다.(MAY) 결과적으로 새로운 페이지가 렌더링 됩니다. 301
    • 308 Permanent Redirect
      • 301과 기능은 같습니다.
      • 리다이렉트시 요청 메서드와 본문을 유지합니다.(처음 POST를 보내면 리다이렉트도 POST 유지) 301의 문제를 해결된 모습을 볼 수 있습니다. 308 스펙의 설명일 뿐 실무에선 내부 전달 데이터가 모두 함께 바뀌기 때문에 301이 사용된다고 합니다. POST 유지보다 GET으로 전환하는 경우가 많다고 합니다. 대부분 301로 구현되어서 스펙도 일부 변경되었다고 합니다.
  • 일시 리다이렉션 : 리소스의 URI가 일시적인 변경되었음을 의미합니다.

    • 따라서 검색 엔진 등에서 URL을 변경하면 안됩니다.
    • 302,307,303
      • 302 Found
        • 리다이렉트시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있습니다.(MAY)
      • 307 Temporary Redirect
        • 302와 기능은 같음
        • 리다이렉트시 요청 메서드와 본문을 유지해야 합니다.(요청 메서드를 변경하면 안됩니다. MUST NOT)
      • 303 See Other
        • 302와 기능은 같음
        • 리다이렉트시 요청 메서드가 GET으로 변경됩니다.
    • PRG : Post/Redirect/Get, 일시적인 리다이렉션 - 예시
      • POST로 주문 후에 데이터 결과가 남아있는 상태에서 웹 브라우저를 새로고침하면?
      • 물론 서버에서 잘 조치해야하지만, 새로고침은 다시 요청이기 때문에 서버에 주문 데이터가 중복 될 수 있습니다. beforePRG
      • 이러한 문제를 해결하기 위해 PRG 패턴을 사용합니다.
        • POST로 주문후에 새로 고침으로 인한 중복 주문을 방지합니다.
        • POST로 주문후에 주문 결과 화면을 GET 메서드로 리다이렉트합니다.
        • 따라서 새로고침해도 결과 화면을 GET으로 조회합니다.
        • 중복 주문 대신에 결과 화면만 GET으로 다시 요청하게 됩니다.
        • 이러한 이유로 301, 302가 많이 사용됩니다. afterPRG
      • PRG 이후 리다이렉트는 URL이 이미 POST -> GET으로 리다이렉트된 상태이기 때문에, 새로 고침을 해도 GET으로 결과 화면만 조회하게 됩니다. 이렇게 하면 사용자의 사용성도 좋아지고 서버의 오류도 줄어들게 됩니다.
    • 잠깐 정리
      • 302 Found -> GET으로 변할 수 있습니다.
      • 307 Temporary Redirect -> 메서드가 변하면 안됩니다.
      • 303 See Other -> 메서드가 GET으로 변경해야만 합니다.
    • 역사
      • 처음 302 스펙의 의도는 HTTP 메서드를 유지하는 것이었습니다.
      • 그런데 웹 브라우저들이 대부분 GET으로 바꾸어버렸습니다.(일부는 다르게 동작)
      • 그래서 모호한 302를 대신하는 명확한 307, 303이 등장했습니다.(301 대응으로 308도 등장)
    • 현실
      • 307, 303을 권장하지만 현실적으로 이미 많은 애플리케이션 라이브러리들이 302를 기본값으로 사용합니다.
      • 자동 리다이렉션시에 GET으로 변해도 되면 그냥 302를 사용해도 큰 문제 없다고 합니다.
    • 기타 리다이렉션 : 300, 304
      • 300 Multiple Choices: 안쓴다고 합니다.
      • 304 Not Modified
      • 결과대신 캐시를 목적으로 사용합니다.
      • 클라이언트에게 리소스가 수정되지 않았음을 알려줍니다. 따라서 클라이언트는 로컬PC에 저장된 캐시를 재사용하게 됩니다. (캐시로 리다이렉트 합니다.)
      • 304 응답은 응답에 메시지 바디를 포함하면 안됩니다. (로컬 캐시를 사용해야 하므로)
      • 조건부 GET, HEAD 요청시에 304를 사용합니다.
  • 4xx (Client Error): 클라이언트 오류, 요청자의 잘못된 문법등으로 서버가 요청을 수행할 수 없음을 의미합니다.

    • 오류의 원인이 클라이언트에 있기 때문에 이미 잘못된 요청, 데이터를 보내어 재시도가 실패하게 됩니다.
    • 400 Bad Request : 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음을 의미합니다.
      • 주로 요청 구문, 메시지 등등 오류이기 때문에 클라이언트는 요청 내용을 다시 검토하고 보내야 합니다. 예) 요청 파라미터가 잘못되거나, API 스펙이 맞지 않을 때 백엔드 개발자는 클라이언트의 잘못을 명확히 표현해줘야 한다고 합니다. 500은 서버 쪽의 잘못이라고 생각하기 때문에..
    • 401 Unauthorized : 클라이언트가 해당 리소스에 대한 인증이 필요함을 의미합니다.
      • 인증(Authentication)이 되지 않았기 때문에 401 오류 발생시 응답에 WWW-Authenticate 헤더와 함께 인증 방법을 설명해줘야 합니다.
      • 참고
      • 인증(Authentication): 본인이 누구인지 확인, (로그인)
      • 인가(Authorization): 권한부여 레벨(ADMIN 권한처럼 특정 리소스에 접근할 수 있는 권한, 인증이 있어야 인가가 있습니다.)
      • 오류 메시지가 Unauthorized 이지만 인증 되지 않음 (이름이 아쉽다고 합니다.)
    • 403 Forbidden : 서버가 요청을 이해했지만 승인을 거부함을 의미합니다.
      • 주로 인증 자격 증명은 있지만, 접근 권한이 불충분한 경우
      • 예) 어드민 등급이 아닌 사용자가 로그인은 했지만, 어드민 등급의 리소스에 접근하는 경우
    • 404 Not Found : 요청 리소스를 찾을 수 없음을 의미합니다.
      • 요청 리소스가 서버에 없어서 찾을 수 없습니다.
      • 또는 클라이언트가 권한이 부족한 리소스에 접근할 때 403을 보여주지않고 아예 해당 리소스를 숨기고 싶을 때 사용됩니다.
  • 5xx (Server Error) : 서버 오류, 서버가 정상 요청을 처리하지 못함을 의미합니다.

    • 서버에 문제가 있기 때문에 재시도하면 성공할 수도 있습니다. 예) 서버 복구
    • Internal Server Error : 서버 문제로 오류 발생, 애매하면 500 오류
      • 서버 내부 문제로 오류가 발생했음을 의미합니다.
      • 백엔드에서 발생한 애매한 오류는 대부분 500 오류라고 생각하면 된다고 합니다.
    • 503 Service Unavailable : 서비스 이용 불가
      • 서버가 일시적으로 과부하되거나 또는 예정된 작업으로 잠시 요청을 처리할 수 없음을 의미합니다.
      • Retry-After 헤더 필드로 얼마뒤에 복구되는지 예상 시간을 보낼 수도 있습니다.
  • 주의 : 400은 명확하게 클라이언트의 잘못(주로 스펙이 맞지 않거나 인증이 되지 않은 경우)이지만 500은 서버 내부의 문제입니다. 웬만하면 서버에서는 5xx 오류를 만들면 안 됩니다. 정말 서버에 내부적으로 문제가 생겼을 때(예를 들면 NullPointerException) 5xx 에러를 만들어야 합니다.

만약 모르는 상태 코드가 나타나면 어떻게 해야할까요?

  • 클라이언트가 인식할 수 없는 상태코드를 서버가 반환하면?
  • 클라이언트는 상위 상태코드로 해석해서 처리하기 때문에 미래에 새로운 상태 코드가 추가되어도 클라이언트를 변경하지 않아도 됩니다.
    • 예)
    • 299 ??? -> 2xx (Successful)
    • 451 ??? -> 4xx (Client Error)
    • 599 ??? -> 5xx (Server Error)

HTTP 헤더1 - 일반 헤더

HTTP 헤더 필드는 "헤더명:필드값"으로 구성되어 있습니다.

header

  • header-field = field-name ":" OWS field-value OWS (OWS:띄어쓰기 허용)

기본적인 HTTP 헤더의 용도는 다음과 같습니다.

  • HTTP 전송에 필요한 모든 부가정보를 담습니다.
    • 예) 메시지 바디의 내용, 메시지 바디의 크기, 압축, 인증, 요청 클라이언트, 서버 정보, 캐 시 관리 정보...
  • 표준 헤더 필드가 굉장히 많습니다.
  • 참고 : 필요시 임의의 헤더 필드 추가도 가능합니다.
    • 예) helloworld:field

과거에는 헤더를 메시지 전체, 클라이언트의 요청, 서버의 응답, 실제 메시지 바디 관련 내용과 같이 크게 4가지로 분류했다고 합니다.

lastDayHeader

  • 메시지 본문은 엔티티 본문을 전달하는데 사용된다고 정의되었습니다.
  • 엔티티 본문 : 요청이나 응답에서 전달할 실제 데이터를 의미합니다.
  • __엔티티 헤더__는 __엔티티 본문__의 데이터를 해석할 수 있는 정보를 제공합니다.
    • 데이터(html, json), 데이터 길이, 압축 정보.. messageBody

그런데 2014년에 스펙이 다음과 같이 변경됩니다.

HTTP 표준

  • 1999년 RFC2616 (폐기)
  • 2014년 RFC7230~7235 등장

스펙이 변경되면서 엔티티 바디라는 용어가 다음과 같이 대체됩니다.

RFC723x 변화

  • 엔티티(Entity) -> 표현(Representation)
  • Representation = representation Metadata + Representation Data
  • 표현 = 표현에 대한 메타데이터 + 표현 데이터

대체된 최신 스펙을 살펴보면 다음과 같습니다. representation

  • 메시지 본문(message body)을 통해 표현 데이터를 전달한다고 합니다.
  • 메시지 본문을 페이로드(payload)라고도 합니다.
  • 표현이란 요청이나 응답에서 전달할 실제 데이터를 의미합니다.
    • 데이터 유형(html, json), 데이터 길이, 압축 정보..
  • 참고 : 표현 헤더는 표현 메타데이터와 페이로드 메시지를 구분해야 합니다.

자세히 알아보겠습니다.

표현(Representation)

리소스는 실제로 구체적이지 않고 추상적이라고 합니다. DB일수도 있고 바이트코드로 어딘가에 저장되어 있을지 모르는 추상적 데이터이기 때문에 클라이언트와 서버가 주고받기 위해선 서로가 이해할 수 있는 약속으로 변환해야 할 필요가 있습니다.

최신 스펙에서는 실제 데이터와 리소스를 포함한 데이터들을 어떤 유형으로 전달할지에 대해 __"표현(Representation)"__이라고 정의해두었습니다. 실제로 사용하는 모습은 다음과 같습니다.

representation

"표현"을 사용하는 방식은 다음과 같습니다.

  1. Content-Type : 표현 데이터의 형식
  • 본문에 들어가는 내용이 무엇인지를 의미합니다. 예) text/ html; chartset(인코딩)=UTF-8, application/json, 이미지일 경우 image/png content-type
  1. Content-Encoding: 표현 데이터를 압축하기 위해 사용합니다.
  • 데이터를 전달하는 곳에서 압축 후 인코딩 헤더를 추가합니다.
  • 데이터를 읽는 쪽에서 인코딩 헤더의 정보로 압축을 해제합니다.
  • 예) gzip, deflate, identity contentEncoding
  1. Content-Language: 표현 데이터의 자연 언어를 표현했음을 의미합니다.
  • ko(korean), en(english)
  • 클라이언트에서 언어를 선택하도록 부가 작업을 할 수도 있다고 합니다. contentLanguage
  1. Content-Length: 표현 데이터의 길이를 의미합니다.
  • 바이트 단위로 구성되어있습니다.
  • Transfer-Encoding(전송 코딩)을 사용하면 Content-Length를 사용하면 안됩니다. contentLength
  • 참고 : 표현 헤더는 전송, 응답 둘다 사용할 수 있습니다.

협상(Content Negotiation)

클라이언트는 서버에게 자신이 선호하고 원하는 표현을 요청할 수 있습니다. 서버는 클라이언트가 원하는 형태로 응답할 수 있습니다. 이것을 협상(Content Negotiation)이라고 하며 협상의 종류는 다음과 같습니다.

  • Accept: 클라이언트가 선호하는 미디어 타입 전달을 요청합니다.
  • Accept-Charset: 클라이언트가 선호하는 문자 인코딩을 요청합니다.
  • Accept-Encoding: 클라이언트가 선호하는 압축 인코딩을 요청합니다.
  • Accept-Language: 클라이언트가 선호하는 자연 언어를 요청합니다.
  • 협상 헤더는 요청시에만 사용합니다.

Accept-Language의 예시를 통해 협상의 사용전과 사용후를 알아보겠습니다.

적용 전 : 한국어 브라우저를 사용하는 클라이언트가 데이터를 요청하면 다중 언어를 지원하는 서버에서 기본값인 영어로 응답합니다. beforeAcceptLanguage 적용 후 : 한국어 브라우저를 사용하는 클라이언트가 자신의 선호 언어를 명확히 표현해주면 다중 언어 지원 서버에서 요청을 응답할 때 협상한 결과를 반환해줍니다. afterAcceptLanguage

조금 복잡한 상황을 예로 들어보겠습니다.

  • 클라이언트는 한국어 브라우저를 사용합니다.
  • 서버는 기본적으로 독일어를 지원하지만 보조로 영어도 지원합니다.
  • 클라이언트는 한국어가 좋지만 한국어를 지원하지 않는다면, 독일어보단 영어를 선호합니다. acceptLanguageExam 이러한 상황에선 우선순위가 필요합니다.

협상과 우선순위

  • Quality Values(q) : 협상에서 우선순위를 표현할 때 사용합니다.
  • 0~1로 구성되어 있으며, 숫자가 클수록 높은 우선순위를 가집니다.
  • 예) Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
      1. ko-KR;q=1 (q생략 가능)
      1. ko;q=0.9
      1. en-US;q=0.8
      1. en:q=0.7 위와 같은 예를 적용하면 다중 언어 지원 서버에서 Accept-Language를 해석하여 적절한 데이터를 응답하게 됩니다. acceptLanguageExam2

실제 구글에서 검색 결과를 개발자 도구로 살펴보면 다음과 같이 요청 헤더가 한국어를 우선순위로 하는 것을 알 수 있습니다.

ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6

협상과 우선순위2

협상은 구체적인 것을 우선하기도 합니다. 자세하게 적을 수록 우선합니다.

  • 예) "Accept: text/*, text/plain, text/plain;format=flowed, /"의 우선 순위는 다음과 같습니다.
    1. text/plain, text/plain;format=flowed
    2. text/plain
    3. text/*
    4. /

또한 구체적인 것을 기준으로 미디어 타입을 맞춥니다. contentsNegotiation3

전송 방식

전송 방식은 단순히 4가지로 분류할 수 있습니다.

  • 단순 전송
  • 압축 전송
  • 분할 전송
  • 범위 전송
  1. 단순 전송 Content-Length 요청과 응답 발생 시 메시지 바디에 대한 길이를 지정합니다.

contentLength

  1. 압축 전송 Content-Encoding 클라이언트의 요청 데이터를 용량이 줄어들도록 압축하고 압축에 대한 정보를 포함하여 전달하면 서버가 해당 데이터의 압축을 해제하여 분석 후 응답 데이터를 전달합니다.

Content-Encoding

  1. 분할 전송 Transfer-Encoding chunked : 덩어리 분할된 데이터를 순서대로 전달합니다. 용량이 큰 데이터의 경우 응답 대기 시간을 단축하기 위해 사용합니다.
  • 참고 : 분할 전송에선 content-length를 포함하면 안됩니다. chunked별로 각각 다른 길이가 지정되기 때문이라고 합니다. Transfer-Encoding
  1. 범위 전송 Range, Content-Range 마찬가지로 큰 용량의 데이터일 경우 요청받는 서버에서 낭비없이 할당할 수 있도록 범위를 지정해서 전송하는 방법입니다.

Range

일반 정보

쉽고 단순한 HTTP 헤더의 일반 정보들은 다음과 같습니다.

  • From: 유저 에이전트의 이메일 정보

    • 일반적으로 잘 사용되진 않지만, 검색 엔진 같은 곳에서 주로 사용한다고 합니다.(ex. 크롤링 거부)
    • 요청에서 사용합니다.
  • Referer: 현재 요청된 페이지의 이전 웹 페이지 주소

    • 예를 들어 Google -> Naver로 이동하는 경우 Naver를 요청할 때 Referer: Google을 포함해서 요청합니다.
    • Referer을 사용해서 유입 경로를 분석할 때 많이 사용한다고 합니다.
    • 요청에서 사용합니다.
    • 참고 : refer는 단어 rederer(참조자)의 오타입니다.
  • User-Agent: 유저 에이전트 애플리케이션 정보

    • user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/ 537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36
    • 클라이언트의 애플리케이션 정보(웹 브라우저 정보..)를 의미합니다.
    • 서버 입장에서 버그를 발견했을 때 로그를 파싱해서 특정 브라우저의 장애 여부를 파악 가능합니다.
    • 사용자들의 브라우저 접근 경로를 통계내는데에도 사용할 수 있습니다.
    • 요청에서 사용합니다.
  • Server: 요청을 처리하는 ORIGIN 서버의 소프트웨어 정보

    • ORIGIN : HTTP에서 요청 시 중간에 여러 프록시 서버들을 거치게 됩니다. 프록시 서버를 거치고 나서 요청에 해당하는 표현 데이터를 생성해서 응답해주는 마지막 서버를 ORIGIN 서버라고 합니다. 서버의 소프트웨어 정보는 다음과 같습니다.
    • Server: Apache/2.2.22 (Debian)
    • server: nginx
    • 응답에서 사용합니다.
  • Date: 메시지가 발생한 날짜와 시간

    • 예) Date: Tue, 15 Nov 1994 08:12:31 GMT
    • 응답에서 사용(과거에는 요청에서도 사용했지만 최신 스펙에서는 응답에서만 사용하도록 변경되었습니다.)

특별한 정보

실제 애플리케이션에 영향을 주는 특별한 정보를 의미하는 HTTP 헤더는 다음과 같습니다.

  • Host: 요청한 호스트 정보(도메인)
  • Location: 페이지 리다이렉션
  • Allow: 허용 가능한 HTTP 메서드
  • Retry-After: 유저 에이전트가 다음 요청을 하기까지 기다려야 하는 시간
  1. Host: 요청한 호스트 정보(도메인)
  • 요청에서 사용합니다.
  • __필수__값입니다.
  • IP로만 통신할 경우 하나의 서버에서 여러 도메인을 구분할 수 없습니다.
  • 하나의 서버가 여러 도메인을 처리해야 할 때 구분하기 위해 사용합니다.
  • 하나의 IP 주소에 여러 도메인들이 적용되어 있을 때 사용합니다. host
  1. Location : 페이지 리다이렉션
  • 웹 브라우저는 3xx 응답 결과에 Location 헤더가 있으면, Location 위치로 자동 이동합니다.(리다이렉트)
  • 응답코드 3xx 설명 다시 볼 것
  • 201 (Created): 201에서 사용할 경우 Location 값은 요청에 의해 생성된 리소스 URI를 뜻합니다.
  • 3xx (Redirection): Location 값은 요청을 자동으로 리디렉션하기 위한 대상 리소스를 가리킵니다.
  1. Allow : 허용 가능한 HTTP 메서드
  • 405 (Method Not Allowed) 에서 응답에 포함해야합니다.
  • 예) 경로는 있는데 POST를 제공하지 않을 경우 405 에러를 보내면서 응답에 다음과 같이 지원하는 Allow를 포함시켜 인식하도록 해야합니다.
  • Allow: GET, HEAD, PUT
  1. Retry-After : 유저 에이전트가 다음 요청을 하기까지 기다려야 하는 시간
  • 503 (Service Unavailable): 서비스가 언제까지 불능인지 알려줄 수 있습니다.
  • Retry-After: Fri, 31 Dec 1999 23:59:59 GMT (날짜 표기)
  • Retry-After: 120 (초단위 표기)

인증

인증과 관련된 헤더는 다음과 같습니다.

  • Authorization: 클라이언트 인증 정보를 서버에 전달할 수 있습니다.

    • 예) Authorization: Basic xxxxxxxxxxxxxxxx
    • 참고 : 인증 방식은 다양합니다.(OAuth..) 각각 입력값이 다르기 때문에 추가 학습이 필요합니다.
  • WWW-Authenticate: 리소스 접근시 필요한 인증 방법 정의합니다.

    • 만약 리소스에 접근했는데 인증에 문제가 발생한 경우, 401 Unauthorized 응답과 함께 사용합니다.
    • 401 오류 시 다음과 같은 헤더를 추가하여 인증을 위해 참조할 수 있는 정보를 제공합니다.
    • WWW-Authenticate: Newauth realm="apps", type=1, title="Login to "apps"", Basic realm="simple"

쿠키

중요하게 다뤄야하는 "쿠키"의 원리는 다음과 같습니다.

  • Set-Cookie: 서버에서 클라이언트로 쿠키 전달합니다.(응답)

  • Cookie: 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청시 서버로 전달합니다.

  • 예) 방문자 welcome 페이지 접근

  • 방문자 "홍길동"으로 로그인 nonCookie

  • "홍길동" welcome 페이지 접근

    • 쿠키 미사용시, "안녕하세요. 홍길동님"이 아닌 "안녕하세요. 손님"이라는 응답을 볼 수 있습니다. userHasNonCookie
  • 서버 입장에서 로그인한 사용자인지 여부를 알 수 없기 때문에 문제가 발생했습니다.

  • 이러한 문제는 무상태 프로토콜을 떠올려보면 왜 생겼는지 알 수 있습니다.

  • Stateless

    • HTTP는 기본적으로 무상태(Stateless) 프로토콜입니다.
    • 클라이언트와 서버가 요청과 응답을 주고 받으면 어느정도 연결이 지속되긴 하지만 결국 연결이 끊어집니다.
    • 클라이언트가 다시 요청하면 서버는 이전 요청을 기억하지 못합니다.
    • 무상태 프로토콜에서 클라이언트와 서버는 서로 상태를 유지하지 기 때문입니다.

이 문제에 대해 모든 요청에 사용자 정보를 포함하는 대안이 있었습니다.

  • 하지만 모든 요청과 링크에 사용자 정보를 포함해도 문제가 발생합니다. serverHasAllUserInfo
  • 브라우저를 완전히 종료하고 다시 열면 없어집니다. (요즘은 웹스토리지에 저장하는 것으로 대응한다고 합니다.)
  • 모든 요청에 사용자 정보가 포함되도록 개발해야 합니다. 너무 많은 비용이 들어간다고 합니다.

이것을 해결하기 위해 도입된 것이 __"쿠키"__라고 합니다.

  • 웹 브라우저가 로그인 정보를 요청합니다.
  • 서버는 "Set-Cookie:user=value" 형식의 헤더를 포함한 상태로 응답합니다.
  • 웹 브라우저 내부에 쿠키 저장소에서 로그인 정보를 저장합니다. setCookie
  • 로그인 이후 welcome 페이지 접근시 클라이언트가 요청을 보낼 때마다 쿠키 저장소에 접근하는 과정을 반드시 거칩니다.
  • 클라이언트는 쿠키 저장소에서 조회한 값을 참조하여 생성시킨 헤더를 포함하여 요청하게 됩니다. cookieGet

쿠키는 매번 모든 요청에 사용자 정보를 포함하도록 개발해야하는 것이 아닌, 쿠키 정보를 자동으로 포함하여 사용할 수 있는 도구입니다.

cookie

쿠키2

  • 예) set-cookie: sessionId=abcde1234; expires=Sat, 26-Dec-2020 00:00:00 GMT; path=/; domain=.google.com; Secure
  • 쿠키의 사용처
    • 사용자 로그인 세션을 관리할 때 정말 많이 쓰인다고 합니다.
    • 광고 정보 트래킹(사용자 취향 파악)에도 많이 사용됩니다.
  • 쿠키 정보는 항상 서버에 전송되기 때문에 다음과 같은 유의사항이 있습니다.
    • 네트워크 추가 트래픽이 유발됩니다.
    • 따라서 최소한의 정보만 사용해야 합니다.(세션 id, 인증 토큰)
    • 서버에 전송하지 않고 웹 브라우저 내부에 데이터를 저장하고 싶으면 웹스토리지(localStorage, sessionStrage)를 사용하면 됩니다.
    • 주의! : 보안에 민감한 데이터는 저장하면 안됩니다.(주민번호, 신용카드 번호..)

쿠키 - 생명주기 Expires, max-age

쿠키를 서버에서 영원히 보관할 수는 없기 때문에 다음과 같은 생명주기가 존재합니다.

  • 세션 쿠키: 만료 날짜를 생략하면 브라우저 종료시 까지만 유지합니다.

    • 예) Set-Cookie: max-age=3600 (3600초)
      • 0이나 음수를 지정하면 유효기간 이후에 쿠키 가 자동으로 삭제됩니다.
  • 영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지합니다.

    • 예) Set-Cookie: expires=Sat, 26-Dec-2020 04:39:21 GMT
      • 만료일이 되면 쿠키를 자동으로 삭제됩니다.

쿠키 - 도메인 Domain

쿠키에 도메인을 지정할 수도 있습니다. __도메인을 명시__하게 되면 "명시한 문서 기준 도메인 + 서브 도메인" 을 포함해서 적용하게 됩니다.

  • 예) domain=example.org를 지정해서 쿠키 생성
    • example.org는 물론이고
    • dev.example.org도 쿠키 접근 후 전송됩니다.

__생략__할 경우, __"현재 문서 기준 도메인"__만 적용됩니다.

  • 예) example.org 에서 쿠키를 생성하고 domain 지정을 생략
    • example.org 에서만 쿠키 접근
    • dev.example.org는 쿠키 미접근

쿠키 - 경로 Path

쿠키의 경로를 지정하게 되면 해당 경로를 포함한 하위 경로 페이지만 쿠키가 접근 할 수 있습니다.

  • 예) path=/home
  • 이 경로를 포함한 하위 경로 페이지만 쿠키 접근합니다. (보통 한 도메인에서 쿠키를 전부 전송하길 원하기 때문이라고 합니다.)
  • 일반적으로 path=/ 루트로 지정합니다.
  • 예) path=/home 으로 지정한 경우
    • path=/home 지정
    • /home -> 가능
    • /home/level1 -> 가능
    • /home/level1/level2 -> 가능
    • /hello -> 불가능

쿠키 - 보안 Secure, HttpOnly, SameSite

쿠키와 관련된 보안은 다음과 같은 3가지가 있습니다.

  • Secure
    • 원래 쿠키는 http, https를 구분하지 않고 전송 했으나,
    • Secure를 적용하면 https인 경우에만 클라이언트에서 서버로 쿠키를 전송합니다.
  • HttpOnly
    • XSS 공격을 방지하는 방법입니다.
    • HttpOnly를 적용하면 자바스크립트에서 접근 불가(document.cookie)합니다.
    • 대신 HTTP 전송에만 사용할 수 있습니다.
  • SameSite
    • XSRF 공격을 방지하는 방법입니다.
    • 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송할 수 있습니다. 서로 다른 경우에 쿠키가 전송되지 않도록 막는 방법입니다.
    • 지원하게 된지 얼마 안 된 기능이라 브라우저에서 지원 여부를 확인 후 사용해야 한다고 합니다.

HTTP 헤더2 - 캐시와 조건부 요청

캐시 기본 동작

캐시가 어떻게 동작하는지부터 알아보겠습니다.

캐시가 없을 때는 다음과 같은 단점들이 있었습니다.

  • 데이터가 변경되지 않아도 계속 네트워크를 통해서 데이터를 다운로드 받아야 했습니다.(중복)
  • 인터넷 네트워크는 매우 느리고 비쌉니다.
  • 동일한 요청을 중복해서 수행하게 되니 사용자 입장에서 브라우저 로딩 속도가 느립니다.
  • 즉, 느린 사용자 경험이 발생합니다. nonCache1 nonCache2

캐시를 적용하게 되면,

  • 웹 브라우저에서 헤더에 cache-control(캐시가 유효한 시간 의미(초단위))이란 설정 추가를 통해 서버와 같련된 데이터를 설정합니다.
  • 웹 브라우저 내부 캐시 저장소에 설정한 유효시간만큼 응답 결과를 저장하는 상태를 유지합니다.

cache1

  • 두 번째 요청 시에 우선 캐시를 유효시간을 검증합니다.
  • 유효시간을 충족할 경우 캐시에 저장된 데이터를 조회하기만 하면 됩니다. cache2

캐시 적용 이후

  • 캐시 덕분에 캐시 가능 시간동안 네트워크를 사용하지 않아도 됩니다.
  • 따라서 비싼 네트워크 사용량을 줄일 수 있습니다.
  • 최소한의 요청만을 수행하기 때문에 브라우저 로딩 속도가 매우 빨라집니다.
  • 즉, 빠른 사용자 경험이 가능해집니다.

캐시 시간 초과

만약 캐시의 유효 시간이 초과된다면 어떻게 될까요? 당연히 다시 요청하고 응답 결과를 다시 캐시에 저장해야 합니다.

  • 서버를 통해 데이터를 다시 조회하고 캐시를 갱신합니다.
  • 이때 다시 네트워크 다운로드가 발생합니다.

만약 캐시가 만료되었어도 클라이언트와 서버가 가진 데이터가 동일하다면, 네트워크 다운로드를 반복할 필요가 없습니다. 이를 해결하기 위한 방법이 있습니다.

검증 헤더와 조건부 요청1

  • 캐시 유효시간이 초과해서 서버에 요청하면 다음 두 가지 상황이 나타납니다.
    1. 서버에서 기존 데이터를 변경한 경우
    2. 서버에서 기존 데이터를 변경하지 않은 경우
    • 생각해보면 데이터를 전송하는 대신 저장해두었던 캐시를 재사용할 수 있습니다.
    • 단, 클라이언트와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법이 필요합니다. 이를 위해 "검증 헤더"를 추가합니다.

검증 헤더 추가

검증 헤더란 "데이터가 마지막에 수정된 시간"에 대한 정보를 Last-Modified라는 형식으로 헤더에 추가해주면 브라우저 캐시는 다음과 같은 내용을 저장합니다.

  • 응답 결과를 캐시에 저장하는 것 뿐만 아니라
  • 서버에 데이터 최종 수정일을 포함하여 저장합니다. verificationHeader

캐시 시간 초과 이후 클라이언트가 "if-modified-since:"라는 헤더를 추가하게 되면 포함된 헤더의 정보를 읽은 서버가 데이터 수정일의 동일 여부를 통해 검증합니다. modified

  • 서버는 데이터 변경이 되지 않았을 경우 "304 Not Modified"를 통해 알려주면서 동일한 데이터를 응답합니다. 이 때, HTTP Body는 존재하지 않습니다.(수정되지 않았기 때문에)
  • 따라서 데이터와 네트워크 부하의 최소화만을 허용합니다. modified2
  • 검증 여부를 참조하여 브라우저는 캐시를 다시 설정하고 브라우저 캐시 저장소에서 데이터를 조회하여 재사용합니다. reuseCache

검증 헤더(Last-Modified)와 조건부 요청(if-modified-since)은 같이 사용됩니다.

  • 캐시 유효 시간이 초과해도 서버의 데이터가 갱신되지 않으면 304 Not Modified + 헤더 메타 정보만 응답합니다. (BODY 미포함)
  • 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신합니다.
  • 클라이언트는 캐시에 저장되어 있는 데이터를 재활용할 수 있습니다.
  • 네트워크 다운로드가 발생하긴 하지만, 용량이 적은 헤더 정보만 다운로드 하기 때문에 굉장히 실용적인 해결책이라고 할 수 있습니다.
  • 웹 브라우저들은 대부분 이 메커니즘을 실행하고 있다고 합니다.

검증 헤더와 조건부 요청2

검증 헤더와 조건부 요청에 대해 자세히 알아보겠습니다.

  • 검증 헤더
    • 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터입니다.
    • 크게 2가지가 있습니다. (Last-Modified, ETag)
  • 조건부 요청 헤더
    • 검증 헤더로 조건에 따른 분기를 서버에 요청합니다.
    • If-Modified-Since: Last-Modified 사용
    • If-None-Match: ETag 사용
    • 조건이 만족하면 200 OK,
    • 조건이 만족하지 않으면 304 Not Modified입니다.

예시)

  • If-Modified-Since : 이후에 데이터가 수정되었으면?
  • 데이터 미변경 예시
    • 캐시: 2020년 11월 10일 10:00:00 vs 서버: 2020년 11월 10일 10:00:00
    • 304 Not Modified, 헤더 데이터만 전송(BODY 미포함)
    • 전송 용량 0.1M (헤더 0.1M, 바디 1.0M)
  • 데이터 변경 예시
    • 캐시: 2020년 11월 10일 10:00:00 vs 서버: 2020년 11월 10일 11:00:00
    • 200 OK, 모든 데이터 전송(BODY 포함)
    • 전송 용량 1.1M (헤더 0.1M, 바디 1.0M)

Last-Modified, If-Modified-Since 단점

  • 1초 미만(0.x초) 단위로 캐시 조정이 불가능합니다.
  • 날짜 기반의 정해진 로직을 사용합니다.
  • 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 데이터 결과가 똑같은 경우
    • 인식 오류로 전체 데이터를 다시 다운로드하는 단점이 있습니다.
  • 서버에서 별도의 캐시 로직을 관리하고 싶은 경우, 서버에서 완전히 컨트롤할 수 있도록 ETag, If-None-Match을 사용합니다.
  • 예) 스페이스나 주석처럼 크게 영향이 없는 변경에서 캐시를 유지하고 싶은 경우

검증 헤더와 조건부 요청 ETag(Entity Tag)

  • 캐시용 데이터에 날짜가 아닌 임의의 고유한 이름을 달아둘 수 있습니다.
    • 예) ETag: "v1.0", ETag: "a2jiodwjekjl3"
  • 데이터가 변경되면 이 이름를 바꾸어서(Hash를 다시 생성해서) 변경합니다.
    • 예) ETag: "aaaaa" -> ETag: "bbbbb"
  • 진짜 단순하게 ETag만 보내서 같으면 유지, 다르면 다시 받습니다. Etag1 Etag2

ETag, If-None-Match

  • 진짜 단순하게 ETag만 서버에 보내서 같으면 유지, 다르면 다시 받습니다.
  • __캐시 제어 로직을 서버에서 완전히 관리__합니다.
  • 클라이언트는 단순히 이 값을 서버에 제공(클라이언트는 캐시 메커니즘을 모름)합니다.
  • 예)
  • 서버는 배타 오픈 기간인 3일 동안 파일이 변경되어도 ETag를 동일하게 유지하고
  • 애플리케이션 배포 주기에 맞추어 ETag 모두 갱신할 수 있습니다.

캐시와 조건부 요청 헤더

캐시는 다음과 같은 __캐시 제어 헤더__가 있습니다.

  • Cache-Control: 캐시 제어
  • Pragma: 캐시 제어(하위 호환)
  • Expires: 캐시 유효 기간(하위 호환)
  1. Cache-Control: 캐시 제어(directives)
  • Cache-Control: max-age
  • 캐시 유효 시간을 뜻하며, 초 단위를 사용합니다.
  • Cache-Control: no-cache
  • 데이터는 캐시해도 되지만, 항상 원(origin) 서버에 반드시 검증하고 사용해야 합니다.
  • Cache-Control: no-store
  • 데이터에 민감한 정보가 있으므로 저장하면 안된다는 의미입니다. (메모리에서 사용하고 최대한 빨리 삭제)
  1. Pragma: 캐시 제어(하위 호환)
  • Pragma: no-cache
  • HTTP 1.0 하위 호환
  • 지금은 거의 사용하지 않고 하위 호환 때문에 필요하면 사용한다고 합니다.
  1. Expires: 캐시 만료일 지정(하위 호환) 캐시 유효시간이 아닌 만료일 지정을 통해 제어합니다. 초 단위가 훨씬 유형하기 때문에 Expires는 하위 호환입니다.
  • 예) expires: Mon, 01 Jan 1990 00:00:00 GMT
  • 캐시 만료일을 정확한 날짜로 지정하는 기능입니다.
  • HTTP 1.0 부터 사용해왔습니다.
  • 지금은 더 유연한 Cache-Control: max-age 권장합니다.
  • Cache-Control: max-age와 함께 사용하면 Expires는 무시됩니다.

검증 헤더와 조건부 요청 헤더

  • 검증 헤더 (Validator)
    • ETag: "v1.0", ETag: "asid93jkrh2l"
    • Last-Modified: Thu, 04 Jun 2020 07:19:24 GMT
  • 조건부 요청 헤더
    • If-Match, If-None-Match: ETag 값 사용
    • If-Modified-Since, If-Unmodified-Since: Last-Modified 값 사용

프록시 캐시

한국에 있는 다수의 클라이언트가 미국에 있는 origin 서버에 접근하면 각 클라이언트들은 다운로드 시간이 지연되어 느린 사용자 경험이 발생할 수 있습니다.

onlyOrigin

프록시 캐시 라는 서버를 한국 어딘가에 도입해서 우선적으로 요청을 처리하도록 지원하면 미국의 원 서버가 아니라 한국 웹 브라우저에서 프록시 캐시 서버를 먼저 접근하게 되고 가까이 있는만큼 빠른 응답이 가능해집니다. 예) 유튜브 한국 컨텐츠와 해외 컨텐츠는 로딩 속도가 크게 차이난다고 합니다. proxy

  • private 캐시 : 웹 브라우저 로컬에 저장되는 캐시를 의미합니다.
  • public 캐시 : 중간에서 공용 목적으로 저장되는 캐시를 의미합니다. (보통 두번째 브라우저부터 빠르게 조회할 수 있도록 하거나 또는 원 서버에서 캐시에 데이터를 넘겨주는 경우도 있다고 합니다.) private&public

Cache-Control :캐시 지시어(directives) - 기타

  • Cache-Control: public
    • 응답이 public 캐시에 저장되어도 된다는 의미입니다.
  • Cache-Control: private
    • 응답이 해당 사용자만을 위한 것이라는 의미이므로, private 캐시에 저장해야 합니다.(기본값)
  • Cache-Control: s-maxage
    • 프록시 캐시에만 적용되는 max-age
  • Age: 60 (HTTP 헤더)
    • 오리진 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)을 의미합니다.

캐시 무효화

Cache-Control : 확실한 캐시 무효화 응답

  • Q: Cache를 적용 안하면 Cache가 안 되는거 아닌가요?

  • A: Cache를 적용 안해도 웹 브라우저들이 Get요청 등으로 임의로 캐싱 하기도 한다고 합니다. 그래서 절대 캐시가 되면 안 되는 상황에서 무효화 응답을 반드시 포함시켜야 합니다.

    • 예) 현재 사용자 통장 잔고(갱신될 수 있기 때문에 캐싱하면 안 됩니다.)
  • Cache-Control: no-cache, no-store, must-revalidate

  • Pragma: no-cache (과거 브라우저)

  • HTTP 1.0 하위 호환

Cache-Control

캐시 지시어(directives) - 확실한 캐시 무효화

  • Cache-Control: no-cache
    • 데이터는 캐시해도 되지만, 항상 __원 서버에 검증__하고 사용(이름에 주의!)해야 합니다.
  • __Cache-Control: no-store
    • 데이터에 민감한 정보가 있으므로 저장하면 안됩니다. (메모리에서 사용하고 최대한 빨리 삭제)
  • Cache-Control: must-revalidate
    • 캐시 만료후 최초 조회시 __원 서버에 검증__해야합니다.
    • 원 서버 접근 실패시 반드시 오류가 발생해야함 - 504(Gateway Timeout)
    • must-revalidate는 캐시 유효 시간이라면 캐시를 사용함
  • Pragma: no-cache
    • HTTP 1.0 하위 호환 (Cache-Control같은 무효화를 모르는 스펙에서 지원)

no-cache로 항상 원 서버에 검증하고 사용하면 될텐데, 왜 must-revalidate를 분류해야 할까요?

no-cache vs must-revalidate

no-cache 사용시 항상 원 서버에서 검증해야하기 때문에 프록시 캐시를 거쳐서 검증후 해당 응답합니다. no-cache

그런데 만약, 순간적으로 프록시 캐시 서버와 원 서버와의 네트워크가 단절된다면?

  • 원 서버에 접근이 불가하게 됩니다. 이럴 경우 프록시 서버는 장애 발생보다 오래된 데이터라도 보여주는 선택을 했다고 합니다.

connectionError

네트워크 단절로 원 서버 접근 불가한 상황에서 must-revalidate는 no-cache와 달리, 504 Gateway Timeout 에러가 발생하는 것을 보여주도록 스펙에 설명되어있습니다.

mustRevalidate

  • 예를 들어 통장 잔고와 같은 민감하고 중요한 데이터는 프록시 캐시에 있는 과거 데이터보다 오류가 발생하는 것이 사용자에게 안정적이라고 합니다.

이러한 차이 때문에 서버 측에서 HTTP 응답 코드를 만들 때에는 확실하게 캐시 무효화 응답을 위해선 no-cache와 must-revalidate를 구분지어야 한다고 합니다.

Cache-Control : 확실한 캐시 무효화 응답

  • Cache-Control: no-cache, no-store, must-revalidate
  • Pragma: no-cache
  • HTTP 1.0 하위 호환

과거부터 스펙이 누적되다보니 조금 복잡할 수 있기 때문에 원리에 대해 알아보는 시간을 가졌습니다.

마무리

추가로 필요한 내용은 지금까지 학습한 내용을 기반으로 필요시 검색을 통한 습득할 수 있습니다. (단, 정확하지 않은 자료가 많으므로 항상 의심해야 합니다.)

  • HTTP에 대해 더 깊이있게 학습
  1. HTTP 스펙
  1. HTTP 완벽가이드
  • 백엔드 개발자가 사용하는 웹 프레임워크나 기술들은 모두 HTTP 기반으로 구현되어 있습니다.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published