패키지 매니저(Package Manager)란?

Ocean·2023년 5월 8일
4

fe🔥

목록 보기
3/3

1️⃣ 패키지 매니저란?

패키지 매니저

  • 패키지 매니저는 패키지를 다루는 작업을 편리하고 안전하게 수행하기 위해 사용되는 툴이다.

  • 여기서 패키지를 다루는 작업이란 아래와 같다.

    1. 설치
    2. 업데이트
    3. 수정
    4. 삭제

패키지

  • 패키지는 라이브러리와 비슷한 개념으로, 라이브러리가 코드의 묶음이라면, 패키지는 코드의 배포를 위해 사용되는 코드의 묶음이다.
  • 패키지는 일반적으로 라이브러리나 실행 파일(executable)을 포함한다.
  • 패키지는 다음 3가지 정보를 가지고 있는 코드의 배포 단위이다.
    1. 컴파일한 소프트웨어의 바이너리(binary)
    2. 환경 설정(configuration)에 관련된 정보
    3. 의존(dependency)에 관련된 정보

Dependency

  • 많은 패키지들은 다른 패키지가 설치되어 있어야만 제대로 동작한다. 이 경우에 기존 패키지를 제대로 동작시키기 위해 필요한 다른 패키지를 'dependency'라고 한다.
  • 패키지를 사용하고자 할 때 dependency에 해당하는 패키지들을 전부 설치해야 한다.

Dependency Hell

  • dependency들을 설치하는 도중, 설치하고 있는 dependency의 dependency를 설치해야 하는 상황이 발생할 수 있다.
  • 이런 상황이 끊임없이 이어질 경우 사용자가 수동으로 패키지를 관리하기가 굉장히 어려워진다.
  • 이런 경우를 Dependency Hell이라고 한다.

👉 각각의 패키지가 자신의 dependency에 대한 정보를 가지게 한다면, 사용하고자 하는 패키지의 dependency를 패키지 매니저를 통해 쉽게 설치할 수 있다.

패키지 매니저가 수행하는 일

  1. 패키지의 dependency 관리
  2. 패키지의 보안 관리 - 신뢰할 수 있음(authenticity), 손상되지 않음(integrity) 보장
  3. 여러 패키지를 기능에 따라 그룹으로 묶어 정리
  4. 패키지 압축 해제
  5. Software repository로부터 패키지를 찾고, 다운로드하고, 설치하고, 업데이트하는 역할

Software repository (= repos)

  • 패키지를 저장하고 관리하는 저장소
  • 커뮤니티에 기여하는 것을 목적으로 다른 사용자들을 위해 패키지를 등록할 수 있다.
  • 성능 문제(load balancing)와 위기상황 대처(fault tolerant)를 위해 여러 개로 분리되어 있으며, 각각의 저장소가 동일한 기능을 수행한다.

패키지 매니저 종류

LanguagePackage managerSoftware repository
PythonpipPyPI
PHPComposerPackagist
Node.jsNPM, YarnNPM, Yarn
JavaMaven, GradleMaven
RubyRubyGems, BundlerRubyGems, Bundler

2️⃣ Javascript 패키지 매니저 비교 - npm, yarn 또는 pnpm?

오늘날 세 가지 주요 Javascript 패키지 매니저가 있다.

  • npm
  • Yarn - Yarn이 Yarn Classic(<v2) 또는 Yarn Berry(\geq v2)를 참조할 수 있다
  • 고성능(performant) npm - (pnpm)

사실상, 모든 패키지 매니저의 기능은 거의 동일하다. 그래서 설치 속도나 스토리지 사용량, 기존 워크플로와 결합되는 방식 등 기능 외적인 요구 사항을 기준으로 사용할 패키지 매니저를 결정하게 됩니다.

패키지 매니저로 수행할 수 있는 일

  • 메타데이터 처리 및 쓰기
  • 모든 의존성을 일괄(Batch) 설치 또는 업데이트
  • 스크립트 실행
  • 패키지 배포(publish)
  • 보안 감사(audit) 수행

따라서 설치 속도나 디스크 사용량, 또는 기존 워크 플로우 등과 어떻게 매칭 시킬지와 같은 기능 외적인 요구 사항에 따라 패키지 관리자를 선택하는 시대가 도래했다.

겉으로는 기능적으로 비슷해보이고 무엇을 선택하든 별 차이는 없어보이지만, 패키지 관리자들의 내부 동장은 매우 다르다.

  • npm과 yarn의 경우 flat한 node_modules 폴더에 dependency를 설치한다. → 비판에 자유롭지 못 함
  • pnpm은 이러한 dependency를 중첩된 node_modules 폴더에 효율적으로 저장하기 시작했다.
  • yarn berry는 plug and play(pnp) 모드를 도입했다.

자바스크립트 패키지 관리자의 역사

최초의 자바스크립트 패키지 매니저는 2010년 1월에 나온 npm이다.

10여년이 넘는 시간 npm이 사용되었는데 yarn, pnpm 등이 등장하게 된 이유는 무엇일까?

npm이 등장한 이후 아래와 같은 니즈가 발생했다.

  • node_modules 효율화를 위한 다른 구조 (nested vs flat, node_modules vs pnp mode)
  • 보안에 영향을 미치는 호이스팅 지원
  • 성능에 영향을 미칠 수 있는 lock 파일 형식
  • 디스크 효율성에 영향을 미치는 패키지를 디스크렝 저장하는 방식
  • 대규모 모노레포의 유지 보수성과 속도에 영향을 미치는 workspace라 알려진 멀티 패키지 관리 및 지원
  • 새로운 도구와 명령어 관리에 대한 관리
    • 이와 관련된 다양하고 확장 가능한 플러그인과 커뮤니티 툴
  • 다양한 기능 구현 가능성과 유연함

npm이 최초로 등장한 이래로 이러한 니즈가 어떻게 나타났는지, yarn classic은 그 이후 등장해서 어떻게 해결했는지, pnpm이 이러한 개념을 어떻게 확장했는지, yarn berry가 전통적인 개념과 프로세스에 의해 설정된 틀을 깨기 위해 어떠한 노력을 했는지 간략한 역사를 파악해보자.

# 선구자 npm

npm은 무엇의 약자일까? 많은 사람들이 npmnode package manager의 약자라고 생각하지만 사실 아니다!
npm의 전신은 사실 pm이라 불리는 bash 유틸리티인데, 이는 pkgmakeinst의 약자이다. 그리고 이의 node 버전이 npm인 것이다.

npm 이전에는 프로젝트의 dependency를 수동으로 다운로드하고 관리하였기 때문에 엄청난 혁명을 가져왔다고 볼 수 있다. 이와 더불어 metadata를 가지고 있는 package.json와 같은 개념, dependency를 node_modules라 불리는 폴더에 설치한다는 개념, 커스텀 스크립트, public & private 패키지 레지스트리와 같은 개념들 모두 npm에 의해 도입되었다.

# 많은 혁명을 가져온 yarn classic

2017년 페이스북은 구글과 몇몇 다른 개발자들과 함께 npm이 가지고 있던 일관성, 보안, 성능 문제 등을 해결하기 위한 새로운 패키지 매니저인 yarn(Yet Another Resource Negotiator)를 발표했다.

yarn은 대부분의 개념과 프로세스를 npm 기반으로 설계했지만, 이외에 패키지 관리자 환경에 큰 영향을 미쳤다. npm과 큰 차이점은, yarn은 초기 버전의 npm의 주요 문제점 중 하나였던 설치 프로세스의 속도를 높이기 위해 작업을 병렬화 했다.

yarn의 DX(개발자 경험), 보안 및 성능에 대한 기준을 높였으며 다음과 같은 개념을 패키지 매니저에 도입하였다.

  • native 모노레포 지원
  • cache-aware 설치
  • 오프라인 캐싱
  • lock files

yarn classic은 2020년부터 유지보수 모드로 전환되었다. 그리고 1.x 버전은 모두 레거시로 간주하고 yarn classic으로 이름이 바뀌었다.

# pnpm 빠르고 효율적인 디스크 관리

pnpm은 2017년에 만들어졌으며, npm의 drop-in replacement(설정을 바꿀 필요 없이 바로 사용가능하며, 속도와 안정성 등 다양한 기능 향상이 이루어지는 대체품)으로, npm만 있다면 바로 사용할 수 있다.

pnpm 제작자들이 생각한 npm과 yarn의 가장 큰 문제는 프로젝트 간에 사용되는 dependency의 중복 저장이다. yarn classic이 물론 npm보다 빠르지만, 두 매니저 모두 node_modules 내부에 flat하게 패키지를 설치하여 (= 동일한 디렉토리에 flat하게 저장) 관리했다.

pnpm은 이러한 호이스트 방식 대신, 다른 dependency를 해결하는 전략인 content-addressable storage를 사용했다. 이 방법을 사용하면, home 폴더의 글로벌 저장소('~/.pnpm-store')에 패키지를 저장하는 중첩된 node_modules 폴더가 생성된다. 따라서 모든 버전의 dependency는 해당 폴더에 물리적으로 한번만 저장되므로, single source of truth를 구성하고, 상당한 디스크 공간을 절약할 수 있다.

이는 node_modules의 레이아웃을 통해 이루어지고 'symlinks'를 사용하여 dependency의 중첩된 구조를 생성한다. 여기서 폴더 내부의 모든 패키지 파일은 저장소에 대한 하드 링크로 구성되어 있다.

# yarn berry, plug n play

yarn berry는 2020년 1월에 출시되었으며 yarn classic의 업그레이드 버전이다.

yarn berry에서 눈여겨 봐야 할 것은 plug n play로 node_modules를 수정하기 위한 전략이다. node_modules를 생성하는 대신, '.pnp.cjs'라 불리는 의존성 lookup 파일이 생성되는데, 이는 중첩된 폴더 구조 대신 단일 파일이기 때문에 더 효율적으로 처리할 수 있다. 또한 모든 패키지는 '.yarn/cache' 폴더 내부에 zip 파일로 저장되므로 node_modules 폴더보다 더 디스크 공간을 적게 차지한다.

이 모든 변화들은 너무나 빨랐고 출시 후 많은 논란을 불러일으켰다. pnp의 주요 변경 사항으로 인해 메인테이너들은 기존 패키지와 호환 되도록 업데이트해야 했다. 새로운 pnp 접근 방식이 기본적으로 사용되었으며 node_modules로 되돌리는 것이 처음에는 간단하지 않았다.

yarn berry 팀은 이후 후속 릴리스에서 많은 문제를 해결했다. pnp의 비호환성을 해결하기 위해 팀은 기본 작동 모드를 쉽게 변경할 수 있는 몇 가지 방법을 제공했다. → node_modules 플러그인의 도움으로 기존의 node_modules 접근 방식을 사용하는데 설정 하는데 설정 한 줄이면 가능하다.

또한, 자바스크립트 생태계는 시간이 지남에 따라 pnp에 대한 지원을 점점 더 많이 제공했으며 일부 대규모 프로젝트에서는 Yarn Berry를 채택했다.


설치 워크플로

패키지 매니저를 사용하기 위해서는, 개발자의 로컬 혹은 CI/CD 시스템에 설치해야 한다.

# npm

node.js 내부에 npm이 내장되어 있으므로, 추가적으로 작업을 할 필요가 없다. nvm이나 volta를 사용하면 node와 npm 버전을 관리하는데 매우 유용하게 쓸 수 있다.

# yarn classic

'npm i -g yarn'으로 설치할 수 있다.

# yarn berry

yarn classic에서 yarn berry로 넘어가는 방법은 다음과 같다.

  • yarn classic을 최신 1.x 버전으로 설치 또는 업데이트
  • yarn set version berry 명령어 입력

사실 권장하는 방법은 Corepack을 사용하는 것이다.

Corepack은 yarn berry 개발자에 의해 만들어진 도구이다.

Corepack의 도움으로 node는 yarn classic, yarn berry, pnpm의 바이너리를 shim으로 가지고 있기 때문에 npm의 대체 패키지 매니저를 별도로 설치할 필요는 없다. 이 shim을 활용하면, yarn 과 pnpm 명령어를 명시적으로 설치할 필요 없이, 실행할 수 있다.

Corepack은 nodejs@16.9.0부터 사전 설치되며, 이전 버전에서는 'npm install -g corepack'으로 설치할 수 있다.

❗️Corepack을 사용하기 위해서는, 먼저 활성화를 해야 한다❗️

$ corepack enable
$ corepack prepare yarn@3.1.1 -- activate

# pnpm

pnpm도 마찬가지 두 가지 방법으로 설치할 수 있다.

  • $ npm i -g pnpm
  • $ corepack prepare pnpm@6.24.2 --activate

프로젝트 구조

프로젝트 구조를 살펴보면, 특정 패키지 매니저를 구성하는데 사용되는 파일과, 설치 단계에서 생성되는 파일을 쉽게 알아볼 수 있다.

기본적으로, 모든 패키지 매니저는 모든 중요한 메타 정보를 package.json에 저장한다. 또한 루트 레벨에 설정파일을 사용하여 프라이빗 레지스트리나 dependency를 파일 구조에 저장하고 lock 파일이 생성된다.

# npm

$npm install 또는 $npm i 명령어를 입력하면 package-lock.json이 생성되고 node-modules 폴더도 생성된다. 이외에도 .npmrc 설정 파일도 생성될 수 있다.

.
├── node_modules/
├── .npmrc
├── package-lock.json
└── package.json

# yarn classic

$yarn을 실행하면, 'yarn.lock'node_modules 폴더가 생성된다. 마찬가지로 '.yarnrc'파일도 optional로 생성할 수 있다. 이에 더해 .npmrc 파일이 있으면 이를 이용할 수도 있다. 그리고 캐시 폴더인 'yarn/cache/'와 현재 yarn classic의 버전을 저장하는 '.yarn/releases/'도 생성될 수 있다. 이처럼 설정에 따라서 다양하게 변경될 수 있다.

.
├── .yarn/
│   ├── cache/
│   └── releases/
│       └── yarn-1.22.17.cjs
├── node_modules/
├── .yarnrc
├── package.json
└── yarn.lock

# yarn berry와 'node_modules'

yarn berry는 더이상 .npmrc.yarnrc를 사용하지 않는다. 대신 'yarnrc.yml' 설정 파일을 필요로 한다. 전통적인 'node_modules'를 생성하는 워크플로우가 존재하는 경우, nodeLinker config 파일을 아래와 같은 형태로 제공해야 한다.

# .yarnrc.yml
nodeLinker: node-modules # or pnpm

'$ yarn'을 실행하면, 모든 의존성을 'node_modules'에 설치한다. 'yarn.lock' 파일이 생성되는데, 이 파일은 기존 'yarn classic'과 호환되지는 않는다. 또한 오프라인 모드에서 설치를 위해 '.yarn/cache' 폴더도 생성된다. 'releases' 폴더는 프로젝트에서 사용하는 yarn berry의 버전을 저장하기 위해 optional로 생성된다.

.
├── .yarn/
│   ├── cache/
│   └── releases/
│       └── yarn-3.1.1.cjs
├── node_modules/
├── .yarnrc.yml
├── package.json
└── yarn.lock

# yarn berry with pnp

pnp 모드에는 strict와 loose 모드가 있는데, 일단은 모드에 상관없이 yarn을 실행하면 '.yarn/cache''.yarn/unplugged', '.pnp.cjs' 'yarn.lock' 파일이 생성된다. strict 모드는 기본 값이고, loose는 아래처럼 optional로 설정해두어야 한다.

# .yarnrc.yml
nodeLinker: pnp
pnpMode: loose

pnp 프로젝트에서 '.yarn/' 폴더 내부에는 'release/' 외에도 ide 지원을 위한 'sdk/' 폴더를 포함할 가능성이 높다.

.
├── .yarn/
│   ├── cache/
│   ├── releases/
│   │   └── yarn-3.1.1.cjs
│   ├── sdk/
│   └── unplugged/
├── .pnp.cjs
├── .pnp.loader.mjs
├── .yarnrc.yml
├── package.json
└── yarn.lock

# pnpm

pnpm도 다른 패키지 매니저와 마찬가지로 package.json이 필요하다. $ pnpm i를 실행하면, node_modules가 생성되는 것까지는 대른 패키지 관리자와 동일하지만, 앞서 언급한 content-addressable storage approach라는 특성 때문에 이후의 구조가 완전히 다른다.

pnpm은 자체 lock 파일인 pnp-lock.yml을 생성한다. 그리고 마찬가지로 .npmrc로 설정을 추가할 수도 있다.


성능과 디스크 관리의 효율성

성능으로 미뤄 보건데, yarn berry + Plug n Play strict가 가장 설치도 빠르고 디스크 효율적인 모습을 보여주었고, 그 다음으로는 pnpm이 뒤를 이었다.

보안

# npm

npm은 역사가 오래된만큼 사건사고도 많았다.

  • npm v5.7.0에서 파일시스템 권한을 바꿀 수 있는 버그가 발견된 적이 있음
  • sudo npm 명령어를 사용하면, 시스템 파일의 소유권을 변경하게 되어 os를 사용할 수 없게된 적이 있다.
  • EventStream 패키지 v3.3.6에서 악의적인 의존성이 추가되어, 개발자의 컴퓨터에서 비트코인을 훔치고자 하는 악의적인 코드가 존재한 적 있다.

이러한 문제를 해결하기 위해, 요즘 최신버전의 npm에서는 package-lock.json에서 SHA-512 알고리즘을 확인하여 설치하고자 하는 패키지의 무결성을 확인한다.

# yarn

yarn classic, yarn berry 둘 다 처음부터 yarn.lock에 지정된 체크섬을 활용하여 각 패키지의 무결성을 확인한다. 또한 package.json 내부에 선언되지 않은 의심스러운 패키지가 존재하면 설치가 중단된다.

yarn berry는 이에 더해 package.json에서 명시한 의존성의 바이너리 파일만 실행할 수 있다. 이는 pnpm과 유사하다.

# pnpm

pnpm 또한 체크섬을 활용하여 패키지의 무결성을 확인한다. pnpm은 npm과 yarn classic에서 이슈가 되는 패키지 호이스팅을 하지 않기 때문에 이러한 문제를 피한다. 대신, 위험한 dependency 액세스의 위험성을 제거하는 내부에 중첩된 node_modules 폴더를 생성한다.


결론

pnpm은 npm과 비슷해보이지만, 종속성 관리 측면에서 매우 다른 모습을 보인다. pnpm을 사용하면 성능이 향상되고, 디스크 효율성을 극대화 할 수 있다.

yarn classic도 훌륭한 선택지이지만, 레거시로 간주되고 가까운 미래에 지원이 중단될 수도 있는 가능성이 존재해서 선택하는 것을 추천하지는 않는다.

yarn berry의 plug n play는 완전히 새로운 혁신으로 다가왔지만, 아직 그 모든 잠재력을 달성한 것 같지는 않다. 그럼에도 요즘 사람들이 많이 쓰는 패키지 매니저는 yarn berry의 pnp인 것으로 보인다. 성능과 디스크 효율성, 속도 모두에서 뛰어난 모습을 보이고 있다.


참고

npm, yarn, pnpm 비교해보기

패키지 매니저(Package Manager)란?

profile
chick! chick!

0개의 댓글