http?
웹 브라우저와 웹 서버가 통신하는 절차와 형식을 규정한 것.
특징
- 확장이 쉽다
- 헤더에 언제든 새로운 기능을 추가할 수 있음
- 서버 개발자와 클라이언트 개발자가 합의만 하면 커스텀 헤더를 통해 필요한 정보를 주고 받을 수 있음
- Connectionless
- 클라이언트와 서버가 연결 후 한번 요청과 응답을 주고 받으면 연결을 지속하지 않는다.
- HTTP는 불특정 다수와의 통신을 위해 만들어졌기 때문에 다수의 클라이언트와의 연결을 서버가 계속 유지하는 것은 부담이 되기 때문.
- 그러나 동일한 클라이언트의 모든 요청마다 매번 새 연결을 시도, 해제해야하므로 비효율적이다
- 요청헤더에 Connection: keep-alive 속성 사용!
- http 1.0에서는 ‘Connection: close’가 디폴트
- http 1.1에서는 ‘Connection: keep-alive’가 디폴트
- Stateless
- 서버는 클라이언트의 상태를 유지하지 않습니다.
- 누가 언제 요청을 하더라도 요청이 같으면 결과가 같음.
- 즉, 각 클라이언트에 맞는 리소스를 선별적으로 보내주는 것은 기본적으로 불가능함.
- 이를 해결하기 위해 쿠키, 세션, 토큰등을 사용. (쿠키도 헤더를 바탕으로 만들어짐.)
HTTP 메시지
HTTP 메시지 구조 |
---|
start-line 시작 라인 |
header 헤더 |
empty line 공백 라인 |
body |
<StartLine> GET /index.html HTTP/1.1 </StartLine>
<Header>
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/91.0.4472.124 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
</Header>
**<Blank>**
<Body>
</Body>
StartLine
StartLine은 꼭 한 줄로만 작성되어야 한다. 절대 두 줄은 안 됨!
-
요청 메시지
- HTTP 메서드 : 서버가 수행해야 할 동작 지정
- 요청 대상 : 절대경로 = "/" 로 시작하는 경로
- HTTP 버전
-
응답 메시지
- HTTP 버전
- HTTP 상태 코드
- 상태에 대한 문구 : 사람이 이해할 수 있는 상태 코드 설명 글
- HTTP 전송에 필요한 모든 부가정보
- 위의 예시를 보면 Host, User-Agent, Accept 등등
- Header에 들어갈 수 있는 정보는 수 백개 정도는 되고 그것들에 대한 정보는 여기서 참조
- 중요한 헤더 몇 개만 예시:
- Authorization: 사용자 인증 토큰, credentials 등을 전달. 주로 JWT 같은 걸 전달할 때 사용
- Cookie: 클라이언트 로컬(메모리 혹은 보조기억장치)에 저장되는 키-값 형태의 데이터 파일
- Accept: 요청을 보낸 클라이언트가 받을 수 있는 컨텐츠의 타입을 명시
- Accept-Language: 선호 언어를 우선순위에 따라 지정
- Origin: 요청이 시작된 출처
- User-Agent: 클라이언트 정보
- Content-length: 응답 길이
- Content-Type: 응답 타입
Body
- 실제 전송할 데이터
- ex) HTML 문서, 이미지, 영상, JSON 등등
속도와 효율을 위한 노력들
캐시 (1.0)
- 문제:
- 접속할 때마다 파일을 다시 다운로드 하는 것은 부담
- Contents가 바뀌지 않았다면 로컬에 저장된 것을 재사용하고 싶다
- 해결 방안
- Http1.0 시절에는 정적 컨텐츠 위주. → 콘텐츠가 갱신 됐는지만 체크하면 됨!
- 작동 방식:
- 서버가 응답을 할 때 헤더에 Last-Modified에 수정 일시를 기록해서 응답함 (클라이언트는 캐시)
- 다음에 클라이언트가 재요청을 하면 last-modified에 있던 기록을 바탕으로 If-Modified-Since를 붙여서 요청함
- 만약 바뀐 것이 없다면 304 (Not modified)로 응답
- 바뀌었다면 200 응답, 다시 캐시
- 다시 문제 및 해결 방안:
- 근데 이러면 요청은 어쨌든 보내야 되잖아!
- 그래서 Expires라는 헤더도 있습니다!
- Expires 헤더에 설정된 기한 내라면 아예 서버에 요청을 보내지 않는다
캐시 (1.1)
- 문제:
- 근데 단순히 last-modified나 expires처럼 날짜, 시간으로만 캐시를 컨트롤하기는 어려워
- 데이터가 복잡해지면서 캐시 여부가 단순 시간에 종속적이지 않기 때문
- 해결 방안:
- 파일 자체의 해시 값을 비교하기로 함 → Etag
- Etag: 리소스의 특정 버전에 대한 고유한 식별자
- 작동 방식:
- 서버는 리소스에 대한 ETag 값을 생성 (보통 컨텐츠의 해시값)
- 클라이언트는 요청 시 'If-None-Match' 헤더에 가지고 있는 ETag 값을 포함
- 서버는 현재 리소스의 ETag와 비교하여 변경 여부를 판단
- 일치하면 304 Not Modified 응답, 불일치하면 200 OK와 새로운 컨텐츠 전송
max-age
: 캐시의 유효 시간
no-cache
: 캐시는 저장하지만 항상 서버에 재검증
no-store
: 캐시를 저장하지 않음
private
: 브라우저만 캐시 가능
public
: 중간 프록시도 캐시 가능
캐시 (2)
- 문제:
- http 1.0에는 요청에 대한 응답을 받아야 다음 요청을 할 수 있었음
- Http 1.1로 들어오면서 pipelining이라는 기법을 사용해 요청을 연속적으로 보내 지연시간을 단축
- 그러나 여전히 앞에 응답이 오래걸리면 뒤의 응답도 같이 지연되는 문제가 있었음
- HOLB (Head Of Line Blocking)라고 부르는 문제
- 해결 방안
- 파이프라이닝은 http2에서 스트림이라는 구조로 다시 태어남!
- 1.1까지 메시지를 기반으로 통신하던 것에 프레임이라는 새로운 단위가 추가되었음
- 순차적인 처리가 필수적이었던 1.1에 반해 2는 순서가 상관없게 되었다고 함