만족
[Nodejs] 패키지 관리 도구: pnpm vs npm vs Yarn 본문
[Nodejs] 패키지 관리 도구: pnpm vs npm vs Yarn
Nodejs Satisfaction 2025. 8. 20. 13:17
오늘날 프론트엔드와 백엔드를 막론하고 Node.js 생태계는 빠르게 성장하고 있으며, 그 중심에는 수많은 패키지와 의존성이 있다. 이 의존성들을 효율적으로 관리하는 것은 프로젝트의 성능과 안정성을 좌우하는 중요한 요소이다. npm, Yarn에 이어 새롭게 떠오르는 pnpm에 대해 자세히 알아보면서, 각 도구가 어떻게 패키지를 관리하고 디스크 공간을 사용하는지 함께 파헤쳐 본다.
npm과 Yarn, 그리고 그들의 node_modules 이야기
Node.js 생태계에서 가장 널리 사용되는 패키지 관리 도구는 역시 npm과 Yarn이다. 두 도구 모두 package.json 파일을 기반으로 프로젝트 의존성을 설치하고 관리하며, 설치된 패키지들은 대부분 프로젝트 루트의 node_modules 디렉토리에 저장된다.
1. npm과 node_modules의 진화: 호이스팅(Hoisting)
npm은 초기 버전에서 의존성을 중첩 구조로 설치했다. 예를 들어, A 패키지가 B 패키지에 의존하고 B 패키지가 C 패키지에 의존한다면, node_modules/A/node_modules/B/node_modules/C와 같은 깊은 구조가 만들어졌다. 이는 디스크 공간을 엄청나게 차지하고 모듈 해석에 비효율적인 문제가 있었다.
이러한 문제를 해결하기 위해 npm v3부터는 호이스팅(Hoisting)이라는 개념을 도입했다. 호이스팅은 의존성 트리를 가능한 한 평평하게 만들어, 여러 패키지가 공통으로 의존하는 패키지들을 node_modules 최상위 레벨로 끌어올리는 방식이다.
장점
- 디스크 공간 절약: 중복 패키지 설치를 줄여 디스크 공간을 아낄 수 있다.
- 모듈 로딩 효율화: node_modules 경로를 단순화하여 모듈 해석 시간을 단축한다.
단점
- 유령 의존성(Phantom Dependencies): package.json에 명시적으로 선언하지 않은 패키지라도, 호이스팅으로 인해 node_modules 최상위에 존재한다면 코드에서 해당 패키지를 사용할 수 있게 된다. 이는 프로젝트의 실제 의존성을 파악하기 어렵게 만들고, 예상치 못한 버그를 유발할 수 있다.
- 버전 충돌: 서로 다른 버전이 필요할 때 문제 발생 소지가 있다.
2. Yarn의 등장과 캐싱 시스템
Yarn은 npm의 초기 버전이 가지고 있던 성능과 보안 문제를 개선하기 위해 페이스북에서 개발되었다. Yarn 역시 npm과 유사한 평면화된 node_modules 구조와 호이스팅 개념을 사용하지만, 다음과 같은 특징으로 효율성을 높였다.
장점
- 빠른 설치 속도: npm보다 빠른 설치 속도를 제공한다.
- 정확한 버전 관리: yarn.lock 파일을 통해 모든 개발 환경에서 동일한 의존성을 보장한다.
- 오프라인 설치: 패키지 전역 캐시를 통해 한 번 다운로드한 패키지는 오프라인에서도 재설치가 가능하다.
하지만 Yarn 역시 기본적으로 npm과 유사하게 각 프로젝트마다 node_modules 디렉토리를 생성하고 패키지 복사본을 저장하는 방식이어서, 대규모 프로젝트나 모노레포 환경에서는 여전히 디스크 공간 효율성에 아쉬움이 있었다.
pnpm: 패키지 관리의 새로운 대안
pnpm은 이러한 npm과 Yarn의 한계, 특히 디스크 공간 효율성과 유령 의존성 문제를 해결하기 위해 등장했다. pnpm은 이름 그대로 Performance NPM을 목표로 하며, 다음과 같은 독특한 방식으로 이 문제를 해결한다.
1. 콘텐츠 주소 지정 저장소와 심볼릭 링크
pnpm의 핵심은 콘텐츠 주소 지정 저장소(Content-addressable store)와 심볼릭 링크(Symbolic Link)이다.
- pnpm은 모든 패키지를 전역적인 한 개의 저장소(일반적으로 ~/.pnpm-store)에 한 번만 설치한다. 이 저장소는 각 패키지 파일의 해시 값을 기반으로 경로가 결정되므로, 동일한 파일은 디스크에 한 번만 저장된다.
- 프로젝트의 node_modules에는 실제 패키지 파일 대신, 이 전역 저장소에 있는 패키지를 가리키는 심볼릭 링크만 생성된다.
이 방식은 아래와 같은 다단계 node_modules 구조를 만든다:
my-project/node_modules/
├── .pnpm/ <- pnpm이 관리하는 가상의 '호이스팅' 공간
│ ├── express@4.17.1/
│ │ └── node_modules/ <- 실제 패키지 파일을 가리키는 심볼릭 링크
│ │ ├── express -> ~/.pnpm-store/express/4.17.1/...
│ │ └── body-parser -> ~/.pnpm-store/body-parser/...
│ └── lodash@4.17.21/
│ └── node_modules/
│ └── lodash -> ~/.pnpm-store/lodash/4.17.21/...
├── express -> ./.pnpm/express@4.17.1/node_modules/express <- 프로젝트에서 사용하는 'express' 링크
└── lodash -> ./.pnpm/lodash@4.17.21/node_modules/lodash <- 프로젝트에서 사용하는 'lodash' 링크
2. 엄격한 의존성 관리 및 유령 의존성 방지
pnpm은 프로젝트의 package.json에 명시적으로 선언된 의존성만 최상위 node_modules에 심볼릭 링크로 연결한다. 즉, 내가 사용하겠다고 명시한 패키지만 직접 접근할 수 있다.
pnpm의 핵심 장점
- 디스크 공간 초절약: 전역 저장소에 한 번만 설치되므로, 동일한 패키지가 수십 개의 프로젝트에서 사용되더라도 디스크에는 단 한 번만 저장된다. 모노레포 환경에서 특히 빛을 발한다.
- 빠른 설치 속도: 패키지를 다시 다운로드할 필요가 없으므로 설치 및 업데이트 속도가 매우 빠르다.
- 유령 의존성 제거: package.json에 명시된 패키지만 사용할 수 있게 하여, 예상치 못한 의존성 문제(버그)를 근본적으로 방지한다.
- 안정적인 node_modules 구조: 예측 가능한 의존성 트리를 통해 빌드 환경의 안정성을 높인다.
왜 pnpm을 사용해야 할까?
- 대규모 프로젝트 및 모노레포 환경: 수많은 하위 프로젝트가 같은 의존성을 공유하는 환경에서 디스크 공간과 설치 시간 면에서 압도적인 효율을 보여준다.
- 빠른 CI/CD: 패키지 설치 시간이 곧 CI/CD 파이프라인의 속도에 직접적인 영향을 미치므로, pnpm의 빠른 속도는 개발 및 배포 워크플로우를 가속화한다.
- 의존성 관리에 대한 엄격함 추구: '유령 의존성' 같은 잠재적 문제를 사전에 차단하여 코드 베이스의 건전성을 높이고 싶을 때 최적의 선택이다.
결론
npm과 Yarn은 오랜 시간 동안 Node.js 생태계를 지탱해 온 훌륭한 도구이지만, pnpm은 패키지 관리의 새로운 표준을 제시하며 기존 도구들이 가지고 있던 아쉬움을 효과적으로 해소하고 있다.
개발 효율성과 프로젝트의 안정성을 중요하게 생각한다면, 다음 프로젝트에서는 pnpm을 꼭 한번 경험해 보기를 강력히 추천한다.
'Nodejs' 카테고리의 다른 글
| [Nodejs] file의 birthtime과 mtime (0) | 2024.10.11 |
|---|---|
| [Nodejs] 더 작고 빠른 패키지 매니징을 위한 yarn berry (0) | 2023.08.12 |
| [Nodejs] pm2로 nodejs 데몬 프로세스 관리하기 (0) | 2023.03.13 |
| [Nodejs] 커맨드 동시 실행으로 커밋 검사 속도 향상 (0) | 2023.01.22 |
| [Nodejs] husky 를 이용한 Githook 추가 (0) | 2022.05.23 |