pnpm이란 무엇입니까?
PNPM은 "빠르고 디스크 공간 효율적인 패키지 관리자"를 의미합니다. 이는 npm 및 Yarn의 일부 제한 사항과 단점을 해결하도록 특별히 설계된 JavaScript 프로젝트를 위한 대체 패키지 관리자입니다. 기존 패키지 관리자와 달리 pnpm은 패키지 설치 및 저장에 독특한 접근 방식을 취합니다.
주요 특징
- 공간 효율성 : pnpm의 뛰어난 기능 중 하나는 디스크 공간을 효율적으로 사용한다는 것입니다. npm과 Yarn은 각 프로젝트에 대해 별도의 종속성 복사본을 만드는 반면, pnpm은 전역 저장소를 사용하여 각 패키지 버전의 복사본 하나만 저장합니다.
- 전역 저장소에서 프로젝트의 node_modules 폴더로 하드링크를 만듭니다. 하드링크는 디스크상 원본 파일이 있는 위치를 가리킵니다.
- 속도 : pnpm은 패키지 연결에 대한 고유한 접근 방식으로 인해 속도가 뛰어납니다. 파일을 복사하는 대신 글로벌 스토어에 대한 링크를 생성함으로써 설치 시간을 크게 줄여 작업 흐름을 간소화하려는 개발자에게 매력적인 옵션이 됩니다.
- 일관성 : pnpm은 각 패키지 버전의 단일 복사본을 사용하여 프로젝트 간 일관성을 보장합니다. 이를 통해 종속성 지옥을 방지하고 종속성을 더 쉽게 관리하고 업데이트할 수 있습니다.
- 원자적 및 결정적 : pnpm은 종속성을 원자적으로 설치합니다. 즉, 설치가 중단되더라도 프로젝트가 손상된 상태로 남아 있지 않습니다. 또한 pnpm의 결정론적 특성으로 인해 다양한 환경에서 설치를 재현할 수 있습니다.
단점/어려움
- 호환성 문제 : pnpm은 npm 및 Yarn과의 호환을 목표로 하지만 특정 패키지나 특정 프로젝트 설정에서 호환성 문제가 발생할 수 있는 경우가 있을 수 있습니다. 이러한 경우는 일반적으로 pnpm 팀에서 해결하지만 사용자는 예상치 못한 문제에 직면할 수 있습니다.
- 제한된 커뮤니티 및 생태계 지원 : pnpm 커뮤니티는 성장하고 있지만 npm이나 Yarn만큼 광범위하지는 않습니다. 이로 인해 리소스, 문서 및 커뮤니티 지원이 줄어들 수 있습니다. 일부 프로젝트나 라이브러리는 pnpm 사용에 대한 지침을 명시적으로 제공하지 않아 특정 시나리오에서 잠재적으로 어려움을 초래할 수 있습니다.
- 글로벌 스토어의 인지된 위험 : 글로벌 스토어 개념은 일부 개발자에게 우려를 불러일으킬 수 있습니다. pnpm의 접근 방식은 디스크 공간을 절약하도록 설계되었지만 공유 글로벌 저장소가 프로젝트 종속성에 미치는 영향에 대한 우려가 있을 수 있습니다.
- 종속성 버전 관리 문제: 일부 개발자는 여러 프로젝트가 동일한 글로벌 스토어를 공유할 때 버전 충돌이나 의도하지 않은 부작용에 대해 걱정할 수 있습니다. 한 프로젝트에 특정 버전의 패키지가 필요한 반면 다른 프로젝트에는 다른 버전이 필요한 경우 호환성 문제가 발생할 수 있습니다.
- 보안 문제 : 글로벌 스토어에 저장된 패키지에서 취약점이 발견되면 여러 프로젝트가 동시에 영향을 받을 수 있습니다. 이러한 인식은 보안 문제가 발생할 경우 다양한 프로젝트에 잠재적인 영향을 미칠 수 있다는 우려를 불러일으킵니다.
- 환경 전반에 걸친 일관성 : 개발자는 다양한 개발 환경이나 팀 구성원의 컴퓨터 전반에서 패키지 버전의 일관성에 대해 우려할 수 있습니다. 모든 사람이 동일한 버전의 종속성을 갖도록 하는 것은 특히 대규모 개발 팀의 경우 문제가 될 수 있습니다.
pnpm, npm 및 Yarn 간의 비교
npm 은 v3 중복된 패키지를 호이스팅(위로 끌어올려서) 평탄화된 종속성 트리를 사용합니다.
그래서 디스크 크기를 덜 사용하지만, 복잡한 node_modules 디렉토리 구조를 야기합니다.
pnpm 은 전역 저장소(global on-disk content-addressable store) 에 하드 링크, 심볼릭 링크 방식으로 연결하여 관리합니다.
pnpm 의 node_modules 구조는 프로젝트의 package.json 에 명시되지 않은 모듈을 사용할 수 없게 함으로써 의도하지 않은 버그를 피하게 해줍니다.
pnpm의 node_modules 레이아웃은 심볼릭 링크를 사용하여 의존성의 중첩 구조를 생성합니다.
node_modules 안에 있는 모든 패키지의 모든 파일은 전역 저장소(정확히는 콘텐츠 주소 지정 저장소)에 대한 하드 링크 입니다.
예시입니다.
foo 패키지를 신규로 설치했습니다. foo 는 bar 에 의존합니다. (foo 안에 bar 있다.)
두 패키지는 node_modules 에 하드 링크 합니다.
node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
│ ├── index.js
│ └── package.json
└── foo@1.0.0
└── node_modules
└── foo -> <store>/foo
├── index.js
└── package.json
그 다음은 중첩된 의존성 그래프 구조를 구축하기 위해 심볼릭 링크를 생성합니다.
foo 는 bar 에 의존하므로 bar 는 foo@1.0.0/node_modules 폴더에 심볼릭 링크 됩니다.
node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
└── foo@1.0.0
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../bar@1.0.0/node_modules/bar
foo 는 프로젝트의 의존성이기 때문에 foo 는루트 node_modules 폴더에 심볼릭 링크 됩니다.
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
└── foo@1.0.0
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../bar@1.0.0/node_modules/bar
결국 레이아웃을 보면 플랫 구조는 아니고 중첩 구조입니다.
중첩 구조이면서 심볼릭 링크를 이용하여 연결되어 있습니다.
그러나 패키지가 peer-dependency 의 다른 버전에 의존하는 경우 잠재적으로 호환성 문제를 일으킬 수 있습니다.
pnpm은 고유한 연결 메커니즘을 통해 이 문제를 해결하려고 시도하지만 공유 종속성 동작에 대한 기대치의 차이로 인해 패키지가 호환되지 않는 시나리오가 여전히 있을 수 있습니다.
일부 패키지는 고유한 피어 종속성 인스턴스가 있다고 가정할 수 있으며, 프로젝트의 여러 패키지가 동일한 피어 종속성의 서로 다른 버전에 종속되는 경우 충돌이 발생할 수 있습니다.
이와 같이 특정 시나리오에서는 종속성이 이 접근 방식과 호환되지 않을 수 있으며 문제가 발생할 수 있습니다. 이럴 때 사용할 수 있는 --shameously-hoist 플래그는 pnpm의 기본 동작을 무시하고 필요할 때 강제로 끌어올리는 방법을 제공합니다.
그러나 종속성 관리에 대한 pnpm의 기본 접근 방식의 이점이 줄어들 수 있습니다. 혹은 개발자가 누락된 패키지를 수동으로 설치하거나 pnpm 후크를 활용하여 설치 프로세스를 미세 조정할 수 있습니다.
- 패키지 무결성
- yarn : Yarn은 node_modules 디렉터리 내에 .yarn-integrity 파일을 생성합니다. 이 파일에는 설치된 패키지의 체크섬이 포함되어 있어 설치된 종속성의 무결성을 확인하는 방법을 제공합니다.
- pnpm : pnpm은 패키지를 전역 저장소 디렉터리에 저장합니다. 이 디렉터리에는 평면 구조의 모든 종속성이 포함되어 있으며 프로젝트는 하드 링크를 사용하여 이러한 종속성을 참조합니다. 이는 프로젝트별로 패키지를 별도로 다운로드하고 저장하는 Yarn 및 npm과 다릅니다.
- 잠금파일
- yarn : 종속성을 잠그기 위해 yarn.lock 파일을 생성합니다. 이 파일에는 해결된 패키지 버전과 해당 종속성에 대한 자세한 정보가 포함되어 있습니다. yarn.lock 파일을 사용하면 다양한 환경에서 설치를 재현할 수 있습니다.
- pnpm : pnpm-lock.yaml 파일을 사용하여 종속성을 잠급니다. 이 파일에는 패키지 버전과 글로벌 스토어에서의 해당 위치에 대한 정보가 포함되어 있습니다.
- 설치 및 글로벌 스토리지
- npm: 각 프로젝트에 로컬로 패키지를 설치하고 패키지에 대한 전역 저장소를 갖습니다.
- Yarn: 각 프로젝트에 로컬로 패키지를 설치하고 전역 저장소를 갖습니다.
- pnpm: 패키지에 전역 저장소를 사용하지만 패키지를 각 프로젝트에 연결하여 디스크 공간을 절약합니다.
- 디스크 공간 효율성
- npm: 각 프로젝트에 대해 각 패키지 버전의 별도 복사본을 생성하므로 잠재적으로 파일이 중복되고 디스크 사용량이 높아집니다.
- Yarn: npm과 유사하게 각 프로젝트에 종속성을 복사합니다.
- pnpm: 각 패키지 버전의 복사본을 전역적으로 하나만 저장하여 디스크 공간 사용량을 크게 줄입니다.
- 설치 속도
- npm: 각 프로젝트에 종속성을 설치하기 때문에 속도가 느려질 수 있습니다.
- Yarn: 일반적으로 npm보다 빠르지만 여전히 종속성을 복사합니다.
- pnpm: 복사하는 대신 글로벌 스토어에 연결하여 속도가 뛰어나 설치 속도가 빨라집니다.
- 프로젝트 전반의 일관성
- npm: 각 프로젝트에는 고유한 종속성 세트가 있어 잠재적으로 불일치가 발생할 수 있습니다.
- Yarn: npm과 유사하게 각 프로젝트에는 종속성이 있습니다.
- pnpm: 전 세계적으로 각 패키지 버전의 단일 복사본에 대한 링크를 사용하여 일관성을 보장합니다.
- 원자적 및 결정적 설치
- npm: 설치는 원자적이지 않으며 중단으로 인해 프로젝트가 손상된 상태가 될 수 있습니다. 결정론은 어려울 수 있습니다.
- Yarn: npm에 비해 결정성이 향상되었지만 여전히 문제에 직면할 수 있습니다.
- pnpm: 종속성을 원자적으로 설치하여 중단으로 인해 프로젝트가 중단되지 않도록 합니다. 강력한 결정성을 제공합니다.
- 사용하는 방법
- npm: 전역적으로 설치되며 각 프로젝트에서 로컬로 사용됩니다.
- Yarn: 전역적으로 설치되고 로컬로 사용됩니다.
- pnpm: 전역적으로 설치되지만 각 프로젝트의 전역 저장소에 대한 링크를 사용합니다.
- 커뮤니티와 생태계
- npm: 가장 큰 패키지 레지스트리이며 널리 채택됩니다.
- Yarn: 속도와 결정론적 설치로 인기를 얻었습니다.
- pnpm: 특히 효율성과 속도로 인해 인기가 높아지고 있습니다.
결론
결론적으로 pnpm은 JavaScript 생태계의 패키지 관리에 대한 새로운 관점을 제공합니다.
공간 효율성, 속도 및 일관성에 중점을 두어 작업 흐름을 최적화하려는 개발자에게 매력적인 선택이 됩니다.
JavaScript 커뮤니티가 계속 발전함에 따라 pnpm은 개발자 툴킷에서 귀중한 도구로 두각을 나타내고 있습니다.
소규모 개인 프로젝트에서 작업하든 대규모 엔터프라이즈 애플리케이션에서 작업하든, pnpm을 탐색하는 것은 보다 효율적이고 간소화된 개발 경험의 열쇠일 수 있습니다. 한번 사용해 보시고 이 혁신적인 패키지 관리자의 이점을 활용하여 JavaScript 프로젝트를 향상시키십시오.
- npm: 가장 널리 사용되는 기본 패키지 관리자를 선호하는 경우.
- Yarn: 속도와 결정성에 중점을 두고 npm과 pnpm 간의 균형을 원하는 경우.
- pnpm: 효율적인 디스크 사용, 빠른 설치 및 프로젝트 전반의 일관된 종속성을 우선시하는 경우
위에까지는 chatGPT 의 도움을 받은 pnpm 에 대한 설명이었습니다.
아래는 제 생각이나 경험입니다.
생각/경험 공유
꽤 예전에 pnpm 에 대한 글을 읽으며 플랫구조의 node_modules 에 대해 감명받아서, pnpm 도입에 대해 많은 관심이 있었습니다.
여유가 생기면 프로젝트에 pnpm 도입에 대한 테스트 해보고 싶었는데 감사하게도 동료분께서 먼저 도입을 해주셔서 간접 경험 정도는 남길 수 있을 것 같습니다. 😁
pnpm 왜 도입 했는가
프로젝트에 수많은 패키지들이 쌓이다 보면 빌드 속도가 점점 느려지고 느려지다가 더 이상 기다리고만 있을 순 없는 수준에 까지 도달하게 되었습니다. 한마디로 프로젝트에 pnpm 을 도입하게 된 이유는 프로젝트 빌드속도 개선을 위해서였습니다.
옛날에 또 다른 동료분께서 설치된 패키지가 별로 없는 신규 프로젝트의 초창기에 pnpm 도입 테스트 해주셨을때 오히려 빌드시간이 늘어나는 엽기적인 상황이 있었는데요. 지금 생각해보면 설치된 패키지가 거의 없어서 pnpm의 효과를 보지 못했고, 원래는 없던 pnpm 설치까지 추가로 해야 했기 때문이 아닌가 싶습니다.
pnpm 언제 도입해야 하는가
프로젝트에 설치된 패키지가 많아져서 빌드 속도가 심각한 수준으로 느려진 경우라고 대답할 수 있을 것 같습니다.
기존 프로젝트의 pnpm 로의 마이그레이션
기존에 yarn/npm 을 사용하던 큰 규모의 프로젝트에서 pnpm 으로 마이그레이션 하는 경우 어려움이 있을 겁니다...
생각 이상으로 많은 종속성 문제가 터져서 저희도 급한대로 shameously-hoist 옵션을 켤 수 밖에 없었는데요.
아마도 yarn/npm 과 pnpm 의 구조적인 차이에서 발생하는 이슈인 것 같은데 정확히 무엇 때문인지 아직 이해하지는 못했습니다.
추측이지만, 동일 패키지의 여러 버전들의 충돌 같은 이유가 아닐까요...
아무튼 shameously-hoist 옵션을 켜면, pnpm 의 장점을 누릴 수 없게 된다고는 하지만, 이 옵션을 사용하더라도 yarn/npm 에 비해서는 월등히 빌드속도가 빨랐습니다. 왜냐하면 이 옵션을 켜더라도 모든 종속성에 적용되는게 아니라 일부 종속성에만 적용되기 때문이라고 합니다.
단순히 pnpm 으로 마이그레이션 하는 것 만으로도 큰 이점이 있기 때문에 들어가는 노력 대비 큰 이점을 누릴 수 있습니다.
벤치마크
yarn berry vs pnpm
- yarn berry 가 빠른 이유는 플러그엔 플레이 모드를 지원하기 때문인데요. pnp 모드 지원하지 않는 패키지가 하나라도 있으면 zero install 의 장점을 누릴 수 없어서 사실상 pnp 모드를 지원하지 않는 패키지를 못쓰는 것과 같이 말이죠. 그럴바에 그냥 pnpm 쓰는게 훨 낫다는 의견이 있습니다.
- 그리고 yarn/cache 폴더 통째로 원격 저장소에 올려서 zero-install 로 쓰는게 장점 이기도 하지만, 이게 양이 많고 패키지가 많아 질수록 git 체크아웃 등 모든 작업에 영향을 줄 수 밖에 없는게 찜찜한 부분도 있습니다.
- 불편함을 감소하더라도 압도적 성능을 추구한다면 yarn berry 가 괜찮은 선택일 수도 있지만, pnpm 이 좀 더 안정적인 게 아닐까 싶기도 합니다.
'프론트엔드 개발 > 패키지 매니저' 카테고리의 다른 글
npm/yarn 모든 패키지 종속성에 대한 버전 정보를 나열 (0) | 2023.12.03 |
---|