핀테크그룹 프론트엔드가 pnpm에서 Bun(패키지 매니저)으로 전환한 뒤 Nx 18.3.3에서 21.4+로 올린 기록이다. 문서상 Nx 19.5부터 Bun 공식 지원이지만 안정적으로 쓰려면 21.4+가 필요했다는, 공식 지원과 프로덕션 준비의 간극을 다룬다.
핵심 포인트- Nx 18.x는 Bun을 감지하지 않아 bun.lockb가 있어도 yarn/npm으로 fallback되며, Nx 소스 코드를 직접 읽고서야 감지 우선순위를 파악했다.
- Nx 19 + @nx/webpack + generatePackageJson:true에서 Cannot read properties of undefined 오류가 났고 원인은 바이너리 bun.lockb의 External Node 메타데이터 해석 누락이었다.
- 우회보다 Nx 21.4 + Bun 1.2로 올려 bun.lockb를 텍스트 bun.lock으로 전환하는 게 근본 해결이었다.
- 텍스트 lock 전환으로 PR diff·Merge·Dependabot 파싱이 가능해지고 내부 최적화로 약 30% 성능이 개선됐다.
- 결과는 빌드 약 60% 단축, CI 파이프라인 -14%, Artifact -15%다.
상세 정리- 배경: 2024년 3월 pnpm→Bun 전환(런타임은 Node 유지), Bun 벤치마크는 pnpm 대비 17배·npm 대비 29배 설치 속도를 주장했고, 실측 nx build web이 pnpm 1분46초 vs Bun 42초로 약 60% 단축이었다(M2 Pro 32GB, cold 10회 평균).
- 문제 1: nx migrate가 yarn으로 설치를 시도했으나 yarn 미설치로 실패, Nx 18.x 감지 우선순위는 yarn.lock→pnpm-lock→package-lock이라 bun.lockb는 대상 밖이었다.
- 해결 1: bunfig.toml의 print="yarn"로 생성되던 fallback yarn.lock을 마이그레이션 동안만 제거해 npm fallback을 유도, 19.5+에서 Bun 공식 인식을 획득했다.
- 문제 2: Nx 19 + Bun 1.0.33 webpack 빌드에서 HookWebpackError, 원인은 빌드 산출물 package.json 생성 시 프로젝트 그래프의 External Node 메타데이터를 읽는데 바이너리 lockb에서 해석이 누락된 것이었다.
- 우회 검토: generatePackageJson:false는 배포 시 별도 스크립트가 필요하고 커스텀 webpack 플러그인은 유지보수 부담이라 둘 다 기각했다.
- 의사결정: Storybook 9 마이그레이션이 Nx 21.2+를 요구해 Bun과 Nx를 함께 올리면 자연히 해소되는 이슈로 판단했다.
- 최종: Bun 1.2.x로 올려 bun.lockb를 JSONC 텍스트 bun.lock으로 전환하고 Nx 21.4+(bun.lock 파싱 개선)로 올렸다.
- lockb→lock 이유: 바이너리는 PR 리뷰 불가·Merge 충돌 수동 해결 불가·Nx/Dependabot 파싱 불가였다.
- 결과(GitLab Runner 5회 평균): CI 13분30초→11분40초(-14%), Artifact 330MB→280MB(-15%), bun.lockb+yarn.lock 이중 관리를 bun.lock 단일로 통합했다.
- 교훈: 막히면 오픈소스 코드를 직접 확인하고, 안정화 시점이 오면 메이저 업그레이드를 미루지 말며, 공식 미지원 조합은 로드맵·이슈·lock 호환성·롤백 플랜을 사전 확인한다.
왜 읽나Nx 모노레포에서 Bun을 쓰거나 lock file 호환성·빌드로 막힌 프론트엔드 개발자에게 버전별 함정과 해결 순서 레퍼런스.