본문 바로가기
빌드노트 검색

빌드노트

npm run --if-present는 없는 스크립트만 건너뛴다

npm run --if-present는 scripts에 없는 이름만 건너뛴다. 설치 없는 Node 예제로 성공·실패·누락을 옵션 유무별로 비교하고, stderr가 비어 있어도 종료 코드 7인 경우와 CI에 실패를 전달하는 방법을 확인한다.

공통 CI에서 선택적인 npm 스크립트에 --if-present를 붙였는데도 그 작업이 실패한다면, 옵션을 잘못 붙인 것부터 의심할 필요는 없다. 이 옵션이 허용하는 것은 스크립트의 누락이다. 이름이 등록되어 실행됐다면 그 안의 실패는 그대로 실패다.

npm v10의 if-present 설명 (새 탭에서 열림)은 기본값을 false로 두며, package.json의 scripts에 없는 이름을 요청했을 때 오류 종료하지 않도록 하는 옵션이라고 설명한다. 스크립트가 있으면 실행하고, 그 실행이 실패하면 실패하게 두는 용도다.

CI가 실행한 이름부터 확인한다

CI가 선택한 패키지의 package.json이 있는 폴더에서 다음 명령으로 등록된 스크립트를 확인한다. Windows PowerShell용 명령이며 Node.js와 npm이 설치되어 있어야 한다.

npm.cmd run

이름 없이 호출하면 스크립트 목록을 보여 준다. 작업을 실행하는 명령은 아니다. 아래 예제에서는 ok와 fail이 보였고 missing은 없었다. 자신의 CI가 요청한 이름이 목록에 있다면 --if-present는 그 작업을 생략해 주지 않는다. 실행된 명령의 오류를 조사해야 한다. 이름이 없다면 그 누락을 정말 허용할지, 이름을 잘못 적은 것인지 먼저 판단한다. npm run-script의 목록 조회 설명 (새 탭에서 열림)

같은 파일에서 성공·실패·누락을 비교했다

동작 차이를 확인하려면 기존 프로젝트 대신 새 빈 폴더에 다음 내용을 package.json으로 저장한다. 두 스크립트는 Node의 종료 코드만 정하므로 의존성 설치나 lock 파일이 필요하지 않다.

{
  "name": "if-present-lab",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "ok": "node -e \"process.exit(0)\"",
    "fail": "node -e \"process.exit(7)\""
  }
}

ok는 성공 상태인 0, fail은 의도한 실패 상태인 7로 끝난다. missing이라는 이름은 만들지 않는다. 이 파일을 바꾸지 않고 옵션 유무만 달리해 실행한 결과는 다음과 같았다. 실제 CI 장애 기록이 아니라 별도 합성 예제의 관찰이다.

  • npm.cmd run ok와 npm.cmd run ok --if-present: 둘 다 종료 코드 0. stdout에는 ok의 실행 배너와 명령이 나왔고 stderr는 비어 있었다.
  • npm.cmd run fail과 npm.cmd run fail --if-present: 둘 다 종료 코드 7. stdout에는 fail의 실행 배너와 명령이 나왔고 stderr는 비어 있었다.
  • npm.cmd run missing: 종료 코드 1. stdout은 비어 있었고 stderr에 npm error Missing script: "missing"이 나왔다. 같은 이름에 옵션을 붙인 npm.cmd run missing --if-present는 종료 코드 0이며 stdout과 stderr 모두 비어 있었다.

실패한 fail에서 오류 문구가 나오지 않았다는 점도 봐야 한다. 이 스크립트는 메시지 없이 process.exit(7)만 호출했고, npm도 stderr에 별도 오류를 쓰지 않았다. stderr가 비어 있다는 사실만으로 성공을 판단할 수 없다. 반대로 누락을 허용한 호출의 조용한 종료는 작업 완료를 뜻하지 않는다. 실행할 스크립트가 없었던 것이다.

직접 비교한 환경은 Windows 11, PowerShell 7.6.6, Node.js 22.23.2, npm 10.9.8이다. 자식 프로세스의 PATH에는 필요한 설치 도구 경로만 넣었고, 별도의 빈 npm 설정으로 사용자 설정의 영향을 분리했다. v10 웹 문서의 표시는 10.9.9였으며, 실제 실행 버전인 npm 10.9.8 태그의 if-present 설정 설명 (새 탭에서 열림)에서도 같은 규칙을 확인했다.

누락 허용과 실패 허용을 따로 정한다

여러 패키지에 같은 CI 명령을 적용하면서 일부 패키지에만 해당 작업이 있는 구성이라면 --if-present를 쓸 수 있다. 있는 패키지는 정상 실행해야 하고, 없는 패키지만 건너뛰는 정책이다. 반대로 모든 패키지가 반드시 제공해야 하는 작업이라면 옵션을 빼서 누락도 드러나게 한다.

PowerShell로 작성한 CI 단계에서는 npm의 종료 코드를 바로 보관하고 단계의 결과로 전달한다. 아래는 앞의 합성 예제에서 실패를 확인한 코드이며, CI용 PowerShell 스크립트의 마지막에 놓는 형태다. fail은 실제로 실행할 스크립트 이름으로 바꾼다.

npm.cmd run fail --if-present
$npmExit = $LASTEXITCODE
"npm exit=$npmExit"
exit $npmExit

예제에서는 실행 배너 뒤에 npm exit=7이 출력됐고, 이 PowerShell 프로세스도 7로 끝났다. exit는 보관한 npm 종료 코드를 호출자에게 전달한다. 이 값이 0인데 기대한 작업이 없었다면 이름 누락 여부부터 확인한다. 0이 아니라면 실행 로그와 스크립트 내부 명령을 확인한다. npm은 실패했는데 실제 CI가 성공한다면 CI의 실패 허용 설정과 바깥 스크립트의 종료 코드 전달을 볼 차례다.

작업이 존재해도 실패를 허용하려는 요구라면 별도의 정책이 필요하다. 예를 들어 선택적인 보고서 업로드를 독립 CI 단계로 두고, 그 단계가 실패했다는 결과와 로그를 남기면서 전체 작업을 계속할지 정할 수 있다. 명령 뒤에서 모든 실패를 성공으로 바꾸면 허용하려던 누락과 실제 실행 오류를 구별할 수 없게 된다. --if-present를 그 정책의 대용으로 쓰지 않는다.

여기서 실행한 범위는 자동 pre/post 스크립트가 없는 단일 패키지다. 다른 npm 버전, Linux·macOS, workspaces, 호스팅된 CI의 최종 상태 판정은 실행하지 않았다. 실패 시 정확히 7이 반환된 것은 이 예제의 관찰이며, 모든 종류의 오류가 같은 코드를 낸다는 뜻은 아니다.

← 전체 글 보기