회사에서 일하면서 점점 프로젝트가 커질수록, 성능의 중요성을 느꼈기 때문에 열심히 체크리스트를 만들고 성능 최적화에 도전하고 있다. 그 중 가장 기초적인 최적화를 진행했던 경험에 대해 적어보려고 한다.
최신 프론트엔드 환경을 개선하기 위해 꼭 필요한 도구는 Webpack Bundle Analyzer이다.
초기에 bundle analyzer를 돌렸을 당시, 위와 같은 결과를 얻었다. 번들 사이즈도 굉장히 크고, 개선할 사항이 많다고 여겼기에 Frontend 팀은 대규모의 개선작업을 시작한다.
위의 분석 결과를 보면, pdfMake와 xlsx가 큰 비중을 차지하는 것을 볼 수 있다. 이는 현재 사용하는 라이브러리(amchart)의 dependency들이다. 현재 콘솔에서는 위의 기능을 사용하지 않기에, vue.config.js의 configureWebpack 부분에 아래와 같은 코드를 넣어준다.
externals(context, request, callback) {
if (/xlsx|canvg|pdfmake/.test(request)) {
return callback(null, `commonjs ${request}`);
}
callback();
},
위는 예시이지만, 이와 같이 꼭 필요하지 않은 외부 라이브러리가 있다면 externals에 넣어주면 번들링할 때 제거할 수 있다.
필요없는 라이브러리가 같이 번들링 되면, 로딩 타임이나 빌드 타임 등이 늘어나기 때문에 제거해주는 것이 좋다.
한 페이지만 로드하면 되는 첫 시작페이지에서 기존에는 모든 페이지를 로딩했었던 이슈가 있었다.
이를 방지하기 위해, Code Splitting이라는 작업을 수행하였고 경로별로 필요한 컴포넌트와 페이지만 로딩할 수 있게 되었다.
이 작업을 위해선 우선 바벨의 동적 임포트 플러그인을 설치해야 한다. 그 후 .babelrc에 추가한다.
"plugins": ["@babel/plugin-syntax-dynamic-import"]
그 후 route의 config 파일을 이러한 방식에서
import UserPage from '@/views/identity/user/pages/UserPage.vue';
아래와 같은 방식으로 바꿔준다.
const UserPage = () => import(/* webpackChunkName: "User" */ '@/views/identity/user/pages/UserPage.vue');
이 때 webpackChunkName을 넣어주지 않으면 맨 처음의 스크린샷처럼 js/chunk-*.js 이렇게 어떤 청크인지 알아볼 수 없기 때문에, webpackChunkName이라는 웹팩의 magin comment를 이용해 chunk에 이름을 달아주는 것이 좋다.
Bundle 사이즈를 줄이기 위해, Compression plugin을 추가하여 압축하였다.
const CompressionPlugin = require('compression-webpack-plugin');
plugins: [
new CompressionPlugin(),
...
],
그 후 Optimization 옵션을 추가하여 최적화하였다.
optimization: {
runtimeChunk: true,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
chunks: 'initial',
name: 'vendor',
enforce: true,
},
},
},
},
코드를 분리할때 중복을 없애고, entry 파일을 여러 개로 분리한다. 또한 cacheGroups를 통해 초기에 vendor를 분리하여 로딩 시간을 최적화하였다. (추가적으로 최적화할 필요 있음. node_modules 등)
https://jeonghwan-kim.github.io/series/2020/01/02/frontend-dev-env-webpack-intermediate.html
크롬의 성능 측정 도구인 Lighthouse에서 중요하게 평가하는 지표 중 하나는 바로 First Contentful Paint, 즉 초기 렌더링 시간이다.
(처참했던 과거의 로딩 시간..)
이렇게 초기 렌더링이 지연되는 이유를 고민해보니, 맨 처음 콘솔의 초기화 작업이 꽤 많고 오래 걸린다는 것을 알 수 있었다.
콘솔 초기화 때 도메인을 확인하고, 언어를 설정하고, GA를 위한 tag를 설정하는 등의 여러 작업들이 이루어지기 때문이다.
기존에는 이 작업을 모두 기다린 후 로그인 창이 뜨는 방식이라 빈 화면이 나타나는 시간이 매우 길었다. 이를 개선하기 위해
Site initializer라는 유틸 함수를 도입하였다. 해당 유틸함수가 해주는 역할은, 모든 초기화 작업들을 진행하며 store의 display항목에 초기화가 모두 완료되었는지 여부를 판단하는 값을 설정해주는 것이다.
이 값을 통해 App.vue에 로딩을 설정하여 초기화가 진행중일 경우에는 로딩 UI를 보여주도록 하여 아래와 같이 로딩 시간을 크게 단축할 수 있었다. (물론 초기화 로직들도 어느정도 최적화하였다.)
이외에도 디자인시스템을 npm packaging하고, 무거운 라이브러리를 교체하는 등의 작업(https://velog.io/@sian/빌드시간-잡아먹는-범인-찾기-feat.-webpack-bundle-analyzer)을 통해 아래와 같이 큰 개선을 이룰 수 있었다. (필요없는 외부 라이브러리가 제거되었으며, 모든 chunk를 식별할 수 있고, 번들 사이즈가 절반으로 줄었다!)
이렇게 우선 간단한 최적화를 진행하고, 지속적으로 여유가 있을 때마다 bundle analyzer를 이용해 최적화를 하려고 노력😅하고 있다.