이번 장에서 살펴볼 주제
- 캐시는 불필요한 데이터 전송을 줄여서, 네트워크 요금으로 인한 비용을 줄여준다.
- 캐시는 네트워크 병목을 줄여준다. 대역폭을 늘리지 않고도 페이지를 빨리 불러올 수 있게 된다.
- 캐시는 원 서버에 대한 요청을 줄여준다. 서버는 부하를 줄일 수 있으며 더 빨리 응답할 수 있게 된다.
- 페이지를 먼 곳에서 불러올수록 시간이 많이 걸리는데 캐시는 거리로 인한 지연을 줄여준다.
불필요한 데이터 전송
복수의 클라이언트가 자주 쓰이는 원 서버 페이지에 접근할 때, 서버는 같은 문서를 각각 한 번씩 전송하게 됩니다. 이 떄 똑같은 바이트들이 네트워크를 통해 반복해서 이동하는데 이 불필요한 데이터 전송은 웹 서버
에 부하를 줍니다. 이 때 캐시를 이용하면, 서버 응답은 캐시
에 보관되며 캐시된 사본이 뒤이은 요청들에 대한 응답으로 사용되기 때문에 낭비가 줄어듭니다.
대역폭 방목
캐시는 네트워크 병목을 줄여줍니다. 클라이언트들이 서버에 접근할 때의 속도는, 그 경로에 있는 가장 느린 네트워크의 속도와 같습니다. 이 때 만약 클라이언트가 빠른 LAN에 있는 캐시로부터 사본을 가져온다면 성능이 대폭 향상될 것입니다.
갑작스런 요청 쇄도
캐싱은 갑작스런 요청 쇄도에 대처하기 위해 특히 중요합니다. 많은 사람들이 동시에 웹 문서에 접근할 때 이런 일이 발생합니다. 이 상황을 막기 위해 캐시를 이용합니다.
거리로 인한 지연
대역폭이 문제가 되지 않는 상황이라면, 거리가 문제가 될 수도 있습니다. 기계실 근처에 캐시를 설치해서 문서가 전송되는 거리를 줄이는 방법이 있습니다.
적중과 부적중
캐시가 세상 모든 문서의 사본을 저장하지는 않는데, 캐시에 요청이 들어왔을 때 해당하는 사본이 있다면 요청이 처리되어 캐시 적중
이 되고 만약 해당되는 사본이 없다면 그냥 우너 서버로 전달되는 캐시 부적중
이 발생합니다.
재검사
원 서버 컨텐츠는 변경될 수 있기 떄문에, 캐시는 반드시 그들이 갖고 있는 사본이 여전히 최신인지 서버를 통해 점검해야합니다. 이를 재검사
라고 합니다. 대부분의 캐시는 클라이언트가 사본을 요청하였으며 그 사본이 검사를 할 필요가 있을 정도로 오래된 경우에만 재검사를 합니다. 이 때 사본이 여전히 유효하면 서버에 304 Not Modified
응답을 보내고 클라이언트에게 제공합니다. HTTP는 캐시된 객체를 재확인 하기 위해 몇 가지 도구를 제공하는데 그 중에서 가장 많이 쓰이는 것은 If-Modified-Since
헤더입니다.
서버에게 보내는 GET 요청이 이 헤더를 추가하면 캐시된 시간 이후에 변경된 경우에만 사본을 보내달라는 의미가 됩니다. 아래는 GET If-Modified-Since 요청이 서버에 도착했을 때 일어나는 세 가지 상황에 대해서 알아보겠습니다.
- 재검사 적중 : 만약 서버 객체가 변경되지 않았다면, 서버는 클라이언트에게 304 Not Modified를 보냅니다.
- 재검사 부적중 : 만약 서버 객체가 캐시된 사본과 다르다면은 서버는 컨텐츠 전체와 함께 평범한 HTTP 200 OK 응답을 클라이언트에게 보냅니다.
- 객체 삭제 : 만약 서버 객체가 삭제되었다면, 서버는 404 Not Found 응답을 돌려보내며 캐시는 사본을 삭제합니다.
적중률
캐시가 요청을 처리하는 비율을 캐시 적중률
, 혹은 문서 적중률
이라고 부릅니다. 적중률은 0 ~ 1까지의 값으로 되어있지만, 흔히 퍼센트로 표현되기도 합니다. 0%는 모든 요청이 캐시 부적중, 100%는 모든 요청이 캐시 적중임을 의미합니다.
바이트 적중률
모든 문서가 같은 크기가 아니기 때문에 특정 사람들은 바이트 적중률
을 더 선호합니다.
적중과 부적중의 구별
클라이언트가 응답이 캐시에서 왔는지 알아내는 한 가지 방법은 Date 헤더
를 이용하는 것입니다. 응답의 Date 헤더값을 현재 시각과 비교하여, 응답의 생성일이 더 오래되었다면 클라이언트는 응답이 캐시된 것임을 알아낼 수 있습니다.
캐시 토폴로지
캐시는 한 명의 사용자에게만 할당될 수도 있고 반대로 수천 명의 사용자들 간에 공유될 수도 있습니다.
개인 전용 캐시
개인 전용 캐시
는 많은 에너지나 저장 공간을 필요로 하지 않으므로, 작고 저렴할 수 있습니다. 웹 브라우저는 개인 전용 캐시를 내장하고 있으며 대부분의 브라우저는 자주 쓰이는 문서를 개인용 컴퓨터의 디스크나 메모리에 캐시해 놓고 사용자가 캐시 사이즈 및 설정을 수정할 수 있도록 합니다.
공용 프락시 캐시
공용 캐시는 캐시 프락시 서버 혹은 더 흔히 프락시 캐시
라고 불리는 특별한 종류의 공유된 프락시 서버입니다. 프락시 캐시는 로컬 캐시에서 문서를 제공하거나, 혹은 사용자의 입장에서 서버에 접근합니다. 공용 캐시에는 여러 사용자가 접근하기 때문에, 불필요한 트래픽을 줄일 수 있는 더 많은 기회가 있습니다.
프락시 캐시 계층들
작은 캐시에서 캐시 부적중이 발생했을 때 더 큰 부모 캐시가 그 ‘걸러 남겨진’ 트래픽을 처리하도록 하는 계층을 만드는 방식이 합리적입니다.
캐시망, 컨텐츠 라우팅, 피어링
몇몇 네트워크 아키텍쳐는 단순한 캐시 계층 대신 복잡한 캐시망을 만들고 복잡한 방식으로 서로 대화하여 서로간의 커뮤니케이션 결정을 동적으로 내립니다. 이러한 한층 더 복잡한 캐시 사이의 관계는, 서로 다른 조직들이 상호 이득을 위해 그들의 캐시를 연결하여 서로를 찾아볼 수 있도록 해줍니다.
캐시 처리 단계
웹 캐시는 7단계로 이루어져 있습니다.
- 요청 받기 - 캐시는 네트워크로부터 도착한 요청 메시지를 읽습니다.
- 파싱 - 캐시는 메시지를 파싱하여 URL과 헤더들을 추출합니다.
- 검색 - 캐시는 로컬 복사본이 있는지 검사하고, 사본이 없다면 사본을 받아옵니다.
- 신선도 검사 - 캐시는 캐시된 사본이 충분히 신선한지 검사하고, 신선하지 않다면 변경사항이 있는지 서버에게 물어봅니다.
- 응답 생성 - 캐시는 새로운 헤더와 캐시된 본문으로 응답 메시지를 만듭니다.
- 발송 - 캐시는 네트워크를 통해 응답을 클라이언트에게 돌려줍니다.
- 로깅 - ㅅ너택적으로, 캐시는 로그파일에 트랜잭션에 대해 서술한 로그 하나를 남깁니다.
사본을 신선하게 유지하기
캐시된 사본 모두가 서버의 문서와 항상 일치하는 것은 아닙니다. HTTP는 어떤 캐시가 사본을 갖고 있는지 서버가 기억하지 않더라도, 캐시된 사본이 서버와 충분히 일치하도록 유지할 수 있게 해주는 단순한 메커니즘을 가지고 있습니다. 이를 서버 재검사
라고 부릅니다.
문서 만료
HTTP는 Cache-Control
과 Expires
라는 특별한 헤더들을 이용해서 원 서버가 각 문서에 유효기간을 붙일 수 있게 해줍니다. 이 헤더들은 컨텐츠가 얼마나 오랫동안 신선한 상태로 보일 수 있는지 좌우합니다. 캐시된 문서가 만료되면 캐시는 반드시 서버와 문서에 변경된 것이 있는지 검사해야하며, 만약 그렇다면 신선한 사본을 얻어 와야 합니다.
유효기간과 나이
Cache-Control : max-age
과 Expires
헤더는 기본적으로 같은 일을 하지만, 절대 시간은 컴퓨터의 시계가 올바르게 맞추어져 있을 것을 요구합니다.
서버 재검사
캐시된 문서가 만료되었는다는 것은, 그 문서가 우너 서버에 현재 존재하는 것과 실제로 다르다는 것을 의미하지 않으나 다만 이제 검사할 시간이 되었다는 것을 뜻합니다. 이 문서가 변경되었는지의 여부를 물어보는 것을 의미하는 것이 서버 재검사
라고 합니다.
- 재검사 결과 컨텐츠가 변경되었다면, 캐시는 그 문서의 새로운 사본을 가져와 오래된 데이터 대신 저장한 뒤 클라이언트에게도 보내줍니다.
- 재검사 결과 컨텐츠가 변경되지 않았다면, 캐시는 새 만료일을 포함한 새 헤더들만 가져와서 캐시 안의 헤더들을 갱신합니다.
조건부 메소드와의 재검사
HTTP의 조건부 메소드
는 재검사를 효율적으로 만들어줍니다. HTTP는 캐시가 서버에게 조건부 GET
이라는 요청을 보낼 수 있도록 해줍니다. 이 요청은 서버가 가지고 있는 문서가 캐시가 가지고 있는 것과 다른 경우에만 객체 본문을 보내달라고 하는 것입니다. 캐시 재검사를 할 때 가장 유용한 If-Modified-Since, If-None-Match에 대해서 살펴보겠습니다.
If-Modified-Since : 날짜 재검사
가장 흔히 싸용되며 흔히 IMS라고 불립니다. IMS 요청은 서버에게 리소스가 특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 합니다. 몇몇 웹 서버는 실제 날짜 비교로 구현하지 않지만, IMS 날짜와 최근 변경일 간의 문자열 비교를 수행합니다.
If-None-Match : 엔티티 태그 재검사
퍼블리셔 등 누군가가 문서를 변경했을 때, 문서의 엔티티 태그를 새로운 버전으로 표현할 수 있습니다. 엔티티 태그가 변경되었다면, 캐시는 새 문서의 사본을 얻기 위해 If-None-Match
조건부 헤더를 사용할 수 있습니다.
약한 검사기와 강한 검사기
캐시는 캐시된 버전이 서버가 갖고 있는 걳에 대해 최신인지 확인하기 위해 엔티티 태그를 사용합니다. 이 경우엔 엔티티 태그와 최근 변경일시는 둘 다 캐시 검사기입니다.강한 엔티티 태그
는 대응하는 엔티티 값이 어떻게 바뀌든 매번 반드시 같이 바뀌어야 합니다. 약한 엔티티 태그
는 대응하는 엔티티에 유의미한 변경이 있을 때마다 같이 변경되어야 합니다. 원 서버는 서로 다른 두 엔티티에 대해 강한 엔티티 태그 값을 재활용 해서는 안되며, 약한 엔티티 태그 값이라고 해도 재활용해선 안됩니다.
언제 엔티티 태그를 사용하고 언제 Last-Modified 일시를 사용하는가
HTTP/1.1 클라이언트는 만약 서버가 엔티티 태그를 반환했다면, 반드시 엔티티 태그 검사기를 이용해야 합니다. 만약 서버가 Last-Modified 값만을 반환했다면, 클라이언트는 If-Modified-Since 검사를 사용할 수 있습니다. HTTP/1.1 캐시나 서버가 If-Modified-Since와 엔티티 태그 조건부 헤더를 모두 받았다면, 요청의 모든 조건부 헤더 필드의 조건에 부합되지 않는 한 304 Not Modified 응답을 반환해서는 안됩니다.
캐시 제어
HTTP는 문서가 만료되기 전까지 얼마나 오랫동안 캐시될 수 있게 할 것인지 서버가 설정할 수 있는 여러 가지 방법을 정의합니다.
no-cache와 no-store 응답 헤더
HTTP/1.1은 신선도를 관리하기 위해, 객체를 캐시하는 것을 제한하거나 캐시된 객체를 제공하는 여러가지 방법을 제공합니다.
- no-store : 캐시가 그 응답의 사본을 만드는 것을 금지합니다.
- no-cache : 로컬 캐시 저장소에 저장될 수도 있지만 먼저 서버와 재검사를 하지 않고서는 캐시에서 클라이언트로 제공될 수 없습니다.
Max-Age 응답 헤더
Cache-Control : Max-age
헤더는 신선하다고 간주되었던 문서가 서버로부터 온 이후로 흐른 시간이고, 초로 나타냅니다.
Expires 응답 헤더
더 이상 사용하지 않기를 권하는 Expires 헤더
는 초 단위의 시간 대신 실제 만료 날짜를 명시합니다.
Must-Revalidate 응답 헤더
캐시는 성능을 개선하기 위해 신선하지 않은 객체를 제공하도록 설정할 수 있습니다. Cache-Control : Must-revalidate
응답 헤더는 캐시가 이 객체의 신선하지 않은 사본을 원 서버와의 최초의 재검사 없이는 제공해서는 안 됨을 의미합니다.
휴리스틱 만료
만약 응답이 Cache-Control : Max-age 헤더나 Expires 헤더 중 어느 것도 포함하지 않고 있다면, 캐시는 경험적인 방법으로 최대 나이를 계산할 것입니다. 유명 휴리스틱 만료 알고리즘인 LM 인자 알고리즘은 최근 변경 일시를 문서가 얼마나 자주 바뀌는지 추정합니다.
클라이언트 신선도 제약
웹 브라우저는 브라우저나 프락시 캐시의 신선하지 않은 컨텐츠를 강제로 갱신시켜주는 리프레시나 리로드 버튼을 가지고 있습니다. 이 리프레시 버튼은 Cache-control 요청 헤더가 추가된 GET 요청을 발생시켜서, 강제로 재검사하거나 서버로부터 컨텐츠를 무조건 가져옵니다. 클라이언트는 Cache-Control 요청 헤더를 사용하여 만료 제약을 엄격하게 하거나 느슨하게 할 수 있습니다.
주의할 점
문서 만료는 완벽한 시스템이 아닙니다. 만약 퍼블리셔가 잘못해서 유효기간을 아주 먼 미래로 잡는다면 만료되기 전까지는 그 문서에 대한 변경사항이 캐시에 반영되지 않을 것입니다. 또한 아예 유효기간을 사용하지 않아서 신선도를 알기 어렵게 될 수도 있습니다.
캐시 제어 설정
아파치 웹 서버가 캐시 제어를 어떻게 지원하는지 알아보겠습니다.
아파치로 HTTP 헤더 제어하기
아파치의 웹 서버는 여러가지 메커니즘을 제공하지만 대부분 디폴트로 가능하지 않아서 활성화 시킬 필요가 있습니다.
- mod_headers : 개별 헤더들을 설정할 수 있게 해줍니다.
- mod_expires : 적절한 만료 날짜가 담긴 Expires 헤더를 자동으로 생성하는 프로그램 로직을 제공합니다.
- mod_cern_meta : HTTP 헤더들의 파일을 특정 객체와 연결시켜줍니다.
HTTP-EQUIV를 통한 HTML 캐시 제어
웹 서버 설정 파일과의 상호작용 없이도 쉽게 HTML 문서에 HTTP 헤더 정보를 부여할 수 있도록 하기 위해, HTML 2.0은 <META HTTP-EQUIV>
태그를 정의했습니다. 이 태그는 HTML 문서의 최상단에 위치하여 문서와 연동되어야 하는 HTTP 헤더들을 정의합니다.
1 | <html> |
위와 같이 표현이 가능합니다.