728x90

Browser에서 Javascript 파일 실행과정

 

같은 용량이더라도 이미지는 파일을 다운로드한 후 디코딩만 하면되지만 JS를 실행하기위해서는 위와같이 작업단계가 많으므로 JS 실행이 이미지 로딩보다 오래걸리게된다.

브라우저에서 JS실행을 최적화 하기위해서 번들 최적화는 매우 중요하다.

 

1. 원인 찾기

Webpack을 기준으로 Bundle 분석 도구로는 Webpack Analyse, Webpack Visualizer, Webpack Bundle Analyzer 3개가 있다.

Webpack Bundle Analyzer가 용량별로 시각화를 해주기때문에 어떤 라이브러리가 많이 사용되고 있는지 알 수있고 사이드바를 이용해 검색과 필터링도 가능하다사용하기 쉽고 기능이 좋다.

같은 라이브러리이지만 버전이 다른경우

  • npm이 dependency conflict를 해결하는 특성때문이다. npm은 라이브러리간 관계를 검색하고 필요한 라이브러리를 다운받는다. 이 때 dependency가 같은 라이브러리를 참조하지만 이 라이브러리의 버전이 다르다면 해당 버전을 모두 설치한다. 각 library별로 가상 환경의 node_modules를 이용하기 때문에 충돌이 없다. 
    • browserfy는 직접 사용자에게 어떤 버전을 설치할지 물어본다.
  • node_modules의 용량이 과도하게 커질 수 있다.


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

  • node_modules의 용량이 과도하게 커지는 문제를 해결하기위해 npm은 라이브러리들이 시멘틱버전(semver)을 지킨다고 가정한다. major 버전이 바뀌지 않는한 더 높은 버전을 사용해도 문제가 생기지 않는다고 가정
  • npm dedupe 명령어로 중복된 모듈을 지워준다.
  • 특정 라이브러리가 다양한 버전의 라이브러리가 있다면 webpack의 resolve.alias기능을 이용하여 특정 라이브러리만 사용하도록한다.
    • lodash와 같은 라이브러리에는 다양한 라이브러리가 있다는 것이다. (lodash-es, lodash.get, lodash.isPlainObject, ...)  
    • {
        resolve: {
          alias: {
            	'lodash-es':'lodash',
              'lodash.get':'lodash/get',
              'lodash.isPlainObject':'lodash/isPlainObject'
          }
        }
  • tree shaking을 지원하지 않는 라이브러리의 경우(lodash), lodash-es를 쓰거나, babel-lodash-plugin을 이용해 사용중인 부분만 가려낼 수 있다.
  • 같은 기능을 하는  라이브러리의 경우 낮은 용량의 라이브러리를 찾아 사용한다.
    • webpack은 브라우저와 node환경을 최대한 맞추기위해 polyfill을 추가할 수 있다. 이로인해 라이브러리의 자체용량이 적어도 polyfill이 추가되어 느려질 때도 있다.
    • node-rsa 라이브러리의 경우 node.js의 지원만 생각하고 만들어진 라이브러리이기 때문에 브라우저로 넘어갈 경우 polyfill로 cryto, Buffer, big number등의 polyfill이 추가된다. (100kb)
    • bundle phobia에서 미리 용량 및 dependency를 확인한다. (export analysis)

 

라이브러리를 만들 때

tree-shaking

불필요한 코드를 정적분석을 통해 제거하는 기술. 정말 사용중인 코드만 bundle에 포함되기때문에 더 가벼운 번들을 만들 수 있다.

 

tree shaking이 어려울때

//module boo
export const a = foo();
export const b = bar();

//user
import {b} from './boo';

이때 과연 boo의 a를 bundle에서 빼도 될까? 정답은 그럴수도 있고 아닐수도 있다

// a를 제거해도 될때
function foo(){
  return 'Hello World';
}

export const a = foo();
export const b = bar();


// a를 제거해서는 안될때
let count = 0;
const foo(){
  count++;
  return count;
}

export const a = foo();
export const b = bar();

위의 코드의 경우 a를 제거해도 동작이 달라지지 않지만 밑의 코드의 경우 a를 제거하면 동작이 달라질 수 있다. 이 때를 side effect가 있다고 한다. 

tree-shaking을 하려면 side-effect가 없을때만 가능하다

이 side-effect판별능력은 bundler에 따라 다르다. webpack은 별로 안좋은편

 

 

webpack의 side-effect 판별 향상방법

webpack에게 side effect여부를 알려줘야한다. 이는 webpack에 sideEffects 필드를 설정하면된다.

이 필드가 있을때만 treeshaking을 시도한다. side effect를 발견하거나 분석이 어려운경우 tree shaking을 하지않는다.

 

프로덕션 빌드에서는 terser의 도움을 받아 deadcode를 삭제할 수 있다.

terser도 side effect의 유무를 판단하여 코드를 지운다. 이 때 terser의 판단을 돕는것이 pure annotation이다.

pure annotation이 붙어있으면 side effect가 없다고 판단한다.

const a = /*#__PURE__#*/ foo();
const b = /*#__PURE__#*/ bar();
console.log(b);


//prduction
const b =bar();
console.log(b);

side effect를 발생할때 이를 발견할 ㅅ수 있는방법은 무엇일까?

webpack의 stats (nextjs에서는 webpack-stats-plugin)를 사용한다.

 

라이브러리 영향 줄이기

라이브러리 용량을 더이상 줄이는 것이 어렵다면 무거운 라이브러리의 영향을 최소화한다.

이 때 bundle의 chunk를 나누도록한다.

 

단일 chunk일때 하나의 라이브러리가 추가되면 모든 페이지의 용량이 커지게된다.

 

dynamic import

용량이 큰 라이브러리를 필요할때만 받는 기법

import dynamic from 'next/dynamic';

const pageA = dynamic(()=> import('./PageA'));
const pageB = dynamic(()=> /*webpackPrefetch: true*/ import('./PageB'));

webpack magic comment를 사용하면 prefetch기능을 사용할 수 있다. prefetch를 이용하면 여유로운 시간에 미리 받아둔다. 

 

 

'Webpack' 카테고리의 다른 글

Package-lock.json  (0) 2021.07.27
Package.json dependencies versioning  (0) 2021.07.27
Webpack 이란?  (0) 2021.01.15

+ Recent posts