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

빌드노트

engines에 Node 버전을 적었는데 npm install이 성공한다

engines의 Node 버전이 맞지 않아도 기본 npm install은 경고만 남기고 성공할 수 있다. 같은 로컬 패키지에서 기본·엄격 설치의 종료 코드와 파일을 비교하고, 설치 정책과 실행 시 버전 검사를 구분한다.

package.json의 engines.node와 현재 Node 버전이 다른데도 npm install이 성공할 수 있다. npm warn EBADENGINE만 출력됐다면 먼저 설치 정책을 확인한다. engines는 호환 버전 선언이고, 불일치를 설치 실패로 처리하는 설정은 engine-strict다. 이 설정도 이미 설치한 프로그램을 실행할 때의 버전 검사까지 대신하지는 않는다.

npm 공식 문서는 의존 패키지의 engines가 기본적으로 경고를 위한 선언이라고 설명한다. engine-strict의 기본값은 false이고, true이면 현재 Node와 호환되지 않는다고 선언한 패키지의 설치를 거부한다. engines 설명 (새 탭에서 열림), engine-strict 설정 (새 탭에서 열림)

경고의 required와 current를 먼저 읽는다

실제 설치 로그의 required는 패키지가 선언한 범위이고, current는 설치에 사용한 Node·npm 버전이다. 함께 출력된 패키지 이름도 확인한다. 의존 패키지가 낸 경고라면 루트 프로젝트의 engines만 바꿔서는 그 선언이 바뀌지 않는다.

실패한 설치와 같은 작업 환경에서 버전과 현재 설정을 조회한다. 다음은 Node.js와 npm이 설치된 Windows PowerShell용 명령이다. 프로젝트의 package.json이 있는 폴더에서 실행하며, 설치나 스크립트 실행은 하지 않는다.

node --version
npm.cmd --version
npm.cmd config get engine-strict
npm.cmd config get force

이번 격리 환경에서는 차례로 v22.23.2, 10.9.8, false, false가 나왔다. 이 조건이면 버전 불일치가 경고로 끝나는 것을 예상할 수 있다. 다만 별도 조회는 현재 호출의 설정이다. 앞선 설치 명령에만 붙인 --engine-strict를 기억하지 않으므로, CI의 실제 설치 줄과 그 단계의 환경변수·적용되는 .npmrc도 함께 읽는다. npm은 명령행, 환경변수, npmrc 등에서 설정을 받는다. npm 설정 입력 (새 탭에서 열림)

force도 확인할 이유가 있다. 공식 문서상 --force는 engine-strict의 거부 동작을 우회할 수 있다. 엄격하게 검사하려는 설치에 이 옵션이 함께 들어가 있으면 제거하고 의도한 정책을 다시 확인한다. 이 우회 조합은 이번에 실행하지 않았다. force 설명 (새 탭에서 열림)

Node를 바꾸지 않고 설치 정책만 비교했다

루트 패키지에서도 같은 차이가 나는지 확인하려고, 현재 Node가 만족할 수 없는 >=999.0.0을 넣었다. 실제 지원 버전의 권장값이 아니라 불일치를 확실히 만드는 합성 입력이다. 다음 내용을 package.json으로 저장하면 별도 의존성 없이 비교할 수 있다.

{
  "name": "engines-lab",
  "version": "1.0.0",
  "private": true,
  "engines": { "node": ">=999.0.0" }
}

직접 비교하려면 기존 프로젝트 밖의 빈 폴더 두 곳에 같은 파일을 놓고 시작한다. 두 폴더 모두 package-lock.json과 node_modules가 없는 상태이며, 기본 조회에서 engine-strict=false, force=false인 조건이다. 이번 실행은 사용자 설정 대신 빈 실험용 설정과 전용 캐시를 사용했고, 오프라인으로 진행했다.

첫 번째 폴더에서는 기본 설치의 종료 코드를 확인한다. --ignore-scripts는 설치 스크립트를 실행하지 않도록 두 비교에 똑같이 붙인 옵션이다. 엔진 검사를 끄는 옵션은 아니다. ignore-scripts 설명 (새 탭에서 열림)

npm.cmd install --ignore-scripts
$LASTEXITCODE

두 번째 새 폴더에서는 같은 선언을 엄격하게 검사한다. $LASTEXITCODE는 바로 앞 npm 명령의 종료 코드를 읽는다.

npm.cmd install --ignore-scripts --engine-strict
$LASTEXITCODE

실제 결과는 다음과 같았다.

  • 기본 설치는 npm warn EBADENGINE Unsupported engine을 출력했지만 종료 코드는 0이었다. required는 >=999.0.0, current는 Node v22.23.2와 npm 10.9.8이었고, 새 package-lock.json이 생겼다.
  • 엄격 설치는 npm error code EBADENGINE을 출력하고 종료 코드 1로 끝났다. 이 새 폴더에는 lock 파일이 생기지 않았다.

위 패키지는 의존성이 없으므로 두 경우 모두 node_modules는 없었다. 설치할 파일이 있는 경우도 확인하려고, 같은 불일치 선언을 넣은 자체 패키지를 로컬 tarball 의존성으로 설치했다. 기본 설치는 종료 코드 0과 added 1 package를 출력했고 해당 패키지 파일이 생겼다. 엄격 설치는 종료 코드 1이었고 패키지 파일도 없었다. 따라서 기본 설치의 성공을 단지 “설치할 의존성이 없었기 때문”으로 설명할 수는 없다.

반대로 루트 선언을 현재 Node에 맞는 >=22.0.0 <23.0.0으로 바꾼 새 폴더에서는 엄격 설치도 경고 없이 종료 코드 0이었다. 실험이 보여 준 차이는 엄격 옵션 자체의 오류가 아니라 현재 버전과 선언 범위의 일치 여부를 설치 실패로 취급하는가였다.

자신의 실행이 처음부터 실패한다면 설정이 이미 엄격한지 확인한다. 다른 오류 코드로 실패했다면 그 오류를 먼저 조사한다. 엄격 설치의 EBADENGINE이 확인되면 해당 패키지의 지원 범위에 맞는 Node를 빌드 환경에 준비하거나, 그 환경을 지원하는 패키지 버전을 선택한다. 설치를 통과시키려고 확인 없이 선언 범위만 넓히지는 않는다. 명령에 붙인 --engine-strict는 그 호출에 적용되므로 CI에서 강제하려면 실제 설치 단계에도 반영해야 한다.

설치한 뒤 실행할 Node는 별도로 확인한다

경고 후 설치한 합성 패키지를 node로 직접 불러오자 종료 코드 0으로 문자열을 반환했다. 패키지에는 여전히 node >=999.0.0 선언이 있었다. 이 관찰은 engines가 해당 실행을 차단하지 않았다는 근거다. 패키지는 문자열만 내보내므로, 실제로 새 Node 기능이 필요한 코드도 구버전에서 정상 동작한다는 뜻은 아니다.

빌드할 때의 Node와 실제 프로그램을 실행하는 Node가 같은지도 따로 확인한다. 예를 들어 Node 22.x만 허용하는 CommonJS 프로그램이라면, 다른 require 호출보다 앞선 시작 지점에 다음 검사를 둘 수 있다. process.versions.node는 실행 중인 Node의 버전 문자열을 제공한다. Node.js process 문서 (새 탭에서 열림)

if (Number(process.versions.node.split('.')[0]) !== 22) {
  throw new Error('Node.js 22.x required');
}

이 코드만 runtime-check.cjs로 저장해 node ./runtime-check.cjs로 실행하면 현재 Node가 검사에 통과하는지 확인할 수 있다. 이번 Node 22.23.2에서는 출력 없이 종료 코드 0이었다. 같은 코드의 허용 메이저와 오류 문구를 999로 바꾼 비교에서는 Node.js 999.x required 오류와 종료 코드 1을 확인했다. 전체 버전 범위를 해석하는 코드는 아니므로, 패치 최소 버전이나 여러 허용 범위가 필요하면 프로젝트의 실제 조건에 맞는 검사로 바꿔야 한다.

직접 확인한 범위는 Windows, PowerShell 7.6.6, Node.js 22.23.2, npm 10.9.8의 새 로컬 패키지 설치와 직접 Node 실행이다. 설치 스크립트는 모두 껐다. npm 11의 문서에서도 같은 기본 정책을 확인했지만 npm 11 실행, 다른 OS, 원격 CI, 실제 앱의 시작 과정과 네이티브 모듈 호환성은 검증하지 않았다. 실행 버전에 대해서는 npm 10.9.8의 설정 정의 (새 탭에서 열림)도 대조했다. npm 11 engine-strict 문서 (새 탭에서 열림)

← 목록으로 돌아가기