Rollup 모듈 번들러로 React 라이브러리 개발 경험기

Deinal·2023년 2월 8일
1
post-thumbnail

들어가기 앞서,,,

부족한 점이 많은 글 봐주셔서 감사합니다. 틀린 내용이 있으면 댓글 첨부 부탁드리겠습니다. 후기는 편의상 반말로 작성하였습니다.

개인 프로젝트를 진행하면서 프로젝트 간 사용하는 공통기능을 묶어 하나의 라이브러리로 분리하고 싶었다. 처음에는 Webpack으로 분리하려 했지만 라이브러리 내부에서 오류(Cannot read properties)가 해결 이 안되어 Rollup 번들러를 사용하게 되었다.

1. 프로젝트 생성 및 의존 패키지 설치 (패키지 툴은 yarn을 사용했다.)

프로젝트 폴더를 생성하고 yarn 초기화를 한다. 초기화 명령어는 init이다. 필요한 의존성 패키지는 아래와 같다.

yarn init

## devDependencies에 의존성 추가시
yarn add -D rollup

## peerDependencies에 의존성 추가시
yarn add -P react

## 프로젝트의 진입점
"main": "./dist/bundle.js", 
"type": "module",
"devDependencies": {
    "@babel/core": "^7.20.12",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-react": "^7.18.6",
    "@rollup/plugin-babel": "^6.0.3",
    "rollup": "^3.14.0"
},
"peerDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
}

여기서 "peerDependencies" 속성이 중요하다. peerDependencies 속성을 사용하면 react, react-dom 라이브러리를 사용하고 있는 루트 프로젝트의 node_modules 하위 react, react-dom을 참조한다는 뜻이다. 만약에 루트 프로젝트에 없다면 일반적인 의존성으로 대체된다.

만약 peerDependencies를 사용하지 않으면 react의 경우 Hooks 에러가 발생한다. 정확히는 아래와 같은 오류가 발생했다.

리액트 공식 홈페이지(https://ko.reactjs.org/warnings/invalid-hook-call-warning.html)에 해당 이슈에 대해 잘 나와 있는데 요약하면 프로젝트 React 패키지의 두 복사본이 있는 경우 오류가 발생한다고 한다. 두 복사본을 사용하는지 아닌지 확인할 수 있는 예제 소스도 알려주는데 직접 적용을 해봤다.

  1. 라이브러리 package.json에 react, react-dom을 devDependencies 속성에 명시한다.
devDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "@babel/core": "^7.20.12",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-react": "^7.18.6",
    "@rollup/plugin-babel": "^6.0.3",
    "rollup": "^3.14.0"
}
  1. 루트 프로젝트의 node_modules/react-dom/index.js에 아래 소스를 추가한다.
window.React1 = require('react');
console.log(window.React1);
  1. 라이브러리 프로젝트의 컴포넌트 파일에 아래 소스를 추가한다.
import ReactDom from 'react-dom';
import React from 'react';

window.React2 = React;
console.log(window.React1 === window.React2);
  1. 결과를 확인한다. 만약 false가 뜨면 두 복사본을 사용하고 있다는 뜻이다.

만약 peerDependencies에 react, react-dom을 옮기고 적용하면 true 값이 뜨면서 오류가 발생하지 않는다.

2. rollup.config.js 파일 생성

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

export default {
  input: "./index.js",
  external: ['uuid', 'react', 'react-dom'],
  output: {
    file: "./dist/bundle.js",
    format: "es",
    sourcemap: true,
  },
  plugins: [
    babel({
      babelHelpers: "bundled",
      presets: ["@babel/preset-env", "@babel/preset-react"],
    })
  ],
};

재그지그님의 블로그에서 "Rollup 기반 라이브러리 개발 환경 구성하기"글을 참고하였다. 설정에 사용된 속성을 살펴보자.

input: "./index.js"

라이브러리의 시작파일 경로 및 파일명을 지정하면된다.

external: ['uuid', 'react', 'react-dom']

라이브러리에서 의존하는 외부 패키지를 추가한다. 추가하지 않을 경우 "Unresolved dependencies" 경고가 발생한다.
rollup으로 빌드 시 외부 패키지가 라이브러리의 node_module에 없더라도 오류가 발생하지 않는다. 다만 경고만 노출하며 개발자가 node_module에 없는 것을 의도한다면 external 옵션을 주면 경고가 사라진다.
나의 경우엔 루트 프로젝트에 react, react-dom, uuid가 기본적으로 있기에 external 옵션을 줬다.

만약 라이브러리 번들에 외부 패키지를 포함시키고 싶으면 "@rollup/plugin-node-resolve" 플러그인 을 찾아보자

output: {
    file: "./dist/bundle.js",
    format: "es",
    sourcemap: true,
},

output 옵션은 빌드 결과로 나온 변들 파일에 대한 옵션이다.

file : 경로 및 번들 파일명으로 지정한다.

format : 번들 된 파일의 포맷을 정의한다. 아래 링크 접속 시 이해할 수 있다.
https://rollupjs.org/repl/

sourcemap : 디버깅을 하기 위해 추가하였다. true로 주면 map 파일을 생성하고 브라우저에서 디버깅할 수 있게 된다.

plugins: [
    babel({
      presets: ["@babel/preset-env", "@babel/preset-react"]
    })
]

rollup에서 사용할 외부 플러그인을 추가하는 옵션이다. 예를 들어 rollup은 jsx 구문을 처리하지 못한다. 그래서 바벨 플러그인을 설치해서 처리하도록 한다. 이렇듯 rollup에서 처리하지 못하는 부분들을 플러그인을 사용해서 해결한다.

"scripts": {
  "build": "rollup -c",
  "watch": "rollup -cw"
}

package.json 파일에 scripts를 추가 후 yarn build를 실행해보자.

yarn link를 사용하면 라이브러리를 npm에 배포하지 않고 테스트할 수 있는 방법이다. 사용법은 간단하다.

  1. 라이브러리 폴더로 이동

  2. yarn link 명령어 실행
    라이브러리의 심볼릭 링크가 "~/.config/yarn/link" 경로에 생긴다.

  3. 루트 프로젝트 이동

  4. yarn link <라이브러리 프로젝트 이름>
    link 경로에 있는 라이브러리를 참조하여 사용할 수 있다.

후기

이번 경험 글을 작성하면서 개발 시 모르고 지났던 세세한 부분들도 공부할 수 있어 좋았다. 나만 보는 글이 아닌 다른 분들도 볼 수 있기에 잘못된 정보가 있는지 꼼꼼히 체크하고 모르는 부분들은 찾아보았다.

참고 사이트

react - Invalid Hook Call Warning
yarn - yarn link
재그지그 - Rollup 기반 라이브러리 개발 환경 구성하기
심심재 - npm link 이해하기

profile
궁금증이 많은 사람입니다.

0개의 댓글