이메일

React+Webpack에서 Code Splitting 적용기

테크

2019년 12월 03일


code splitting

작업 배경

안녕하세요 저는 와탭에서 프론트엔드 개발을 진행중인 원성철이라고 합니다. 와탭의 프론트는 현재 Spring boot + Velocity 의 환경에서 React로 넘어가는 과정 중에 있으며 저는 그 과정 중에 와탭에 합류를 하여, 현재 많은 legacy 코드들을 React에 맞게 변환을 한 상태입니다.

React는 기존의 페이지별로 HTML 파일을 가지고 돔을 작성하는 방식이 아닌 자바 스크립트로 페이지의 요소들을 그리는 SPA(Single Page Application) 이기 때문에 스크립트의 비중이 굉장히 높고, 많은 스크립트 파일을 가져올 경우에 페이지 로딩에 병목이 발생할 수 있어, 통상적으로 Webpack 을 이용하여 스크립트 파일을 하나의 Bundle 파일로 만드는 작업을 진행합니다.

Webpack을 이용하여 병목은 줄게 되었으나 서비스의 규모가 커지게 되면서 모든 스크립트 파일을 합친 Bundle의 사이즈가 커지게 되었는데, Bundle의 사이즈가 커지게 되면서 페이지의 첫 진입이 느려지는 불편이 발생하게 되었고, 이 불편을 해결하기 위해 Code Splitting이라는 기술을 이용해 모든 코드의 집합이었던 Bundle의 코드를 특정한 기준을 가지고 나누어 Chunk라는 특정 스크립트와 모듈의 부분집합이 되는 파일들로 나누는 작업을 필요로 하게 되었습니다.

와탭의 서비스는 react-router를 이용하여 주소에 따라서 다른 컴포넌트를 불러오도록 되어있는데, 이 주소에 따라서 불러오는 컴포넌트들을 기준으로 Chunk를 나눌 수 있도록 Code Splitting작업을 진행해보도록 하겠습니다.

현재의 상황 파악

우선 현재 webpack의 설정 파일을 확인합니다.

Wepack_config

저희 서비스의 경우에는 이렇게 production build, development build설정이 나누어져 있었습니다. 개발 환경과 배포 환경의 설정을 나누는 것은 좋지만, 두 설정에 중복되는 내용도 많고 내용 추가시에도 양쪽에 같은 작업을 해주어야 하며 개발 환경과 배포 환경의 설정이 혹여나 다르게 들어가기라도 하면 배포시에 개발 환경에서는 확인할 수 없는 버그들이 발생할 수 있기 때문에 작업을 하는 겸사 하나로 합쳐주었습니다.

Wepack_config2

파일은 하나로 합치고, env로 분기를 하여 빌드시의 명령어에 따라서 production build, development build의 설정을 다르게 할 수 있도록 하였고, 외에는 달라지면 사이드 이펙트가 발생할 수 있기 때문에 설정은 모두 동일하게 지정하였고, 여러 플러그인이나 설정이 서비스에서 하고있는 역할만 간단하게 파악하여 주석을 달았습니다.

그리고 현재 빌드된 결과를 확인하기 위해 webpack-bundle-analyzer 라는 플러그인을 추가하여 결과를 확인하였습니다.

webpack_bundle_analyzer

프론트의 모든 스크립트가 main.bundle.js 라는 파일에 포함되어 있는 것을 볼 수 있으며, 사이즈는 node_modules의 비중이 가장 컸으며, 노드 모듈에서는 의존성으로 중복된 내용이 있는 모듈, 실제 거의 사용하지 않으나 사이즈가 큰 모듈 등도 확인할 수 있었습니다.

file_size

마우스를 올려보면 파일의 사이즈를 자세하게 확인할 수 있습니다.

  • Stat size(minification 등의 변환이 일어나기 이전 input된 사이즈)
  • Parsed size(변환 이후 output된 파일의 사이즈
  • Gzipped size(gzip 압축 이후 사이즈)

(저희 서비스는 폐쇄망에서 package 형태로도 제품을 지원하기 때문에 네트워크는 빠르나 PC의 성능은 좋지 못한 경우 gzip 파일의 압축을 해제하는데 시간이 더 걸리는 경우가 있을 수 있어, 21.85MB의 파일을 실제 이용하고 있습니다. 테스트 이후 gzip을 이용할 계획은 있습니다.)

21.85MB라는 큰 사이즈의 번들 파일을 확인할 수 있었습니다.

Code Splitting 적용

우선 리액트 공식 문서(https://reactjs.org/docs/code-splitting.html#import)에서도 적혀있듯이 코드 스플리팅을 적용하기에 dynamic import 방식이 좋다고 판단되어, 컴포넌트가 랜더링 되는 타이밍에 dynamic import를 진행하는 HOC를 작성하였습니다.

HOC

그리고 실제 react-router에서 이용하는 컴포넌트를 위 HOC를 이용하여 dynamic import 하도록 수정했습니다.

HOC_dynamic_import

분리가 잘 되었겠지? 하는 생각을 가지고 빌드를 한 결과

webpack_bundle_analyzer_result

기존과 같은 결과가 나온 것을 확인할 수 있었습니다. (테스트의 경우 빠른 결과 확인을 위하여 minimize 옵션을 사용하지 않고 테스트 하여 용량이 크게 표시되고 있습니다.) 왜 적용이 되지 않았을까 하고 webpack의 설정들을 살펴본 결과 dynamic-import-node 라는 플러그인을 babel에서 이용중인 것을 확인했습니다.

dynamic import 구문을 require로 바꾸어주는 역할을 수행하는 플러그인인데, 검색해보니 node 서버쪽에서 사용하는 플러그인이고 여태까지 프론트에서 dynamic import를 이용한 부분도 없어 고민없이 제거했습니다.

webpack_bundle_analyzer_result

main.bundle.js에 있던 요소들이 많이 사라지고 chunk 파일들에 그 사라진 내용들이 있는 모습입니다. 그리고 다시 minimize 옵션을 사용한 상태로 결과를 확인해 보았더니

file_size

아직도 큰 사이즈이지만 처음에 비해 용량이 확연하게 줄어든 것을 확인할 수 있습니다.

webpack_bundle_analyzer_result (해당 작업 당시의 스크린샷이 없어, 현재 와탭 서비스에서 적용된 모습으로 대체합니다. 서비스에서는 당시보다 조금 더 개선이 된 상황입니다.)

실제 페이지에서도 main.bundle.js 파일을 불러오고, 상황별로 chunk 파일들을 추가로 불러오는 것을 확인할 수 있습니다.

마치며

한동안 기능 개발, 버그 수정, 기능 개발, 버그 수정만이 연속된 나날이었는데요, 웹팩에 대해서 완전 문외한이던 제가 이렇게 개선을 하게 되어 배운 점도 많고, 즐거운(+힘든) 작업이었고, bundle analyzer 등 시각적으로 상황을 보여주는 도구를 이용해보니 스플리팅 외에도 많은 개선점들이 보여서 앞으로도 많은 개선 작업들을 진행할 예정입니다.

이번에는 정말 간단하게 Code Splitting을 도입해 보았는데요 사실 여기까지 다룬 내용은 정말로 간단하게 도입만 할 수 있는 내용을 다뤄 보았고 이런 내용 외에도 특정 모듈 몇개에서만 사용하는 모듈을 어떻게 분리할 것인지, chunk를 불러오는 타이밍 조절, dynamic import 구문의 es5에서 이용 등등 정말 다뤄야할 내용이 많지만 그러한 내용들은 다음에 또 기회가 된다면 다뤄보도록 하겠습니다.

부족한 필력의 글 봐주셔서 감사드리고 앞으로도 나날이 발전하는 와탭의 프론트가 될 수 있도록 노력하겠습니다.

감사합니다.

IT 서비스 성능 관리, 와탭으로 시작하세요.
와탭 무료로 시작하기
원성철
원성철([email protected])
Development TeamFront-End Developer

지금 바로 데모를 통해 와탭을 경험해보세요!

어려웠던 모니터링 분석이 와탭 하나로 쉽게 가능합니다.