Webpack과 Tree Shaking

jhj46456·2020년 9월 25일
0
post-thumbnail

React 및 JS/TS 기초에 대해 설명하진 않습니다. 초보자를 위한 내용이 아닙니다.

webpack v4 기준입니다.

Tree Shaking

직역하면 나무를 흔들다입니다. 나무를 흔들면 어떤 일이 일어날까요?

썩은 나뭇가지가 떨어질 겁니다. 그렇다면 떨어지지 않고 붙어있는 것은 건강한 나뭇가지겠죠.

가을이 오고 사과 추수를 하려고 나무를 보니 열린 사과에 비해 나뭇가지가 너무 많아서 사과를 딸 수가 없습니다.

그래서 나뭇가지를 자세히 살펴보았는데, 썩은 나뭇가지건강한 나뭇가지가 있는 것을 발견합니다.

물론 이런 상황에도 사과 추수를 할 순 있지만, 사과를 건드리면서 썩은 나뭇가지가 함께 떨어질 것입니다.

그렇게 필자는 가지치기를 하기로 합니다. 썩은 나뭇가지를 먼저 자른 뒤 청소를 하고 사과만 따로 운송합니다.

이제 아래의 단어 설명을 보고 다시 한번 읽어봅니다.

나무 = 프로젝트
나뭇가지 = 모듈
사과 = 프로젝트 구동에 꼭 필요한 코드
썩은 나뭇가지 = 필요없는 코드 (dead code)
가지치기 = Tree Shaking

tree-shaking에 대한 설명은 이정도로 충분한 것 같습니다.


Condition

tree-shaking을 위해서는 다음의 조건이 필요합니다.


Process

가지치기가 실행되는 과정을 보겠습니다.

다음과 같은 코드가 있습니다.

📚 module.ts
      👇
export const add = (a: number, b: number) => console.log("add");

export const divide = (a: number, b: number) => console.log("divide");

📚 index.ts
      👇
import { add } from "./module";

add(1, 2);

mode: production 실행 시 과정을 보여드릴 수 없어 mode: development 부터 시작합니다.

webpack -d를 실행하면 다음과 같은 bundle.js를 얻을 수 있습니다.


지금은 썩은 나뭇가지가 남아있는 모습입니다.

썩은 나뭇가지를 청소하는 역할은 JS Compressor toolkit이 합니다.

mode: production으로 webpack을 실행하면 terser가 난독화를 해줍니다.


코드 난독화가 된 모습.
같은 console.log("~")을 실행하는 module.ts의 두 함수 중 add만 보입니다.

이런 방식으로 Tree Shaking이 진행됩니다.


node_modules

안타깝게도 node_modules에 대한 tree-shaking은 해당 패키지가 최적화 옵션을 내놓지 않는 한 불가능합니다.
(이 부분은 tree-shaking으로 줄일 수가 없습니다.)

npm 모듈 최적화 방법

  1. 해당 모듈이 최적화 옵션을 지원하는지 확인하세요.
  2. 동일한 기능의 더 가벼운 npm 패키지를 설치하세요.
  3. 직접 구현하세요.

필자의 사이드 프로젝트에서 사용하는 @apollo/clientbundle optimization을 지원하여 한번 따라해봤습니다.

📚 src/index.tsx
        👇
import React from "react";
import ReactDOM from "react-dom";
import {
  ApolloClient,
  InMemoryCache,
  HttpLink,
  ApolloProvider,
} from "@apollo/client";
👆 @apollo/client에서 모두 가져옴

const httpLink = new HttpLink({
  uri:
    process.env.NODE_ENV === "production"
      ? process.env.REACT_APP_PROD_API_URL
      : process.env.REACT_APP_DEV_API_URL,
});
const cache = new InMemoryCache();

const client = new ApolloClient({
  link: httpLink,
  cache,
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <div>work</div>
  </ApolloProvider>,
  document.getElementById("root")
);

이 상태로는 패키지의 모든 내용물을 가져옵니다.


95.04 KB

@apollo/client v3 부터는 부분적 import가 지원됩니다.

Apollo Client 3.0 includes built-in support for React hooks, but it absolutely still supports non-React view layers.
To use Apollo Client 3.0 with Vue, Angular, or another view layer of your choosing, 
import ApolloClient from the @apollo/client/core entry point:

문서를 보고 변경해봤습니다.

📚 src/index.tsx
        👇
import React from "react";
import ReactDOM from "react-dom";

import { ApolloClient, HttpLink } from "@apollo/client/core";
import { InMemoryCache } from "@apollo/client/cache";
import { ApolloProvider } from "@apollo/client/react/context";
👆 @apollo/client의 각 디렉터리에서 부분적으로 가져옴

const httpLink = new HttpLink({
  uri:
    process.env.NODE_ENV === "production"
      ? process.env.REACT_APP_PROD_API_URL
      : process.env.REACT_APP_DEV_API_URL,
});
const cache = new InMemoryCache();

const client = new ApolloClient({
  link: httpLink,
  cache,
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <div>work</div>
  </ApolloProvider>,
  document.getElementById("root")
);
   


80.94 KB

무려 15 KB나 다이어트한 모습을 확인할 수 있습니다.

또한 육안으로도 @apollo/client 크기가 줄어든 것을 볼 수 있습니다.

패키지가 위와 같은 최적화를 제공하지 않는다면 방법이 없습니다.

0개의 댓글