"npm이면 됐지"라고 생각했던 내가 pnpm에 정착하기까지

"npm이면 됐지"라고 생각했던 내가 pnpm에 정착하기까지

2026년 2월 15일

프로젝트마다 npm install, yarn add, pnpm i

어느 순간부터 package manager를 세 개씩 알고 있는 나를 발견했다. 언제부터, 왜 이렇게 떠돌아다녔을까.

1. 문제 상황

JavaScript 생태계에는 package manager가 유독 많다. npm, yarn, yarn berry, pnpm — 각자 이름도 다르고 쓰는 명령어도 조금씩 다르다. 처음에는 그냥 있는 것들이었는데, 쓰다 보니 왜 이렇게 많은지가 궁금해졌다.

각각의 등장 배경을 정리해보면 이렇다.

npm (2010)

Node.js와 함께 등장한 최초의 JavaScript package manager다. 처음에는 획기적이었지만, 시간이 지나면서 문제가 드러났다.

  • 설치 속도가 느렸다. 패키지를 순차적으로 하나씩 받았다.
  • 같은 package.json인데 다른 환경에서 설치하면 node_modules 구조가 달라질 수 있었다. lockfile이 없었기 때문이다.
  • node_modules가 깊고 중첩된 구조를 가져 디스크를 많이 차지했다.

yarn (2016)

Facebook(현 Meta)이 npm의 문제를 해결하기 위해 만들었다. 핵심은 두 가지였다.

  • yarn.lock 도입: 같은 파일이면 어느 환경에서든 동일한 패키지가 설치된다.
  • 병렬 다운로드와 오프라인 캐시: 눈에 띄게 빨라졌다.

당시 npm이 느리다는 불만이 많았고, yarn은 그 대안으로 빠르게 퍼졌다.

yarn berry (2020)

yarn의 2버전이다. 더 급진적인 방향을 택했다.

  • Plug’n’Play(PnP): node_modules 폴더 자체를 없애버렸다. 의존성을 zip으로 압축해 .yarn/cache에 두고, Node.js가 직접 그걸 참조하게 한다.
  • Zero-installs: lockfile과 캐시를 git에 커밋하면 yarn install 없이도 바로 실행할 수 있다.

아이디어는 흥미로웠지만, 기존 생태계와 호환성 문제가 많았다. IDE 설정도 별도로 건드려야 했고, node_modules가 없으니 일부 라이브러리가 동작하지 않는 경우도 있었다.

pnpm (2017~)

npm과 yarn이 가진 node_modules 비효율 문제를 다른 방식으로 해결했다.

  • Content-addressable store: 패키지를 전역 저장소에 한 번만 저장하고, 프로젝트마다 하드링크로 연결한다. 같은 패키지를 여러 프로젝트에서 써도 디스크를 한 번만 차지한다.
  • 유령 의존성(phantom dependencies) 차단: npm과 yarn은 node_modules 구조상 명시하지 않은 패키지에도 접근할 수 있다. pnpm은 이를 막는다.
  • npm과 유사한 CLI: 명령어 구조가 거의 같아 진입장벽이 낮다.

2. 내가 처음 한 생각

개발을 처음 배울 때는 그냥 npm install이면 됐다.

강의에서 시키는 대로 따라했고, node_modules가 생기고 실행이 됐다. 왜 느린지, 왜 node_modules가 이렇게 큰지 같은 질문은 하지 않았다. 그냥 동작하면 됐으니까.

“npm이면 됐지, 굳이 다른 걸 써야 하나?”라는 생각이었다.

3. 실제로 해 본 선택

npm → yarn

주변 개발자들이 yarn을 쓴다고 했다. “npm보다 빠르다”는 말에 한 번 써봤는데, 실제로 속도 차이가 느껴졌다. 그 이후로 새 프로젝트에서는 yarn을 쓰기 시작했다.

사실 yarn을 선택한 이유가 명확한 기술적 판단이었다기보다는, 빠르다는 느낌이었다. lockfile이 뭔지, 왜 중요한지는 나중에 알게 됐다.

yarn → yarn berry 시도

yarn berry가 나왔을 때 한 번 써봤다. Zero-installs라는 개념이 매력적으로 느껴졌고, node_modules를 없앤다는 발상도 흥미로웠다.

그런데 막상 쓰려니 손댈 게 너무 많았다. VSCode에서 TypeScript를 인식하려면 SDK를 따로 설치해야 했고, 익숙한 node_modules 구조가 없으니 디버깅할 때 감이 오지 않았다. 며칠 쓰다가 결국 다시 돌아갔다.

낯선 것이 나쁜 건 아닌데, 이 낯섦이 생산성을 깎는다는 느낌이 들었다.

pnpm 정착

yarn berry를 포기하고 대안을 찾다가 pnpm을 알게 됐다. 처음엔 반신반의했다.

그런데 써보니 달랐다. 명령어는 npm/yarn이랑 거의 같아서 배울 게 없었고, node_modules도 그대로 있었다. 그러면서도 yarn berry가 약속했던 것들 — 빠른 설치, 디스크 절약, 유령 의존성 방지 — 을 실용적인 방식으로 제공하고 있었다.

4. 결과

pnpm을 쓰면서 실제로 달라진 것들이 있다.

디스크 공간

여러 프로젝트를 동시에 굴리다 보면 node_modules가 금방 수십 GB가 된다. pnpm의 전역 저장소는 같은 버전의 패키지를 한 번만 저장한다. 실제로 디스크 여유가 눈에 띄게 생겼다.

유령 의존성 없음

package.json에 명시하지 않은 패키지는 쓸 수 없다. 처음엔 불편하게 느껴졌는데, 이게 오히려 의존성을 명확하게 관리하는 습관을 만들어줬다.

익숙한 CLI

pnpm install, pnpm add, pnpm run dev — yarn에서 넘어왔는데 거의 그대로였다. 러닝커브가 사실상 없었다.

5. 지금 다시 해본다면?

처음부터 pnpm을 썼을 것 같다.

그런데 이렇게 옮겨다닌 경험이 있으니, “왜 pnpm인가”를 설명할 수 있다. npm이 왜 느렸는지, yarn이 왜 나왔는지, yarn berry가 왜 실용적이지 않았는지를 몸으로 겪었기 때문이다.

도구를 바꾸는 데 명확한 이유가 있을 때와 “그냥 남들이 쓰니까”일 때의 차이를 이제는 구분한다. pnpm은 전자였다.

package manager를 새로 도입할 때, 다음엔 이 순서로 먼저 생각해볼 것 같다.

  1. 현재 쓰는 도구의 어떤 점이 불편한가?
  2. 대안이 그 문제를 어떻게 해결하는가?
  3. 전환 비용이 얻는 이점보다 작은가?

npm이면 됐지라고 생각하던 나는 틀리지 않았다. 그때는 그게 맞는 선택이었다. 다만 지금은 더 나은 도구가 있고, 그 이유를 알고 쓰는 게 다르다.