2026년, npm 대신 pnpm 11을 선택해야 하는 이유
npm을 아직 쓰고 계신가요?
최근 회사 프로젝트들을 정리하다가 문득 깨달은 게 있습니다. 아직도 많은 팀이 npm을 기본 패키지 관리자로 쓰고 있더라고요. 사실 이해됩니다. npm은 Node.js에 기본으로 들어있고, 튜토리얼마다 npm install을 치라고 하고, 별 문제 없이 잘 돌아가니까요.
하지만 지난 4월 pnpm 11이 나온 이후로는 상황이 좀 달라졌습니다. 이제는 단순히 "pnpm이 더 빨라서" 수준이 아니라 보안 측면에서도 무시하기 어려운 차이가 생겼거든요.

npm이 갖고 있는 근본적인 문제
패키지 관리자를 바꾸는 일은 우선순위가 낮아 보입니다. "당장 급한 건 아니니까" 하면서 스프린트에서 밀려나죠. 하지만 공급망 공격이 점점 정교해지는 2026년에는 다른 접근이 필요합니다.
일반적인 공격 시나리오를 보면 이렇습니다:
- 인기 패키지의 관리자 토큰이 유출됨
- 공격자가 악성 코드가 포함된 패치 버전을 배포
- CI/CD 파이프라인이
^1.2.0같은 semver 범위로 자동 업데이트 postinstall스크립트가 실행되면서 환경변수와 비밀 정보 탈취
최근에는 Mini Shai-Hulud 같은 공격도 나타났는데, npm, PyPI, Packagist를 동시에 감염시키면서 Bun 런타임까지 다운로드해서 난독화된 자격증명 탈취 프로그램을 실행하더라고요.
npm은 이런 공격에 대해 기본적인 보호 장치가 없습니다. 패키지가 올라오면 바로 설치할 수 있고, 어디서든 하위 의존성을 가져올 수 있고, 모든 postinstall 스크립트가 기본으로 실행됩니다.
pnpm 11에서 달라진 보안 기능들
24시간 대기 정책 (minimumReleaseAge)
pnpm 11에서 가장 중요한 변화입니다. 기본적으로 24시간 이내에 배포된 패키지는 설치되지 않습니다.
# pnpm-workspace.yaml
pnpm:
minimumReleaseAge: 1440 # 분 단위 = 1일
왜 이게 중요한가요? 대부분의 공급망 공격은 속도에 의존하기 때문입니다. 악성 버전을 배포하고 레지스트리에서 삭제되기 전까지 몇 시간밖에 안 되거든요. 24시간의 쿨다운 기간이 있으면 Socket.dev나 Snyk 같은 보안 도구들이 악성 패키지를 미리 탐지할 수 있습니다.
급하게 특정 패키지가 필요하면 예외 처리도 가능합니다:
pnpm:
minimumReleaseAge: 1440
minimumReleaseAgeExclude:
- "@types/*"
- "my-trusted-package"
외부 의존성 차단 (blockExoticSubdeps)
npm 레지스트리의 패키지들은 하위 의존성을 GitHub 저장소나 tarball URL에서 가져올 수 있습니다. 이런 "특이한" 소스들은 표준 레지스트리 감사를 우회하는 경로가 되죠.
pnpm 11에서는 blockExoticSubdeps: true가 기본값입니다. 전이 의존성은 반드시 구성된 레지스트리에서만 가져올 수 있고, 하위 의존성이 외부 URL에서 뭔가를 가져오려고 하면 설치가 중단됩니다.
빌드 스크립트 제어 (allowBuilds)
postinstall 같은 라이프사이클 스크립트는 악성코드가 실행되는 주요 경로입니다. pnpm 11에서는 어떤 패키지가 빌드 스크립트를 실행할 수 있는지 명시적으로 정의할 수 있습니다.
pnpm:
allowBuilds:
"electron": true
"esbuild": true
"core-js": false
기본적으로 모든 스크립트는 차단되고, 신뢰하는 패키지만 허용 목록에 추가하는 방식입니다. 더 이상 "패키지에 악성코드가 없기를" 바라는 게 아니라, 내 컴퓨터에서 코드를 실행할 수 있는 패키지를 적극적으로 결정하게 되죠.
신뢰 정책 (trustPolicy)
pnpm:
trustPolicy: no-downgrade
이전 버전에 비해 신뢰도가 떨어진 패키지(예: 서명이 없어진 최신 릴리스)의 설치를 거부합니다. 관리자 계정이 해킹당한 경우, 공격자들은 보통 기존과 같은 서명 절차를 거치지 않고 새 버전을 배포하거든요. 이런 이상 징후를 자동으로 감지해줍니다.
보안 외에도 개선된 점들
네이티브 레지스트리 명령어
이전까지는 pnpm publish나 pnpm login 같은 명령어들이 내부적으로 npm CLI를 호출했는데, pnpm 11에서는 모든 걸 네이티브로 처리합니다. npm 의존성이 필요 없어졌죠.
내장 SBOM 생성
pnpm sbom
CycloneDX 1.7이나 SPDX 2.3 JSON 형식으로 소프트웨어 자재 명세서(SBOM)를 생성할 수 있습니다. 규제가 까다로운 산업군이나 벤더 보안 검토가 필요한 팀에게는 꽤 유용한 기능입니다.
SQLite 기반 저장소
기존에는 패키지마다 JSON 파일 하나씩 만들어서 인덱스를 관리했는데, Store v11에서는 단일 SQLite 데이터베이스로 바뀌었습니다. 시스템 호출 횟수가 줄어들면서 설치 속도가 눈에 띄게 빨라졌어요. 캐시와 락 파일이 있는 상태에서는 약 2.3초 만에 설치가 완료됩니다.
npm에서 pnpm으로 마이그레이션하기
실제 마이그레이션은 생각보다 단순합니다.
1. pnpm 설치
npm install -g pnpm
# 또는 독립 설치
curl -fsSL https://get.pnpm.io/install.sh | sh -
2. 기존 프로젝트 가져오기
pnpm import
package-lock.json이 pnpm-lock.yaml로 변환됩니다. package.json은 그대로 유지되고요.
3. 의존성 설치
pnpm install
4. 보안 설정 구성
# pnpm-workspace.yaml
pnpm:
minimumReleaseAge: 1440
blockExoticSubdeps: true
trustPolicy: no-downgrade
allowBuilds:
"electron": true
"esbuild": true
# 빌드 스크립트가 필요한 패키지들 추가
주의사항
pnpm은 기본적으로 더 엄격한 모듈 해석을 사용합니다. package.json에 선언하지 않은 패키지에 접근하는 숨겨진 의존성이 있다면 에러가 날 수 있어요. 이건 사실 좋은 기능인데, 숨겨진 의존성을 명시적으로 드러내주거든요. 코드베이스에 몇 가지 작은 수정이 필요할 수는 있습니다.

간단한 비교표
| 기능 | npm | pnpm 11 |
|---|---|---|
| 디스크 사용량 | 프로젝트마다 복제 | 하드링크 공유 저장소 |
| 설치 속도 | 기준선 | 더 빠름 (SQLite 저장소) |
| 24시간 대기 정책 | 없음 | 기본 활성화 |
| 외부 의존성 차단 | 없음 | 기본 활성화 |
| 빌드 스크립트 제어 | 기본 모두 실행 | 명시적 허용 목록 |
| 신뢰 정책 | 없음 | no-downgrade 옵션 |
| SBOM 생성 | 없음 | 내장 |
앞으로의 방향: pnpm 12
pnpm 12에서는 Pacquet이라는 Rust 기반 설치 엔진이 도입될 예정입니다. 초기 벤치마크를 보면 웜 설치는 2.3초에서 1초 미만으로, 콜드 설치는 4.7초에서 약 3.1초로 단축된다고 하네요. npm과의 성능 격차는 더욱 벌어질 것 같습니다.

마치며
개인 프로젝트나 CI/CD가 없는 소규모 앱이라면 당장 급할 건 없습니다. 하지만 프로덕션 인프라를 관리하거나 자동화된 배포 파이프라인을 운영한다면, pnpm 11의 보안 기능들은 선택이 아니라 필수에 가깝다고 생각해요.
마이그레이션 비용은 낮고, 유지보수 부담도 거의 없으면서, 보안 수준은 확실히 올라갑니다. 몇 년 전에 이런 기능들이 있었다면 여러 보안 사고를 예방할 수 있었을 텐데 하는 아쉬움이 드는 건 저뿐일까요.