multipart form data
http의 단순 전송은 크게 신경쓸게 없다.
보통 http 응답은 한 번에 한 파일씩 반환하므로 경계를 신경쓸 필요가 없다. 그런데 multipart를 이용하는 경우 한 번의 요청에 여러 파일을 전송할 수 있으므로 받는 쪽에서 파일을 나눠야 한다.
예시
Content-Type: multipart/form-data; boundary=boundary-example
--boundary-example
Content-Disposition: form-data; name="text"
Sample text part.
--boundary-example
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
This is the content of the file.
--boundary-example--
Content-Type은 multipart/form-data.
boundary에는 랜덤하게 생성된 경계 문자열이 들어간다.
body는 이 경계 문자열로 블록이 나뉜다.
- 경계 문자열에 –이 prefix로 붙는다
각각의 블록 내부도 http와 같이 header 후에 빈 줄 그리고 body로 이루어진다.
그리고 파일의 마지막도 경계 문자열로 끝난다.
- 마지막 경계 문자열은 prefix와 postfix 모두 –를 붙여 끝낸다.
content negotiation
통신 방법을 최적화하고자 하나의 요청 안에 서버와 클라이언트가 서로 최고의 설정을 공유하는 시스템을 말한다.
이를 위해 header를 이용한다.
| request header | response header | negotiation target |
|---|---|---|
| Accept | Content-Type | mime type |
| Accept-Language | Content-Language | 표시 언어 |
| Accept-Charset | Content-Type | 문자의 문자셋 |
| Accept-Encoding | Content-Encoding | body 압축 |
파일 종류 결정
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
서버는 요청에서 요구한 형식 중에서 파일을 반환한다.
우선 순위를 해서개 위에서부터 차례로 지원하는 포맷을 찾고 일치하면 그 포맷을 반환한다.
서로 일치하는 요청이 없다면 서버가 406 Not Acceptable 오류를 반환한다.
표시 언어
Accept-Language: en-US,en;q=0.8,ko;q=0.6
동일하게 적혀있는 우선순위로 요청을 보낸다. en-Us, en, ko 순
문자셋
Accept-Charset: windows-949,utf-8;q=0.7,*;q=0.3
현대의 어떤 브라우저도 Accept-Charset을 송신하지 않는다.
브라우저가 문자셋 인코더를 내장하고 있어 미리 negotiation 할 필요가 없어졌기 때문으로 여겨진다.
문자셋은 mime type과 함께 Content-Type header에 실려 응답된다.
Content-Type: text/html; charset=UTF-8
압축
압축은 전송속도 향상을 위한 것이다.
통신에 걸리는 시간보다 압축과 해제가 짧은 시간에 이루어짐으로 압축을 함으로써 웹페이지를 표시할 때 걸리는 전체적인 처리 시간을 줄일 수 있다.
통신량이 줄어들기 때문에 시간 뿐만 아니라 통신 비용(사용자 데이터, 서버 데이터, 전력 소비 등)도 줄어든다.
Accept-Encoding: deflate, gzip
이렇게 header를 보내면 전송받은 목록 중 지원하는 방식이 있으면 응답할 때 그 방식으로 압축된 콘텐츠를 반환한다.
서버가 gzip을 지원한다면 헤더는 이렇다.
Content-Encoding: gzip
이제는 서버에서 받아올 때 뿐 아니라 서버로 올릴 때도 압축을 사용한다.
목적은 압축을 통해 얻는 이득을 업로드에도 누리기 위해서.
이런 경우엔 동일하게 Content-Encoding을 클라이언트가 header에 싣어서 보낸다.
쿠키
쿠키랑 웹사이트의 정보를 브라우저 쪽에 저장하는 작은 파일이다.
쿠키도 http header를 기반으로 구현되었다.
예를 들면 서버에서 access 시간을 저장하기 위해 이렇게 header를 보낼 수 있다.
Set-Cookie: LAST_ACCESS_DATE=Jun/18/2023
Set-Cookie: LAST_ACCESS_TIME=12:04
이러면 클라이언트가 이 값을 저장하고 다음에 방문할 때 서버에 이런 형식으로 보낸다.
Cookie: LAST_ACCESS_DATE=Jun/18/2023
Cookie: LAST_ACCESS_TIME=12:04
이렇게 서버가 상태를 유지하는 stateful처럼 보이게 서비스를 제공할 수 있다.
쿠키를 사용할 때 주의할점
- 영속성 문제가 있다. 데이터는 사라진다.
- 따라서 db 대신 쓸 수 없다.
- 용량 문제도 있다. 최대 4KB로 더 보낼 수 없다.
- 쿠키는 header로 항상 통신되는데 쿠키가 많아지면 통신량이 늘고 성능이 떨어진다.
- 사용자가 자유롭게 접근할 수 있고 수정도할 수 있어 민감정보는 넣으면 안된다.
- http의 경우 평문으로 전송되서 유의할 필요가 있다.
쿠키는 몇 가지 속성(Expires, Max-Age, Domain, Path, Secure, …)으로 제어하고 제한할 수 있다.
쿠키의 이런 기능 추가 역사는 웹 보안의 역사와도 이어진다고.
캐시
콘텐츠가 변경되지 않았을 때 로컬에 저장된 파일을 재사용함으로써 다운로드 횟수를 줄이고 성능을 높이는 매커니즘.
GET / HEAD method 이외는 기본적으로 캐시되지 않는다.
캐시의 방식
- 갱신 일자에 따른 캐시
- 서버가 데이터 수정 시간을 헤더로 주고, 클라이언트가 다음 요청에 이를 헤더에 넣는다.
- 수정이 생겼으면 200에 새로운 데이터를
- 수정이 생기지 않았으면 304 Not Modified를 반환
- expries
- 갱신 일자에 따른 캐시는 서버에 콜을 한번 더 넣어야 한다.
- expires에 날짜와 시간을 넣어서 지정한 기간 내에선 캐시를 강제로 사용한다.
- ETag
- 날짜와 시간을 이용한 캐시 비교만으로 해결할 수 없을 때
- 동적으로 바뀌는 요소가 많아져서 캐시 유효성 판단이 어려워질 때
- ETag(Entity Tag)는 순차적 갱신 일시가 아니라 파일의 해시 값으로 비교한다.
- 갱신 일자에 따른 캐시처럼 ETag를 받고 다음 요청에 받은 ETag를 넣어 보낸다.
- 서버에서 비교해서 같다면 304 Not Modified를 반환
- ETag는 http 1.1의 기능이다.
캐시는 Cache-Control header로 유연하게 캐시 제어를 지시할 수 있다.
- 서버에서 내리는 값들도 있고, 클라이언트에서 요청할 때 사용할 수 있는 값들도 있다.
- Cache-Control도 http 1.1의 기능이다.
reference
- Real World Http chapter 2