HTTP 헤더
1. 일반헤더
HTTP 헤더의 구조
'필드이름: 필드값 ' |
- 필드값 앞 뒤로 띄어쓰기 가능
- 필드이름은 대소문자 구분이 없다
ex : Host: http://www.google.com
HTTP 헤더의 용도
- HTTP 전송에 필요한 모든 부가정보를 전달 (메시지 바디의 내용, 바디의 크기, 압축, 인증, 요청 클라이언트, 서버정보 등)
- 표준 헤더가 많음
- 필요하면 임의의 헤더 추가도 가능
HTTP 헤더의 과거 (1999년 RFC2616)
General 헤더 : 메시지 전체에 적용되는 정보 |
Request 헤더 : 요청 정보 |
Response 헤더 : 응답 정보 |
Entity 헤더 : 엔티티 바디 정보 (폐기됐음) |
- 과거엔 메시지 본문(message body)는 엔티티 본문을 전달하는데 사용했다.
- 엔티티 본문이 실제 데이터고 그걸 메시지 본문에 넣어서 보내는 것.
- 엔티티 헤더는 엔티티 본문의 데이터를 해석할 수 있는 정보를 제공한다. (데이터 유형이나 데이터 길이 압축 정보 등..)
- 그런데 새로운 HTTP 표준의 등장으로 엔티티 바디라는게 폐기가 됐음
HTTP 헤더의 현재 (2014년 RFC7230~7235)
- 표현의 변화
엔티티(Entity) → 표현(Representation)
표현(Representation)은 표현에 대한 메타데이타(Representation Metadata)와 표현 데이터(Representation Data)를 합친 것
* restAPI의 r이 Representation
- 메시지 본문(message body)을 통해 표현 데이터 전달
- 메시지 본문 = 페이로드(payload)
- 표현은 요청이나 응답에서 전달할 실제 데이터
- 표현 = 표현 메타데이타 + 표현 데이타
- 표현 헤더는 표현 데이터를 해석할 수 있는 정보를 제공
- 표현 헤더는 표현 메타데이터와 페이로드 메시지를 구분하지만 생략!
표현 헤더
- Content-Type: 표현 데이터의 형식 (미디어타입, 문자 인코딩)
text/html; charset=utf-8, application/json, image/png |
- Content-Encoding: 표현 데이터의 압축 방식
전달하는 곳에 데이터 압축 후 인코딩 헤더 추가
데이터를 읽는 쪽에서 인코딩 헤더의 정보로 압축 해제
gzip, deflate, identity(압축안함) |
- Content-Language: 표현 데이터의 자연 언어
ko, en, en-US |
- Content-Length: 표현 데이터의 길이 (바이트단위)
+ Transfer-Encoding(전송코딩)을 사용하면 Content-Length를 사용하면 안됨!
협상 헤더 (콘텐츠 네고시에이션)
Accept : 클라이언트가 선호하는 미디어 타입 전달 |
Accept-Charset : 클라이언트가 선호하는 문자 인코딩 |
Accept-Encoding : 클라이언트가 선호하는 압축 인코딩 |
Accept-Language : 클라이언트가 선호하는 자연 언어 |
- 클라이언트가 선호하는 표현 요청, '클라가 원하는걸로 줘볼게...'
- 협상 헤더는 요청시에만 사용한다
예를들면 한국어 브라우저 사용 중 한국어 지원 안하는 독일 서버 접속,
클라이언트에서 한국어 요청 → 독일 서버에서는 한국어가 없으니 뭘 줄지 고민..
이럴때 우선순위가 필요
우선순위1
- Quality Values(q) 값 사용
- 0~1, 클수록 높은 우선순위
- 1이 default
Accpet-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7 |
ko-KR은 q 값이 생략됐으니 젤 높음! (디폴트 q=1.0)
순위 : ko-kR > ko > en-US > en:q
우선순위2
- 구체적인 것이 우선
Accept: text/*, text/plain, text/plain;format=flowed, */* |
text/plain;format=flowed가 구구절절하니까 젤 높음
순위 : text/plain > text/* > */*
우선순위3
- 구체적인걸 기준으로 미디어 타입을 맞춤
(생략)
전송 방식
- 단순 전송
Content-Length: 123 |
컨텐츠의 길이를 알 수 있어야함
한번에 요청, 한번에 응답
- 압축 전송
Content-Encoding: gzip |
압축해서 보낼게
- 분할 전송
Transfer-Encoding: chunked |
용량 커서 나눠서 보낼게!
참고로 분할 전송땐 Content-Length를 보내면 안됨
- 범위 전송
Range: bytes=1001-2000 |
이정도 범위만 요청이요
일반 정보
- From : 유저 에이전트의 이메일 정보 (잘안씀; 검색엔진같은 곳에서 사용), 요청에서 사용
- Referer : 이전 웹 페이지 주소, 요청에서 사용, 현재 요청된 페이지의 이전 웹 사이트 주소 (많이 쓴다)
A → B로 이동하는 경우 B를 요청할때 Referer:A를 포함해서 요청 (이전 사이트는 A에요~)
* 참고로 referer은 referrer의 오타라고 한다ㅋㅋㅋ
- User-Agent : 유저 에이전트 애플리케이션 정보, 클라이언트의 애플리케이션 정보(웹 브라우저 정보 등등) , 요청에서 사용
서버 입장에서 특정 브라우저 장애를 파악하는데 좋음
- Server : 요청을 처리하는 ORIGIN 서버의 소프트웨어 정보, 응답에서 사용함
중간에 여러 프록시 서버가 아닌 진짜 응답해주는 서버가 ORIGIN 서버의 정보
- Date : 메시지가 발생한 날짜와 시간, 걍 날짜임, 응답에서 사용함
특별한 정보
- Host : 요청한 호스트 정보(도메인), 걍 완전 필수라고 한다, 요청에서 사용
실제로 여러 애플리케이션 서버가 구동될 수 있으니, 하나의 서버에서 여러 도메인을 처리할 때 구분하는 용
- Location : 페이지 리다이렉션
웹브라우저가 3XX 응답 결과에 Location 헤더가 있으면 해당 위치로 자동 이동(리다이렉트)
- Allow : 허용 가능한 HTTP 메서드
응답으로 GET만 가능하다~ 이런 너낌~
- Retry-After : 유저 에이전트가 다음 요청을 하기까지 기다려야하는 시간 (잘안씀)
서버가 언제까지 불능인지 알려줌..
인증
- Authorization : 클라이언트 인증 정보를 서버에 전달하는 것
- WWW-Authenticate : 리소스 접근시 필요한 인증 방법 정의
401 에러와 함게 사용
리소스 접근시 필요한 인증방법을 정의해줌
쿠키 ***
- Set-Cookie : 서버에서 클라이언트로 쿠키를 전달(응답)
- Cookie : 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청시 서버로 전달
HTTP는 기본적으로 무상태(Stateless) 프로토콜! 그래서 응답 끝나면 연결도 끝!
클라이언트가 다시 요청해도 서버는 이전 요청을 모름! (클라이언트와 서버는 서로 상태를 유지하지 않음!)
그래서 대안1 → 모든 요청과 링크에 사용자 정보 포함;;; (당연히 이러면 개인정보 보안 개망) 💀
그래서 대안2 → 쿠키가 도입된다.
로그인을 하면 응답으로 Set-Cookie로 보낸다.
그리고 받은 응답을 쿠키 저장소에 저장!
이 후 다른 요청을 할 때마다 쿠키 저장소를 확인하고 Cookie에 넣어서 요청
쿠키가 저장되면 모든 요청에 쿠키 정보 자동 포함! 근데 이것도 보안 엉망; 💀
그래서 쿠키를 세팅할 때 ...
set-cookie: sessionId=abcde1234; expires=Sat, 26-Dec-2020 00:00:00 GMT; path=/; domain=.google.com; Secure |
- sessionId로 사용자 로그인 세션 관리 (요새는 광고 정보 트래킹에 쓰기도 한다)
- 추가로 더 보내려니 네트워크 트래픽 추가를 유발한다;
- 그래서 최소한의 정보만 사용하자 (세션 id, 인증 토큰)
- 서버에 전송하지 않고 웹 브라우저 내부에 데이터를 저장하고 싶으면 웹 스토리지(localStorage, sessionStorage)를 참고
- 주의! 보안에 민감한 데이터는 저장하면 안된다 (주민번호, 신용카드 번호 등)
쿠키의 생명 주기
expires로 만료일이 되면 쿠키 삭제
Set-Cookie: max-age=3600 |
3600초 뒤에 제거
세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료시 까지만 유지
영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 유지
쿠키의 도메인
domain으로 명시한 문서 기준 도메인과 서브 도메인을 전송
domain=expmple.org |
즉, domain=expmple.org 로 쿠키 생성하면 example.org랑 dev.example.org도 쿠키 접근
domain 생략하면 현재 문서 기준 도메인만 적용됨
쿠키의 경로
path를 통해 이 경로를 포함한 하위 경로 페이지만 쿠키 접근
일반적으로 path=/ 루트로 지정
쿠키의 보안
Secure : 쿠키는 http, https 구분하지 않고 전송하는데 Secure 적용하면 Https인 경우에만 전송함
HttpOnly : XSS 공격 방지. 자바스크립트에서 접근 불가. HTTP 전송에만 사용
SameSite : XSRF 공격 방지. 현재 요청하는 도메인과 쿠키에 설정된 도메인이 같은 경우에만 전송!
2. 캐시와 조건부 요청 헤더
캐시가 없을 때
요청 → 응답
한번 더 요청 → 똑같이 응답
- 데이터가 변경되지 않아도 계속 네트워크를 통해서 데이터를 다운받아야함
- 인터넷 네트워크는 매우 느리고 비싸다
- 브라우저 로딩 속도가 느림
- 느린 사용자 경험 제공 .. 최악ㅠㅠ
캐시 적용
요청 → cache-control과 함께 응답
cache-control: max-age=60 |
브라우저 캐시에서 60초동안 유효함
한번 더 요청 → 그냥 캐시에서 조회 (네트워크도 사용안함)
- 캐시 덕분에 캐시 가능 시간동안 네트워크 사용안함
- 비싼 네트워크 사용량을 줄임
- 브라우저 로딩 속도가 매우 빠름
- 빠른 사용자 경험 제공 ㅎ
근데 60초 이후에 요청 → 다시 cache-control과 함께 응답
- 캐시 시간이 초과되면 서버를 통해 데이터를 다시 조회하고 캐시를 갱신함
- 이때는 네트워크 다운로드가 다시 발생 .. 어쩔수없지ㅠㅠ
캐시 시간 초과
캐시 유효 시간이 초과 후 다시 요청하면 다음 두가지 상황
서버에서 기존 데이터를 변경하거나 vs 서버에서 기존 데이터를 변경하지 않거나
생각해보면 데이터를 전송하는 대신 저장해둔 캐시 재사용하면 된다.
단, 클라이언트의 데이터와 서버의 데이터가 같다는 걸 확인할 수 있어야함
검증 헤더
캐시 데이터와 서버 데이터가 같은지 검증하는 데이터
조건부 요청 헤더
- 검증 헤더로 조건에 따른 분기
- 조건이 만족하면 200 OK, 아니면 304 Not Modified
(1) If-Modified-Since: '수정됐나요?' 묻는 느낌
if-Modified-Since: Last-Modified |
만족 → 데이터 변경 됐을 시 200 OK, 모든 데이터 전송
불만족 → 데이터 미변경 시 304 Not Modified, 헤더 데이터만 전송 (304인 이유는 200은 아니니까)
(2) If-None-Match: '같나요?' 묻는 느낌
if-None-Match: Etag |
만족 → 데이터가 미변경 됐을 시 304 Not Modified, 헤더 데이터만 전송
불만족 → 데이터가 변경 시 200 OK
검증 헤더 조건부 요청1
요청 → 응답할 때 캐시와 Last-Modified(최종수정일) 를 추가!
시간 초과 후 if-modified-since(수정됐나요?)과 함께 요청
최종 수정일이 같으면 → 데이터가 미변경 된거니 304 Not Modified 캐시와 함께 헤더 데이터만 응답
- 캐시 유효 시간이 초과해도, 서버의 데이터가 갱신되지 않으면 304 응답
- 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신
- 클라이언트는 캐시 유효시간이 지나도 캐시에 저장되어 있는 데이터를 재활용
- 네트워크 다운로드가 발생하긴 하지만 용량이 적음! (헤더 데이터만 수신하니까!)
- 존나 실용적이네 ...
위와 같이 Last-Modified 응답 & If-Modified-Since 요청의 단점
- 1초 미만 단위는 수정 불가능
- 날짜 기반의 로직을 사용하여 데이터를 수정해서 날짜가 다른데, 같은 데이터를 수정해서 데이터 결과가 똑같은 경우 문제!
- 서버에서 별도의 캐시 로직을 관리하고 싶은 경우에 문제
이럴 때 if-None-Match응답 & Etag요청 활용
검증 헤더 조건부 요청2
요청 → ETag 와함께 응답
If-None-Match(같나요?) ETag 넣어서 재요청
같으면 → 데이터가 미변경 된거니 304 헤더 데이터만 보내고 캐시에서 조회
- 단순하게 ETag만 서버에 보내서 같으면 유지, 다르면 다시 받기
- 캐시 제어 로직을 서버에서 완전히 관리함
- 클라이언트는 단순히 이 값을 서버에 제공하기만 하면 됨
Etag응답 & If-None-Match요청
- 캐시용 데이터에 임의의 고유한 버전 이름을 달음
- 데이터가 변경되면 이 버전 이름을 변경 (Hash 다시 생성)
- 걍 단순하게 ETag만 보내서 같으면 유지, 다르면 다시 받는 것
캐시 제어 헤더
캐시 지시어(directives)
Cache-Control: max-age |
캐시 유효 시간, 초 단위로
Cache-Control: no-cache |
데이터는 캐시해도 되는데, 항상 origin 서버에 검증하고 사용함
Cache-Control: no-store |
데이터에 민감한 정보가 있으니까 저장하지마 (메모리에서 사용하고 최대한 빨리 삭제)
캐시 제어
Pragma: no-cache |
걍 하위호환임 쓰지말자..ㅋㅋ
캐시 만료일 지정
Expries: Mon, 01 Jan 어쩌구 |
캐시 만료일을 초가 아니라 정확한 날짜로 지정함
초 단위가 더 유연하니까 쓰지말자
검증 헤더
ETag, Last-Modified
조건부 요청 헤더
If-Match, If-None-Match (ETag 값 사용)
If-Modified-Since, If-UnModified-Since (Last-Modified 값 사용)
프록시 캐시
origin(원) 서버 직접 접근
한국에 있는 클라이언트가 미국에 있는 원 서버 접근할 때 개느림
그래서 프록시 캐시를 도입했다.
프록시 캐시 서버는 그 중간 어딘가에 있는 서버에 경유하고 감
첫번째 요청 → 프록시 서버가 원서버로부터 다운 (느림)
두번재 요청 → 프록시 서버에서 다운 (빠름)
Cache-Control: public |
응답이 public(프록시 서버) 캐시에 저장되어도 됨!
Cache-Control: private |
응답이 private(해당 사용자) 캐시에만 저장해야함! (default)
Cache-Control: s-maxage |
프록시 캐시에만 적용되는 max-age
Age: 60 |
원 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)
캐시 무효화
캐시를 적용 안해도 웹브라우저가 임의로 해버리기도 한다 ;;
그래서 확실한 캐시 무효화 응답이 필요
Cache-Control: no-cache, no-store, must-revalidate |
위에 것들 다 써야한다.
Pragma: no-cache |
혹시 몰라서 HTTP 1.0 하위호환이라 이것도 쓰긴한다.
Cache-Control: must-revalidate |
캐시 만료 후 최초 조회시 origin 서버에 검증해야해!
origin 서버 접근 *실패시 반드시 오류 발생(504 Gateway Timeout)!!
캐시 유효 시간이라면 캐시 사용함 (no-cache, no-store은 위에 적음)
no-cache하면 프록시 캐시에 접근 시 origin 검증을 요청
원래는 검증 후에 origin이 프록시에게 304 응답, 프록시는 private에게 304 응답
근데! 그 중간에 네트워크 단절되면... 프록시 서버가 200 응답하기도함;
must-revalidate 이게 추가되면 ... 네트워크 단절되면 반드시 504 응답
참고 강의 : https://taylog.tistory.com/203
'🧠 저장 > Http' 카테고리의 다른 글
HTTP 상태코드 간단 정리 (0) | 2024.02.20 |
---|---|
HTTP API 설계 개념 간단 정리 (0) | 2024.02.17 |
HTTP 메서드 종류, 속성 간단 정리 (0) | 2024.02.14 |
HTTP 특징, 구조 간단 정리 (0) | 2024.02.11 |
웹 브라우저 요청 흐름 간단 정리 (0) | 2024.02.08 |