본문 바로가기
> 레벨업의 테크노트

[프론트엔드] Vue build time 최적화 해보기

by 알 수 없는 사용자 2021. 7. 12.

📌 목차

  • 서론
  • usedExport 나라 탐방
  • devtool 무기 획득
  • webpack-bundle-analyzer 세계 지도 획득
  • Element-UI 나라와의 전쟁 그리고 패배…
  • Lottie.js 나라와의 전쟁
  • 결론

📌 서론

안녕하세요. 빅픽처에서 종선버를 맡고 있는 최종선이라고 합니다. 🙇

요즘 걸리버 여행기를 너무 재밌게 읽고 있어서 main 프로젝트에서 build 최적화를 시키는데 기승전결의 형식으로 재밌게 풀어나가보려고 합니다. 그럼 재밌게 읽어주세요! (피드백도 너무나 환영합니다. ) 그럼 출발 ! 🚀

📌 usedExport 나라 탐방

💡 사용되지 않는 코드들이 build가 되는 모습을 보았고 나는 그걸 없애고 싶었습니다.

 

오랜만에 첫 출근을 하여 들뜬 마음으로 build가 된 main.js를 읽고 있었을때 였습니다… 사용되지 않는 코드들이 우후죽순으로 붙어있는걸 보았습니다.
마침 webpack tree shaking이 떠오르게 되었습니다.

먼저 해당 코드를 줄이고자 webpack에서 optimization(usedExports)을 설정해주었습니다.

 

🚨 usedExports 란?
해당 값은 bool의 값으로 선택이 가능하며 exports이지만 사용되지 않는 코드는 빌드하지 않습니다. production모드 일때는 default로 true가 정 이 되어있습니다.

 

// vue.config.js

configureWebpack: config => {
    if (!config.optimization) config.optimization = {};
    config.optimization.usedExports = true;
  },
적용 전 적용 후

main.js의 크기를 19.9MB -> 19.6MB로 줄일 수 있었습니다.

0.3MB를 줄인 셈이니 1바이트가 글자 하나니 보통 평균 한줄이 60바이트라고 가정을 한다면 약 5000줄을 줄인셈 입니다. 하지만 종선버는 이것으로 만족을 할 수 없었습니다. 그래서 좀 더 강력한 전설의 무기를 찾아 떠나게 되었습니다.

📌 devtool 무기 획득

💡 source-map은 잘 압축해준다는 장점은 있지만 느리다는 단점이 있습니다.

 

아직 build의 속도를 감안해봤을때 근본적인 부분을 해결을 못한것 같은 느낌이었습니다. 그래서 devtools관련 링크를 찾아보게 되었습니다.

 

 

그래서 devtool source-map에서 eval로 무기를 대체 하였습니다. 현재 production mode 이거나 development mode 두개다 devtool로 source-map을 사용해주고 있는데 이제 development mode 일때 가장 빠르다는 eval이라 불리는 무기로 대체 해보았습니다.

 

// vue.config.js

configureWebpack: config => {
    config.devtool = isDevOrLocal ? 'eval' : 'source-map';
  },

이 방법으로 꽤 많은 시간을 줄일수 있었지만 종선버는 조금 더 과감한 선택을 하고 싶었습니다.

📌 webpack-bundle-analyer 세계 지도 획득

💡 세계에는 큰 왕국이 이미 자리 잡고 있었다.

 

조금 더 과감한 선택을 하기에 앞서 실제 bundle의 크기를 알 필요가 있었습니다. 그래서 해당 번들 크기를 알려주는 webpack-bundle-analyzer를 사용하였습니다. 다음과 같이 설치를 해주었습니다.

 

// vue.config.js

// 🚧 Intentinally commented - by eddie
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

configureWebpack: config => {

    // 🚧 Intentinally commented only used for anaylzing bundle - by eddie
    config.plugins.push(new BundleAnalyzerPlugin());
  },

 

그렇게 작업이 끝난후 어떤 번들의 크기가 어떻게 되어있는지 알 수 있었습니다.

 

 

📌 Element-UI 나라와의 전쟁 그리고 패배...

💡 너무나 강력한 상대를 만나 좌절

 

세계 지도를 획득후 패기롭게 가장 많은 영역을 차지하고 있는 Element-ui 나라를 정복하고 싶었습니다. 해당 방법으로 cdn을 사용하는 방법이 있어 해당 방법론으로 공략을 해보았습니다. 하지만 이내 여러가지 문제점이 있었습니다.

 

🚧 문제점들?

  • Element ui 자체가 전역에 선언이 되어있지 않고 각각의 파일들로 선언되어 Vue.use()의 의존성을 제거하기가 힘들었습니다. (약 110개의 파일들)
  • 제거 하더라도 element ui dark theme으로 css 자체가 깨지는 문제가 발생하였습니다.

이런 문제점들 때문에 cdn으로 해결하기가 어려웠습니다. 따라서 해당 방법으로의 접근 방법을 번들 크기가 가장 큰 순서가 아닌 의존성이 많이 엮여있지 않지만 파일이 큰 것을 선택해야 한다는 것을 알게되었습니다.

📌 Lottie.js 나라와의 전쟁

💡 한번의 패배 이후 전략을 수정하여 대승리

 

위의 교훈을 토대로 정리해본 결과 `lottie.js 라는 타겟`이 눈에 들어왔습니다.

 

  • 의존성이 적어야 함 → 총 4개의 파일에서 Lottie.js를 사용하고 있음.
  • 번들의 크기가 커야함 → 위의 그림에서 보다싶이 번들크기가 5번째로 큼 (element > lodash > sendbird > moment > lottie)

 

그렇게 lottie.js를 공략을 결정하고 index.html, index-dev.html에 각각 cdn을 삽입해주었습니다. async와 defer를 걸어주지 않은 이유는 혹시 vue-script가 실행된 이후 실행이 되어버릴 위험성이 있어 생략을 해주었습니다. (정말 만약의 경우 → 99.9%의 대부분 그렇지 않을거라고 생각은 했음.)

 

 

<!-- index.html & index-dev.html -->

<!-- LOTTIE-WEB -->
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.6.10/lottie.min.js"
      integrity="sha512-QGaslwkb2XoQM7dTYUB8YucbFcUC+Mhm/xTkz+pXNv0Dm9aZSj1Js7OwXrXa1OrMJUpSi2Bj21srh8mwsFNKTQ=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
    ></script>

 

그리고 vue.config.js에 externals를 추가해주었습니다.

 

// vue.config.js

chainWebpack: config => {
    config.externals({
      IMP: 'IMP',
      'lottie-web': 'lottie',
    });
  },

🚧 webpack externals란?
import packages의 번들링을 막고 외부 dependecies를 runtime에 주입 시킵니다.

그렇게 lottie.js왕국을 멸망시킬수 있었습니다.🎉🎉🎉

 

 

📌 결론

💡 오픈소스는 매직이 너무 많아요. by 이문국님

 

실제로 빌드를 최적화 시키면서 오픈소스가 정말 양날의 검이구나 라는 생각을 많이 했습니다.

 

사실 저는 오픈소스를 매우 좋아합니다. 제가 그들보다 잘 만들 자신도 없기도 하고 능력자분들이 잘 만들어 놓았기도 했기 때문입니다. 잘 쓰면 좋지만 그렇지 않은 대부분의 경우 프로덕트의 기능과는 상관없는 기능(매직)들이 많이 들어가 크기만 많이 차지하는 경우가 많습니다. 그래서 프로덕트에 '정말 필요한 오픈소스 외엔 협의후 따로 프로덕트를 위한 기능을 개발을 해야하겠다' 생각이 많이 드는 유익한 시간 이었던거 같네요. 그럼 다음 여행기도 기대해주세요!

긴글 읽어주셔서 감사합니다. 🙇

댓글