React App 빌드 최적화로 First Load JS 줄이기

·

3 min read

Nextjs로 개발한 프로젝트를 빌드하면 페이지별 용량 정보를 확인할 수 있다.

한 줄에 각 페이지에 대한 정보가 표시된다. 오른쪽에 두개의 용량이 표시되는데, 차례대로 페이지 크기, 최초에 로딩해야하는 자바스크립트의 크기이다.

아래에는 모든 페이지에서 공통으로 필요한 자바스크립트의 크기를 보여주고, 이 자바스크립트가 어떤 파일들로 구성되어있는지도 확인할 수 있다. 이 정보만으론 자세한 분석이 힘들어서, 각 파일이 어떤 모듈로 구성되어있는지, 그리고 각 모듈의 대략적인 크기가 얼마인지를 시각적으로 확인하기 위해서 bundle-analyzer를 추가로 사용할 수 있다. (Docs)

설명대로 환경변수를 세팅한 후 next app을 빌드하면 세 개의 탭이 열리는데, 그중 클라이언트 탭을 확인한다.

왼쪽 상단의 아이콘을 누르면 필터링을 위한 UI가 나오는데, 여기서 빌드 결과 생성된 위의 파일들을 하나씩 확인해보면 main과 framework파일은 각각 Next.js와 React임을 알 수 있다.

그러므로 이 두 파일에서 용량을 줄일 수는 없고, 아래에 있는 app에서 용량을 줄여야 한다. app만 필터링해서 확인해보면, 전체 페이지에서 공통적으로 사용되는 모듈들이 크기별로 구분되어 표시된다.

Dynamic import

Next.js에서는 React.lazySuspense를 합친 next/dynamic모듈을 제공한다. 이 모듈을 사용하면 클라이언트에서 컴포넌트가 마운트되는 시점까지 자바스크립트 파일 로딩을 미룰 수 있다.

데브툴의 네트워크 탭에서 쓰로틀을 걸어보거나, 자바스크립트 파일 로드 목록을 살펴보면 dynamic import적용 여부를 확인할 수 있다. 이렇게되면 번들 용량도 줄어들게 된다. (이전보다 7KB 감소)

트리셰이킹

라이브러리마다 트리셰이킹을 제공하는지 조사해본다.

빌드 분석 결과에선 Sentry모듈의 용량이 꽤 큰데, Sentry Docs에는 트리셰이킹을 적용하기 위한 방법이 설명되어 있다. config파일에서 debug, tracing 플래그를 false로 세팅해주니 약 10KB의 용량이 줄었다.

config 파일을 커스텀하여 용량을 줄일 수도 있다. 예를들어 sentry를 앱에 설치하면 기본적으로 40KB짜리 Replay integration이 추가되는데, 필요하지 않다면 지우면 된다.

이 외에도 프로덕션에서 config가능한 다른 라이브러리가 있다면 Docs에 번들링 용량을 줄일 방법이 있을 수도 있다.

불필요한 모듈이 import되는지 확인하기

불필요한 모듈까지 import하는 경우는, 여러 모듈을 re-export하는 목적으로 만들어진 파일(Barrel File)을 import하는 경우에 발생할 수 있는 문제이다.

위 예시처럼 mod/index.ts에서 a, b, c를 re-export할 때, mod로부터 import를 시도하면, mod/a.ts에 있는 heavy라이브러리까지 같이 import를 하게 된다.

불필요한 다른 모듈이 import되지 않도록, 하위디렉토리까지 명시하여, mod/b.tsmod/c.ts에서 bc를 각각 import하면 heavy를 불러오지 않을 수 있다. (cherry-picking)

나의 경우, 앱에서 랜덤한 닉네임을 생성하기 위해 라이브러리를 사용했는데, 닉네임을 생성하는 페이지 이외에는 해당 라이브러리가 쓰이지 않았지만 위에서 언급한 이유로 import되고 있음을 발견했다. 라이브러리가 필요한 페이지에서만 import되도록 코드를 변경했더니, 약 20KB의 용량이 줄어들었다.

비슷한 원인으로 불필요하게 import되고 있던 다른 컴포넌트들을 위와 같은 방식으로 개선했더니 용량을 더 줄일 수 있었다.

관련 이슈에서도 비슷한 문제가 언급되고 있으며, Barrel import를 금지하는 린트 룰도 있다.

라이브러리 교체하기

동일한 기능을 제공하지만 용량이 다른 라이브러리가 있을 수 있다. 용량이 많이 나가는만큼 더 많은 기능을 제공할 수도 있지만, 다양한 기능들을 사용하지 않는다면 불필요하게 용량만 차지하는 셈이다.

내 경우 html문자열에서 태그를 제거하는 라이브러리가 필요했는데, 처음에 다운로드 받은 string-strip-html이란 라이브러리는 용량이 37KB로, 필요한 기능에 비해 용량이 너무 컸다.

라이브러리에서 제공하는 함수의 반환값을 확인해보니, 실제로 필요한 result프로퍼티 이외에 불필요한 데이터들이 많이 보였다. 이런 부가적인 기능을 제공하는것도 용량이 높아진 원인이 됐을 것이다.

필요한 기능을 제공하는 다른 라이브러리를 찾아봤더니, 단순하게 태그만 제거해주는 striptags라이브러리가 있었다. 기존 라이브러리는 용량이 37KB인데, 바꾼 라이브러리의 용량은 바이트 단위였다.

결과적으로 약 40KB정도를 줄일 수 있었다.


Dynamic import, 트리셰이킹, import최적화, 라이브러리 교체 등의 방법으로 번들 용량을 360KB에서 230KB로, 약 35%를 줄일 수 있었다.

bundle-analyzer와 같은 시각화 도구가 도움이 많이 되었다. 코드만 보고 최적화를 시도했다면 시간이 배로 걸렸을 것 같다.


++

이후에도 최적화 작업으로 30KB 더 감소시켰는데(200KB), 중간에 추가된 라이브러리로 15KB가 늘어나서 현재는 215KB로 마무리된 상태다.