[ HTTP] HTTP 완벽가이드 스터디 - 7장 캐시

Space Developher·2022년 10월 23일
0

HTTP

목록 보기
1/1

0. 들어가며

웹 캐시란?

자주 쓰이는 문서의 사본을 자동으로 보관하는 HTTP 장치로, 웹 요청이 캐시에 도착했을 때 캐시된 로컬 사본이 존재한다면, 원 서버가 아니라 캐시로부터 제공된다.

웹 캐시의 혜택

  • 불필요한 데이터 전송을 줄여서 네트워크 비용을 줄여준다.
  • 네트워크 병목을 줄여준다. 대역폭을 늘리지 않고도 페이지를 빨리 불러 올 수 있다.
  • 원 서버에 대한 요청을 줄여준다. 이로 인해 서버의 부하를 줄일 수 있으며, 더 빨리 응답할 수 있게된다.
  • 페이지를 먼 곳에서 불러올수록 긴 시간이 소요되나, 캐시를 통해 지연시간을 줄여준다.

1. 불필요한 데이터 전송

여러 클라이언트들이 하나의 서버에 동일한 문서를 요청을 하면, 동일한 데이터를 각각 전송해야하기 때문에 불필요한 데이터 전송으로 인해 값비싼 네트워크 대역폭을 잡아먹고, 전송을 느리게 만들며, 웹 서버에 부하를 준다.

캐시를 이용하면 첫 번째 서버 응답은 캐시에 보관되고, 캐시된 사본이 뒤이은 요청들에 대한 응답으로 사용되어 원 서버의 트래픽 낭비를 줄일 수 있다.

2. 대역폭 병목

일반적으로 많은 네트워크가 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭을 제공하며 속도가 더 빠른 장점이 있다.

3. 갑작스런 요청 쇄도(Flash Crowds)

갑작스러운 사건(유명인사 관련 뉴스)으로 인해 갑자기 많은 트래픽이 발생할 때 네트워크와 서버에 장애를 야기한다.

4. 거리로 인한 지연

거리가 멀수록 서버로 부터 응답받는 지연시간이 늘어난다.
도쿄-샌프란시스코 사이의 왕복시간보다, 사무실에 캐시를 설치하면 수천 킬로에서 수십미터로 거리를 줄일 수 있다.

5. 적중과 부적중

캐시는 유용하지만, 모든 문서의 사본을 저장하지는 않는다. (비용 문제와 최신 상태에 대한 문제)
캐시에 요청이 도착했을 때 대응하는 사본이 있는 경우 요청이 처리되며, 이를 캐시 적중이라고 한다.
반면, 대응되는 사본이 없는 경우는 원 서버로 전달되기만 한다. 이를 캐시 부적중이라고 한다.

1) 재검사

원 서버가 가지고 있는 콘텐츠는 변경될 수 있기 때문에 캐시된 사본이 최신인지 점검해야한다.
이를 HTTP 재검사라고 부르며, 콘텐츠의 일부를 가져와서 검사할 수 있는 특별한 요청을 정의했다.

캐시는 클라이언트가 사본을 요청했을 때 충분히 오래된 경우에만 재검사를 진행한다.

느린 적중

재검사는 원 서버에 작은 재검사 요청을 보내고, 콘텐츠가 변경되지 않았다면 아주 작은 사이즈의 304 Not Modified 라는 응답을 받는다. 캐시는 이를 신선하다고 표시한 뒤 클라이언트에 제공한다.
이를 재검사 적중 혹은 느린 적중 이라 한다.
이는 캐시 적중 보다는 느리나 캐시 부적중 보다는 빠르다. (서버로부터 객체 데이터를 받을 필요가 없기 떄문이다.)

HTTP는 캐시된 객체를 재확인하기 위해 몇 가지 도구를 제공하며, 가장 많이 사용되는 것은 If-Modified-Since 헤더 이다.
서버에게 보내는 GET 요청에 이 헤더를 추가하면 캐시된 시간 이후에 변경된 경우에만 사본을 보내달라는 의미가 된다.

GET If-Modified-Since 요청이 서버에 도착하는 경우 세가지 상황이 발생할 수 있다.
1. 서버 콘텐츠가 변경되지 않은 경우
2. 서버 콘텐츠가 변경된 경우
3. 객체가 삭제된 경우

재검사 적중

서버 객체가 변경되지 않았다면 304 Not Modified 라는 응답을 보낸다.

재검사 부적중

서버 객체가 캐시된 사본과 다르다면 서버는 콘텐츠 전체와 함께 HTTP 200 OK 응답을 보낸다.

객체 삭제

서버 객체가 삭제됬다면 서버는 404 Not Found 응답을 보내며, 캐시는 사본을 삭제한다.

2) 캐시 적중률

캐시가 요청을 처리하는 비율을 캐시 적중률 이라 하며, 0 에서 1 의 값으로 되어있지만, 퍼센트 로 표시되기도 한다.
0% 는 모든 요청이 캐시 부적중이며, 100% 는 모든 요청이 적중된 경우다.
캐시 적중률이 40% 만 되어도 괜찮은 편이다.

얼마나 많은 웹 트랜잭션을 외부로 내보내지 않았는지를 보여준다. 이를 개선하면 전체 지연시간이 줄어든다.

3) 바이트 적중률

큰 객체의 경우 덜 접근되지만, 그 크기로 인해 전체 트래픽에 더 영향을 끼친다.
바이트 단위 적중률은 캐시를 통해 제공된 모든 바이트의 비율을 표현하며, 이 측정값은 트래픽이 절감된 정도를 포착해낸다.
바이트 단위 적중률 100% 는 모든 바이트가 캐시에서 왔으며, 어떤 트래픽도 인터넷으로 나가지 않았음을 의미한다.

바이트 단위 적중률은 얼마나 많은 바이트가 인터넷으로 나가지 않았는지를 보여주며, 이를 개선하면 대역폭 절약을 최적화한다.

4) 적중과 부적중의 구별

HTTP는 클라이언트에게 응답이 캐시 적중되었는지, 아니면 원 서버 접근인지를 표현해주지 않는다.
두 경우 모두 응답코드는 200 OK 로 전달된다.

클라이언트가 응답이 캐시에서 왔는지 알아내는 방법은 Date 헤더Age 헤더 를 통해 알 수 있다.
응답의 Date 헤더 값을 현재 시각과 비교하여 응답의 생성일이 더 오래되었다면 클라이언트는 응답이 캐시된 것임을 알아낼 수 있다.
응답이 오래되었는지 말해주는 Age 헤더 를 통해 알 수 있다.

6. 캐시 토폴로지

캐시는 한 명의 사용자에게만 할당될 수 있지만, 수천 명의 사용자들에게 공유될 수도 있다.
한명에게만 할당된 캐시는 개인 전용 캐시(Private Cashe) 라고 부르며, 한명의 사용자가 자주 찾는 페이지를 담는다.
공유된 캐시는 공용 캐시(Public Cashe) 라고 부르며, 사용자 집단에게 자주 쓰이는 페이지를 담는다.

1) 개인 전용 캐시

개인 전용 캐시는 많은 저장 공간을 필요로 하지 않으므로 작고 저렴하다.
웹 브라우저는 개인 전용 캐시를 내장하고 있으며, 자주 쓰이는 문서를 PC 의 디스크와 메모리에 캐시해놓는다.
사용자가 캐시 사이즈와 설정을 수정할 수 있도록 허용한다.

2) 공용 프락시 캐시

공용 캐시는 프락시 캐시 라고 불리는 프락시 서버 다. 여러 사용자가 접근하기 때문에 불필요한 트래픽을 줄일 수 있다. 처음 접근했을 때 객체를 단 한번만 가져와 사본을 공유하기 때문에 네트워크 트래픽을 줄인다.

3) 프락시 캐시 계층들

작은 캐시에서 캐시 부적중이 발생했을 때 더 큰 부모 캐시가 트래픽을 처리하도록 만드는 계층 방식이다.
클라이언트 단에는 작은 캐시 단위를 사용하고, 상단에는 문서를 공유하기 위한 더 크고 강력한 캐시를 사용하자는 형태이다.
단, 프락시 연쇄가 길어질수록 성능 저하가 심해진다.

4) 캐시망, 콘텐츠 라우팅, 피어링

네트워크 아키텍처를 통해 프락시 캐시는 복잡한 방법으로 상호 대화하여 어떤 캐시와 통신할 것인지, 아니면 요청이 캐시를 우회해서 원 서버로 바로 가도록 할 것인지에 대한 캐시 커뮤니케이션을 동적으로 내린다.

콘텐츠 라우팅을 위해 설계된 캐시들은 다음과 같은 일을 할 수 있다.

  • URL에 근거하여 부모 캐시와 원 서버 중 하나를 동적으로 선택한다.
  • URL에 근거하여 특정 부모 캐시를 동적으로 선택한다.
  • 부모 캐시에게 가기 전에 캐시된 사본을 로컬에서 찾아본다.
  • 다른 캐시들이 캐시된 컨텐츠에 부분적으로 접근할 수 있도록 허용하되 캐시를 통한 인터넷 트랜짓을 허용하지 않는다.

선택적인 피어링을 지원하는 캐시는 형제 캐시라고 한다. HTTP 는 형제 캐시를 지원하지 않기에 인터넷 캐시 프로토콜(ICP)나, 하이퍼텍스트 캐시 프로토콜(HTCP) 같은 프로토콜을 통해 HTTP 를 확장했다.

7. 캐시 처리 단계

  1. 요청 받기 - 캐시는 네트워크로부터 도착한 요청 메시지를 읽는다.
  2. 파싱 - 캐시는 메시지를 파싱하여 URL과 헤더들을 추출한다.
  3. 검색 - 캐시는 로컬 복사본이 있는지 검사하고, 사본이 없다면 사본을 받아온다. (그리고 로컬에 저장한다.)
  4. 신선도 검사 - 캐시는 캐시된 사본이 노후된 사본인지 확인하고, 노후되었다면 변경사항이 있는지 서버에 확인한다.
  5. 응답 생성 - 캐시는 새로운 헤더와 캐시된 본문으로 응답 메시지를 만든다.
  6. 발송 - 캐시는 네트워크를 통해 응답을 클라이언트에게 돌려준다.
  7. 로깅 - 선택적으로 캐시는 로그파일에 트랜잭션에 대해 서술한 로그 하나를 남긴다.

1) 요청 받기

캐시는 네트워크 커넥션에서의 활동을 감지하고, 들어오는 데이터를 읽어들인다.
고성능 캐시는 여러 개의 들어오는 커넥션들로부터 데이터를 동시에 읽어들이고 메시지 전체가 도착하기 전에 트랜잭션 처리를 시작한다.

2) 파싱

캐시는 요청 메시지를 여러 부분으로 파싱하여 헤더 부분을 조작하기 쉬운 자료 구조에 담는다.
이는 캐싱 소프트웨어가 헤더 필드를 처리하고 조작하기 쉽게 만들어준다.

3) 검색

캐시는 URL을 알아내고 그에 맞는 로컬 사본이 있는지 검사한다. 로컬 복사본은 메모리에 저장됬을 수도 있고 디스크나 다른 PC에 있을 수도 있다. 만약 문서를 로컬에서 가져올 수 없다면 캐시는 상황이나 설정에 따라서 원 서버나 부모 프락시에서 가져오거나 실패를 반환한다.

캐시된 객체는 서버 응답 본문과 원 서버 응답 헤더를 포함하고 있으므로, 캐시 적중 동안 올바른 서버 헤더가 반환될 수 있다. 캐시된 객체는 객체가 얼마나 오랫동안 캐시에 머무르고 있었는지를 알려주는 기록이나 얼마나 자주 사용되었는지 등에 대한 몇몇 메타데이터를 포함한다.

4) 신선도 검사

HTTP는 캐시가 일정 기간 동안 서버 문서의 사본을 보유할 수 있도록 해준다. 이 기간 동안 문서는 신선 한 것으로 간주되고 캐시는 서버와의 접촉 없이 문서를 제공할 수 있다. 하지만 일단 캐시된 사본이 너무 오래되었을 경우 그 객체는 노후 된 것으로 간주되어 변경점을 확인하기 위해 재검사를 진행한다.
신선도 검사 규칙은 매우 복잡하다.

5) 응답 생성

캐시된 서버 응답 헤더를 토대로 응답 헤더를 생성한다. 이 기저 헤더들은 캐시에 의해 수정되고 늘어난다.
캐시는 클라이언트에 맞게 이 헤더를 조정해야한다. 예를 들어 클라이언트가 HTTP/1.1 응답을 기대한다면 서버가 다른 응답을 반환한다면 헤더를 적절하게 번역해야한다.
또한, 캐시 신선도 정보를 삽입하며, 요청이 프락시 캐시를 거쳐갔음을 알려주기 위한 Via 헤더 를 종종 포함시킨다.
단, 캐시가 Date 헤더 를 조정해서는 안된다. Date 헤더 는 객체가 원 서버에서 최초로 생겨난 일시를 표현하는 것이다.

6) 전송

응답 헤더가 준비되면 캐시는 응답을 클라이언트에게 돌려준다. 프락시 캐시는 클라이언트와 커넥션을 유지할 필요가 있다. 고성능 캐시는 종종 로컬 저장장치와 네트워크 I/O 버퍼 사이에서 문서의 콘텐츠 복사를 피함으로써 데이터를 효과적으로 전송하기 위해 노력한다.

7) 로깅

각 캐시 트랜잭션이 완료된 후 통계 캐시 적중과 부적중 횟수에 대한 통계를 갱신하고 로그 파일에 요청 종류, URL, 무엇이 일어났는지 알려주는 항목을 추가한다.

8) 캐시 처리 플로 차트

8. 사본을 신선하게 유지하기

문서들은 시간에 따라 변경될 수 있으며, 오래된 데이터를 제공하는 캐시는 불필요하다. 캐시된 데이터는 서버의 데이터와 일치하도록 관리되어야한다.
HTTP는 어떤 캐시가 사본을 갖고 있는지 서버가 기억하지 않더라도 캐시된 사본이 서버와 일치하도록 유지하는 문서 만료서버 재검사 라는 매커니즘을 가지고 있다.

1) 문서 만료

HTTP는 Cache-ControlExpires 라는 특별한 헤더를 이용해서 원 서버가 각 문서에 유효기간을 붙일 수 있게 해준다. (우유팩의 유통기한과 유사하다.)
이 헤더들은 콘텐츠가 얼마나 오랫동안 신선한 상태를 보일 수 있는지 보여준다.

캐시 문서가 만료되기 전이라면 캐시는 서버와의 접촉없이 사본을 제공할 수 있다. 그러나 캐시된 문서가 만료되면 캐시는 반드시 서버와 문서 사이의 변경점을 검사하고, 변경되었다면 새 유효기간이 달린 신선한 사본을 가져와야한다.

2) 유효기간과 나이

서버의 응답 헤더로 아래의 내용이 전달된다.

헤더설명
Cache-Control: max-agemax-age는 문서의 최대 나이를 정의한다.
문서가 처음 생성된 이후부터 신선하지 않다고 변경될 때까지의 경과한 시간의 합법적 최대값(초 단위)이다.
Cache-Control: max-age=484200
Expires절대 유효기간을 명시한다.
유효기간이 경과했다면 문서는 더 이상 신선하지 않다.
Expires: Fri, 05 Jul 2002, 05:00:00 GMT

3) 서버 재검사

캐시된 문서가 만료되었다는 것은 문서와 원 서버의 데이터가 다르다는 것을 의미하지는 않는다.
다만 검사할 시간이 되었음을 의미하며, 문서가 변경되었는지 여부를 물어보는 것을 서버 재검사 라고 부른다.

  • 재검사 결과 컨텐츠가 변경되었다면 캐시는 새로운 사본을 가져와 오래된 데이터 대신 저장한 뒤 클라이언트에게도 보내준다.
  • 재검사 결과 컨텐츠가 변경되지 않았다면, 캐시는 새 만료일을 포함한 새 헤더들만 가져와서 캐시 안의 헤더들을 갱신한다.

캐시는 문서의 신선도를 매 요청마다 검증할 필요가 없고 문서가 만료되었을 때 한번만 서버와 재검사를 하면 된다.
서버 트래픽을 절약하고 사용자 응답 시간을 개선하는 방법이다.

HTTP 프로토콜이 요구하는 캐시의 행동

캐시는 다음 중 하나를 반환하는 행동이 요구된다.

  • 충분히 신선한 캐시된 사본
  • 원 서버와 재검사되었기 때문에 충분히 신선하다고 확신할 수 있는 캐시된 사본
  • 에러메시지(재검사 해야하는 원 서버가 다운된 경우)
  • 경고 메시지가 부착된 캐시된 사본

4) 조건부 메서드와의 재검사

HTTP는 캐시가 서버에게 조건부 GET 이라는 요청을 보낼수 있도록 해주며, 서버가 갖고 있는 문서가 캐시가 갖고 있는 것과 다른 경우에만 객체 본문을 보내달라고 한다. 조건부 GETGET 요청 메시지에 특별한 조건부 헤더를 추가함으로써 시작된다. 웹 서버는 조건이 참인 경우에만 객체를 반환한다.

헤더설명
If-Modified-Since: 문서가 주어진 날짜 이후로 수정되었다면 요청 메서드를 처리한다.
캐시된 버전으로부터 콘텐츠가 변경된 경우에만 콘텐츠를 가져오기 위해 Last-Modified 서버 응답 헤더와 사용된다.
If-None-Match: 마지막 변경된 날짜를 맞춰보는 대신, 서버는 문서에 대한 일련번호와 같이 동작하는 태그를 제공할 수 있다.
If-None-Match 헤더는 캐시된 태그가 서버에 있는 문서 태그와 다를 때만 요청을 처리한다.

5) If-Modified-Since: 날짜 재검사

가장 흔히 쓰이는 캐시 재검사 헤더는 If-Modified-Since 이다.
IMS 요청 이라고 불리며, 서버에게 리소스가 특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 한다.

  • 만약 날짜가 변경되었다면, 조건은 참이되고, GET 요청은 성공하며, 새 문서에 만료 날짜와 그 외의 정보들이 담긴 헤더와 함께 캬시에 반환된다.
  • 만약 날짜 이후에 변경되지 않았다면, 조건은 거짓이고, 서버는 304 Not Modified 를 클라이언트에게 돌려주며, 별도의 본문은 전달하지 않는다. 응답은 헤더 및 갱신에 필요한 내용만 전달한다.
    예를 들어 Content-Type 헤더는 잘 변하지 않기에 보내지 않고, 새 만료 날짜는 보내준다.

If-Modified-Since 헤더는 응답 헤더인 Last-Modified 와 함께 동작한다.
원 서버는 제공하는 문서에 최근 변경 일시를 붙이고, 재검사하려고 할 때 사본이 마지막 수정된 날짜가 담긴 if-Modified-Since 헤더를 포함한다.

6) If-None-Match: 엔터티 태그 재검사

최근 변경 일시 재검사가 행해지기 어려운 경우, 태그를 비교해서 새 객체를 요청하는 방법으로 재검사를 진행하는 방법이다.

  • 일정 간격으로 다시 쓰여지지만 동일한 데이터를 포함하는 문서
  • 철자나 주석의 변경같이 사소한 변경으로, 전 세계 캐시가 변경하기엔 불필요한 문서

태그가 변경되지 않은 경우 새 객체를 요청하는 방식으로 재검사 여부를 확인한다.

만약 위의 경우 엔터티 태그가 변경되었다면 서버는 200 OK 응답으로 새 콘텐츠를 새 ETag 와 함께 반환한다.

7) 약한 검사기와 강한 검사기

약한 검사기 란 모든 캐시된 사본을 무효화시키지 않고 문서의 특정 부분만 고칠 수 있도록 허용하고 싶은 경우 사용하기 위한 방법이다. 단, 중요한 내용이 변경된 경우 변경된다. 이 방법은 HTTP/1.1 부터 지원한다.
추가적으로 조건부 특정 범위 가져오기 같은 몇몇 동작들은 서버에서 구분하지 못하기 때문에 W/ 같은 접두사로 약한 검사기를 구분한다.
강한 검사기 란 콘텐츠가 바뀔때 마다 바뀐다.

8) 언제 태그를 사용하고 언제 Last-Modified 일시를 사용하는가?

  • 서버가 엔터티 태그를 반환했다면 반드시 엔터티 태그 검사기를 사용해야한다.
  • 서버가 Last-Modified 값만 반환했다면 클라이언트는 If-Modfied-Since 검사를 사용할 수 있다.
  • 엔터티 태그와 최근 변경 일시가 모두 사용 가능하다면 HTTP/1.0 과 HTTP/1.1 캐시 모두 적절히 응답할 수 있도록 클라이언트는 각각을 위해 두 가지의 재검사 정책을 사용해야한다.
  • HTTP/1.1 원 서버는 실현 불가능하지만 않다면 엔터티 태그 검사기를 보내야하며, 강한 엔터티 태그 대신 약한 엔터티 태그를 보낼수도 있다. 또한 Last-Modified 값을 같이 보내는 것도 선호된다.
  • HTTP/1.1 캐시나 서버가 If-Modified-Since 와 엔터티 태그 조건부 헤더를 모두 받았다면 요청의 모든 조건부 헤더 필드의 조건에 부합되지 않는 한 304 Not Modified 를 반환해서는 안된다.

9. 캐시 제어

HTTP는 문서가 만료되기 전까지 얼마나 오랫동안 캐시될 수 있게 할 것인지 서버가 설정할 수 있는 여러 가지 방법을 정의한다.

  • Cache-Control: no-store >> 응답의 사본을 만드는 것을 금지한다.
  • Cache-Control: no-cache >> 서버와 재검사를 하지 않고서는 캐시에서 클라이언트로 제공하지 않는다.

no-store와 no-cache 헤더는 캐시가 검증되지 않은 캐시된 객체로 응답하는 것을 막는다.

  • Cache-Control: must-revalidate >> 캐시가 이 객체의 신선하지 않은 사본을 원 서버와의 최초의 재검사 없이는 제공해서는 안됨을 의미한다. 또한 캐시가 신선도 검사를 시도했을 때, 원 서버가 사용할 수 없는 상태라면 캐시는 반드시 504 Gateway Timeout error 를 반환해야 한다.
  • Cache-Control: max-age >> 신선하다고 간주된 문서가 서버로부터 온 이후로 흐른 시간이다.
  • Expires >> 초 단위의 시간 대신 실제 만료 날짜

-- 휴리스틱 만료

Cache-Control: max-ageExpires 헤더를 포함하지 않은 경우 사용되며, 캐시는 경험적인 방법으로 최대 나이를 계산한다.
LM 알고리즘에 의해 동작하며, 신선도 유지기간의 상한선을 설정하여 운영한다.
아무런 단서가 없을 경우는 기본 신선도의 유지기간은 1시간에서 하루로 설정된다.

-- 클라이언트 신선도 제약

클라이언트는 Cache-Control 요청 헤더를 사용하여 만료 제약을 엄격하게 하거나 느슨하게 할 수 있다.

주의할 점

문서 만료 기간을 잘못 설정했다면 어떤 변경사항도 캐시에 반영되지 않는 점을 주의해야한다.

profile
어제보다 오늘 더, 오늘보다 내일 더

0개의 댓글