브라우저 캐싱 활용 하기

wwlee94·2024년 5월 28일
0

트러블 슈팅

목록 보기
3/3
post-thumbnail

들어가며

이전에 포스팅에서는 CloudFrontLambda@Edge 를 활용하여 이미지를 리사이징 및 압축해서 이미지를 최적화 했던 후기를 공유 해드렸습니다.

11MB 이미지 파일을 적절한 손실 압축, 리사이징 옵션을 통해 8.9KB 크기로 대략 1000배 가까이 줄일 수 있었는데요.

서비스를 운영하던 도중 아래와 같은 현상을 보게 되었습니다.

이미지는 정상적으로 리사이징이 되었는데 304 Not Modified 로 응답되는 요청들은 몇가지 특징이 있었는데요.
1. HTTP Status가 304 Not Modified 로 응답
2. 크기가 동일하게 340B-350B 크기로 응답
3. 응답 속도가 50ms 내로 상당히 빠름

응답이 304 Not Modified 인 요청의 응답 헤더의 상세 내용을 확인해 봤습니다.

X-Cache 헤더에 Hit from cloudfront 라는 값이 들어가 있고 분명 CDN에는 캐싱된 데이터를 조회한 것인데 200도 아니고 왜 304가 뜬 것일까?

이에 대한 궁금증과 해결 방법을 이번 글에서 공유하려고 합니다.

먼저, 브라우저 캐싱에 대해서 알아봅시다.

브라우저 캐싱이란?

프로그래밍을 하다보면 마주하는 캐싱의 종류는 굉장히 다양하게 존재합니다.
CPU 캐시, Redis 캐시, JPA 영속성 캐시, CDN 등등 다양한 레이어에서 최적화를 위한 캐싱 기술이 많습니다.

이와 같은 캐싱 기능을 브라우저에서도 제공하고 있습니다.

클라이언트는 서버에서 HTTP 네트워크 통신을 통해서 리소스를 가져오게 되는데
이때, HTTP 커넥션을 시작으로 데이터를 받기까지 데이터의 크기가 클 수록 왕복이 여러번 발생해 불필요한 네트워크 비용이 많이 발생하게 됩니다.

브라우저 캐싱은 동일한 리소스를 요청할때 브라우저 단에서 리소스를 캐싱하여 서버와의 통신 없이 바로 리소스를 제공할 수 있는 기능을 제공합니다.

HTTP 캐시의 동작 방식

캐시의 동작은 Request HeaderResponse Header 의 조합으로 제어가 됩니다.

브라우저의 모든 HTTP 요청은 브라우저 캐시로 라우팅된 후 사용할 수 있는 유효한 캐시가 있는지 확인하고 있으면 저장된 캐시를 사용하기 때문에 네트워크 지연 시간과 전송 데이터 비용이 모두 없어집니다.

캐시 제어

Cache-Control 헤더

캐싱 정책을 정의할때 사용되는 헤더로 아래와 같은 주요 옵션이 있습니다.

Cache-Control 헤더 주요 파라미터

  • no-store : 클라이언트 요청, 서버 응답 관련하여 어떤 것도 저장하지 않음
  • no-cache : 캐시하지만 , 항상 Origin Server 에 검증후 사용
  • public : 공유 서버 (프록시 서버)의 캐싱 + 브라우저 캐시 허용
  • private : 브라우저만 캐시를 저장
  • max-age : 캐시의 유효 시간 (초)

Cache-Control 예시

캐시 재검증

캐시의 유효 기간이 지난 이후에는 어떻게 동작할까요?

리소스를 새로 다시 요청하는 것 같지만, 재검증 헤더를 통해서 브라우저에 저장된 캐시가 유효한지 재검증하는 절차를 거칩니다.

응답 헤더ETag 또는 Last-Modified 를 설정하면 If-Modified-Since 또는 If-None-Match 요청 헤더를 트리거하도록 하여 재검증을 수행할 수 있습니다.

ETag 헤더

  • 특정 버전의 리소스를 식별하는 고유 식별자
  • If-None-Match 요청 헤더와 함께 사용
  • 파일이 변경될 경우 해당 해시는 계속 변경됨

Last-Modified 헤더

  • 데이터의 최종 수정 시각
  • If-Modified-Since 요청 헤더와 함께 사용

브라우저는 ETag 응답 헤더로 받았던 값을 서버로 If-None-Match 요청 헤더에 값을 넣어 전달하거나
Last-Modified 응답 헤더로 받았던 값을 서버로 If-Modified-Since 요청 헤더에 값을 넣어 전달합니다.

HTTP 표준에 따르면 If-None-MatchIf-Modified-Since 보다 높은 우선순위를 가져야합니다.

이렇게 전달 된 경우 웹서버에서는 아래와 같이 동작합니다.

  • 브라우저의 HTTP 캐시가 웹 서버의 리소스와 동일하다.
    • 304 Not Modified 응답
      • 저장했던 값 그대로 사용해도 된다.
  • 브라우저의 HTTP 캐시가 웹 서버의 리소스와 다르다.
    • 200 OK 응답
      • 새로운 데이터 전송 (네트워크 다운로드)

ETag 예시

원인 확인

위에서 설명한 것을 토대로 저희가 알 수 있었던 내용은 304 Not Modified 가 발생했던 원인은 브라우저 캐싱의 재검증 과정 떄문에 발생했다는 것을 알 수 있습니다.

304 Not Modified 로 응답 온 결과를 보면 크기는 대략 350B, 응답 시간은 대략 30ms 으로 짧았던 이유도 서버로부터 온 응답이 실제로 콘텐츠를 전송하지 않았기 때문이었습니다.

두번째 의문점

그런데? 한가지 의문점이 다시 생깁니다.

304 Not Modified 응답이 내려왔다는 것은 브라우저 캐싱이 적용이 된 것이고 캐싱 정책을 적용하려면 Cache-Control 헤더를 적용 해야하는데 CloudFront의 응답 헤더에는 캐시 관련 헤더가 전혀 없습니다.

얼마나 캐싱되었는지 시간을 나타내주는 Age 헤더는 존재하나 Cache-Control 응답 헤더는 보이지 않습니다.

특정 이미지들을 확인해보니 200 OK 를 내려주며 브라우저 캐싱이 정상적으로 적용된 상태였습니다.

확인해보니 해당 현상의 원인은 MDN Docs 설명에 잘 나와있습니다.

요약하자면, Cache-Control 응답 헤더가 명시되지 않아도 캐싱이 되는 이유는 브라우저 내부적으로 heuristic expiration 시간을 계산하여 캐싱 보관 기간을 정하기 때문에 브라우저 캐싱 자체가 동작을 했던 것입니다.

304 Not Modified 가 뜨는 이유는 명시적 캐싱 만료 기간이 없기 때문에 브라우저가 캐싱이 만료되었다 판단해서 원본 서버로 재검증을 매 요청마다 보냈던 것으로 파악되었습니다.

해결 방법

명시적으로 Cache-Control 헤더를 적용시키면 지정된 만료 시간까지는 재검증 요청을 보내지 않을 것이므로 정상 동작할 것이라고 판단했습니다.

그래서, Devops팀에 요청하여 CloudFront의 응답 헤더에 Cache-Control 헤더를 설정하여 내릴 수 있도록 응답 헤더 설정 변경 요청을 드렸습니다.

  • 캐시 기간은 7일로 설정

결과

명시적으로 Cache-Control 설정 후 정상적으로 브라우저 캐싱이 잘 동작하는 것을 확인하였습니다.

번외

S3, CloudFront를 적용한 경우 ETagLast-Modified 응답 헤더는 어디서부터 생성되어 응답되는 걸까?

정답은 AWS S3 원본 서버로부터 생성되어 CloudFront에 캐싱된 후에 클라이언트 응답으로 내려가게 됩니다.

응답 헤더에 잘보면 Server 헤더에 AmazonS3 가 명시되어 있습니다.

요약 및 마무리

  • 서비스 운영 중 발생한 304 Not Modified 문제는 브라우저 캐싱 재검증 과정에서 발생했습니다.
  • 브라우저가 Cache-Control 헤더 없이도 휴리스틱 만료를 계산해 캐싱을 수행하지만, 명시적 캐시 설정이 없으면 매번 재검증 요청을 보냅니다.
    • 이를 해결하기 위해 CloudFront 응답 헤더에 Cache-Control 을 추가하여 캐시 만료 시간을 명시적으로 설정했고, 이후 브라우저 캐싱이 정상적으로 작동하는 것을 확인했습니다.
  • S3 원본 서버에서 생성된 ETagLast-Modified 헤더는 CloudFront를 거쳐 클라이언트로 전달됩니다.

백엔드 개발자로 업무하다보면 백엔드 관련 지식뿐 아니라 인프라 지식 그리고 웹 브라우저의 동작 등을 자세히 알고 이해하고 있어야 빠른 원인 파악 및 해결이 가능함을 느꼈습니다.
다양한 분야의 지식을 이해하고 활용할 수 있는 백엔드 개발자로 성장해야겠다고 느껴지는 경험이었습니다.

레퍼런스

Web.dev - HTTP 캐시로 불필요한 요청 방지

웹 브라우저의 Cache 전략 & 헤더 다루기

MDN - HTTP 캐싱

Toss - 웹 서비스 캐시 똑똑하게 다루기

How does the browser cache work?

profile
개발 블로그 📝

0개의 댓글