18만줄 js파일 배포하기

jingjinge·2025년 3월 7일
0

OpenSource

목록 보기
4/9
post-thumbnail

https://github.com/gronxb/hot-updater/pull/164

hot-updater의 firebase plugin을 제작하던 중이었다.

package.json에서 exports라는 property가 나에게 너무나도 도움이 되어서 따로 정리하고자 한다.


functions deploy

내가 추측한 firebase의 functions deploy 과정은 아래와 같다.

  1. package.json의 engines부분을 읽어 노드 버전을 확인하고 노드를 서버에 설치한다.

  2. packge.json의 진입점인 main에 적혀져 있는 부분을 읽고 해당 부분을 서버에 올린다.

근데 이건 내가 직접 할게 아니라, user 측면 터미널에서 이루어져야한다.

라이브러리를 배포해야하는데 functions에 packge.json이 존재한다면 레포지토리로 인지하지만, workspace에서 관리하는 영역이 아니기 때문에 override를 통해 @hot-updater/firebase를 별도의 패키지로 보고 해야할 것인데, 무조건 한번에 install되야만 하기 때문에 하나의 workspace로 가져가고 싶었다.

그래서 packge.json의 이름을 바꿔두고 user의 레포지토리에서 복사해서 처리하는 과정을 가져주어야 한다.

쉽게 보자면, @hot-updater/firebase라는 하나의 레포지토리만 배포를 하고, 그 안에 레포지토리가 아닌척 숨겨놓는 것이다


고난

하지만 나는 이미 bundling 되지 않아 사이즈가 크고, user에서 사용하려면 의존성 설치를 해야하는 상황이 최적화가 되지 않았다고 느꼈다.

최대한 functions의 serverless 서버에서 사용되야만 하는 library인 firebase-functions을 제외한 나머지는

tsup라이브러리를 통해 index.cjs로 번들링한 것을 functions에 올려주고 싶었다.

firebase/dist/firebase/index.cjs가 build를 통해 bundling된 firebase/functions/index.ts이다.

처음엔 이를 functions에 build해서 의존성을 설치 당할때 같이 넘겨주려고 했지만, 번들링을 하고 보니 18만줄이었기 때문에 biome의 CI에서 failed되었다..

결국 dist에 있는 번들링된 저 index.cjs를 index.ts와 바꿔주는 작업을 user 측면에서 진행해주어야만 했다..!


packge.json

hot-updater는 터미널을 통해 init을 제공하는데, 이때 필요한 의존성이나 DB, storage, serverless 등을 설정을 해준다.

yarn berry같은 경우에는 node_modules가 없기 때문에, node_modules에서 작업하는 것이 아닌, 누구나에게나 똑같은 경로를 지정해서 해야만 한다.

이건 @hot-updater/firebase의 내부인데 package.json인데, exports 부분이 보일 것이다.


exports

오늘의 핵심이다.

exports는 본래 ESM과 CJS을 둘 다 사용할 수 있게끔 해주려고 도입되었다.

    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    },

이 부분은 만약 @hot-updater/firebase를 import를 한다면 dist/index.js를 바라보고, require한다면 index.cjs를 바라보라는 뜻이다.

어디서든 import를 해도 설치되어있고, 모든 로직이 존재하는 index.js를 import하거나, index.cjs를 require할 수 있을 것이다.

    "./functions": {
      "require": "./dist/firebase/index.cjs"
    }

그렇다면 이 녀석은 @hot-updater/firebase/functions를 require한다면, 저 경로에 있는 녀석을 바라보라는 뜻이다.

이 녀석들을 경로로도 쓸 수 있는데, 나는 이를 이렇게 활용했다.

//모듈이 설치된 경로
  const indexFile = require.resolve("@hot-updater/firebase/functions");

  const destPath = path.join(functionsDir, path.basename(indexFile));
  await fs.copyFile(indexFile, destPath);

실제로 내가 user side에서 옮기고자 했던 bundling된 functions/index.ts는 indexFile이며, 이는 누구에게나 동일하게 적용이 될 것이다.

이를 통해, 원하는 경로에 dist에 있는 user의 파일을 복사하는데 성공하였다!


결론

이 외에도 name이나 main등 중요성이 많이 떨어진다고 생각했던 package.json의 property들이 정말 많은 필요로 하고 실제로 모르면 많이 고생을 할 수 있겠다고 생각한다.

결국 18만줄의 번들링된 js코드를 직접 넘기는게 아닌 사용자의 dist폴더에서 빼왔기 때문에, .gitignore에 추가하지 않고, workspace로 따로 관리하지 않고, 상대 경로도 원활하게 사용할 수 있었다!

2개의 댓글

comment-user-thumbnail
2025년 3월 8일

대단해요! 멋집니다!

1개의 답글