NPM(Node Pacakge Manager)

2025. 8. 15. 12:19프로젝트

0. 목차

  1. 개요
  2. npm이 뭐에요?
  3. package.json, node-modules, package-lock.json
  4. npm / pnpm / npx 의 차이
  5. package.json 구성
  6. monorepo ( workspaces )

1. 개요

최근 폐쇄망 개발 프로젝트에 투입되면서, 독특한 개발 환경 설정 과정을 직접 경험했고 그 과정에서 npm에 대한 다양한 정보를 얻게 되었습니다. 이번 글에서는 그 경험을 기록해두려 합니다.

 

특히, Nexus에 자체 npm registry를 띄워 프로젝트의 기본 저장소로 설정하고, 모듈 파일을 직접 Nexus에 업로드하는 등 평소라면 접하기 어려운 절차를 거쳤습니다. 이러한 비일상적인 과정 속에서 뜻밖의 인사이트도 많이 얻을 수 있었습니다.


2.  npm이 뭐에요?

npm(Node Package Manager)은 Node.js 환경에서 가장 널리 사용되는 패키지 관리자입니다. 프로젝트를 개발하다 보면 외부에서 만든 기능 모듈(라이브러리)을 가져다 쓰는 경우가 많은데, 이때 필요한 패키지를 쉽게 설치·관리·배포할 수 있도록 도와주는 도구가 바로 npm입니다.

 

npm은 크게 다음과 같은 역할을 합니다.

  1. 패키지 설치 (Install)
    • npm install 패키지명 명령으로 수많은 오픈소스 라이브러리를 손쉽게 프로젝트에 추가할 수 있습니다.
    • 예: npm install express → 웹 서버 프레임워크 Express 설치
  2. 의존성 관리 (Dependency Management)
    • package.json 파일을 통해 프로젝트에서 사용하는 패키지와 버전을 기록합니다.
    • 다른 개발자가 프로젝트를 받아도 npm install만 실행하면 동일한 환경이 세팅됩니다.
  3. 패키지 배포 (Publish)
    • 자신이 만든 모듈을 npm registry에 업로드하여 전 세계 개발자들과 공유할 수 있습니다.
  4. 스크립트 실행 (Scripts)
    • package.json에 정의한 빌드, 테스트, 배포 스크립트를 npm 명령어로 실행할 수 있습니다.

정리하면, npm은 Node.js 생태계의 필수 인프라로, 개발자가 재사용 가능한 코드를 쉽게 공유·활용할 수 있도록 하는 중심 허브 역할을 합니다.


3. package.json, node-modules, package-lock.json

프로젝트를 비행기로 비유하여 package.json, node-modules와 package-lock.json 파일이 각각 어떤 역할을 담당하는지 표현해보겠습니다.

비행기 프로젝트
부품 설계도  package.json
부품 창고 node_modules
부품 마켓 npm registry
부품 구매 영수증 package-lock.json

 

 

1. package.json — 부품 설계도

 

비행기를 만들려면 먼저 어떤 부품이 필요한지와 어느 정도 규격(버전)의 부품이면 되는지를 적은 설계도가 필요합니다.

 

 

2. node_modules — 부품 창고

 

설계도가 있다면, 이제 부품 마켓(npm registry)에 가서 부품을 사와야 합니다.

 

사온 부품들은 비행기 조립 공장 옆 창고( node_modules )에 차곡차곡 쌓입니다. 이 창고에는 설계도에 적힌 부품뿐 아니라, 그 부품이 또 필요로 하는 하위 부품까지 모두 들어옵니다.

 

 

3. package-lock.json — 부품 구매 영수증

 

package.json에는 보통 이렇게 버전의 범위가 적혀 있습니다.

"axios": "^1.6.0"

 

여기서 ^1.6.0은 “1.6.0 이상, 2.0 미만이면 다 OK”라는 뜻입니다. 즉, 오늘 설치하면 1.6.0이 깔릴 수 있지만,

한 달 뒤에 설치하면 1.7.2가 깔릴 수도 있습니다.

  • A 개발자: 어제 npm install 해서 axios 1.6.0 설치
  • B 개발자: 오늘 npm install 했더니 axios 1.7.2 설치

겉으로 보기엔 같은 패키지를 쓴 것 같지만, 내부 동작이 조금 달라서 A는 정상 동작하지만 B는 에러가 발생하는 일이 생길 수 있습니다. 이러한 상황 때문에 필요한 것이 영수증( package-lock.json ) 입니다.

 

package-lock.json은 “설치할 때 정확히 어떤 버전을 썼는지”를 스냅샷처럼 기록합니다.

axios@1.6.0 resolved: https://registry.npmjs.org/axios/-/axios-1.6.0.tgz integrity: sha512-...

 

이 기록을 기반으로 npm install 하면, 모든 개발자·서버가 무조건 같은 버전의 axios를 설치하게 됩니다.


4. npm / pnpm / npx 의 차이

1. npm (Node Package Manager)

  • 역할: Node.js의 기본 패키지 관리자
  • 주요 기능:
    • 패키지 설치: npm install react
    • 의존성 관리: package.json, package-lock.json 생성·갱신
    • 스크립트 실행: npm run build
  • 특징:
    • Node.js 설치 시 기본 포함
    • 패키지를 로컬(node_modules) 또는 전역(-g)에 설치 가능
    • 의존성 설치 시 중복 패키지가 많아질 수 있음 (폴더 구조 깊어짐)

 

2. pnpm (Performant npm)

  • 역할: npm을 대체하는 고속·저용량 패키지 관리자
  • 주요 기능: npm과 동일 (명령어도 유사)
    • 예: pnpm install react
  • 특징:
    • *하드 링크 + *심볼릭 링크 구조를 사용해 패키지를 전역 저장소에 한 번만 설치하고, 각 프로젝트에서 참조
    • 중복 설치를 줄여 속도↑, 디스크 사용량↓
    • 의존성 충돌 방지: 프로젝트마다 독립적인 환경 유지
    • 모노레포(Monorepo) 환경 지원 강화
  • 단점:
    • npm 대비 상대적으로 역사가 짧아 일부 생태계 호환성 이슈가 있을 수 있음
*하드링크 : 같은 파일 데이터를 가리키는 또 다른 이름으로, 원본이 삭제돼도 데이터는 유지됨.
*심볼릭링크 : 원본 경로를 가리키는 포인터로, 원본이 삭제되면 링크가 깨짐.

 

 

3. npx (npm package executor)

  • 역할: 패키지를 설치하지 않고 바로 실행할 수 있는 도구
  • 주요 기능:
    • 로컬에 설치된 패키지의 실행 파일 호출
      • 예: npx jest → node_modules 안의 jest 실행
    • 설치 없이 일회성으로 실행
      • 예: npx create-react-app my-app → create-react-app 패키지를 다운로드 후 즉시 실행, 사용 후 폐기
  • 특징:
    • CLI 도구나 생성기(generator) 실행에 자주 사용
    • 설치를 최소화해 전역 환경 오염 방지

5. package.json 구성

1. package.json 예시

{
  "name": "my-project",               // 패키지 이름 (npm 배포 시 식별자)
  "version": "1.0.0",                  // 패키지 버전 (SemVer 규칙)
  "description": "React 기반 웹 앱",   // 프로젝트 설명
  "author": "홍길동 <hong@example.com>", // 작성자 정보
  "license": "MIT",                    // 라이선스 종류

  "main": "index.js",                  // CommonJS 환경 진입점
  "module": "index.mjs",               // ES Module 환경 진입점
  "type": "module",                    // 모듈 유형 ('commonjs' or 'module')

  "scripts": {                         // npm run <스크립트명> 으로 실행
    "start": "node index.js",          // 예: npm run start
    "build": "webpack",                // 예: npm run build
    "test": "jest"                     // 예: npm run test
  },

  "dependencies": {                    // 런타임(서비스 실행 시) 필요한 패키지
    "express": "^4.19.2",
    "react": "^18.2.0"
  },

  "devDependencies": {                 // 개발·빌드 시에만 필요한 패키지
    "webpack": "^5.89.0",
    "jest": "^29.7.0"
  },

  "peerDependencies": {                // 호스트 앱이 설치해야 하는 호환 패키지
    "react": ">=18"
  },

  "optionalDependencies": {            // 설치 실패해도 무시되는 선택적 의존성
    "fsevents": "^2.3.3"
  },

  "engines": {                          // 지원 Node.js / npm 버전
    "node": ">=18",
    "npm": ">=9"
  },

  "repository": {                       // 소스 코드 저장소 정보
    "type": "git",
    "url": "https://github.com/user/my-project.git"
  },

  "keywords": ["react", "frontend", "web"], // npm 검색 키워드
  "files": ["dist", "src/index.js"],    // npm 배포 시 포함할 파일
  "private": true                       // true면 npm 배포 방지
}

 

2. 버전 표기법


예: 1.0.0 → 주버전 1, 부버전 0, 패치 0

  • 주버전  : 하위 호환이 깨지는 변경
  • 부버전  : 하위 호환을 유지한 기능 추가
  • 패치  : 버그 수정/미세 변경

 

 

3. 범위 지정자 (dependencies 등)

비교 연산자 지정한 조건에 맞는 버전 설치 >=1.6.0 / < 2.0.0 /

>=1.6.0 <2.0.0
1.6.0 이상 / 2.0.0 미만 /

1.6.0 이상 2.0.0 미만
^ (캐럿) 주버전 유지 내에서 자동 업데이트,
단 0대 버전은 틸드와 동일
^5.89.0 / ^0.0.5 >=5.89.0 < 6.0.0 / >=0.0.5 < 0.0.6
~ (틸드) 부버전 유지 내에서 자동 업데이트 ~1.2.3 / ~1.2 >=1.2.3 < 1.3.0 / >=1.2.0 < 1.3.0
하이픈 범위 시작~끝 버전 범위 지정 1.2.3 - 1.4.0 >=1.2.3 <=1.4.0
X-Range / 와일드카드 일부 자리만 고정, 나머지는 자유 1.2.x / 1.x /  *   >=1.2.0 <1.3.0 / >=1.0.0 <2.0.0 /
모든 버전 허용(권장 X)
프리릴리스 사전배포 버전 지정 1.2.0-beta.2 일반 범위(^1.2.0)에는 자동 포함 안 됨, 정확히 지정 필요

 


6. monorepo ( workspace )

모노레포(Monolithic Repository)는 여러 개의 프로젝트(패키지)를 하나의 저장소(repository)에서 관리하는 방식입니다.


예를 들어, 프론트엔드 웹 앱, 백엔드 API 서버, UI 컴포넌트 라이브러리, 공용 유틸리티 모듈 등을 각각 별도의 저장소로 운영하는 대신, 하나의 Git 저장소 안에 디렉터리 구조로 함께 배치하는 것입니다.

 

아래 코드 예시로 monorepo 구조와 사용법을 확인하며 포스팅을 마치겠습니다.

 

 

1. 디렉터리 구조

.
├─ apps/
│  ├─ web/
│  │  └─ package.json
│  └─ admin/
│     └─ package.json
├─ packages/
│  ├─ ui/
│  │  └─ package.json
│  └─ utils/
│     └─ package.json
├─ pnpm-workspace.yaml
├─ package.json
└─ pnpm-lock.yaml

 

 

2. 워크스페이스 선언 ( pnpm )

//pnpm-workspace.yaml

packages:
  - "apps/*"
  - "packages/*"

 

 

3. 로컬 패키지 의존성 연결 (workspace: 프로토콜)

 

packages/ui/package.json

 
{
  "name": "@acme/ui",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "lint": "eslint ."
  }
}
 

 

packages/utils/package.json

 
{
  "name": "@acme/utils",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "lint": "eslint ."
  }
}
 

 

apps/web/package.json — 로컬 패키지를 workspace로 참조

{
  "name": "@acme/web",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "vite",
    "build": "tsc -p tsconfig.json && vite build",
    "test": "vitest",
    "lint": "eslint ."
  },
  "dependencies": {
    "@acme/ui": "workspace:^",        // 로컬 패키지 연결(호환범위 ^)
    "@acme/utils": "workspace:*",     // 동일 버전 강제하지 않고 최신 workspace 사용
    "react": "^18.2.0"
  }
}

 

workspace:*, workspace:^, workspace:~ 중 선택

  • * : 워크스페이스 최신
  • ^ : 주버전 호환
  • ~ : 부버전 호환