Tree Shaking

contability·2024년 4월 9일
0

소개


웹 애플리케이션의 크기, 특히 js의 비중은 과거에 비해 꽤나 커졌다.
일반적으로 네트워크를 오갈 때는 압축된 번들이 온다는 것을 고려해보면, 실제 압축이 해제된 크기는 더 클 것이다.
리소스를 처리할 때는 압축이 되지 않은 파일을 기준으로 하기 때문에 고려해야한다.

그리고 js는 처리하는데 많은 비용이 드는 리소스다. 다운로드 후 비교적 가벼운 디코딩 시간만 소용되는 이미지와는 다르게,
js는 파싱, 컴파일, 실행도 되어야 한다.
즉, 다른 리소스에 비해 js는 비싼 리소스라고 할 수 있다.

js 엔진의 효율성을 개선하기 위한 작업이 지속적으로 이루어지고 있지만 js의 성능 향상 작업은 항상 개발자의 몫이다.

이를 위해 다양한 기술들이 있는데
Code Splitting과 같이 청크로 분할하고, 이러한 청크를 필요한 애플리케이션 경로에만 제공하여 성능을 향상시킬 수도 있다.
괜찮은 기술이지만 사용하지 않는 코드가 포함될 수도 있다.
이러한 문제를 해결하기 위한 것이 바로 Tree Shaking이다.

Tree Shaking?


나무를 흔들어서 죽은 나뭇잎을 떨어뜨리는 것과 같이 코드를 빌드할 때 실제로 사용하지 않는 코드들을 제외한다는 뜻이라고 한다.
애플리케이션을 일종의 나무와 같은 구조로 보는데에서 유래된 것이며 트리의 각 노드는 앱에 고유한 기능을 제공하는 종속성을 나타낸다.
최신 애플리케이션에서는, 다음과 같은 import를 활용하여 디펜던시를 가져온다.

import utils from 'utils-lib'

let count = 0;

const handlePlus = (a) => {
	count += a	
};

utils.parseResult(handlePlus(5)); // '현재까지 누적된 카운트는 : (a)'

앞뒤로 스트링 메시지가 감싸져 리턴되는 utils.parseResult() 라는 메서드가 있다고 가정해보자.
utils 라이브러리에는 위와 같이 표출을 도와주는 수 많은 메서드들을 가지고 있는 모듈이지만
여기서 실제 사용하는 메서드는 parseResult() 단 하나 뿐이다.

애플리케이션 초기 단계에서는 이런 디펜던시가 상대적으로 적을 수도 있고 모든 메서드를 사용했을 수도 있다.
그러나 애플리케이션이 커질 수록 이 디펜던시도 같이 커지게 되고, 시간이 지날 수록 사용하지 않는 디펜던시가 제거되지 않는 경우도 생기게 된다.
이 문제의 해결을 위해, 트리쉐이킹은 Static import문을 사용하여 ES6 모듈의 특정 부분만을 가져오는 방법을 사용한다.

import { parseResult } from 'utils-lib'

let count = 0;

const handlePlus = (a) => {
	count += a
};

parseResult(handlePlus(5);

위 import문과 차이점은 모든 것을 import하는 이전 코드와는 다르게 여기는 딱 필요한 메서드들만 import했다는 것이다.
'utils-lib'에서 사용하지 않는 모듈들은 함께 번들링 되지 않기 때문에 번들링된 js 크기는 분명 다를 것이다.

고려사항


모듈들이 사이드 이펙트를 발생시키는지 여부를 확인해야 한다.

안전한 트리쉐이킹이란, 예측 가능한 입력을 가지고 동일하게 함수의 스코프 밖에 어떤 것도 변경하지 않으면서 
예측 가능한 결과를 반환하는 모듈이 안전하게 트리쉐이킹 할 수 있는 디펜던시 이다.

웹팩의 경우 package.jsonsideEffects: false로 지정하여 패키지와 패키지 사이에 부수효과가 없음을 암시할 수 있다.

웹팩 설정을 통한 사이드 이펙트 옵션 체킹

{
  "name": "webpack-tree-shaking-example",
  "version": "1.0.0",
  "sideEffects": false
}

혹은, 특정 파일에 대해서만 부수효과가 없다고 지정할 수 있다.

{
  "name": "webpack-tree-shaking-example",
  "version": "1.0.0",
  "sideEffects": ["./src/utils/utils.js"]
}

후자의 예제의 경우, 여기에서 지정된 파일은 부수효과가 없는 것으로 가정한다.
package.json에 추가하고 싶지 않으면, module.rules를 활용하여 설정할 수 있다.

예외


트리쉐이킹이 제대로 동작하지 않는 경우가 있다. 예를 들어 Lodash와 같은 경우가 있다.
Lodash의 설계적인 특성상, Lodash-es를 설치해서 사용해야 한다.

이외에도 트리 쉐이킹의 적용을 받지 않는 라이브러리가 있다면, es6 구문을 활용하여 메서드를 내보내는지 여부를 확인해야 한다.
CommonJS 방식인 module.exports를 사용하는 경우 웹팩에서 트리쉐이킹이 불가능하다.
그럼에도, webpack-common-shake와 같은 라이브러리를 사용하면 가능할 수도 있지만, 일부 케이스의 경우 트리쉐이킹이 완벽하게 되지 않는다.
그러므로 안정적인 트리 쉐이킹을 위해서는 es6 모듈을 사용하는 것이 좋다.

END


트리 쉐이킹에서 어떤 일이 발생할지는 애플리케이션과 애플리케이션의 의존성, 아키텍처에 달려있다.
번들에서 사용하지 않는 코드를 삭제한다면 최적화를 일궈낼 수 있다.

물론, 이득이 없을수도 있다. 그러나 프로덕션 빌드에서 이러한 최적화를 활용하도록 빌드 시스템을 구성하고, 애플리케이션에서 필요한 것만 선택적으로 가져오면 애플리케이션을 최소한의 크기로 유지할 수 있다.
이는 성능 측면에서, 사용자 측면에서 모두 좋은 영향을 줄 수 있다.

0개의 댓글