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

빌드노트

npm install --package-lock-only는 끝났는데 새 패키지를 import할 수 없다

package-lock-only로 lock을 갱신해도 실제 패키지 파일은 설치되지 않는다. 새 로컬 tarball에서 파일·resolve·import의 실패와 실제 설치 뒤 성공을 비교하고, 현재 옵션 값과 프로젝트 npmrc를 확인해 필요한 설치를 이어서 수행한다.

npm install --package-lock-only가 성공하고 package-lock.json에 새 패키지도 생겼는데, 실행하면 ERR_MODULE_NOT_FOUND가 날 수 있다. 이 옵션은 실제 패키지를 node_modules에 설치하는 단계를 생략한다. lock에 의존성이 기록됐다는 사실과 지금 실행할 파일이 있다는 사실은 따로 확인해야 한다.

npm v10 설치 문서 (새 탭에서 열림)는 이 옵션이 node_modules를 확인하며 설치하는 대신 lock을 갱신한다고 설명한다. 기본값은 false다. npm v11 문서 (새 탭에서 열림)에도 같은 경계가 명시돼 있다. 여기서 성공 종료는 요청한 lock 작업이 끝났다는 뜻으로 읽어야 한다.

내 프로젝트에서는 무엇을 비교할까

Node.js와 npm이 설치된 Windows PowerShell에서, 문제가 난 코드를 실행하는 프로젝트의 package.json이 있는 폴더로 이동한다. 다음은 일반적인 단일 패키지 프로젝트의 진단이다. lock-only-proof는 이 글의 실험 패키지 이름이므로, 세 곳 모두 실제 실패한 패키지 이름으로 바꾼다. 마지막 줄은 파일 위치 조회에 그치지 않고 모듈을 실제로 불러온다.

현재 옵션 값, lock 항목, 디스크의 파일, 실제 import 결과를 차례로 비교한다.

npm.cmd config get package-lock-only
node -e "console.log(Boolean(require('./package-lock.json').packages?.['node_modules/lock-only-proof']))"
Test-Path -LiteralPath './node_modules/lock-only-proof/package.json'
node --input-type=module -e "import('lock-only-proof').then(() => console.log('import OK'))"

이 lock 조회는 packages가 있는 lock 형식과 루트에 설치되는 직접 의존성을 대상으로 한다. 실험에서는 lock 항목 조회가 true, 파일 조회가 False였고, import는 Cannot find package 'lock-only-proof'를 포함한 ERR_MODULE_NOT_FOUND로 종료했다. lock 항목의 node_modules/...는 기록된 패키지 위치이지, 그 경로에 파일이 존재한다는 검사 결과가 아니다. 이 키의 의미는 package-lock.json의 packages 설명 (새 탭에서 열림)에서 확인할 수 있다.

현재 옵션 조회가 false여도 직전에 실행한 설치 명령에 --package-lock-only가 붙어 있었다면 설치를 생략했을 수 있다. 명령행 옵션은 그 명령에 적용되므로, 지금 조회한 값과 당시 명령을 함께 본다. 반대로 플래그를 빼고 실행해도 현재 값이 true라면 설정에서 활성화돼 있는지 확인한다.

파일이 있고 import도 성공한다면 이 시점의 설치 누락은 확인되지 않은 것이다. 원래 오류가 난 실행 위치와 같은지부터 맞춘다. 파일이 있는데 다른 오류로 실패하거나, workspace·중첩 설치 구조를 사용한다면 위 루트 경로 조회만으로 원인을 확정할 수 없다.

같은 lock으로 실제 설치를 이어서 실행했다

이를 확인하기 위해 새 폴더에서 lock-only-proof@1.0.0을 로컬 tarball로 만들었다. 패키지는 문자열 하나를 내보내는 ESM 모듈이며, 다른 의존성이나 설치 스크립트가 없다. 소비자 package.json에는 이 tarball을 dependencies에 미리 적고 시작했다. 기존 프로젝트에서 겪은 장애가 아니라 별도로 실행한 비교 실험이다.

Windows 11, Node.js 22.23.2, npm 10.9.8에서 새 전용 캐시를 사용하고 네트워크를 쓰지 않는 조건으로 실행했다. 사용자·전역 npmrc는 빈 실험 파일로 지정했다.

  • npm install --package-lock-only는 종료 코드 0과 up to date in 5s를 출력했다. lock에는 버전·로컬 tarball 경로·integrity가 생겼지만 node_modules 자체가 없었다. require.resolve와 ESM import는 모두 종료 코드 1로 실패했다.
  • 이어 npm install --package-lock-only=false는 종료 코드 0과 added 1 package in 5s를 출력했다. 패키지 파일이 생기고 resolve가 node_modules/lock-only-proof/index.js를 가리켰다. import한 값은 local-tarball-ready였다.
  • 두 단계 사이에 package.json은 바뀌지 않았다. 실제 설치 전후의 lock 파일도 바이트가 같았다. 같은 의존성 기록을 두고 디스크의 설치 상태가 달라진 것이다.

위 시간은 해당 실행의 출력 원문이며 성능 비교값은 아니다. 이 실험에서는 의존성을 미리 선언했으므로 첫 명령이 lock만 만들었다. 패키지 이름을 인자로 추가하는 다른 사용법까지 package.json이 절대 바뀌지 않는다고 해석해서는 안 된다.

실행에 필요한 것은 실제 설치 단계다

의존성 선언이 맞고 파일이 빠진 이유가 이 옵션으로 확인됐다면, 같은 프로젝트에서 lock 전용 모드를 끄고 설치한다. 평소 프로젝트에 필요한 나머지 설치 옵션은 유지한다. 이미 생성한 lock을 지우는 것부터 시작할 이유는 없다.

다음 명령은 설치를 수행한 뒤 같은 패키지를 다시 import해 실행 가능 여부를 확인한다. 첫 줄이 실패하면 다음 줄로 성공을 판단하지 말고 설치 오류부터 해결한다.

npm.cmd install --package-lock-only=false
node --input-type=module -e "import('lock-only-proof').then(() => console.log('import OK'))"

실험 패키지에서는 설치 뒤 import OK를 확인했다. 실제 프로젝트는 레지스트리나 로컬 tarball에 접근할 수 있어야 하며, 설치 명령 자체가 실패했다면 파일이 준비됐다고 볼 수 없다.

명령에 옵션을 적지 않았는데도 lock 전용 모드가 켜지는 경우도 별도로 비교했다. 새 소비자 폴더의 .npmrc에 package-lock-only=true를 넣자 옵션 조회가 true였고, 그냥 npm install을 실행해도 파일은 생기지 않았다. 같은 설정을 둔 채 명령행에 --package-lock-only=false를 주자 실제 설치와 import가 성공했다. 이 명령은 .npmrc의 값을 고치지는 않았다.

npm 설정 문서 (새 탭에서 열림)에 따르면 설정은 명령행, 환경 변수, npmrc 파일 등에서 온다. 현재 값만으로 출처를 단정하지 말고 프로젝트 .npmrc, npm_config_package_lock_only 같은 환경 변수, 사용자·전역 설정 중 어디에서 지정했는지 확인한다. 여기서 직접 비교한 것은 실험용 프로젝트 .npmrc와 명령행 재정의다. 실제 사용자 설정은 조사하거나 변경하지 않았다.

두 설치 단계에는 모두 --ignore-scripts를 적용했다. 따라서 이 결과로 lifecycle 스크립트의 실행 여부까지 판단할 수는 없다. 기존 설치 폴더가 남아 있는 경우, workspace, 레지스트리 의존성과 다른 npm 버전도 실행하지 않았다. 확인한 범위에서는 lock을 다시 고치는 작업 없이 실제 설치를 이어서 수행하자 누락된 패키지를 불러올 수 있었다.

← 전체 글 보기