캐시와 조건부 요청 프록시 캐시

개발로 쓰는 개발 노트·2023년 6월 14일
0

캐시(Cache)란?

  • 자주 사용하는 데이터나 값을 미리 복사해 놓는 임시 장소
  • 저장공간이 작고 비용이 비싼 대신 빠른 성능을 제공한다.
  • 캐시는 웹 페이지 요소를 저장하기 위한 임시 저장소로 웹 페이지를 렌더링할 때 발생하며 웹 페이지를 빠르게 렌더링 해주는 역할을 한다. 주로 오디오, 비디오 파일을 저장하고 클라이언트가 직접 삭제해야 한다.
  • 쿠키와 유사하다고 생각할 수 있는데 쿠키는 보통 특정한 웹 사이트를 누군가 접속하여 로그인과 같은 인증을 할 때 발생하며 사용자의 인증을 도와주고 로그인 정보나 방문 기록등을 저장한다. 주로 정보를 저장하는 것이며 만료기간이 있어서 시간이 지나면 자동으로 삭제된다.

캐시의 기본 동작

캐시가 없을 때

  • GET방식을 통해 이미지를 요청했다.
  • 서버에서는 응답으로 특정 이미지를 응답했다.
  • 해당 이미지가 1GB라고 가정하자.
  • 캐시가 없다면 요청을 할 때마다 1GB의 네트워크를 차지하면서 이미지를 클라이언트가 가져간다.
  • 계속해서 메모리의 손실이 발생하는 것이다.

  • 캐시가 없다면 데이터를 사용해서 다운로드 받아야한다.
  • 상대적으로 인터넷 네트워크는 컴퓨터의 하드디스크에 비해서 매우 비싸다.
  • 클라이언트는 계속해서 느린 경험을 하게 된다.

캐시가 있다면?

  • 서버에서 캐시에 대한 세팅을 우선 해야한다.
  • cache-control: max-age=60 과 같은 세팅이 필요하다. 이는 60초간 캐시를 유지한다는 뜻이다.
  • 웹 브라우저에는 캐시 저장소가 존재한다.
  • 클라이언트가 서버에 이미지를 요청했다.
  • 서버는 캐시 저장소에 이미지를 저장시켰다.
  • 60초 이내에 다시 클라이언트가 이미지를 요청한다면
  • 하드디스크에서 바로 불러오기 때문에 빠른 경험, 네트워크 손실이 발생하지 않는다.
  • 60초가 초과되면 당연히 서버에서 다시 다운로드 받아야한다. 그 결과 기존 이미지를 지우고 다시 캐시 저장소에 저장한다.
  • 다시 다운로드 받으면서 네트워크 다운로드가 발생한다.
  • 그런데 이미지가 똑같은데 유효기간이 지났다고 해서 캐시 저장소에 있는 이미지를 지우고 다시 다운로드를 받아야 할 이유가 있을까?

캐시 시간 초과

  • 캐시 유효 시간이 초과했을 때 두 가지 경우가 있다.
  1. 서버에서 기존 데이터가 변경되었을 경우
  2. 서버에서 기존 데이터가 변경되지 않았을 경우

1번의 경우에는 다시 받아야한다. 데이터가 변경되었기 때문에! 그런데 2번의 경우에는 네트워크가 매우 아깝다. 이를 해결하기 위해 검증헤더라는 것이 등장한다.


검증헤더

  • Last-Modifed: 2020년 11월 10일 10:00:00
    위와 같이 서버에서 응답한다. 마지막 변경 시점을 추가하는 것이다.
  • 캐시 저장소에 해당 검증헤더 까지 같이 저장한다.
  • 그러면 캐시 저장소에서 해당 검증 헤더를 비교해본다.
  • 만약 검증헤더가 같으면? 다운로드 받을 필요 없이 기존에 있던 것을 사용하는 방식이다.

검증헤더를 추가하고 캐시를 이용하는 방식

  • 클라이언트가 이미지를 서버에 요청한다.
  • 서버는 이미지를 캐시 저장소에 저장하면서 검증헤더를 같이 넣어준다.
  • 클라이언트는 max-age 60초(캐시 유효기간)가 경과된 뒤 다시 요청한다.
  • 클라이언트에서 요청할 때 if-modified-since에 받은 캐시의 검증헤더를 보낸다.
  • 서버는 응답하기 위해 last-modified의 검증헤더를 if-modified-since의 값과 서로 비교한다.
  • 값이 같다면 서버에서는 캐시 저장소에 있는 것을 다시 사용하라고 응답하며 304 Not Modified라고 응답한다.(변경된 사항 없음)
  • 그리고 응답 메시지에 HTTP Body가 없다.
  • 수정된게 없기 때문에 위의 방식으로 네트워크 부하를 줄일 수 있는 것이다.

검증헤더를 통해 캐시를 이용하는 방식

  • 캐시 유효 시간이 초과해도, 서버의 데이터가 갱신되지 않으면
  • 304 Not Modified + 헤더 메타 정보만 응답(Body 존재하지 않는다.)
  • 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신한다.
  • 클라이언트는 캐시에 저장되어 있는 데이터를 재활용 한다.
  • 결과적으로 네트워크 다운로드가 발생하지만 용량이 적은 헤더 정보만 다운로드 한다.
  • 이는 매우 실용적인 해결책으로 지금도 많이 쓰이고 있는 방식이다.

검증헤더와 조건부 요청

  • 검증헤더
    • 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터
    • Last-Modified, ETag
  • 조건부 요청 헤더
    • 검증 헤더로 조건에 따른 분기
    • If-Modified-Since: Last-Modified 사용
    • If-None-Match: ETag와 함께 사용한다.
    • 조건이 만족하면 200 OK
    • 조건이 만족하지 않으면 304 Not Modified를 발생시킨다.
    • 조건부 요청 헤더는 If-Match, If-None-Match에서 ETag를 사용하고
    • If-Modified-Since, If-Unmodified-Since에서 Last-Modified를 사용한다.

If-Modified-Since

  • 이후에 데이터가 수정되었는지 물어본다.(요청메시지에 쓰인다.)
  • 이 때 데이터가 변경되지 않았다면 If-Modified-Since와 Last-Modified의 값이 같게 된다.
    • 그럴 경우 304 Not Modified(변경 값 없음)을 발생하며 캐시 저장소에서 기존 캐시를 가져온다.
    • HTTP Header 영역만 응답하며 Body영역은 응답하지 않기 때문에 네트워크 부하를 줄일 수 있다.
  • 이 때 데이터가 변경되었다면 If-Modified-Since와 Last-Modified의 값이 다르게 된다.
    • 200 OK 를 발생하고 재전송을 시작한다.
    • 서버에서는 HTTP Header와 Body 모두 전송해야한다.

If-Modified-Since의 단점

  • 이는 1초 미만 단위로 캐시 조정이 불가능하다.
  • 날짜 기반의 로직 사용
  • 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 결과가 같은 경우
    • 컨텐츠를 변경하지 않았는데 수정을 시도하면 날짜가 갱신되었다. 그럼에도 전체를 다시 다운로드한다.
  • 서버에서 별도의 캐시 로직을 관리하고 싶은 경우
    • 크게 영향을 주는 변경이 아니어서 캐시가 유지되었음 하는 경우에도 자동으로 다운로드한다.

ETag(Entity Tag)

  • 위의 단점을 개선하기 위해서 ETag를 사용한다.
  • 날짜가 아닌 고유한 버전 이름을 붙여주는 것이다.
  • 수정을 시도해도 ETag의 이름을 동일하게 한다면 다운로드가 발생하지 않는다.
  • If-Modified-Since와 Last-Modified가 If-None-Match와 Etag로 변경되어 전송 응답한다.
  • 위와 로직은 동일하다.

ETag와 If-None-Match

  • 캐시 제어 로직을 서버에서 완전히 관리한다.
  • 단순 ETag만 서버에 보내서 같으면 유지하고 다르면 다시 받는다.
  • 클라이언트는 단순히 이 값을 서버에 제공한다.(클라이언트는 캐시 메커니즘을 모른다.)
  • 예시)
    • 서버는 베타 오픈 기간인 3일간 파일이 변경되어도 ETag를 유지
    • 애플리케이션 배포 주기에 맞춰서 ETag 모두 갱신
  • 위와 같이 사용된다.

캐시 제어 헤더

  • Cache-Control : 캐시를 제어한다.
  • Pragma : 캐시 제어(하위 호환)
  • Expires : 캐시 유효 기간(하위 호환)
  • 현재는 Cache-Control로 모든 기능을 수행할 수 있다.

Cache-Control(캐시 지시어)

  • Cache-Control: max-age
    • 캐시 유효 시간, 초 단위이다.
    • 보통은 굉장히 긴 시간을 입력한다.
  • Cache-Control: no-cache
    • 데이터는 캐시해도 되지만, 항상 원(origin)서버에 검증하고 사용한다.
    • 항상 검증할 것을 요청하는 것이다.
  • Cache-Control: no-store
    • 데이터에 민감한 정보가 있으므로 저장하면 안됨.
    • 메모리에서 사용하고 최대한 빠르게 삭제한다.

Pragma(캐시 제어, 하위 호환)

  • Pragma: no-cache
  • no-cache와 동일하게 작동한다.
  • HTTP/1.0에서 사용하는 하위 호환이다.
  • HTTP/1.0에서 사용해야할 때 주로 사용한다.

Expires(캐시 만료일 지정, 하위 호환)

  • 캐시 만료일을 지정할 수 있다.
  • 초 단위가 훨씬 유연하게 작동하기 때문에 하위 호환이다.
  • HTTP 1.0부터 사용하고 있다.
  • 지금은 더 유연한 Cache-Control: max-age를 권장하고 있다.
  • Cache-Control: max-age와 함께 사용되면 Expires는 무시된다.

프록시 캐시

  • 캐시가 직접 origin 서버에 캐시를 요청한다면 굉장히 오랜 시간이 걸린다.
  • 미국에 origin서버가 있다고 가정하고 요청 후 응답을 받는데 걸리는 시간이 0.5초라고 가정하자
  • origin서버가 한국에 있는 프록시 캐시 서버에 0.5초에 걸려 미리 캐시를 저장해둔다.
  • 한국 클라이언트는 origin서버가 아닌 한국용 프록시 캐시 서버에 요청을 해서 원 서버보다 훨씬 빠른 응답(예를 들면 0.1초?)을 받을 수 있게끔 한다.
  • 미국 대기업의 한국지부 느낌으로 생각하면 편할 듯 하다. 고객센터에 미국에 직접 요청하면 불편함이 있고 속도도 느리게 응답이 되겠지만 한국지부에 고객센터에 전화를 한 통 한다면 빠르게 응답하는 것처럼 캐시도 빠른 응답을 위해 프록시 캐시를 도입한다.
  • 이 때 프록시 캐시 서버에 등록된 캐시는 public 캐시라고 하고 클라이언트가 요청해서 얻는 캐시는 private 캐시라고 한다.

Cache-Control(프록시 캐시)

  • Cache-Control: public
    • 응답이 public 캐시에 저장되어도 된다.
  • Cache-Control: private(기본값)
    • 응답이 해당 사용자만을 위한 것이다.
    • private 캐시에만 저장해야한다.
    • 로그인 후 사용자 정보 이미지가 공용 캐시에 저장되면 안되기 때문에 기본값으로 가진다.
  • Cache-Control: s-maxage
    • 프록시 캐시 서버에서만 적용되는 max-age를 뜻한다.
  • age:60(HTTP 헤더)
    • 오리진 서버에서 응답 후 프록시 캐시 내에서 머문 시간(초)을 뜻한다.
  • age와 Cache-Control: s-maxage는 크게 중요하지 않다.

Cache-Control(캐시 무효화)

  • 확실한 캐시 무효화 응답
  • Cache-Control: no-cache, no-store, must-revalidate
  • Pragma: no-cache
    • HTTP 1.0 하위 호환
  • 위의 4가지를 전부 넣어줘야 확실하게 대응이 된다.

  • Cache-Control: no-cache
    • 데이터는 캐시해도 되지만 항상 원서버에서 검증할 것
  • Cache-Control: no-store
    • 데이터에 민감한 정보가 있으므로 저장하면 안된다.
    • 빠르게 쓰고 삭제한다.
  • Cache-Control: must-revalidate
    • 캐시만료 후 최초 조회 시 원 서버에서 검증한다.
    • 원 서버 접근 실패 시에는 반드시 오류가 발생해야 한다. 504(Gateway Timeout)
    • must-revalidate는 캐시 유효 시간이라면 캐시를 사용한다.
  • Pragma: no-cache
    • HTTP/1.0 버전에서 요청이 들어온다면 원서버에서 검증할 수 있도록 한다.
    • 하위 호환, 낮은 버전에서 요청이 올 수도 있기 때문에 명시해야 함.
    • 크로스 브라우징같은 느낌이라고 생각하면 될 듯 하다.

no-cache와 must-revalidate

  • 이 둘은 언뜻 보면 비슷해보인다.
  • no-cache는 항상 원 서버(origin)에 검증하고 사용한다.
  • must-revalidate는 캐시 만료 후 최초 조회 시 원 서버에서 검증한다.
  • no-cache는 프록시 캐시 서버에 요청한 뒤 프록시 캐시 서버는 처리하는 것을 회피한다. 원 서버에 올려보내고 원 서버는 응답하여 프록시 캐시 서버로 내리면 프록시캐시 서버에서 그 때 클라이언트로 응답해준다. 그 때 브라우저 캐시 저장소에서 해당 캐시를 사용할 수 있는 것이다.
  • 그러나 순간적으로 프록시 캐시 서버와 원 서버의 네트워크가 단절된다면 프록시 캐시 서버는 기존에 있는 것을 보여주는 경우가 있다.
  • 위의 경우에서 must-revalidate를 통해 항상 오류를 발생시켜 504 Gateway Timeout을 내려주어야 한다. 통장 잔고나 결제내역 등을 기존에 있던 프록시 캐시 서버에 있는 것을 내려준다고 하면 인터넷 결제를 해도 통장 잔고가 그대로인 캐시를 내려주면서 끔찍한 결과가 발생하는 것이다.
  • 이렇게 복잡한 이유는 과거부터 이를 무효화했기 때문이라고한다..

이로써 모든 개발자를 위한 HTTP 웹 기본 지식 강의의 정리가 끝났다. 상당히 유익했고 애매한 개념을 다시 정립하는 기회가 된 것 같아서 종종 까먹을 때 다시 보게될 것 같다. 그 때마다 부족한 부분이 있으면 채워넣으면서 완성시켜보려 한다.

비전공자다보니 처음 보는 개념이 많아서 강의를 3회정도 돌아본 것 같은데.. 이 다음 강의는 스프링 MVC 1편- 백엔드 웹 개발 핵심 기술을 정리해보려 한다.

그 전에 정보처리기사 자격증을 따야하기 때문에 이제 정처기 공부를 하며 요약 정리를 하는 시간을 가진 뒤 인강 정리를 해볼예정이다!

출처 : 모든 개발자를 위한 HTTP 웹 기본 지식(김영한 강사님 인프런 강의)

profile
비전공자 개발초보입니다!

0개의 댓글