홈으로 돌아가기

npm 의존성 비대화: 3가지 원인과 해결책

이 기사는 npm 의존성 성장의 세 가지 이유를 분해합니다: legacy environment support, 원자 아키텍처, outdated ponyfills. 중복 문제, 공급망 위험, 정리 권장사항을 설명합니다. 중/시니어 개발자를 위한 자료입니다.

JS npm 의존성 '비대화'의 세 가지 이유
Advertisement 728x90

npm 의존성 트리가 부풀어 오르는 세 가지 주요 이유

JavaScript 프로젝트의 의존성 트리는 종종 오래된 패키지와 중복된 플랫폼 기능 때문에 비대해집니다. 이로 인해 불필요한 다운로드 비용, 버전 충돌, 공급망 취약점이 발생합니다. 이 상황을 설명하는 세 가지 전형적인 시나리오와 해결 방법을 살펴보겠습니다.

레거시 환경 지원 및 영역별 문제

is-string이나 hasown 같은 많은 패키지는 ES3(IE6/7, 초기 Node.js) 같은 오래된 런타임과의 호환성을 위해 존재합니다. 이들은 누락된 메서드(Array.prototype.forEach, Array.prototype.reduce, Object.keys, Object.defineProperty)를 구현합니다.

추가적인 이유:

Google AdInline article slot
  • 전역 네임스페이스 보호: Node는 오버라이드로 인한 오류를 방지하기 위해 프리미티브(예: Map)를 임포트합니다. math-intrinsics 같은 패키지는 비슷한 목적으로 Math.*를 재내보냅니다.
  • 영역 호환성: instanceof는 iframe과 부모 간에 작동하지 않습니다(다른 RegExp 생성자). 대안으로 Object.prototype.toString.call(val) === '[object RegExp]'를 사용하며, chaiis-string에서 new String(val)에 활용됩니다.
const shebangRegex = /^#!(.*)/;
export default shebangRegex;

대부분의 중급 이상 개발자에게 이는 과도한 작업입니다: 최신 Node(10년 이내)와 브라우저는 이런 해킹이 필요하지 않습니다. 권장사항: browserslist를 확인하고 불필요한 폴리필을 제거하세요.

원자적 아키텍처: 이론에서 문제까지

'원자적' 패키지 철학은 코드를 재사용 가능한 최소 블록으로 분해하는 것을 포함합니다. 예를 들어 execa의 의존성 그래프는 shebang-regex(5줄 코드) 같은 패키지를 포함합니다.

전형적인 원자적 유틸리티:

Google AdInline article slot
  • arrify: Array.isArray(val) ? val : [val]
  • slash: 경로 슬래시 정규화
  • cli-boxes: UI 요소 테두리를 위한 JSON
  • path-key: env['PATH'] || env['Path']
  • onetime: 일회성 함수 호출
  • is-wsl: process.platform === 'linux' && /microsoft/.test(os.release())
  • is-windows: process.platform === 'win32

이론적으로 이는 CLI 구축을 단순화합니다. 실제로는:

  • 일회성 사용: shebang-regexshebang-command(동일 저자)에서만 사용됩니다; cli-boxesboxen/ink에서만 사용됩니다.
  • 호이스팅 중복: nuxt에서 is-docker, is-stream, is-wsl(각 2개 버전) 같은 중복이 발생합니다. 인라인 코드는 npm 오버헤드 없이 무료로 중복됩니다.
  • 공급망 위험: 더 많은 패키지는 더 많은 취약점을 의미합니다. 한 유지 관리자가 손상되면(작년에 발생한 사례) 수백 개의 의존성이 영향을 받습니다.

해결책: 간단한 함수를 인라인화하고, 트리 셰이킹을 사용하며, 엄격한 호이스팅이 있는 pnpm을 활용하세요.

시대를 초월한 포니필

포니필은 전역을 변형하지 않고 직접 임포트하는 폴리필입니다. 라이브러리에 유용합니다: @fastly/performance-observer-polyfill은 환경을 변경하지 않고 PerformanceObserver를 사용할 수 있게 합니다.

Google AdInline article slot

문제: 네이티브 지원이 도래한 후 제거되지 않습니다.

오래된 포니필 예시(주간 다운로드 포함):

  • globalthisglobalThis용(2019년부터 지원, 주간 49M 다운로드)
  • indexofArray.prototype.indexOf용(2010년부터 지원, 주간 2.3M 다운로드)
  • object.entriesObject.entries용(2017년부터 지원, 주간 35M 다운로드)

eslint-plugin-react에서 그래프의 절반은 레거시 폴리필로 구성됩니다. 라이브러리의 경우: 소비자 측 폴리필(core-js)에 의존하세요. 추가 전에 caniuse.com을 확인하세요.

핵심 요약

  • 레거시 최적화: 90%의 패키지는 ES3/ES5 폴리필이 필요하지 않습니다; is-string 대신 typeof를 사용하세요.
  • 원자성의 해로움: 5줄 함수를 인라인화하여 중복과 공격 표면을 줄이세요.
  • 포니필 감사: 대상 런타임에서 5년 이상 지원된 기능을 위한 패키지를 제거하세요.
  • 도구: 분석을 위해 npm ls --depth=0, depcheck, npm-check-updates를 사용하세요.
  • e18e 이니셔티브: 성능을 위해 오래된 의존성 정리에 집중하세요.

실용적인 정리 단계

  • npm dedupenpm prune을 실행하세요.
  • 강제 호이스팅을 위해 package.jsonoverrides를 추가하세요.
  • 트리 셰이킹이 있는 esbuild/vite를 사용하세요.
  • webpack-bundle-analyzer로 번들 크기를 모니터링하세요.

이렇게 하면 기능을 잃지 않고 번들 크기를 20-50% 줄일 수 있습니다.

— Editorial Team

Advertisement 728x90

다음 읽기