[Study Log] 패키지 매니저와 package.json

jihun·2026년 6월 11일

studylog

목록 보기
14/15

패키지 매니저

패키지 매니저는 프로젝트에서 필요한 외부 라이브러리들을 설치, 삭제, 업데이트, 버전 고정, 실행을 관리하는 도구이다.

예를 들어서 React 프로젝트를 만든다고 하면 React 소스 코드를 직접 다운 받지 않고 아래와 같은 명령어를 입력한다.

npm install react

이렇게 입력하면 패키지 매니저가 다음과 같은 과정을 거친다.
1. react 패키지를 찾는다
2. 필요한 버전을 다운받는다.
3. React가 내부적으로 의존하는 다른 패키지를 설치한다.
4. package.json에 기록한다.
5. 설치 결과를 node_modules에 넣는다.
6. 정확한 설치 버전을 lock 파일에 기록한다.

(npm은 Node.js 표준 패키지 매니저로, 프로젝트 의존성을 설치 & 업데이트 & 관리하는 도구이다)

package.json

package.json은 프로젝트의 설명

{
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "dependencies": {
    "react": "^19.0.0",
    "axios": "^1.7.0"
  },
  "devDependencies": {
    "vite": "^6.0.0",
    "typescript": "^5.0.0"
  }
}

dependencies : 실제 서비스 실행에 필요한 패키지 (react, axios, zustand 등)
devDepenedencies : 개빌/빌드/테스트할 때만 필요한 패키지 (vite, eslint, typescript, jest 등)
scripts : 명령어 단축키 (npm run dev, npm run build 등)

node_modules

패키지 코드가 설치되는 폴더

project
├─ package.json
├─ package-lock.json
└─ node_modules
   ├─ react
   ├─ axios
   └─ ...

보통 node_modules는 너무 크기 때문에 git에 올리지 않는다.

대신 package.json과 lock 파일만 올리고, 다른 사람은 프로젝트를 받으면 npm install을 통해 다시 설치한다.

lock 파일

lock 파일은 정확히 어떤 버전들이 설치됐는지를 기록하는 파일이다.

패키지 매니저마다 파일명이 다르다.
| 패키지 매니저 | lock 파일 |
|-|-|
| npm | package-lock.json |
| yarn | yarn.lock |
| pnpm | pnpm-lock.yaml |
| bun | bun.lock |

예를 들어 package.json에는 이렇게 적혀있다.

"axios": "^1.7.0"

(^는 1.x.x 버전이면 업데이트 가능이라는 걸 뜻한다.)

패키지 매니저 종류

npm

npm은 Node.js 기본 패키지 매니저이다.

가장 기본적이라 자료가 많고, 대부분의 라이브러리와 호환성이 좋다. Node.js를 설치하면 기본으로 내장되어 있기 때문에 별도 설치 없이 바로 적용 가능하다.

하지만 프로젝트가 많아지면 node_modules 중복으로 디스크를 많이 차지할 수 있다.

yarn

yarn은 npm의 대안으로 빠르고 lock 파일 관리가 안정적인 패키지 매니저이다.

yarn은 Yarn 1.xYarn 2+로 나뉘는데, Yarn 1.x은 기존 npm과 비슷하게 node_modules를 사용하는 방식이다. 최신 Yarn(Yarn 2+)은 Plug'n'Plan, PnP라는 방식을 지원한다. PnP는 node_modules 대신 .pnp.cjs같은 파일을 통해 의존성 위치를 관리하는 방식이다.

모노레포 기능이 강하고, 최신 Yarn은 의종성 경계를 엄격하게 관리할 수 있다. Zero-Install, PnP 같은 고급 기능이 있다.

하지만 단점으로는 PnP 방식이 처음 보면 낯설고, 일부 도구가 node_modules를 전제로 만들어져 있으면 설정이 필요할 수 있다. 그리고 Yarn 1과 Yarn 2+의 차이가 커서 헷갈릴 수 있다는 특징이 있다.

pnpm

pnpm은 최근에 많이 사용되는 패키지 매니저로, 빠른 설치와 디스크 절약, 엄격한 의존성 관리가 가능하다.

pnpm의 가장 큰 특징은 패키지를 프로젝트마다 복사하지 않고, 전역 저장소에 저장한 뒤 각 프로젝트에서는 링크로 연결된다는 것이다.

공식 문서 기준으로는 pnpm은 모든 패키지 파일들은 content-addressable store에 저장하고, node_modules 내부에는 hard link와 symlink를 사용해 의존성 구조를 만든다고 나와있다.

npm:
프로젝트 A/node_modules/lodash
프로젝트 B/node_modules/lodash
프로젝트 C/node_modules/lodash
-> 각각 따로 저장

pnpm:
전역 저장소/lodash
프로젝트 A -> 링크
프로젝트 B -> 링크
프로젝트 C -> 링크
-> 실제 파일은 공유

장점은 설치가 빠르고 디스크 공간을 아낄 수 있다. 또 잘못된 의존성을 더 잘 잡아주고, 모노레포에 강하다.

단점은 일부 오래된 라이브러리나 도구가 node_modules 구조를 강하게 가정하면 이슈가 날 수 있다.

pnpm은 어떻게 잘못된 의존성을 더 잘 잡을까?

예를 들어 package.json이 다음과 같다고 가정해보자

예를 들어 프로젝트에 vite만 설치했다고 가정해보자

npm install vite
{
  "dependencies": {
    "vite": "^6.0.0"
  }
}

그러나 vite는 내부적으로 여러 패키지를 사용한다.

vite
 |
 |- esbuild
 |- rollup
 |- postcss

이 중 직접 설치한 건 vite이기 때문에 vite는 직접 의존성에 해당한다. 그러나 vite가 내부적으로 사용해서 같이 설치된 esbuild, rollup, postcss 등은 간접 의존성에 해당한다.

문제는 다음처럼 rollup를 설치하지 않은 상황에서 rollup를 import한 상황이다.

import rollup from "rollup";

해당 프로젝트는 rollup을 직접 의존성으로 선언하지 않았기 때문에 package.json에는 rollup이 없다. 따라서 안되는 것이 맞지만, npm이나 Yarn Classic은 의존성을 node_modules 상단으로 끌어올리는 hoisting을 많이 한다.

그래서 vite 또는 다른 패키지가 내부적으로 쓰던 rollup이 우연히 루트 node_modules/rollup에 올라와 있으면, 자신의 코드에서도 rollup를 import할 수 있다.

이를 유령 의존성(phantom dependency)이라고 부른다.

나중에 vite 버전이 바뀌거나, 다른 패키지가 rollup을 더이상 안 쓰거나, 배포 환경에서 설치 구조가 달라지면 안되는 문제가 발생한다.

pnpm은 node_modules/.pnpm 내부에 패키지를 저장한 뒤 필요한 위치에 symlink로 연결한다. pnpm 공식 문서도 npm은 flatten된 dependency tree를 만들지만, pnpm은 hard link와 symbolic link를 사용하기 때문에 더 정돈된 node_modules 구조를 만든다고 설명하고 있다.

따라서 pnpm의 node_modules 구조는 프로젝트의 package.json에 명시되지 않은 모듈을 사용하는 것을 막는다.

bun

Bun은 JavaScript 런타임, 번들러, 테스트 러너, 패키지 매니저를 한 번에 제공하는 도구다.

장점은 매우 빠르고, 패키지 설치와 실행, 번들링까지 한 도구로 처리가 가능하다는 것이다.

단점은 Node.js 생태계 기준으로는 npm/pnpm/yarn보다 검증 기간이 짧고, 일부 패키지나 배포 환경에서 호환성 확인이 필요하다는 것이다.

Bun도 lock 파일 기반 재현 설치를 지원하며, bun install --frozen-lockfile을 사용하면 burn.lock에 기록된 버전을 설치하고 package.json과 lock 파일이 맞지 않으면 실패하도록 동작한다.

명령어 비교

작업npmyarnpnpmbun
전체 설치npm installyarn installpnpm installbun install
패키지 추가npm install axiosyarn add axiospnpm add axiosbun add axios
개발 의존성 추가npm install -D viteyarn add -D vitepnpm add -D vitebun add -d vite
패키지 삭제npm uninstall axiosyarn remove axiospnpm remove axiosbun remove axios
스크립트 실행npm run devyarn devpnpm devbun run dev
일회성 실행npx viteyarn dlx vitepnpm dlx vitebunx vite

결론

프로젝트를 시작할 때 때에 따라서 npm, yarn, pnpm, bun 중 알맞는 것을 선택하면 된다.

처음 배우는 상태라면 가장 기본적인 npm을 사용하는 것이 일반적으로 좋을 거 같고, 모노레포 기반일 때는 pnpm이나 yarn, Bun 런타임을 주로 쓴다면 bun, 안정성을 최우선한다면 npm이나 pnpm을 사용하는 것이 좋을 거 같다.
특히 React/Vite/Next.js처럼 의존성이 많아지기 쉬운 프로젝트에서는 디스크를 덜 쓰고 설치가 빠른 pnpm이 좋을 거 같다.

0개의 댓글