OTTER-LOG

rollup 환경 구축하기

rollup 환경 구축하기
by otter2022년 11월 30일에 최종수정되었습니다.
잘못된 내용이 있으면 댓글을 달아주세요.
  • 리액트 컴포넌트 배포를 위해 환경을 구축하며 작성한 내용이어서 배포를 염두에 두고 작성한 부분이 많습니다. 😄

rollup 을 이용한 환경을 구축합니다. rollup 의 대부분의 기능은 webpack 으로도 물론 가능합니다. (오히려 webpack 이 더 편한 부분도 있지 않나 하는 생각도 들었습니다. ) 그렇지만, rollup 을 사용하면 cjs , esm 모듈을 쉽게 구축할 수 있습니다. 또, 라이브러리를 배포하기 위한 환경 구성에서 레퍼런스가 다른 번들러보다 많았다는 점도 좋았습니다.

체크리스트

우리는 rollup 을 사용해 어떤 환경을 구축해야 할까요? 저는 간단히 다음과 같이 생각하며 시작했습니다.

  1. javascript (es6이상 문법에 대한 트랜스파일링)
  2. react, ts
  3. jest로 테스트
  4. cjs, esm 모듈로 구분해 배포할 수 있을 것

javascript

rollup 은 웹팩과 비슷한, 모듈 번들러 입니다. 다만, 바벨의 기능을 rollup-config 에서 간단히 적용할 수 있습니다.

yarn add -D rollup rollup-plugin-babel @babel/core @babel/preset-env
  • @bable/core : 바벨의 기본적인 기능을 가지고 있습니다. es6 자바스크립트를 트랜스파일링 합니다.

  • @babel/preset-env : 이를 통해, 다양한 환경들에 특정시킬 수 있습니다. (특정 브라우저, 또는 비율로 )

    module.exports = { "presets": [ [ "@babel/preset-env", { "targets": ">= 0.25%, not dead, ie >= 10", "useBuiltIns": "entry", // 이 부분은 usage로 바꾸면 실제 사용하는 코드만을 변환합니다. "corejs":3 } ] ] } // 위와 같은 방식으로, 타겟을 지정해 사용할 수 있습니다.
  • @bable/cli : 커맨드 인터페이스로, 바벨을 실행시킵니다. 우리는 rollup을 사용해 실행할 것이므로, 따로 설치하지 않았습니다. 대신, 아래로 실행합니다.

  • @rollup/plugin-babel : rollup 에서 babel 을 사용할 수 있도록 하는 플러그인입니다.

위를 설치해주고, rollup.config.mjs 를 작성해 주겠습니다.

(Error: Node tried to load your configuration file as CommonJS even though it is likely an ES module 오류를 쉽게 해결하고자, .mjs 로 작성했습니다. )

// rollup.config.mjs import babel from '@rollup/plugin-babel'; export default { input: './src/index.js', output: [ { file: './dist/esm/index.js', format: 'es', sourcemap: true, }, { file: './dist/cjs/index.js', format: 'cjs', sourcemap: true, }, ], // input, output을 설정해주고 우리는 esm, cjs를 모두 생성하기 위해 위처럼 진행해줍니다. plugins: [ // 바벨 트랜스파일러 설정 babel({ presets: [ [ '@babel/preset-env', // { // targets: '>= 0.25%, not dead, ie >= 10', // useBuiltIns: 'usage', // corejs: 3, // 속성을 넣을 수 있어요! 저는 일단 제거했습니다. // }, ], ], }), ], }; // babel을 따로 파일로 관리할 수 있습니다. // 저는, 한 파일에 몰아 두는게 rollup의 컨셉이라 생각해 위처럼 진행했씁니

이제는, 이 빌드를 진행하게 되는 pacakge.json 들을 수정해 줍시다.

// package.json { "main": "dist/cjs/index.js", // csj를 받아옵니다. require에 대응됩니다. "module": "dist/esm/index.js", // exm을 받아옵ㄴ디ㅏ. import에 대응됩니다. "devDependencies": { "@babel/core": "^7.20.2", "@babel/preset-env": "^7.20.2", "@rollup/plugin-babel": "^6.0.2", "rollup": "^3.3.0", "rollup-plugin-babel": "^4.4.0" }, "scripts": { "build": "rollup -c", "watch": "rollup -cw" } }

src 폴더에 간단한 파일을 두고, 빌드시켜보면 dist 폴더에 번들이 생성된 것을 확인할 수 있습니다.


react

reactjsx 를 읽기 위해 babel 을 사용합니다.

yarn add -D @babel/preset-react yarn add -P react react-dom // 우리는 , react를 사용하지 않습니다. 받는 쪽에서 필요한 디펜던시를 제공하기 위해 // peer로 설치합니다.
// rollup.config.js export default { input: "./src/index.jsx", // 이제 jsx를 받으니까 수정합시다! ... plugins: [ babel({ presets: [ "@babel/preset-env", ["@babel/preset-react", { runtime: "automatic" }], ], }), ], };

역시 index.jsx 에 간단한 리액트 컴포넌트를 작성하고, 빌드를 해 보면 잘 빌드가 되는 것을 확인할 수 있습니다.

const Button = () => { return <button>버튼</button>; }; export default Button; // cjs import { jsx } from 'react/jsx-runtime'; var Button = function Button() { return /*#__PURE__*/jsx("button", { children: "\uBC84\uD2BC" }); }; export { Button as default }; //# sourceMappingURL=index.js.map

typescript

이제 타입스크립트를 지원해야 합니다. 그런데, rollup 에서 제공하고 있는 typescript 플러그인에는 문제가 있습니다. dist 루트에 index.d.ts 를 생성하지 못합니다. 여기 에 그 해결법이 존재했지만 제대로 적용되지 않았고 (모노레포를 만들 때에 사용해야만 했고 경로관련 문제가 해결하기 어려웠습니다.) 그래서 기존 bable 을 이용했습니다.

yarn add -D @babel/preset-typescript typescript tslib @babel/plugin-transform-runtime @types/react @types/react-dom

그리고, rollup.config.js 를 수정합니다.

... import babel from "@rollup/plugin-babel"; const extensions = [".js", ".jsx", ".ts", ".tsx"]; export default { input: "./src/index.ts", output: [ { file: "./dist/esm/index.js", format: "es", sourcemap: true, }, { file: "./dist/cjs/index.js", format: "cjs", sourcemap: true, }, ], watch: { exclude: "node_modules/**", include: "./src/*", }, plugins: [ babel({ presets: [ "@babel/env", ["@babel/preset-react", { runtime: "automatic" }], "@babel/preset-typescript", ], plugins: ["@babel/plugin-transform-runtime"], babelHelpers: "runtime", exclude: ["node_modules/**", "dist"], extensions, }), ], };

추가적인 rollup plugin

yarn add -D @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-peer-deps-external
  • rollup/plugin-node-resolve : node_modules 에서 써드파티 모듈을 사용할때 사용합니다.
  • rollup-plugin-peer-deps-external : 피어 디펜던시로 설치된 라이브러리를 번들링에 포함하지 않습니다. import 를 통해 불러옵니다.
  • rollup-plugin-commonjs : cjs 형태로 이루어진 모듈의 코드를 es6 로 포함하여 결과물에 포함합니다.

jest

jest 에도 ts 를 사용하기 위해 ts-jest 를 사용했습니다.

yarn add -D @testing-library/dom @testing-library/jest-dom @testing-library/react @testing-library/user-event jest jest-environment-jsdom @types/jest ts-jest ts-node @types/jest
import type { JestConfigWithTsJest } from 'ts-jest'; const jestConfig: JestConfigWithTsJest = { preset: 'ts-jest', testEnvironment: 'jsdom', moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], moduleNameMapper: { '^@src/(.*)$': '<rootDir>/src/$1', }, watchPathIgnorePatterns: ['node_modules', 'dist'], }; // jest.config.ts

그리고, 간단한 컴포넌트 테스트를 진행해보면 잘 진행 되는 것을 확인할 수 있습니다.

한가지 문제가 있는데, 여기서 react, react-dom 을 디펜던시로 추가해야 합니다. dom 을 테스트 하기 위한 tesing-library/domreact-dom 에서 몇가지 유틸리티를 가지고 오는 메세지가 출력되어 추가할 수 밖에 없었습니다. 더 좋은 해결방법이 있었을지.. 🥲

이로써 환경설정은 마무리 했습니다. 저는 이를 기반으로 lerna + yarn workspace 를 이용해 리액트 컴포넌트 라이브러리를 모노레포로 구성해보려고 합니다.

Ref