패키지매니저(npm, yarn classic, pnpm, yarn berry)

Hyeon·2025년 2월 13일
0
post-thumbnail

패키지 매니저?

소프트웨어 개발에서는 다양한 라이브러리와 모듈을 사용하여 개발 생산성을 높이고 유지보수를 쉽게 합니다. 하지만 이러한 라이브러리를 직접 다운로드하고 관리하는 것은 번거로운 일입니다. 이를 해결하기 위해 패키지 매니저(Package Manager)가 등장했습니다. 패키지 매니저는 개발자가 필요한 라이브러리를 간편하게 설치, 업데이트, 삭제할 수 있도록 도와주는 도구입니다. 또한 프로젝트에 사용된 모든 패키지를 관리하고, 의존성 문제를 해결하는 역할을 합니다.이 글에서는 javascript의 패키지 매니저인 npm, yarn, pnpm에 대해 정리해보려고 합니다.

npm

2010년 npm이 등장하기 전에는 프로젝트에서 필요한 라이브러리나 도구들을 직접 다운로드하고 수동으로 관리해야 했습니다. npm은 이 과정을 자동화하며 javascript 개발방식을 획기적으로 바꿨습니다.

1. package.json: 프로젝트의 메타데이터와 의존성을 정의

package.json 파일은 프로젝트에 대한 중요 정보를 담고 있는 파일입니다. 이 파일은 프로젝트의 이름, 버전, 의존성, 스크립트 등을 정의합니다.

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "My awesome project",
  "main": "index.js",
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  },
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  }
}
  • name, version, description: 프로젝트 이름, 버전, 설명
  • dependencies: 프로젝트에서 실제 실행 중 필요한 라이브러리(예: express)
  • devDependencies: 개발 중에만 필요한 라이브러리(예: nodemon)
  • scripts: 커스텀 스크립트를 정의하여 반복적인 작업을 자동화

2. node_modules: 의존성이 설치되는 디렉토리

node_modules 폴더는 프로젝트에서 사용하는 모든 라이브러리와 의존성이 설치되는 장소입니다. npm이 패키지를 설치할 때 이 폴더 안에 필요한 파일들이 들어가게 됩니다.

my-project/
  node_modules/
    express/
    nodemon/
  package.json

node_modules 폴더 안에는 프로젝트에서 사용되는 모든 외부 패키지가 설치됩니다.

3. 커스텀 스크립트: 자동화된 빌드 및 실행 명령

npm은 package.json 파일 내에 커스텀 스크립트를 정의할 수 있게 해줍니다. 이를 통해 자주 사용하는 명령어나 작업을 자동할 수 있습니다.

{
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js",
    "test": "mocha test.js"
  }
}

4. 공개/비공개 패키지 레지스트리: 패키지 배포 및 관리 시스템

npm은 패키지 레지스트리를 통해 전 세계 개발자들이 만든 패키지를 공유하고 배포할 수 있게 합니다. 이 레지스트리에는 공개 레지스트리와 비공개 레지스트리가 있으며, 각각 오픈 소스 프로젝트와 기업 내에서만 사용되는 패키지를 다룹니다.

npm install lodash  # 공개 패키지 설치
npm install @my-org/my-package  # 비공개 패키지 설치 

npm은 javaScript 개발방식을 크게 개선하고, 의존성 관리와 패키지 배포를 훨씬 더 효율적으로 만들어 주었습니다.

yarn classic

npm은 javaScript 생태계에서 필수적인 패키지 매니저였지만, 몇 가지 문제가 있었습니다.

  • 속도
    npm은 패키지를 하나씩 순차적으로 설치하는 방식이었습니다. 여러개의 패키지를 설치할 때 시간이 많이 소요되며, 동일한 패키지를 여러 번 다운로드하는 문제가 있었습니다.

  • 안정성
    npm에서는 패키지를 설치할 때 버전이 정확히 관리되지 않아 동일한 프로젝트라도 설치된 패키지가 다를 수 있는 문제점이 있었습니다. 이로 인해 다른 개발자나 CI/CD 환경에서 버그가 발생할 수 있었습니다.

  • 보안
    npm은 패키지의 무결성 검증이 부족해 중간에 패키지가 변조되거나 의도치 않은 코드가 포함될 위험이 있었습니다.

이러한 문제들을 해결하기 위해 2016년 페이스북에서 yarn을 공개했습니다.

1. 속도

yarn은 패키지 데이터를 캐시에 저장하여 중복 다운로드를 방지합니다. 이로 인해 패키지 설치 속도가 빨라지며, 여러개의 패키지를 병렬로 설치할 수 있어 설치 속도가 크게 개선되었습니다. npm은 패키지를 순차적으로 설치하므로 상대적으로 속도가 느릴 수 있습니다. 반면, yarn은 병렬 처리로 빠른 속도를 제공합니다.

2. 안정성

yarn은 yarn.lock 파일을 사용하여 패키지의 버전과 의존성을 명확하게 기록하고(npm은 v5 부터 package.lock 사용), 모든 디바이스에서 동일한 패키지를 설치하도록 보장합니다. 이로 인해 설치된 패키지들이 일관되게 관리되고, 버전 차이로 인한 문제를 방지할 수 있습니다.

3. 보안

yarn은 보안성을 강화하는 데 중점을 두었습니다. yarn은 패키지 레지스트리와의 통신 시 패키지 무결성을 체크하고, 해시 검증을 통해 다운받은 패키지가 예상과 일치하는지 확인합니다. 이로 인해 의도치 않은 코드 변경이나 패키지 변조로부터 프로젝트를 보호할 수 있습니다.

node modules의 문제

캐싱, 병렬 설치 등 yarn을 사용하게 되면서 많은 부분이 개선되었지만, node module 구조하에서 여전히 문제는 있었습니다.

문제 1. 비효율적인 모듈 탐색

모듈을 require()하거나 import할 때, 해당 모듈이 node_modules 폴더 내에 존재하는지 탐색하는 방식은 기본적으로 디스크 I/O 작업입니다. 특정 모듈을 찾기 위해 여러 경로를 순차적으로 탐색하며, 이 과정에서 여러 폴더를 여닫고 파일 시스템을 검색하게 됩니다. 이런 탐색은 효율적이지 않고, 성능을 저하시킬 수 있습니다.

모듈 탐색 과정 예시

///home/ry/projects/foo.js에서 require('bar.js')를 찾을 때
// Node.js가 다음과 같은 경로들을 차례대로 확인합니다:

/home/ry/projects/node_modules/bar.js        // (1차 탐색)
/home/ry/node_modules/bar.js                 // (2차 탐색)
/home/node_modules/bar.js                    // (3차 탐색)
/node_modules/bar.js                         // (4차 탐색)
...

위와 같은 방식으로, 모듈을 찾을 때마다 여러 경로를 순차적으로 탐색합니다. 만약 프로젝트가 깊은 디렉토리 구조를 가질 경우 node_modules 폴더가 여러 번 중첩될 수 있고, 이러한 경로들을 계속해서 확인해야 하기 때문에 디스크 I/O 작업이 과도하게 발생합니다.

탐색이 계속 깊어질수록 시간이 더 오래 걸리고, 실제로 해당 모듈을 찾지 못할 가능성도 존재합니다. 이로 인해 프로젝트의 규모가 커질수록 모듈을 찾는 시간이 길어지고, 결과적으로 애플리케이션 실행 성능에 영향을 미칠 수 있습니다.모듈 검색이 자료구조를 기반으로 이루어지지 않고 파일 시스템 I/O 작업을 반복적으로 처리하므로 성능을 최적화하기 어렵습니다.

문제 2. 유령 의존성

npm과 yarn classic은 중복 설치를 방지하고, 모듈 탐색 효율성을 높이기 위해 종속성 트리 아래에 존재하는 패키지들에 대한 호이스팅(hoisting) 및 병합을 시도했습니다.

각 패키지의 의존성들을 최상위 node_modules 폴더로 올려놓고, 이를 통해 모듈을 더 빠르게 찾을 수 있도록 하는 방식입니다. 이 방식은 디스크 I/O 작업을 줄이고, 모듈 탐색을 더 효율적으로 만들 수 있기 때문에 성능 상 이점을 제공합니다. 모듈이 중복으로 설치되지 않으므로 탐색 범위를 줄이고, 최상위 폴더에서 바로 원하는 패키지를 빠르게 찾을 수 있습니다.

이와 같이 종속성인 패키지 B가 최상위 node_modules 폴더로 호이스팅되어 효율적인 모듈 탐색을 가능하게 합니다.

하지만, 이런 효율성의 반대 급부로 발생하는 문제는 바로 유령 의존성(Phantom Dependency)입니다. 호이스팅된 패키지들은 개발자가 명시적으로 설치하지 않은 패키지도 의존성 트리에서 자동으로 상위 디렉토리로 올려 사용될 수 있습니다. 이로 인해 개발자가 직접 설치하지 않은 의존성이 실행되는 문제가 발생할 수 있습니다.

예를 들어 패키지 A가 패키지 B를 의존하고 있는데, 개발자는 패키지 B를 직접 설치하지 않았습니다. 그럼에도 불구하고 패키지 A를 설치하면, 패키지 B는 최상위 node_modules 폴더에 호이스팅되어 사용되므로 마치 개발자가 B를 설치한 것처럼 동작하게 됩니다. 이로 인해 패키지 B가 잘못 사용되어 예기치 않은 버그를 초래할 수 있습니다.

pnpm

비효율성을 개선하기 위해 호이스팅도 적합하지 않은 것 같습니다. 2017년 등장한 pnpm은 npm, yarn과 유사한 방식으로 패키지를 관리하지만, 패키지를 효율적으로 저장하고, 탐색하는 방법을 개선했습니다.

pnpm은 각 프로젝트에 패키지를 복사하지 않고 하드 링크를 사용하여 동일한 패키지를 여러 프로젝트에서 공유합니다. 하드 링크는 물리적으로 파일을 복사하지 않고, 다른 위치에서 파일을 참조할 수 있게 만드는 방식입니다. 이 방식은 디스크 공간 절약과 빠른 속도를 제공합니다.

동일한 패키지 버전이 여러 프로젝트에서 필요할 때, pnpm은 그 패키지를 한 번만 저장하고 다른 프로젝트에서는 하드 링크로 참조합니다. 이를 통해 중복된 저장을 방지하고 디스크 공간을 아낍니다.

2. node_modules 구조 유지

pnpm은 기존의 node_modules 디렉토리 구조를 그대로 사용합니다. 즉, 기존 npm 프로젝트에서 pnpm을 바로 사용할 수 있습니다. 다만, npm은 패키지를 직접 복사하는 반면, pnpm은 하드 링크를 사용하여 패키지 설치를 최적화합니다.

3. 공유 저장소(~/pnpm-store)

pnpm은 로컬의 공통 저장소(~/.pnpm-store)에 모든 의존성 패키지를 저장합니다. 각 프로젝트는 이 저장소에서 패키지를 참조하고, 필요한 패키지를 심볼릭 링크로 연결하여 사용합니다. 이로 인해 여러 프로젝트에서 중복되는 패키지 설치를 방지하고, 디스크 공간을 절약합니다.

//npm
Project A와 Project B가 각각 lodash 패키지를 설치합니다.
각 프로젝트는 lodash를 별도로 설치하여 디스크에 중복 저장됩니다.
이로 인해 동일한 패키지가 여러 번 설치되고, 디스크 공간을 낭비하며 속도도 느려질 수 있습니다.

//pnpm
pnpm은 모든 프로젝트가 동일한 패키지를 공유된 저장소(예: pnpm Store)에서 참조합니다.
lodash는 하나의 공통 저장소에만 설치되고, 각 프로젝트는 해당 패키지를 심볼릭 링크(alias)로 참조합니다.
이렇게 하면 디스크 공간을 절약하고 설치 속도도 빠릅니다. ​

pnpm을 사용하면서 얻는 이점

npm은 패키지를 매번 복사하고 설치하는 방식이기 때문에 시간이 많이 걸립니다. pnpm은 하드 링크를 사용하여 패키지를 한번만 저장하고 참조하므로, 속도가 훨씬 빠릅니다. npm에서 중복된 패키지 설치가 계속 반복되는 것을 개선하여 성능이 향상됩니다.

디스크 공간 절약

pnpm은 각 패키지를 하드 링크로 참조하기 때문에, 동일한 패키지가 여러 프로젝트에서 필요해도 중복 저장하지 않고 하나만 저장됩니다. 이 방식은 디스크 공간을 절약하고, 패키지 크기가 커질수록 그 효과가 더 큽니다.

기존 프로젝트와의 호환

pnpm은 기존의 npm과 동일한 node_modules 구조를 사용하기 때문에, npm에서 pnpm으로 쉽게 전환할 수 있습니다. 기존 프로젝트에서 변경 없이 pnpm을 사용할 수 있어, 호환성 문제가 발생하지 않습니다.

비효율적인 모듈 탐색 개선

pnpm은 패키지를 설치할 때 하드 링크 방식으로 의존성 파일을 처리하므로, npm에서 발생하던 불필요한 파일 복사와 중복 설치 문제를 해결합니다.

yarn berry

yarn berry는 yarn의 최신 버전(v2 이상)으로, 기존 yarn v1과 비교해 성능과 의존성 관리를 개선한 패키지 관리 도구입니다.

Plug'n'Play(PnP)

Plug'n'Play(PnP)는 yarn berry에서 도입된 새로운 패키지 관리 방식으로, 기존의 node_modules 폴더 없이 의존성을 관리하는 시스템입니다. 성능을 개선하고, 패키지 설치 시간을 단축하며, 디스크 공간을 절약하는 것을 목표로 합니다.

기존 node_modules 방식의 문제점

파일 시스템 접근 비효율: 깊은 의존성 트리로 인해 패키지를 탐색하는 속도가 느려짐
불필요한 중복 설치: 동일한 패키지가 여러 경로에 중복 저장될 가능성이 있음
관리 어려움: node_modules이 손상되면 전체 삭제 후 재설치해야 함

Plug'n'Play는 이러한 문제를 해결하기 위해 의존성 정보를 파일로 관리하고, 필요할 때만 로드하는 방식을 채택했습니다.

Plug'n'Play의 동작 방식

Yarn Berry에서 yarn install을 실행하면

  1. node_modules 폴더를 생성하지 않음
  2. .yarn/cache 폴더에 패키지 데이터를 압축된 zip 파일로 저장
  3. .pnp.cjs 파일을 생성하여 패키지 위치 및 의존성 정보를 관리
  4. require()를 오버라이드하여 node_modules 없이도 패키지를 찾을 수 있도록 설정

예를 들어, react 패키지는 .pnp.cjs 파일에 다음과 같이 기록됩니다

["react", [
  ["npm:17.0.1", {
    "packageLocation": "./.yarn/cache/react-npm-17.0.1-xxx.zip/node_modules/react/",
    "packageDependencies": [
      ["loose-envify", "npm:1.4.0"],
      ["object-assign", "npm:4.1.1"]
    ],
  }]
]],

Plug'n'Play을 사용하면서 얻는 이점

  1. node_modules 폴더 제거 → 불필요한 파일 낭비 없이 의존성 관리 가능
  2. 패키지 로딩 속도 향상 → .pnp.cjs 파일을 이용해 빠르게 패키지를 찾음
  3. 의존성 충돌 방지 → 패키지가 명확한 버전으로 관리되어 예상치 못한 충돌 없음
  4. 파일 시스템 접근 최적화 → node_modules 탐색 과정이 없어 실행 속도 증가
  5. 패키지 중복 설치 방지 → 동일한 패키지를 여러 번 설치하지 않아 디스크 공간 절약

Plug'n'Play 활성화 방법

yarn berry를 사용하려면 다음 명령어를 실행하여 최신 버전의 yarn을 설치하고 berry로 전환해야 합니다

npm install -g yarn
cd /path/to/project
yarn set version berry

이제 Plug'n'Play를 활성화하려면 .yarnrc.yml 파일에 다음 옵션을 추가한 뒤, install 합니다.

nodeLinker: pnp
yarn install

이제 프로젝트가 node_modules 없이 동작하도록 설정되었습니다.

zero install

zero install은 PnP를 활용하여 .yarn/cache 폴더에 저장된 패키지를 Git 저장소에 포함하는 방식입니다. 이를 통해 프로젝트를 클론한 후 별도의 yarn install 없이 즉시 실행할 수 있습니다. 기존에는 저장소를 클론한 후 패키지를 설치해야 했지만, zero install을 적용하면 git에서 의존성 파일까지 함께 내려받기 때문에 설치 과정이 필요 없습니다.

zero install을 사용하면서 얻는 이점

  1. 패키지 설치 과정 생략 → yarn install 없이 프로젝트를 즉시 실행 가능
  2. 일관된 개발 환경 유지 → 팀원 및 CI/CD 환경에서 동일한 패키지 사용
  3. 네트워크 의존도 감소 → 오프라인 상태에서도 프로젝트 실행 가능
  4. CI/CD 속도 향상 → 패키지 설치 과정이 필요 없어 빌드 시간이 단축됨
  5. 브랜치 변경 시 패키지 일관성 유지 → 브랜치 변경 시마다 패키지를 재설치할 필요 없음

https://toss.tech/article/node-modules-and-yarn-berry
https://toss.tech/article/27772
https://yarnpkg.com/features/pnp
https://tech.remember.co.kr/%EB%A6%AC%EB%A9%A4%EB%B2%84-%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A2%8C%EC%B6%A9%EC%9A%B0%EB%8F%8C-yarn-berry-%EB%8F%84%EC%9E%85%EA%B8%B0-0e01c9531079

profile
즐겁게하자

0개의 댓글

관련 채용 정보