ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [HTTP] 헤더 - 캐시와 조건부 요청
    HTTP 2023. 1. 29. 17:55

    캐시가 없을때

    만약 웹 브라우저가 서버에 GET/star.jpg라는 요청을 보낸다고 가정해보자. 서버는 웹브라우저의 요청을 받고 200상태코드와 함께 star.jpg파일에 대한 HTTP헤더: 0.1M, HTTP바디:1.0M 총 1.1M에 달하는 용량의 데이터를 보낸다. 이미지가 필요해서 다시 한번 브라우저가 방금과 똑같은 GET/star.jpg요청을 했다. 만약 캐시가 없다면 서버는 1.1M용량의 데이터를 또 클라이언트에 보내게된다.

    • 같은요청 즉, 데이터가 변경되지 않아도 계속 네트워크를 통해서 데이터를 다운로드 받아야한다.
    • 인터넷 네트워크는 매우 느리고 비싸기 때문에 효율적이지 못하다.
    • 브라우저 로딩 속도가 느려진다. 그리고 이는 사용자의 느린 경험으로 이어진다.

    캐시가 있을때

    -캐시 제어 헤더

    웹 브라우저는 서버에 GET/star.jpg요청을 보낸다. 이때 cashe-control: max-age=60(캐시가 60초까지 유효함)와 같이 캐시 제어 헤드 를 같이 보낸다. 이렇게 되면 1.1M의 jpg파일 + 60초동안 캐시가 유효하다는 응답결과가 브라우저 캐시에 저장되게 된다. 위 예제와 같이 브라우저가 다시한번 같은 사진을 요청했을때 ,브라우저 캐시의 캐시 유효 시간을 검증하고 유효하다면  바로 캐시에서 조회를 해 데이터를 얻을 수 있게 된다. 서버까지 가지 않아도 되는것이다!

     

    • 캐시 덕분에, 캐시 가능 시간동안 네트워크 사용(클라이언트-서버 통신)을 하지 않아도 된다.
    • 비싼 네트워크 사용량을 줄일 수 있다.
    • 브라우저 로딩속도가 빨라져 빠른 사용자 경험을 낳는다.

     

    캐시 유효시간 초과

    만약 위의 예제에 이어 3차로 웹 브라우저가 같은 파일을 서버에 요청했다고 가정해보자, 캐시가 있으므로 캐시 유효시간을 먼저 검증할것이다. 그런데 캐시 시간 60초가 초과하여 더이상 유효하지않다. 이렇게 되면 웹 브라우저는 서버로 재요청을 보내게된다. 서버는 요청결과1.1M와 함께 다시 캐시정보를 담아서 응답한다.

     

    • 캐시유효 시간이초과하면, 서버를 통해 데이터를 다시 조회하고 캐시를 갱신한다
    • 이때 다시 네트워크 다운로드가 발생한다. 

    캐시 시간이 초과된후 서버에 다시 요청하면 다음 두가지 상황이 나타난다.

    1. 서버에서 기존 데이터를 변경한경우 -> 캐시로 데이터 다시 받아야함 (재요청)
    2. 서버에서 기존 데이터를 변경하지 않은 경우 -> 캐시로 다시 데이터 받을 필요없이, 기존에 사용자 캐시에 갖고있는 데이터가 서버 데이터와 같다는 사실확인만 한다면 불필요한 재요청 없이 이전에 쓰던 캐시 사용가능. (검증헤더, 조건부요청)
    만약 2. 경우라면 어차피 서버에서 데이터를 변경하지 않았기때문에 데이터를 다시 전송하는것보다 저장해 두었던 캐시를 재사용하는것이 효율적일 것이다. 단, 클라이언트의 데이터와 서버의 데이터가 같다는 사실만 확인할수있다면 OK 이떄 사용하는것이 이번 글의 주제인 검증헤더와 조건부 요청이다.

     


    검증헤더 

    캐시(클라이언트) 데이터와 서버 데이터가 같은지 검증하는 데이터(장치)

    ex) Last-Modified, ETag

     

    조건부 요청 헤더

    • 검증헤더정보를 통해 조건에 따른 분기를 만듦(쉽게 말해 캐시에 저장했던 검증헤더정보를 다시 서버에 보내 확인받게함)
    • 조건이 만족하면 200OK (수정되었음), 조건이 만족하지 않으면 304 Not Modified (수정되지 않았음)

    ex) If-Modified-Since, If-Unmodified-Since  (Last-Modified 와 사용)

    ex) if-None-Match, If-None-Match (ETag와 사용)

     

    예시1) 검증헤더의 동작원리  Last-Modified
    1. 캐시 유효 시간 초과후 클라이언트의 요청에 서버는 서버의 데이터가 갱신되지 않았다면 304상태코드+ 헤더 메타 정보(0.1M)만 응답한다.
    2. 클라이언트는 서버가 보낸 응답헤더 정보로 캐시의 메타정보를 갱신한다.
    3. 클라이언트는 캐시에 저장되어 있는 데이터를 재활용한다.
    4. 결과적으로 네트워크 다운로드가 발생하지만 용량이 적은 헤더 정보만 다운로드되어 매우 실용적인 해결책이된다.
    5. 어떤 해결책? 캐시이용시간 초과후 재요청할때마다 모든 데이터를 받는것에대한 해결책 -> 검증헤더, 조건부요청

    캐시를 사용하는 서버 클라이언트 관계에서, 첫번째 요청에 서버는 Last-Modified라는 검증헤더를 포함해서 응답한다.

    ex) 요청 1에대한 서버의 응답 http
    cache-control: max-age=60  //캐시제어 헤더
    Last-Modified:2020년11월10일 10:00:00)  //검증 헤더

    응답받은 웹브라우저 캐시에는 검증헤더의 응답값을 기록해둔다 (ex 데이터 최종 수정일 2020년 11월 10일 10:00:00), 두번째 요청이 있을때 캐시 시간이 초과된경우 웹 브라우저는 검증헤더로부터 받은 정보로 조건부 요청(if-modified-since)을 하게된다.

    2) 캐시시간이 초과된 클라이언트의 요청 2
    GET /star.jpg
    if-modified-since: 2020년 11월 10일 10:00:00 //조건부 요청

    만약 조건부요청으로 보낸 데이터 최종수정일과, 서버의 데이터 최종 수정일이같다면 데이터가 아직 수정되지 않은것으로, 서버는 304상태코드와 함께 캐시 유효시간을 포함한 HTTP 헤더만 재전송하게된다.(데이터 안바꼈으니까 그냥 너가 갖고있던 캐시 쓰거라~) body는 전송하지않는다.(우린 데이터 다시안보낸다~) 브라우저는 갱신된 헤더 데이터를 통해 응답결과를 재사용 할 수있고, 헤더만 보내므로써 네트워크 사용도 줄일 수 있다.

     

     

    <정리>

    If-MOdified-Since : 조건부분기-> 이후에 데이터가 수정되었니?

    • NO : 304 + 헤더 데이터만 전송 , 전송용량 0.1M
    • YES : 200OK, 모든 데이터 전송, 전송용량 1.1M

    Last-Modified, If-Modified-Since의 단점

    • 1초미만 즉 0.1초 단위로 캐시 조정이 불가능하다. (ex 데이터 최종 수정일 2020년 11월 10일 10:00:00) -> 초단위임
    • 날짜 기반의 로직을 사용한다.
    • 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 데이터 결과가 똑같은경우(A->b->A)에도 캐시 재사용하지 못하고 재요청해야함.
    • 서버에서 별도의 캐시 로직을 관리할 수 없음 (수정된 날짜만으로 조건분기를 하다보니, 유연하지못함)
    • 예를 들어 스페이스나 주석처럼 크게 데이터에 영향을 끼치지 않는 변경에서도 캐시를 유지할수 없음. (개발자는 별로 영향을 끼치지 않는 수정이기때문에 캐시를 유지하고 싶으나, Last-Modified, If-Modified-Since는 수정되기만 하면 무조건 캐시정보를 재요청하게 만들기때문에..)

    이를 해결하기 위해 고안된 것이 ETag , If-None-Match 이다.

      • Etag헤더는 캐시용 데이터에 임의의 고유한 버전 이름을 달아둔다. ( ETag: "v1.0", ETag: "a2jiodwjekjl3")
      • 데이터가 변경되면 이 이름을 바꾸어서 변경한다. ( ETag: "aaaaa" -> ETag: "bbbbb")
      • 단순하게 ETag만 서버에 보내서 이름이 같으면 유지하고, 다르면 캐시정보 다시 받는것이다. (시간과 무관함)
      • 캐시 제어 로직을 서버에서 완전히 관리한다.
      • 클라이언트는 단순히 이 값을 서버에 제공할뿐 캐시 메커니즘을 전혀 모르게 설계할 수 있다.
      • ex) 애플리케이션 배포 주기에 맞추어 ETag 모두 갱신

     

    캐시 제어 헤더

    Cache-Control (캐시 지시어)

    Cache-Control : max-age

    • 캐시 유효시간, 초 단위

     

    Cache-Control : no-cache

    • 데이터는 캐시해도 되지만, 항상 원서버에 검증하고 사용 (client <> 캐시 프록시서버 <> 원서버)

     

    Cache-Control : no-store

    • 데이터에 민감한 정보가 있으므로 저장(하드디스크에)하면 안됨 (메모리에서 사용하고 최대한 빨리 삭제 필요)

     

     

    Pragma (캐시 제어)

    Pragma : no-cache

    • HTTP 1.0에 쓰이는 하위호환을 위한 제어 헤더이다. 거의 사용하지않는다.

     

    Expires (캐시 만료일 지정)

    expires: Mon, 01 Jan 1990 00:00:00 GMT

    • 캐시 만료일을 정확한 날짜로 지정한다.
    • HTTP 1.0에 쓰이는 하위호환을 위한 제어 헤더이다.
    • 지금은 더 초단위로 더 유연한 Cache-Control : max-age를 권장한다.
    • Cache-Control과 함께 사용하면 Expires는 무시된다.

    프록시 캐시

    사실 클라이언트와 서버는 직접연결하는 것이 아니라, 그 중간에 있는 프록시 서버를 거쳐서 연결된다.

    예를 들어보자

    만약 한국에 있는 클라이언트가 미국에 있는 원서버에 직접 접근한다면 (0.5초 가걸림) 시간이 너무 오래걸린다. 그러나 한국 어딘가에 미국에 있는 원서버와 연결되어 있는 프록시 캐시 서버가 있다면 사용자는 더 빠르게(0.1초)만에 원하는 캐시정보를 얻을 수 있다.

    프록시캐시 서버는 미국 원서버에서 자주 요청되는 캐시 정보들의 응답을 기억했다가 클라이언트에 그대로 응답해주는 중간 다리 역할이다. 만약 우리가 youtube로 외국영상을 보면 느린데.. 남들 다보는 한국영상보면 빠르다. ->프록시 캐시 서버가 있기 때문에

    (여기서 public은 프록시캐시 서버이고, private는 클라이언트이다.)

    Cache-Control : public

    • 응답이 public 캐시에 저장되어도 됨

     

    Cache-Control : private

    • 응답이 해당 사용자만을 위한 것임, private캐시에 저장해야함 (기본값이다.)

     

    Cache-Contorl : s-maxage

    • 프록시 캐시에만 적용되는 max-age

     

    Age:60 

    • 원 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)

     

     

    캐시 무효화

    확실한 캐시 무효화 응답에는 아래와 같은 예가 있다.
    Cache-Control: no-cache, no-store, must-revalidate
    Pragma : no-cache

    Cache-Control : no-cache

    • 데이터는 캐시해도 되지만, 항상 원 서버에 검증하고 사용
    • 원 서버에 접근하지 못하는 경우 프록시 캐시를 통해 오래된 데이터라도 받음

     

    Cache-Control : no-store

    • 데이터에 민감한 정보가 있으므로 저장하면 안됨.

     

    Cache-Control : must-revalidate

    • 캐시 만료 후 최초 조회 시 원 서버에 검증해야함.
    • 원 서버 접근 실패시 반드시 오류가 발생해야함 - 504

     

     

    no-cache vs must-revalidate

    no-cache는  항상 원 서버에 검증하고 사용하는것이 일반적이지만, 만약 순간적인 네트워크 단절로 원서버에 접근하지 못하는 상황이 생기는 경우 캐시 서버 설정에 따라 프록시 캐시에서 캐시 데이터를 반환할 수 있다. (오류를 주는것보다는 오래된 데이터라도 주자 !!)

     

    must-revalidate는 위와 동일한 상황일때 즉 원 서버에 접근할 수 없는경우, 무조건적으로 504 오류를 발생시킨다 (매우 중요한 돈과 관련된 결과로 생각해보자, 오래된 데이터를 줄빠엔 안주는게 낫다!)

    댓글

lee-ding