brunch

You can make anything
by writing

C.S.Lewis

by swimjiy Oct 11. 2020

번들링도 미니멀리즘이 필요하다: Tree Shaking

더 가벼운 서비스를 위한 webpack 최적화 방법을 알아보자

프런트엔드 프로젝트를 배포하기 위해서 주로 webpack을 이용하여 여러 모듈을 묶어서 빌드합니다. 이 과정을 번들링이라고 하죠.

번들링을 완료한 파일의 경우는 실제 개발 단계의 파일들을 압축하고 최적화하는 과정을 거치기 때문에 사이즈가 비교적 작아집니다. 다만 작은 규모의 프로젝트에서는 크게 상관이 없는데, 규모가 커지고 더불어 여러 모듈에 의존하다 보면 번들링 된 파일이 당연히 더 커질 수밖에 없습니다. 그렇게 파일이 커지면 초기 컴파일/실행 속도가 느려지고 자연스레 사용자 경험에도 안 좋은 영향을 미치겠죠. 이런 상황에서 쓰지 않는 코드를 제거하여 파일의 크기를 줄일 수 있다면 어떨까요?




Tree Shaking이란

그런 역할을 가능하게 하는 게 Webpack4부터 제공하는 트리 셰이킹(Tree Shaking)입니다. 이름에서도 알 수 있듯 쓰지 않는 코드들을 DOM트리로부터 제거하는 기능입니다. 나무로 따지면 가지치기라고도 볼 수 있겠네요.

webpack에서는 번들링 하는 모드를 두 가지 제공합니다. 하나는 개발 단계에서 사용하는 development, 나머지 하나는 배포 단계에서 사용하는 production인데요, 트리 셰이킹의 경우 development 모드에서는 사용되지 않고 production 모드로 실제 배포를 위해 번들링을 할 때에만 적용됩니다.


그럼 트리 셰이킹을 할 때와 하지 않을 때를 간단하게 비교해보겠습니다. 먼저 트리 셰이킹을 하지 않고 일반적으로 모듈 안에 있는 함수 전부를 가져오는 import 구문입니다. 

이렇게 작성하고 번들링을 수행하면 webpack은 해당 모듈 안에 있는 모든 코드를 가져오게 됩니다. 그래프로 보면 아래와 같습니다. 만약 내가 function들 중 일부만 사용하고 싶어도 전부를 번들링 하게 되는 거죠.


반면에 import로 모듈을 불러올 때 필요한 함수만 불러오게 되면 webpack에서 쓰지 않는 함수들을 번들링 과정에서 제거하고 최적화된 파일을 받을 수 있게 됩니다.

필요한 함수만 정의하는 방법은 다양한데 라인1 예시처럼 destructuring을 이용하여 필요한 오브젝트만 가져오거나 라인3 예시처럼 cherry-pick 방식으로 가져올 수도 있습니다.

그래프로 보면 아래와 같이 필요한 함수만 가져오게 되는 거죠. 지금은 확인할 수 없지만 bundle.js의 크기도 줄어들었겠고요.



참고로 이러한 import 형태는 라이브러리를 만드는 쪽에서 설정을 하기 때문에 node_modules/의 패키지처럼 스스로 작성한 모듈이 아닌 경우 해당 패키지에서 이런 형태의 import를 지원하는지 꼭 확인을 하고 사용해야 합니다.

가령 React UI 프레임워크인 Material UI의 경우 아래와 같이 가이드를 통해 트리 셰이킹 하는 방법을 자세히 설명하고 있습니다.


다만 실제 개발 프로젝트에서 트리 셰이킹을 적용하려고 할 때 꼭 확인해야 할 부분들이 있습니다. 트리 셰이킹을 하기 위해서는 일종의 조건이 필요하기 때문이죠.


 




ES Module를 사용하는지 확인하기

첫 번째는 해당 패키지가 ES module, 정확히는 import 구문을 사용하고 있는가입니다. ES module이 중요한 이유는 위 예제에서도 알 수 있듯 사용하는 함수만 명시하기 위해서 import 구문을 사용하는데, 설계 방식에서 ES Module을 사용하지 않는 패키지에서는 적용할 수 없기 때문입니다. 가령 lodash 패키지의 경우 CommonJS 모듈을 사용하기 때문에 tree shaking을 사용하기 위해서는 ES 구문을 지원하는 lodash-es 패키지를 대신 사용하는 방법 등이 있습니다.


또한 바벨이 js파일을 트랜스파일링 하는 과정에서 ES Module을 CommonJS로 변환하는지도 확인해야 합니다. 하위 브라우저 중에서는 ES 문법을 지원하지 않는 경우가 종종 있으므로 주로 babel로 범용적인 버전으로 트랜스파일링을 거치는 경우가 많은데, 그 과정에서 import구문까지 변환하기 때문에 webpack에서는 어떤 함수만 번들하면 되는지 확인할 수 없게 되는 거죠. 이런 이슈를 막기 위해서 아래와 같이 .babelrc 파일에서 babel-preset-env modules옵션을 false로 설정하여 commonJS로 트랜스파일링되는 상황을 방지할 수 있습니다.





Side Effect 고려하기

다음 주의 할 점은 사이드 이펙트가 발생하는지 입니다. 사용하지는 않지만 다른 코드에 영향을 미칠 수 있다고 판단하는 경우 사이드 이펙트가 발생했다고 하는데 이렇게 사이드 이펙트가 발생하는 경우 webpack에서 필요 없는 파일을 판단할 수 없기에 tree shaking을 수행하지 않기 때문입니다.

참고로 이런 사이드 이펙트는 webpack이 자체적으로 발생 여부를 판단할 수 없기 때문에 package.json의 sideEffects 옵션을 확인하고 유무를 판단합니다. 따라서 package.json 파일에서 아래와 같이 sideEffects를 false로 설정하면 webpack이 해당 플래그를 인식하여 사이드 이펙트가 없다고 가정하여 트리 셰이킹을 수행합니다.




마무리

오늘은 번들링 최적화를 위한 트리 셰이킹에 대해 알아봤습니다. 사실 이런 최적화 작업이 실제 서비스 개발 단계에 있어서 큰 영향을 미치진 않지만 이런 디테일이 결국 서비스의 퀄리티를 결정하는 요소라고 생각합니다. 

때로 엄청난 퍼포먼스보다 사소한 디테일이 더 마음을 울리게 하는 것처럼요.

이번 시간을 통해 새로운 최적화 방법에 대해 알 수 있었고 더 좋은 사용자 경험에 대해 고민해볼 수 있었던 시간이었습니다.




참고 자료

트리 쉐이킹으로 자바스크립트 페이로드 줄이기 | TOAST UI :: Make Your Web Delicious!

Webpack 4의 Tree Shaking에 대한 이해 | Huns.me

Webpack에서 Tree Shaking 적용하기 | by Daybrush (Younkue Choi) | NAVER FE Platform | Medium


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari