Alert

이 글은 Claude Code의 도움을 받아 작성되었습니다

알아볼 내용

  1. Node.js와 JavaScript 생태계 구조
  2. npm — 패키지 매니저의 기본
  3. package.json / package-lock.json
  4. node_modules
  5. pnpm — npm의 대안
  6. yarn — 또 다른 대안
  7. 버전 표기법 (SemVer)
  8. 글로벌 설치 vs 로컬 설치
  9. npx와 실행 도구

Python 개발자를 위한 대응 관계표

  • Python(uv)을 기준으로 Node.js 생태계를 빠르게 매핑하면 아래와 같음
개념Python (uv)Node.js
언어PythonJavaScript / TypeScript
런타임python 인터프리터Node.js
패키지 매니저uvnpm, pnpm, yarn
패키지 저장소PyPInpm registry
의존성 정의 파일pyproject.tomlpackage.json
버전 잠금 파일uv.lockpackage-lock.json, pnpm-lock.yaml
패키지 설치 위치.venv/lib/site-packages프로젝트의 node_modules
스크립트 실행uv run python script.pynpm run, pnpm run
일회성 실행uvxnpx

Node.js와 JavaScript 생태계

JavaScript
  • 원래 웹 브라우저 안에서만 돌아가는 스크립트 언어로 탄생
  • Node.js의 등장 이후 서버, CLI 도구, 데스크톱 앱 등 범용 언어로 확장
  • TypeScript는 JavaScript에 정적 타입을 추가한 상위 언어로, 컴파일하면 JavaScript가 됨
Node.js
  • JavaScript를 브라우저 밖에서 실행하게 해주는 런타임 환경
  • Python에서 python 인터프리터가 .py 파일을 실행하는 것처럼, Node.js가 .js 파일을 실행
  • V8 엔진(Chrome에서 사용하는 JS 엔진) 기반으로 빠른 실행 속도
왜 브라우저 밖에서 JS를 실행해야 하나
  • JS를 잘 쓰는 프론트엔드 개발자가 이미 매우 많았고, “프론트도 백엔드도 같은 언어로 하고 싶다”는 수요에서 Node.js가 탄생 (2009년)
  • 가장 큰 실용적 이유는 프론트엔드 빌드 도구를 돌리기 위해서
    • React, Vue, TypeScript 등을 브라우저가 이해하는 JS로 변환(빌드)하는 과정 자체가 Node.js 위에서 실행
    • npm run build → Node.js가 webpack/vite를 실행하여 JSX/TS를 순수 JS로 변환
    • 프론트엔드만 해도 Node.js가 필수인 이유
Node.js로 할 수 있는 것
용도가능 여부예시
웹 서버 / API 서버Express, Fastify, NestJS
파일 읽기/쓰기fs 모듈 (Python의 open()과 동일)
DB 연결PostgreSQL, MongoDB, Redis 등
CLI 도구eslint, prettier, Claude Code 자체도 Node.js
빌드 도구webpack, vite, tsc (TypeScript 컴파일러)
스크립트/자동화Python 스크립트 대신 JS로 작성
데스크톱 앱Electron (VS Code, Slack, Discord가 이걸로 만듦)
데이터 과학 / ML❌ 약함이 영역은 Python이 압도적 (numpy, pandas, pytorch 등)
시스템 프로그래밍❌ 약함저수준 작업은 C/Rust 영역
브라우저 JS vs Node.js — 같은 언어, 다른 환경
  • 같은 JavaScript 문법이지만 사용할 수 있는 API가 다름
  • Python도 Jupyter에서의 Python과 서버에서 돌리는 Python이 같은 언어지만 환경에 따라 쓸 수 있는 기능이 다른 것과 비슷
브라우저 JSNode.js
DOM 조작document.getElementById()❌ 브라우저가 없으니 DOM도 없음
화면 그리기✅ HTML/CSS 렌더링
파일 시스템❌ 보안상 접근 불가fs.readFile()
네트워크 서버http.createServer()
OS 정보os.cpus(), os.platform()
프로세스 실행child_process.exec()
Node.js 기본 사용
# Node.js 버전 확인
node --version
# v20.11.0
 
# JavaScript 파일 실행 (uv run python app.py와 동일한 감각)
node index.js
 
# REPL 모드 (python 입력 시 대화형 모드와 동일)
node
# > console.log("Hello")
# Hello
프론트엔드 프레임워크와의 관계
  • React, Next.js, Vue.js 등도 결국 npm/pnpm으로 설치하는 Node.js 패키지
  • 개발 시에는 Node.js 환경에서 빌드하고, 빌드 결과물(HTML, CSS, JS)이 브라우저에서 실행됨
  • 프론트엔드 프로젝트를 열어도 package.json, node_modules가 있는 이유

npm (Node Package Manager)

개념
  • Node.js의 기본 패키지 매니저로 Node.js 설치 시 함께 설치됨
  • 역할
    • JavaScript/TypeScript 패키지 설치, 업데이트, 제거
    • 프로젝트 의존성 버전 관리
    • 스크립트 실행 인터페이스 (npm run ...)
  • Python의 uv와 가장 유사하지만, 스크립트 실행 기능까지 포함
npm 핵심 명령어
npm 명령어 (uv 비교 포함)
# 프로젝트 초기화 — package.json 생성
npm init          # 질문에 답하며 설정 (uv init과 유사)
npm init -y       # 기본값으로 빠르게 생성
 
# 의존성 설치
npm install                    # package.json 기반 전체 설치 (uv sync)
npm install axios              # 런타임 의존성 추가 (uv add requests)
npm install -D jest            # 개발용 의존성 추가 (uv add --dev pytest)
npm install axios@1.6.0        # 특정 버전 설치 (uv add requests==2.31.0)
 
# 패키지 제거
npm uninstall axios            # 패키지 삭제 (uv remove requests)
 
# 스크립트 실행 (package.json의 "scripts"에 정의된 명령)
npm run dev                    # 개발 서버 실행
npm run build                  # 프로덕션 빌드
npm run test                   # 테스트 실행
npm start                      # "start" 스크립트는 run 생략 가능
 
# 설치된 패키지 확인
npm list                       # 전체 의존성 트리 (uv pip list)
npm list --depth=0             # 직접 설치한 패키지만
npm outdated                   # 업데이트 가능한 패키지 확인

npm run과 uv run의 차이

  • npm run dev : package.json"scripts"미리 정의된 명령어 별칭을 실행
  • uv run python app.py : 가상환경 내에서 임의의 명령어를 실행
  • Node.js에서는 "scripts"에 자주 쓰는 명령을 등록하고 npm run으로 실행하는 것이 관례
// package.json
"scripts": {
    "dev": "next dev",        // npm run dev → next dev 실행
    "build": "next build"     // npm run build → next build 실행
}
  • Python(uv)에는 이런 스크립트 등록 기능이 내장되어 있지 않아서 pyproject.toml[project.scripts]나 외부 도구를 사용

package.json — 프로젝트의 모든 것을 담는 설정 파일

역할
  • Python의 pyproject.toml과 동일한 위치의 파일
  • 프로젝트 메타데이터, 의존성 목록, 실행 스크립트를 모두 포함
구조 상세 설명
package.json 예시
{
  "name": "my-app",              // 프로젝트 이름
  "version": "1.0.0",            // 프로젝트 버전
  "description": "My awesome app",
  "main": "index.js",            // 진입점 파일
 
  "scripts": {                   // 실행 가능한 스크립트 정의
    "dev": "next dev",           // npm run dev → next dev 실행
    "build": "next build",       // npm run build → 프로덕션 빌드
    "start": "next start",       // npm start → 프로덕션 서버
    "test": "jest",              // npm run test → 테스트 실행
    "lint": "eslint ."           // npm run lint → 코드 검사
  },
 
  "dependencies": {              // 런타임에 필요한 패키지
    "react": "^18.2.0",          // ^ : 마이너/패치 업데이트 허용
    "next": "~14.0.0",           // ~ : 패치 업데이트만 허용
    "axios": "1.6.0"             // 버전 고정
  },
 
  "devDependencies": {           // 개발 시에만 필요한 패키지
    "jest": "^29.7.0",           // 테스트 프레임워크
    "eslint": "^8.56.0",         // 코드 린터
    "typescript": "^5.3.0"       // 타입스크립트 컴파일러
  }
}
dependencies vs devDependencies
항목dependenciesdevDependencies
용도앱 실행에 필요한 패키지개발/테스트/빌드에만 필요한 패키지
설치 명령npm install axiosnpm install -D jest
프로덕션 배포 시포함됨제외 가능 (npm install --production)
uv 비교uv add requestsuv add --dev pytest
예시react, axios, expressjest, eslint, typescript, webpack
pyproject.toml과 나란히 비교
pyproject.toml (uv)
[project]
name = "my-app"
version = "1.0.0"
description = "My awesome app"
dependencies = [            # ← package.json의 "dependencies"
    "requests>=2.31.0",
    "fastapi>=0.100.0",
]
 
[dependency-groups]
dev = [                     # ← package.json의 "devDependencies"
    "pytest>=7.0.0",
    "ruff>=0.1.0",
]
 
[project.scripts]           # ← package.json의 "scripts" (제한적)
my-app = "my_app:main"     # CLI 진입점만 정의 가능

package-lock.json — 정확한 버전 스냅샷

역할
  • package.json"axios": "^1.6.0" 처럼 범위로 버전을 명시
  • package-lock.json은 실제 설치된 정확한 버전전체 의존성 트리를 기록
  • 팀원 모두가 완전히 동일한 의존성 환경을 재현하기 위한 파일
중요한 규칙
  • 반드시 git에 커밋해야 함 (.gitignore에 넣지 않음)
  • 직접 수정하지 않음 — npm install 시 자동 생성/업데이트
  • Python의 uv.lock과 동일한 역할
파일역할git에 포함직접 수정uv 대응
package.json”이런 패키지가 필요해” (범위 버전)pyproject.toml
package-lock.json”실제로 이 버전이 설치됐어” (정확한 버전)uv.lock
node_modules실제 패키지 파일들.venv/lib/site-packages

node_modules — 패키지가 실제로 설치되는 폴더

개념
  • npm install 또는 pnpm install 실행 시 프로젝트 루트에 생성되는 폴더
  • 프로젝트에서 사용하는 모든 패키지와 그 하위 의존성이 설치되는 장소
  • Python에서 .venv/lib/python3.x/site-packages와 동일한 역할
특징
  • 매우 큰 용량 : 작은 프로젝트도 수백 MB에 달할 수 있음
    • 패키지 하나를 설치해도 그 패키지의 의존성, 의존성의 의존성… 이 모두 포함
  • 반드시 .gitignore에 추가 : Python에서 .venv를 git에 올리지 않는 것과 동일
  • 언제든 재생성 가능 : 삭제 후 npm install로 다시 설치 가능
node_modules 관리
# .gitignore에 추가 (필수)
echo "node_modules/" >> .gitignore
 
# node_modules가 꼬였을 때 — 삭제 후 재설치 (uv에서 .venv 삭제 후 uv sync 하는 것과 동일)
rm -rf node_modules
npm install
 
# node_modules 용량 확인
du -sh node_modules
# 487M    node_modules    ← 흔한 광경...
 
# 프로젝트별 node_modules 한번에 정리 (디스크 절약)
npx npkill            # 터미널 UI에서 node_modules 폴더를 선택적으로 삭제
npm의 node_modules 구조 (Flat 방식)
npm의 의존성 구조
node_modules/
├── axios/              # 직접 설치한 패키지
├── follow-redirects/   # axios의 의존성이지만 최상위에 배치 (hoisting)
├── form-data/          # axios의 의존성
└── proxy-from-env/     # axios의 의존성
 
→ npm은 의존성을 최대한 최상위(flat)로 올려서 중복을 줄임
→ 단점: 직접 설치하지 않은 패키지도 import할 수 있는 문제 (유령 의존성)

pnpm — 더 빠르고 엄격한 패키지 매니저

개념
  • npm과 동일한 역할이지만 디스크 효율성의존성 엄격성에 초점을 맞춘 대안
pnpm의 핵심 차별점
  • 공용 스토어(Content-Addressable Store) 기반
    • 패키지 파일을 전역 스토어(~/.pnpm-store)에 한 번만 저장
    • 각 프로젝트의 node_modules에는 하드링크로 연결
    • 10개 프로젝트에서 같은 버전의 React를 써도 디스크에는 1벌만 존재
    • uv가 전역 캐시(~/.cache/uv)를 활용하는 것과 유사한 접근
  • 유령 의존성(Phantom Dependency) 차단
    • npm은 의존성을 flat하게 올리기 때문에 package.json에 선언하지 않은 패키지도 실수로 import 가능
    • pnpm은 직접 선언한 패키지만 접근 가능하게 제한 → 더 안전한 의존성 관리
pnpm의 node_modules 구조
node_modules/
├── .pnpm/                      # 실제 패키지들 (하드링크)
│   ├── axios@1.6.0/
│   │   └── node_modules/
│   │       ├── axios/          # 실제 파일 (스토어에서 하드링크)
│   │       └── follow-redirects/  # axios의 의존성
│   └── follow-redirects@1.15.0/
└── axios -> .pnpm/axios@1.6.0/node_modules/axios   # 심볼릭 링크
 
→ 직접 설치한 axios만 최상위에 심볼릭 링크로 노출
→ follow-redirects는 직접 import 불가 (유령 의존성 차단)
pnpm 핵심 명령어
pnpm 명령어 (npm과 비교)
# 프로젝트 초기화
pnpm init                      # npm init과 동일
 
# 의존성 설치
pnpm install                   # npm install
pnpm add axios                 # npm install axios
pnpm add -D jest               # npm install -D jest
pnpm remove axios              # npm uninstall axios
 
# 스크립트 실행
pnpm run dev                   # npm run dev
pnpm dev                       # pnpm은 run 생략 가능
 
# 설치된 패키지 확인
pnpm list                      # npm list
pnpm outdated                  # npm outdated
npm vs pnpm 비교
항목npmpnpm
설치 속도보통빠름 (캐시 + 하드링크)
디스크 사용량프로젝트마다 복사전역 스토어에서 하드링크 → 절약
유령 의존성허용됨 (flat 구조)차단됨 (엄격한 구조)
모노레포 지원workspace 지원더 효율적인 workspace 지원
잠금 파일package-lock.jsonpnpm-lock.yaml
Node.js 기본 제공❌ (별도 설치 필요)

Yarn — Facebook이 만든 패키지 매니저

개념
  • 2016년 Facebook(현 Meta)이 npm의 속도/보안 문제를 개선하기 위해 만든 패키지 매니저
  • npm, pnpm과 동일한 역할을 하는 또 하나의 선택지
  • 현재는 Yarn Classic(v1)과 Yarn Berry(v2+) 두 갈래로 나뉨
Yarn 핵심 명령어
Yarn 명령어 (npm과 비교)
# 프로젝트 초기화
yarn init                      # npm init
 
# 의존성 설치
yarn                           # npm install (yarn install도 가능)
yarn add axios                 # npm install axios
yarn add -D jest               # npm install -D jest
yarn remove axios              # npm uninstall axios
 
# 스크립트 실행
yarn dev                       # npm run dev (run 생략 가능)
yarn build                     # npm run build
npm vs pnpm vs yarn 한눈에 비교
항목npmpnpmyarn
개발 주체npm, Inc (GitHub)커뮤니티Meta (Facebook)
설치Node.js 기본 포함별도 설치별도 설치
잠금 파일package-lock.jsonpnpm-lock.yamlyarn.lock
설치 명령npm installpnpm installyarn
추가 명령npm install pkgpnpm add pkgyarn add pkg
디스크 효율낮음높음 (하드링크)보통 (v1) / PnP(v2+)
주 사용처범용 (기본값)모노레포, 대규모React/Meta 생태계

어떤 패키지 매니저를 쓸까?

  • 프로젝트에 이미 잠금 파일이 있으면 그에 맞는 매니저 사용
    • package-lock.json → npm
    • pnpm-lock.yaml → pnpm
    • yarn.lock → yarn
  • 새 프로젝트라면 npm(기본, 무난)이나 pnpm(빠름, 엄격)을 추천
  • 하나의 프로젝트에서 여러 패키지 매니저를 섞어 쓰면 안 됨

버전 표기법 — SemVer (Semantic Versioning)

개념
  • npm 생태계에서 패키지 버전은 SemVer 규칙을 따름
  • 형식 : MAJOR.MINOR.PATCH (예: 1.6.2)
  • Python(PyPI)도 동일한 규칙을 따르지만, npm에서 특히 ^, ~ 같은 범위 기호를 자주 사용
구분의미올라가는 경우
MAJOR주 버전하위 호환이 깨지는 변경 (Breaking Change)
MINOR부 버전하위 호환을 유지하며 기능 추가
PATCH패치 버전버그 수정
package.json에서의 버전 범위 기호
버전 범위 기호 예시
{
  "dependencies": {
    "axios": "1.6.2",       // 정확히 1.6.2만 설치
    "react": "^18.2.0",     // ^(캐럿) : 18.x.x 허용 (MAJOR 고정, MINOR/PATCH 유동)
    "lodash": "~4.17.0",    // ~(틸드) : 4.17.x 허용 (MAJOR.MINOR 고정, PATCH만 유동)
    "express": ">=4.0.0",   // 4.0.0 이상
    "webpack": "*"          // 어떤 버전이든 (비추천)
  }
}
기호예시허용 범위설명
(없음)1.6.21.6.2만정확히 고정
^^1.6.21.6.2 ~ 1.x.xMAJOR 고정, 가장 많이 사용
~~1.6.21.6.2 ~ 1.6.xMINOR까지 고정
>=>=1.6.21.6.2 이상 전부범위 지정
**모든 버전위험 — 사용 비추천

uv에서의 버전 범위 비교

# pyproject.toml (uv)
dependencies = [
    "requests>=2.31.0",           # npm의 ">=2.31.0"과 동일
    "requests>=2.31.0,<3.0.0",   # npm의 "^2.31.0"과 유사
    "requests~=2.31.0",          # npm의 "~2.31.0"과 유사 (Compatible release)
    "requests==2.31.0",          # npm의 "2.31.0"과 동일 (정확히 고정)
]

글로벌 설치 vs 로컬 설치

로컬 설치 (기본, 권장)
  • 프로젝트의 node_modules에 설치
  • 프로젝트 간 의존성이 격리됨
  • uv가 .venv에 패키지를 설치하는 것과 동일한 개념
로컬 설치
npm install axios              # ./node_modules/axios 에 설치
글로벌 설치
  • 시스템 전역에 설치하여 어디서든 CLI 명령으로 사용 가능
  • uv에서 uv tool install로 CLI 도구를 전역 설치하는 것과 유사
글로벌 설치
npm install -g typescript      # 어디서든 tsc 명령 사용 가능
npm install -g vercel          # 어디서든 vercel 명령 사용 가능
 
# 글로벌 설치 목록 확인
npm list -g --depth=0
 
# 글로벌 설치 경로 확인
npm root -g
# /usr/local/lib/node_modules
uv에서 대응되는 명령어
# uv로 CLI 도구 전역 설치
uv tool install ruff           # 어디서든 ruff 명령 사용 가능
uv tool list                   # 전역 설치된 도구 목록

글로벌 설치는 최소한으로

  • 글로벌 설치는 프로젝트 간 버전 충돌 위험이 있음
  • 가능하면 로컬 설치 + npx로 실행하는 것을 권장
  • 글로벌로 설치할 만한 것: CLI 도구 (vercel, netlify-cli 등)

npx — 패키지를 설치하지 않고 실행

개념
  • npm 5.2+부터 함께 제공되는 패키지 실행 도구
  • 글로벌 설치 없이 패키지를 임시로 다운로드하여 한 번 실행하고 버림
  • uv의 uvx와 동일한 개념
npx vs uvx 비교
# Node.js — npx
npx create-next-app my-app     # Next.js 프로젝트 생성 도구를 임시로 받아 실행
npx prettier --write .         # 코드 포맷터를 설치 없이 실행
npx tsc --init                 # TypeScript 설정 파일 생성
 
# Python — uvx (동일한 개념)
uvx ruff check .               # ruff를 설치 없이 실행
uvx black .                    # black 포맷터를 설치 없이 실행
npx 추가 활용
# 프로젝트 생성 도구 (가장 흔한 용도)
npx create-react-app my-app          # React 프로젝트 생성
npx create-next-app my-app           # Next.js 프로젝트 생성
 
# 특정 버전으로 실행
npx node@18 index.js                 # Node.js 18 버전으로 실행
 
# 로컬에 설치된 패키지의 CLI 실행
# node_modules/.bin/jest를 직접 쓸 필요 없이
npx jest                             # 로컬 jest 실행

워크플로 비교 — Python (uv) vs Node.js

Python (uv)
Python 프로젝트 시작
mkdir my_app && cd my_app
uv init                            # pyproject.toml 생성 + .venv 자동 생성
uv add requests                    # 패키지 설치
uv run python app.py               # 가상환경 내에서 실행
Node.js (npm)
Node.js 프로젝트 시작
mkdir my_app && cd my_app
npm init -y                        # package.json 생성
npm install axios                  # 패키지 설치 → node_modules 생성
node index.js                      # 실행
Node.js (pnpm)
pnpm 프로젝트 시작
mkdir my_app && cd my_app
pnpm init                          # package.json 생성
pnpm add axios                     # 패키지 설치
node index.js                      # 실행 (런타임은 동일하게 node)

Python과의 핵심 차이

  • Python(uv)은 가상환경 활성화 또는 uv run이 필요하지만, Node.js는 node_modules가 프로젝트 로컬에 있어서 별도의 활성화가 불필요
  • Node.js에서 require() 또는 import는 자동으로 가까운 node_modules를 탐색
  • uv의 .venv ↔ Node.js의 node_modules : 둘 다 프로젝트 로컬에 패키지를 격리하는 같은 역할

실무에서 Node.js 프로젝트를 처음 열었을 때 체크리스트

프로젝트 파악 순서
# 1. 어떤 패키지 매니저를 쓰는지 확인
ls package-lock.json    # 있으면 → npm
ls pnpm-lock.yaml       # 있으면 → pnpm
ls yarn.lock            # 있으면 → yarn
 
# 2. 의존성 설치
npm install             # 또는 pnpm install, yarn
 
# 3. 사용 가능한 스크립트 확인
cat package.json | grep -A 20 '"scripts"'
# "dev", "build", "start", "test" 등 확인
 
# 4. 개발 서버 실행
npm run dev             # 또는 pnpm dev, yarn dev