웹 캐시 : 자주 쓰이는 문서의 사본을 자동으로 보관하는 HTTP 장치
캐시가 주는 혜택
- 불필요한 데이터 전송을 줄여서, 네트워크 요금으로 인한 비용을 줄여줌
- 네트워크 병목을 줄여줌. 대역폭을 늘리지 않고도 페이지를 빨리 불러올 수 있게 됨
- 원 서버에 대한 요청 감소, 서버 부하를 줄일 수 있으며 더 빨리 응답할 수 있게 됨
- 거리로 인한 지연 감소
복수의 클라이언트가 한 개의 원 서버에 같은 콘텐츠 중복 요청.
- 같은 바이트 반복 이동 → 불필요한 데이터 전송
- 값비싼 네트워크 대역폭을 잡아먹음 / 전송 느려짐 / 웹 서버 부하 발생
캐시 이용 : 첫 번째 응답을 캐시에 저장 → 캐시된 사본을 이어지는 똑같은 요청에 대해 응답으로 사용
대역폭 병목
캐시는 네트워크 병목을 줄여준다.
- 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭을 제공한다.
- WAN 접속 → 그 경로에 있는 가장 느린 네트워크의 속도
Flash Crowds
캐싱은 갑작스런 요청 쇄도에 대응하기 위해 중요하다.
- 웹 서버에 과부하 발생
거리로 인한 지연
대역폭이 문제가 되지 않더라도, 거리가 문제 될 수 있음.
- 네트워크 라우터에서 트래픽 지연
- 빛 속도 그 자체가 유의미한 지연 유발.
- 병렬이면서 keep-alive 커넥션이라도 빛의 속도는 뚜렷한 지연을 발생시킴
보스턴 ~ 샌프란시스코 거리 = 4,400km
빛의 속도 = 300,000km/s
가정 : 20 개의 작은 이미지를 포함하고 있는 웹 페이지. 이미지는 전부 샌프란시스코에 있는 한 서버에 있음.
왕복 30m/s.
동시에 네 개의 커넥션을 열고(병렬), 유지(keep-alive) -> 빛의 속도로 인한 지연은 거의 1/4초(240밀리초)
서버가 도쿄에 위치할 경우(10,800km)
지연이 600ms로 커짐
적중과 부적중
캐시 적중
요청에 대응하는 사본이 있는 경우.
- cache hit
캐시 부적중
대응하는 사본이 없다면 요청은 원 서버로 전달됨
- cache miss
캐시된 객체가 최신인지 검사해야 함.
- If-Modified-Since 헤더와 같은 도구로 신선도 검사를 하고, 최신 여부에 따라 달라짐
- 재검사 적중은 느린 적중이라고도 함.
- 부적중 : 서버 객체가 캐시된 사본과 다르다면, 서버는 콘텐츠 전체와 함께 평범한 HTTP 200 OK 응답을 클라이언트에게 보냄
캐시 삭제
원 서버 객체 삭제 → 404 Not Found 응답, 캐시는 사본을 삭제
캐시 적중률
캐시가 요청을 처리하는 비율
- 캐시가 얼마나 큰지
- 캐시 사용자들의 관심사가 얼마나 비슷한지
- 캐시된 데이터가 얼마나 자주 변경되거나 개인화되는지
- 캐시가 어떻게 설정되어 있는지
문서 적중률과 바이트 적중률
문서 적중률 | 바이트 적중률 |
얼마나 많은 웹 트랜잭션을 외부로 내보내지 않았는지 보여줌 | 얼마나 많은 바이트가 인터넷으로 나가지 않았는지 보여줌 → 캐시를 통해 제공된 모든 바이트의 비율 표현 |
트랜잭션(고정된 소요 시간 포함), 커넥션을 맺는 등 종종 길어질 수 있음. → 개선 시 전체 대기시간(지연) 감소 | 대역폭 절약 최적화 |
- 모든 문서의 크기가 같지 않음 → 적게 접근되는 크기가 큰 객체는 전체 트래픽에는 더 크게 기여함.
- 이런 이유로 어떤 사람들은 바이트 단위 적중률 측정값을 더 선호함.
적중과 부적중의 구별
HTTP는 클라이언트에게 응답이 캐시 적중이었는지 원 서버 접근이었는지 말해줄 방법 제공하지 않음
- 모두 응답 코드는 200 OK
- Via 헤더에 추가 정보를 붙이는 상용 프락시 캐시
- Date 헤더 : 응답의 Date 헤더 값을 현재 시각과 비교함. 응답의 생성일이 더 오래되었다면 클라이언트는 응답이 캐시된 것임을 알아낼 수 있음
- Age 헤더 : 응답이 얼마나 오래되었는지 말해줌
캐시 토폴로지
개인 캐시 / 공용 캐시
개인 전용 캐시들이 서버에 제각각 접근해서 객체를 가져온다.
- 새롭고 ‘따끈따끈한’ 문서 → 개인 전용 캐시에 아직 들어있지 않은 문서
- 같은 문서를 네트워크를 거쳐 여러 번 가져옴
자주 찾는 객체를 한 번만 가져와 모든 요청에 대해 공유된 사본을 제공함으로써 네트워크 트래픽을 줄임.
캐시 계층
작은 캐시에서 부적중이 발생했을 때, 더 큰 부모 캐시가 ‘걸러 남겨진’ 트래픽을 처리하자 → 캐시 계층
- 클라이언트 주위에는 작고 저렴한 캐시
- 계층 상단에는 많은 사용자들이 공유하는 문서를 유지하기 위한 크고 강력한 캐시
프락시 연쇄가 길어질수록 각 중간 프락시는 현저한 성능 저하가 발생함.
- 실제로 두 개 or 세 개의 프락시만 거치도록 스스로 제한.(책 기준)
캐시망
동적인 캐시 커뮤니케이션 결정
콘텐츠 라우팅
- URL에 근거하여, 부모 캐시와 원 서버 중 하나를 동적으로 선택한다.
- URL에 근거하여 특정 부모 캐시를 동적으로 선택한다.
- 부모 캐시에게 가기 전에, 캐시된 사본을 로컬에서 찾아본다.
- 다른 캐시들이 그들의 캐시된 콘텐츠에 부분적으로 접근할 수 있도록 허용하되, 그들의 캐시를 통한 Internet Transit은 허용하지 않는다. → 트래픽이 다른 네트워크로 건너가는 것을 금지함
형제 캐시
선택적인 피어링을 지원하는 캐시
- HTTP는 형제 캐시를 지원하지 않음
- ICP(인터넷 캐시 프로토콜)이나 HTCP(하이퍼텍스트 캐시 프로토콜)로 HTTP를 확장함.
캐시 처리 단계
- 요청 받기
- 고성능 캐시는 여러 개의 들어오는 커넥션들로부터 데이터를 동시에 읽어들이고 메시지 전체가 도착하기 전에 트랜잭션 처리를 시작함.
- 파싱
- 요청 메시지 파싱
- 헤더 부분을 조작하기 쉬운 자료구조에 넣음.
- 캐싱 소프트웨어가 헤더 필드를 처리하고 조작하기 쉽게 만들어줌
- 검색
- 신선도 검사
- 신선도 한계를 넘음 → ‘신선하지 않은 것’
- 신선하지 않은 문서는 제공하기 전 원 서버와 재검사
- 응답 생성
- 발송
- 로깅 - 선택적으로 트랜잭션에 대해 서술한 로그를 남김
- Squid log format
- Netscape extended common log format
문서 만료
- Cache-Control
- Expires
캐시된 문서가 만료되면, 캐시는 반드시 서버와 문서에 변경된 것이 있는지 검사해야 하며, 만약 그렇다면 신선한 사본을 얻어와야 함.(새 유효기간과 함께)
헤더 | 설명 |
Cache-Control: max-age | max-age : 문서의 최대 나이 정의 문서가 처음 생성된 이후~제공하기엔 더 이상 신선하지 않다고 간주될 때까지 경과한 시간의 합법적인 최댓값(초 단위) Cache-Control: max-age=48400 |
Expires | 절대 유효기간 명시. 유효기간이 경과됐다면, 그 문서는 더 이상 신선하지 않다. Expires: Fri, 05 Jul 2002, 05:00:00 GMT |
서버 재검사
원 서버에게 문서가 변경되었는지의 여부를 물어볼 필요가 있음.
- 재검사 결과 콘텐츠가 변경되었다면, 캐시는 그 문서의 새로운 사본을 가져와 오래된 데이터 대신 저장한 뒤 클라이언트에게도 보내준다.
- 콘텐츠 변경 x → 새 만료일을 포함한 새 헤더들 가져옴 → 캐시 안의 헤더들을 갱신함.
문서의 신선도를 매 요청마다 검증할 필요 없이 문서가 만료되었을 때 한 번만 서버를 재검사하면 됨.
- 신선하지 않은 콘텐츠 제공 x
- 서버 트래픽 절약
- 사용자 응답 시간 개선
HTTP는 캐시가 다음 중 하나를 리턴하도록 요구
- ‘충분히 신선한’ 캐시된 사본
- 원 서버와 재검사 된 충분히 신선하다고 확신하는 캐시된 사본
- 에러 메시지(재검사해야 하는 원 서버가 다운된 경우)
- 경고 메시지가 부착된 캐시된 사본(부정확하다면)
조건부 메서드
- 조건부 GET → 조건부 헤더
- 최근 변경 일시 검사 or 엔티티 태그 검사
If-Modified-Since
특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 함
- 변경 o : 새 문서가 새로운 만료 날짜와 그 외 다른 정보들이 담긴 헤더들과 함께 캐시에게 반환됨.
- 변경 x : 304 Not Modified. 본문x(효율)
- 헤더들 중에서도 갱신이 필요한 것만 보내줌 - 새 만료 날짜
- 오래된 서버는 ims 헤더를 인식하지 못하고 GET 요청으로 인식함.
- 변경되지 않은 문서 데이터를 불필요하게 전송 → 덜 효율적
서버 응답 헤더의 Last-Modified 헤더와 함께 동작함.
- 원 서버는 제공하는 문서에 최근 변경 일시를 붙임.
- 캐시된 사본이 마지막으로 수정된 날짜가 담긴 IMS 헤더를 포함함.
몇몇 웹서버는 실제 날짜 비교로 구현하지 않는다.
- “이 날짜 이후로 변경되었다면” (x)
- “정확히 이 날짜에 마지막 변경이 일어난 것이 아니라면” (o)
If-None-Match
엔티티 태그 검사 방식
- 만약 엔티티 태그가 변경되었다면, 서버는 200 OK 응답으로 새 콘텐츠를 새 ETag와 함께 반환함.
If-None-Match: "v2.4", "v2.5", "v2.6"
If-None-Match: "foobar", "A32425", "Profiles in Courage"
- 캐시가 객체에 대한 여러 개의 사본을 갖고 있는 경우, 그 사실을 서버에게 알리기 위해 하나의 If-None-Match 에 여러 개의 엔티티 태그를 포함할 수 있다.
HTTP/1.1 은 약한 검사기(weak validator)
약한 검사기 어느 정도 콘텐츠 변경 허용, 중요한 의미가 변경되면 함께 변경됨. W/ 접두사를 붙여서 표현
강한 검사기 | 콘텐츠가 바뀔때마다 바뀜. |
약한 엔티티 태그 | 대응하는 엔티티에 유의미한 변경이 있을 때 변경 |
강한 엔티티 태그 | 대응하는 엔티티 값이 어떻게 바뀌든 매번 반드시 같이 바뀌어야 함. |
- 엔티티 태그 값은 서로 다른 두 엔티티에 대해 재활용 불가
ETag, Last-Modified 일시 사용 시기
- 서버가 ETag 반환 → 반드시 ETag 검사기 사용
- 서버가 Last-Modified 반환 → 클라이언트 IMS 검사
클라이언트는 1.0, 1.1 캐시 모두 적절히 응답할 수 있도록 두 가지의 재검사 정책을 모두 사용해야 한다.
HTTP/1.1 캐시
- HTTP/1.1 원 서버는 실현 불가능하지만 않다면 ETag 검사기를 보내야 함.
- 이점이 있다면 강 대신 약한 태그 검사기 (+ Last-Modified 일시)
- IMS, ETag 헤더를 모두 받았다면, 모든 조건부 헤더 필드의 조건에 부합하지 않는 한 304 응답 반환 불가
캐시 제어
헤더
서버가 캐싱 규칙 설정을 위한 헤더를 응답에 첨부할 수 있음.
Cache-Control: no-store 캐시가 그 응답의 사본을 만드는 것을 금지함
Cache-Control: no-cache | 재검사 없이 캐시에서 클라이언트에 제공 금지 |
Cache-Control: must-revalidate | 신선하지 않은 객체를 반드시 원 서버와의 최초의 재검사 없이는 제공해서는 안 됨. |
- 원 서버 접근 불가 : 504 Gateway Timeout error 반환
- 캐시는 성능 개선을 위해 만료된 객체를 제공할 수 있음. | | Cache-Control: max-age | 문서가 서버로부터 온 이후 흐른 시간. (공용 캐시 s-maxage 사용) maximum-aging=0 : 캐시 무효화, 실시간 업데이트, 성능 저하(네트워크 부하, 페이지 로딩 속도) | | Expires | 많은 서버가 동기화되어 있지 않거나 부정확한 시계를 갖고 있음 → 절대 시각 대시 경과된 시간으로 표현.
- 신선도 수명 근사값 = 만료일 - 생성일
- Expires: 0 : 문서를 항상 만료되도록 → 문법 위반, 수용 o, 생성 x | | 휴리스틱 | 아무런 만료 정보도 주지 않고, 캐시가 스스로 결정
- 최대 나이를 계산 |
LM 인자 알고리즘
<aside> 💡
휴리스틱 만료 : 서버가 명시적으로 캐시 제어 헤더를 제공하지 않는 경우 사용
</aside>
- 문서의 마지막 변경 일시가 매우 예전 → 잘 변경되지 않는 안정적인 문서. 캐시에 더 오래 보관해도 안전함
- 문서의 마지막 변경 일시가 최근 → 자주 변경, 서버와 재검사하기 전까지 짧은 기간 동안만 캐싱.
$마지막_수정_이후로_경과한_시간 = max(0, $서버의_Date - $서버의_Last_Modified);
$서버_신선도_한계 = int($마지막_수정_이후로_경과한_시간 * $lm_인자);
예: LM 인자 = 0.1
- 리소스가 10일 전에 마지막으로 수정되었다면, 브라우저는 이 리소스를 추가로 1일(10%) 동안 캐시에 저장하고 사용 가능하다고 판단할 수 있음.
아파치 HTTP 헤더 제어
mod_headers
- 모든 HTTP 응답 헤더를 제어하는 더 범용적인 도구.
- 캐시 설정 외에도 보안 및 쿠키 정책 등 다양한 헤더를 추가하거나 수정할 수 있다.
<IfModule mod_headers.c>
**Header set Cache-Control "max-age=3600, must-revalidate"**
Header unset X-Powered-By <!-- 서버정보 등 기본적으로 추가된 헤더 제거-->
Header set X-Frame-Options "DENY" <!-- 보안 헤더 : 클릭재킹 공격 방지 -->
</IfModule>
<Files *.html>
Header set **Cache-control** no-cache
</Files>
mod_expires : 캐시 만료 헤더를 설정하는 데 사용됨.
Apache 설정 파일 (httpd.conf)이나 .htaccess 파일에 설정 가능
<IfModule mod_expires.c>
ExpiresActive On
**Expires**Default "access plus 1 month"
ExpiresByType text/html "access plus 1 day"
ExpiresByType image/jpeg "access plus 1 week"
</IfModule>
- html 파일은 1일 동안, jpeg 이미지는 1주일
mod_cern_meta
- 메타 파일을 통한 헤더 설정 : 웹 문서 자체와는 별도의 파일로 응답 헤더를 관리함.
- 서버가 특정 리소스에 접근할 때, 연결된 메타 파일을 확인해서 추가적인 HTTP 헤더를 응답에 포함시킴.
- 특정 리소스에 대해 개별적인 헤더 설정이 필요할 때 유용함.
- /var/www/html/index.html이라는 파일이 있다면, 그 파일과 관련된 메타 파일은 /var/www/html/index.html**.meta**로 저장
HTTP-EQUIV 태그
- HTML 2.0
<meta http-equiv="속성" content="값">
<meta http-equiv="Cache-Control" content="no-cache">
<!--구형 캐시 제어용-->
<meta http-equiv="**Pragma**" content="no-cache">
<meta http-equiv="Expires" content="Wed, 21 Oct 2024 07:28:00 GMT">
- 클라이언트 측(브라우저)에서만 적용됨. → 서버에서 응답 헤더를 설정하는 것만큼 강력하지 않음.
- 최신 웹 환경에서는 서버 설정
- 이 기능을 지원하는 웹 서버나 프록시는 거의 없음.
- 브라우저와 프락시 캐시는 다른 캐시 제어 규칙을 적용 → 캐시 만료 동작 혼란
- 서버의 부하를 가중시킴, 설정값이 정적임, HTML 이외 타입 지원 x
나이 계산
HTTP 나이 계산
- 캐시는 그 문서가 로컬 캐시에 얼마나 오래 머물렀는지 알 수 있음 → 캐시 = 로컬 캐시?
$나이 = $문서가_우리의_캐시에_도착했을_때의_나이 + $사본이_우리의_캐시에_머무른시간;
클록 스큐(clock skew)
두 컴퓨터의 시계 설정 차이로 인한 문제
$겉보기_나이 = **max(0, $응답을받은시각 - $Date헤더값)**;
$문서가_우리의_캐시에_도착했을_때의_나이 = $겉보기_나이;
- 겉보기 나이는 클록 스큐때문에 부정확한 음수가 되기도 함.
- 음수 → 0으로 만들어야 함.
점층적 나이 계산
http/1.1
‘동기화된 시계라는 것이 존재하지 않는다’ 우회 → Age 헤더에 프록시를 지날때마다 상대적인 나이를 누적해서 더하도록 함.
- 서버 간의 시간 비교, 종단 간의 시간 비교를 필요로 하지 않음.
- HTTP/1.1 장치가 아니면 Age 헤더를 인식하지 못하고 삭제
- Date 기반 나이보다 보수적인 것(max) 선택 → Date 값 or 나이 계산 값 = 실제보다 작을 수 있음.
$보정된_겉보기_나이 = max($겉보기_나이, $Age_헤더값);
$문서가_우리의_캐시에_도착했을_때의_나이 = $보정된_겉보기_나이;
네트워크 지연에 대한 보상
Date 헤더는 언제 문서가 원 서버를 떠났는지 말해주지만, 캐시로 옮겨가는 도중에 얼마나 시간을 소비했는지는 말해주지 않음.
- 긴 연쇄 → 상당한 네트워크 지연
- Date 헤더는 부모 캐시가 아닌 원 서버의 날짜를 반영함
$응답_지연_추정값 = ($응답을_받은_시각 - $요청을_보낸_시각);
**$문서가_우리의_캐시에_도착했을_때의_나이** = $보정된_겉보기_나이 + $응답_지연_추정값;
완전한 나이 계산 알고리즘
$나이 = $보정된_겉보기_나이 + $응답_지연_추정값 + $사본이_우리의_캐시에_머무른시간;
신선도 서버 알고리즘
클라이언트가 서버 신선도 한계를 가져와서 클라이언트의 제약에 맞게 수정한다.
캐시 무력화
캐시는 원 서버가 실제 접근 횟수를 알 수 없게 숨길 수 있음.
- 접근 횟수를 캐시가 흡수
광고 회사 → 캐시가 광고 시청 수를 가로채지 못하도록
- 모든 접근에 대해 원 서버와 재검사
- 본문은 없지만, 트랜잭션을 느리게 만듦.
로그 마이그레이션
- 캐시 적중 로그를 콘텐츠 제공자에게 제공
단점
- 적중 로그는 크기 때문에 옮기기 어려움
- 개별 콘텐츠 제공자별로 분리될 수 있도록 표준화, 조직화 X
- 인증과 프라이버시 이슈
적중 측정과 사용량 제한
Meter 헤더 : 서버가 캐시된 문서가 적중한 횟수의 정기적인 업데이트를 캐시로부터 받음.
- 사용량 제한 : 문서를 제공할 수 있는 횟수, 소모할 수 있는 처리시간 등 제어
'HTTP' 카테고리의 다른 글
HTTP 완벽 가이드 스터디 (9) (0) | 2025.03.10 |
---|---|
HTTP 완벽 가이드 스터디 (8) (0) | 2025.03.06 |
HTTP 완벽 가이드 스터디 (6) (0) | 2024.10.08 |
HTTP 완벽 가이드 스터디 (5) (0) | 2024.10.03 |
HTTP 완벽 가이드 (4) (0) | 2024.10.02 |