캐시리미터 – 3.HTTP/1.1 캐시

캐시(cache)는 임시 저장소로 웹 브라우저가 홈페이지를 받아오기 전에 먼저 검색하여 똑같은 홈페이지의 내용이 캐시에 있으면 캐시로부터 읽어들입니다. 이와 같이 웹상에서 동작되는 캐시(web cache)에는 각 클라이언트에서 개별적으로 동작되는 브라우저 캐시(browser cache)와 모든 인터넷 또는 인트라넷 상의 사용자가 공유할 수 있는 공유캐시(shared cache)로 동작되는 프록시 캐시(proxy cache)가 있습니다. 본 란에서는 이 중에 브라우저 캐시에 대해서만 살펴보고 최종적으로는 PHP 스크립트로 캐시 동작을 제어하는 캐시리미터(cache limiter) 함수를 작성해 보려고 합니다.

양해 사항

이 문서를 정리한 본인도 캐시리미터 함수를 만들면서 HTTP/1.1 문서를 정식으로 접해봅니다. HTTP/1.1 문서를 읽어본 소감을 말한다면 “이게 무슨 말을 하는 건지……”라고 할까요? 그러니 여러분이 본 내용을 읽다가 의문이 나더라도 제 홈페이지 Q&A 게시판에는 “HTTP/1.1″에 관련해서는 가능하면 질문하지 마시기 바랍니다. 의문나는 사항은 여러분이 직접 HTTP/1.1 문서를 읽어보기 바랍니다.

HTTP/1.1에 관련된 문서들

(수정:2010.1.8) HTTP/1.1 한글문서가 링크된 웹주소가 삭제되어 접속되지 않습니다. 복사본을 참조하세요.

본 란에서는 HTTP/1.1 내용 중에 캐시에 관련된 부분만 일부 정리한 것으로 위에 있는 http://pec.etri.re.kr/~qkim/HTTP/http11v2.html의 “HTTP/1.1 번역서”의 내용에서 주로 발췌한 것입니다. “HTTP/1.1 번역서”의 전체 내용에 관심있는 분은 해당 웹사이트에 가셔서 문서를 다운로드 받아 참조하기 바랍니다.

동적으로 생성되는 웹 컨텐트

동적으로 생성되는 웹 컨텐트의 경우에는 캐시가 동작되어서는 안되는데 HTTP/1.0에는 이를 지원하는 메카니즘이 없기 때문에 사용자(end-users)가 보게되는 정보가 잘못 전달될 수 있습니다. 그러나 HTTP/1.1에는 이러한 오류를 방지할 수 있는 명시적인 캐시 관리 메카니즘을 지원합니다.

HTTP/1.1을 지원하는 서버 및 브라우저

현재 거의 모든 웹브라우저(넷스케이프 4.0, 익스플로러 4.0, 핫자바 1.1)가 HTTP/1.1을 지원하고 있습니다. 새버전의 웹서버들도 거의 모두 HTTP/1.1을 일부 또는 전체를 지원하고 있습니다.

요구(request) & 응답(response)

HTTP 규약은 요구/응답 규약입니다. 클라이언트는 요구 메소드, URI, 규약 버전의 형태로 서버에 요구 메시지를 전송합니다. 요구 변경자, 클라이언트 정보, 서버와의 접속에 사용되는 본문 내용을 포함하는 MIME 유형의 메시지가 뒤따릅니다. 서버는 메시지의 규약 버전 및 성공/실패 코드를 포함하는 상태 정보로서 응답합니다. 서버 정보, 엔터티 메타 정보, 엔터티 본문 내용을 포함하는 MIME 유형의 메시지도 뒤따릅니다.

< 요구 및 응답 체인 >

요구는 HTTP 요구 메시지를 의미하며, 클라이언트로부터 서버로의 요구 메시지는 해당 메시지의 첫 라인 내에 자원, 자원의 식별자 및 사용 중인 규약 버전에 적용할 메소드를 포함합니다.

다른 부분은 위에서 언급한 “HTTP/1.1 번역서”를 참조하기 바라며, 여기서는 일반 헤더(general-header) 필드의 구조만 아래에 적습니다.

위에서 Cache-Control 헤더가 우리가 관심을 가져야 되는 캐시 제어를 구현하는 부분입니다.

응답은 HTTP 응답 메시지를 의미하며, 요구 메시지를 수신하고 해석한 후 서버는 HTTP 응답 메시지로 응답합니다.

요구 메시지 부분과 마찬가지로 여기에도 일반 헤더(general-header) 필드가 있습니다. 일반 헤더는 요구와 응답 메시지에 모두 적용되는 부분으로, 메시지 본문의 내용에 영향을 주지 않는 메시지 전송 관련 헤더입니다.

  • 원서버(origin server) : 해당 자원이 보관되어 있거나 자원을 생성할 수 있는 서버
  • 경과시간(age) : 응답 메시지의 경과 시간은 원서버로부터 전송된 후, 또는 성공적으로 검증된 후의 시간
  • 신선한 기간(freshness lifetime) : 응답의 생성 시점과 만기시간(expiration time) 사이의 시간 길이
  • 신선한(fresh) : 응답의 경과시간이 신선한 기간을 넘어서지 않았을 때
  • 낡은(stale) : 응답의 경과시간이 신선한 기간을 넘어섰을 때
  • 의미투명한(semantically transparent) : 성능을 향상시키고자 하는 목적을 제외하고 캐시 사용이 요구되는 클라이언트나 원서버에 영향을 미치지 않을 때 특정한 요구에 대하여 캐시가 “의미투명한” 동작을 한다고 할 수 있습니다. 캐시가 의미투명한 동작을 할 때 클라이언트는 원서버가 직접 처리했을 때와 완전히 동일한 응답을 받게 됩니다.
  • 사용자 에이전트(user agent) : 요구 메시지를 시작하는 클라이언트. 이것은 종종 브라우저, 편집기, 스파이더(웹을 탐색하는 로봇) 또는 다른 사용자 툴이 될 수 있습니다.
  • 서버설정 만기시간(explicit expiration time) : 원서버가 캐시된 데이터의 유효성을 보장할 수 있는 시간.
  • 자동설정 만기시간(heuristic expiration time) : 서버설정 만기시간을 지정하지 않았을 때 캐시가 스스로 할당하는 만기시간
  • 검증자(validator) : 캐시 엔트리가 엔터티의 복사본과 동일한 지 알아내는데 사용하는 규약 요소(예를 들면 엔터티 태그 또는 Last-Modified 시간)

HTTP/1.1 캐시의 목적

많은 경우에 요구(request)를 발송할 필요를 제거하고 또 다른 많은 경우에 완전한 응답을 발송할 필요를 제거하는 것입니다.

첫째, 요구 발송 감소

많은 운영에서 네트워크의 왕복 여행 숫자를 줄여 줍니다. 이 목적을 위해서 “만기일(expiration)” 메커니즘을 사용합니다.

둘째, 응답 발송 감소

네트워크 대역폭 요구를 감소시켜 줍니다. 이 목적을 위해서 “검증(validation)” 메커니즘을 사용합니다.

만기일 모델(expiration model)

사용자 에이전트(예를 들어 브라우저)가 웹문서를 요구할 때, 캐시에 저장되어 있는 캐시된 문서의 만기일을 확인하며, 이 때 만기일이 지나지 않은 문서라면 캐시된 문서를 브라우저로 응답하게 됩니다. 이러한 경우의 요구/응답 체인은 아래 그림과 같습니다.

< 요구 및 응답 체인(만기일 계산) >

캐시된 문서를 브라우저로 응답하게 되면 위 그림과 같은 예에서는 캐시에서 원서버로 가는 부분이 요구 및 응답 체인에서 빠지게 되며 따라서 네트워크의 동작이 신속히 이루어지게 됩니다. 이와 같이 HTTP 캐시는 원서버로 요구를 발송하는 것을 완전히 피할 수 있을 때 최상으로 동작합니다.

요구를 피하는 주요 메커니즘은 원서버가 분명하게 해당 응답이 계속되는 요구를 만족시킬 수 있다는 것을 표시하는 미래의 만기시간을 제공하는 것입니다. 다른 말로 표현하면 캐시가 먼저 서버와 접촉하지 않고도 새로운 응답을 리턴할 수 있다는 것입니다.

이와 같이 HTTP 요구 발송을 감소시키기 위한 방법으로 사용하게 되는 만기일은 원서버가 문서를 발행할 때 함께 지정할 수도 있으며 원서버에서 지정하지 않았을 때는 캐시에서 스스로 알아서 만기일을 계산하여 지정하게 됩니다.

“HTTP/1.1 번역서”에는 expiration을 “유효일”이라고도 번역되어 있고 “만기일”이라고도 번역되어 있습니다. 반면 Microsoft 웹사이트에서는 이를 “만료날짜”라는 용어를 사용합니다. 여기서는 모두를 대표하여 “만기일”이라는 용어를 사용하겠습니다.

만기일 메커니즘은 캐시에서 얻은 응답에만 적용되며 요구한 클라이언트에게 직접적으로 전달되는 첫 응답에는 적용되지 않습니다.

서버설정 만기일(server-specified expiration)

우리가 기대하는 것은 서버가 만기일이 도착 전에 엔터티가 의미상으로 중대하게 변화하지 않을 것이라는 믿음으로 미래의 분명한 만기시간을 부여하는 것입니다. 이렇게 하면 서버의 만기시간이 신중하게 선택되는 한 대개의 경우에 의미투명성을 보존합니다.

서버는 expires 헤더 또는 Cache-Control 헤더의 max-age 지시자를 사용하여 만기시간을 지정합니다.

원서버가 모든 요구를 검증하기 위해 의미투명한 캐시를 요구한다면 과거 시점의 만기시간을 부여할 수도 있습니다. 이는 응답이 항상 낡은 것이기 때문에 계속되는 요구에 이것을 사용하기 위해서는 반드시 먼저 검증을 해야 한다는 것을 의미합니다.

원서버가 HTTP/1.1 캐시가 모든 요구를 강제로 검증하도록 하려면 어떤 방식으로 환경이 설정되었든 “must-revalidate” Cache-Control 지시자를 사용해야 합니다.

자동설정 만기일(heuristic expiration)

원서버가 언제나 서버설정 만기시간(explicit expiration time)을 제공하는 것이 아니므로, expires 및 cache-control: max-age 모두가 응답에 없으면 HTTP 캐시는 전형적으로 그럴듯한 만기시간을 짐작하기 위해 다른 헤더값(last-modified 시간과 같은)을 사용하는 알고리즘을 활용하는 자동설정 만기시간(heuristic expiration time)을 할당합니다. 자동설정 만기시간은 의미투명한 동작을 하지 않을 수도 있기 때문에 조심해서 사용해야 하며 가능한 한 원서버가 서버설정 만기시간을 제공하도록 권고합니다.

만기일 계산

응답이 신선한 것인지 낡은 것인지 결정하기 위해 경과시간(age)과 신선한 기간(freshness lifetime)을 비교할 필요가 있습니다.

max-age 지시자는 expires 보다 우선권을 갖습니다. 따라서 응답에 max-age 지시자가 있으면 이 지시자가 가지고 있는 값이 신선한 기간이 됩니다. 그렇지 않고 expires가 응답에 있으면 expires 헤더값에서 원서버의 Date 값을 빼 준 값이 신선한 기간이 됩니다.

  • 신선한 기간 = max-age 값 또는
  • 신선한 기간 = expires 값 – 원서버의 Date값

경과시간이 신선한 기간을 넘어서지 않았을 때 응답이 신선하다고 할 수 있으며, 이 때는 원서버에 계속 요구하지 않고 캐시된 문서를 브라우저에 보내게 됩니다.

  • 신선한 응답 = 신선한 기간 > 경과시간

검증 모델(validation model)

캐시가 클라이언트 요구에 대한 응답으로 사용하고자 하는 낡은 엔트리를 가지고 있을 때 캐시된 엔트리를 아직도 사용할 수 있는지 알아보기 위해서 클라이언트는 먼저 원서버(또는 새로운 응답을 가진 중간 캐시)를 점검해야 합니다. 이것을 캐시 엔트리를 “검증한다”고 합니다.

< 요구 및 응답 체인(캐시 엔트리 검증) >

최종 갱신 날짜(last-modified dates)

Last-Modified 엔터티 헤더 필드 값은 종종 캐시 검증자로 사용됩니다. 간단히 말하면 캐시 엔트리는 엔터티가 Last-Modified 값 이후에 변경되지 않았으면 유효한 것으로 간주된다는 것입니다.

엔터티 태그 캐시 검증자(validators)

ETag 엔터티 헤더 필드 값, 엔터티 태그는 “불투명한” 캐시 검증자를 제공합니다. 이 검증자는 변경된 날짜를 저장하는 것이 불편한 상황에서, HTTP 날짜 값을 1초 동안 분석하는 것이 충분하지 않은 상황에서 또는 원서버가 변경된 날짜를 사용하여 발생하는 특정 역설을 피하고자 하는 상황에서 좀 더 신뢰성있는 검증을 가능하게 합니다.

응답 캐시 정도(response cachability)

다음 장에서 설명하는 Cache-Control 지시자가 특별히 통제하지 않는 한 캐시 시스템은 언제나 성공적인 응답을 캐시 엔트리로서 저장할 수 있고, 신선한 것이라면 검증없이 리턴할 수 있으며 성공적인 검증 후에 리턴할 수도 있습니다.

캐시 검증자도 없고 응답과 관련된 명확한 만기시간도 없는 문서는 이 문서가 캐시될 것이라고 생각되지 않습니다. 그러나 네트워크가 약하게 연결되었거나 연결되지 않았을 때와 같은 경우에는 캐시된 문서가 제공될 수 있으며, 클라이언트는 보통 이러한 응답이 캐시에서 나왔다는 것을 Date 헤더와 현재 시간을 비교하여 탐지할 수 있습니다.

그러나 어떤 경우에는 캐시가 엔터티를 보유하고 있는 것이, 또는 캐시를 계속되는 요구에 대한 응답으로 리턴하는 것이 적절하지 않을 수 있습니다. 이는 서비스 저작자가 완전한 의미투명한 문서를 제공하려고 하거나 또는 보안이나 사생활 보호에 관한 고려 사항 때문입니다. 다른 고려 사항에 관계없이 특정 엔터티 또는 엔터니의 일부를 캐시할 수 없다는 것을 표시하기 위해 특정 Cache-Control 지시자가 제공되었습니다.

캐시의 정확성

정확한 캐시는 반드시 아래의 조건 중 하나를 만족하며 요구에 적합한 보유하고 있는 캐시 중 가장 최근의 응답으로 요구에 답해야 합니다.

  1. 원서버가 원서버를 사용하여 응답을 재검증한 후 되돌려 주었을 것과 같은 것인지 점검하였다.
  2. 충분히 신선하다. 기본적인 경우 이것은 클라이언트, 서버 및 캐시의 최소한도의 신선도 필요 조건을 만족한다는 것을 의미한다. 원서버가 그렇게 명시하였으면 그것은 원서버의 신선도 필요 조건일 뿐이다.
  3. 클라이언트 또는 서버의 신선도 요구가 위반되었을 경우 경고를 포함하고 있다.
  4. 적절한 304(Not Modified), 305(Use Proxy), 또는 error(4xx 또는 5xx) 응답 메시지이다.

캐시가 원서버와 통신할 수 없다면 정확한 캐시는 위처럼 응답해야 합니다(캐시가 정확한 응답을 할 수 있다면). 그렇지 못하면 캐시는 통신 실패가 있었음을 알리는 에러 또는 경고를 리턴해야 합니다.

캐시와 history 메커니즘의 차이점

사용자 에이전트는 종종 “이전” 버튼과 history 목록과 같은 history 메커니즘을 사용합니다. 이것은 세션에서 이전에 조회한 엔터티를 다시 화면에 표시하기 위해 사용합니다.

history 메커니즘과 캐시는 다릅니다. 특히 history 메커니즘은 자원의 현재 상태를 의미상 투명한 모양으로 보여 주려 시도해서는 안됩니다. history 메커니즘은 자원을 조회했을 때 사용자가 본 것과 동일한 것을 보여 주기 위한 것입니다.

기본적으로 만기시간은 history 메커니즘에 적용되지 않습니다. 엔터티가 여전히 저장소에 있으면 history 메커니즘은 엔터티가 만료되었다 할 지라도 사용자가 만료된 history 문서를 갱신하도록 상세하게 에이전트의 환경을 설정하지 않는 한 이것을 보여 주어야 합니다.

이것은 history 메커니즘이 사용자에게 현재의 화면이 낡은 것일 수도 있음을 알리는 것을 금지하는 것으로 해석해서는 안됩니다.

history 목록 메커니즘이 불필요하게 사용자가 낡은 자원을 볼 수 없도록 한다면 이는 서비스 저작자들에게 그렇지 않았더라면 사용하고 싶어하는 HTTP 만기일 제어 및 캐시 제어의 사용을 피하도록 강요할 수 있습니다. 서비스 저작자들은 그들이 운행제어(“이전” 버튼과 같은 navigaton control)를 사용하여 이전에 가져온 자원을 보고자 할 때 사용자에게 에러 메시지나 경고 메시지를 표시하지 않는 것이 중요하다고 생각할 수 있습니다. 때때로 이러한 자원을 캐시하지 말거나 빨리 만료해야 할 수도 있지만 서비스 저작자들은 사용자 인터페이스를 고려하여 사용자가 history 메커니즘이 부적합하게 작동하여 고통을 받지 않도록 캐시를 방지할 수 있는(예를 들어 “once-only” URL) 다른 방법에 호소하도록 만듭니다.

답글 남기기