사내에서 리액트 관련 패키지를 개발하면서 Rollup에 대해 간단히 알아보았습니다.
리액트로 개발을 진행하다 보면 다양한 형태의 파일을 생성하여 작업하지만, 실제 배포를 위해서는 브라우저가 이해할 수 있도록 js, css 등의 형태로 합친 이후 압축하여야 합니다. 이러한 과정을 번들링이라고 하며 리액트는 대표적으로 웹팩(Webpack) 번들러를 이용하는 경우가 많았습니다. 그러나 웹팩은 Tree Shaking이 잘 이루어지지 않으며 ESM(ES Module) 번들이 불가능한 문제가 있어 이를 해결하기 위해 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"
}
}
리액트 설치는 주의해야 할 부분이 있습니다. 리액트를 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'],
}),
],
};
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 패키지 개발을 위한 번들러 설정이 완료됩니다.
패키지 개발을 위해 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