자주 사용되는 HTTP 캐시 옵션 알아보기

Develop
자주 사용되는 HTTP 캐시 옵션 알아보기

안녕하세요! 오늘은 웹 서비스에서 자주 사용되는 HTTP 캐시 옵션에 대해서 알아보도록 하겠습니다.


이전 그리고 최근에 AWS의 CloudFront를 건드리는 일이 많았었는데, 이를 통해서 알게된 내용들을 정리해보도록 하겠습니다.

1. 캐시의 중요성 🚀

먼저 캐시란 시스템에서 데이터나 정보를 임시로 저장하는 공간을 의미합니다. 이는 이전에 가져온 데이터에 대해 기억하고, 이후 동일한 결과에 대해서 재연산없이 캐시에서 값을 가져와서 작업을 빠르게 처리할 수 있도록 도와줍니다. 즉 캐시는 시스템의 불필요한 연산을 없애고, 성능을 향상시키는 역할을 합니다.


웹 서비스에서는 어떤것들이 캐싱의 대상이 될까요? 바로 네트워크를 통해 다운로드 되는 모든 리소스들이 대상이 됩니다. 대표적으로 HTML/CSS, 자바스크립트, 폰트/이미지 파일들이 리소스에 해당됩니다.


이중에서 특정 리소스는 거의 내용이 바뀌지 않는데, 다른 리소스는 웹 페이지를 불러올때마다 내용이 변경될 수 있는 리소스들도 있습니다. 그래서 이를 HTTP 캐시를 사용하여 제어할 수 있는데요. 이를 밑 섹션에서 자세하게 알아보겠습니다.

2. HTTP 캐시 옵션 📗

기본적으로 리소스에 대해 HTTP 캐시를 지정하려면 Cache-Control 헤더를 통해서 지정할 수 있습니다. 그렇다면 이 Cache-Control 헤더에는 어떤 값들을 담을 수 있을까요?

http 1.0 하위 호환을 위해서 Pragma라는 헤더도 같이 사용하는 경우도 있습니다. (Cache-Control과 동일한 역할)

2-1. max-age

max-age 속성은 해당 리소스를 언제까지 브라우저 캐시에 저장해둘것인지를 지정하는 속성으로, 가장 대표적인 HTTP 캐시 속성이기도 합니다. 브라우저에서 캐시의 만료여부를 확인할때는 max-age의 값을 통해서 검증하기에 캐싱에 필수적입니다.

{
  "Cache-Control": "max-age=3600"
}

max-age는 초 단위로 지정을 해주어야 하는데요. 만약 위 코드처럼 3600 이라는 값을 넣는다면 브라우저는 3600초(1시간)가 지나기 전까지 저장된 캐시를 사용합니다.


만약 max-age로 설정한 시간이 지난경우에는 어떻게 동작할까요?

브라우저에서 캐시를 재검증했을때 새로운 값과 캐시가 같다면 304 Not Modified 응답을 받을 수 있습니다. 304 응답을 받았을때는 매우 적은 리소스(몇백 바이트 단위) 를 네트워크로 내려받기에 속도 측면에서 이점을 가집니다.

그러나 이전 캐시와 새로운 값과 다를경우에는 처음 리소스를 다운받을때와 동일하게 원본 용량을 네트워크로 다운받게 됩니다.

2-2. If-None-Match, If-Modified-Since

max-age로 지정한 시간이 지났을때 브라우저에서는 두 리소스가 동일한지 검증하는 절차를 진행하는데, 이 두개의 속성을 통해서 브라우저가 검증을 하게됩니다.

브라우저에서 검증시 자동으로 속성이 들어갑니다.

If-None-Match는 리소스의 ETag 속성이 같은지 검증하는데요. ETag란 간단하게 말해서 리소스를 해싱한 문자열 값이라고 생각하시면 됩니다. 리소스 내용이 변경되면 해싱 내용도 변경되기에 브라우저가 검증을 할 수 있습니다.


If-Modified-Since는 리소스의 Last-Modified 속성이 같은지 검증하는데요. Last-Modified는 리소스가 마지막으로 수정된 시간을 나타낸 값인데요. 리소스 내용이 수정되었을때 해당 값이 변경되므로 브라우저가 검증을 할 수 있습니다.

2-3. s-maxage

s-maxage 속성은 위의 max-age와 유사하지만 한가지 다른점이 있습니다. max-age는 브라우저에서 캐시를 저장하는 시간이지만 s-maxage중간 서버에서 캐시를 저장하는 시간입니다.

주로 원본 서버(Origin Server)에서 사용됩니다.

브라우저, 중간서버, 원본서버 관계도


중간 서버란, 리소스가 저장되어있는 원본 서버(Origin Server)브라우저 사이에 위치한 서버인데요. 가장 대표적인것이 CDN 서버입니다. CDN 서버는 어느곳에서나 접속하는 사용자들에 대해 물리적으로 가까운 서버에서 응답을 줄 수 있고, 원본 서버에서 가져온 리소스를 캐싱할 수 있기에 최근 웹 서비스에서는 필수적으로 사용됩니다.


CDN과 같은 중간서버에서도 언제까지 캐시를 저장할지에 대한 속성이 바로 s-maxage 입니다.

{
  "Cache-Control": "s-maxage=3600"
}

2-4. no-cache

no-cache 속성은 사용하려는 리소스를 캐시에 저장 가능하되, 항상 서버에 재검증 요청하도록 지정하는 속성입니다. 즉 304 Not Modified 응답을 받은 경우에만 리소스를 사용할 수 있습니다.

저는 처음에 이름을 들었을때는 캐싱을 완전히 안하겠다라는 뜻인줄 알았는데 아니더라고요.

{
  "Cache-Control": "no-cache"
}

눈치채신분들도 있겠지만, no-cachemax-age=0과 동일한 의미를 가집니다.

2-5. no-store

no-store 속성은 리소스를 절대로! 캐시에 저장하지 않도록 지정하는 속성입니다. 주로 민감한 리소스를 다룰때 사용됩니다. no-cache 속성은 리소스가 같을때는 적은량의 네트워크를 수신받지만, no-store 속성은 항상 원본용량의 네트워크를 수신받는 차이가 있습니다.

{
  "Cache-Control": "no-store"
}

2-6. must-revalidate

must-revalidate 속성은 캐시가 만료되고 재검증 요청을 했을때 만약 원본 서버(Origin Server)에서 오류가 발생한경우 타임아웃을 발생시킵니다.


만약 must-revalidate 속성이 없다면 원본 서버에서 오류가 발생한경우 중간서버는 이전 캐시를 다시 사용하게 됩니다. 만약 이전 캐시를 사용하는것을 원치않고, 오류를 발생시키는것을 원한다면 must-revalidate 속성을 사용해야 합니다.

{
  "Cache-Control": "max-age=6000, must-revalidate;"
}

3. 실제로 사용해보기 🔨

그렇다면 웹 서비스에서 이러한 캐시 속성들을 언제, 어떻게 사용할 수 있을까요? Next.js로 만들어진 웹 서비스에서 저는 아래와 같이 사용하고 있습니다.

3-1. HTML 파일

HTML 파일은 항상 동일한 URL을 가지고 있기 때문에 내용이 변경되는 타이밍을 예측하기 어렵습니다. 그렇기에 항상 사용하려고 할때마다 재검증을 요청하는 no-cache를 지정합니다.

3-2. CSS, JS 파일

항상 고유한 URL을 가진 CSS, JS 파일

Next.js에서 빌드를 하고나면 모든 CSS, JS 파일들은 고유한 URL을 가지고 있기 때문에 배포를 통해서 교체되면 URL이 자동으로 변경되어 새로운 캐시가 생성됩니다. CSS와 JS 파일은 절대 변경될 수 없는 캐시라고 생각하고 넉넉한 max-age (약 1년)를 지정합니다.

3-3. 이미지/폰트 파일

이미지와 폰트 파일 또한 내용이 변경된 경우에는 대부분 해당 파일의 URL이 변경될 수 있지만, 그렇지 않은 경우를 대비해서 no-cache를 지정해두고 중간서버에서 넉넉하게 s-maxage (약 1년)을 지정합니다.


그러나 만약 이미지 파일의 URL이 변경되지 않고 내용만 변경되었을때 중간 서버의 캐시를 초기화해야 하는데, 이는 어떻게 해결할 수 있을까요?

4. CDN Invalidation ♻️

CloudFront CDN 무효화

중간 서버의 캐시를 초기화 하기 위해서 대부분의 CDN에서는 CDN 무효화(Invalidation)를 제공하고 있습니다. 대표적인 CDN 플랫폼 CloudFront에서도 위처럼 무효화를 지원하는데요. 무효화 하려는 리소스의 경로를 와일드카드 패턴으로 지정해줄 수 있습니다.


예를 들어 s-maxage로 캐시를 관리하는 이미지 파일의 URL이 변경되지 않고 내용만 변경된 경우에 이처럼 무효화를 통해 중간 서버의 캐시를 초기화 해줄 수 있습니다.

그렇다면 max-age가 지정된 브라우저 캐시는 어떻게 초기화 하나요?

개발자단에서 사용자의 브라우저 캐시를 초기화 하는것은 불가능합니다. 그래서 max-age 속성은 항상 신중하게 고려하여 지정해주어야 합니다.

5. 마치며 📌

오늘은 자주 사용되는 HTTP 캐시의 속성들에 대해서 정리해보는 시간을 가졌습니다. 웹 서비스를 운영할때 캐시를 잘 조정하면 리소스를 최적화하여 관리할 수 있기에 정말 좋은 기술이라고 생각합니다.


여러분들도 HTTP 캐시를 사용하여 리소스를 최적화 할때 많은 도움이 되었으면 좋겠습니다. 이상으로 글을 마치겠습니다. 글 읽어주셔서 감사합니다! 😀

Reusable Workflow로 GitHub Actions 워크플로우 재사용하기
이전글

Reusable Workflow로 GitHub Actions 워크플로우 재사용하기

GitHub Actions의 재사용 가능한 워크플로우를 사용하여 로직을 재사용 해보겠습니다.

npm install과 npm ci 명령어 알아보기
다음글

npm install과 npm ci 명령어 알아보기

npm 패키지를 설치할때 대표적으로 사용되는 두 명령어를 알아보겠습니다.