npm ERESOLVE 에러 5분 해결: unable to resolve dependency tree 원인별 처방
어제까지 멀쩡히 돌아가던 npm install이 오늘 아침 갑자기 빨간 에러를 토해내며 멈췄다면, 그리고 그 콘솔 출력을 그대로 복사해 검색하다 이 글에 도착했다면 제대로 찾아왔습니다. 이 글의 목표는 단 하나입니다. 막힌 빌드부터 5분 안에 뚫고, 그다음 근본 원인까지 잡는 것. 급한 분을 위해 가장 빠른 임시 탈출구부터 먼저 던지겠습니다.
# 일단 막힌 설치를 뚫는 가장 빠른 한 줄
npm install --legacy-peer-deps이 명령으로 일단 빌드가 통과되면 한숨 돌리고, 아래에서 왜 이런 일이 벌어졌는지와 더 깔끔한 해결책을 차근차근 보면 됩니다.
당신이 본 그 에러, 바로 이것들이죠?
검색 유입의 정확도를 위해 실제로 콘솔에 찍히는 출력을 그대로 올려둡니다. 아래 변형들 중 하나라도 본 적이 있다면 이 글이 정확히 당신을 위한 글입니다.
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR! react@"^18.2.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^17.0.0" from [email protected]
npm ERR! node_modules/some-legacy-lib
npm WARN ERESOLVE overriding peer dependency
npm error ERESOLVE could not resolve핵심 키워드는 ERESOLVE unable to resolve dependency tree, Could not resolve dependency: peer, overriding peer dependency입니다. 메시지가 영어라 당황스럽지만 구조만 알면 5초 만에 원인을 짚어낼 수 있습니다.
ERESOLVE의 정체: npm v7부터 바뀐 peer dependency 정책
이 에러의 뿌리는 npm의 정책 변화입니다. npm v6까지는 peer dependency 충돌을 그냥 무시(자동 평탄화)하고 경고만 띄운 뒤 설치를 진행했습니다. 하지만 **npm v7부터는 peer dependency를 자동으로 설치하려고 시도하며, 의존성 트리가 동시에 만족 불가능한 상태(예: A는 react 17을, B는 react 18을 요구)가 되면 아예 설치를 중단(ERESOLVE)**합니다. npm v10이 기본인 지금은 이 엄격한 동작이 표준이고, 특히 React 19 릴리스 이후 아직 peer 범위를 갱신하지 못한 구버전 라이브러리들 때문에 이 에러가 폭증하고 있습니다. 로그를 한 줄씩 해부하면 이렇게 읽힙니다.
While resolving: [email protected]→ "어떤 패키지를 설치하다가" 충돌이 났는지Found: [email protected]→ 현재 트리에 이미 확정된 버전Could not resolve dependency: peer react@"^17.0.0" from some-legacy-lib→ 어떤 패키지가 무엇을 원하는데 못 맞췄는지
즉 위 로그는 "내 프로젝트는 react 18을 쓰는데, some-legacy-lib는 react 17만 받겠다고 우긴다"는 뜻입니다.
4가지 해결 레시피 비교표
상황에 맞는 처방을 고르세요. 위로 갈수록 빠르고 거칠며, 아래로 갈수록 느리지만 근본적입니다.
| 방식 | 명령/설정 | 동작 | 부작용 | 권장 상황 |
|---|---|---|---|---|
--legacy-peer-deps | npm install --legacy-peer-deps | peer 검증을 npm v6 방식으로 무시 | 잠재적 런타임 호환성 위험 | 급한 빌드 복구, 대부분 무난 |
--force | npm install --force | 충돌 무시하고 강제 설치 | 깨진 트리 그대로 남음 | 최후의 수단 |
overrides | package.json에 JSON 설정 | 특정 하위 의존성 버전 강제 고정 | 잘못 지정 시 런타임 오류 | 정밀 수술, 팀 공유 권장 |
| 버전 정렬 | 직접 버전 맞춤 | peer 요구에 맞게 근본 해결 | 시간 소요, 코드 수정 동반 | 근본 해결 |
1) --legacy-peer-deps (가장 무난한 임시 탈출구)
npm install --legacy-peer-deps매번 치기 귀찮다면 프로젝트 루트의 .npmrc에 박아둡니다.
# .npmrc
legacy-peer-deps=true2) --force (정말 급할 때만)
npm install --force깨진 트리를 그대로 끌고 가므로 런타임에 더 골치 아픈 버그를 부를 수 있습니다. 데모 직전이 아니라면 피하세요.
3) package.json overrides (제가 가장 선호하는 방식)
실무에서 저는 --legacy-peer-deps로 전체를 무시하기보다 문제가 된 패키지 하나만 콕 집어 overrides로 버전을 고정하는 걸 선호합니다. 무엇을 어떻게 바꿨는지가 코드로 남아 팀원도 추적할 수 있기 때문입니다.
{
"overrides": {
"some-legacy-lib": {
"react": "$react"
},
"another-lib": {
"react-dom": "18.2.0"
}
}
}"$react"는 "루트가 쓰는 react 버전을 그대로 따르게 하라"는 의미입니다. 적용 후 rm -rf node_modules package-lock.json && npm install로 다시 잠가주세요.
4) 버전 정렬 (근본 해결)
npm ls react 같은 명령으로 누가 어떤 버전을 요구하는지 추적한 뒤, 충돌 패키지를 peer를 지원하는 최신 버전으로 올리거나 대체합니다. 시간은 들지만 lock 파일이 깨끗해지는 정공법입니다.
CI/Docker에서 npm ci가 깨질 때
로컬에서는 되는데 CI에서만 터지는 경우가 가장 흔합니다. npm ci는 npm install과 달리 lock 파일과 package.json이 100% 일치해야 하고, 불일치하거나 peer 충돌이 있으면 가차 없이 실패합니다.
GitHub Actions에서는 두 가지 방법이 있습니다.
# 방법 A: step에 명시
- run: npm ci --legacy-peer-deps
# 방법 B: .npmrc를 커밋 (legacy-peer-deps=true) → step은 그냥 npm ci.npmrc 커밋 방식은 로컬·CI·Docker가 동일하게 동작해 일관성이 좋습니다. Dockerfile에서는 환경 변수로 처리하면 캐시 레이어를 살리면서 깔끔합니다.
ENV NPM_CONFIG_LEGACY_PEER_DEPS=true
COPY package*.json ./
RUN npm ci
COPY . .가장 자주 만나는 함정은 로컬과 CI의 npm 버전 차이입니다. 로컬은 npm v9, CI는 v10이면 resolve 결과가 달라 "내 PC에선 됐는데?" 사태가 벌어집니다. package.json의 engines와 .nvmrc로 버전을 못 박으세요.
{ "engines": { "node": ">=20", "npm": ">=10" } }package-lock.json 꼬임 초기화 절차
lock 파일이 부분적으로 꼬여 어떤 명령도 안 먹힐 때의 클린 리셋 순서입니다.
rm -rf node_modules package-lock.json
npm cache verify
npm install다만 lock 초기화는 양날의 검입니다. 모든 의존성이 최신 호환 버전으로 다시 잠기므로 재현성을 잃을 수 있습니다. 팀이 공유하는 lock 파일을 혼자 초기화해 커밋하면 "방금 전까지 멀쩡하던 동료 환경"이 무더기로 깨질 수 있으니, 반드시 PR로 변경 사항을 공유하고 리뷰를 거치세요. 또한 pnpm이나 Yarn Berry는 기본적으로 strict peer 정책을 쓰므로, 마이그레이션을 고려 중이라면 이 충돌이 더 빈번해질 수 있다는 점을 염두에 두세요. monorepo(workspaces)라면 호이스팅 위치까지 함께 확인해야 합니다.
참고로 Python 진영에도 똑같은 고통이 있습니다. pip에서는 동일한 의존성 충돌이 ResolutionImpossible이라는 이름으로 나타나는데, 해결 철학은 비슷하지만 도구는 전혀 다릅니다(관련 글: pip ResolutionImpossible 해결 가이드).
결론: 상황별 의사결정 체크리스트
급한 순서대로 적용하되, 임시방편에 머물지 말고 로드맵을 따라가세요.
- 지금 당장 빌드만 뚫어야 한다 →
npm install --legacy-peer-deps - 문제 패키지를 특정했다 →
package.jsonoverrides로 핀포인트 고정 - CI에서만 깨진다 → npm 버전 통일(
engines/.nvmrc) +.npmrc커밋 - lock이 완전히 꼬였다 → 클린 리셋(팀 공유 시 PR 필수)
- 시간 여유가 있다 → 충돌 패키지 버전 정렬로 근본 해결
--legacy-peer-deps는 응급처치이지 완치가 아닙니다. 일정에 쫓겨 임시방편으로 막았다면, 백로그에 "의존성 정리" 티켓 하나만 남겨두세요. 다음 React 메이저 업그레이드 때 당신을 구해줄 겁니다.
자주 묻는 질문 (FAQ)
Q. --legacy-peer-deps와 --force는 뭐가 다른가요?
A. --legacy-peer-deps는 peer dependency 검증만 건너뛰고 나머지 트리는 정상적으로 구성합니다. --force는 충돌을 무시하고 깨진 트리까지 강제로 설치하므로 훨씬 위험합니다. 평소엔 --legacy-peer-deps를, --force는 정말 최후에만 쓰세요.
Q. 로컬은 되는데 CI의 npm ci만 ERESOLVE로 실패합니다.
A. 십중팔구 로컬과 CI의 npm 버전 차이 또는 lock 파일 불일치입니다. .nvmrc와 engines로 Node/npm 버전을 고정하고, .npmrc에 legacy-peer-deps=true를 커밋해 환경을 일치시키세요.
Q. overrides로 강제 고정하면 런타임에 문제가 없나요?
A. 라이브러리가 실제로는 상위 버전과 호환되는데 peer 범위만 갱신을 안 한 경우라면 안전합니다. 다만 진짜 호환되지 않는 메이저 차이를 억지로 맞추면 런타임 오류가 날 수 있으니, 고정 후 핵심 기능 테스트를 꼭 돌려보세요.
이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.
댓글
불러오는 중...