Rollup을 이용한 React 패키지 번들링

Seoyong Lee·2024년 5월 6일
0

개발 공부

목록 보기
21/21
post-thumbnail

사내에서 리액트 관련 패키지를 개발하면서 Rollup에 대해 간단히 알아보았습니다.

패키지 번들링이란

리액트로 개발을 진행하다 보면 다양한 형태의 파일을 생성하여 작업하지만, 실제 배포를 위해서는 브라우저가 이해할 수 있도록 js, css 등의 형태로 합친 이후 압축하여야 합니다. 이러한 과정을 번들링이라고 하며 리액트는 대표적으로 웹팩(Webpack) 번들러를 이용하는 경우가 많았습니다. 그러나 웹팩은 Tree Shaking이 잘 이루어지지 않으며 ESM(ES Module) 번들이 불가능한 문제가 있어 이를 해결하기 위해 Rollup이 등장하였습니다. Rollup은 가벼운 무게로 번들링이 가능하며 개별 패키지 개발 등에 유용하게 사용이 가능합니다.

Rollup 설치 및 설정

먼저 Rollup을 설치합니다.

yarn add -D rollup

이후 루트 경로에 설정 파일(rollup.config.js)을 추가합니다.

// rollup.config.js

export default {
  input: './src/index.js', // 진입 경로
  output: {
    file: './dist/bundle.js', // 출력 경로
    format: 'es', // 출력 형식
    sourcemap: true, // 소스 맵(디버깅)
  },
};

이후 rollup -c 명령어를 이용해 루트 설정 파일(rollup.config.js)을 사용하도록 설정합니다.

// package.json

{
  "main": "./dist/bundle.js" // 진입 경로
  // ...
  "scripts": {
    "build": "rollup -c",
    "watch": "rollup -cw"
  }
}

React 설정

리액트 설치는 주의해야 할 부분이 있습니다. 리액트를 dependency로 바로 추가하게 되면 실제로 만든 패키지를 사용하는 쪽(호스트)의 리액트와 충돌이 일어날 수 있으므로 호스트 리액트를 사용하도록 설정해야 합니다. 이는 (PeerDependency)를 이용해 설정이 가능합니다.

// package.json
{
  // ...
  "peerDependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  }
}

이후 JSX를 인식할 수 있도록 바벨 관련 패키지를 추가합니다.

# 바벨 설치
yarn add -D @babel/core @babel/preset-env @babel/preset-react

# 롤업에서 바벨을 사용하게 해주는 플러그인도 설치
yarn add -D @rollup/plugin-babel

rollup.config.js에 바벨 관련 설정을 추가합니다.

// rollup.config.js

import babel from '@rollup/plugin-babel';

export default {
  input: './src/index.js',
  output: {
    file: './dist/bundle.js',
    format: 'es',
    sourcemap: true,
  },
  plugins: [
    // 바벨 트랜스파일러 설정
    babel({
      babelHelpers: 'bundled',
      presets: ['@babel/preset-env', '@babel/preset-react'],
    }),
  ],
};

TypeScript 추가

TS 추가를 위해서는 다음의 패키지를 추가합니다.

# 롤업 타입스크립트 플러그인 설치
yarn add -D @rollup/plugin-typescript

# 롤업 타입스크립트 플러그인의 피어 디펜던시 설치
yarn add -D typescript tslib

# 바벨에서도 이를 해석하게 추가
yarn add -D @babel/preset-typescript

# 리액트, 리액트 DOM 타입 패키지 추가
yarn add -D @types/react @types/react-dom

config 파일에 TS를 추가합니다.

// rollup.config.js

import babel from '@rollup/plugin-babel';
import typescript from '@rollup/plugin-typescript';

export default {
  input: './src/index.ts',
  output: {
    file: './dist/bundle.js',
    format: 'es',
    sourcemap: true,
  },
  plugins: [
    // 바벨 트랜스파일러 설정
    babel({
      babelHelpers: 'bundled',
      presets: [
        '@babel/preset-env',
        '@babel/preset-react',
        '@babel/preset-typescript',
      ],
      extensions: ['.js', '.jsx', '.ts', '.tsx'],
    }),

    // 타입스크립트
    typescript(),
  ],
};

tsconfig.json을 추가합니다.

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "esnext"],
    "jsx": "react",
    "module": "es6",
    "moduleResolution": "node",
    "baseUrl": "./",
    "strict": true,
    "esModuleInterop": true
  }
}

위 설정까지 진행하면 TypeScript + React 패키지 개발을 위한 번들러 설정이 완료됩니다.

HTML 파일 Serve 하기

패키지 개발을 위해 DEV 환경을 구성해서 실제 만든 패키지를 테스트 해보려면 rollup-plugin-serve 를 이용할 수 있습니다. 먼저 다음과 같이 테스트 폴더를 추가합니다.

// example/template.html

<html>
  <head>
  </head>
  <body>
    <div id="app"></div>
    <script src="./example.js"></script>
  </body>
</html>
// example/index.js

import React from "react";
import ReactDOM from "react-dom";
// 테스트하려는 패키지를 불러옵니다 ex) import ShakaPlayer from 'shaka-player-react';

function App() {
  return <div>HELLO!!!</div>;
}

ReactDOM.createRoot(document.getElementById("app")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

이후 개발 환경에서의 빌드 시 html serve를 통해 확인해 볼 수 있도록 config 파일을 수정합니다.

// rollup.config.js

import babel from "@rollup/plugin-babel";
import typescript from "@rollup/plugin-typescript";
import alias from '@rollup/plugin-alias';
import serve from "rollup-plugin-serve";
import replace from "@rollup/plugin-replace";
import commonjs from "rollup-plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import html from "@rollup/plugin-html";
import fs from "fs";

const plugins = [
  replace({
    preventAssignment: true,
    // process undefined 관련 에러 방지를 위해 추가
    "process.env.NODE_ENV": JSON.stringify("production"),
  }),
  babel({
    babelHelpers: "bundled",
    presets: [
      "@babel/preset-env",
      "@babel/preset-react",
      "@babel/preset-typescript",
    ],
    extensions: [".js", ".jsx", ".ts", ".tsx"],
    exclude: "node_modules/**",
  }),
  typescript(),
  resolve({ browser: true }),
  commonjs(),
];

const lib = {
  input: "src/index.tsx",
  output: {
    file: "dist/bundle.js",
    format: "es",
  },
  external: ["react"],
  plugins,
};

const builds = [];

builds.push(lib); 

// 개발환경
if (process.env.DEV) {
  const devtool = {
    input: "example/index.js",
    output: {
      file: "dist/example.js",
      format: "umd",
    },
    plugins: [
      ...plugins,
      // 기본 포트는 localhost:10001으로 설정되어 있습니다.
      serve({
        open: true,
        contentBase: "dist",
      }),
      // dist 경로에 index.html을 추가합니다.
      html({
        dest: "dist",
        filename: "index.html",
        template: () => fs.readFileSync("./example/template.html"),
      }),
       alias({
        entries: [
          {
            find: '내 패키지 이름',
            replacement: 'src/index.js' // 내 패키지 위치
          }
        ]
      })
    ],
  };
  builds.push(devtool);
}

export default builds;

package.json의 script를 다음과 같이 수정합니다.

// package.json
{
  // ...
  "scripts": {
    "build": "rollup -c",
    "start": "DEV=1 rollup -c --watch"
  },
}

빌드 결과

실행 결과 → http://localhost:10001/

References

https://rollupjs.org/
https://wormwlrm.github.io/2021/11/07/Rollup-React-TypeScript.html
https://github.com/matvp91/shaka-player-react/tree/master

0개의 댓글