프로그래밍 공부하기

JavaScript Bundle Diet 본문

감상문

JavaScript Bundle Diet

ihl 2021. 5. 23. 00:56
 

JavaScript Bundle Diet

웹사이트 기능이 많아짐에 따라 점점 커지는 JavaScript Bundle Size. 웹사이트 속도는 느려지고, 잠재 고객을 잃을 수도 있습니다. 라이브러리를 사용하는 그리고 제공하는 관점에서 Bundle Size가 커질

toss.im


  웹 페이지 로딩속도를 줄이기 위한 방법 중 하나는 JS 번들의 크기를 줄이는 것이다. 위 영상에서는 Toss에서 어떻게 번들을 최적화 하는지 엿볼 수 있었다.

 

1. 번들 분석툴

  번들 최적화에 앞서 번들의 상태를 알아볼 수 있는 번들 분석 도구를 선택해야 한다. 나는 Webpack Bundle Analyzer만 사용해 보았는데 위 영상에서도 bundle analyzer를 추천한다. 각 도구의 특징은 다음과 같다.

  • Webpack Analysis: 가장 다양한 정보, 사용법이 복잡하고 느리다.
  • Webpack Visualizer: 깔끔한 시각화, 기능 부족
  • Webpack Bundle Analyzer: 용량 별 시각화, 검색/필터링 제공

 

2. 라이브러리 중복 줄이기

  npm yarn 등의 패키지 매니저는 라이브러리 간의 관계를 분석하고 필요한 라이브러리를 다운로드한다. 그런데 라이브러리A와 라이브러리 B가 각각 라이브러리C의 다른 버전을 사용하는 경우가 발생하면 Dependency confilict가 발생한다.

 

  npm과 webpack은 tree 구조로 필요한 버전을 모두 받는다. 예를 들어 라이브러리 C를 사용하는 라이브러리 A를 npm install로 설치했다면 node-modules 폴더에 A폴더가 생성되고 그 아래에 다시 node-modules 폴더가 생성되어 라이브러리 C폴더가 만들어진다. 이러한 경우 node-moules가 과도하게 큰 용량을 차지하게 될 수 있다. npm은 npm dedupe라는 명령어를 통해 중복을 제거하고 적합한 버전으로 통합할 수 있다.(semver를 준수한다는 가정 하에 major 버전이 바뀌지 않는 한 더 높은 버전을 받는다.)

 

  yarn은 문서 상으로는 Dependency confilict를 완벽하게 처리해준다고 써있다. 그래도 완벽한 것은 아니므로 yarn-dedeplicate 라이브러리를 사용하거나 yarn dedupe 명령어를 사용하는 것이 좋다.

 

  lodash는 JS 유틸리티 라이브러리로 array객체의 fill(), forEach(), filter() 등 유용한 코드들이 포함되어 있다. 따라서 내가 직접 쓰지 않아도 라이브러리 내에 포함되어 있는 경우가 많다. 또한 다양한 버전이 있으며 기능 별로 단독 패키지가 존재하므로 라이브러리가 중복될 가능성이 높다. 이 경우 Webpack의 alias 기능을 사용하고 코드에서도 babel-plugin-lodash를 사용하여 lodash에서 사용 중인 부분만 가져와서 사용하도록 한다. 

 

  lodash 외에 비슷한 문제가 있는 라이브러리는 babel-plugin-transform-imports 를 사용하여 소스코드 수정 없이 최적화할 수 있다.

 

3. 더 가벼운 라이브러리 사용하기

  라이브러리는 shorthands 표현, 캐싱 등을 지원하는 경우 생각보다 용량이 클 수 있다. 따라서 가능한 네이티브 함수를 사용하거나 더 가벼운 라이브러리를 찾아보는 것이 좋다. 가벼운 라이브러리를 찾을 때는 번들포비아라는 사이트를 이용하면 좋다. 번들 포비아는 gzip으로 압축되었을 때의 용량, 다운로드 속도, 각 버전별 용량, 라이브러리의 디펜던시를 확인할 수 있으며, 비슷한 라이브러리의 용량 비교도 제공한다.

 

  가벼운 라이브러리에 polyfill이 추가될 경우 오히려 용량이 커질 수 있다는 점을 주의하자. 반대로 무거운 라이브러이인데 포함된 라이브러리들이 이미 사용하고 있는 라이브러리와 많이 겹쳐있다면 실제 이 라이브러리를 씀으로써 추가되는 용량은 오히려 적을 수도 있다.

 

  라이브러리를 만들 때는 트리 쉐이킹을 고려하는 것이 좋다. 트리 쉐이킹은 정적 분석을 통해 필요한 코드만 취하는 매우 강력한 최적화 기능이다. 번들러의 트리 쉐이킹 성능은 사이드 이펙트를 얼마나 잘 판단하느냐에 따라 달라진다. 웹팩은 기본적으로 sideEffects 라는 필드를 참고하며 코드 분석 도중 사이드 이팩트가 발견되거나 분석이 어렵다면 트리 쉐이킹에 실패한다.

 

  Rollup은 Webpack에 비해 비교적 사이드 이펙트 판단을 잘 하는 편이다. 특히 preserveModules 옵션을 주면 모든 파일들이 하나로 합쳐지는 것이 아니라 기존 폴더구조 그대로 빌드되므로 일부 트리쉐이킹이 불가능한 코드가 섞여있더라도 그 부분만 빼고 나머지 부분은 트리쉐이킹된다.

 

  컴파일 툴을 잘 고르는 것도 중요하다. 웹펙의 production 모드에서는 terser라는 라이브러리가 사이드 이펙트 유무를 판단하여 코드를 최적화한다. terser는 pure-anotaion 주석이 있으면 side-effect가 없다고 판단하는데 babel의 경우 pure-anotaion을 붙여주지만, TypeScript 컴파일러는 붙여주지 않으므로 TypeScript를 사용하더라도 bable 컴파일을 하는 것이 좋다.(참고로 react는 빌드하면 pure-anotaion이 자동삽입된다)

 

  내가 생각하지 못한 사이드이펙트를 찾기 위해선 Webpack-cli의 stats 옵션 혹은 StatsWriterPlugin을 사용하는 것이 좋다. 이들은 웹팩이 번들링 과정에서 분석한 정보를 json 포맷으로 저장하며, json 파일을 Webpack Analysis Service에 올려 분석하면 사이드 이펙트 추적에 도움이 된다.

 

4. 라이브러리 영향 줄이기

  라이브러리의 영향을 최소화시키는 것은 중요하다. 예를 들어 웹 서비스가 단일 청크나 2개 정도의 청크로 구성되어 있다면 웹 페이지 운영 시 라이브러리가 추가되면 모든 페이지의 용량이 한꺼번에 증가한다. 이 경우 dynamic import와 웹팩의 Webpack magic comment(prefatch)를 이용하는 것이 좋다.(내 생각엔 리엑트 lazy-load, pre-load와 같은 것인데 next.js를 이용한 서버사이드 렌더링에서는 dynamic과 Webpack magic comment를 사용하는 것 같다.)

 

  next.js의 경우 Framework, Commons, 공유 Chunk, 페이지별 Chunk와 같은 형태로 청크를 자동으로 분리한다.

  • Framework: 쉽게 바뀌지 않는 react, react-dom, next. 캐싱 지원
  • Commons: 모든 페이지에서 사용되는 코드. 캐싱 지원
  • Shared: 두 페이지 이상에서 사용되는 코드
  • page: 해당 페이지에서만 사용되는 코드

  웹팩에 대해 관심이 생겨서 찾아본 영상인데 고급정보를 얻은 기분이다. 매일 쓰는 npm의 숨겨진 기능에 대해서도 알 수 있었고, 평소 서버 사이드 렌더링이 초기 로딩 속도는 빠를지라도 페이지를 이동할 때 또 모든 코드를 가져와야 하지 않나?는 생각을 갖고 있었는데 Next.js가 자체적으로 Code Spliting을 제공하여 보완하고 있다는 점에 감탄하였다. 영상을 보면서 웹팩과 Next.js를 더 공부해보고 싶다는 생각이 많이 들었다. 조만간 Next.js 강의를 찾아봐야겠다.

Comments