CDN과 캐시 설정으로 정적파일을 빠르게

구경회·2021년 9월 30일
19
post-thumbnail

정적파일

웹 사이트는 일반적으로 이미지, 자바스크립트, css 등의 여러가지 추가 파일을 제공해야 한다. 이것들을 흔히 정적 파일이라고 부른다. 서버에서 직접 이 모든 정적 파일을 제공하는 방법을 택할 수도 있지만, 이런 경우는 단점이 많다.

  1. 서버의 저장 용량은 상대적으로 비싸다.
  2. 서버의 저장 용량은 증설하기 어렵다.
  3. 그러나 많은 컴퓨팅 파워를 요구하지는 않는다.

따라서 위와 같은 정적 파일들은 일반적으로 클라우드 업체에서 제공하는 정적 파일 저장소를 사용한다. S3 등의 정적 파일 저장소는 위 단점들에 대한 완벽한 대안이 된다. 이 글에서는 S3CloudFront를 활용하여 정적 파일을 좀 더 빠르고 안정적으로, 싼 값에 제공해보도록 하겠다.

CDN은 무엇인가

개념

CloudFront는 대표적인 CDN중 하나로 Amazon에서 제공하고 있는 서비스이다. CDN은 Contents Delivery Network의 약어로, 지리적으로 분산된 여러 개의 서버이다. 수많은 이미지 콘텐츠의 빠른 전송, 대용량 콘텐츠의 초고속 다운로드, 미디어 콘텐츠의 끊김 없는 스트리밍 서비스 등 많은 분야에서 활용된다.

일반적으로 지역별로 여러 개의 서버를 두어 사용자의 요청을 가장 가까운 로컬 서버에서 처리하도록 한다. 이 과정에서 정적 파일 저장소에서 가까운 CDN 로컬 서버로 데이터를 전송해야 하는데, 해당 지역에 데이터가 들여있나 없나에 따라 사용자의 응답 속도에 차이가 난다.

CloudFront의 경우 215개 이상의 엣지 로케이션을 가지고 있다. 또한 기본적인 로깅과 통계를 제공해 유용하게 사용할 수 있다.

CloudFront 생성


cloudfront 콘솔에서 생성을 누른뒤, 연결할 S3 도메인을 선택한다.


그 아래 캐싱 정책과 원본 요청 정책을 선택할 수 있다. S3의 CORS 설정을 존중하기 위해
관리형 정책 중 CORS 관련된 설정을 선택할 수 있다.

그 후 CNAME 등을 추가로 설정할 수 있다. 이 과정 없이 CloudFront의 도메인을 받을 경우, abcd33445.cloudfront.net과 같이 읽기 힘든 도메인이 나오게 된다. 저 url을 직접 사용자에게 노출하는 것보다 cdn.heka1024.com과 같이 의미를 가진 도메인을 이용하는 것이 차후 다른 서비스로 변경하거나 직접 서빙을 한다는 등의 변경사항이 생겼을 때 훨씬 유연하게 대처할 수 있다. 따라서 저 부분을 꼭 의미있는 이름으로 설정해주기 바란다. 이 때 HTTPS로 서비스를 제공하기 위해서는 Cname으로 등록할 url에 대한 SSL 인증서를 등록해야 한다.

캐싱 확인

그 과정을 거치면 위와 같이 배포가 된 것을 확인할 수 있다. 그러면, 이미지가 CDN을 거쳤는지, 거친다면 얼마나 빨라지는지 확인할 수 있을까?


클라우드 프론트를 거치지 않은 경우, x-cacheMiss라는 내용이 있는 걸 확인할 수 있다. 2.5MB의 이미지인데, 받아오는데 1.8초가 걸렸다.

동일한 사진을 다시 한번 요청하면 어떻게 될까?

이번엔 x-cacheHit라는 내용이 있다. 시간은 948ms가 걸렸다. 반 정도로 줄어든 것이다. 하지만 만족하기엔 이르다. 이 시간을 훨씬 줄여보자. 물론 CDN에서 할 수 있는 일은 아니고, S3 단에서 HTTP Header를 건드려서 할 것이다.

압축까지

JPG등 이미 압축이 이루어진 포맷들은 압축을 하지 않지만, CloudFront는 그렇지 않은 파일 포맷들 가령, 자바스크립트 등에 대해 압축을 해 준다.

응답의 Content-Encoding에서 Brotli 압축이 사용된 것을 확인할 수 있다. 해당 내용은 이전 글 Accept-Encoding의 이해를 참고하자.


압축 설정은 캐시 정책에 함께 존재한다. 일반적으로 Brotli 압축이 gzip 압축에 비해 정적 파일에 대해 좀 더 나은 압축률을 보여주는 것으로 알려져 있다. 따라서 Brotli Compress를 켜주도록 하자.

HTTP 헤더 설정

개념 이해

Cache Control

HTTP 통신에서 서버는 클라이언트에게 응답을 내려주며 해당 응답은 캐시해두고 있어도 된다고 알려줄 수 있다. 일반적으로 Cache-Control 헤더를 이용해 지시하며 다음과 같은 표준 문법을 사용할 수 있다.

Cache-control: must-revalidate
Cache-control: no-cache
Cache-control: no-store
Cache-control: no-transform
Cache-control: public
Cache-control: private
Cache-control: proxy-revalidate
Cache-Control: max-age=<seconds>
Cache-control: s-maxage=<seconds>

다음과 같은 확장 문법을 사용할 수도 있으나 브라우저에 따라 지원하지 않는 경우도 있다.

Cache-control: immutable
Cache-control: stale-while-revalidate=<seconds>
Cache-control: stale-if-error=<seconds>

자주 사용하게 되는 식은 Cache-Control: max-age=86400과 같이 캐시의 지속 시간(초 단위)를 적어주는 방식이다. 그렇다면 max-age가 만료되지 않은 시점에 브라우저는 어떻게 동작할까?


바로 위와 같이 memory cache에 있다고 요청을 날리지도 않게 된다. 이 경우 주어진 max-age동안 캐시를 이용할 뿐 서버로 직접 요청을 날리지는 않게 된다.

하지만 이 방식의 큰 단점은 이미 전파된 캐시를 주워담기 매우 힘들다는 것이다. 그렇다면 최대한 캐싱하고 싶지만 업데이트 된 것을 빠르게 전파하고 싶은 경우라면 어떻게 할까?

If None Match

클라이언트가 다음과 같은 응답을 받았다고 해보자.

HTTP/1.1 200 OK
ETag: "heka1024"
Cache-control: private
Content-Length: 16384

이후에 서버에 요청을 보낼 경우, 브라우저는 다음과 같이 보내게 된다.

GET heka1024/files/twice HTTP/1.1
Host: x.y
If-None-Match: "heka1024"

ETag값이 heka1024로 일치하므로 이 요청을 받은 서버에서는 304 Not Modified를 보내 로컬에서 들고 있는 파일을 이용해도 됨을 알려준다. 이 과정은 서버를 한 번 거치기는 하지만 컨텐츠를 통째로 다시 내려주는 것보다는 훨신 효율적인 과정임을 알 수 있다.

만약 ETag값이 다르다면, 서버는 새로운 ETag 값과 함께 컨텐츠를 내려주게 된다. 대략 다음과 같은 응답을 내려줄 것이다.

HTTP/1.1 200 OK
ETag: "4201akeh"
Cache-control: private
Content-Length: 8192

S3에서 적용하기


웹 콘솔에서는 객체 혹은 폴더를 선택한 후 작업 > 메타데이터 편집을 선택하자.


그 후 Cache-Control등의 헤더를 선택할 수 있다.

그 후 max-age=<seconds>를 통해 캐시의 지속 시간을 써주자. 이 과정을 통해 정적 파일을 CDN과 로컬 모두에 캐싱함으로서 서버의 부하를 줄이고 유저에게 좀 더 빠르게 데이터를 내려줄 수 있다.

참고문헌

profile
즐기는 거야

0개의 댓글