그룹 프로젝트를 시작하면서 팀원들과 함께 패키지 매니저 선택에 대한 고민을 하게 되었다. 기존에 많이 사용되어 온 npm
의 문제점을 개선한 다양한 모던 패키지 매니저가 등장했는데, 그 중 특히 주목받고 있는 pnpm
을 사용하기로 했다. 멘토님께서 yarn berry
도 언급해 주셔서 pnpm
이 yarn berry
와 비교를 해본 뒤 패키지 매니저를 결정하기로 했다. (근데 사실 이미 pnpm을 사용하기로 이야기를 나눈 후에 yarn berry도 찾아보는 형식으로 이야기가 진행된 것이라 pnpm 걍 쓸 것 같다.)
이 글에서 npm
의 문제점을 짚어보고, 이를 해결하려는 yarn berry
와 pnpm
에 대해 간략하게 정리했다.
https://classic.yarnpkg.com/blog/2018/02/15/nohoist/
기존 npm
은 각 프로젝트마다 의존성 패키지를 node_modules
폴더에 설치한다. 이 방식은 간단하지만, 의존성 패키지가 중복 설치되는 문제가 생길 수 있다.
예를 들어 이미지의 왼쪽처럼 의존성 트리가 만들어진다고 가정하자. 여러 패키지가 A
와 B
에 의존할 때, 각각의 의존성에 따라node_modules
폴더에 중복으로 설치되어 디스크를 낭비하게 된다. 이를 보완하고자 npm(과 Yarn 1)
은 이미지의 오른쪽처럼 중복되는 패키지를 끌어올리는(Hoisting) 방법을 사용했다.
예시로 express
패키지를 살펴보면 package.json
에 다음과 같은 의존성 패키지가 명시되어 있다.
"dependencies": {
**"accepts": "~1.3.8",**
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
**"debug": "2.6.9",**
...
},
중복되지 않는 패키지는 express 패키지 내부에 있는 node_modules 폴더에 존재한다. debug 패키지는 중복 사용되지 않는 패키지인 듯하다.
accepts는 호이스팅된 것으로 보아 중복 사용되는 패키지인 것 같다.
하지만, 이렇게 끌어올리기를 하게 되면 직접 의존하지 않던 B
라이브러리를 불러올 수 있게 되는 문제점이 발생한다. 이를 유령 의존성이라고 부른다. 즉, package.json
에 명시하지 않은 라이브러리를 몰래 사용하게 되면서 의존성 관리에 혼란을 야기할 수 있는 것이다.
Yarn Berry와 Pnpm 모두 호이스팅 대신 각자의 방식을 채택해 유령 의존성 문제를 해결했다.
Yarn Berry(Yarn 2)
는 이러한 문제를 해결하기 위해 Plug'n'Play(PnP)
방식을 도입했다. (이름이 너무 복잡한데…)
Yarn Berry는 node_modules
폴더 없이도 패키지를 관리할 수 있도록 설계되어, .yarn/cache
폴더에 패키지를 압축된 .zip
형태로 저장한다. 이를 통해 모든 의존성을 한 곳에서 직접 관리한다.
pnpm
은 위 문제를 해결하기 위해 프로젝트에서 사용하는 의존성 패키지(dependencies/devDependencies)는 node_modules
폴더 바로 아래에 해당 패키지의 심볼릭 링크를 만든다.
근데 node_modules
아래에 있는 express
가 [email protected]
의 심볼릭 링크가 아니라 [email protected]/node_modules/express
의 심볼릭 링크인 이유는 무엇일까? 왜 express 패키지가 express 안에 또 존재하는 것일까?