김영한님의 모든 개발자를 위한 HTTP 웹 기본 지식 강의를 듣고 정리한 내용입니다.
https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC
HTTP 헤더 - 캐시와 조건부 요청
캐시 기본 동작
캐시가 없을 때
클라이언트에서 star.jpg를 요청하면 서버에서는 응답을 내려준다. HTTP 헤더와 관련된 용량이 0.1메가, HTTP 바디와 관련된 용량이 1.0메가라고 했을 때, 1.1메가의 네트워크를 차지하면서 보내진다.
만약 똑같은 요청을 또 보낸다면 아까와 똑같이 1.1M를 보내게 된다.
캐시가 없다면 데이터가 변경되지 않아도 계속 네트워크를 통해 데이터를 다운로드 받아야 한다.
인터넷 네트워크는 매우 느리고 비싸기 때문에 이 방법은 비효율적이다.
데이터를 매번 새로 받아와야 하기 때문에 브라우저 로딩 속도가 느려져 느린 사용자 경험을 선사하게 된다.
캐시 적용
서버에서 캐시가 유효한 시간인 cache-control을 http 헤더에 넣어줄 수 있다. max-age=60이라고 하면 60초 동안은 캐시가 유효하다는 뜻이다.
웹 브라우저에는 내부에 캐시를 저장하는 저장소가 있다. 서버에서 받은 응답 데이터를 이용해 유효 시간 60초와 응답 결과를 캐시에 저장한다.
두 번째로 같은 요청을 했을 때는 먼저 브라우저의 캐시를 확인해 유효 시간을 검증한다. 60초가 유효하고 아직 60초가 지나지 않았다면 네트워크를 탈 필요없이 바로 캐시에서 데이터를 가져오면 된다.
캐시를 적용하면 캐시 덕분에 캐시 가능 시간동안 네트워크를 사용하지 않아도 된다. 이는 비싼 네트워크 사용량을 줄일 수 있고, 브라우저의 로딩 속도가 매우 빨라진다.
그렇다면 캐시의 시간이 초과되었을 때는 어떻게 동작할까?
똑같은 요청이 발생했을 때 브라우저는 먼저 캐시의 유효 시간을 검증한다. 캐시의 유효 시간이 초과되었다면 서버를 통해 데이터를 다시 조회하고, 캐시를 갱신한다. (이 때 다시 네트워크 다운로드가 발생한다.)
하지만 만약 클라이언트가 캐시에 가진 데이터와 서버가 데이터베이스에 가지고 있는 데이터가 같다고 하면 굳이 캐시 시간이 초과되었다고 해서 다시 다운로드를 받을 필요는 없을 것이다. -> 해결하려면 검증 헤더와 조건부 요청을 사용하면 된다.
검증 헤더와 조건부 요청
캐시의 유효 시간이 초과해서 서버에 다시 요청하면 두 가지 상황이 나타난다.
- 서버에서 기존 데이터를 변경함
- 서버에서 기존 데이터를 변경하지 않음
캐시가 만료된 후에도 서버에서 데이터를 변경하지 않은 경우, 동일한 요청이 발생했을 때 데이터를 다시 전송하는 대신 저장해 두었던 캐시를 재사용하도록 할 수도 있다.
단, 클라이언트의 데이터와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법이 필요하다.
검증 헤더, 조건부 요청 추가
서버에서 응답을 할 때 해당 데이터가 마지막으로 수정된 시간을 Last-Modified 헤더로 보내준다.
브라우저는 응답 결과인 유효 시간과 데이터, 최종 수정일을 캐시에 기록한다.
만약 캐시의 유효 시간이 초과되었을 경우, 서버로 요청을 보낼 때 if-modified-since 헤더에 캐시에 저장되어 있던 최종 수정일을 함께 보낸다.
서버에서는 데이터베이스에 있는 최종 수정일과 요청으로 온 최종 수정일을 비교해 데이터가 수정되었는지 여부를 검증할 수 있다.
만약 변경되지 않았다면 서버에서는 304 Not Modified를 내보낸다. 수정된 내용이 없기 때문에 HTTP Body부분이 없이 응답 메시지를 보낸다. (바디 부분을 빼고 보내기 때문에 네트워크 부하가 줄어든다.)
클라이언트는 캐시가 안바뀌었기 때문에 써도 되겠구나 하고 캐시의 유효시간을 갱신하고 캐시를 다시 세팅한다. 그리고 이 캐시에서 데이터를 불러와서 쓴다.
검증 헤더, 조건부 요청 헤더
검증 헤더는 캐시 데이터와 서버 데이터가 같은 지 검증하는 데이터이다.
Last-Modified와 Etag가 있다.
조건부 요청 헤더는 검증 헤더로 조건에 따른 분기를 서버에 요청을 한다.
If-Modified-Since와 Last-Modified를, If-None-Match와 ETag를 함께 사용한다.
조건이 만족하면 200 OK, 만족하지 않으면 304 Not Modified를 응답한다.
Last-Modified, If-Modified-Since
If-Modified-Since의 날짜 이후에
- 데이터가 변경되지 않았을 경우
- 304 Not Modified. 헤더 데이터만 전송(BODY 미포함)
- 전송 용량 0.1M(헤더 0.1M, 바디 1.0M일 때)
- 데이터가 변경되었을 경우
- 200 OK. 모든 데이터 전송(BODY 포함)
- 전송 용량 1.1M(헤더 0.1M, 바디 1.0M일 때)
단점
- 1초 미만(0.x초) 단위로 캐시 조정이 불가능
- 날짜 기반의 로직 사용
- 데이터를 수정해서 날짜가 다르지만 같은 데이터를 수정해서 데이터 결과가 똑같은 경우에도 다시 데이터를 받게 됨(만약 a->b로 변경 후 다시 b->a로 변경해 기존의 데이터와 똑같지만 날짜가 변경된 경우)
ETag, If-None-Match
서버에서 별도의 캐시 로직을 관리하고 싶은 경우에 사용할 수 있다.
ETag(Entity Tag)를 사용하면 캐시용 데이터에 임의의 고유한 이름을 달아둘 수 있고, 데이터가 변경되면 이 이름을 바꾸어서 변경하면 된다. (버전을 지정하거나 파일을 해시 알고리즘에 넣은 결과를 지정. 해시는 파일이 동일하면 똑같은 결과가 나온다.)
eg) ETag: "v1.0", ETag: "a2jiodwjekjl3"
단순하게 ETag만 보내서 같으면 유지, 다르면 다시 받도록 할 수 있다.
서버에서 응답 데이터, cache-control과 ETag를 보내면, 클라이언트는 이를 캐시에 저장한다.
캐시의 시간이 초과되었다면 클라이언트에서 서버로 다시 요청을 보내면서 If-None-Match 헤더에 캐시가 가지고 있는 ETag를 함께 보낸다.
클라이언트에서 온 ETag와 서버에 있는 ETag를 비교해 같다면 304 Not Modified를 보낸다(Body 미포함). 클라이언트는 캐시의 유효시간을 갱신해 캐시를 다시 세팅한다. 그리고 이 캐시에서 데이터를 불러와서 쓴다.
ETag와 If-None-Match를 사용하면 캐시 제어 로직을 서버에서 완전히 관리할 수 있다. 클라이언트는 캐시 메커니즘을 알 필요없이 단순히 이 값을 서버에 제공만 하면 된다.
캐시와 조건부 요청 헤더
캐시 제어 헤더
- Cache-Control
- Pragma
- Expires
Cache-Control
- Cache-Control: max-age
- 캐시 유효 시간. 초 단위. (보통 길게 잡는다)
- Cache-Control: no-cache
- 데이터는 캐시해도 되지만, 항상 오리진 서버에 If-Modified-Since나 If-Not-Match 등 조건부 요청을 해서 로컬에 있는 데이터가 바뀌었는지 검증하고 사용하라는 의미.
- Cache-Control: no-store
- 데이터에 민감한 정보가 있으므로 저장하면 안됨(메모리에서 사용하고 최대한 빨리 삭제)
Pragma
- Pragma: no-cache
HTTP 1.0 하위 호환이기 때문에 거의 사용하지 않는다.
Expires
- expires: Mon, 01 Jan 1990 00:00:00 GMT
캐시 만료일을 정확한 날짜로 지정한다(날짜로 지정하는 것이 더 편해보이지만 사실 초 단위로 하는 것이 훨씬 유연하게 지정할 수 있다.).
HTTP 1.0부터 사용했기 때문에 지금은 더 유연한 Cache-Control: max-age를 권장한다.
Cache-Control: max-age와 함께 사용하면 Expires는 무시된다.
검증 헤더와 조건부 요청 헤더
- 검증 헤더(Validator)
- ETag
- Last-Modified
- 조건부 요청 헤더
- If-Match, If-None-Match: ETag값 사용
- If-Modified-Since, If-Unmodified-Since: Last-Modified 값 사용
'TIL' 카테고리의 다른 글
[TIL] 22.12.28 JWT 토큰 (0) | 2022.12.28 |
---|---|
[TIL] 22.12.23 HTTP 헤더 - 캐시와 조건부 요청(2) (0) | 2022.12.23 |
[TIL] 22.12.18 HTTP 헤더 - 일반 헤더(2) (0) | 2022.12.19 |
[TIL] 22.12.17 HTTP 헤더 - 일반 헤더(1) (1) | 2022.12.18 |
[TIL] 22.12.15 HTTP 메서드 활용, 상태코드 (0) | 2022.12.16 |