김영한님의 "모든 개발자를 위한 HTTP 웹 기본 지식"을 보면서 정리한 내용입니다.
https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard
1. 인터넷 네트워크
PC와 PC 통신하기 위해서는 네트워크로 통신하는데 네트워크는 복잡한 노드로 구성되어 있다. 복잡한 노드로 구성된 네트워크에서 데이터를 전달하기 위해서는 각 계층별로 프로토콜(데이터 규약)이 존재한다.
- 애플리케이션 계층: HTTP / FTP
- 전송 계층: TCP / UDP
- 인터넷 계층: IP
- 네트워크 인터페이스 계층
a. IP(Internet Protocol)
IP 역할
- 네트워크로 전달되는 데이터는 목적지 주소를 알아야 데이터가 목적지를 향해 전달될 수 있다. 이 때 사용되는 프로토콜이 IP(Internet Protocol)이다.
- 패킷이라는 통신단위로 데이터를 전달하는데, 패킷에는 IP 정보를 담고있어 목적지 PC에 패킷을 성공적으로 보낼 수 있다.
IP 패킷 구성
- IP 패킷에는 출발지 IP, 목적지 IP 기타 정보와 전송데이터를 포함하고 있다.
IP 한계
- 비연결성: 목적지 PC와 연결 상태를 확인하지 않고 패킷을 전송한다.(전송 보장X)
- 비신뢰성: 패킷이 사라지거나, 패킷이 순서대로 도착하지 않을 수 있다.
- 프로그램 구분: 하나의 PC에서 애플리케이션이 두 개 이상이면 구분할 수가 없다.
b. TCP(Transmission Control Protocol) / UDP(User Datagram Protocol)
IP는 PC의 IP 주소만 담고 있기 때문에 프로그램을 구분할 수 없다. TCP / UDP에는 프로그램을 구분할 수 있도록 PORT 정보를 담고 있다.
TCP 세그먼트
- TCP 세그먼트는 출발지 PORT, 목적지 PORT, 전송제어, 순서, 검증 정보 등을 담고있다.
TCP 특징
- 연결 지향 - TCP 3 way handshake
- 데이터 전달 보증
- 순서 보장
- 신뢰성
UDP 특징
- IP 패킷에서 PORT, 체크섬 정도만 추가
- 연결 지향 X
- 데이터 전달 보증 X
- 순서 보장 X
- 단순하고 빠름.
C. DNS(Domain Name System)
- 쉽게 말해서 변경될 수 있고 외우기 어려운 IP 주소를 도메인 명으로 바꾸는 것이다.
- DNS 서버에 도메인을 등록하여 사용한다.
2. URI와 웹 브라우저 요청 흐름
a. URI / URL / URN
URI(Uniform Resource Identifier)
- 리소스 식별자
- URI가 포괄적인 개념이고 URL과 URN이 URI의 하위 개념
URL(Uniform Resource Locator)
- 리소스가 있는 위치
- URL에 맞는 프로토콜을 알아야 하고, 그와 동일한 프로토콜을 사용해야 한다.
URL 기본 구조
// 기본 문법
scheme://[userinfo@]host[:port][/path][?query][#fragment]
// 예시
https://www.google.com:443/search?q=hello&hl=ko
scheme host port path query
scheme
- 주로 프로토콜 사용
userinfo
- URL에 사용자정보를 포함해서 인증(거의 사용하지 않음)
host
- 호스트명(도메인 or ip 주소)
port
- 접속 포트
- 일반적으로 생략(생략시 http는 80, https는 433)
path
- 리소스 경로
- 계층적 구조
query
- key=value 형태
- ?로 시작, &로 쿼리 추가 가능
- 쿼리파라미터, 쿼리스트링으로도 불림.
fragment
- html 내부 북마크 등에 사용
- 서버에 전송하는 정보 아님
URN(Uniform Resource Name)
- 리소스 이름
- 리소스 이름만을 가지고 리소스를 찾는 방법은 보편화 되지 않아 잘 사용하지 않음.(대부분 URL 만 사용)
b. 웹 브라우저 요청 흐름
브라우저에서 아래의 URL로 요청을 했다고 가정해서 요청/응답 과정을 살펴보자.
https://www.google.com:443/search?q=hello&hl=ko
클라이언트(사용자)로부터 요청을 받은 구글 서버는 적절한 응답을 보내게 되는데 역순으로 진행된다고 생각하면 된다.
크게 4단계로 표현하긴 했는데 세부적인 과정은 생략되었다. OSI 7 계층에 대해서 공부하면 도움될 것이다.
3. HTTP 기본
a. HTTP로 전송 가능한 데이터
HTTP는 바이트로 표현할 수 있는 모든 데이터를 전송할 수 있다.
- HTML, TEXT
- 이미지, 음성, 영상, 파일
- JSON, XML
- 서버간에 통신도 HTTP 사용 가능
b. HTTP 역사
- HTTP/0.9: GET 메서드만 지원, HTTP 헤더X
- HTTP/1.0: 메서드, 헤더 추가
- HTTP/1.1: 현재 가장 많이 사용
- RFC2068 (1997) -> RFC2616 (1999) -> RFC7230~7235 (2014)
- 현재는 RFC7230~7235가 표준
- HTTP/2: 성능 개선
- HTTP/3 진행중: TCP 대신에 UDP 사용, 성능 개선
HTTP2, HTTP3은 성능 개선에 중점을 둔 것이기 때문에 HTTP1.1 스펙만 알아도 사용하는 데 큰 문제가 없음.
c. HTTP 특징
기본적으로 서버 - 클라이언트 구조(클라이언트가 request를 보내고 서버가 결과를 만들어서 response를 보냄)
1. 무상태 프로토콜(Stateless)
- 서버가 클라이언트의 상태를 보존하지 않음.
무상태 프로토콜 장점
- 클라이언트는 아무 서버에게나 요청을 보내면 된다. ⇒ 클라이언트의 요청이 증가하면 서버를 수평확장(스케일 아웃)할 수 있음.
무상태 프로토콜의 한계
- 클라이언트가 추가 데이터 전송
- 실제로는 클라이언트의 상태를 유지해야 하는 특수한 상황(로그인 상태)이 있다. 이럴 때는 브라우저의 쿠키와 서버 세션 등을 사용해서 추가로 데이터를 전송해서 상태를 확인할 수 있다.
2. 비연결성
- HTTP는 기본이 연결을 유지하지 않는다. ⇒ 서버의 자원을 효율적으로 사용
비연결성 한계
- TCP/IP 기반이기 때문에 3-way handshake 시간이 추가적으로 발생한다.
비연결성 한계 극복
- 현재는 HTTP 지속연결(Persistent Connections)로 문제를 해결하였다.
d. HTTP 메시지 구조
HTTP 메시지 기본 구조
- 시작라인
- header
- 공백라인
- message body
1. 시작라인
요청메시지 시작라인
구조: 메서드 요청대상 HTTP버전
- 메서드: GET, POST, PUT, DELETE
- 요청대상: 절대경로(”/”)[?쿼리파라미터]
- HTTP버전: HTTP/1.1, HTTP/2 …
응답메시지 시작라인
구조: HTTP버전 상태코드 이유문구
- HTTP버전: HTTP/1.1, HTTP/2 …
- 상태코드: 요청 결과를 나타냄
- 200: 성공
- 400: 클라이언트 요청 오류
- 500: 서버 내부 오류
- 이유문구: 사람이 이해할 수 있는 짧은 상태코드 설명
2. 헤더
구조: 헤더필드 = 필드명: 필드value
- HTTP 전송에 필요한 모든 부가정보
- 메시지 바디의 내용, 메시지 바디 크기, 압축, 인증, 요청 클라이언트 정보, 서버 애플리케이션 정보, 캐시 관리 정보 등
- 표준 헤더는 너무 많음(뒤에서 정리하겠음)
- 필요시 헤더를 임의로 추가해서 사용 가능
3. Message Body
- 실제 전송할 데이터
- HTML, 이미지, 영상, JSON 등 다양함.
4. HTTP 메서드
회원관리 시스템을 만들기 위해 API 설계를 한다고 가정하자.
API는 리소스를 식별하도록 설계하는 것이 좋은 방법이다.
API 설계 예시
- 회원 목록 조회 /members
- 회원 조회 /members/{id}
- 회원 등록 /members/
- 회원 수정 /members/{id}
- 회원 삭제 /members/{id}
회원은 리소스이지만, 조회, 등록, 수정, 삭제는 리소스가 아니다!
하지만, 회원조회, 회원수정, 회원삭제를 보면 리소스 정보만을 가지고 식별하도록 설계했지만 각 API에 대한 행위를 식별할 수 없다.
식별하기 위해서 HTTP 메서드를 사용한다.
먼저, HTTP 메서드가 포함된 API 설계 예시를 보자.
HTTP 메서드를 포함한 API
- 회원 목록 /members -> GET
- 회원 등록 /members -> POST
- 회원 조회 /members/{id} -> GET
- 회원 수정 /members/{id} -> PATCH, PUT, POST
- 회원 삭제 /members/{id} -> DELETE
왜 이렇게 설계했는지 HTTP 메서드에 대해 알아보자.
a. HTTP 메서드 종류
- GET: 리소스 조회
- 서버에 전달하고 싶은 데이터는 쿼리파라미터를 통해 전달
- 메시지 바디를 전달할 수 있긴 하지만, 지원하지 않는 곳이 많음
- POST: 요청 데이터 처리, 주로 신규 리소스 생성에 사용
- 메시지 바디를 통해 서버로 요청 데이터 전달
- 클라이언트는 등록된 리소스의 URI 모른다.
- 주로 전달된 데이터로 신규 리소스 등록, 프로세스 처리에 사용(회원가입, 주문, 게시판(글, 댓글) 작성
- PUT: 리소스를 대체, 해당 리소스가 없으면 생성
- 리소스가 없으면 생성, 있으면 대체(리소스를 덮어버림)
- 클라이언트가 리소스 위치를 알고 요청. (POST와 차이점)
- PATCH: 리소스 부분 변경
- DELETE: 리소스 삭제
- HEAD: GET과 동일하지만 메시지 부분을 제외하고, 상태 줄과 헤더만 반환
- OPTIONS: 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명(주로 CORS에서 사용)
- CONNECT: 대상 자원으로 식별되는 서버에 대한 터널을 설정
- TRACE: 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행
b. HTTP 메서드 속성
안전(Safe)
- 호출해도 리소스를 변경하지 않음.
- GET 같은 경우 단순 조회만 할 뿐 리소스를 변경하지 않음.
멱등(Idempotent)
- 몇 번을 호출하든 같은 결과가 나온다.
- 멱등 메서드
- GET: 몇 번을 조회하든 같은 결과가 조회
- PUT: 리소스에 대해 PUT 요청을 하면 항상 리소스를 대체한다.
- DELETE: 같은 요청을 여러번 해도 결과는 같다.
- POST는 멱등이 아니다.(두 번 호출하면 같은 결제가 중복된다.)
- 멱등은 자동복구 메커니즘에 활용(“서버가 정상 응답을 못주었을 때, 클라이언트가 같은 요청을 다시 해도 되는가?”) 판단 근거로 활용
- 외부 요인으로 중간에 리소스가 변경되는 것까지 고려하지 않는다.
캐시가능(Casheable)
- 응답 결과 리소스를 캐시해서 사용해도 되는가?
- GET, HEAD, POST, PATCH 캐시가능(실제로는 GET, HEAD만 캐시로 사용)
c. HTTP 메서드 활용
클라이언트가 서버로 데이터를 전송할 때 전달방식
- 쿼리 파라미터를 통한 데이터 전송
- GET
- 주로 정렬 필터(검색어)
- 메시지 바디를 통한 데이터 전송
- POST, PUT, PATCH
- 회원 가입, 상품 주문, 리소스 등록, 리소스 변경
클라이언트가 서버로 데이터 전송하는 4가지 상황
- 정적 데이터 조회
- 이미지, 정적 텍스트 문서
- 동적 데이터 조회
- 주로 검색, 게시판 목록에서 정렬 필터(검색어)
- HTML FORM을 통한 데이터 전송(html form 태그)
- 회원가입, 상품 주문, 데이터 변경
- GET, POST만 지원
- HTTP API를 통한 데이터 전송
- 회원가입, 상품 주문, 데이터 변경
- 서버 to 서버, 앱 클라이언트, 웹 클라이언트(Ajax)
참고하면 좋은 URI 설계 개념
문서(document)
- 단일 개념(파일 하나, 객체 인스턴스, 데이터베이스 row)
- 예) /members/100, /files/star.jpg
컬렉션(collection)
- 서버가 관리하는 리소스 디렉터리
- 서버가 리소스의 URI를 생성하고 관리
- 예) /members • 스토어(store)
클라이언트가 관리하는 자원 저장소
- 클라이언트가 리소스의 URI를 알고 관리
- 예) /files
컨트롤러(controller), 컨트롤 URI
- 문서, 컬렉션, 스토어로 해결하기 어려운 추가 프로세스 실행
- 동사를 직접 사용 • 예) /members/{id}/delete
5. HTTP 상태코드
클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능
a. 상태코드 - 1xx(informational)
- 요청이 수신되어 처리중(거의 사용하지 않음)
b. 상태코드 - 2xx(Successful)
- 요청 정상 처리
- 200 OK: 요청 성공
- 201 Created: 요청 성공해서 새로운 리소스가 생성됨
- 202 Accepted: 요청이 접수되었으나 처리가 완료되지 않았음(배치 처리 같은 곳에서 사용)
- 204 No Content: 서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음.
c. 상태코드 - 3xx(Redirection)
- 요청을 완료하려면 추가 행동이 필요
- 웹 브라우저는 3xx 응답의 결과에 Location 헤더가 있으면, Location 위치로 자동 이동(리다이렉트)
- 영구적 리다이렉션
- 특정 리소스의 URI가영구적으로 이동 (/members → /users)
- 301 Moved Permanently
- 리다이렉트시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있음
- 308 Permanent Redirect
- 301과 기능은 같음. 리다이렉트시 요청 메서드와 본문 유지(처음 POST를 보내면 리다이렉트도 유지)
- 일시적 리다이렉션
- 일시적인 변경(주문 완료 후 주문 내역으로 이동)
- PRG(Post/Redirect/Get)
- 302 Found
- 리다이텍스시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있음.
- 307 Temporary Redirect
- 302와 기능은 같음.(리다이렉트시 요청 메서드와 본문 유지)
- 303 See Other
- 302와 기능은 같음(리다이렉트시 요청 메서드가 GET으로 변경)
- 특수 리다이렉션
- 결과 대신 캐시를 사용
- 300 Multiple Choices: 잘 사용하지 않음.
- 304 Not Modified
- 캐시를 목적으로 사용, 클라이언트에게 리소스가 수정되지 않았음을 알려준다. 따라서 클라이언트는 로컬 PC에 저장된 캐시를 재사용.(캐시로 리다이렉트)
- 응답에 메시지 바디를 포함X(캐시를 사용해야 하기 때문)
d. 상태코드 - 4xx(Client Error)
- 클라이언트 오류
- 잘못된 문법 등으로 서버가 요청을 수행할 수 없음.
- 클라이언트가 재시도해도 실패.
- 400 Bad Request
- 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음.
- 요청 구문, 메시지 등 오류
- 요청 파라미터가 잘못되거나, API 스펙이 맞지 않을 때
- 401 Unauthorized
- 클라이언트가 해당 리소스에 대한 인증이 필요
- 인증(Authentication) 되지 않음.
- 401 오류 발생시 응답에 WWW-Authenticate 헤더와 함께 인증 방법을 설명
- 인증(Authentication): 본인이 누구인지 확인(로그인)
- 인가(Authorization): 권한 부여(특정 리소스에 접근할 수 있는 권한)
- 403 Forbidden
- 서버가 요청을 이해했지만 승인을 거부함
- 주로 인증 자격 증명은 있지만, 권한(인가)이 없을 때
- 404 Not Found
- 요청 리소스가 서버에 없을 때
- 리소스가 존재하지만 권한(인가)이 부족한 클라이언트에게 숨기고 싶을 때 사용
e. 상태코드 - 5xx(Server Error)
- 서버 오류, 서버가 정상 요청을 처리하지 못함.
- 서버가 복구되었을 때 재시도하면 성공할 수도 있음
- 500 Internal Server Error
- 서버 문제로 오류 발생, 애매하면 500 오류
- 503 Service Unavailable
- 서비스 이용 불가
- 서버가 일시적인 과부하 또는 예정된 작업으로 잠시 요청을 처리할 수 없음
- Retry-After 헤더 필드로 얼마뒤에 복구되는지 보낼 수도 있음.
6. HTTP 헤더
과거에는 RFC2616 HTTP 표준을 사용했는데 현재는 폐기되어 사용되지 않는다. 현재는 2014년 등장한 RFC7230~RFC7235를 표준으로 사용한다. 강의에서는 RFC2616을 다루긴 하는데 추후 필요하다면 정리 하겠다.
현재 사용되는 다양한 표준헤더를 정리하겠다.
a. 일반 헤더
1. 표현헤더
- 표현헤더는 표현 데이터(message body)를 해석할 수 있는 정보를 담고 있다.
- 표현 헤더는 요청/응답에 모두 사용한다.
Content-Type
- 표현 데이터(message body)의 형식을 설명
Content-Type 예시
Content-Type: text/html;charset=UTF-8
Content-Type: application/json
Content-Type: image/png
Content-Encoding
- 표현 데이터(message body)를 인코딩
- 표현 데이터를 압축하기 위해 사용
- 송신측에서 인코딩 헤더 추가
- 수신측에서 인코뎅 헤더 정보로 압축 해제
Content-Encoding 예시
Content-Encoding: gzip
Content-Encoding: deflate
Content-Encoding: identity
Content-Language
- 표현 데이터(message body)의 자연언어를 표현
Content-Language 예시
Content-Language: ko
Content-Language: en
Content-Language: en-US
Content-Length
- 표현 데이터(message body)의 길이(바이트 단위)
- Transfer-Encoding(전송 코딩)을 사용하면 Content-Length를 사용하면 안됨.
Content-Length 예시
Content-Length: 5
Content-Length: 25
2. 콘텐츠 협상 헤더
- 클라이언트가 선호하는 정보(타입)를 서버에게 요청
- 협상 헤더는 요청 시에만 사용
- 예) 다중 언어(영어, 한국어 등..)를 지원하는 서버가 있을 때 클라이언트(브라우저)에서 협상 헤더에 원하는 언어(한국어)를 포함해서 요청하면 한국어 콘텐츠를 제공한다.
협상 헤더 우선순위
- 협상 헤더는 우선순위 정보를 포함할 수 있다.
Qualty Values(q)
- q 값을 사용
- 0 ~ 1 사이의 값을 사용, 1에 가까우면 높은 우선순위
- 생략 시 1
- 구체적인 것이 우선한다.
// 협상 우선 순위 예시1
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
자연어 우선 순위
1. ko-KR
2. ko
3. en-US
4. en
// 협상 우선 순위 예시2
Accept: text/*, text/plain, text/plain;format=flowed, */*
표현 우선 순위
1. text/plain;format=flowed
2. text/plain
3. text/*
4. */*
Accept
- 클라이언트가 선호하는 표현 요청
Accept-Charset
- 클라이언트가 선호하는 문자 인코딩
Accept-Encoding
- 클라이언트가 선호하는 압축 인코딩
Accept-Language
- 클라이언트가 선호하는 자연 언어
3. 전송방식
- Transfer-Encoding
- Range, Content-Range
전송방식 설명
- 단순 전송: Content-Length
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3423
- 압축 전송: Content-Encoding
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Encoding: gzip
Content-Length: 3423
- 분할 전송: Transfer-Encoding
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
5
hEllo
5
World
0
\r\n
- 범위 전송: Range, Content-Range
// Range
GET /event
Range: bytes=1001-2000
// Content-Range
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Range: bytes 1001-2000 / 2000
qweqwe1l2iu3019u2oehj1987askjh3q98y
4. 일반 정보
FROM
- 유저 에이전트 이메일 정보
- 잘 사용되지 않음.
- 검색 엔진 같은 곳에서 주로 사용
- 요청에서만 사용
Refer
- 현재 요청된 페이지의 이전 웹이지 주소
- 유입 경로 분석 때 사용
- 요청에서 사용
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 서버의 소프트웨어 정보
- 응답에서 사용
- Server: Apache/2.2.22 (Debian)
Date
- 메시지가 발생한 날짜와 시간
- 응답에서 사용
- Date: Tue, 15 Nov 1994 08:12:31 GMT
5. 특별한 정보
HOST
- 요청한 호스트 정보(도메인)
- 요청에서 사용(필수)
- 하나의 서버가 여러 도메인을 처리해야 할 때
- 하나의 IP 주소에 여러 도메인이 적용되어 있을 때
Location
- 201(Created): Location 값은 요청에 의해 생성된 리소스 URI
- 3xx(Redirection): Location 값은 요청을 자동으로 리다이렉션 하기 위한 대상 리소스를 가르킴.
Allow
- 허용 가능한 HTTP 메서드
- 405(Method Not Allowed) 에서 응답에 포함
- Allow: GET
Retry-After
- 유저 에이전트가 다음 요청을 하기까지 기다려야 하는 시간
- 503(Service Unabailable): 서비스가 언제까지 불능인지 알려줌.
- Retry-After: Fri, 31 Dec 1999 23:59:59 GMT (날짜 표기)
- Retry-After: 120 (초단위 표기)
6. 인증
Authorization
- 클라이언트 인증 정보를 서버에 전달
- Authorization: Basic xxxxxxxxxxxxxxxx
WWW-Authenticate
- 리소스 접근 시 필요한 인증 방법 정의
- 리소스 접근시 필요한 인증 방법 정의
- 401 Unauthorized 응답과 함께 사용
- WWW-Authenticate: Newauth realm="apps", type=1, title="Login to \"apps\"", Basic realm="simple"
7. 쿠키
HTTP는 무상태 프로토콜이다. 클라이언트와 서버가 연결을 유지하지 않아 클라이언트는 매요청마다 서버에게 본인이 누구인지 알려줘야 한다. 이 때 사용하는 것이 쿠키이다.
쿠키
- 로그인 세션 관리, 광고 정보 트래킹에 사용
- 쿠키 정보는 항상 서버에 전송되기 때문에 트래픽이 추가로 발생된다. (최소한의 정보만 사용하는 것이 보안, 성능면에서 좋다.)
Set-Cookie:
- 서버에서 클라이언트로 쿠키 전달(응답)
Cookie
- 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청시 서버로 전달
쿠키 생명주기(Expire, max-age)
- Set-Cookie: expires=Sat, 26-Dec-2020 04:39:21 GMT
- 만료일이 되면 쿠키 삭제
- • Set-Cookie: max-age=3600 (3600초)
- 0이나 음수를 지정하면 쿠키 삭제
- 세션 쿠키: 만료 날짜를 생략하면 브라우저 종료시 까지만 유지
- 영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지
쿠키 도메인(Domain)
- 명시: 명시한 문서 기준 도메인 + 서브 도메인 포함
- domain=example.org를 지정해서 쿠기 생성
- example.orgm(도메인) + dev.example.org(서브도메인)도 쿠키 접근
- domain=example.org를 지정해서 쿠기 생성
- 생략: 현재 문서 기준 도메인만 적용
- example.org에서 쿠키를 생성하고, domain 지정을 생략
- example.org에서만 쿠키 접근
쿠키 경로(Path)
- 이 경로를 포함한 하위 경로 페이지만 쿠키 접근
- 일반적으로 path=/ 루트로 지정하는데 루트 경로를 포함하여 하위 경로까지 쿠키 접근 가능
쿠키 보안(Secure, HttpOnly, SameSite)
- Secure
- 쿠키는 http, https를 구분하지 않고 전송
- Secure를 적용하면 https인 경우에만 전송
- HttpOnly
- XSS 공격 방지
- 자바스크립트에서 접근 불가(document.cookie)
- HTTP 전송에서만 사용
- SameSite
- XSRF 공격 방지
- 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송
b. 캐시와 조건부 요청
1. 캐시 기본 동작
캐시가 없으면 클라이언트는 서버의 리소스의 변경이 없어도 네트워크를 통해 데이터를 다운로드 받아야한다. 매 요청마다 같은 리소스를 받아오는 것은 비효율적이며, 브라우저의 로딩속도도 느려진다, 이런 문제를 해결하기 위해 브라우저에캐시에 저장해놓고 사용한다.
즉, 캐시를 사용하면 캐시 유효 시간 동안 서버측 리소스를 캐시에 저장해놓고 사용하여, 매번 네트워크를 통해서 요청해서 리소스를 사용할 필요가 없다.
그리고 무한정 캐시를 저장할 수 없기 때문에, 캐시 유효 시간이 있는데 유효 시간이 지나면, 서버에 다시 요청을 보내고, 캐시를 갱신한다.
위 내용을 보면 두가지 의문점이 생긴다.
1. 클라이언트는 서버측의 데이터가 변경되었는지 어떻게 알 수 있을까?
2. 캐시 유효시간이 지났음에도 서버측 리소스의 변경이 없으면 다시 요청할 필요가 있을까?
검증헤더와 조건부 요청을 통해 두가지 의문점을 해결할 수 있다.
2. 검증 헤더와 조건부 요청1
캐시 유효 시간이 초과해서 다시 요청하면 두 가지 상황이 나타난다.
1. 서버에서 기존 데이터를 변경함
2. 서버에서 기존 데이터를 변경하지 않음.
위 상황에서 조금 더 효율적으로 캐시를 사용하려면, 서버 측의 데이터가 변경되었는지 확인할 수 있는 방법이 필요하다.
왜냐하면, 캐시 유효시간이 끝났음에도 서버의 데이터가 변경되지 않았으면 데이터를 받아올 필요가 없기 때문이다.
여기서 사용되는 방법이 검증헤더를 추가하는 것이다.
검증헤더 추가
1. 첫번째 요청
- 서버가 응답을 보낼 때 최종 수정시간 정보를 보낸다.
- 클라이언트는 캐시에 응답 결과를 저장한다.
HTTP/1.1 200 OK
Content-Type: image/jpeg
cache-control: max-age=60
Last-Modified: 2020년 11월 10일 10:00:00
Content-Length: 34012
lkj123kljoiasudlkjaweioluywlnfdo912u34ljko98udjklasl
kjdfl;qkawj9;o4ruawsldkal;skdjfa;ow9ejkl3123123
2. 두번째 요청 - 캐시 유효시간 초과
- 클라이언트가 서버에 요청을 보낸다.
GET /star.jpg
if-modified-since: 2020년 11월 10일 10:00:00
- 서버에서 최종 수정일을 보고 변경되지 않았다면 실제 데이터(바디)를 빼고 응답을 보낸다.
- 응답 결과를 보고 캐시를 갱신한다.
- 메시지 바디를 빼고 보내기 때문에 트래픽을 줄일 수 있다.
HTTP/1.1 304 Not Modified
Content-Type: image/jpeg
cache-control: max-age=60
Last-Modified: 2020년 11월 10일 10:00:00
Content-Length: 34012
정리하면, 캐시 유효 시간이 초과해도, 서버의 데이터에 변경이 없으면, 서버는 304 Not Modified + 헤더 메타 정보(바디제외)만 응답하면 용량이 작은 헤더 정보만 주고 받기 때문에 트래픽을 줄일 수 있다.
3. 검증 헤더와 조건부 요청2
Last-Modified, If-Modified-Since는 초 미만 단위로 캐시 조정이 불가능하고, 날짜 기반의 로직을 사용한다는 단점이 있다.
ETAG와 If-None-match를 사용하여 위 단점들을 해결할 수 있다.
ETag(Entity Tag)
- 캐시용 데이터의 임의의 고유한 버전 이름을 달아둔다.
- Hash를 통해 생성하여 고유한 값을 가지며, 데이터가 변경되면 값이 변경된다.
- 단순하게 ETag만 보내서 같으면 유지, 다르면 다시 받는 것 이다.
1. 첫번째 요청
- 첫 요청시 서버는 ETag를 포함해서 응답을 보낸다.
HTTP/1.1 200 OK
Content-Type: image/jpeg
cache-control: max-age=60
ETag: "aaaaaaaaaa"
Content-Length: 34012
lkj123kljoiasudlkjaweioluywlnfdo912u34ljko98udjklasl
kjdfl;qkawj9;o4ruawsldkal;skdjfa;ow9ejkl3123123
2. 두번째 요청 - 캐시 유효시간 초과
- 요청시 ETag를 보내고 서버측에서 비교하여 ETag가 같으면 304 Not Modified 응답을 내려주고, 다르다면 요청한 데이터를 보내줄 것이다.
- 응답 결과를 보고 캐시를 갱신한다.
GET /star.jpg
If-None-Match: "aaaaaaaaaa"
ETag를 장점
- 캐시 제어 로직을 서버에서 온전히 관리
- 애플리케이션 배포 주기에 맞추어 ETag를 모두 갱신한다.
4. 캐시와 조건부 요청 헤더
캐시 제어 헤더
- Cache-Control - 캐시 지시어
- Cache-Control: max-age // 캐시 유효시간, 초단위
- Cache-Control: no-cache // 데이터는 캐시해도 되지만, 항상 원 서버에 검증하고 사용
- Cache-Control: no-store // 데이터에 민감한 정보가 있으므로 저장하면 안됨
- Pragma - 캐시 제어(하위 호환)
- Pragma: no-cache
- HTTP 1.0 하위 호환
- Expires - 캐시 유효 기간(하위 호환)
- expires: Mon, 01 Jan 1990 00:00:00 GMT
- 캐시 만료일을 정확한 날짜로 지정
- 더 유연한 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 값 사용
5. 프록시 캐시
클라이언트가 미국에 있는 구글 원서버에 데이터 요청시 0.5초가 걸린다고 하자. 이 때 중간에 프록시 캐시 서버를 둬서 원서버로 요청하지 않고 프록시 캐시서버에 요청해서 원서버에 요청하는 시간을 줄일 수 있다.
캐시 지시어
- Cache-Control: public: 응답이 public 캐시에 저장되어도 됨
- Cache-Control : private: 응답이 해당 사용자만을 위한 것임. private 캐시에 저장해야함(기본값)
- Cache-Control : s-maxage: 프록시 캐시에만 적용되는 max-age
- Age:60(HTTP헤더): 오리진 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)
6. 캐시 무효화
- Cache-Control : no-cache
- 데이터는 캐시해도 되지만 항상 원(origin) 서버에 검증하고 사용
- Cache-Control : no-store
- 데이터에 민감한 정보가 있으므로 저장하면 안 됨
- Cache-Control: must-revalidate
- 캐시 만료 후 최초 조회시 원서버에 검증해야 함
- 원서버 접근 실패시 반드시 오류 발생해야 함(504 Gateway Timeout)
- must-revalidate는 캐시 유효 시간이라면 캐시를 사용함
- Pragma: no-cache
- HTTP 1.0 하위 호환