저번 시간에 이어서 이번에도 HTTP 통신의 여러 기능들과 상태를 학습할 예정이다.
HTTP 상태 코드
클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능
상위 상태코드
만약 모르는 상태코드가 나타난다면
클라이언트는 아래와 같이 100대, 200대 등으로 상위 상태코드로 해석하여 처리한다.
- 1xx: 요청이 수신되어 처리중
- 잘 사용하지 않는다
- 2xx: 요청 정상 처리 - 성공
- 200 Ok
- 201 Created
- POST같은 것으로 리소스를 등록했을 때
응답 HTTP헤더에 서버에서 만든 리소스 경로를 넣어준다.
- POST같은 것으로 리소스를 등록했을 때
- 202 Accepted
- 요청이 접수되었으나 처리가 완료되지 않음
- 204 No Content
- 서버가 요청을 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음
결과 내용이 없어도 204 메시지만으로 성공을 인식할 수 있다.
- 서버가 요청을 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음
- 3xx: 리다이렉션, 요청을 완료하려면 추가 행동이 필요
- 리다이렉션: 3xx 응답 결과에 Location 헤더가 있으면, Locatoin 위치로 자동 요청을 보낸다.
- 영구 리다이렉션: 특정 리소스의 URI가 영구적으로 이동
/members -> /users 이런 거 완전히 바뀐 것을 응답으로 알려준다.- 301 Moved Permanently
- 리소스의 경로가 완전히 바뀌었다는 것을 알려줌
-> 리다이렉트시 요청 메서드가 GET으로 변하고 본문이 완전히 사라질 수 있다. (대부분 이런 식)
- 리소스의 경로가 완전히 바뀌었다는 것을 알려줌
- 308 Permanent Redirect
- 301과 기능은 같다.
-> 리다이렉트시 요청 메서드와 본문 유지한다. 허나, 실무에서 거의 사용하지 않는다.
- 301과 기능은 같다.
- 301 Moved Permanently
- 일시 리다이렉션: 일시적인 변경
리소스의 URI가 일시적으로 변경
- 302 Found
- 리다이렉트 시 요청 메서드가 GET으로 변하고 본문이 완전히 사라질 수 있다.
- 307 Temporary Redirect
- 302와 기능은 같지만, 리다이렉트시 요청 메서드와 본문을 유지해야 한다.
- 303 See Other
- 302와 기능은 같지만, 리다이렉트시 요청 메서드가 GET으로 변경된다.
- PRG: Post/Redirect/Get
- POST로 주문 후에 새로 고침하면 다시 주문이 들어가는 중복 주문 상황이 발생할 수 있다.
- 따라서 POST로 주문 후에 결과 화면을 GET 메서드로 리다이렉트, -> 302나 303 사용!
새로 고침해도 결과 화면만 GET으로 조회 - 사용성이 좋다. 서버 입장에서도 오류가 줄어든다.
- 많은 애플리케이션 라이브러리들이 302를 사용하고 있다. 허나 303, 307을 권장한다.
- 302 Found
- 특수 리다이렉션: 결과 대신 캐시를 사용
- 304 Not Modified
- 캐시를 목적으로 사용
- 클라이언트에게 리소스가 수정되지 않았음을 알려준다. -> 클라이언트는 로컬 PC에 저장된 캐시를 재사용한다.
- 304 응답은 응답에 메시지 바디를 포함해선 안된다.
- 304 Not Modified
- 4xx: 클라이언트 오류, 서버가 요청을 수행할 수 없음 -> 이미 데이터를 보내고 있어서 재시도해도 실패함 (복구 불가)
- 오류의 원인이 클라이언트에 있음
- 400 Bad Request
- 요청 구문, 메시지 등 오류 -> 클라이언트가 요청 내용을 다시 검토하고 보내야 함
- 401 Unauthorized
- 인증되지 않음 == 로그인이 안되었다
- 응답에 WWW-Authenticate 헤더와 함께 인증 방법을 설명
- 인증(Authenrication): 본인이 누구인지 로그인
- 인가(Authorization): 권한부여, 특정 리소스에 접근할 수 있는 권한
- 404 Forbidden
- 인증 자격 증명은 있지만, 접근 권한이 불충분한 경우 -> 로그인은 했지만 다른 등급의 리소스에 접근하는 경우
- 404 Not Found
- 요청 리소스가 서버에 없음
- 클라이언트가 권한이 부족한 리소스에 접근
- 5xx: 서버 오류, 서버가 정상 요청을 처리하지 못함
- 서버 문제로 오류 발생, 재시도하면 성공할 수도 있음
- 자주 사용하면 안되고, 정말 서버에 문제가 있을 때만 사용한다! -> null포인터 오류나 DB 다운 이런 거
- 500 Internal Server Error
- 애매하면 500 오류를 사용하자
- 503 Service Unavailable
- 서버가 일시적인 과부하 또는 예정된 작업으로 잠시 요청을 처리할 수 없다
HTTP 헤더 - 일반 헤더
헤더에는 http 전송에 필요한 모든 부가정보를 넣는다.
(과거)엔티티 -> (현재)표현
표현 = 표현 메타데이터 + 표현 데이터

- 메시지 본문 = 페이로드
- 표현 헤더는 표현 데이터를 해석할 수 있는 정보를 제공
표현
표현 헤더는 요청, 응답 둘 다 사용한다!
- Content-type: 표현 데이터의 형식
- 미디어 타입: json인지 html인지
- 문자 인코딩: utf-8
- Content-Encoding: 표현 데이터의 압축 방식
- 표현 데이터 압축할 때 사용:
데이터를 전송하는 쪽에서 압축 후 인코딩 헤더 추가 - 데이터를 읽는 쪽에서는 인코딩 헤더의 정보로 압축 해제
- gzip
- 표현 데이터 압축할 때 사용:
- Content-Language: 표현 데이터의 자연 언어
- ko, en, en-US
- Content-Length: 표현 데이터의 길이
콘텐츠 협상
클라이언트가 선호하는 표현 요청 => 요청 시에만 사용한다!
- Accept: 클라이언트가 선호하는 미디어 타입 전달
- Accept-Charset: 클라이언트가 선호하는 문자 인코딩
- Accept-Encoding: 클라이언트가 선호하는 압축 인코딩
- Accept-Language: 클라이언트가 선호하는 자연 언어
- 협상 우선순위1: Quality Values(q) 값 사용
- 0 ~ 1: 클수록 높은 우선순위, 생략하면 1
- 예시) Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
- 협상 우선순위2: 구체적인 것이 우선한다.
- 예시) text/*, text/plain, text/plain;format=flowed, */*
- 협상 우선순위3: 구체적인 것을 기준으로 미디어 타입을 맞춘다.
- 예시) Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5
- 협상 우선순위1: Quality Values(q) 값 사용
전송 방식
- 단순 전송
- 콘텐츠의 길이를 알고 있을 때
- 압축 전송
- 서버에서 압축하여 전송
- Content-encoding 을 추가로 넣어주기
- 분할 전송
- Transfer-encoding: chunked 사용
- 쪼개서 보냄 -> 용량이 크다면 오는대로 바로 표시할 수 있다
- Content-length 를 넣으면 안된다.
- 범위 전송
- 범위를 지정해서 요청할 수 있다.
- Content-Range를 사용하여 서버에서 응답
- GET /event
Range: bytes=1001-2000 ----> (서버)
(클라이언트) <---- Content-Range: bytes 1001-2000 / 2000
일반 정보 (정보성 헤더)
- From 유저 에이전트의 이메일 정보
- 검색 엔진 같은 곳에서 사용
- 요청에 사용된다!
- Referer 현재 요청된 페이지의 이전 웹 페이지 주소
- 유입 경로 분석에서 사용
- 요청에 사용된다!
- 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 서버라고 한다.
- 응답에서 사용!
- Date 메시지가 발생한 날짜와 시간
- 응답에서 사용!
특별 정보 헤더
- HOST 요청한 호스트 정보(도메인)
- 요청에서 필수적으로 사용하는 헤더
- 하나의 서버가 여러 도메인을 처리해야 할 때
- Location
- 3xx 응답의 결과에 Location 헤더가 있으면 Location 위치로 자동 이동! (리다이렉션)
- Allow
- 405 Method Not Allowed 에서 응답에 사용한다!
- URI만 있고 메서드가 없는 경우
- Retry-After
- 503 서비스가 언제까지 불능인지 알려줄 수 있다
인증 헤더
- Authorization 클라이언트의 인증 정보를 서버에 전달
- Authorization: 값은 인증마다 다 다르다, 인증과 관련된 값이 들어감
- WWW-Authenticate 리소스 접근 시 필요한 인증 방법 정의
- 401 응답과 함께 사용
- 예시) WWW-Authenticate: Newauth realm="apps", type=1,
title="Login to \"apps\"", Basic realm="simple" --> 이렇게 맞춰서 와 클라이언트야 이런 식!
쿠키
HTTP는 무상태 프로토콜이다. (stateless)
클라이언트와 서버가 요청과 응답을 주고 받으면 연결이 끊어진다.
클라이언트가 다시 요청하면 서버는 이전 요청을 기억하지 못한다. => 클라이언트와 서버는 서로 상태를 유지하지 않는다.
(랜딩페이지를 다시 요청한다고 생각해보자)
- 대안: 모든 요청에 사용자 정보를 보낸다? 비효율, 보안 취약 => 쿠키 도입
- 로그인 후 사용자 정보와 함께 요청 -> 서버에서 쿠키(사용자 정보)를 말아서 클라이언트로
-> 이후 클라이언트는 쿠키를 찾아서 요청 - 모든 요청에 쿠키 정보가 자동으로 포함된다.
- Set-Cookie: 서버에서 클라이언트로 쿠키 전달 (응답)
- 사용자 로그인 세션 관리, 광고 정보 트래킹
- 쿠키 정보는 항상 서버에 전송된다. -> 네트워크 트래픽 추가 유발 -> 따라서 최소한의 정보만 사용!
- 요청할 때마다 보내는 것이 아닌 웹 브라우저에 저장하고 싶으면 웹 스토리지를 사용
- expires: 만료일이 되면 쿠키 삭제 expires=Sat, 26-Dec-2020 04:39:21 GMT
- max-age: 0이나 음수를 지정하면 쿠키 삭제 max-age=3600
- 세션 쿠키: 만료 날짜를 생략 - 브라우저 종료시 까지만 유지
- 영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지
- Cookie: 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청에서 서버로 전달
- domain
- 도메인을 지정하면 기준 도메인과 서브 도메인을 포함
- 도메인을 지정하지 않으면 현재 문서 기준 도메인만 적용한다.
- path
- 이 경로를 포함한 하위 경로 페이지만 쿠키 접근 가능
- path=/home
- /home => 가능
- /home/level1 => 가능
- /home/level1/level2 => 가능
- /hello => 불가능
- Secure
- 쿠키는 http, https 구분하지 않고 전송
- Secure 적용하면 https일 때만 전송
- HttpOnly
- xss공격 방지
- 자바스크립트 접근 불가
- HTTP 전송에만 사용
- SameSite
- xsrf 공격 방지
- 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송
- domain
캐시와 조건부 요청
캐시 기본 동작
- 캐시가 없을 때:
클라이언트에서 1번째 요청 -> 서버에서 데이터 응답
클라이언트에서 2번째 요청 -> 다시 서버에서 데이터 응답
데이터가 변경되지 않아도 계속 네트워크를 통해서 다운받아야 한다.- 네트워크는 비싸고 느리다
- 브라우저 로딩 속도가 느리다
- 느린 사용자 경험
- 캐시 적용:
클라이언트에서 1번째 요청: 서버에서 캐시 유효 시간과 데이터 응답, 브라우저에서 캐시 저장
클라이언트에서 2번째 요청: 브라우저 캐시에서 캐시 유효 시간을 검증하고, 시간이 남아있다면 이걸 사용한다.- 비싼 네트워크 사용량을 줄인다
- 브라우저 로딩 속도가 빠르다
- 빠른 사용자 경험
- 만약 두 번째 요청을 넘어 세 번째 요청을 한다면?
- 다시 요청
- 다시 캐시를 브라우저 캐시에 덮어씌운다. -> 데이터가 변경되지 않아도 네트워크 다운로드 발생
- 캐시 시간 초과: 서버를 통해 데이터를 다시 조회하고, 캐시를 갱신한다.

검증 헤더와 조건부 요청
캐시 유효 시간이 초과해서 서버에 다시 요청한 상황
- 서버에서 기존 데이터를 변경함
- 서버에서 기존 데이터를 변경하지 않음
- 브라우저 캐시에 저장해두었던 캐시를 재사용할 수 있다 (기존 데이터가 변경되지 않은 경우!)
클라이언트에 있는 데이터와 서버에 있는 데이터가 같다는 사실을 확인할 방법이 필요 - 검증 헤더: 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터
ETag / Last-Modified- 서버에서 데이터가 마지막에 수정된 시간을 넣어서 응답을 준다. (Last-Modified: xxxx/xx/xx를 넣어서 보내준다)
- 브라우저 캐시에 받은 캐시와 함께 데이터 최종 수정일 저장
- 캐시 가능시간이 만료되어서 다시 요청을 보낼 때, 데이터 최종 수정일을 넣어서 서버에 요청을 보낸다
(if-modified-since: 2020/11/10/xx:xx 를 넣어서 보내준다.)- 서버에 있는 데이터 수정일과 요청에 있는 데이터 수정일이 같다면, (데이터가 변하지 않았다)
응답을 보낼 때 304 Not Modified 와 빈 HTTP Body를 보낸다!! -> 즉 HTTP 헤더만 보낸다는 말이다.
응답 결과를 재사용하여 헤더 데이터를 갱신한다. 브라우저가 가지고 있는 캐시에 있는 데이터를 사용할 수 있다. - 클라이언트는 캐시에 저장되어 있는 데이터를 재활용할 수 있다
- 네트워크 다운로드가 발생하지만 용량이 적은 헤더만 다운로드한다.
- 서버에 있는 데이터 수정일과 요청에 있는 데이터 수정일이 같다면, (데이터가 변하지 않았다)
- 브라우저 캐시에 저장해두었던 캐시를 재사용할 수 있다 (기존 데이터가 변경되지 않은 경우!)
- 조건부 요청 헤더
If-Match, If-None-Match: ETag 값 사용
If-Modified-Since, If-Unmodified-Since: Last-Modified 값 사용 - If-Modified-Since: 이후에 데이터가 수정되었으면?
- 수정안됨(거짓) 304 (HTTP 헤더 데이터만 전송)
- 수정이 되었다(참) 200: 모든 데이터 전송 (HTTP 헤더와 Body 포함)
- 단점
- 1초 미만 단위로 캐시 조정이 불가능
- 날짜 기반의 로직 사용
- 데이터를 수정하면 날짜가 달라지지만, 다시 그 데이터를 수정해서 데이터 결과가 같은 경우
A -> B -> A 인 결과 - 서버에서 별도의 캐시 로직을 관리하지 못함
- ETag(Entity Tag)
- 캐시용 데이터에 임의의 고유한 버전 이름을 달아둠
- 데이터가 변경되면 이 이름을 바꾸어서 변경함!
- ETag만 보내서 같으면 유지, 다르면 다시 받기!
- 서버에서 ETag를 내려준다
- 브라우저 캐시에 ETag 값을 저장한다.
- 캐시 만료되고 다시 요청을 보내면 If-None-Match: "aaaaaaaaaaa" 를 넣어서 요청을 보낸다
- 만약 서버에 있는 데이터의 ETag 가 매치가 된다 (거짓) -> 304 Not Modified 보내면서
HTTP Body를 전송하지 않는다. -> 브라우저에서 캐시를 갱신하여 재사용한다! - 캐시 제어 로직을 서버에서 완전히 관리

캐시 관련 헤더
- Cache-Control 캐시 제어 ( Cache-Control: max-age 이런 식으로 사용한다)
- max-age: 캐시 유효 시간, 초 단위
- no-cache: 데이터는 캐시해도 되지만, 항상 origin(중간 말고 뒤에 있는 진짜) 서버에서 검증하고 사용!
- no-store: 데이터에 민감한 정보가 있어서 저장하면 안된다
- Pragma: 캐시 제어 (하위 호환)
- no-cache -> 거의 사용 안함
- Expires: 캐시 유효 기간 (하위 호환)
- expires:Mon, 01 Jan 1990 00:00:00 GMT
- 캐시 만료일을 정확한 날짜로 지정
- Cache-Control: max-age ->이걸 권장한다. 같이 사용하면 expires는 무시된다.
프록시 캐시
CDN 서비스
프록시 캐시 서버 -> public 캐시
웹 브라우저 -> private 캐시

캐시 무효화
확실한 캐시 무효화 응답이다, 만약 웹 브라우저에 있는 특정 페이지에는 무조건 캐시를 넣으면 안된다는 상황을 가정하자.
- Cache-Control: no-cache, no-store, must-revalidate
- must-revalidate: 캐시 만료 후 최초 조회 시 원 서버에 검증해야 함
프록시와 원 서버 사이의 네트워크가 단절되었을 때, 504 Gateway Timeout 을 보여준다.
프록시 캐시에 있는 과거 데이터가 보이지 않게 한다.(내 계좌에 갑자기 월급이 사라졌다고 생각해보자,,)
- must-revalidate: 캐시 만료 후 최초 조회 시 원 서버에 검증해야 함
- Pragma: no-cache
- HTTP 1.0 으로 오는 요청 관리
- 위 2가지를 다 넣어주어야 한다!
여기까지 HTTP 강의 끝났다. API부터 HTTP 통신에서 필수적으로 알아야 할 요소들을 배웠다.
네트워크 과목도 수강하겠지만, 이렇게 들어보니 정말 편리하게 알 수 있었다.
통신에 관해 무지했는데 이렇게 강의를 듣고 기록하며 미니 프로젝트로 직접 API도 만들어보니
더 기억에 남는다! HTTP는 정말 방대해서 모르는 부분이 생기면 이곳에서 종종 찾아서 공부해야겠다.