본문 바로가기
📍ETC/🜸 기타지식

Rollup을 활용한 번들링 최적화 (Import cost 줄이기) - 2편

by 예리Yelee 2023. 10. 11.
반응형
 

Rollup을 활용한 번들링 최적화 (Import cost 줄이기) - 1편

이슈를 발견하고 해결하는 삽질,, 아 아니 과정을 포스팅 하겠습니다 👿 이슈 MUI(Material-UI) 기반으로 개발된 부서 공통 컴포넌트 라이브러리에서 하나의 컴포넌트를 가져올 때 해당 모듈의 사이

yelee.tistory.com

앞서 작성한 1편에 이어 2편에서 해결 과정을 포스팅해보겠습니다

 

🥳 해결 과정

이슈가 있는 코드로 돌아와 rollup.config.js 파일을 살펴보았습니다.

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
import dts from 'rollup-plugin-dts';

export default [
  {
    input: 'src/index.ts',
    output: [
      {
        file: 'dist/cjs/index.js',
        format: 'cjs',
        sourcemap: true,
        name: 'thedevdesigner-react-lib',
      },
      {
        file: 'dist/esm/index.js',
        format: 'esm',
        sourcemap: true,
      },
    ],
    plugins: [
      external(),
      resolve(),
      commonjs(),
      typescript({ tsconfig: './tsconfig.json' }),
      terser(),
    ],
  },
  {
    input: 'dist/esm/types/index.d.ts',
    output: [{ file: 'dist/index.d.ts', format: 'esm' }],
    external: [/\.css$/],
    plugins: [dts()],
  },
];

(참고 : rollup config 설정 옵션)

기존에는 rollup으로 번들링 할 때 input으로 src/index.ts 파일 하나만 설정되어 있었습니다.
반면 output은 배열 형태 설정되어 있어서 input도 배열 형태로 넣어주면 컴포넌트 별로 번들링이 되지 않을까? 생각했습니다.

 input : ['src/index.ts', './src/**/*.tsx', '!./src/**/*.stories.tsx']

위와 같이 배열에 글로브 형태로 각 컴포넌트 파일을 input에 배열 형태로 할당했습니다.
(여기서 스토리북을 실행을 위해 필요한 stories.tsx 파일은 번들링에서 제외했습니다)

 

그리고 build를 해본 결과 위와 같이 entry module 을 찾을 수 없다는 에러를 맞닥뜨렸습니다. → 실패

rollup config 설정에서 각 컴포넌트별로 input/output 설정이 담긴 object를 만들어주면 해당 에러는 해결이 되겠지만,
컴포넌트 개수가 많은 만큼 번거롭기도 하고 효율적인 방법이 아니라고 판단되어 관련 rollup plugin이 있는지 찾아보았습니다

 

 

 

rollup-plugin-multi-input

rollup plugin for bundling modular libraries. Latest version: 1.4.1, last published: 7 months ago. Start using rollup-plugin-multi-input in your project by running `npm i rollup-plugin-multi-input`. There are 45 other projects in the npm registry using rol

www.npmjs.com

npm에서 찾은 위 플러그인이 가장 적합해 보여 바로 config 파일에 적용 후 다시 build 한 결과

앞서 보았던 에러와는 다른 에러를 맞닥뜨렸습니다.

JavaScript 이외의 파일 (CSS, 이미지, JSON)이 존재할 수도 있기 때문에
@rollup/plugin-json으로 JSON 파일이 존재한다면 Javascript 모듈로 변환해 주도록 플러그인을 설정했습니다.

(참고로 모든 문제가 해결되고 난 후에 json 플러그인 설정 부분을 주석해 보니 build에 아무런 문제가 없었습니다. 코드 리팩토링할 때 필요에 따라 패키지에서 제거해도 무방해 보입니다)

 

babel.config.js

module.exports = function getBabelConfig(api) {
  const presets = [
    [
      '@babel/preset-env',  // JavaScript 코드를 대상 환경에 맞게 변환하는 프리셋
      {
        modules: false, // ES6 모듈을 다른 모듈 시스템으로 변환하지 않도록 설정
        shippedProposals: api.env('modern'), // 최신 JavaScript 제안 사양을 지원
      },
    ],
    [
      '@babel/preset-react', // React 애플리케이션을 위한 Babel 프리셋
      {
        runtime: 'automatic', // React 17부터 도입된 자동 JSX 변환을 활성화
      },
    ],
    '@babel/preset-typescript', // TypeScript 코드를 JavaScript 코드로 변환
  ];

  return {
    presets,
  };
};

추가로 babel을 활용하여 저희의 환경에 맞게 프리셋을 설정했습니다.

다시 build해 본 결과 tsconfig.json에서 outDir, declarationDir 경로에 이슈가 발생하긴 했지만
경로 수정 후 원하는 형태로 빌드 파일을 만드는데 성공했습니다 🎉

 

 

 

← cjs 포맷은 기존과 동일하게 index.js 라는 하나의 파일로 번들링 되었고
esm 포맷은 components 폴더 하위에 컴포넌트 별로 분리되어 있습니다.

🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉

테스트 결과 해당 이슈도 해결된 것을 확인할 수 있습니다.

🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉

 

 

 

← 그런데 여기서 components와 types 폴더 외에
알수 없는 수많은 js 파일이 수백개 생성된 점이 개인적으로 불편하게 느껴졌습니다.

이 부분은 다음 챕터인 “추가 개선 사항”에서 이어서 작성하겠습니다

 

 

 

 

 

 

 

 

 

 

 

 

😖 추가 개선 사항

 

1. 불필요한 패키지 제거

번들링 최적화를 위해 코드 분석 하는 과정에서
팀 공통 컴포넌트 라이브러리에 불필요한 패키지가 많이 설치되어 있는 것을 발견했습니다.

이전에 테스트를 위해 설치한 라이브러리들이 여전히 남아 있어서 이를 제거하고,
로딩 버튼을 나타내기 위해 사용했던 react-spinners를 MUI의 Circular Progress 컴포너트로 교체하여 react-spinners 의존성을 제거했습니다.

또한, 아이콘이 필요한 컴포넌트의 경우 외부 아이콘 라이브러리에 의존하는 방식에서 프로젝트 내에서 직접 아이콘 리소스를 관리하는 방식으로 변경하여 외부 패키지에 대한 종속성을 최소화했습니다. 이러한 작업을 통해 모듈별 사이즈를 줄이는데 성공했습니다.

그리고 개발 및 빌드 과정에서만 필요한 패키지가 dependencies에 설치되어 있어서 이를 devDependencies로 이동시켰으며, 이러한 변경 사항을 부서 공통 라이브러리 코드에도 적용했습니다.

결과적으로 패키지 전체 크기는 약간 줄었지만, 현저한 차이는 나타나지 않았습니다.

 

2. rollup 추가 설정


부서 공통 라이브러리는 @mui/material 기반의 UI 컴포넌트로 구성되어 있으며
rollup-plugin-bundle-size라는 rollup 플러그인을 사용하여 번들 사이즈를 측정한 결과,
전체 패키지 사이즈에서 MUI가 차지하고 있는 비율이 상당히 큰 것을 확인했습니다

뿐만 아니라 최근 @mui/x-date-pickers, @mui/x-tree-view와 같이 필요로 하는 외부 패키지들이 추가되면서,
@mui/material만 의존하는 팀 공통 라이브러리의 전체 패키지 사이즈에 비해 부서 공통 라이브러리의 크기가 훨씬 더 큰것을 발견했습니다.

해결하기 위해, 이번 기회로 rollup에 대해 공부를 하면서 알게된 external 설정을 사용했습니다.
external 설정은 번들에 포함되지 않아야 하는 외부 패키지를 지정할 수 있는 옵션인데
이전에는 react와 react-dom만 설정되어 있었으나, 아래와 같이 사용하는 모든 외부 패키지를 external 설정에 지정하였습니다.

    external: [
      'react',
      'react-dom',
      '@emotion/react',
      '@emotion/styled',
      '@mui/icons-material',
      '@mui/material',
      '@mui/styles',
      '@mui/system',
      '@mui/x-date-pickers',
      'dayjs',
    ],

 

부서 공통 라이브러리 (9.18MB → 1.58MB) 82.78%감소

팀 공통 라이브러리 (1.2MB → 27.19kB) 약 97.734%감소

🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉

external 설정을 적용한 결과, 크기가 현저하게 줄어든 것을 확인할 수 있었습니다.

추가로 external 설정을 했더니 앞서 언급했던 지저분한 js 파일들이 같이 정리 되었는데, 정확한 원인은 모르겠습니다.
아시는 분 추가 설명 부탁드립니다.

🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉

 

이상으로 포스팅을 마치겠습니다.
사내에 공유한 문서를 가져오는 과정에서 어색한 표현이 있으니 양해 부탁드립니다 🤭🤗

 

반응형

댓글