✅ 1. 인터넷 네트워크
인터넷 통신
- 인터넷에서 컴퓨터는 어떻게 통신할까?
- 예를 들어 클라이언트와 서버가 케이블로 연결 되어 있다고 생각해 보자 그러면 그냥 클라이언트는 케이블로 요청을 하면 되고, 서버는 케이블로 응답하면 된다.
- 하지만 이렇게 연결이 되어 있지 않으면 두 컴퓨터 사이에는 인터넷을 통해 서로 통신한다.
- 그런데 인터넷이 라는 것이 단순 하지가 않아서 데이터가 전달 되는 과정에서 광케이블을 통할 수 도 있고 인공위성을 통할 수도 있다. 즉, 수많은 중간 노드 라고 하는 서버들을 거쳐서 통신을 하게 된다.
- 이렇게 복잡한 상황에서 어떻게 안전하게 데이터를 주고 받을 수 있을까?
- 이걸 이해 하려면 먼저 인터넷 프로토콜 이라고 하는 IP프로토콜을 알아야 한다.
IP 프로토콜
개념
- 클라이언트도 IP주소를 갖고, 서버도 IP주소를 갖는다.
- IP주소로 데이터를 요청하고, 보낼 수 있도록 정해 놓은 규칙이 IP프로토콜이다.
- 패킷(Packet)이라는 통신 단위로 데이터를 전달 하게 된다.
- IP패킷 안에는 클라이언트의 IP주소, 서버의 IP주소, 데이터가 담긴다.
- 데이터를 주고 받는 과정에서 만나는 수많은 노드들도 모두 IP프로토콜을 따르고 있기에 데이터를 도착하고자 하는 곳의 IP주소로 보낼 수 있는 것이다.
한계
-
패킷을 받을 대상이 없거나( ex -> 서버IP를 갖는 컴퓨터가 꺼져 있는 경우) 서비스 불능 상태여도 패킷은 그냥 전송이 된다. 데이터를 요청한 사람은 이 사실을 확인할 수 없다.
-
중간에 패킷이 사라질 수도 있고, 여러개의 패킷을 보낼 때 순서대로 도착한다는 보장이 없다.
-
또한 예를 들어 PC에서 게임도 하고 음악도 들으면 이 두가지 경우의 IP는 같다. 따라서 같은 IP를 사용하는 서버로 통신하는 애플리케이션이 둘 이상인 경우 어디로 보내야 할지 알 수 없다.
-
이러한 문제들을 해결해 주는 것이 TCP와 UDP다.
인터넷 프로토콜 4계층
-
TCP / UDP를 알아 보기 전에 인터넷 프로토콜 계층에 대해 알아 보자.
-
먼저 인터넷 프로토콜 스택의 4계층을 살펴 보자
- 애플리케이션 계층 (HTTP / FTP)
- 전송 계층 ( TCP / UDP )
- 인터넷 계층 ( IP )
- 네트워크 인터페이스 계층 ( LAN 장비 )
-
애플리케이션 계층에서 시작해서 각각의 계층을 지나 데이터를 외부로 전송하게 된다.
-
여기서 IP프로토콜의 문제점을 해결 하기 위해 IP위에 TCP라는 것을 추가한 것을 TCP 프로토콜 이라고 한다.
-
예를 들어 채팅 프로그램을 통해 미국에 있는 친구 한테 Hello라는 메세지를 보낸 다고 가정해 보자.
-
여기서 사용되는 것이 Socket 라이브러리 인데, Soket에 Hello라는 메세지를 담아서 os 계층에 전달 한다.
-
그러면 os 계층에서 전달 받은 Socket을 TCP 정보로 감싸서 IP계층 으로 넘기면 여기서 한번 더 IP 정보로 감싸서 최종적으로 IP 패킷이 만들어 진다.
-
마지막으로 IP 패킷이 네트워크 인터페이스를 통해 외부로 나갈 때 Ethernet Frame 이라는 것으로 한번 더 감싸서 나가게 된다.
-
박스 포장에 비유를 해보면 클라이언트가 HTTP를 통해 요청을 하면(첫번째 박스) TCP를 거쳐서(첫번째 박스를 또 다른 박스에 담음) 인터넷 망 속으로 보내지고 여기서 IP프로토콜을(또 박스에 담음) 통해 목적지에 도달 한다.
TCP / UDP
TCP 란?
- TCP는 Transmission Control Protocol의 약자로 직역하면 전송 제어 프로콜을 의미한다.
- 위에서 애플리케이션 계층으로 부터 전달 받은 Socket을 TCP 정보로 감싼다고 했는데 그 정보들은 무엇일까?
- 그 정보들은 출발지 Port, 도착지 Port, 전송 제어, 순서, 검증 정보 등이다.
- 위의 정보들을 바탕으로 TCP를 사용하여 연결 지향, 데이터 전달 보증, 순서 보증 등을 통해 기존의 IP 프로토콜의 문제점을 해결할 수 있다.
- 이처럼 TCP는 신뢰할 수 있는 프로토콜이기 때문에 현재는 대부분 TCP를 사용하고 있다.
연결 지향
- TCP 3 way handshake라는 방법을 통해 연결을 확인 한다.
- 먼저 클라이언트는 서버로 SYN이라는 메세지를 보낸다.
- 그러면 연결이 되어 있다면 서버는 메세지를 받을 것이고, 응답으로 SYN + ACK 라는 메세지를 클라이언트로 보낸다.
- 메세지를 받은 클라이언트는 다시 서버로 ACK 라는 메세지를 보내고 이제 연결이 확인이 되었으니 데이터를 전송 한다.
- 요즘엔 최적화가 되어 마지막에 ACK를 보낼 때 데이터도 함께 전송한다.
- 즉, 연결 지향 이라는 것은 데이터를 전송하기 전에 먼저 클라이언트와 서버가 서로 연결이 되었는지 확인한 다음 데이터를 전송을 하는 것을 의미한다.
데이터 전달 보증
- TCP를 통해 데이터를 전송 하면 서버에서 데이터를 정상적으로 받았을 경우 응답을 해주게 된다.
- 이러한 응답을 통해 데이터가 전달이 된 건지 확인할 수 있다.
순서 보증
- 만약 서버에 도착한 패킷의 순서가 클라이언트가 보낸 데이터의 순서와 다르다면 어떻게 될까?
- 예를들어 클라이언트는 A, B, C 순서로 보냈는데, 서버에는 A, C, B 라고 도착을 했다면 서버는 클라언트에게 순서가 잘못된 두번째 패킷 부터 다시 보내라고 클라이언트에게 응답한다.
- 이러한 방법으로 순서를 보증할 수 있다.
- 이게 가능한 이유는 TCP로 Socket을 감쌀 때 전송 제어 정보, 순서 정보, 검증 정보 등이 포함 되어 있기 때문이다.
UDP란?
- UDP는 User Datagram Protocol의 약자로 직역하면 사용자 데이터 그램 프로토콜 이다.
- UDP는 TCP와 달리 기능이 거의 없다. 연결지향, 데이터 전달 보증, 순서 보증등의 역할을 전혀 하지 못한다.
- IP프로토콜과 거의 비슷 한데 여기에 Port 정보 만 추가 된 거라고 생각하면 된다.
PORT
-
예를 들어 하나의 PC에서 게임도 하고 있고, 음악도 듣고 있다고 가정해 보자.
-
이 경우 클라이언트 PC는 여러개의 서버랑 통신 해야 한다.
-
그러면 게임 서버 에서도 패킷이 올거고, 음악 서버에서도 패킷이 올 텐데 두 패킷이 하나의 IP로 오게 될 것이다.
-
그러면 각각의 패킷이 어떤 애플리케이션으로 온 것인지 구분할 필요가 있어지는데, 이때 사용되는 개념이 PORT라는 개념이다.
-
즉, IP는 큰 목적지를 찾는 것이고 그 안에서 돌아가는 애플리케이션을 구분하는 것이 PORT인 것이다.
-
TCP프로토콜에는 출발지 PORT와 목적지 PORT가 들어가 있기 때문에 하나의 IP에서 여러개의 애플리케이션이 돌아가도 서로 데이터를 주고 받을 수 있는 것이다.
DNS
-
DNS는 Domain Name System 의 약자다.
-
IP주소는 100.100.100.1 처럼 기억하기 어려운 형태를 가지고 있다.
-
따라서 DNS 서버에 각각의 IP주소에 도메인 이름을 지정해서 저장해 두면 클라이언트는 복잡한 IP주소로 요청 하는 것이 아니라 google.com과 같은 도메인 이름 으로 요청을 하면 DNS서버에서 IP로 변환하여 요청하게 된다.
✅ 2. URI와 웹 브라우저 요청 흐름
- URI란 Uniform Resource Identifier 의 약자로 해석해 보면 자원을 식별하는 통합된 방법 이라는 뜻이다.
- URI에 우리가 흔히 알고 있는 URL이라는 개념이 포함되어 있는 것
웹 브라우저 요청 흐름
https://www.google.com:443/search?q=hello&hl=ko 라고 요청을 했다고 생각하고 과정을 살펴 보자.
- 먼저 DNS서버로 들어가서 www.google.com:443의 ip주소와 포트 번호를 찾는다.
- 그리고 나서 웹 브라우저는 아래와 같이 Http 요청 메세지를 생성한다.
// 요청 메서드, 쿼리 정보, HTTP버전 정보, Host정보
GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com
- 웹 브라우저는 생성한 Http 메세지를 소켓라이브러리를 통해 os계층에 전달
- os계층 에서는 전달 받은 Http 메세지를 포함하는 TCP/IP 패킷 생성하여 인터넷 망으로 전송
- 해당 패킷을 받은 서버는 패킷안의 Http 메세지를 해석하여 Http응답 메세지 생성
// Http 버전 정보, 상태 코드
HTTP/1.1 200 OK
// 응답하는 데이터의 형식
Content-Type: text/html;charset=UTF-8
// 응답는 데이터의 길이
Content-Length: 3423
<html>
<body>...</body>
</html>
- 이렇게 생성한 Http응답 메시지를 똑같이 TCP/IP패킷에 감싸서 전송
- 브라우저가 데이터를 렌더링
✅ 3. HTTP의 기본
모든 것이 HTTP
-
HTTP는 Hyper Text Transfer Protocol의 약자다.
-
HyperText 문서를 통해서 html을 전송하는 프로토콜로 처음 사용이 되었지만 현재는 HTTP 메시지에 거의 모든 형태의 데이터를 전송할 수 있다.
-
HTML / TEXT / IMAGE / 음성 / 영상 / 파일 / JSON / XML 등등...
-
서버간에 데이터를 주고 받을 때도 대부분 HTTP를 사용한다.
HTTP의 주요 특징
- 클라이언트 서버 구조
- 클라이언트가 HTTP 메세지를 통해서 서버에 요청을 보내면 서버는 HTTP메시지로 응답을 할 것이고, 클라이언트는 응답을 기다렸다가 응답이 오면 결과를 열어서 렌더링 하는 구조다.
- 무상태 프로토콜 ( Stateless )
- HTTP는 무상태 프로토콜을 지향한다. 서버가 클라이언트의 상태를 보존하지 않는다는 것이다.
- 얼핏 생각하면 상태를 보존하는 것이 좋은게 아닌가? 라고 생각할 수 있지만 그렇지 않다.
- 왜냐하면 이전의 상태를 유지하고 있다면 추가적인 요청을 할 때 이전의 값을 전달하지 않고 추가적인 정보만 전달하게 된다. 그런데 만약 해당 요청을 받는 서버가 바뀌게 되면 이전의 상태를 모른 채 추가적인 정보만 받게 되어 일 처리를 할 수 없게 된다.
- 반면 상태를 보존하지 않는 다면 클라이언트는 매 요청 마다 모든 정보를 다 넘기게 된다. 따라서 요청을 받는 주체가 바뀌더라도 모든 정보를 받으므로 어떤 상황에서도 처리를 할 수 있게 되는 것이다.
- 이렇게 Stateless하게 유지하면 갑자기 클라이언트 요청이 증가해도 서버를 대거 투입할 수 있고, 응답 서버도 쉽게 바꿀 수 있어 무한한 서버 증설이 가능하다.
- 비 연결성 ( connectionsless )
- HTTP는 기본적으로 클라이언트와 서버가 연결을 유지하고 있지 않다.
- 그 이유는 서버 자원을 매우 효율적으로 사용하기 위해서 이다.
- 만약 클라이언트가 10000명이고 서버는 1개 라고 가정한다면 10000개를 계속 연결하고 있으면 서버에 부하가 올 수 있으니, 필요한 요청이 있을 때만 연결을 하고 응답이 완료가 되면 연결을 끊어버리는 것이다.
- 하지만 비 연결성도 한계가 있다. 클라이언트가 서버로 부터 받은 파일은 html 뿐만이 아니라 js파일,css파일도 있을텐데 파일 하나하나 받을 때마다 연결하고 끊고, 연결하고 끊고 하다 보면 계속해서 TCP/IP 연결을 새로 맺어야 하고 그렇게 하면 3way handshake가 계속 반복 되어 불필요한 시간이 계속 추가 된다.
- 이걸 해결하기 위해 HTTP는 지속 연결 (Persistent Connections)을 사용한다.
- Keep Alive라고도 부르는데 이걸 통해 첫번째 파일의 응답이 끝나도 연결을 끊지 않고 유지하고 있고, 모든 파일이 다 받아 지면 그 때 연결은 끊는다.
- HTTP 메세지
- HTTP 메세지는 크게 4개의 카테고리를 가지고 있다.
- start-line / header / emptyline / message body
- start-line에는 요청 메세지의 경워 요청 메서드(get,post,put...) , 요청 경로(path), HTTP 버전이 들어 가고 응답 메세지의 경우 HTTP 버전, 응답 코드가 들어 간다.
- header의 정보는 요청 메세지의 경우 서버의 Host 정보가 들어가고, 응답 메세지에는 message body의 Content-Type과 Content-Length가 들어간다.
- messagebody의 경우 요청할 때 전달할 내용이 없다면 공백으로 해도 상관 없고, 응답 할 때는 여기에 html같은 것들이 담겨서 온다.
✅ 4. HTTP 메서드
HTTP API 만들기
요구사항
-
회원 정보 관리 API를 만들어라
- 회원 목록 조회 / 회원 조회 / 회원 등록 / 회원 수정 / 회원 삭제
-
위와 같은 요구 사항을 받고 API URI를 설계 하라고 하면 어떻게 해야 할까? 일단은 아래와 같이 생각해 볼 수 있다.
- 회원 목록 조회 / read-member-list
- 회원 조회 / read-member-by-id
- 회원 등록 / create-member
- 회원 수정 / update-member
- 회원 삭제 / delete-member
-
위의 방법은 좋은 URI 설계가 아니다.
-
URI에서 가장 중요한 것은 "리소스 식별" 이다.
-
리소느는 동사와 같은 어떤 행위가 아니라 명사이다.즉, 위의 경우에서는 "회원"이 리소스인 것이다.
-
"회원"이라는 리소스만 식별 할 수 있게 회원 리소스를 아래와 같이 매핑 하면 된다.
- 회원 목록 조회 / members
- 회원 조회 / members/{id}
- 회원 등록 / members/{id}
- 회원 수정 / members/{id}
- 회원 삭제 / members/{id}
-
URI는 계층 구조로 설계해야 하니까 members다음에 id가 들어가는 것까지 하고 나면 문제가 생긴다. 회원 조회,등록,수정,삭제를 어떻게 구분해야 하지??
-
즉, 리소스는 잘 식별을 했는데 행위는 어떻게 해야 하는 걸까?
-
이때 사용되는 것이 HTTP 메서드다.
-
정리하면 URI는 리소스만 식별 하고, 리소스를 대상으로 하는 행위는 HTTP 메서드로 구분한다.
HTTP 메서드 ( GET / POST / PUT / PATCH / DELETE )
- HTTP 메서드는 클라이언트가 서버에 뭔가 요청할 때 기대하는 행동이라고 이해하면 된다.
GET 메서드
- GET 메서드는 리소스를 조회할 때 사용한다.
- 서버에 전달하고 싶은 데이터는 query( 쿼리 파라미터, 쿼리 스트링 )를 통해서 전달 한다.
// 🥊 조회 하고 싶은 데이터에 관한 정보를 쿼리 파라미터를 통해 전달
GET /search?q=hello HTTP/1.1
Host: www.google.com
- message body를 통해서 데이터를 전달 할 수도 있지만 지원하지 않는 곳이 많아 권장 되지 않는다.
- 예를 들어 id가 100인 회원의 정보를 GET 요청을 했다면 아래와 같은 HTTP요청 메세지가 작성 될 것이다.
GET /members/100 HTTP/1.1
Host: localhost:8080
// 응답 데이터가 Json이라면 Content-Type이 아래와 같이 작성 될 것이다.
// 만약 응답 데이터가 HTML이면 Content-Type이 text/html; charset=utf-8 일 것.
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 34
{
"username" : hdh
"age" : "30"
}
POST 메서드
- POST 메서드는 요청 데이터를 처리할 때, 주로 데이터를 등록할 때 사용한다. POST의 경우 무조건 데이터를 담아서 서버에 전달해야 하는 것이다.
- message body를 통해 서버로 요청 데이터를 전달한다.POST의 경우 반드시 message body에 데이터를 담아 보내기 때문에 당연히 Content-Type을 입력해야 한다.
// 서버로 Post 요청 HTTP 메세지
// POST의 경우 특정 url로 요청이 오면 뭘 할 것인지 사전에 정해 놔야 한다.
// 아래의 경우 전달 받은 데이터를 저장하기로 약속했닫고 가정하자.
POST /members HTTP/1.1
Content-Type: application/json
{
"username" : "hdh"
"age" : "30"
}
- 이렇게 데이터를 전달 받은 서버는 요청 데이터를 처리하고, 주로 신규 리소스 등록 하고 프로세스 처리에 이용한다.
- 사전에 데이터를 저장하기로 약속 했으니 서버는 아래와 같이 응답할 것이다.
// 서버 내부에서 Post 요청시 데이터를 저장하기로 했으니 아래 아이디를 생성하여
// Location에 자원이 생성된 경로를 보내고, message body에 생성된 자원도 보내준다.
HTTP/1.1 201 created
Content-Type: application/json
Content-Length: 34
Location: /members/100
{
"username" : "hdh"
"age" : "30"
}
- POST의 경우 요청이 오면 요청 데이터를 어떻게 처리할 것인지 리소스 마다 따로 정해줘야 한다.
- 단순히 데이터를 등록하는 역할만 하는 것이 아니다.
PUT 메서드
- PUT 메서드는 리소스를 대체 하는 역할을 한다.
- 만약 전송한 데이터가 서버에 없다면 리소스를 생성하고, 있다면 기존의 데이터를 새로 전송한 데이터로 덮어 버린다.
- PUT을 사용한 HTTP 요청 메세지는 아래와 같다.
PUT /members/100 HTTP/1.1
Content-Type: application/json
{
"username" : "hdh"
"age" : "31"
}
- 그렇다면 POST와 PUT이 뭐가 다른 걸까?
- 위의 요청 메세지를 보면 이전의 POST의 경우 데이터가 저장 되고 응답으로
Location: /members/100 라는 리소스의 전체 경로를 받았는데 PUT의 경우 클라이언트가 이미 리소스 전체 경로를 알고 요청 메세지에 보낸다.
- 정리하면 PUT의 경우 클라이언트가 리소스의 전체 경로를 이미 알고 있다는 것이 POST와의 차이점이다.
- PUT을 사용할 때 가장 주의 해야 할 점이 있다.바로 PUT의 경우 기존 리소스를 "완전히" 대체 한다는 것이다.
- 위의 요청 메세지를 보면 /members/100에 있는 데이터를 message body의 데이터로 대체를 한다는 것인데 만약 기존에 /members/100에는 "height"이라는 필드가 추가적으로 더 있었다고 가정해 보자.
- 이 상황에서 PUT 요청을 하면 height가 null이 되는 것이 아니라 그냥 원래 없던 것처럼 "username"과 "age"만 있는 데이터로 완전히 대체가 된다.
- 이처럼 PUT은 데이터를 완전히 대체할 때 사용하는 것이지 수정하거나 업데이트 할 때 사용하는 것이 아니다.
- 데이터를 부분적으로 업데이트 하고 싶다면 PATCH 메서드를 사용해야 한다.
PATCH 메서드
- PATCH 메서드는 리소스를 부분 변경 하고 싶을 때 사용한다.
- 기존에 /members/100에 아래와 같이 데이터가 저장되어 있다고 가정해 보자.
{
"username" : "hdh"
"age" : "31"
}
- 이 상황에서 아래와 같이 PATCH 요청을 하면 어떻게 될까?
PATCH /members/100 HTTP/1.1
Content-Type: application/json
{
"age": 20
}
- 아래와 같이 부분적으로 데이터가 수정이 된다.
{
"username" : "hdh"
"age" : 20
}
- 간혹 PATCH 메서드를 받아 들이지 못하는 서버가 있을 수 있다. 이 경우 데이터를 부분적으로 업데이트 하고 싶다면 POST를 사용하면 된다.
DELETE 메서드
- 리소스를 제거 하고 싶다면 DELETE 메서드를 사용하면 된다.
- 아래와 같이 요청 메세지가 작성이 된다.
DELETE /members/100 HTTP/1.1
Host: localhost:8080
HTTP 메서드의 속성
- HTTP 메서드는 크게 3가지의 속성을 갖는다. 안전 / 멱등 / 캐시 가능 이 그것이다.
안전 ( Safe Methods )
- 안전의 의미는 호출해도 리소스가 변경되지 않는 것을 의미한다.
- POST / PUT / PATCH / DELETE 의 경우 전부 데이터가 바뀔 수 있으므로 안전하지 않고, GET의 경우는 데이터가 수정되지 않으므로 안전하다.
멱등 ( Idempotent )
- 멱등의 의미는 한 번 호출 하든 100번 호출 하든 결과가 똑같은 것을 의미한다.
- GET의 경우 한번 조회하든 100번 조회하든 항상 같은 결과가 조회되기 때문에 멱등하다.
- PUT의 경우 데이터를 완전히 대체 하는 것이기 때문에 한번 요청 하든 100번 요청 하든 항상 똑같은 데이터로 대체될 것이므로 멱등하다.
- DELETE의 경우도 한번 삭제 하든 100번 삭제하든 삭제된 결과는 항상 같이 때문에 멱등하다.
- POST의 경우는 멱등하지 않다. POST의 경우 단순히 데이터를 저장하는 역할만 하는 것이 아니라 여러가지 처리를 할 수 있기 때문이다. 예를 들어 결제를 처리한다면 여러번 요청 하면 중복 결제가 일어날 것이다. 따라서 멱등하지 않다.
- 이러한 멱등의 개념이 실무에서 어떻게 활용이 될까?
- 예를들어 자동 복구 메커니즘을 생각해 보자.
- 서버가 TIME OUT 등의 오류로 정상 응답을 하지 못하였을 때 클라이언트가 다시 똑같은 요청을 할 수 있냐 없냐의 기준으로 활용할 수 있다. POST를 제외한 나머지 메서드의 경우 몇 번을 요청하든 결과가 똑같기 때문에 다시 똑같은 요청을 하도록 해도 되지만 POST의 경우는 안되는 것이다.
캐시 가능 ( Cacheable )
- 해당 속성은 " 응답 결과로 받은 리소스를 캐시해서 사용해도 되는가? " 에 관한 속성이다.
- GET / HEAD / POST / PATCH 모두 캐시가 가능하다.
- 하지만 POST / PATCH는 본문 내용까지 캐시 키로 고려해야 하는데 구현이 쉽지 않다.
- 따라서 실무에서는 GET 정도만 캐시로 사용한다.
✅ 5. HTTP 메서드 활용
클라이언트에서 서버로 데이터 전송 하는 방법
1. 쿼리 파라미터를 통한 데이터 전송
2. 메세지 바디를 통한 데이터 전송
- POST / PUT / PATCH 등의 메서드와 사용
클라이언트에서 서버로 데이터를 전송하는 4가지 상황
1. 정적 데이터 조회
- 이미지나 정적 텍스트를 조회하는 경우다.
- 이 경우 주로 GET 메서드가 사용이 되고, 일반적으로 쿼리 파라미터 없이 리소스 경로만으로 조회가 가능하다. 요청 메세지는 아래와 같다.
// 서버에 stat.jpg파일을 요청 한 것
GET /static/star.jpg HTTP/1.1
Host: localhost:8080
HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 34012
이미지 정보......(ex -> asfwerhwqkxcbvgierjkhwqekhsdkjfhkj )
2. 동적 데이터 조회
- 동적으로 데이터를 전달 할 때는 서버로 데이터를 전달해야 한다.
- 이 경우 쿼리 파라미터로 데이터를 전달한다.
- 요청 메세지는 아래와 같다.
// 구글 서버에 hello를 검색한 결과를 한국어로 데이터를 요청 한것
// ?부터 ko까지가 쿼리 파라미터
GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com
- 위와 같은 요청 메세지를 받은 서버는 쿼리 파라미터의 key 와 value를 꺼내서 관련 데이터를 찾아서 응답한다.
- HTML Form 태그에 입력한 데이터를 서버로 POST 메서드를 통해 보낼 수 있다.
// "/save"라는 경로로 username과 age 데이터를 post 요청하는 Form
<form action="/save" method="post">
<input type="text" name="username"/>
<input type="text" name="age"/>
<button type="submit">전송</button>
</form>
- submit이 되면 웹 브라우저가 아래와 같은 HTTP 메세지 생성
// message body에 쿼리 파라미터와 거의 유사한 형식으로 데이터 전달
POST /save HTTP/1.1
Host: localhost:8080
Content-Type:application/x-www-form-urlencoded
username=hdh&age=30
- HTML form을 통한 데이터 전송을 할 때 GET 메서드도 사용할 수 있다.
- 🔥 GET을 통해 데이터를 생성하는 것이 아니라 Form 데이터를 파라미터로 넘겨 데이터를 조회할 때 사용하는 것!
<form action="/members" method="get">
<input type="text" name="username"/>
<input type="text" name="age"/>
<button type="submit">전송</button>
</form>
- GET 메서드의 경우 message body를 사용하지 않기 때문에 아래와 같이 HTTP 메세지가 생성이 된다.
GET /members?username=hdh&age=30 HTTP/1.1
Host: localhost:8080
- HTML Form을 통해 파일을 전송할 수 도 있다. 이 경우 enctype으로 multipart/form-data를 입력해야 한다.
<form action="/save" method="post" enctype="multipart/form-data">
<input type="text" name="username"/>
<input type="text" name="age"/>
<input type="file" name"file1"/>
<button type="submit">전송</button>
</form>
- 위의 form이 submit이 되면 웹 브라우저는 아래와 같은 요청 메세지를 생성한다.
- boundary가 각각의 데이터를 분류해서 개별적으로 Content-Disposition을 만든다. 이렇게 여러가지 Content-Type을 가지는 데이터를 전송할 수 있다.
POST /save HTTP/1.1
Host:localhost:8080
Content-Type:multipart/form-data; boundary=----XXX
Content-Length: 10457
------XXX
Content-Disposition: form-data; name="username"
hdh
------XXX
Content-Disposition: form-data; name="age"
30
------XXX
Content-Disposition: form-data; name="file1"; filename="intro.png"
Content-Type: image/png
123llh2lhlkhlk56hlkh7lhlkhklh.......
------XXX--
4. HTTP API를 통한 데이터 전송
- HTTP API는 서버에서 서버로 통신을 할 때 많이 사용이 된다.
- 아이폰, 안드로이드와 같은 앱 클라이언트에서 서버로 바로 데이터를 전송 할 때도 사용이 되고, HTML Form 전송 대신 자바 스크립트를 활용한 AJAX 통신을 할 때도 사용이 된다.
- HTTP API를 통해 POST / PUT / PATCH 메서드를 활용할 때는 메세지 바디를 통해 데이터를 전송하고 GET의 경우 쿼리 파라미터로 데이터를 전송한다.
- Content-Type은 거의 대부분 application/json을 사용한다.
- 요청 메세지는 아래와 같다.
POST /members HTTP/1.1
Content-Type:application/json
{
"username="hdh"
"age"=30
}
HTTP API 설계 예시
✅ 6. HTTP 상태 코드
- 상태 코드란 클라이언트가 보낸 요청의 처리 상태를 알려주는 기능이다.
- 상태 코드는 100번대 부터 500번대 로 5가지로 나누어 진다.
1xx
- 100번대 상태 코드는 요청이 수신되어 처리중이라는 것인데 거의 사용 되지 않는다.
2xx
- 200번대 상태 코드는 요청에 관한 응답이 정상적으로 이루어 졌음을 의미한다.
200 OK
- 클라이언트의 요청을 성공적으로 처리 했음을 의미
201 Created
202 Accepted
- 요청이 접수가 되었으나 처리가 완료되지 않았음을 의미한다.
204 No Content
- 서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음을 의미
- 예를 들어 웹 문서 편집기로 내용을 수정하고 save버튼을 눌렀다면 그냥 저장이 되었는지 안 되었는지에 대한 것만 알고 싶지 굳이 어떻게 바뀌었는지에 관한 데이터까지 받아볼 필요가 없다.
3xx
- 300번대 상태 코드는 요청을 완료하려면 추가적인 행동이 필요함을 의미한다.
- 300번대 상태 코드에는 Redirection이라는 개념이 사용 된다.
- 만약 300번대 상태 코드를 가지는 응답 메세지에 Location 헤더가 있으면 Location 헤더의 경로로 자동으로 이동하는 것을 Redirection이라고 한다.
- Redirection은 영구, 일시, 특수 Redirection 이렇게 3가지로 구분 할 수 있다.
영구 Redirection
-
특정 리소스의 URI가 영구적으로 변경 되었을 때 사용
301 ( Moved Permanently )
- 해당 응답 코드는 영구 Redirection에 해당한다.
- 리소스의 URI가 영구적으로 이동 된 것
- 예를 들어 기존에 /event 경로 였던 것이 /new-event라는 경로로 바뀌었다면 해당 상태 코드와 함께 새롭게 바뀐 경로를 Location에 담아 아래와 같이 전달한다.
HTTP /1.1 301 Moved Permanently
Location : /new-event
- 웹 브라우저는 3xx 응답 결과에 Location이 있으면 자동으로 해당 경로로 이동한다.
( Redirect )
- 301의 경우 Redirect가 발생하면 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있다.
308 ( Permanent Redirect )
- 308의 경우 301과 기능은 똑같다.
- 301과의 차이는 Redirect가 발생해도 기존의 요청 메서드와 본문이 그대로 유지 된다는 것이다.
일시 Redirection
-
일시적 Redirection은 리소스의 URI가 일시적으로 변경되어 잠깐 이동시킬 때 사용한다.
-
일시 Redirection은 보통 PRG라는 패턴으로 사용이 된다.
PRG ( Post / Redirect / Get )
-
예를들어 클라이언트가 주문 버튼을 누르면 POST로 데이터가 전송이 된다고 생각해 보자.
-
만약 주문을 한 후 새로 고침을 하면 어떻게 될까?
-
브라우저는 또 다시 POST요청을 보내게 된다. 즉, 중복 주문이 되는 것이다.
-
이것을 막기 위해 POST로 데이터를 받으면, 주문 결과 화면을 GET메서드로 Redirect 시킨다.
-
그러면 새로 고침을 해도 주문 결과 화면을 GET 요청 하게 되므로 중복 주문을 막을 수 있다.
302 ( Found )
-
Redirect시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있다.
303 ( See Other )
-
302와 기능은 같다.
-
다른점은 Redirect시 요청 메서드가 GET으로 변경 된다는 것.
304 ( Not Modified )
- 캐시를 목적으로 사용한다.
- 클라이언트에게 서버의 리소스가 수정되지 않았음을 알려준다. 따라서 클라이언트는 로컬 PC에 저장된 캐시를 재사용 한다. 즉, 캐시로 Redirect한다.
- 304 응답은 응답에 메세지 바디를 포함 하면 안되는데 그 이유는 로컬 캐시를 사용해야 하기 때문이다.
- 따라서 조건부 GET / HEAD 요청시는 보통 304가 사용 된다.
307 ( Temporary Redirect )
- 302와 기능은 같다.
- 다른점은 Redirect시 요청 메서드와 본문을 유지한다는 것.
특수 Redirection
- 결과 대신 캐시를 사용
- 예를 들어 클라이언트가 서버에게 기존의 캐시된 데이터가 만료 된 것 같으니 확인해 달라고 생성일자와 같은 정보들을 전달한다.
- 이 경우 만료가 되지 않았으니 캐시에서 다시 조회하라고 서버가 응답을 보내주는 것이 특수 Redirection이다.
4xx
- 400번대 상태 코드는 클라이언트 오류, 잘못된 문법 등으로 서버가 요청을 수행할 수 없음을 의미한다.
- 해당 응답 코드는 클라이언트에게 오류의 원인이 있음을 의미한다.
- 따라서 클라이언트 코드의 수정이 있지 않으면 아무리 재시도를 해도 성공할 수 없다.
401 ( Unauthorized )
- 클라이언트가 해당 리소스에 대한 인증이 필요함을 의미한다.(쉽게 말하면 로그인이 필요하다는 의미)
- 이 경우 WWW-Authenticate 헤더와 함께 인증 방법을 설명해줘야 한다.
- 차모로 인증이란 본인이 누구인지 확인 하는 것을 의미하고, 인가는 권한이 부여되어 있음을 의미한다.
403 ( Forbidden )
- 서버가 요청을 이해했지만 승인을 거부한 경우를 의미한다.
- 주로 인증 자격 증명은 있지만(로그인은 했지만) 접근 권한이 불충분한 경우를 뜻한다.
- 예를 들면 일반 사용자가 로그인하여 어드민 등급의 리소스에 접근하는 경우를 들 수 있다.
404 ( Not Found )
- 요청 리소스가 서버에 없음을 의미한다.
- 또한 403처럼 클라이언트가 권한이 부족한 리소스에 접근할 때 해당 리소스를 숨기고 싶은 경우에 사용한다.
5xx ( Server Error )
- 500번대 상태 코드는 서버 오류로, 서버가 정상적인 요청을 처리하지 못하는 것을 의미한다.
- 서버에 문제가 있기 때문에 중간에 서버의 문제가 해결 되면 재시도한 클라이언트의 request가 성공할 수 있는 가능성이 있는 오류다.
500 ( Internal Server Error )
- 서버 문제로 오류가 발생했음을 의미한다.
- 오류의 원인이 애매하면 500 오류로 내면 된다.
503 ( Service Unavailable )
- 서버가 일시적인 과부하 또는 예정된 작업으로 잠시 요청을 처리할 수 없음을 의미한다.
- Retry-After 헤더 필드로 얼마뒤에 복구되는지 보낼 수도 있다.
✅ HTTP 헤더
- HTTP 헤더에는 HTTP 전송에 필요한 모든 부가정보가 들어간다.
- 예를 들면 메시지 바다의 내용, 메시지 바디의 크기, 압축, 인증, 요청 클라이언트, 서버 정보, 캐시 관리 정보 등이 있다.
표현
Content-Type
- Content-Type : 표현 데이터의 형식을 입력해 주는 곳, 예를 들어 어떤 리소스가 있는데 이걸 HTML로 표현할지, JSON으로 표현할지 지정하는 것이다.
//리소스가 html로 표현되어 있다는 의미
Content-Type: text/html; charset=utf-8
//리소스가 JSON으로 표현되어 있다는 의미
Content-Type: application/json
//리소스가 이미지임을 의미
Content-Type: image/png
Content-Encoding
- Content-Encoding: 표현 데이터의 압축 방식이 어떤 것인지 나타내는 곳.
- 표현 데이터를 압축하기 위해 인코딩을 하게 되는데, 이때 데이터를 전달하는 곳에서 압축 후 인코딩 헤더에 추가한다.
- 이후 데이터를 읽는 쪽에서 인코딩 헤더의 정보를 바탕으로 압축을 해제 한다.
//인코딩 방식으로 아래와 같은 것들이 있다.
- gzip
- deflate
- identity
Content-Language
- Content-Language: 표현 데이터의 자연 언어가 어떤 것인지 나타내는 곳
// 표현 데이터가 한국어로 되어 있음을 의미
Content-Language:ko
// 표현 데이터가 영어로 되어 있음을 의미
Content-Language: en
Content-Length
- Content-Length: 표현 데이터의 길이를 나타내는 곳.
- 길이는 바이트 단위로 표현 된다.
콘텐츠 협상
- 클라이언트가 선호하는 표현 요청을 헤더에 담아 보낼 수 있다.
Accept
- 클라이언트가 선호하는 미디어 타입을 전달할 수 있다.
Accept-Charset
- 클라이언트가 선호하는 문자 인코딩 방식을 전달할 수 있다.
Accept-Encoding
- 클라이언트가 선호하는 압축 인코딩 방식을 전달하 수 있다.
Accept-Language
- 클라이언트가 선호하는 자연 언어를 전달할 수 있다.
- 예를들어 다중 언어를 지원하는 해외 사이트에 접속한다고 가정해 보자
- 해당 사이트에 그냥 접속하면 기본으로 영어로 된 사이트가 열릴 것인데 만약 Accept에 선호하는 자연 언어를 ko로 전달을 하면 해당 요청을 받은 해외 사이트에서 영어가 아닌 한국어로 번역 된 사이트를 보여줄 것이다.
전송 방식
- 전송 방식은 단순하게 4개로 분리할 수 있다.
- 단순 전송, 압축 전송, 분할 전송, 범위 전송이 그것이다.
단순 전송
- 단순 전송은 말 그대로 그냥 그대로 전송 받는다는 의미다.
// 단순하게 전송 받을 경우 Content-Length가 길다.
HTTP/1.1 200 OK
Content-Type:text/html;charset=UTF-8
Content-Length:3423
<html>
<body></body>
</html>
압축 전송
- 압축 전송은 말 그대로 압축해서 전송 받는 걸 의미
// 압축 했기 때문에 인코딩 방식을 입력해 주고, Content-Length가
단순 전송 보다 줄어든 것을 볼 수 있다.
HTTP/1.1 200 OK
Content-Type:text/html;charset=UTF-8
Content-Encoding:gzip
Content-Length:521
<html>
<body></body>
</html>
분할 전송
- 분할 전송은 청크 단위로 분할 하여 전송 받는 것을 의미한다.
HTTP/1.1 200 OK
Content-Type:text/plain
Transfer-Encoding: chunked
// 여기서 숫자 5는 5바이트를 의미, 즉 5바이트씩 끊어서 보내는 것
5
Hello
5
World
// 마지막에 0이라고 표시하고 슬러시 아래 엔터를 쳐서 보내면 데이터를 다 보냈음을 의미
0
/r/n
- 분할 전송의 장점은 데이터가 처음 부터 끝까지 다 오지 않아도 오는 순서대로 화면에 나타낼 수 있는 것이다.
- 참고로 분할 전송의 경우 Content-Length가 예상이 안되기 때문에 입력하면 안된다.
범위 전송
- 범위 전송이라는 것은 예를 들어 이미지를 다운 받고 있는데 절반 정도 받다가 끊겼다면 이미지를 처음 부터 다시 요청하는 것이 아니라 끊긴 부분 부터 요청 하여 전송 받는 것을 말한다.
// 범위를 지정해서 요청한다.
GET /event
Range:bytes=1001~2000
// 해당 범위의 데이터를 응답한다.
HTTP/1.1 OK
Content-Type:text/plain
Content-Range: bytes 1001~2000 / 2000
asdfasdfasdfasdfasdfasfd
일반 정보
- 단순한 정보를 담고 있는 헤더 항목들이 있다.
From
- 유저 에이전트의 이메일 정보다.
- 검색 엔진 같은 곳에서 주로 사용되지만 일반적으로 잘 사용되지 않는다.
- 요청에서 사용된다.
Referer
- 현재 요청된 페이지의 이전 웹 페이지 주소다.
- 예를 들면 A페이지에서 B페이지로 이동하는 경우
Referer: A를 포함해서 요청한다.
- Referer를 사용해서 유입 경로를 분석할 수 있다.
- 요청에서 사용된다.
User-Agent
- 클라이언트의 애플리케이션 정보다.
- 예를들면 웹 브라우저 정보를 들 수 있다.
- 어떤 종류의 브라우저에서 장애가 발생하는지 파악할 수 있다.
- 요청에서 사용된다.
Server
- 요청을 처리하는 ORIGIN 서버의 소프트웨어 정보다.
- ORIGIN 서버란 HTTP요청을 보냈을 때 거쳐가는 프록시 서버들 말고 정말 나에게 응답을 해주는 마지막 끝에 있는 서버를 말한다.
- 응답에서 사용된다.
Date
- 메세지가 발생한 시간이다.
- 응답에서 사용된다.
특별한 정보
Host
- 요청한 호스트의 정보다. (도메인)
- 반드시 들어가야 하는 정보다.
- 왜냐하면 하나의 IP 주소에서 여러 도메인을 처리해야 할 때 도메인들을 구분해 주는 역할을 한다.
Location
- 리다이렉션할 페이지 정보가 들어간다.
- 3xx 응답의 결과에 Location 헤더가 있으면 웹 브라우저는 Location 위치로 자동 이동(Redirect) 한다.
- 201 (Created) 응답의 결과에도 Location 값이 있는데 이건 요청에 의해 생성된 리소의 URI다.
Allow
- 허용 가능한 HTTP 메서드가 들어간다.
- 405 (Method Not Allowed ) 에서 응답에 포함해야 한다.
Retry-After
-
유저 에이전트가 다음 요청을 하기까지 기다려야 하는 시간을 의미한다.
-
503 ( Service Unavailable ) 응답 코드에 들어가게 되는데, 서비스가 언제까지 불능인지 알려줄 수 있다.
-
언제까지인지 날짜가 들어간 시간으로 표시할 수도 있고, 초 단위로 표시할 수도 있다.
인증
Authorization
WWW-Authenticate
- 리소스 접근시 필요한 인증 방법을 정의한다.
- 401 ( Unauthorized ) 응답과 함께 사용된다.
쿠키
- HTTP는 무상태 프로토콜이다.
- 따라서 클라이언트와 서버가 요청과 응답을 주고 받으면 연결이 끊어지기 때문에 클라이언트가 다시 요청하면 이전 요청을 기억하지 못한다.
- 에를 들어 로그인의 경우 로그인을 한 다음 서버에 새로운 요청을 보내면 클라이언트는 로그인 된 상태의 페이지를 기대하지만 서버는 이전 요청을 기억하지 못하기 때문에 그냥 새로운 요청일 뿐이라 로그인 정보를 기억하지 못한다.
- 이때 사용되는 것이 쿠키다.
- 서버는 로그인 된 사용자의 정보를 받고, Set-Cookie를 통해 쿠키에 담아 응답한다.
- 클라이언트는 전달 받은 쿠키를 브라우저의 쿠키 저장소에 저장한다.
- 그러면 웹 브라우저는 서버에 요청을 할 때마다 쿠키를 함께 보낸다.
- 이렇게 로그인 상태를 유지한 채 새로운 요청을 할 수 있는 것.
캐시와 조건부 요청
- 클라이언트가 똑같은 요청을 계속해서 반복하면 어떻게 될까?
- 같은 요청에 대한 응답을 받기 위해 데이터가 변경되지 않았음에도 계속 네트워크를 통해 데이터를 다운로드 받아야 한다.
- 인터넷 네트워크는 매우 느리고 비싸기 때문에 이 경우 브라우저 로딩 속도가 저하 되면서 좋지 않은 사용자 경험을 제공할 수 있다.
- 이때 응답한 데이터를 브라우저의 캐시 저장소에 잠시 저장해 둘 수 있는데 이것이 캐시다.
cache-control
- 서버에서 응답할 때 cache-control정보를 헤더에 포함시킬 수 있는데 여기에 캐시가 유요한 시간이 들어간다.
- 사용법은 아래와 같다.
// max-age 60 이라는 의미는 60초 동안 응답이 유요하다는 의미
cache-control: max-age=60
-
위와 같이 해둔 상태에서 60초 내에 똑같은 요청을 한다면 서버에서 데이터를 다시 내려 받는 것이 아니라 캐시에서 꺼내온다.
-
이렇게 캐시를 사용하면 캐시 가능 시간동안 네트워크를 통한 다운로드를 하지 않아도 되어 네트워크 사용량을 줄일 수 있고, 브라우저 로딩 속도를 향상 시킬 수 있다.
-
만약 캐시된 상태로 캐시 유효 시간이 초과하면 브라우저는 서버를 통해 데이터를 다시 조회하고, 캐시를 갱신한다.
-
왜냐하면 그 사이에 서버의 데이터가 변경이 되었을 수도 있기 때문이다.
-
하지만 만약 변경이 되지 않았다면 불필요한 데이터 통신을 하게 되는 건데 이걸 해결할 수 있는 방법은 조건부 요청을 하는 것이다.
검증 헤더와 조건부 요청
- 만약 캐시 유효시간이 만료가 되었지만 서버의 데이터가 변하지 않았다면 다시 다운로드를 받아야 하지만 똑같은 데이터를 다시 받는 것은 비효율적이다.
- 그러면 그냥 기존의 캐시된 데이터를 재사용하면 될 것이다.
- 단 재사용하기 위해서는 현재 클라이언트에 캐시된 데이터와 유효 시간 만료 후 서버의 데이터가 변경 되지 않아 두 데이터가 같다는 사실을 확인할 수 있는 방법이 필요하다.
- 방법은 아래와 같다.
// 서버에서 요청에 대한 응답을 할 때 cache 유효 시간과
데이터의 최종 수정 날짜를 함께 보낸다.
// 이것이 검증 헤더다.
HTTP/1.1 200 OK
Content-Type:image/png
cache-control: max-age=60
Last-Modified: 2023-12-21 10:52:00
Content-Length: 34026
asdfasdfasdfasdfasfasfdasfdasfdasdfasfdasfdasfd
// 그러고 캐시 유효 시간이 지나면 브라우저는
데이터를 다시 요청하는 것이 아니라
먼저 기존에 전달 받은 최종 수정일을 서버에 보낸다.
// 이것이 조건부 요청이다.
GET/ star.jpg
if-modified-since: 2023-12-21 10:52:00
// 만약 데이터의 변경이 없어 클라이언트가 보낸 최종 수정일과
현재 서버의 최종 수정일이 같다면 서버는 아래와 같은 응답을
한다.
HTTP/1.1 304 Not Modified
Content-Type: image/png
cache-control: max-age = 60
Last-Modified : 2023-12-21 10:52:00
Content-Length: 34026
HTTP Body가 없다!
- 정리하자면 캐시 유효 시간이 초과해도 최종 수정 날짜를 통해 서버의 데이터가 갱신되지 않았음이 확인이 되면 서버는 바디를 제외한 헤더 정보만 응답 함으로써 네트워크 부하를 줄이고, 클라이언트는 기존에 캐시에 저장되어 있는 데이터를 재활용하게 되는 것이다.
캐시와 조건부 요청 헤더
Cache-Control
// 캐시 유효 시간, 초 단위
Cache-Control: max-age
// 데이터는 캐시해도 되지만, 항상 이 캐시를 쓰기 전에
Origin서버에 조건부 요청을 통해 데이터의 변화가 있는지
없는지 검증하고 사용하라는 의미
Cache-Control: no-cache
// 데이터에 민감한 저오가 있으므로 저장하면 안된다는 의미
메모리에서 사용하고 최대한 빨리 삭제하라는 뜻
Cache-Control:no-store
Pragma
- HTTP 1.0의 하위 호환이다.
- 거의 사용되지 않음
Expires
- 캐시 만료일을 정확한 날짜로 지정
- 지금은 Cache-Control:max-age가 권장된다.
- Cache-Control:max-age와 Expires가 함께 사용이 되면 Expires는 무시된다.
프록시 캐시
- 한국에 있는 클라이언트가 외국에 있는 서버에 요청을 하면 시간이 오래 걸릴 것이다.
- 따라서 그 중간에 프록시 캐시 서버라는 것이 있다.
- 한국에 있는 A가 a요청을 한번 하면 프록시 서버에 해당 데이터가 캐시가 된다.
- 그 다음 한국에 있는 B가 a요청을 하면 외국에 있는 서버에서 가져오는 것이 아니라 프록시 서버에서 가져 오게 된다.
- 이렇게 함으로써 네트워크 부하를 줄이고 빠른 사용자 경험을 제공할 수 있다.
- 여기서 프록시 서버에 캐시 하는 것을 public 캐시 라고 하고, 이전에 했던 것처럼 내 로컬 브라우저에 캐시 하는 것을 private 캐시 라고 한다.
- 사용법은 아래와 같고 서버에서 응답할 때 아래의 내용이 헤더에 들어간다.
// 응답이 public 캐시에 저장되어도 됨
Cache-Control:public
// 응답이 해당 사용자만을 위한 것이기 때문에 private 캐시에
저장해야 함( 기본 값 )
Cache-Control:private
// 프록시 캐시에서만 적용되는 max-age
Cache-Control:s-maxage
// Origin서버에서 응답 후 프록시 캐시 내에 머문 시간(초)
Age: 60
캐시 무효화
- 캐시를 적용하지 않으면 캐시가 안된다고 생각할 수 있지만 브라우저는 GET 요청의 경우 임의로 캐시를 하기도 한다.
- 따라서 캐시가 되지 않기를 원한다면 명확히 명시해줘야 한다.
- 아래 처럼 전부 입력해줘야 한다.
Cache-Control:no-cache, no-store, must-revalidate
Pragma:no-cache