패키지 매니저부터 모노레포까지 (02) - 패키지 매니저는 왜 npm 하나로 끝나지 않았나
npm이 기본 축인데도 Yarn과 pnpm 같은 도구가 계속 등장한 이유를 속도, lockfile, 설치 모델 문제를 중심으로 정리합니다.
용어 보기접기/펼치기
npm이 기본 축이라는 사실과, npm 하나로 모든 문제가 끝나지 않았다는 사실은 같이 봐야 합니다. 다른 패키지 매니저는 취향이 아니라 불만과 한계에서 나왔습니다.
들어가며
앞 글에서 npm이 단순한 설치 명령어가 아니라, registry와 package.json, lockfile, install 흐름을 함께 묶는 기본 축이라는 점을 정리했습니다. 그런데 여기서 바로 한 가지 질문이 이어집니다. 기본 축이 이미 있었다면, 왜 사람들은 다른 패키지 매니저를 만들었을까요.
이 질문은 생각보다 중요합니다. 도구가 하나 더 생겼다는 사실보다, 사람들이 어떤 불편을 오래 겪었는지를 더 잘 보여주기 때문입니다. 새로운 패키지 매니저는 보통 "더 멋진 명령어"를 만들기 위해 나오지 않습니다. 이미 널리 쓰이던 기본값으로 해결되지 않는 문제를 밀어붙이는 과정에서 등장합니다.
비유를 쓰면 조금 더 이해하기 쉽습니다. 표준 열차 노선이 하나 있다고 해서 모든 승객이 만족하는 것은 아닙니다. 누군가는 더 빠른 노선을 원하고, 누군가는 환승이 덜 꼬이는 구조를 원하고, 누군가는 짐을 더 안정적으로 옮길 방법을 원합니다. 패키지 매니저도 비슷합니다. npm이라는 기본 노선은 있었지만, 규모가 커질수록 다른 요구가 생겼습니다.
이번 글은 "누가 더 낫다"를 고르는 글이 아닙니다. 왜 npm 하나로 끝나지 않았는지, 그리고 Yarn과 pnpm이 각각 어떤 문제를 강하게 의식했는지를 먼저 정리하는 글에 가깝습니다.
초창기 npm에서 무엇이 불편했는가
npm이 늘 나빴다는 뜻은 아닙니다. 다만 생태계가 커지던 시기에는, 특히 팀 개발과 대형 프로젝트 관점에서 불편이 분명히 쌓였습니다. 이 불편이 바로 다른 패키지 매니저가 등장한 배경이 됩니다.
1. 설치 속도와 체감 성능에 대한 불만이 컸습니다
패키지가 적을 때는 큰 문제가 아닙니다. 하지만 프로젝트가 커지고 의존성이 많아질수록 설치 시간은 팀 전체의 체감 비용이 됩니다. 새로 프로젝트를 내려받을 때, CI를 돌릴 때, 캐시가 깨졌을 때마다 기다려야 하는 시간이 길어집니다.
이 불편은 단순히 "조금 느리다" 수준이 아니었습니다. 패키지 매니저는 하루에도 여러 번 반복해서 손이 가는 도구이기 때문에, 설치 속도는 금방 개발 경험 전체를 좌우하는 문제로 커집니다.
2. 재현 가능한 설치 결과가 더 중요해졌습니다
팀 개발에서는 "내 컴퓨터에서는 됐다"가 큰 의미가 없습니다. 같은 package.json을 가지고도 누군가의 환경에서는 다른 버전이 잡히거나, CI에서만 결과가 달라지면 바로 운영 리스크가 됩니다.
지금은 lockfile이 너무 당연해서 감각이 무뎌졌지만, 일관된 설치 결과를 안정적으로 재현하는 일은 패키지 매니저에서 매우 중요한 축입니다. Yarn Classic이 초기에 강하게 주목받은 이유 중 하나도 이 지점에 있었습니다. 공식 문서도 yarn.lock이 여러 머신에서 일관된 설치를 위해 더 많은 정보를 저장한다고 설명합니다.
3. 네트워크에 덜 흔들리는 설치가 필요했습니다
패키지 설치는 늘 인터넷 상태와 얽혀 있었습니다. 회사 네트워크가 불안정하거나, CI 환경이 제한적이거나, 같은 패키지를 여러 번 반복해서 설치해야 하는 상황에서는 "그때그때 다시 받아오는 구조"가 답답하게 느껴질 수 있습니다.
Yarn Classic이 오프라인 미러를 강조했던 배경도 여기에 있습니다. 공식 문서와 블로그는 네트워크에 덜 의존하는 반복 가능하고 신뢰 가능한 빌드를 주요 문제로 다룹니다. 즉 단순히 빠르기보다, 설치를 더 믿을 수 있게 만들려는 문제의식이 있었습니다.
4. 설치 구조가 예측하기 어려운 순간이 있었습니다
패키지 매니저를 쓰다 보면 "왜 이 import가 되지?" 같은 순간을 겪게 됩니다. 분명 내 package.json에 적지 않은 패키지인데도 로컬에서는 동작하는 경우가 있고, 반대로 환경이 조금만 바뀌면 갑자기 깨지는 경우도 있습니다.
이 문제는 훗날 유령 의존성이라고 더 자주 설명되지만, 지금 단계에서는 이렇게 이해해도 충분합니다. 설치 구조가 너무 느슨하면, 개발자는 실제로 어떤 의존성을 직접 선언했고 어떤 패키지가 우연히 보이는 것인지 구분하기 어려워집니다.
즉 다른 패키지 매니저는 "npm이 싫어서"가 아니라, 속도, 재현성, 네트워크 의존성, 설치 구조의 예측 가능성 같은 문제를 더 세게 해결하려는 흐름에서 나왔습니다.
Yarn이 처음 주목받은 이유
Yarn Classic이 처음 강하게 주목받은 이유는 꽤 실용적입니다. 이미 사람들이 겪고 있던 불편을 빠르게 흡수했기 때문입니다.
공식 Getting Started 문서도 Yarn을 빠르고, 안정적이고, 믿을 수 있게 패키지를 다루는 도구로 설명합니다. 즉 처음부터 "새 철학"보다는 "지금 불편한 경험을 더 낫게 만들겠다"는 인상이 강했습니다.
특히 당시 Yarn Classic은 아래와 같은 이유로 매력적으로 보였습니다.
- lockfile을 전면에 내세워 일관된 설치 결과를 강조했습니다
- 오프라인 미러처럼 네트워크 의존성을 줄이는 기능을 밀었습니다
- npm에서 넘어오더라도
package.json을 그대로 활용할 수 있었습니다
이 점이 중요합니다. Yarn은 완전히 다른 생태계를 만들겠다고 출발한 것이 아니라, npm 생태계와 호환되면서도 실무에서 답답했던 지점을 빠르게 개선하는 쪽으로 움직였습니다.
그래서 많은 팀에게 Yarn Classic은 "새로운 세계로 넘어가는 도구"보다 "지금 겪는 불편을 줄여 주는 대안"으로 보였습니다. 설치가 더 예측 가능해 보이고, lockfile 경험이 더 안정적으로 느껴지고, 대형 프로젝트에서도 팀이 함께 쓰기 좋다는 인상이 강했습니다.
그래서 이 시기의 Yarn은 거대한 철학 경쟁이라기보다, 사용자의 불만을 더 민감하게 받아들이는 패키지 매니저에 가까웠습니다.
pnpm이 다른 해법을 택한 이유
pnpm은 Yarn과 같은 문제를 보면서도 해법은 조금 다르게 택했습니다. 핵심은 기존 Node 생태계의 관성을 완전히 버리지는 않되, 구조는 더 효율적이고 엄격하게 만들겠다는 쪽에 가깝습니다.
pnpm 공식 사이트는 스스로를 빠르고 디스크 효율적인 패키지 매니저라고 소개합니다. 여기에는 두 가지 메시지가 함께 들어 있습니다.
- 설치 속도를 개선하고 싶다
- 같은 패키지를 여러 프로젝트에서 중복 저장하는 낭비를 줄이고 싶다
이 문제는 규모가 커질수록 더 크게 느껴집니다. 회사 노트북에 여러 프로젝트를 두고 일하다 보면, 거의 같은 의존성을 여러 번 깔고 있다는 감각이 생깁니다. pnpm은 이 지점을 더 직접적으로 건드립니다.
동시에 pnpm은 구조적 엄격성도 강조합니다. 겉으로는 node_modules를 유지하지만, 내부 구성 방식은 npm이나 초창기 Yarn보다 더 강하게 경계를 의식합니다. 그래서 어떤 패키지가 정말로 선언된 의존성인지, 무엇이 우연히 보이는 것인지가 더 빨리 드러나는 편입니다.
즉 pnpm은 "기존과 너무 멀어지지 않으면서도, 설치 구조를 더 낭비 없이 더 엄격하게 만들 수 없을까"라는 질문에서 나온 해법에 가깝습니다.
이 차이 때문에 Yarn과 pnpm은 같은 대체재처럼 보이면서도 결이 다릅니다. Yarn Classic이 초기 실무 불편을 빠르게 흡수한 도구였다면, pnpm은 호환성은 유지하면서 구조를 다시 설계하려는 쪽에 더 가깝습니다.
핵심은 취향이 아니라 문제의식이다
패키지 매니저를 이야기할 때 종종 "npm파냐 Yarn파냐 pnpm파냐"처럼 취향 대결로 흘러가곤 합니다. 하지만 실제로는 그보다 문제의식이 먼저입니다.
조금 단순화해서 정리하면 이렇게 볼 수 있습니다.
- npm은 생태계의 기본 축을 넓게 담당합니다
- Yarn Classic은 초창기 npm 사용 경험의 불편을 더 빠르게 흡수했습니다
- pnpm은 디스크 효율과 설치 구조의 엄격성을 더 강하게 밀었습니다
결국 차이는 세련된 명령어 취향이 아니라, 무엇을 먼저 해결하려 했는가에 있습니다.
이 관점이 중요한 이유는 다음 글로 자연스럽게 이어지기 때문입니다. 같은 문제를 본다고 해도, 각 도구는 결국 설치 모델을 다르게 선택합니다. 누구는 node_modules를 전제로 개선하고, 누구는 lockfile 경험을 밀고, 누구는 구조 자체를 더 엄격하게 다룹니다. 그래서 다음 단계에서는 "왜 서로 다른 길로 갔는가"를 비교하게 됩니다.
다음 글 예고
이번 글에서는 왜 대체재가 생겼는지를 먼저 봤습니다. 기본 축은 있었지만, 속도와 재현성, 네트워크 의존성, 설치 구조의 예측 가능성 같은 문제는 계속 남아 있었습니다. 그리고 각 도구는 이 문제를 서로 다른 방식으로 풀기 시작했습니다.
다음 글에서는 여기서 한 걸음 더 나가겠습니다. npm, Yarn, pnpm은 왜 결국 서로 다른 설치 철학으로 갈라졌을까요. 다음 편에서는 그 차이를 본격적으로 비교해 보겠습니다.
참고
- npm install:
npm install이 lockfile과 어떤 우선순위로 동작하는지 설명합니다. - package-lock.json: npm lockfile이 왜 필요한지와 어떤 역할을 하는지 설명합니다.
- Yarn Getting Started: Yarn Classic이 자신을 어떤 문제의식으로 소개했는지 볼 수 있습니다.
- yarn.lock: Yarn Classic이 일관된 설치 결과를 위해 lockfile을 어떻게 설명하는지 확인할 수 있습니다.
- Configuring an Offline Mirror: Yarn Classic이 오프라인 미러를 왜 강조했는지 보여줍니다.
- pnpm: pnpm이 속도, 디스크 효율, workspace를 어떤 강점으로 내세우는지 확인할 수 있습니다.