나의 rollup.js 삽질기

MM·2023년 7월 31일

make it worth

목록 보기
5/10
post-thumbnail

라이브러리화를 해보자

rollup 없이 디펜던시 설정만 해서 npm publish로 배포를 해 봤다. 결과..

음~대가리가 아파온다

rollup.js를 쓰자

남이 만든 라이브러리 쓰는게 제일 마음 편하다

웹팩과 비교했을 때의 장점은 아래와 같다. Rollup 사용하기 | Dan DevLog )

!https://dantechblog.gatsbyjs.io/favicon-32x32.png?v=b4877091fcb7706797ec10f5f1597d26

  • webpack과 다르게 코드들을 동일한 수준으로 호이스팅 한 후 한 번에 번들링을 진행하기 때문에 속도에서는 webpack보다 빠르고 결과물도 훨씬 가볍다.
  • es 모듈 형태로 빌드를 할 수 있어 라이브러리나 패키지를 작업하는데 활용할 수 있다.
    • webpack은 es 모듈로 번들을 할 수 없음, 오직 commonJS 형태로 번들링 진행 가능

설치 플러그인은 다음과 같다(rollup으로 라이브러리 npm에 배포하기 - chanyeong )

!https://chanyeong.com/favicon.png

  • rollup: rollup 모듈
  • @rollup/plugin-babel: rollup에서 babel을 사용 할 수 있게 해주는 플러그인
  • rollup-plugin-peer-deps-external: peerDependency로 설치된 라이브러리의 코드가 번들링 결과에 포함되지 않고 import를 통해 불러와서 사용할 수 있게 해주는 플러그인
  • @rollup/plugin-url: 파일을 데이터 URI 혹은 es모듈로 가져오는 플러그인
  • rollup-plugin-terser: 생성된 es번들을 최소화하기 위한 플러그인
  • @rollup/plugin-alias: 번들링 시 alias(절대경로)를 인식하게 해주는 플러그인
  • @rollup/plugin-commonjs: commonjs 형태의 모듈을 es모듈로 변환해주는 플러그인
  • @rollup/plugin-node-resolve: node_modules에서 써드파티 모듈을 사용할 수 있게해주는 플러그인
  • rollup-plugin-dts: typescript 번들링 플러그인 (타입 정의파일도 가능)
  • rollup-plugin-prettier: 번들링된 파일에 스타일을 적용해주는 플러그인
  • rollup-plugin-typescript2: typescript 번들링 플러그인
  • rollup-plugin-uglify: 번들링된 파일 난독화 및 minify

여기에 에러문구가 발생할 때마다 이것저것 추가한 최종 설정파일이 이것..

import commonjs from "@rollup/plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import resolve from "@rollup/plugin-node-resolve";
import svgr from "@svgr/rollup";
import image from "@rollup/plugin-image";
import url from "@rollup/plugin-url";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import sourcemaps from "rollup-plugin-sourcemaps";
import babel from "@rollup/plugin-babel";
import json from "@rollup/plugin-json";
import jsx from "acorn-jsx";

const extensions = [".js", ".jsx", ".ts", ".tsx"];
const external = ["react", "react-dom", "styled-components"];

export default [
  {
    // 번들링 기준 파일
    input: "src/index.ts",
    // 번들링 결과 파일과 형식(esm -> es modules, cjs -> commonjs)
    output: {
      file: "dist/index.js",
      format: "es",
    },
    external,
    acornInjectPlugins: [jsx()],
    plugins: [
      commonjs({ include: "node_modules/**" }), //commonJS형태로 만들어진 모듈도 불러와서 사용 할 수 있게 해줌
      resolve({ extensions }), //node_modules에서 모듈을 불러올수 있도록 해줌, ts/tsx 파일도 불러올수 있음
      url(), //미디어 파일을 dataURI 형태로 불러와서 사용 할 수 있게 해줌
      svgr(), //svg를 컴포넌트로 사용 할 수 있게 해줌
      peerDepsExternal(), //peerDependencies에 설치된 라이브러리들을 external모듈로 설정하여 번들 결과물에서 제외
      babel({ exclude: "node_modules/**" }), //babel을 사용 할 수 있게 해줌
      typescript({ tsconfig: "./tsconfig.json" }), //typescript 플러그인
      svgr(),
      image(),
      url(),
      peerDepsExternal(),
      sourcemaps(),
      json({ compact: true }),
    ],
  },
];

추가로 이 라이브러리를 사용하는 앱은 리액트 라이브러리가 이미 설치되어 있을 가능성이 높으므로, 기존의 dependencies에 있는 패키지들을 peerDependencies로 이동시킵니다. 참고로 peerDependencies를 갖고 있다는 것은 어떤 사람이 자신 프로젝트에 당신의 패키지를 설치할 때, 완전히 똑같은 dependencies를 갖고 있다는 것을 의미합니다. 따라서 react 패키지와 함께 사용되는 react-dom 패키지에는 reactpeerDependencies로 설치되어 있는 것을 확인할 수 있습니다.

→ 참고로 npm i는 dependencies만 읽으므로.. dependencies와 peerDependencies 둘 다 설정해 주어야 한다.

위 문서에서 작성하던 롤업 관련 글을 이어서 작성한다.

참고 문서

Rollup.js 환경설정

!https://so-so.dev/favicon-32x32.png?v=94575be6e89ca1ae1da4180e8a9e4e2b

드디어 성공!!

import commonjs from "@rollup/plugin-commonjs";
import typescript from "rollup-plugin-typescript2";
import resolve from "@rollup/plugin-node-resolve";
import svgr from "@svgr/rollup";
import image from "@rollup/plugin-image";
import url from "@rollup/plugin-url";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import babel from "@rollup/plugin-babel";
import json from "@rollup/plugin-json";

export default [
  {
    input: "src/index.ts",
    output: [
      {
        file: "dist/index.js", // Output file
        format: "es",
      },
    ],
    external: ["react", "react-dom", "styled-components", "canvas"],
    browser:true,
    plugins: [
      resolve(),
      commonjs({ include: "node_modules/**" }), //node_modules에서 모듈을 불러올수 있도록 해줌, ts/tsx 파일도 불러올수 있음
      url(), //미디어 파일을 dataURI 형태로 불러와서 사용 할 수 있게 해줌
      svgr(), //svg를 컴포넌트로 사용 할 수 있게 해줌
      peerDepsExternal(), //peerDependencies에 설치된 라이브러리들을 external모듈로 설정하여 번들 결과물에서 제외
      typescript({ tsconfig: "./tsconfig.json" }), //typescript 플러그인
      image(), //이미지 쓸 수 있게 해줌
      json({ compact: true }), //json 파싱 관련 옵션
      babel({
        //jsx로 파싱해 주는 바벨 관련 옵션
        babelHelpers: "bundled",
        presets: ["@babel/preset-env", "@babel/preset-react"],
        extensions: [".js", ".jsx", ".ts", ".tsx"],
        exclude: "node_modules/**",
        include: ["src/**/*"],
      }),
    ],
  },
];

하지만, 이제 두번째 문제가 남아 있다.

public으로 배포한 테스트 라이브러리와 달리 new activity tool는 private하게, 그러나 사내에서는 사용할 수 있어야 한다..

찾아본 방법은 다음과 같다.

1. private npm package(유료)

사용인원당 월7달러.

가장 쉬운 방법.

2. Github packages

50mb까지는 무료이고 private package도 배포 가능. 단 개인 액세스토큰을 필요로 한다.

우선은 이쪽으로 라이브러리화 시도.

레포지토리에 추가된 패키지 모습.

해당 프라이빗 패키지에 접근하려면, 프로젝트의 루트에 .npmrc를 추가하고 아래 내용을 복사해 넣으면 된다.

//npm.pkg.github.com/:_authToken=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
@hm970506:registry=https://npm.pkg.github.com/

테스트

이제 만든 뉴 액티비티툴 패키지를 가져왔으나……

수많은 오류가 발생했다.

폴리필이란

오류에서 줄줄이 폴리필 이야기를 하고 있길래 뭔가 싶어 찾아봤다.

폴리필

!https://ko.javascript.info/img/favicon/favicon.png

명세서엔 새로운 문법이나 기존에 없던 내장 함수에 대한 정의가 추가되곤 합니다. 새로운 문법을 사용해 코드를 작성하면 트랜스파일러는 이를 구 표준을 준수하는 코드로 변경해줍니다. 반면, 새롭게 표준에 추가된 함수는 명세서 내 정의를 읽고 이에 맞게 직접 함수를 구현해야 사용할 수 있습니다. 자바스크립트는 매우 동적인 언어라서 원하기만 하면 어떤 함수라도 스크립트에 추가할 수 있습니다. 물론 기존 함수를 수정하는 것도 가능합니다. 개발자는 스크립트에 새로운 함수를 추가하거나 수정해서 스크립트가 최신 표준을 준수할 수 있게 작업할 수 있습니다.

이렇게 변경된 표준을 준수할 수 있게 기존 함수의 동작 방식을 수정하거나, 새롭게 구현한 함수의 스크립트를 "폴리필(polyfill)"이라 부릅니다. 폴리필은 말 그대로 구현이 누락된 새로운 기능을 메꿔주는 역할을 합니다.

그러니까 쉽게 말하면 바벨의 땜빵 도구라는 의미같다.

그럼 롤업 설정에서 폴리필을 추가해 주면 되나?

놀랍게도 라이브러리에서 에러가

아니었다. 삭제.

에러 문구를 읽어보니.. 노드의 바닐라 모듈 폴더를 인식을 못 하고 있어서, 해당 기능들이 모두 에러가 나고 있는 것 같았다. 그러니까 라이브러리 문제가 아니라 읽는 놈 문제라는 것.

관련 사항으로 검색해보니 웹팩 문제라고 하는데..

원인을 찾았다.

image-20230706-005336.png

06 7월 2023, 09:53 오전

폴리필 이야기도 있는 걸 보니 이게 문제가 맞는듯.

webpack.config.js를 루트에 만들어서 아래 내용을 추가해 주었다.

module.exports = function (webpackEnv) {
  return {
    resolve: {
      fallback: {
        fs: false,
        os: false,
        path: false,
      },
    },
  };
};

안된다.

package.json에 아래 사항도 추가하라는 글도 있어 추가.

"browser": {
  "fs": false,
  "path": false,
  "os": false
}

안된다.

cra특성상 웹팩 설정을 직접 건드릴 수 없다고 하는데..

react-app-rewired 로 웹팩 설정을 수정할 수 있다고 한다.

루트폴더에 config-overrides.js를 추가하고, 다음 내용을 추가했다.

var webpack = require("webpack");
module.exports = function override(config) {
  const fallback = config.resolve.fallback || {};
  Object.assign(fallback, {
    crypto: require.resolve("crypto-browserify"),
    stream: require.resolve("stream-browserify"),
    assert: require.resolve("assert"),
    http: require.resolve("stream-http"),
    https: require.resolve("https-browserify"),
    os: require.resolve("os-browserify"),
    url: require.resolve("url"),
    tty: require.resolve("tty-browserify"),
  });
  config.resolve.fallback = fallback;
  config.plugins = (config.plugins || []).concat([
    new webpack.ProvidePlugin({
      process: "process/browser",
      Buffer: ["buffer", "Buffer"],
    }),
  ]);
  return config;
};

패키지에서 스크립트 내용도 바꿨다.

"start": "react-app-rewired start",

노드 관련 에러들이 사라졌다!

남은 에러는 두 개.

이래도 되는가?

활동툴 라이브러리를 사용하기 위해 사용하는 쪽에다 누덕누덕 설정파일들을 추가하고 있는데..

근본적인 의문이 들었다.

단순하게 사용하려고 만든 라이브러리인데 사용 전 과정이 이렇게 복잡하면..의미가..있나?

해당 라이브러리를 사용하기 위해 루트에 추가한 설정 파일만 3개째다.

무엇보다도,

  • 해당 라이브러리 하나만을 위해 웹팩 설정을 직접 조정하는 것이 실제 사내 코드에 적용하여도 문제 없는 사항인가?
  • 해당 코드가 이러한 작업을 통해 실행이 가능해도, 실제 사내 코드에 적용이 불가능한 사항이라면 라이브러리화 외에 다른 작업(번들링 파일 직접 넣기 등)을 찾아보는 것이 좋지 않을까?

..이러한 생각을 과장님과 함께 논의했고,

이러한 복잡한 과정 대신 차라리 번들링 파일을 박아버리면 어떨까 하는 생각이 나와 이를 시도해보기로 했다.

→ 실패.

애초에 폴리필을 사용하는 이유가 뭘까?

resolve를 줬음에도 계속 서버에서 사용해야 하는 코드를 프론트로 불러와서 문제가 되는 것 같은데..

Rollup including unnecessary server code in client bundle

https://cdn.sstatic.net/Sites/stackoverflow/Img/favicon.ico?v=ec617d715196

여러모로 구글링한 결과 현재 나와 비슷한 이유의 스택오버플로우를 찾았다.

이 사람의 경우 서버사이드 코드가 일부 프론트에 포함되어서라고 하는데…..나는 서버사이드 코드가 없는데..? (설마 파이어스토어를 사용해서?? 아니면 로컬 디렉토리에접근해서..?)

profile
중요한 건 꺾여도 그냥 하는 마음

1개의 댓글

comment-user-thumbnail
2023년 7월 31일

이런 유용한 정보를 나눠주셔서 감사합니다.

답글 달기