CloudFront + CloudFront functions 이용하여, Next.js 번들파일 효율적으로 서빙하기! 🧞

안녕하세요.
원티드랩 프론트엔드팀 김성광 입니다.

배경 🧪

원티드를 WebPageTest에서 체크를 해보았습니다.
아래와 같이 Next.js 서버에서 js, css 서빙 시간이 오래 걸리는 것을 발견하였습니다.

webpagetest 결과

느린 원인은 2가지로 판단됩니다.

  1. 지리적인 Latency 발생 🌏
    WebPageTest: Virginia USA 🇺🇸
    Next.js 서버: 한국 🇰🇷
  2. 서버 부하 🔥
    일반 Request 처리
    번들파일 (js, css) Request 처리

CDN Support with Asset Prefix 🌐

CDN Support with Asset Prefix 를 적용하게 되면, 번들파일 (js, css)을 CDN에서 서빙할 수 있게 되고, CDN을 이용하게 되면, 사용자와 가까운 곳에서 콘텐츠를 전송함으로써 더 빠르고 안정적인 온라인 경험을 제공합니다.

CDN은 사용자와 콘텐츠 간의 지리적 거리를 줄여 로드 시간을 줄입니다.

출처: https://imagekit.io/blog/what-is-content-delivery-network-cdn-guide/

CDN에 파일만 올리면 어느 정도 성능이 올라가지만,
더 영리하고, 효율적으로 이용하기 위해서 셋팅이 필요합니다.

이번 글에서
CloudFront + CloudFront functions을 이용하여, Next.js 번들 파일을 효율적으로 이용하는 방법에 대해 소개합니다.

목표 ✨

(번들 파일을 CDN 서빙시)
아래 Response Header 셋팅이 목표 입니다.

access-control-allow-origin: *                   // CORS
cache-control: public,max-age=31536000,immutable // 캐싱 정책
content-encoding: br // 최신압축 알고리즘 적용
x-cache: hit from cloudfront // cache hit

흐름(flow): Browser ↔ CloudFront ↔ S3

본격적인 작업에 들어가기전에
흐름에 대해서 알아보겠습니다.
아래 그림은 Browser ↔ CloudFront ↔ S3 기본 흐름입니다.

여기서 주목해야 할 포인트는 아래와 같습니다.

  1. CloudFront의 캐싱 만료 시간은 TTL 값을 사용한다.
  2. S3(origin) 응답 해더에 Cache-Control설정이 없으면 CloudFront “기본 TTL” 값으로 캐싱 만료가 세팅됩니다. (Clinet1 호출 시)
flow: wbrowser → CloudFront → S3
flow: wbrowser → CloudFront → S3

S3에 Response Header에Cache-Control 을 붙여보겠습니다.

S3 Response Header 추가 방법

  1. 파일, 디렉토리를 체크
  2. 작업 → 메타데이터 편집
  3. 메타데이터 추가

Client 1 호출 시, CloudFront → S3 순서로 호출되고,
S3에서 “메타데이타" 세팅한 값이 내려옵니다.
CloudFront는 S3 Response HeaderCache-Control 값으로, 캐시 만료 타임을 세팅됩니다.

flow: CloudFront → S3(Cache-Control)
flow: CloudFront → S3(Cache-Control)

참고:
더 자세한 캐시 만료 관리에 대한 글은 아래 링크를 확인해 주세요.

관심사 분리 ➗

S3에 “메타데이터” 및 CORS 설정을 추가하게 되면, CloudFront, S3 양쪽 모두 추가될 수 있어, 아래와 같이 역할을 나누겠습니다.

S3

파일 저장만 담당. (다른 역할은 없음)

CloudFront

  • CloudFront 캐시만료
  • Response Header 조작 (CORS, Cache-Control)

이제 본격적으로 원하는 Response Header를 만드는 방법에 대해 알아보겠습니다.

access-control-allow-origin: *                   // CORS
cache-control: public,max-age=31536000,immutable // 캐싱 정책
content-encoding: br // 최신압축 알고리즘 적용
x-cache: hit from cloudfront // cache hit

x-cache: hit from cloudfront

캐시 hit가 일어나야 성능이 좋아지게 됩니다.
CloudFront를 사용하는데, hit가 일어나지 않는다면 사용 의미가 크게 없어집니다.

CloudFront TTL 조정

빌드 될 때마다 번들 파일명이 변경되기 때문에, CloudFront에서 캐시를 오래가지고 있을수록 이득입니다.

CloudFront에 기본 제공하는Managed-CachingOptimized 정책을 살펴보겠습니다.

CloudFront Managed-CachingOptimized 설정

위에 말한 “관심사 분리”를 통해 S3에 max-age 설정하지 않습니다.
(S3 max-age 응답이 없을 시 기본 TTL 값 캐시 만료)

기본 TTL을 최대 TTL 값으로 세팅 하겠습니다. (기본TTL == 최대 TTL)

신규 CloudFront 캐싱 정책 생성

신규로 캐시 정책 생성 하고, CloudFront에 적용을 합니다.
신규로 만든 캐시정책은 아래와 같습니다.

CloudFront 캐시 정책 변경

content-encoding: br

Brotli 압축을 CloudFront 정책에서 쉽게 적용이 가능합니다.
Brotli가 지원 안되는 브라우저도 있기 때문에, Gzip도 활성화 합니다.

CloudFront Brotli

access-control-allow-origin: *
cache-control: public,max-age=31536000,immutable

이 헤더는 CloudFront 순수 세팅만으로 header를 내릴 수 있는 방법이 쉽지 않습니다.

CloudFront Functions

2021년 5월경에 도입 되었으며 4가지 사용 사례를 제공합니다.

캐시 키 정규화

URL 리디렉션 또는 다시 쓰기

요청 권한 부여

해더 조작 👈👈👈👈

“해더 조작” 원하는 기능을 제공하고 있습니다!
친절하게 예재코드까지 제공합니다.

CloudFront Functions

예재코드를 참고하여, 우리가 추가하고 싶은 코드를 추가합니다

function handler(event) {
var response = event.response;
var headers = response.headers;

// CORS header
if (!headers['access-control-allow-origin']) {
headers['access-control-allow-origin'] = {value: "*"};
console.log("Access-Control-Allow-Origin was missing, adding it now.");
}
// cache-control
headers['cache-control'] = {value: 'public,max-age=31536000,immutable;'};
return response;
}

CloudFront에서 아래와 같이 연결 합니다.

적용 결과

Respose Header에 셋팅된 것을 확인하였습니다.

실제 성능은 얼마나 좋아졌을까?

Lighthouse 점수 향상 🚀

테스트 환경 (5번씩 측정, 중간값을 표기)

  • URL: https://www.wanted.co.kr/newintro
  • 브라우저 : 크롬 (시크릿탭)
  • lighthouse 설정:
Lighthouse 설정

AS-IS: 47점

TO-BE: 57점 (+10) 👍👍

번들파일 용량 감소 💫

AS-IS

  • js: 631 kb
  • css: 130 kb

TO-BE (기존대비: 12.8% 용량감소 👍👍)

  • js: 547 kb (13% 용량감소 👍👍)
  • css: 116 kb (11% 용량감소 👍👍)

2021–10–13: 서버 모니터링 결과 내용추가

프론트 서버의 Request 처리량 약 77% 감소 하였습니다. (서버 가용성 Up)

원티드 프론트서버 request 처리량

마무리

CDN Support with Asset Prefix을 적용하다가, 입사 후 첫 장애도 냈습니다.
장애를 발판 삶아 CloudFront에 대해 많이 배우고, 성장하게 된 것 같습니다.
현재에 안주하지 않고, 더 높은 목표를 세우고, 실패하고, 배우고, 성장하는 개발자가 되도록 하겠습니다.

참고

원티드에서는 다양한 직군에서 적극적으로 채용중입니다! 서버, 웹, 앱, 디자인 등 제품을 만들어가는 각자의 분야에서 전문적인 분들과 함께 일하기를 기대하고 있습니다. 회사 채용 정보 페이지를 확인해 주세요!