빌드노트
npm ci --omit=dev는 성공했는데 빌드 도구가 없다
npm ci --omit=dev가 성공해도 빌드에 필요한 개발 도구는 빠질 수 있다. 같은 lock의 설치·파일·빌드 결과를 비교하고, 실제 설치 조건을 확인해 빌드용 의존성과 운영용 의존성을 나누는 방법을 설명한다.
CI에서 npm ci --omit=dev는 성공했는데 이어지는 npm run build가 “명령을 찾을 수 없다”며 실패한다면, 그 명령을 제공하는 패키지가 devDependencies에 있는지 먼저 확인한다. 빌드에 필요한 도구를 설치에서 제외한 채 빌드를 실행했을 수 있다. lock 파일을 다시 만들어도 같은 제외 조건이면 도구는 설치되지 않는다.
package-lock.json에 기록된 패키지와 현재 node_modules에 설치된 패키지는 다를 수 있다. npm 공식 문서에서 omit=dev는 개발 의존성을 디스크 설치에서 제외하는 옵션이다. 제외한 의존성도 lock에는 기록된다. 설치 성공은 이 옵션에 맞게 설치했다는 뜻이지, 다음 빌드가 필요한 도구를 모두 갖췄다는 뜻은 아니다. npm ci의 omit 설명 (새 탭에서 열림)
같은 lock으로 설치해도 빌드 결과는 달랐다
이 차이를 확인하려고 외부 의존성 없는 작은 도구 패키지를 만들었다. 패키지 이름은 cb-omit-dev-tool이고, bin 항목으로 cb-omit-dev-build 명령을 제공한다. 이 명령은 dist/built.txt를 쓰고 BUILD_OK를 출력한다. 실제 번들러 대신 도구 실행 여부와 결과 파일만 확인하는 합성 예제다.
소비자는 이 패키지를 devDependencies의 file:../tool로 선언하고, scripts.build에서 cb-omit-dev-build를 호출한다. 패키지 이름과 명령 이름은 다르며, 둘의 연결은 도구 패키지의 bin에서 확인한다. package.json의 bin (새 탭에서 열림), 로컬 file 의존성 (새 탭에서 열림)
같은 package.json과 lock을 새 소비자 폴더 네 곳에 복사했다. 이전 node_modules나 결과 파일이 없는 상태에서 각각 설치와 빌드를 실행했다. 직접 확인한 환경은 Windows 11, PowerShell 7.6.6, Node.js 22.23.2, npm 10.9.8이다. 설치는 전용 캐시와 오프라인 설정을 사용했다. 로컬 패키지를 링크 대신 복사하도록 install-links=true를 lock 생성과 모든 설치에 동일하게 적용했다. install-links의 동작 (새 탭에서 열림)
NODE_ENV미설정,npm ci: 설치 종료 코드 0, 도구 패키지와 실행 파일 있음, 빌드 종료 코드 0. 결과 파일이 생성됐다.NODE_ENV미설정,npm ci --omit=dev: 설치 종료 코드 0, 도구 패키지와 실행 파일 없음, 빌드 종료 코드 1. 결과 파일도 없었다.NODE_ENV=production,npm ci: 별도 제외 옵션 없이도 두 번째 조건처럼 도구가 빠지고 빌드가 실패했다.NODE_ENV=production,npm ci --omit=dev --include=dev: 도구가 설치됐고 빌드가 성공했다.
실패한 두 빌드의 오류는 다음과 같았다. Windows의 npm 스크립트 셸에서 나온 원문이다.
'cb-omit-dev-build' is not recognized as an internal or external command,
operable program or batch file.
네 조건 모두 lock 안에는 도구 항목과 dev: true가 남아 있었다. 설치·빌드 전후의 lock 바이트도 같았다. 이 관찰에서 실패 원인은 lock 누락으로 볼 수 없다. 이 예제에서는 설치에서 제외된 도구를 다음 단계가 호출했다.
NODE_ENV=production이면 기본 omit 목록에 dev가 들어간다는 것은 문서에 명시된 규칙이다. 운영용 결과물을 만드는 빌드라도 개발 의존성에 둔 컴파일러나 번들러가 필요할 수 있으므로, 빌드 단계의 환경변수까지 함께 읽어야 한다. omit의 기본값 (새 탭에서 열림)
설치했던 조건과 실제 파일을 함께 확인한다
다음은 Node와 npm을 사용할 수 있는 Windows PowerShell에서 실행하는 조회다. 실패한 빌드가 사용한 package.json 폴더에서 시작한다. 두 경로의 패키지 이름과 명령 이름은 자기 프로젝트의 도구로 바꾼다. 이 예제는 일반적인 로컬 node_modules 설치를 기준으로 한다.
npm.cmd pkg get scripts.build devDependencies
npm.cmd config get omit
$env:NODE_ENV
Test-Path -LiteralPath './node_modules/cb-omit-dev-tool/package.json' -PathType Leaf
Test-Path -LiteralPath './node_modules/.bin/cb-omit-dev-build.cmd' -PathType Leaf
첫 명령으로 빌드가 호출하는 명령과 개발 의존성 선언을 대조한다. 여기서는 scripts.build가 cb-omit-dev-build, 개발 의존성이 cb-omit-dev-tool: file:../tool이었다. 마지막 두 명령은 기본 설치에서 True, True였고, 제외 설치 직후에는 False, False였다. npm pkg의 get (새 탭에서 열림)
npm config get omit은 지금 조회하는 호출의 설정을 보여 준다. 실험에서도 --omit=dev를 설치 명령에만 붙인 뒤 별도로 조회하자 빈 줄이었다. 그 조회가 과거 설치 옵션을 기억하는 것은 아니다. NODE_ENV=production인 자식 환경에서는 dev가 나왔다. 실제 설치 줄, 그 단계의 NODE_ENV·npm_config_omit, 적용되는 .npmrc, --include=dev 유무를 함께 확인한다. npm config get (새 탭에서 열림), 설정 입력과 우선순위 (새 탭에서 열림)
패키지와 실행 파일이 모두 없다면 설치 제외 조건을 조사할 근거가 된다. 반대로 패키지는 있는데 명령만 없다면 그 패키지의 bin 선언과 실행 파일 연결을 확인해야 한다. npm은 npm run의 검색 경로에 node_modules/.bin을 추가하지만, 설치하지 않은 도구를 만들어 주지는 않는다. 이번 실패는 일반 터미널에서 이름만 못 찾은 경우와 달리 npm run build에서도 발생했다. npm run-script의 실행 환경 (새 탭에서 열림)
빌드에는 도구를 포함하고 운영 설치는 따로 정한다
개발 의존성에 있는 도구가 빌드에 필요하다면, CI의 빌드용 설치에 --include=dev를 명시할 수 있다. 아래는 일치하는 package.json과 lock, 프로젝트 소스를 갖춘 빌드 작업 사본에서 실행한다. build는 자신의 빌드 스크립트 이름으로 맞추고, lock 생성에 썼던 다른 트리 구성 옵션도 유지한다. npm ci는 기존 node_modules를 제거하므로 실행 중인 서비스 폴더에서 시도하지 않는다. npm ci의 실행 조건 (새 탭에서 열림)
npm.cmd ci --include=dev
if ($LASTEXITCODE -ne 0) { throw 'Dependency installation failed' }
npm.cmd run build
if ($LASTEXITCODE -ne 0) { throw 'Build failed' }
앞서 제외 설치 때문에 실패했던 소비자 폴더에서 이 순서를 PowerShell로 실행하자 설치와 빌드가 모두 종료 코드 0이었고 BUILD_OK와 새 결과 파일을 확인했다. 설치나 빌드가 실패하면 다음 단계로 넘기지 않고 해당 오류를 읽는다. 도구가 설치된 뒤에도 빌드가 실패한다면 그 로그는 별도로 조사해야 한다.
include에 지정한 종류는 omit에도 있어도 포함된다. 위 네 번째 조건이 이를 확인했다. 다만 그 빌드가 출력한 NODE_ENV는 여전히 production이었다. 개발 의존성을 설치하는 선택과 빌드 프로그램이 읽는 환경변수는 별개다. 개발 의존성을 포함하려고 빌드 환경 전체를 무조건 development로 바꿀 필요는 없다. npm ci의 include 규칙 (새 탭에서 열림)
운영 단계는 결과물을 실행하는 데 무엇이 필요한지로 정한다. 정적 파일만 제공한다면 빌드 결과를 전달하면 되고, Node 서버에 운영 의존성이 필요하다면 별도 운영용 작업 디렉터리에서 npm ci --omit=dev로 설치하는 구성을 검토할 수 있다. 그 설치의 postinstall·prepare 등이 다시 개발 도구를 요구한다면 설치 과정 자체도 분리해야 한다. 도구 이름에 development가 들어간다는 이유로 빌드 전에 빼거나, 오류를 피하려고 모든 개발 도구를 운영 의존성으로 옮기지는 않는다.
직접 검증한 범위는 개발 의존성으로만 선언한 합성 도구 한 개의 설치·실행·결과 파일이다. 실제 번들러, 전이 의존성, 네이티브 모듈, Linux, 원격 CI와 운영 실행은 확인하지 않았다. 확인 당시 v10 웹 문서는 10.9.9로 표시되어, 실행 버전인 npm 10.9.8의 설정 정의 (새 탭에서 열림)에서도 omit·include 규칙을 대조했다.