전역 패키지

전역 패키지는 pnpm add -g로 시스템 전체에 설치되는 CLI 도구와 유틸리티입니다. 새로운 pnpm v11에서는 더 나은 격리와 안정성을 위해 전역 패키지 관리가 새롭게 설계되었습니다.

전역 패키지 설치하기#

pnpm add -g <pkg>

예시:

pnpm add -g typescript prettier eslint

주의

이 명령들을 sudo로 실행하지 마세요. pnpm은 전역 패키지와 설정을 명령을 실행한 사용자의 홈 디렉터리에 보관하므로, sudo로 실행하면 사용자의 홈 대신 root 사용자의 홈에서 조용히 작동합니다. 버전 v11.21.0부터 pnpm setup, pnpm self-update 및 전역 설치를 수정하는 모든 명령은 root로 실행하면 경고를 출력하며, pnpm v12에서는 ERR_PNPM_SUDO_NOT_SUPPORTED 오류와 함께 실패합니다. 읽기 전용 명령, 예를 들어 pnpm bin -g는 영향을 받지 않습니다.

격리된 설치#

전역으로 설치된 각 패키지(또는 함께 설치된 패키지 그룹)는 자체 package.json, node_modules/, lockfile을 가진 격리된 설치 디렉터리를 갖습니다. 이를 통해 피어 종속성 충돌, 호이스팅 변경, 버전 해석 변화로 인해 전역 패키지끼리 서로 간섭하는 것을 방지합니다.

격리된 설치는 {pnpmHomeDir}/global/v11/{hash}/에 저장되며, 여기서 hash는 함께 설치된 패키지 집합에서 도출됩니다.

예를 들어, 다음 두 명령을 실행하면:

pnpm add -g typescript
pnpm add -g prettier

두 개의 별도 격리 설치가 생성됩니다 — typescript와 prettier는 각자 자체 node_modules 트리를 가지며 서로의 종속성 해석에 영향을 줄 수 없습니다.

한 명령에서 공백으로 구분된 여러 패키지를 설치해도 각 패키지마다 별도의 격리 설치가 생성됩니다:

pnpm add -g eslint prettier

eslint와 prettier는 각자 자체 node_modules 트리와 lockfile을 가지며 독립적으로 제거할 수 있습니다 — pnpm remove -g eslint는 prettier를 건드리지 않습니다.

여러 패키지를 같은 격리 설치로 묶으려면 — 즉, node_modules 트리와 lockfile을 공유하고, 서로를 기준으로 피어 종속성을 해석하며, 함께 제거되도록 하려면 — 쉼표로 구분된 목록으로 전달하세요:

pnpm add -g eslint,prettier

여기서 eslint와 prettier는 하나의 설치 그룹을 이룹니다. 둘 중 하나를 pnpm remove -g로 제거하면 그룹 전체가 제거됩니다.

두 형식을 섞어 쓸 수도 있습니다. 예시:

pnpm add -g eslint,prettier typescript

위 명령은 eslint와 prettier를 하나의 격리 설치로 묶고, typescript는 따로 설치합니다.

디렉터리 구조#

디렉터리 {pnpmHomeDir}/global/v11/의 내용은 다음과 같습니다:

{pnpmHomeDir}/global/v11/
├── {hash-A}              → symlink → ./{hash-A-target}/
├── {hash-A-target}/      ← isolated install dir
│   ├── package.json      ← lists the packages installed together
│   ├── pnpm-lock.yaml    ← lockfile for this install group
│   └── node_modules/
│       ├── <pkg>/        ← top-level dep, symlinked into the global virtual store
│       └── .pnpm/
├── {hash-B}              → symlink → ./{hash-B-target}/
├── {hash-B-target}/      ← another isolated install dir
└── store/                ← shared global virtual store
    └── ...
  • 각 {hash} 항목은 심볼릭 링크이며, pnpm은 이를 스캔하여 활성 설치 목록을 만듭니다.
  • 대상은 일반적인 pnpm 프로젝트처럼 동작하는 실제 디렉터리이며, 각각 자체 package.json과 lockfile을 갖습니다.
  • 공유 store/ 디렉터리에는 전역 가상 스토어가 있습니다. 각 설치 그룹의 직접 종속성, 즉 node_modules/ 루트에 있는 항목은 해당 스토어를 가리키는 심볼릭 링크이므로, 실제 패키지 내용은 그룹마다 복사되지 않고 공유됩니다.
  • 실행 파일 shim은 {pnpmHomeDir}/bin/에 있으며 해당 설치 그룹의 node_modules를 거쳐 실행 파일을 가리킵니다.

패키지가 제거되거나 설치 그룹이 교체되면 hash 심볼릭 링크가 업데이트되고, 고아가 된 대상 디렉터리는 나중에 pnpm store prune으로 정리됩니다.

전역 패키지 목록 보기#

pnpm list -g
pnpm list -g --json        # machine-readable
pnpm list -g --parseable   # paths only

각 설치 그룹은 자체 lockfile을 가지므로, 여러 그룹에 걸쳐 목록을 표시할 때는 설치 시 지정된 최상위 패키지만 안정적으로 집계할 수 있습니다 — 서로 다른 그룹의 전이 종속성 트리는 일관되게 병합할 수 없습니다. 그 결과:

  • pnpm list -g(기본값 --depth=0)는 항상 작동하며 전역으로 설치된 모든 패키지를 보여줍니다.
  • pnpm list -g --depth=<n>(n > 0인 경우)는 다음 경우에만 전체 종속성 트리를 보여줍니다:
    • 전역 설치 그룹이 하나뿐인 경우, 또는
    • 위치 인수로 요청을 단일 설치 그룹으로 좁힌 경우. 예: pnpm list -g eslint --depth=1.

--depth>0을 요청했지만 단일 설치 그룹으로 좁힐 수 없으면 pnpm은 ERR_PNPM_GLOBAL_LS_DEPTH_NOT_SUPPORTED 오류를 발생시킵니다.

전역 패키지 관리하기#

명령어설명
pnpm add -g <pkg>패키지를 전역으로 설치합니다
pnpm remove -g <pkg>전역으로 설치된 패키지 제거(설치 그룹으로 묶여 있었다면 그룹 전체가 제거됨)
pnpm update -g [pkg]전역 패키지 업데이트(새 격리 디렉터리에 다시 설치)
pnpm list -g전역으로 설치된 모든 패키지 나열

참고

pnpm install -g(인수 없이)는 지원되지 않습니다. 특정 패키지를 설치하려면 pnpm add -g <pkg>을 사용하세요.

바이너리 위치#

전역으로 설치된 바이너리는 PNPM_HOME의 bin 하위 디렉터리(즉, $PNPM_HOME/bin/)에 저장됩니다. 이를 통해 PNPM_HOME 디렉터리를 깔끔하게 유지합니다 — PNPM_HOME이 PATH에 있을 때 global/과 store/ 같은 내부 디렉터리가 셸 자동 완성을 어지럽히지 않습니다.

버전 v11로 pnpm을 업그레이드한 후에는 pnpm setup을 실행하여 $PNPM_HOME/bin이 PATH에 포함되도록 셸 설정을 업데이트하세요.

현재 전역 bin 디렉터리는 다음 명령으로 확인할 수 있습니다:

pnpm bin -g

프로젝트 인식 전역 바이너리#

추가된 버전: v12.0.0-rc.2 (pnpm v12 전용)

전역 명령이 현재 위치한 프로젝트를 따를 수 있습니다. 같은 도구의 다른 버전을 요구하는 프로젝트 안에서 전역 명령을 실행하면, pnpm은 프로젝트가 요구하는 버전을 실행합니다.

가장 유용한 경우는 Node.js 자체입니다. 런타임을 고정하는 프로젝트가 있다고 가정해 보겠습니다:

package.json
{
  "devEngines": {
    "runtime": {
      "name": "node",
      "version": "^22.0.0",
      "onFail": "download"
    }
  }
}

해당 디렉터리 안에서 그냥 node를 실행하면, 전역 Node.js의 메이저 버전이 다르더라도 Node.js 22가 실행됩니다:

cd ~/projects/legacy-app
node --version   # v22.x.x — the version the project pins
cd ~
node --version   # your globally installed version

셸 훅, .bashrc 수정, use 방식의 명령은 필요하지 않습니다 — pnpm이 전역 bin 디렉터리에 기록하는 shim이 직접 디스패치를 수행합니다.

버전 선택 방식#

pnpm은 현재 작업 디렉터리에서 위로 올라가며 해당 명령을 제공하는 가장 가까운 프로젝트를 찾은 다음:

  • 런타임(node, deno, bun)의 경우 매니페스트의 고정 버전만 고려합니다 — devEngines.runtime, 그다음 engines.runtime 순입니다. 고정된 버전은 필요할 때 전역 가상 스토어에 다운로드되어 직접 실행됩니다. 런타임에 대해서는 프로젝트의 node_modules/.bin을 참조하지 않으므로, 종속성이 실행할 node를 제공할 수 없습니다.
  • 버전 v12.0.0-rc.6에서 추가된 패키지 관리자(npm, yarn, bun)의 경우 프로젝트의 packageManager 또는 devEngines.packageManager 고정 버전을 고려하며, pnpm이 필요할 때 해당 버전을 준비합니다. 고정 값은 프로젝트를 무엇으로 설치할지 프로젝트가 직접 명시한 것이므로, 전역으로 설치된 해당 패키지 관리자보다 우선합니다.
  • 그 밖의 패키지의 경우 프로젝트의 node_modules/.bin/<name>이 사용됩니다.

전역 설치는 프로젝트가 아니므로 pnpm 홈 내부의 디렉터리는 건너뜁니다.

프로젝트가 아무것도 제공하지 않거나 조회가 거부되면 전역으로 설치된 버전이 실행됩니다 — 디스패치가 적용되지 않았다는 이유만으로 명령이 실패하지는 않습니다.

신뢰#

사용자가 cd로 들어간 프로젝트가 전역 바이너리 대신 자체 바이너리를 실행하도록 자동으로 허용되지는 않습니다.

이 섹션에서는 기본 auto 정책을 설명합니다. 정책이 auto이면:

  • 안정 Node.js 릴리스는 실행 전에 Node.js 릴리스 팀의 서명으로 검증되므로 묻지 않고 전환됩니다. (참고로 musl 기반 시스템에서는 해당 빌드에 서명이 없으므로 대신 확인 메시지 대상이 됩니다.)

  • 그 밖의 모든 것 — Deno, Bun, Node.js 프리릴리스, 그리고 활성화한 일반 패키지 — 은 프로젝트별, 후보별로 한 번 묻습니다:

    The project at "/home/user/projects/app" provides its own "tsc", which will be used
    instead of the globally installed one.
    Do you trust this project? [y/N]
    

답변은 프로젝트 디렉터리 및 정확한 바이너리의 지문을 키로 하여 머신 로컬 저장소에 기억됩니다. 바이너리나 이를 제공하는 패키지가 변경되면 pnpm은 이전 승인을 재사용하지 않고 다시 묻습니다.

CI 및 비대화형 세션에서는 질문할 수 없으므로 전역 버전이 실행되고 아무것도 기록되지 않습니다.

나머지 두 정책은 어떤 후보가 이 질문을 받을지를 바꿉니다. prompt는 서명이 검증된 안정 Node.js 릴리스를 포함한 모든 후보에 대해 질문합니다. 답변은 여전히 기억되므로 매번 실행할 때마다가 아니라 프로젝트와 후보별로 한 번만 묻습니다. always는 질문을 완전히 건너뛰고 즉시 전환합니다 — 그래서 확인 메시지 방식이라면 전역 버전으로 되돌아갈 CI에서도 사용할 수 있습니다.

정책과 관계없이 적용되는 보호 장치가 하나 더 있습니다: 프로젝트의 명령은 전역 명령과 같은 패키지에서 제공되어야 합니다. 다른 패키지에서 비슷한 이름의 tsc를 제공하는 프로젝트는 일치하지 않으므로 전역 tsc가 실행됩니다.

참여하는 패키지#

기본적으로 런타임만 참여합니다. 버전 v12.0.0-rc.6부터 패키지 관리자를 전역으로 설치하면(pnpm add -g yarn) 해당 항목도 추가되므로, 전역으로 설치된 Node.js가 devEngines.runtime을 따르는 것처럼 프로젝트의 고정 버전을 따릅니다. 직접 설정한 항목은 false를 포함하여 설정한 그대로 유지됩니다.

다른 패키지는 제공하는 패키지의 이름을 키로 하여 globalShims로 활성화하세요:

~/.config/pnpm/config.yaml
globalShims:
  typescript: true

설치 시점에 pnpm이 어떤 bin에 디스패치 shim을 줄지 결정하므로, 이미 전역으로 설치된 패키지는 활성화한 후 다시 설치해야 합니다:

pnpm add -g typescript

패키지를 끄는 경우에는 다시 설치할 필요가 없습니다 — 디스패치할 때마다 설정을 다시 읽습니다. 같은 설정으로 패키지별로 디스패치를 비활성화하거나, globalShims: false로 완전히 비활성화할 수 있습니다. 단일 명령에서는 PNPM_SHIM_BYPASS=1을 설정하세요:

PNPM_SHIM_BYPASS=1 node --version

전역으로 전혀 설치되지 않은 패키지도 pnpm shim add로 디스패치 명령을 가질 수 있습니다 — 이 덕분에 pnpm만 있는 머신에서도 Yarn 프로젝트 안에서 yarn이 작동합니다.

참고

globalShims는 의도적으로 프로젝트의 pnpm-workspace.yaml에서 읽지 않으며, 전역 설정 파일, pnpm 홈 디렉터리 자체의 pnpm-workspace.yaml, 그리고 환경에서만 읽습니다. 자세한 내용은 설정 문서를 참고하세요.

전역 가상 스토어#

전역 설치는 전역 가상 스토어를 사용합니다. 패키지는 {storeDir}/links에 저장되며 전역 설치 간에 공유됩니다. 이를 통해 여러 전역 패키지가 같은 라이브러리에 의존할 때 중복으로 가져오는 것을 방지합니다.

로컬 패키지를 전역으로 등록하기#

로컬 패키지의 바이너리를 시스템 전체에서 사용할 수 있도록 하려면 패키지 디렉터리에서 pnpm add -g .을 사용하세요:

cd ~/projects/my-tool
pnpm add -g .

이 명령은 패키지의 bin 항목을 등록하여 어디서든 호출할 수 있게 합니다. 자세한 내용은 pnpm link를 참고하세요.

빌드 스크립트 승인#

빌드 스크립트(예: postinstall)가 있는 전역 패키지는 승인이 필요합니다. 빌드 스크립트를 실행해야 하는 전역 패키지를 설치하면, pnpm이 빌드를 승인할지 거부할지 대화형으로 묻습니다.

--allow-build 플래그를 사용하여 빌드를 미리 승인할 수도 있습니다:

pnpm add -g --allow-build=esbuild esbuild