npm ci --include=dev가 production 빌드에 필요했던 이유
PM2로 운영하는 Node.js 애플리케이션의 관리자 화면에 배포 기능을 붙였다.
서버 프로세스는 저장소를 최신 커밋으로 맞추고 의존성을 설치한 뒤 빌드했다.
git fetch origin main
git reset --hard <target-commit>
npm ci
npm run buildGit 동기화와 npm ci는 성공했지만 빌드는 실패했다.
npm run build failed (exit 127)
sh: vite: command not found처음 가졌던 의문
Vite는 이미 devDependencies에 있었다.
로컬의 npm ci는 개발 의존성까지 설치했기 때문에 처음에는 lock 파일이나 Vite 설정을 의심했다.
{
"scripts": {
"build": "npm run build:web && tsc -p tsconfig.build.json",
"build:web": "vite build"
},
"devDependencies": {
"typescript": "...",
"vite": "..."
}
}일반적인 개발 환경에서 npm ci가 devDependencies도 설치한다는 이해는 틀리지 않았다.
차이는 명령이 아니라 실행 환경에 있었다.
production 환경에서 달라진 설치
PM2 프로세스에는 다음 설정이 있었다.
env: {
NODE_ENV: 'production'
}Node.js가 별도의 env 없이 자식 프로세스를 실행하면 기본값으로 부모의 process.env를 사용한다.
따라서 셸과 그 안의 npm ci도 NODE_ENV=production을 물려받았다.
npm의 기본 omit 값은 NODE_ENV=production일 때 dev다.
모든 production 배포가 무조건 같은 것은 아니지만, 별도 설정이 없던 이번 실행은 사실상 아래와 같았다.
npm ci --omit=dev개발 의존성이 package-lock.json에서 삭제되는 것은 아니다.
npm은 계속 해석하고 lock 파일에 유지하되 디스크의 설치 트리에서만 제외한다.
npm ci 성공 뒤 빌드가 실패한 과정
npm ci의 성공은 설정에 맞는 설치를 끝냈다는 의미다.dev를 생략하도록 설정됐다면 Vite 없이도 성공한다.
또한 기존 node_modules를 먼저 제거하므로 전에 설치된 Vite도 남지 않는다.
npm run은 node_modules/.bin을 PATH에 추가할 뿐 패키지를 설치하지 않는다.node_modules/.bin/vite가 없으니 셸은 exit code 127과 command not found를 반환했다.
원인 확인 방법
같은 증상에서는 실행 환경과 설치 트리를 먼저 확인한다.
echo "$NODE_ENV"
npm config get omit
test -x node_modules/.bin/vite && echo "vite installed"
npm ls vite앞의 두 명령은 production과 dev, 뒤의 두 명령은 Vite의 실제 설치 여부를 보여준다.
잘못된 해결 방향
Vite를 dependencies로 옮기면 오류는 사라질 수 있다.
그러나 Vite와 TypeScript가 산출물을 만드는 데만 필요하다면 devDependencies가 올바른 분류다.
옮겨버리면 런타임 설치에 불필요한 도구까지 포함되고 빌드와 실행 단계의 차이도 가려진다.
적용한 해결 방법
빌드 단계에 개발 의존성이 필요하다고 명시했다.
npm ci --include=dev
npm run build같은 유형이 include와 omit에 모두 있으면 명령줄 순서와 무관하게 include가 우선한다.
따라서 런타임 환경은 production으로 유지하면서 Vite와 TypeScript를 설치해 빌드할 수 있었다.
배포 구조를 설계할 때의 교훈
단일 서버라면 npm ci --include=dev, 테스트와 빌드, PM2 재시작 순서로 구성할 수 있다.
CI/CD는 빌드 환경에서 산출물을 만들고 런타임에는 운영 의존성과 산출물만 전달하는 편이 명확하다.
Docker multi-stage build도 빌더에는 Vite를 두되 최종 이미지에는 개발 의존성을 남기지 않을 수 있다.
설치 명령은 현재 단계가 빌드인지 실행인지에 맞춰야 한다.
핵심 내용 요약
개발 환경의 npm ci가 개발 의존성도 설치한다는 이해는 맞다.
다만 NODE_ENV=production을 상속하면 기본 omit=dev가 적용될 수 있다.
설치 성공은 빌드 도구의 존재를 보장하지 않는다.
NODE_ENV=production
→ npm ci가 dev dependencies 생략
→ vite 미설치
→ npm run build 실패
→ npm ci --include=dev로 해결참고 자료



























