회사 프로젝트의 개선 항목 중에서 입사를 하고 나서부터 오늘날까지 간곡히 해결하고 싶었던 문제가 있었다. 

 

바로 홈 화면에서의 First Load JS를 줄이는 것이었다. 

 

입사 당시, 회사 서비스의 속도는 매우 느렸다. 

성능이 좋은 맥북 프로 m2로도 조금 답답한 느낌이 들 정도였다. 

 

이렇게 생각해도 될지 모르겠지만, 회사 프로젝트가 "황금 고블린"같은 느낌이 들었다.

경험치를 올린 사냥터가 아주 많은 프로젝트라고 생각했기 때문이었다. 

 

느리다는 것도 렌더링이 느리거나, 로딩이 길거나, 반응속도가 느린 것과 같이 다양한 부분에서 측정이 가능했고, 

회사의 서비스도 다양한 측면에서 느리다고 말할 수 있었다. 

 

서비스에 가장 먼저 접근하게 되는 홈 화면의 First Load JS는 올해 1월 경만 해도 765kB 였다.

 

개선 하기 전, 최초의 빌드 결과(2025.01)

 

 

모든 페이지를 살펴보면서 dynamic import를 하기에 적합한 부분이 있는지 판단을 했다. 

(view port에 당장 보이지 않는 컴포넌트들, 모달 등등)

CLS 성능은 떨어뜨리지 않기 위해서 각 컴포넌트에 맞는 스켈레톤 UI를 next dynamic의 옵션으로 할당하기도 했다.

 

그렇게 적용할 부분들을 검토한 후, 모두 적용하였더니 페이지 별로 평균 약 146kB가 감소되었다. 

평균적으로 약 16.7% 감소되는 효과가 있었다. 

 

 

올해 상반기까지의 최적화 결과(2025.04)

 

 

위의 이미지를 보면 알 수 있겠지만, 메인 페이지는 거의 줄지 않은 것과 같은 효과였다..😅

(765kB -> 758kB)

 

다음 단계로 bundle-analyzer를 통해서 서비스의 번들을 분석해보려고 했다. 

 

처음 해보는 것이다보니 막상 결과물을 보아도 제대로 이해하기가 어려웠다.

아직까지 서비스의 전체적인 구조를 몰라서 그럴 수도 있다고 판단하여 중단했다.

조금 더 프로젝트의 깊은 곳까지 들어간 후에 다시 해결해보기로 했다.

 

 

그렇게 7개월이 흘렀다... ✈️

 

 

그 사이 '결제 관련 새로운 서비스', '로그인/인증 플로우 개선' 등 굵직한 퀘스트들을 클리어했다.
이 과정에서 서버 응답 시간(TTFB)을 개선하며 프로젝트의 더 깊은 곳까지 파고드는 경험을 했다.

 

결제 관련 새로운 기능도 추가하고, 2가지 큰 항목도 개선을 하여 서비스가 이전에 비해 굉장히 빨라졌다. 

메인 페이지만 해도 이전보다 p95 기준으로 봤을 때, 로딩 속도가 약 30% 개선되었다. 

하위 5%의 가장 느린 사용자 기준으로 측정된 메인 페이지

 

하지만, Sentry Performance에서 수집된 데이터를 봤을 때, 여전히 일부 환경에서는 여전히 홈화면이 느린 것을 확인했다. 

 

이때 다시, First Load JS가 머릿속을 스쳤다.

 

bundle-analyzer로 분석하기 전에 다시 확인해보았다.

758kB -> 841kB가 되었다.. 

 

메인 페이지의 살이 더 쪄버린 것이다.

메인 페이지뿐만 아니라, First Load JS shared by all의 항목들이 커지면서 다른 페이지들에도 영향을 미친 듯하다.

 

작업 시작 전

 

 

bundle-analyzer를 확인해보았다.

많은 것들이 있어서 쉽게 비교할 수 있게 시즌 1의 목표 지점들을 빨간 선으로 표기했다.

 

개선 전 _app-[hash].js bundle

 

다른 작업들을 하고 시간이 지나고 와보니 이제는 보이는 것들이 생겼다. 

firebasefirestore, database, storage는 초기에 로딩하지 않아도 되는 모듈들이었다. 

 

정확히 말하자면, 초기 로딩에 전혀 관여하지 않는 함수, 기능, 컴포넌트가 현재 초기 로딩에 관여하고 있었다는 사실이다. 

 

지금 생각해보면 어려운 사실은 아니었다.
단지, 비즈니스 오너의 시각으로 서비스를 대하고, 다른 사람들이 작성한 코드도 내 것이 될 때까지 계속해서 읽고,
이해하는 과정이 없었다면 쉽게 알지 못 했을 것 같다. 

 

 

그래서, lib/firebase/loader.ts 라는 파일을 생성하고, 

필요할 때만, 각각의 모듈들을 로드할 수 있도록 lazy loading을 적용했다. 

 

// lib/firebase/loader.ts
import type * as FirebaseDatabase from 'firebase/database';

// database 모듈 메모리 캐시
let databaseModule: typeof FirebaseDatabase | null = null;

/**
 * Firebase Realtime Database 모듈 로드
 */
export const loadDatabase = async () => {
  if (!databaseModule) {
    databaseModule = await import('firebase/database');
  }
  return databaseModule;
};

 

인증과 관련된 auth를 제외하고 나머지는 초기 로딩에 필요하지 않기 때문에 모두 위와 같이 처리해줬다.

 

결과적으로 _app-[hash].js가 164kB 감소하면서, First Load JS shared by all도 164kB 감소했다.

메인 페이지뿐만 아니라 페이지들이 전반적으로 164kB가 감소하면서
모든 페이지의 로딩 속도와 UX를 향상시키는 결과를 얻을 수 있었다. 

 
항목 최초 상태 (원본) 최종 상태 (제출본) 개선 효과
First Load JS shared by all 498 kB 334 kB -164 kB (약 32.9% 감소)
chunks/pages/_app-[hash].js 407 kB 243 kB -164 kB (약 40.3% 감소)

 

개선 후 빌드 결과

 

개선 후 _app-[hash].js bundle

 

 

이렇게 First Load JS 개선 작업의 시즌 1은 마무리가 되었다.

 

시즌 1이라고 지칭한 이유는 bundle을 보았을 때, 아직 세부적으로 개선해볼게 많다고 판단했고,

이들은 시즌 1만큼의 효과를 불러올지는 모르지만, 도전할 구석이 많아보인다. 

 

시즌 1을 성공적으로 마무리했기 때문에 자신감을 갖고, 시즌 2를 틈틈히 진행해볼 계획이다.