BlueByte
ERESOLVEWorkaround

npm install이 ERESOLVE peer dependency 충돌로 실패

작성 Haneul Seo2026년 8월 23일 업데이트6 min

안녕하세요, BlueByte입니다. ERESOLVE의 긴 오류 벽은 무섭게 보이지만 잡음이 아닙니다 — npm이 버전을 두고 어긋나는 두 패키지를 정확히 짚어주는 것입니다. 오늘은 어느 둘인지 읽고, 호환 릴리즈가 있으면 제대로 고치고, 정말 생태계보다 앞섰을 때만 탈출구를 쓰는 순서로 가보겠습니다.

이 에러가 알려주는 것

npm install이 해석 못 하는 트리로 멈춥니다:

npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error Found: react@19.0.0
npm error Could not resolve dependency:
npm error peer react@"^18.0.0" from some-lib@3.2.0

npm 7부터 peer dependency가 설치되고 강제됩니다. 어떤 패키지가 요구하는 peer 버전이 트리와 충돌하면, npm 6처럼 조용히 넘어가지 않고 설치가 실패합니다. "Found"와 "peer" 줄을 함께 읽으면 무엇이 어긋나는지 짚입니다.

peer 요구가 설치를 막는 이유

에러가 충돌을 정확히 짚습니다: some-lib가 peer 요구(react@^18)를 선언했는데 설치된 버전(react@19)이 이를 못 만족합니다. npm 버그가 아니라 작성자의 실제 호환성 선언입니다. 메이저 프레임워크 릴리즈 직후에 가장 흔합니다 — 일부 라이브러리가 peer 범위를 아직 안 넓힌 때. 또 내 두 의존성이 공유 peer의 비호환 버전을 각각 고정해 어떤 단일 버전도 둘 다 만족 못 할 때도 납니다.

호환 릴리즈가 이미 있는지 확인

npm이 출력한 트리를 읽어 어떤 패키지가 어떤 peer를 원하는지 본 뒤, 더 최신 버전이 맞는지 npm에 물어봅니다:

npm view some-lib peerDependencies
npm view some-lib versions --json

더 최신 some-lib가 peer 범위에 내 프레임워크 버전을 담으면 버전 올림이 답입니다. 아무것도 없으면 생태계보다 앞선 것 — 임시 탈출구가 필요합니다. 내 의존성 둘이 충돌하면 npm ls react가 둘과 각자 기대 버전을 보여줍니다.

올바른 해결, 그다음 탈출구

  1. 버전 맞추기 — 이게 진짜 해결입니다. 문제 패키지를 내 peer를 지원하는 버전으로 올립니다:
npm install some-lib@latest
  1. 호환 버전이 아직 없으면 그냥 설치하고 위험을 알고 감수:
npm install --legacy-peer-deps

npm 6 동작으로 되돌려 해석 중 peer를 무시합니다. 의도적·일시적 선택으로 쓰고 그 패키지 기능을 다시 테스트하세요.

실제 사례: 메이저 릴리즈 다음 날

React 19가 나온 다음 날 npm install이 실패합니다 — some-chart-lib@3이 peer react@^18을 선언했기 때문입니다. npm view some-chart-lib versions가 몇 시간 전 나온 4.0.0을 보여주고, npm view some-chart-lib@4 peerDependencies가 react@^18 || ^19를 나열합니다. 그래서 진짜 해결은 버전 올림 — npm install some-chart-lib@4로 플래그 없이 트리가 깨끗이 풀립니다. v4가 없었다면 --legacy-peer-deps를 임시로 쓰고 차트를 재테스트한 뒤, 관리자가 범위를 넓히면 플래그를 제거했을 것입니다.

설치만이 아니라 동작을 확인

깨끗이 설치하고 런타임이 멀쩡한지 확인:

rm -rf node_modules package-lock.json && npm install
npm test

깨끗한 설치 + 통과 테스트라면 버전이 실제로 함께 동작하는 것이지 npm이 불평을 멈춘 것만은 아닙니다.

다시 겪지 않으려면

한 메이저 버전만 나머지보다 훨씬 앞서 올리지 말고 의존성을 보조 맞춰 움직이고, --legacy-peer-deps 결정은 주석이나 .npmrc에 이유와 함께 남겨 다음 사람이 의도적임을 알고 생태계가 따라잡으면 제거할 수 있게 하세요.

--force·404와의 구분

--legacy-peer-deps는 peer 충돌을 무시하고, --force는 거기에 더해 진짜 깨진 트리를 설치할 수 있으니 전자를 선호하세요. 404나 ETARGET은 다른 실패 — 패키지·버전이 존재하지 않는 것이지 peer 충돌이 아닙니다. ERESOLVE가 보이면 어긋나는 두 버전을 먼저 찾으세요 — 해결은 거의 항상 그 둘을 화해시키는 것입니다.

관련 질문

--legacy-peer-deps와 --force의 차이는?

--legacy-peer-deps는 해석 중 peer 충돌을 무시합니다. --force는 거기에 더해 깨진 트리를 만들 수 있습니다. --legacy-peer-deps를 선호하세요.

legacy-peer-deps를 .npmrc에 넣어도 되나요?

알려진 충돌 하나에 대한 임시 조치로, 메모와 함께만 하세요. 영구히 켜두면 이후 설치에서 진짜 비호환성을 가립니다.

--legacy-peer-deps로 되면 끝인가요?

런타임에 실제로 동작할 때만요. 앱과 테스트를 돌리세요 — peer 요구엔 이유가 있고 무시하면 런타임 버그로 드러날 수 있습니다.

yarn이나 pnpm은 이걸 피하나요?

peer 충돌을 다르게 다뤄 실패 대신 경고할 수 있지만 근본 비호환은 같습니다. 버전 맞추기가 여전히 진짜 해결입니다.

내 두 의존성이 같은 peer의 다른 버전을 원합니다.

npm ls <peer>로 둘을 보세요. 한 의존성의 peer 범위가 다른 쪽과 겹치는 버전이 필요합니다. 없으면 둘 중 하나가 바뀌어야 합니다.

참고 자료

Haneul Seo

Infrastructure engineer · 10+ years running Linux fleets

같은 카테고리 다른 글

detected dubious ownershipFixed

Git: fatal: detected dubious ownership in repository

Git 2.35.2의 CVE-2022-24765 수정 이후, Git은 작업 트리나 .git 디렉터리의 소유자가 명령을 실행한 사용자와 다르면 저장소를 읽지 않습니다. 컨테이너, CI 작업, sudo 세션, 공유 드라이브에서 주로 나타납니다. 저장소가 내 것이어야 한다면 소유권을 바로잡고, 아니라면 global 설정의 safe.directory에 정확한 경로를 추가하세요 — Git은 이 설정을 저장소 자신의 설정에서는 무시합니다.

Git
failed calling webhookFixed

Kubernetes: Internal error occurred: failed calling webhook

admission webhook이 쓰기 요청 앞에 서 있는데 API server가 응답을 받지 못했고, 기본값인 failurePolicy: Fail이 그 침묵을 거부로 바꾼 것입니다. 메시지 끝부분이 곧 진단입니다 — context deadline exceeded는 호출이 도달하지 못한 것, no endpoints available은 떠 있는 게 없는 것, x509 줄은 API server가 webhook 인증서를 신뢰하지 않는 것입니다. 원인마다 해결이 다르고, 어느 것도 매니페스트 문제가 아닙니다.

Kubernetes
1205Fixed

MySQL: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

다른 트랜잭션이 쥐고 있는 행 잠금을 innodb_lock_wait_timeout 만큼 기다리다 포기한 것입니다. sys.innodb_lock_waits 가 막고 있는 세션을 지목하고 KILL 문까지 만들어 주며, blocking_query 가 NULL 이면 블로커가 열린 트랜잭션 위에서 놀고 있다는 뜻입니다. 재시도 로직이 가장 자주 틀리는 지점은 따로 있습니다. 기본값에서는 타임아웃된 문장 하나만 롤백되므로, 트랜잭션은 그대로 열린 채 앞서 잡은 잠금을 전부 쥐고 있습니다.

MySQL
MISCONFFixed

Redis: MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk

읽기는 되는데 모든 쓰기가 거부됩니다. 마지막 백그라운드 저장이 실패했고 stop-writes-on-bgsave-error 기본값이 yes 이기 때문입니다. 진짜 원인은 로그에 적혀 있습니다 — 디스크 공간 부족, redis 사용자가 쓸 수 없는 dir, rename 시점의 read-only 마운트, 또는 fork 가 Cannot allocate memory 로 실패하는 경우입니다. 원인을 고치고 BGSAVE 한 번만 돌리면 재시작 없이 쓰기가 돌아옵니다. rdb_last_bgsave_status 가 err 에서 ok 로 바뀝니다. stop-writes-on-bgsave-error no 는 쓰기를 즉시 되살리지만 스냅샷은 여전히 실패한 상태로 두므로, 해결이 아니라 의도한 맞교환으로 다뤄야 합니다.

Redis
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memoryFixed

Node.js: FATAL ERROR: Reached heap limit — JavaScript heap out of memory (종료 코드 134)

V8 힙에는 시스템 메모리와 Node 릴리스에서 유도되는 자기만의 천장이 있고, 가진 RAM 보다 훨씬 낮은 경우가 흔합니다. 빌드나 서버가 거기 닿으면 V8 은 FATAL ERROR: Reached heap limit 과 종료 코드 134 로 스스로 중단합니다. v8.getHeapStatistics().heap_size_limit 으로 실제 한도를 읽은 뒤, 큰 작업은 --max-old-space-size(MiB)나 NODE_OPTIONS 로 올리고, 컨테이너 안에서는 cgroup 한도보다 낮게 잡고, 오래 도는 프로세스의 누수는 --heapsnapshot-near-heap-limit 으로 잡으세요. FATAL ERROR 줄 없이 137 로 끝나면 컨테이너 kill 이지 이 오류가 아닙니다.

Node.js
exec /docker-entrypoint.sh: exec format errorFixed

Docker: 컨테이너 시작 직후 "exec format error" — 플랫폼이 다른 이미지, 에뮬레이터 없음, 셔뱅 없는 스크립트

컨테이너가 첫 명령에서 exec format error 로 끝납니다. 커널의 ENOEXEC, 즉 파일은 있지만 여기서는 실행할 수 없다는 뜻입니다. 실제로는 한 CPU 아키텍처에서 빌드한 이미지(Apple 실리콘 Mac은 linux/arm64 를 만듭니다)를 binfmt_misc 에 QEMU 핸들러가 없는 다른 아키텍처(x86_64 서버)에서 돌리거나, 엔트리포인트 스크립트의 첫 줄이 셔뱅이 아닌 경우입니다. uname -m, docker image inspect, ls /proc/sys/fs/binfmt_misc 로 원인을 가르고, docker buildx build --platform 을 명시(또는 두 플랫폼 매니페스트 목록 발행)하거나, 에뮬레이션이 목적이면 QEMU 등록과 --platform, 스크립트라면 #!/bin/sh 한 줄로 해결합니다.

Docker