💥사건의 발달
Firebase 무료 호스팅에 배포된 사이드 프로젝트가 있다. 한 통의 메일로 인해 최적화 작업에 들어갔다. 최근 비트코인 가격이 많이 올라 그런지 트래픽이 높아졌나 보다. 무료로 이용하는 만큼 최대한 아껴보도록 하자. Firebase는 아쉽게도
월 10GB
트래픽을 무료로 제공하고 있는데, Vercel에 100GB
에 비하면 한 없이 짠 편이다. 이후에 사용자가 많아진다면 vercel로 이동하는 것도 하나의 방법일 듯하다.🔍 원인찾기
1. 구글 애널리틱스 트래픽 추적
구글 애널리틱스에서 트래픽을 경로를 확인할 수 있는데, 대부분 트래픽은
Direct
에서 온다는 걸 확인할 수 있다. PWA
형태로 만들었기 때문에 모바일 기기에서 홈 화면에 추가해서 접속 했거나 즐겨찾기로 접속하는 사용자가 많을 것이다. 트래픽이 어느정도 증가했기 때문에 사용량 알림 메일이 온 것은 정상적인 흐름으로 보인다.2. Bundle Visualizer 확인
해당 프로젝트는
Vite
번들러를 사용하고 있고 rollup-plugin-visualizer
플러그인을 통해 확인했다. 트래픽을 줄이려면 네트워크 응답 크기를 줄여야 한다. 네트워크 전송은 gzip
압축 형태로 전달되니 Visualizer 에서 gzip
을 체크해서 확인한다. 대략 봤을 때 assets
, moment
정도 있다.3. public 정적 파일 확인
Visualizer에서는 확인할 수 없는 자원인데, 브라우저에서 폰트나 이미지 등 필요에 의해 불러와 사용되기 때문이다. 이 또한 트래픽을 증가하는데 영향을 주므로 확인해 본다.
문제가 될만한 파일을 발견했다. 이미지도 얼마 없지만
1MB
나 달하는 녀석이다. 만들적 원본 PNG 확장자로 5MB
인 거대한 녀석을 webp
확장자로 다이어트시킨 기억이 있는데, 아직도 문제가 될 만하다.4. 코드 분할 적용 확인
사용하는 필요한 자원만 불러오도록 코드 분할을 적용하면 불필요한 자원은 요청하지 않으므로 트래픽 감소 효과가 기대된다. 프로젝트 초반에 개발할 당시 정말 작은 규모의 프로젝트라 코드 분할 까지는 신경 쓰지 못했는데, 이 번 기회에 적용해 본다.
🛠️ 문제해결
1. CJS 모듈 라이브러리 마이그레이션
CJS 모듈 라이브러리를 사용한다면 최신 ES모듈로 작성된 라이브러리로 마이그레이션 한다.
moment
라이브러리는 gzip
압축을 해도 73.1KB
번들 크기를 자랑한다. 반면 dayjs
는 압축을 안해도 6.9KB
로 매우 작다. 10배 이상 차이가 나므로 dayjs
를 사용한다. 이번 기회에
kku-util
이라는 공통 유틸리티 라이브러리를 만들었다. dayjs를 사용하고 있어서 이걸로 마이그레이션을 진행했다.2. Lottie 애니메이션 Json 파일 최적화
JSON의 경우
gzip
압축 사이즈 기준으로는 크게 바뀌지 않지만 Rendered
크기는 15%이상 최적화된다.- block.json
8.76KB
⇒6.84KB
- premium.json
4.74KB
⇒3.74KB
- 404-bicoin.json ⇒
46.47KB
⇒0KB
(삭제)
3. Static Assets 최적화
폰트는 이미 서브셋 파일로 만들어 사용 중이다. 문제는 이미지인데,
1MB
에 달하는 크기를 파이어베이스 호스팅으로 사용하게 두는 건 낭비인 듯하다. 떠오르는 좋은 대안으로는 깃허브에 올라간 이미지의 raw 링크를 사용한다. 브라우저는 Firebase 서버가 아닌 깃허브 서버로 이미지를 요청할 테니 트래픽이 줄어들 것이다.4. 코드 분할(Code Splitting) 적용
React18 부터
Suspense
와 lazy
를 통한 코드 분할을 지원한다. 공식 문서에서는 React-Router를 사용한 Routing 기반 코드 분할을 추천하고 있다.📝 withSuspense.tsx
import React, { Suspense, ComponentType, ReactNode } from "react"; export default function withSuspense<P extends JSX.IntrinsicAttributes>(WrappedComponent: ComponentType<P>, fallback: ReactNode = <div>Loading...</div>) { const ComponentWithSuspense = (props: P) => ( <Suspense fallback={fallback}> <WrappedComponent {...props} /> </Suspense> ); return React.memo(ComponentWithSuspense); }
// routes.ts const HomePage = withSuspense(lazy(() => import("@/pages/home")));
Suspense
를 감싸주는 HOC
컴포넌트를 만들어 페이지별로 적용했다.📈 결과 확인
📦 네트워크 크기
👎 Before
해결책이 적용되기 전 파일 사이는
397KB
로 여기에 1MB
배경 이미지까지 불러온다면 사용되는 트래픽이 적은 편은 아니다. 그렇다면 적용 후 확인해보자.👍 After
하나의 JS 파일이 코드 분할로 인해 4개로 나뉘었고 총
304.77kB
크기로 기존보다 23% 감소했다. 비공식 1MB
도 절약된 샘이다.🖥️ 트래픽 근황
6일 부터 200~300 초반의 유입을 확인할 수 있으며, 큰 변동사항은 없다. 최적화는 모든 작업을 완료 후 배포한게 아닌, 순차적으로 작업 후 배포를 진행했다. 5일 부터 배포 작업이 진행됐고 7일에 모든 최적화 작업을 완료했다.
Firebase 호스팅 사용량에 따르면 5일 부터 점차 낮아지고 있는 추세다. 가장 큰 비교군으로 1일과 10일을 볼 수 있다.
ㅤ | 11/01 | 11/10 |
트래픽 | 167 | 300 |
사용량(MB) | 395.86 | 187.97 |
1일과 10일 트래픽은
167
에서 300
으로 2배 가량 증가했는데, 사용량 395MB
에서 187MB
로 2배 이상 감소됐다. 이 정도면 드라마틱한 결과로 볼 수 있겠다.🏃🏻♂️ 앞으로
호스팅 사용량에 문제가 생긴다면.. Vercel로 이사해야겠다.