BackEnd/WEB

[HTTP] HTTP 헤더 - 캐시와 조건부 요청

샤아이인 2022. 2. 3.

내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 상세히 기록하고 얕은부분들은 가겹게 포스팅 하겠습니다.

 

 

1. 캐시 기본 동작

캐시가 없다면 Client가 요청할때마다 같은 데이터를 계속 새롭게 다운받게 된다는것 정도는 모두 아는내용이다.

- 데이터가 변경되지 않아도 계속 네트워크를 통해서 데이터를 다운로드 받아야 한다.

- 인터넷 네트워크는 매우 느리고 비싸다.

- 브라우저 로딩 속도가 느리다.

- 느린 사용자 경험

 

이번에는 캐시를 적용시켜 보자!

 

첫 요청은 다음과 같다. 그림을 살펴보자.

출처 - 인프런 김영한 HTTP
GET메서드로 /star.jpg로 요청을 하면 서버에서 캐시의 유효 시간을 설정하고, star.jpg와 함께 보내주게 된다.

이를 받은 클라이언트는 브라우저 캐시에 받은 이미지를 캐싱 해두게 된다.

 

두번째 /star.jpg로 요청을 보낸다면, 이번에는 서버가 아닌 브라우저 자신의 캐시부터 확인하여 같은 데이터가 있다면 이를 사용하게 된다.

- 캐시 덕분에 캐시 가능 시간동안 네트워크를 사용하지 않아도 된다.

- 비싼 네트워크 사용량을 줄일 수 있다.

- 브라우저 로딩 속도가 매우 빠르다.

- 빠른 사용자 경험

 

세번째 요청 - 캐시 시간이 초과되었을때

캐시 시간이 초과된 상태에서 이전과 같이 /star.jpg 요청을 한다면 서버에서 새롭게 받아와 캐싱을하게된다.

새롭게 네트워크를 통해 다운하는 것 이다.

 

하지만 뭔가 아쉽다. 비록 캐쉬의 시간이 다 됬지만, 서버의 star.jpg가 기존과 달라진점이 없다면 이를 다시 다운받기는 아쉬운것이 사실이다.

시간이 다되긴 했지만 기존에 갖고있던 캐시를 이용하여 이러한 점을 극복할수 없을까?

 

다음 글에서 알아가 보자!

 

2. 검증 헤더와 조건부 요청1

캐시 유효시간이 다해서 서버에 재요청 하게되면 2가지 상황이 가능하다.

1. 서버에서 기존 데이터를 변경했기때문에 바뀐 데이터를 받게됨

2. 서버에서 기존 데이터를 변경하지 않았기 때문에 기존 데이터와 동일

 

여기서 2번 상황이라고 해보자.

- 캐시 만료후에도 서버에서 데이터를 변경하지 않았다.

- 생각해보면 데이터를 전송하는 대신에 저장해 두었던 캐시를 재사용 할 수 있으면 가장좋다.

- 단 클라이언트의 데이터와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법 필요하다!

 

● 검증 헤더 추가

첫번째 /star.jpg 를 요청했을때 서버에서 이를 받아 알맞은 데이터를 반환해준다.

이때 응답 헤더 정보에 Last-Modified 정보(검증 헤더)를 추가한다. 다음 그림을 확인해보자.

출처 - 인프런 김영한 HTTP

헤더에 검증 헤더부분이 추가되어 클라이언트에게 전달된다.

이를 저장하면 다음과 같은 상황이 될것이다.

출처 - 인프런 김영한 HTTP

최종적으로 브라우저 캐시에 저장되게 되며, 데이터 최종 수정일 또한 알고있다.

 

두번째 요청 - 캐시 시간 초과 상태

두번째 /star.jpg 를 요청하는데 캐시가 시간초과된 상황이다.

이럴때는 다시 서버에 요청하여 다시 받아와야 한다. 하지만 이때 이전과 달리 if-modified-since 헤더정보(조건부 요청)를 추가하여 보낸다.

출처 - 인프런 김영한 HTTP

자신이 알고있는 데이터의 최종 수정일에 대한 정보를 포함시켜서 request를 보낸다.

 

서버와 확인해본 결과 서버쪽 데이터도 변경되지 않았음을 알수있었다. 다음 그림을 살펴보자.

출처 - 인프런 김영한 HTTP

 

이럴때 서버에서는 다음과 같이 304 Not Modified로 응답을 해주면 끝이다.

또한 이 response에는 HTTP body 가 없다! 응답정보의 용량이 0.1M까지 줄은걸을 알수있다. 다음 그림이 이를 보여준다.

출처 - 인프런 김영한 HTTP

 

304 Not Modified 를 받은 클라이언트는 기존의 캐시되있던 star.jpg를 갱신하게되고, 이를 재사용 하면 된다.

 

- 캐시 유효 시간이 초과해도, 서버의 데이터가 갱신되지 않으면 304 Not Modified + 헤더 메타 정보만 응답(바디X)한다.

- 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신

- 클라이언트는 캐시에 저장되어 있는 데이터 재활용

- 결과적으로 네트워크 다운로드가 발생하지만 용량이 적은 헤더 정보만 다운로드하게되니 매우 실용적인 해결책이다.

 

3. 검증 헤더와 조건부 요청2

● 검증 헤더

- 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터이다.

- 종류는 Last-Modified 와 ETag 가 있다.

 

● 조건부 요청 헤더

- 검증 헤더로 조건에 따른 분기를 할수있다.

If-Modified-Since: Last-Modified 사용

If-None-Match: ETag 사용

- 조건이 만족하면 200 OK

- 조건이 만족하지 않으면 304 Not

 

예를 들어 If-Modified-Since(변경 사항이 있습니까?) 로 요청했을때 변경 사항이 있다면 긍정문인 200 OK를, 변경 사항이 없다면 304를 보낸다.

 

● If-Modified-Since: 이후에 데이터가 수정되었으면?

- 데이터 미변경 예시

캐시: 2020년 11월 10일 10:00:00 vs 서버: 2020년 11월 10일 10:00:00 (최종 수정날이 같음 => 변경사항 없다는 말)

304 Not Modified, 헤더 데이터만 전송(BODY 미포함)

전송 용량 0.1M (헤더 0.1M, 바디 1.0M)

 

- 데이터 변경 예시

캐시: 2020년 11월 10일 10:00:00 vs 서버: 2020년 11월 10일 11:00:00 (변경 사항 생김)

200 OK, 모든 데이터 전송(BODY 포함)

전송 용량 1.1M (헤더 0.1M, 바디 1.0M)

 

● Last-Modified, If-Modified-Since 단점

- 1초 미만(0.x초) 단위로 캐시 조정이 불가능

날짜 기반의 로직 사용

- 데이터를 수정해서 날짜가 다르지만, A 데이터를 B로 수정했다가 다시 A로 바꿔서, 최종적으로 결과가 똑같은 경우

- 서버에서 별도의 캐시 로직을 관리하고 싶은 경우

예) 스페이스나 주석처럼 크게 영향이 없는 변경에서 캐시를 유지하고 싶은 경우

 

이러한 단점을 보완하고 사용하는것이 바로 ETag 이다!

 

● ETag, If-None-Match

- ETag(Entity Tag)

 

- 캐시용 데이터에 임의의 고유한 버전 이름을 달아둔다. 또는 데이터를 기반한 해쉬값으로도 설정할수 있다.

예) ETag: "v1.0", ETag: "a2jiodwjekjl3"

 

- 데이터가 변경되면 이 이름을 바꾸어서 변경함 (Hash함수를 통해서 다시 생성한다)

예) ETag: "aaaaa" -> ETag: "bbbbb"

 

- 진짜 단순하게 ETag만 보내서 같으면 유지, 다르면 다시 받기! 다음 그림을 살펴보자.

출처 - 인프런 김영한 HTTP

위의 사진처럼 ETag값이 변경되지 않았다면 서버가 304를 보내고, 이를 받은 클라이언트는 기존의 캐시를 재사용 하면 된다.

 

캐시의 제어 로직을 서버에서 완전하게 관리하게 되었다!

클라이언트는 단순히 이 값을 서버에 검증용으로 제공하면 된다. (클라이언트는 캐시 메커니즘을 모름)

 

4. 캐시와 조건부 요청 헤더

캐시 제어 헤더는 총 3가지가 있다.

1. Cache-Control: 캐시 제어

2. Pragma: 캐시 제어(하위 호환)

3. Expires: 캐시 유효 기간(하위 호환)

 

캐시 지시어(direcrives)

● Cache-Control

- Cache-Control : max-age

캐시 유효 시간, 초 단위

 

- Cache-Control : no-cache

데이터는 캐시해도 되지만, 항상 origin 서버에 검증을 하고 사용해야 한다.

 

- Cache-Control : no-store

데이터에 민감한 정보가 담겨있으니 저장하면 안된다. 메모리에서 사용하고 빠른 삭제를 해야하는 정보들의 경우.

 

● Pragma, Expires

이 두개는 하위 호환을 위해 남겨두었지만, 거의 사용하지 않는다.

 

● 조건부 요청 헤더

If-MatchIf-None-Match : ETag 값 사용

If-Modified-SinceIf-Unmodified-Since : Last-Modified 값 사용

 

5. 프록시 캐시

한국 클라이언트가 미국에 있는 origin 서버와 항상 직접 통신하려면 느리다. 다음 그림을 보자.

출처 - 인프런 김영한 HTTP

이럴때 중간에 프록시 서버를 만들어두고, 중간에서 저장해 둔다면

첫 사용자가 데이터를 요청할때는 오래 걸리겠지만, 이후 캐시 되기때문에 두번째 사용자부터는 같은 데이터를 요청했을때 전달받는 속도가 매우 빨라진다. 다음 그림을 살펴보자.

출처 - 인프런 김영한 HTTP

기타 캐시 지시어(direcrives) - 기타

● Cache-Control: public

응답이 public 캐시에 저장되어도 됨

 

● Cache-Control: private

응답이 해당 사용자만을 위한 것임, private 캐시에 저장해야 함(기본값)

 

● Cache-Control: s-maxage

프록시 캐시에만 적용되는 max-age

 

● Age: 60 (HTTP 헤더)

오리진 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)

 

6. 캐시 무효화

우리가 캐시를 적용하지 않아도 웹 브라우저들이 GET요청인 경우 임의로 캐시 해버리기도 하고, 사용자가 자주 요청하면 임의로 브라우저가 캐시해버리기도 한다.

 

하지만 정말 어떤 경우에도 캐시 되면 안되는 정보가 있다면 다음과 같은 헤더들을 모두 추가해주어야 한다.

- Cache-Control: no-cache, no-store, must-revalidate

- Pragma: no-cache => HTTP 1.0 하위 호환을 위해

 

캐시 지시어(directives) - 확실한 캐시 무효화

● Cache-Control: no-cache

- 데이터는 캐시해도 되지만, 항상 원 서버에 검증하고 사용해야한다.

 

● Cache-Control: no-store

- 데이터에 민감한 정보가 있으므로 저장하면 안됨 (메모리에서 사용하고 최대한 빨리 삭제)

 

● Cache-Control: must-revalidate

- 캐시 만료후 최초 조회시 원 서버에 검증해야함

원 서버 접근 실패시 반드시 오류가 발생해야함 - 504(Gateway Timeout)

- must-revalidate는 캐시 유효 시간이라면 캐시를 사용함

 

● Pragma: no-cache => 하위 호환

 

한가지 의문점이 있다. no-cache를 사용하면 항상 원서버에 검증하기 때문에 이를 사용하면 되는데, 왜 must-revalidate가 따로 있을까?

must-revalidate 도 원서버에 검증하는것은 같은것 아닌가?

 

우선 no-cache의 기본 동작을 살펴보자.

출처 - 인프런 김영한 HTTP

원 서버에 요청해서 검증을 받은 후 캐시를 재사용 하고있다.

 

문제는 프록시 캐시와 원서버 사이의 연결이 끊어졌을때 이다.

 

no-cache같은경우 연결이 끊어지면 다음과 같이 행동한다.

출처 - 인프런 김영한 HTTP

프록시 서버가 원서버에 접근할수가 없기 때문에 일단 예전 데이터라도 보여주는 것 이다. 따라서 200OK 와 함께 예전 데이터를 보여준다.

 

다음으로는 must-revalidate의 경우를 살펴보자.

출처 - 인프런 김영한 HTTP

must-revalidate의 경우 원 서버에 접근하여 검증할수 없는경우, 바로 504 오류를 보여준다.

 

예를 들어 통장 잔고를 조회 한다고 해보자.

10000원이 있었는데, 방금 100만원을 입금하여 101 만원이 통장에 있어야 한다.

이후 돈이 잘 입금되었는지 확인하려 하는데, 프록시 캐시와 원 서버의 연결이 끊어진 상황이다.

이럴때 must-revalidate가 아닌! no-cache라면 예전 정보인 10000원을 보여준다.

이렇게 되버리면 고객에게 매우 큰 타격이 갈수 있다.

must-revalidate 에서는 그대신 오류를 발생시켜버리는 것 이다. 오류 발생이 더 현명한 판단이 되는것 이다.

'BackEnd > WEB' 카테고리의 다른 글

[WEB] JWT (Json Web Token)이란?  (0) 2022.05.11
[HTTP] HTTP 헤더 - 일반헤더  (0) 2022.02.02
[HTTP] HTTP 상태 코드  (0) 2022.02.01
[HTTP] HTTP 메서드 활용  (0) 2022.01.31
[HTTP] HTTP 메서드  (0) 2022.01.28

댓글