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

빌드노트

호스트는 설치됐는데 플러그인 추가가 ERESOLVE로 멈춘다

호스트가 이미 설치돼 있어도 플러그인의 peer 버전 요구와 다르면 npm install이 실패할 수 있다. 로컬 tarball의 충돌·호환 조합을 비교하고, 오류의 Found와 peer 경로로 바꿔야 할 버전을 고른다.

플러그인이 붙는 라이브러리(호스트)를 설치한 뒤 플러그인을 추가했는데 npm install이 ERESOLVE unable to resolve dependency tree로 멈출 수 있다. 프로젝트가 직접 고른 호스트 버전과 플러그인의 peerDependencies 요구가 충돌하는 경우다. 호스트가 있다는 사실에 더해, 그 버전을 플러그인이 허용하는지 확인해야 한다. 오류의 Found와 peer … from을 연결하면 어느 버전 조합을 바꿔야 할지 보인다.

Found와 peer가 가리키는 패키지

먼저 실패한 프로젝트의 package.json이 있는 폴더에서 직접 지정한 버전을 확인한다. 다음은 Windows PowerShell에서 파일을 읽는 명령이다.

Get-Content -Raw ./package.json

dependencies와 devDependencies에서 오류에 나온 호스트와 플러그인을 찾는다. 아래는 이 글을 위해 새로 만든 로컬 패키지로 관찰한 오류의 일부다. 호스트는 peer-demo-host, 플러그인은 peer-demo-plugin이며 외부 제품의 실제 릴리스 번호가 아니다.

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error Found: peer-demo-host@2.0.0
npm error   peer-demo-host@"file:../tarballs/peer-demo-host-2.0.0.tgz" from the root project
npm error Could not resolve dependency:
npm error peer peer-demo-host@"^1.0.0" from peer-demo-plugin@1.0.0

from the root project는 프로젝트가 호스트 2.0.0 tarball을 직접 요구한다는 뜻이다. 마지막 줄은 플러그인 1.0.0이 호스트 ^1.0.0을 요구한다고 알려 준다. 이 범위는 정식 버전 기준으로 1.0.0 이상, 2.0.0 미만이므로 2.0.0은 들어가지 않는다. 호스트를 찾기는 했지만 이 플러그인과 함께 쓸 버전으로는 받아들이지 못한 것이다. node-semver의 caret 범위 규칙 (새 탭에서 열림)

플러그인의 선언에서 그 요구는 다음 필드에 있었다. 이 블록은 독자의 앱에 추가할 설정이 아니라, 플러그인이 제공하는 호환 조건의 예다.

"peerDependencies": {
  "peer-demo-host": "^1.0.0"
}

peerDependencies는 플러그인이 호스트 도구나 라이브러리와 함께 쓰일 수 있는 버전 조건을 표현한다. npm 7부터는 peer 의존성을 기본으로 설치하지만, 프로젝트가 직접 고른 버전과 충돌하는 조합까지 자동으로 호환되게 바꾸지는 않는다. npm의 peerDependencies 설명 (새 탭에서 열림)

같은 플러그인에 호스트 버전만 바꿔 봤다

Windows 11, Node.js 22.23.2, npm 10.9.8에서 호스트 1.0.0·2.0.0과 플러그인 1.0.0을 직접 만들고 각각 로컬 tarball로 포장했다. 플러그인의 peer 범위는 계속 ^1.0.0으로 유지했다. 별도 캐시를 썼고 레지스트리에서 패키지를 받거나 발행하지 않았다.

관찰 결과는 다음과 같다.

  • 호스트 2.0.0만 설치: 종료 코드 0. 이어 같은 프로젝트에 플러그인을 추가하자 ERESOLVE, 종료 코드 1이었다. 실패 후에도 기존 호스트 2.0.0 파일은 남아 있었다.
  • lock 파일과 node_modules가 없는 새 프로젝트에 호스트 2.0.0과 같은 플러그인을 함께 설치: 똑같이 ERESOLVE, 종료 코드 1이었다.
  • 별도 새 프로젝트에 호스트 1.0.0과 같은 플러그인을 함께 설치: 종료 코드 0이었다.

설치 명령은 세 조건 모두 npm install --offline --ignore-scripts --no-audit --no-fund였다. 로컬 tarball만 사용하고 설치 스크립트를 끈 실험이므로, 일반 프로젝트에서 이 옵션들을 그대로 적용하라는 뜻은 아니다. 새 프로젝트에서도 충돌했으므로 이 비교의 원인을 오래된 lock 파일이나 남은 설치 파일로 설명할 수는 없다.

호환 조합에서는 설치 뒤 두 패키지의 연결을 조회했다. 아래 명령의 두 이름은 앞서 만든 합성 패키지이며, 자기 프로젝트에서는 오류에 나온 실제 이름으로 바꾼다. PowerShell 외의 셸에서는 npm.cmd 대신 npm을 쓴다.

npm.cmd ls peer-demo-host peer-demo-plugin --all

출력에서 프로젝트의 절대경로가 있는 첫 줄을 생략하면 다음과 같다.

├── peer-demo-host@1.0.0
└─┬ peer-demo-plugin@1.0.0
  └── peer-demo-host@1.0.0 deduped

deduped는 이 트리에서 플러그인의 peer 요구도 같은 호스트 1.0.0으로 충족한다는 뜻이다. npm ls는 설치된 논리적 의존 관계를 보여 주며, --all로 하위 관계까지 확인할 수 있다. missing이나 invalid가 나오면 그 표시와 종료 코드를 먼저 확인한다. 설치 실패 직후 플러그인이 아직 없어서 조회에 나오지 않는다면, 그 출력만으로 충돌이 해결됐다고 판단하지 않는다. npm ls 설명 (새 탭에서 열림)

호스트를 유지할지, 플러그인을 유지할지 고른다

이 예제에서는 호스트를 1.0.0으로 맞추면 범위가 겹친다. 실제 프로젝트에서는 그 변경이 다른 코드와 플러그인의 요구까지 만족하는지 봐야 한다.

  • 현재 플러그인을 유지해야 한다면, 그 peer 범위 안의 호스트 버전을 고르고 앱이 그 호스트 API로 동작하는지 확인한다.
  • 현재 호스트를 유지해야 한다면, 그 호스트 버전을 허용하는 플러그인 릴리스가 실제로 있는지 확인한다. 버전 번호가 더 크다는 이유만으로 지원한다고 추정하지 않는다.
  • 같은 호스트를 요구하는 플러그인이 여럿이면 각각의 peer 범위를 함께 비교한다. 공통으로 허용하는 버전이 없으면 해당 플러그인들의 버전이나 구성도 바꿔야 한다.

레지스트리에 있는 플러그인의 요구는 npm view '패키지명@버전' peerDependencies --json으로 조회할 수 있다. 따옴표 안에는 오류에 나온 실제 패키지명과 정확한 버전을 넣는다. 버전을 생략하면 기본적으로 latest를 조회하므로 실패한 버전과 다른 조건을 읽을 수 있다. 이는 npm view 문서 (새 탭에서 열림)에 따른 조회 방법이며, 이 글에서는 외부 플러그인의 조회 결과를 실측하지 않았다. 로컬 tarball을 쓴 이번 실험은 직접 작성한 플러그인의 package.json으로 요구를 확인했다.

버전 조합을 정했으면 프로젝트의 직접 의존성 선언을 바꾸고 npm install로 다시 설치해 lock 파일에도 선택한 조합을 반영한다. 이어 같은 두 이름으로 npm ls … --all을 실행하고 플러그인을 사용하는 경로를 확인한다. 다른 패키지가 새 오류의 from에 나타나면 그 패키지의 요구까지 맞춰야 한다. npm install의 설치·lock 갱신 동작 (새 탭에서 열림)

오류 안내에는 --force와 --legacy-peer-deps도 나오지만, 이 옵션들은 호환되는 버전 조합을 골라 주지 않는다. 공식 문서상 legacy-peer-deps는 트리를 만들 때 peer 요구를 무시하고, force는 루트 프로젝트의 충돌하는 peer 설치를 허용한다. 설치가 끝났다는 결과만으로 플러그인의 지원 범위를 만족했다고 볼 수 없다. legacy-peer-deps (새 탭에서 열림), force (새 탭에서 열림)

이번 호환 조합에서는 호스트 버전을 읽는 짧은 플러그인 호출도 실행했고 plugin uses host 1.0.0을 얻었다. 확인한 범위는 합성 패키지의 설치 제약과 이 호출까지다. 실제 제품의 API 호환성, 여러 플러그인이 얽힌 트리, npm 11에서의 실행은 검증하지 않았다. 깊은 의존 관계의 충돌은 npm 설정에 따라 경고로 처리될 수도 있으므로 모든 peer 불일치가 이 예제처럼 실패한다고 일반화하지 않는다. strict-peer-deps의 적용 조건 (새 탭에서 열림)

← 전체 글 보기