[Network] #24 캐시와 조건부 요청
[혼자 공부하는 네트워크↗️], 컴퓨터 네트워킹: 하향식 접근 (제8판)을 바탕으로 정리한 글입니다.
1. 캐시
1.1 캐시란?
캐시(cache)란, 응답 받은 자원의 사본을 임시 저장하는 기술을 의미한다.
- 캐시를 통해 추후 동일한 요청에 대해 캐시된 데이터를 활용할 수 있다.
- 캐시 덕분에 캐시 가능 시간동안 네트워크를 사용하지 않아도 된다. (비싼 네트워크 사용량을 줄일 수 있음) 브라우저 로딩 속도가 매우 빠르다.
-
불필요한 대역폭 낭비 방지, 응답 지연 방지, 빠른 자원 접근 가능
캐시 유형 설명 개인 전용 캐시 웹 브라우저에 저장 공용 캐시 클라이언트와 서버 사이에 위치한 중간 서버에 저장
1.2 캐시 신선도의 검사와 유지
캐시는 자원의 사본을 저장한다. 즉, 캐시된 데이터는 언제든지 원본 데이터와 달라질 수 있다.
- 따라서 캐시 신선도를 고려해야한다.
- 캐시 신선도: 캐시된 사본 데이터가 얼마나 원본 데이터와 유사한지를 표현
- 그렇다면, 어떻게 캐시 신선도를 검사할 수 있을까?
캐시 신선도 검사: 유효 기간 설정
- 캐시 유효 시간이 초과하면, 서버를 통해 원본 데이터를 다시 조회하고, 캐시를 갱신한다.
- 이때 다시 네트워크 다운로드가 발생한다.
유효 기간 설정 방법
- 응답 메시지의 Expires 헤더(날짜)와 Cache-Control 헤더의 Max-Age 값(초)을 사용
- e.g., 캐시의 유효 시간을 2024년 2월 6일 화요일 12:00:00로 설정, 또는 1200초로 설정하는 응답 메시지
유효 기간이 끝났다고 가정했을 때, 새로운 데이터를 처음부터 다시 새롭게 갱신해야 할까?
그렇지 않다. 유효 기간이 끝났더라도, 서버의 원본 데이터가 변경되지 않았다면 그냥 캐시된 데이터를 사용하면 될 것이다.
2. 검증 헤더와 조건부 요청(1)
2.1 캐시 시간 초과
캐시 유효 시간이 초과해서 서버에 다시 요청하면 다음 두 가지 상황이 나타난다.
- 서버에서 기존 데이터를 변경하지 않음
- 서버에서 기존 데이터를 변경함
- 이 상황에서는 데이터를 전송하는 대신에 저장해 두었던 캐시를 재사용 할 수 있다.
- 단 클라이언트의 데이터와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법 필요하다. → 검증 헤더 필요
검증 헤더의 역할
검증 헤더는 클라이언트의 캐시 데이터와 서버의 데이터가 동일한지 확인하는 데 사용된다.
2.2 검증 헤더 추가 (캐시 신선도 재 검사)
2.2.1 If-Modified-Since 헤더
- 날짜를 기반으로 서버에게 물어보는 방법이다.
- If-Modified-Since 헤더에 명시된 시점 이후로 원본에 변경이 있었다면 그때만 새 자원으로 응답하도록 서버에게 요청하는 헤더이다.
- 아래는 “2024년 8월 23일 금요일 09:00:00 이후에 www.example.com/index.html의 자원이 변경 되었을 경우에만 새 자원으로 응답하라”는 요청 메시지이다.
2.2.2 서버의 Last-Modified 헤더
- 상태 코드 304(not Modified)를 통한 자원의 “변경 여부” 뿐만 아니라, 자원이 마지막으로 수행된 시점도 알려줄 수 있다.
- 304(not Modified)는 자원이 변경되지 않았을 경우 응답하는 상태 코드이다.
- 즉, “캐시된 데이터가 변경되지 않았으니 그대로 쓰세요” 가 304(not Modified)인 것이다.
- 아래는 2019년 10월 17일 목요일 7시 18분 26초에 변경 되었음을 알 수 있다.
2.3 검증 헤더 동작 과정
첫 번째 요청
- 클라이언트가 서버로 데이터를 요청한다. (GET /star.jpg)
- 서버는 리소스를 전송하며, 헤더에 Last-Modified 포함한다.
- 클라이언트는 응답 데이터를 캐시에 저장한다.
두 번째 요청 (캐시 만료 후)
① 캐시 유효 시간이 초과하면 클라이언트는 기존의 Last-Modified 값을 이용해 조건부 요청을 보낸다.
② 데이터가 수정되지 않은 경우 “304 Not Modified + 헤더 메타 정보” 만 응답한다.(바디X)
- 데이터의 최종 수정일이 똑같은 경우 데이터가 수정되지 않은 상태이다.
- HTTP Body는 없다. (네트워크 비용 절감)
- 클라이언트는 기존 캐시 데이터를 재사용한다.
- 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신
- 결과적으로 네트워크 다운로드가 발생하지만 용량이 적은 헤더 정보만 다운로드한다. → 매우 실용적인 해결책
- 데이터가 수정된 경우
- 상태 코드 200 OK와 함께 새 데이터를 전송한다.
- 클라이언트는 기존 캐시를 업데이트한다.
3. 검증 헤더와 조건부 요청(2)
3.1 검증 헤더의 역할
검증 헤더의 역할
검증 헤더는 클라이언트의 캐시 데이터와 서버 데이터가 동일한지 확인하는 데 사용된다.
검증 헤더의 종류
크게 Last-Modified와 ETag로 구분된다.
조건부 요청/응답 헤더 | 설명 |
---|---|
Last-Modified / If-Modified-Since | 데이터의 최종 수정 시각을 기준으로 요청/응답 |
ETag / If-None-Match | 데이터를 식별할 고유 태그를 기반으로 요청/응답 |
조건부 요청 헤더
- 서버 데이터의 변경 여부에 따라 조건부로 응답을 전송한다.
- 200 OK: 데이터 변경(HTTP Body 포함)
- 304 Not Modified: 데이터 미변경(HTTP Body 제외)
3.2 Last-Modified와 If-Modified-Since의 한계
- 1초 미만 단위 캐시로 캐시 조정이 불가능
- 날짜 기반 로직이므로 세밀한 캐시 관리가 어렵다.
- 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 결과가 똑같은 경우에도 전체 데이터를 다 다시 다운로드 한다.
- 서버에서 별도의 캐시 로직을 적용하기 힘들다.
- e.g., 주석, 공백 등 크게 영향이 없는 변경에서 캐시를 유지하고 싶은 경우 캐시 메커니즘을 컨트롤하기 힘들다.
3.3 ETag와 If-None-Match 헤더
서버에서 캐시 메커니즘을 컨트롤 할 수 있는 방법이다.
3.3.1 ETag (Entity Tag)
ETag란?
- ETag란, 자원의 버전을 식별하는 정보이다.
- 캐시용 데이터의 고유한 버전 이름을 부여하여 변경 여부를 추적한다.
- 서버에서 데이터가 변경할 때마다 새로운 ETag 값을 생성한다.
특징
- 데이터가 변경되면 ETag 값이 갱신된다.
- 데이터가 변경되지 않으면 ETag 값도 유지된다.
- 일반적으로 데이터의 Hash 값을 기반으로 ETag가 생성된다.
3.3.2 If-None-Match 헤더
클라이언트가 ETag 값을 서버로 전송하여 자원이 변경되었는지 확인한다.
- 클라이언트는 기존 캐시 데이터의 ETag 값을 서버에 If-None-Match 헤더로 보낸다.
- 서버는 요청에 포함된 ETag 값을 비교하여 자원의 변경 여부를 판별한다.
- ⭐ 캐시 제어 로직을 서버에서 완전히 관리하고, 클라이언트는 단순히 이 값을 서버에 제공한다. (클라이언트는 캐시 메커니즘을 모름)
- 진짜 단순하게 ETag만 보내서 같으면 유지, 다르면 다시 받기
서버 응답 사례
- 따라서 클라이언트는 Etag 값과 일치하는 자원이 있니? 라고 서버에게 물어봄으로써 변경사항이 있는지 확인할 수 있다.
- 아래는 “Etag 값이 abc인
www.example.com/index.html
이라는 자원이 있니? 자원이 변경되었다면(Etag 값이 바뀌었다면) 그때만 새 자원으로 응답해줘” 라는 메시지이다.
자원이 변경되지 않은 경우
- 서버는 304 Not Modified 상태 코드를 반환하며, 헤더 정보만 전송한다.
- 클라이언트는 캐시에 저장된 기존 데이터를 그대로 사용한다.
자원이 변경된 경우
- 서버는 200 OK 상태 코드와 함께 변경된 자원을 전송한다.
- 클라이언트는 새로운 데이터를 캐시에 저장한다.
장점
- 효율적인 네트워크 사용
- 자원이 변경되지 않은 경우, 데이터 전송을 최소화 (304 Not Modifie)
- 서버 중심 캐시 제어
- 서버가 캐시 유효성을 판단하고, 필요한 경우에만 데이터를 갱신한다.
4. 캐시와 조건부 요청 헤더
4.1 캐시 제어 헤더
4.1.1 Cache-Control: 캐시 제어
Cache-Control헤더는 캐시 동작을 제어하는 다양한 캐시 지시어 (directives)를 제공한다.
헤더 | 설명 |
---|---|
Cache-Control: max-age | 캐시 유효 시간을 초단위로 설정한다. |
Cache-Control: no-cache | 데이터를 캐시해도 되지만, 항상 원(origin) 서버에 검증 후 사용한다. |
Cache-Control: no-store | 민감한 정보가 포함된 데이터는 저장하지 않고 메모리에서 사용 후 즉시 삭제한다. |
4.1.2 Pragma: 캐시 제어 (하위 호환)
Pragma: no-cache
은 HTTP/1.0에서 사용된 캐시 제어 헤더로, 현재는 거의 사용되지 않는다.
HTTP/1.1 이후에는 Cache-Control
을 사용하는 것이 일반적이다.
4.1.3 Expires: 캐시 유효 기간 (하위 호환)
Expires
는 캐시 만료일을 정확한 날짜로 설정한다.
- e.g.,
expires: Mon, 01 Jan 1990 00:00:00 GMT
- HTTP 1.0부터 사용되었으나, 지금은 더 유연한
Cache-Control: max-age
를 권장한다. Cache-Control: max-age
가 설정된 경우,Expires
는 무시된다.
4.2 검증 헤더와 조건부 요청 헤더
검증 헤더 (Validator)
- 서버가 캐시된 데이터의 유효성을 확인하는 데 사용된다.
- ETag
- 데이터의 고유한 버전 이름을 나타낸다.
- e.g.,
ETag: "v1.0"
,ETag: "asid93jkrh2l"
- Last-Modified
- 데이터가 마지막으로 수정된 시점을 나타낸다.
- e.g.,
Last-Modified: Thu, 04 Jun 2020 07:19:24 GMT
조건부 요청 헤더
- 클라이언트가 서버에 데이터를 요청할 때, 조건에 따라 응답을 제어하는 헤더이다.
-
ETag 기반:
If-Match
: 서버가 제공한 데이터의 ETag 값과 요청 데이터의 ETag 값이 일치해야 요청을 처리한다.If-None-Match
: 서버의 ETag 값이 요청 데이터의 ETag 값과 다를 경우 요청을 처리한다.
- Last-Modified 기반
If-Modified-Since
: 지정된 날짜 이후 데이터가 변경된 경우 요청을 처리한다.If-Unmodified-Since
: 지정된 날짜 이전 데이터가 변경되지 않은 경우 요청을 처리한다.
5. 프록시 캐시
5.1 원 서버 직접 접근
한국에 있는 클라이언트가 미국에 있는 원 서버(origin server)에 직접 접근하면, 지연 시간이 크며 서버 부하가 증가한다.
5.2 프록시 캐시 도입
프록시 캐시는 클라이언트와 원 서버 사이에 위치하여, 요청 및 응답 데이터를 캐싱해 성능을 향상시킨다.
- 첫 번째 요청
- 프록시 캐시 서버가 데이터가 없으므로 원 서버에 요청을 전달한다.
- e.g.,
- 웹 브라우저 1: 응답 시간 100ms (프록시 캐시 응답 시간) + 400ms (원 서버 접근) = 500ms
- 웹 브라우저 2, 3: 500ms
- 두 번째 요청 이후
- 데이터가 캐싱되면, 프록시 캐시 서버가 직접 응답한다.
- e.g., 웹 브라우저 1, 2, 3: 응답 시간 100ms (프록시 캐시만 응답)
5.3 캐시의 종류
Private 캐시
- 클라이언트 브라우저와 같은 private 캐시에만 데이터를 저장한다. (기본값)
Cache-Control: private
가 설정되었을 때 사용된다.
Public 캐시
- 여러 사용자가 공유하는 캐시 (public 캐시)에 데이터를 저장한다.
Cache-Control: public
이 설정된 응답이 저장된다.
프록시 캐시 (공용 캐시)
- 클라이언트와 서버 사이에서 동작하며, 캐시된 데이터를 재사용해 서버 부하를 줄이고 응답 시간을 단축한다.
Cache-Control: s-maxage
: 프록시 캐시에만 적용되는max-age
설정을 정의한다.
Age: 60 (HTTP 헤더)
응답 데이터가 원 서버에서 생성된 후, 프록시 캐시에 머문 시간을 나타낸다.
6. Cache-Control: 확실한 캐시 무효화
6.1 확실한 캐시 무효화를 위한 캐시 지시어
💡 서버 측에서 HTTP 응답 코드를 설계할 때, 확실한 캐시 무효화를 하기 위해서
Cache-Control: no-cache, no-store, must-revalidate
를 넣어주고, 혹시 HTTP 하위 호환까지 생각해서Pragma: no-cache
를 넣어줘야 한다.
6.1.1 Cache-Control: no-cache
데이터를 캐시할 수 있지만, 항상 원 서버에 검증하고 사용해야 한다. (이름에 주의! )
- 원 서버 검증 없이 캐시 데이터를 사용하지 않는다.
- 단, 검증 후 304 Not Modified 응답을 받아 캐시 데이터를 재사용을 할 수 있다.
- e.g., 브라우저가 데이터를 캐싱하더라도, 요청 시 서버로부터 ETag 또는 Last-Modified로 데이터 유효성을 확인한다.
6.1.2 Cache-Control: no-store
데이터가 민감한 정보를 포함하므로, 저장하지 않고 메모리에서만 사용 후 즉시 삭제해야 한다.
e.g., 개인정보, 금융 정보
6.1.3 Cache-Control: must-revalidate
캐시가 만료된 경우, 반드시 원 서버 검증을 수행해야 한다.
- 캐시 유효 시간 내에서는 캐시 데이터를 사용한다.
- 캐시 만료 후에는 반드시 원 서버를 확인해야한다.
- 원 서버 접근 실패 시 반드시 504 Gateway Timeout 오류가 발생해야 한다.
6.1.4 Pragma: no-cache
HTTP/1.0 하위 호환을 위한 지시어이다.
7. no-cache vs must-revalidate
7.1 no-cache 동작 방식
- 클라이언트 요청 시, 데이터가 캐시되어 있더라도 원 서버 검증(Etag, Last-Modified)을 통해 유효성을 확인한다.
- 원 서버 검증 후
- 데이터가 동일: 304 Not Modified 응답.
- 데이터 변경됨: 200 OK 응답과 새로운 데이터 제공.
만약 원 서버 접근을 실패한다면?
캐시 서버 설정에 따라 오류(504) 또는 기존 데이터(200 OK)를 반환할 수 있다.
7.2 must-revalidate 동작 방식
- 캐시가 만료된 경우, 항상 원 서버 검증이 필요하다.
- 원 서버 검증 후
- 데이터가 동일: 304 Not Modified 응답.
- 데이터 변경됨: 200 OK 응답과 새로운 데이터 제공.
만약 원 서버 접근을 실패한다면?
- 항상 오류(504 Gateway Timeout) 가 반환된다.
- 사용 사례: 데이터의 정확성이 절대적으로 중요한 경우 (e.g., 금융 거래, 의료 데이터)
댓글남기기