url에 주소를 입력하면, 해당 도메인에 맞는 파일을 불러옵니다. 그리고 그 파일의 용량 크기에 따라 사용자에게 보여지는 화면 속도가 결정됩니다. 인터넷 환경도 중요하지만 절대적으로 파일의 용량 크기가 작다면 같은 환경이라도 더욱 빠르게 화면을 볼 수 있습니다.
url을 입력했을 때 서버로부터 요청을 하고, 그 요청에 응답을 함으로써 저장된 파일을 받게 되는데 이렇게 ‘어딘가에 저장되어 있는 파일’을 불러오는 방법에는 여러가지 방법이 있습니다. 이번에는 성능 최적화 하기에 앞서 아마존에서 제공하는 S3와 CloudFront를 한번 얘기해 볼까 합니다.
S3는 AWS에서 제공하는 저장 공간입니다. 하나의 단위를 “버킷”이라고 부르고, 이 버킷에 정적 파일이나 리소스들을 저장할 수 있습니다. S3를 사용했을 때 얻을 수 있는 장점은 다음과 같습니다.
즉, 확장/축소가 용이한 저장공간이라고 생각할 수 있습니다. 그저 필요한 파일을 올리고, 특정 요청을 통해서 이 저장공간에 있는 파일들을 불러오기만 하면 됩니다.
CloudFront는 아마존에서 제공하는 CDN 서비스입니다.
CDN(Contents Delivery Network)은 콘텐츠 전송 네트워크를 나타내는 약자로, 웹 콘텐츠와 웹 애플리케이션을 효율적으로 제공하기 위한 분산 네트워크입니다. CDN은 전 세계의 다양한 위치에 위치한 서버와 캐시 노드를 사용하여 웹 콘텐츠를 저장하고 전송함으로써 웹 성능을 최적화하고 가용성을 향상시킵니다.
CloudFront는 엣지 서버라는게 있는데, 바로 이 서버에 해당 캐시된 데이터를 저장하고 있다가 동일한 요청이 오게 된다면 바로 제공하게 됩니다. 즉, 위에서 말한 S3의 서버 위치가 미국 버지니아라면 한국에서 파일을 요청할 때마다 버지니아에 있는 서버로 파일들을 요청하게 됩니다. 이 물리적인 거리는 상당히 깁니다. 해저 케이블을 타고 가서 얻는것 보다 한국 혹은 서버에 요청하는 지역 근처에 정적 파일을 저장하고 있는 엣지서버로 부터 동일한 데이터를 받아온다면 속도는 훨씬 빠를 것 입니다.
이때 콘텐츠가 엣지 서버에 캐싱되어 있지 않으면 CloudFront가 S3 버킷에서 해당 파일들을 가져옵니다. 이 외에도 AWS 프라이빗 네트워크를 통해 전송되는 동시에 CloudFront가 TCP 핸드셰이크를 최적화하기 때문에 요청 및 콘텐츠 반환 속도가 퍼블릭 인터넷을 통해 액세스하는 것보다 훨씬 빠릅니다.
또한, CloudFront의 장점으로는 보안입니다. CloudFront의 보안 기능인 Origin Access Identity(OAI)가 있습니다. 이 기능은 S3 버킷 및 콘텐츠에 대한 액세스를 CloudFront와 CloudFront가 실행하는 작업으로 제한합니다. 이번 블로그 포스트에 포함된 CloudFormation 템플릿에는 콘텐츠를 보호하고 제한할 수 있는 OAI가 있습니다.
즉, 유저가 S3 버킷에 직접적으로 접근할 수 없고 무조건 CloudFront를 통해서만 접근이 가능하도록 설정할 수 있습니다.
또한, CloudFront에서 S3를 통해 받아 온 정적 파일들을 Gzip 혹은 Brotli의 형태로 파일 들을 압축해 제공합니다. (Gzip과 Brotil 모두 제공한다면, Brotil을 우선적으로 제공합니다.)
S3를 통해서 정적 파일들을 확장이 자유롭고 서버 관리를 하지 않아도 되는 저장소에 저장을 하고, 자동으로 캐시와 보안 기능을 제공해주는 CloudFront를 통해 콘텐츠 관리를 해 줄수 있습니다. 일반 적으로 EC2와 같은 서버에 정적 파일을 올려놓기 보다 S3 버킷에 넣어서 관리를 하는 것이 비용적으로 더 좋습니다.
같은 요청을 최소화 하기 위해서는 캐시기능을 활용할 수 있습니다. 이전에 요청했던 데이터를 다시 사용하지 않는 방식입니다. 캐시에는 여러 종류가 존재하는데, CDN에서 S3 버킷으로부터 받은 리소스들을 가지고 있는것도 캐시이고, HTTP 캐시를 통해 브라우저 자체에 가지고 있는 방법 역시 캐시 중 하나입니다.
S3로 직접 리소스를 제공받지 않고, 물리적으로 가까운 CloudFront를 통해 비교적 짧은 거리의 통신으로 시간을 단축시킬 수 있는 것처럼 HTTP 캐시를 통해 주고 받은 리소스들을 클라이언트(사용자)의 브라우저에 저장할 수도 있습니다. 역시 돈을 버는 가장 확실한 방법은 돈을 쓰지 않는거라고, 사용자 경험을 가장 높일 수 있는 최고의 방법은 사용자 경험을 저하시키는 것(서버와의 요청)을 최대한 줄이면 됩니다.그리고 그 중에서 가장 효과적인 방법이 HTTP 캐시입니다.
HTTP 캐시는 Cache-Control
에 값을 설정해 줌으로써 해당 리소스 파일의 캐시 주기를 설정해 줄 수 있습니다. HTTP 캐시에서는 max-age
라는 설정 값을 통해 해당 리소스의 유효기간을 정할 수 있습니다. (Expires 역시 동일하지만, max-age가 우선시 됩니다.)
만약에 설정한 캐시 만료 전에 같은 요청이 들어온다면, 메모리에 저장되어 있는 기존 값을 보여줌으로써 서버 요청을 최소화 할 수 있습니다. S3 버킷을 사용하게 된다면 리소스 별로 Cache-Control
설정이 가능한데,
위와 같이 설정을 할 수 있습니다.
그렇다면 Cache-Control
에 들어가는 값의 의미는 어떤 것인지 한번 알아보겠습니다.
max-age
: 캐시의 유효기간을 의미합니다.no-cache
: max-age = 0과 같은 의미입니다. 매번 서버에 재검증을 요청합니다.no-store
: 브라우저와 다른 중간 캐시(예: CDN)가 파일의 어떤 버전도 저장하지 않도록 지시합니다.public
: 응답은 모든 캐시에 의해 저장될 수 있습니다.private
:브라우저는 파일을 캐시할 수 있지만 중간 캐시(CDN)는 캐시할 수 없습니다.s-maxage
: 중간 서버에만 적용되는 캐시 값을 설정할 수 있습니다.캐시의 유효기간인 max-age
가 만료된다면, 브라우저는 조건부 요청을 통해 캐시가 유효한지 재검증 합니다. 재검증 결과 브라우저 캐시가 유효하다면 304 Not Modified를 반환해 줍니다. 재검증 과정에서는 ETag
와 Last-Modified
라는 것을 기준으로 재검증을 실행합니다.
ETag: 브라우저가 만료된 캐시 응답을 찾으면 작은 토큰(일반적으로 파일 내용의 해시)을 서버로 보내 파일이 변경되었는지 확인할 수 있습니다. 서버가 동일한 토큰을 반환하면 파일이 동일하므로 다시 다운로드할 필요가 없습니다.
Last-Modified: 이 헤더는
ETag
와 동일한 목적을 위해 사용되지만 시간 기반 전략을 사용하여ETag
의 콘텐츠 기반 전략과 반대로 리소스가 변경되었는지 여부를 판단합니다.
HTTP 캐시에 대한 기본적인 이해와 개념을 알 수 있었습니다. 추가적으로 어떤 상황에 어떤 옵션을 설정해야 하는지 잘 모르는 경우가 존재합니다. 그럴 경우에는 아래 도식도를 통해서 본인 서비스에 맞는 캐시 주기 설정이 가능합니다.
https://web.dev/http-cache/
https://aws.amazon.com/ko/blogs/korea/amazon-s3-amazon-cloudfront-a-match-made-in-the-cloud/
https://toss.tech/article/smart-web-service-cache