Bundle Size 최적화
참고: https://dev.to/mbernardeau/6-tips-to-optimize-bundle-size-50n9
default import 사용
default import와 member style import(global import) 했을 때 가져오는 파일 사이즈가 다르다.
// member style imports(global import):
import { Row, Grid as MyGrid } from 'react-bootstrap';
import { merge } from 'lodash';
// default style imports:
import Row from 'react-bootstrap/lib/Row';
import MyGrid from 'react-bootstrap/lib/Grid';
import merge from 'lodash/merge';
lodash를 global import 하면 아래처럼 필요없는 다른 모듈도 함께 가져온다.
vscode 확장프로그램의 import cost 를 사용해서 둘의 차이를 비교해보자.
import cost를 적용하면 아래처럼 import 코드 오른쪽에 번들 사이즈가 나온다.
lodash import를 비교해보자. default import를 하면 훨씬 더 크기가 줄어든다.
import { time } from 'lodash' // 69.4kb (gzipped: 24.5kb)
import times from 'lodash/times' // 2.8kb (gzipped: 1.2kb)
이렇게 일일이 default import 해도 되지만 babel plugin을 써서 member style import를 default import로 변환할 수 있다.
- babel-plugin-transform-imports 또는 babel-plugin-import 을 쓰면 된다.
- 둘 다 member import → default import로 바꿔주지만, 컴파일 결과나 path 표현에 차이가 있다.
babel.config.js
// babel-plugin-import
[
'babel-plugin-import',
{
libraryName: '@material-ui/core',
libraryDirectory: '',
camel2DashComponentName: false,
},
'core',
],
[
'babel-plugin-import',
{
libraryName: 'lodash',
libraryDirectory: '',
camel2DashComponentName: false,
},
'lodash',
],
// transport-imports
[
'transform-imports',
{
lodash: {
// eslint-disable-next-line no-template-curly-in-string
transform: 'lodash/${member}',
preventFullImport: true,
},
'@material-ui/?(((\\w*)?/?)*)': {
// eslint-disable-next-line no-template-curly-in-string
transform: '@material-ui/${1}/${member}',
preventFullImport: true,
},
},
],
Code Split
바로 가져올 필요가 없는 코드는 dynamic import로 변경한다. chunk loading을 비동기적으로 실행하여 필요할 때만 불러와 로드할 때의 bundle size를 줄일 수 있다(전체적인 bundle size는 줄어들지 않는다).
아래처럼 code split을 할 수 있다.
next.configs.js
module.exports = withPlugins(
[
{
webpack: (config, options) => {
return Object.assign({}, config, {
optimization: {
splitChunks: {
chunks: 'all',
},
},
})
},
]
)
그러나 스플릿 되는 코드가 많아질 수록 파싱, 다운로드에 시간이 오래 걸린다. UX에 있어 페이지 로드가 느린 것처럼 느껴질 수 있으므로 남용하지 않는 게 좋다. 우리 팀에서는 아래처럼 next/dynamic을 써서 필요한 부분에만 적용하고 있다.
import dynamic from 'next/dynamic'
const PostGridComponent = dynamic(() => import('./PostGrid'), { ssr: false })
export { default as usePostGridPageSize } from './usePostGridPageSize'
export default PostGridComponent
크기가 큰 library 대체
@next/bundle-analyzer 를 사용해서 번들 사이즈를 분석했다. devDependencies로 설치하고, 실행 시 ANALYZE=true npm run build 를 실행하면 build하면서 번들 사이즈를 분석한다.
기존 번들 사이즈 (번들 사이즈에 대한 설명)
- stat: 14.55 mb | 최적화되기 전 코드의 번들 사이즈다.
- parsed: 3.76mb | minimize된 파일 사이즈다. 브라우저에서 파싱된 자바스크립트 코드의 사이즈.
- gzip: 1.13mb | minimize와 gzip을 거친 후의 사이즈. 네트워크에서 로드될 때의 사이즈.
기존 컴포넌트 파일
기존 node_modules 파일
한 녀석이 유독 컸다. bwip-js라고 바코드 생성할 때 쓰는 library인데 parsed size가 664.96kb로 꽤 컸다. 필요한 코드만 따로 빼서 내부에 Util 파일을 만들려고 했는데 생각보다 코드 양이 너무 많아 안될 것 같았다.
그래서 bwip-js를 jsbarcode 라이브러리로 대체했다. bwip-js unpacked size가 7.56MB인데 반해 jsbarcode는 1.02MB였고 weekly downloads 수도 bwip-js의 2배였다.
대체 후 번들 사이즈
- parsed: 3.17MB
- gzip: 996.25KB
코드도 간단하게.
// 변경 전
bwipjs.toCanvas(canvas, {
bcid: 'code128', // Barcode type
text: data, // Pin number
scale: 3, // 3x scaling factor
height: 10, // Bar height, in millimeters
textxalign: 'center', // Always good to set this
backgroundcolor: 'FFFFFF',
padding: 5,
})
// 변경 후
JsBarcode(canvas, data, { height: 50, displayValue: false })
'개발 이야기 > front-end' 카테고리의 다른 글
css rendering2 | box-sizing? position:absolute? (0) | 2021.02.24 |
---|---|
Component Pattern | Context API 를 활용하자. (0) | 2021.02.24 |
css rendering - 기본 레이아웃 | CSS 속성은 사실 OO이다? (0) | 2021.02.21 |
자바스크립트 이벤트 루프와 비동기 콜백 | how does javascript work? (0) | 2021.02.19 |
[Browser] 브라우저가 화면을 그리는 법 Render Tree, layout, paint | Browser Rendering (0) | 2021.02.11 |