모든 개발자를 위한 HTTP 웹 기본 지식
인프런 강의를 다 듣고, 복습 겸 간단하게 정리해서 올린다.
자세한 내용은 개인 노션에 정리해두었다.
클라이언트와 서버 사이를 연결해주는 것이 인터넷,
클라이언트에서 서버로 메시지를 보내려면 각각의 ip address가 필요하다.
패킷이라는 통신 단위로 각각의 노드를 타다가 도착을 원하는 서버에 도착~
비연결성: 데이터 유실, 서버가 존재하지 않는 등의 문제
비신뢰성: 끊어서 보내면 순서를 지킬 수가 없음
전송 제어 프로토콜이다.
IP 패킷에 있는 정보 외에 출발지 port, 목적지 port, 전송 제어, 순서, 검증 정보 등을 담고 있다.
클라이언트가 서버에 연결하는 방법
1. 클라이언트가 서버에게 SYN: synchronize 메시지 보냄
2. 서버가 확인하고 ACK: 왔네? ㅇㅋㅇㅋ 라는 메시지를 SYN과 함께 보냄
3. 클라이언트가 서버의 SYN을 받고 다시 ACK 보냄
이렇게 '논리적으로' 보장 후 전송
-> 이렇게 하니까 데이터 전달 보증도 되고, 순서 보증도 되더라
연결 지향도 아니고, 순서도 보장하지 않지만 port가 있다.
요즘 뜨는 이유는?
Http3에서 그나마 속도를 개선해보겠다고 UDP 프로토콜을 써본대요
Uniform Resource Identifier: 리소스를 식별하는 통합된 방법
scheme://[userinfo@]host[:port][/path][?query][#fragment]
해석은 노션 보자
HTTP: HyperText Transfer Protocol
start-line
header
empty-line
message body
와 같이 구성됨!
(여기서 참고 SP: 공백, CRLF: 엔터, OWS: 띄어쓰기 허용)
요청일 때는 request-line, 응답일 때는 status-line
설명은... 노션에서
header-field = field-name “:” OWS field-value OWS
http 전송에 필요한 모든 부가 정보 (ex. 메시지 바디의 내용, 크기, 클라이언트 정보 등)
실제 전송할 데이터 (byte로 표현할 수 있는 모든 데이터 전송 가능)
가장 중요한 것은 리소스 식별!!!
(리소스 → Representation)
리소스 조회
서버에 전달하고 싶은 데이터는 query(파라미터, 스트링)를 통해서 전달
메시지 바디를 전달할 수 있지만(ㅇ0ㅇ), 지원하지 않는 곳이 많아서 권장하지 않음
요청 데이터 처리
메시지 바디를 통해 서버로 요청 데이터 전달
리소스를 대체 (있으면 대체, 없으면 생성)
클라이언트가 리소스의 위치를 알고 URI를 지정함 (ex. seq)
리소스를 완전히 대체한다
값이 없는 컬럼은 지워버리는..
진짜 새 거로 대체한다고 생각하면 된다
리소스 부분 변경
PUT이 앞뒤 안 보고 대체해버려서 생긴 메서드
변경된 부분만 변경한다
→ 근데 이게 지원이 안 되는 서버도 있다 (그런 경우에는 POST..)
리소스 제거 ㅎ
호출해도 리소스를 변경하지 않는다
(GET은 조회만 하기 때문에 안전함, 그 외는 안전하지 않음)
f(f(x)) = f(x) 이게 먼 소리고?
한 번 호출하든 두 번 호출하든 100번 호출하든 결과가 똑같다.
자동 복구 매커니즘… 이런 곳에 쓰인댑니다.
DELETE 호출했는데 응답이 없으면? 또 호출해 → 괜찮음! 멱등하니까
HTML 폼 태그에 action, post 넣고 input에 name 설정하고 넣고 싶은 변수 세팅
→ Content-Type을 application/x-www-form-urlencoded로 보냄, messageBody에 값을 넣고 쿼리 파라미터 형식으로 전달함
(데이터를 인코딩 해서..)
쿼리 파라미터에 값을 넣고 전달함 (근데 get은 조회에만 사용하니까 저장에는 사용하지 맙시다)
만약 파일도 같이 전송한다면? (다른 종류의 데이터를 같이 전송해야 할 때)
form의 enctype에 multipart/form-data라고 넣는다
그러면 Content-Type이 multipart/form-data로 들어가고 알아서 데이터를 경계로 잘라줘서 들어감
서버 to 서버, 앱 클라이언트, 웹 클라이언트
Content-Type: application/json을 주로 사용 (사실상 표준)
API 설계 - POST 기반 등록
등록할 거야, 라고 클라이언트가 데이터를 보낼 때까지 ‘리소스 식별자’를 알지 못 한다.
서버가 등록을 한 뒤에야 식별자가 생기고 리소스 URI를 생성한다.
→ 이런 형식을 컬렉션 (Collection) 이라고 부른다. (서버가 리소스 URI 생성하는 거)
API 설계 - PUT 기반 등록
파일 업로드 시, 기존 파일을 지우고 다시 올려야 하니까 PUT 사용
클라이언트가 어떤 파일을 올릴지 아니까 URI를 알고 있음
은 노션에...
클라이언트가 보낸 요청의 처리 상태를 응답에서 알려주는 기능
요청 성공
클라이언트의 요청 성공해서 새로운 리소스가 생성됨
(생성된 리소스는 응답의 Location 헤더 필드로 식별~)
요청이 접수되었으나 처리가 완료되지 않았음
ex. 배치, 요청 접수 후 1시간 뒤에 배치 프로세스가 요청을 처리함
서버가 요청을 성공적으로 수행했지만, 응답 페이로드 본문에 보낼 데이터가 없음
요청을 완료하기 위해 유저 에이전트(웹 브라우저)의 추가 조치 필요
웹 브라우저는 3xx 응답의 결과에 Location 헤더가 있으면, Location 위치로 자동 이동 (=리다이렉트)
리소스의 URI가 영구적으로 이동
원래의 URI를 사용 X, 검색 엔진 등에서도 변경 인지
리소스의 URI가 일시적으로 변경
검색 엔진에서 URL을 변경하면 안 됨
POST로 주문 후에 웹 브라우저에서 새로고침을 하면? → 중복 주문이 될 수 있다.
304 Not Modified
클라이언트의 요청에 잘못된 문법으로 서버가 요청을 수행할 수 없음
오류의 원인이 클라이언트에게 있음
이미 잘못된 요청을 보냈기에 똑같은 재시도가 실패함
클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음
인증되지 않음
서버가 요청을 이해했지만 승인을 거부함
인증 자격 증명은 있지만, 접근 권한이 불충분한 경우
요청 리소스를 찾을 수 없음
또는 해당 리소스를 숨기고 싶을 때
서버 문제로 오류 발생
재시도 하면 성공할 수도 있음 (복구 됐을 때)
서버 내부 문제로 오류 발생, 애매하면 다 500
서비스 이용 불가
서버가 일시적인 과부하 또는 예정된 작업으로 잠시 요청을 처리할 수 없음
Retry-After 헤더 필드로 얼마 뒤에 복구되는지 보낼 수도 있음
header-field = field-name “:” OWS field-value OWS (OWS: 띄어쓰기 허용)
(field-name은 대소문자 구분 없음)
Content-Type: 표현 데이터의 형식
Content-Encoding: 표현 데이터의 압축 방식
Content-Language: 표현 데이터의 자연 언어
Content-Length: 표현 데이터의 길이
클라이언트가 선호하는 표현 요청
Accept: 클라이언트가 선호하는 미디어 타입 전달
Accept-Charset: 클라이언트가 선호하는 문자 인코딩
Accept-Language: 클라이언트가 선호하는 자연 언어
유저 에이전트의 이메일 정보
이전 웹 페이지 주소
유저 에이전트 애플리케이션 정보 (클라이언트의 정보, 웹 브라우저 정보 등)
통계 정보
어떤 종류의 브라우저에서 장애가 발생하는지 파악 가능
요청을 처리하는 ORIGIN 서버의 소프트웨어 정보
요청을 할 때 여러 프록시 서버를 거치게 돼서 나의 요청이 있는 마지막 서버 정보를 갖고 있음
메시지가 발생한 날짜와 시간
요청한 호스트 정보 (도메인)
요청에서 사용, 필수 값!!
하나의 서버가 여러 도메인을 처리해야 할 때
페이지 리다이렉션
허용 가능한 HTTP 메서드
405 (Method Not Allowed)로 응답할 때 사용 가능한 메서드를 적어서 응답 한다
유저 에이전트가 다음 요청을 하기까지 기다려야 하는 시간
503 (Service Unavailable): 서비스가 언제까지 불능인지 알려줄 수 있음
클라이언트 인증 정보를 서버에 전달
인증 매커니즘에 관계 없이 그 값을 헤더에 넣어주면 된다
리소스 접근 시 필요한 인증 방법 정의
401 Unauthorized 응답과 함께 사용
내가 만든 쿠키~
두 개의 헤더를 사용함
Set-Cookie: 서버에서 클라이언트로 쿠키 전달 (응답)
Cookie: 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청 시 서버로 전달
로그인을 했으면, 서버에서 클라이언트에 응답 헤더에 Set-Cookie: user=홍길동 과 같이 보낸다
그럼 클라이언트는 쿠키 저장소에 user=홍길동 을 저장한다
그리고 로그인 이후 다른 페이지에 접근하면 서버에 요청을 보낼 때마다 자동으로 쿠키 저장소를 뒤져서 쿠키로 헤더를 만들어서 요청한다.
Set-Cookie: expires=어쩌고(날짜)
만료일을 넣어준다, 만료일 되면 쿠키 삭제
Set-Cookie: max-age=어쩌고 (초 단위)
0이나 음수를 지정하면 쿠키 삭제
ex) domain=example.org
방법
ex) path=/home
이 경로를 포함한 하위 경로 페이지만 쿠키 접근 가능
일반적으로 path=/ 루트로 지정
계속해서 같은 요청을 반복할 때 캐시가 없으면 네트워크를 통해서 데이터를 다운로드 받아야 하고 네트워크는 느리고 비싸다
헤더에 cache-control: max-age=60를 넣어준다
(캐시가 유용한 시간, 60초 간은 캐시가 유효하다)
→ 웹 브라우저에서 응답 결과를 캐시에 저장한다
→ 두 번째 요청 시 캐시를 먼저 뒤지고, 아직 유효하다면 캐시에서 값을 가져온다
다시 요청한다
요청 결과를 다시 웹 브라우저에 저장한다
Last-modified: 2020년 11월 10일 10:00:00
이 데이터가 마지막으로 수정된 시간을 가져온다
→ 응답 결과를 캐시에 저장하면서 데이터 최종 수정일을 갖고 있는다
요청을 보낼 때 Last-modified가 존재하면, if-modified-since: 날짜를 추가한다
해당 데이터의 최종 수정일을 같이 보내는 것
→ 서버에서 요청을 받고 서버 데이터와 요청된 데이터의 최종 수정일을 검증한다
변경된 게 없다고 리턴
HTTP Body가 없음 (응답이 매우 가벼워짐)
캐시 데이터와 서버 데이터가 같은지 검증하는 데이터 (Last-modified, ETag)
검증 헤더로 조건에 따른 분기
If-Modified-SInce: Last-modified
If-None-Match: ETag
1초 미만 단위로 캐시 조정 불가능
날짜 기반의 로직 사용
Entity Tag
Cache-Control: 캐시 제어
Pragma: 캐시 제어
Expires: 캐시 제어
멀리 있는 원 서버에서 데이터를 가져오려면 한참 걸리잖아요?
그걸 모두가 원한다면 각자 계속 기다려야 하고 응답이 느리니까..
한국 어딘가에 있는 프록시 캐시 서버를 거쳐서 요청이 오도록 한다.
확실한 캐시 무효화 응답
이 페이지는 진짜 캐시 되면 안 돼!! 라고 한다면 이렇게 넣어준다
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache