pile·
프론트엔드·카카오 스타일 (지그재그)카카오 스타일 (지그재그)·

웹폰트 최적화를 통한 CDN 비용절감

카카오스타일 지그재그 프론트엔드 팀이 CloudFront 사용량을 뜯어보다 폰트 호출량이 비정상적으로 높은 것을 발견하고, Safari의 캐시 미스와 폰트 용량 두 가지를 해결해 월 비용을 약 1/10로 줄인 기록이다. 원인은 preload 힌트로 인한 cors 요청과 font-face url을 통한 no-cors 요청이 Vary Origin 때문에 서로 다른 캐시 키를 갖게 된 것이었다. 여기에 서브셋 폰트로 용량까지 줄였다.

핵심 포인트
  • 개선 전 폰트 호출은 월 6억 4천만 건, 데이터 전송량 457TB로 요금 계산기 기준 월 43,126달러 규모였다.
  • Safari는 font-face url 요청을 no-cors로 보내고 link preload 요청은 cors로 보내는데 Chrome은 둘 다 cors다.
  • 응답의 Vary 헤더에 Origin이 있어 같은 URL이라도 Origin 헤더 유무에 따라 캐시 키가 갈렸다.
  • CloudFront 캐시 키에서 Origin을 빼거나 응답 헤더 정책에서 Vary를 지우면 해결된다.
  • 한글 폰트는 조합 가능한 11,172자를 모두 담아 무거우므로 KS X 1001의 2,350자를 기준으로 서브셋을 만들어 약 80% 줄였다.
  • 개선 후 월 비용은 4,446달러로 시점별 트래픽 차이를 감안해도 약 1/10 수준이 됐다.
상세 정리
  • 발견 경위: CloudFront 사용량 분석 중 다른 리소스 대비 폰트 호출이 비정상적으로 많았다. 브라우저 메모리 캐시가 걸리는 게 정상이라 이상하다고 판단했고, WOFF2뿐 아니라 WOFF까지 호출량이 많은 점도 걸렸다.
  • 비용 구조: CloudFront 요금은 데이터 전송량과 요청 수로 산정되므로 호출량을 줄이면 바로 절감으로 이어진다.
  • 재현: Chromium 계열은 첫 진입 때만 폰트를 받고 새로고침에는 메모리 캐시가 걸렸는데, Safari는 새로고침마다 폰트를 다시 요청하고 캐싱된 폰트까지 함께 가져왔다.
  • 단서: UX 개선 작업으로 넣었던 link rel preload를 떠올렸다. FOUT를 막으려 폰트 요청 우선순위를 높인 조치였고 MDN 안내에 따라 crossorigin 옵션을 붙인 상태였다.
  • 검증: preload 힌트를 제거하니 Safari에서도 메모리 캐시가 정상 동작했다. 다만 사용성 때문에 preload를 되돌릴 수는 없었다.
  • 원인 규명: preload 없이 font-face url로만 요청할 때 Safari의 요청은 no-cors라 Origin 헤더가 없었고 Chrome은 cors라 Origin이 있었다.
  • 캐시 키 충돌: 응답의 Vary에 Origin이 설정돼 있어 URL과 Origin 조합으로 캐시 키가 만들어졌다. Safari는 preload 요청에는 Origin이 있고 font-face 요청에는 없어 같은 폰트가 서로 다른 키로 두 번 저장됐다.
  • Vary의 출처 확인: CloudFront 폰트 동작의 캐시 정책에 Origin이 캐시 키로 들어 있었다. 시험 삼아 Access-Control-Request-Method와 Access-Control-Request-Headers로 바꿔보니 Vary 값도 그대로 따라 바뀌는 것을 확인했다.
  • 해결 선택지 둘: 캐시 정책의 캐시 키에서 Origin을 제거하는 방법과 응답 헤더 정책에서 Vary를 명시적으로 제거하는 방법이다.
  • 선택 근거: 캐시 키에 Origin밖에 없어서 Vary 제거 쪽을 택했고, 다른 캐시 키 값이 있는 환경이라면 Origin만 빼는 편이 안전하다고 덧붙인다.
  • 캐시 키 기본값: 캐시 키에 아무것도 남지 않으면 CloudFront는 배포 도메인과 요청 URL을 기본 캐시 키로 쓴다.
  • 서브셋 기준: KS X 1001은 한글 2,350자만 담아 모든 한글을 표현하지 못한다는 비판을 받지만, 자주 쓰는 글자만 모여 있어 용량을 줄일 기준으로는 적합하다.
  • 도구 함정: 널리 안내되는 서브셋 폰트메이커로 만든 폰트가 윈도우 7에서 깨진다는 제보가 들어왔고, 실제로 값이 누락돼 생성된 것을 확인해 다른 도구로 바꿨다.
  • 용량 비교: 일반 폰트가 WOFF2 0.76MB, WOFF 1.1MB였는데 서브셋은 각각 0.16MB, 0.21MB로 약 80% 줄었다.
  • 교훈: 비즈니스 로직은 QA와 테스트 코드로 걸러지지만 캐싱은 눈에 보이는 동작이 같아 문제가 있어도 모르고 지나가기 쉬우므로 배포 후 사후 검증과 모니터링이 더 중요하다고 본다.
왜 읽나CDN 요금에서 폰트 비중이 유독 크다면 Vary Origin과 브라우저별 cors 차이가 캐시를 쪼개고 있는지 먼저 확인하게 해주는 사례다.
카카오 스타일 (지그재그)
카카오 스타일 (지그재그) 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 프론트엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    항공 프론트엔드 구축기 (7/10): 창구를 하나만 두었습니다

    여기어때 항공 서비스 프론트엔드가 웹과 앱 웹뷰 두 환경에서 동일한 함수 호출로 동작하는 앱 브릿지 추상화 레이어를 설계한 과정을 다룬다. iOS·안드로이드 규약 차이와 "웹에 존재하지 않는 브릿지를 어떻게 호출하나"라는 문제를 단일 추상화 층으로 해결한 구현 사례다.

    요약 이어보기
    #react#typescript#webview+2