npm의 --force와 --legacy-peer-deps는 어떻게 다를까요?

이 글에서 다루는 내용
가상의 프로젝트에서 peer dependency 충돌을 재현하고 npm의 --force와 --legacy-peer-deps가 설치 결과를 어떻게 바꾸는지 비교합니다. 이어서 Yarn과 pnpm이 같은 충돌을 처리하는 방식과 각 옵션의 주의점을 살펴봅니다.
--force와 --legacy-peer-deps는 언제 사용해야 할까?
오래된 프로젝트를 수정하다 보면 패키지 버전을 한꺼번에 올리기 어렵지만 당장 설치와 배포는 해야 하는 상황이 생깁니다. 이때 충돌을 우회하는 옵션을 찾게 됩니다.
--force와 --legacy-peer-deps는 npm의 설치 동작을 바꾸는 플래그입니다. 충돌 자체를 해결하는 도구는 아니며, Yarn과 pnpm에는 같은 이름이나 의미의 옵션이 없을 수 있습니다.
--force 옵션에 대해
--force는 npm의 여러 보호 장치를 해제하며, 루트 프로젝트의 충돌하는 peer dependency 설치도 허용합니다. peer dependency를 해석하지 않는 옵션은 아닙니다.
언제 필요할까?
- 테스트를 위해 로컬에서 빠르게 설치할 필요가 있을 때
- 의존성 문제가 작업을 막아 설치 결과부터 확인해야 할 때
- 패키지 사이의 요구 버전이 맞지 않지만 테스트를 위해 설치를 시도할 때
이 점 주의하세요
- 설치 뒤 런타임 오류가 발생할 수 있습니다.
- 의존성 트리가 예상과 달라지거나 다른 충돌이 생길 수 있습니다.
설치가 성공하더라도 peer dependency conflict 경고가 출력되며 패키지 작동 여부를 반드시 테스트해야 합니다.
--legacy-peer-deps 옵션에 대해
--legacy-peer-deps는 npm v7부터 도입된 엄격한 peer dependencies 검증을 비활성화합니다. npm v6 이전 방식처럼 peer dependencies 충돌을 무시하고 설치합니다.
언제 필요할까?
- 레거시 패키지 등 오래된 프로젝트가 특정 버전에 의존하는 경우
- 새로운 패키지와 기존 프로젝트가 충돌하는 상황에서 기존 규칙대로 설치하고 진행하고 싶은 경우
이 점 주의하세요
- 의존성 트리가 패키지 작성자의 기대와 다르게 구성될 수 있습니다.
- 이후 업데이트에서 같은 충돌이 다시 나타날 수 있습니다.
| 옵션 | 동작 방식 | 사용 상황 |
|---|---|---|
| --force | 보호장치를 해제하고 충돌하는 peer 의존성 설치를 허용 | 테스트 환경, 당장 실행이 필요할 때 |
| --legacy-peer-deps | peerDependencies를 npm 6 이전처럼 아예 무시(자동 설치도 안 함) | 레거시 프로젝트 |
간단한 상황 재현으로 동작 이해하기
가상 프로젝트로 peer dependency 충돌을 만들고 두 옵션의 결과를 비교합니다.
프로젝트 구성
.
├── fake-dependency
│ ├── fake-dependency-1.0.0.tgz
│ ├── index.js
│ └── package.json
│
└── npm-install-demo
├── node_modules
├── package-lock.json
├── package.json
└── src
npm-install-demo, fake-dependency 두 개의 가상 프로젝트를 만듭니다.
{
"name": "npm-install-demo",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"dependencies": {
"lodash": "^3.10.1"
}
}
{
"name": "fake-dependency",
"version": "1.0.0",
"description": "A fake library to simulate lodash dependency conflict",
"main": "index.js",
"peerDependencies": {
"lodash": "^4.0.0"
},
"dependencies": {},
"devDependencies": {}
}
npm-install-demo 프로젝트는 lodash@^3.10.1 버전에 의존하고 있는 반면, fake-dependency는 peerDependencies로 lodash@^4.0.0 버전에 의존하고 있습니다.
의존성 충돌
만약 npm-install-demo 프로젝트에서 fake-dependency를 설치하면 어떻게 될까요?
pack 명령을 사용하여 tgz 파일을 미리 만들고 npm-install-demo에서 file 경로로 의존성을 가져오도록 설치해보겠습니다.
$ npm install ../fake-dependency/fake-dependency-1.0.0.tgz

에러 메시지 살펴보기
- npm ERR! code ERESOLVE
- ERESOLVE는 npm이 의존성 트리를 해결하지 못했다는 뜻입니다.
- Found: lodash@3.10.1
- 현재 프로젝트의 node_modules에 lodash@3.10.1이 설치되어 있습니다.
- Could not resolve dependency: peer lodash@"^4.0.0" from fake-dependency@1.0.0
- fake-dependency@1.0.0이 peerDependencies로 lodash@"^4.0.0"을 요구하고 있지만, 현재 프로젝트에 설치된 버전은 lodash@3.10.1이므로 충돌이 발생합니다.
--force 옵션으로 강제하기
아래는 npm install에 --force를 붙여 충돌을 허용한 결과입니다.
$ npm install ../fake-dependency/fake-dependency-1.0.0.tgz --force

출력되는 메시지를 요약하면 아래와 같습니다.
--force로 충돌을 허용한 채 설치가 완료되었습니다.- lodash@3.10.1이 설치된 상태에서 fake-dependency가 요구하는 lodash@^4.0.0은 무시되었습니다.
- 이로 인해 런타임에서 fake-dependency가 올바르게 동작하지 않을 가능성이 있습니다.
- 1 critical severity vulnerability는 별도의 심각한 보안 취약점이 하나 있다는 뜻이며, 이를 수정하려면 업데이트가 필요합니다.
이 예제에서 실행한 기능은 정상 동작하지만, 이것만으로 전체 호환성이 확인된 것은 아닙니다.

npm audit fix --force로 취약성 강제 수정하기
설치 출력은 audit 명령으로 패키지 의존성 문제와 보안 취약점을 자동 수정하라고 제안합니다. 해당 옵션을 실행하면 어떻게 동작하는지 살펴봅니다.

- 기존 lodash@3.10.1에서 lodash@4.17.21로 업데이트되었습니다.
- lodash 패키지를 업데이트하면서 SemVer(Semantic Versioning)의 주요 버전(Major Version)이 변경되었다는 경고가 나타납니다.
- SemVer에서 Major Version 변경은 기존 API와 호환되지 않을 가능성이 있어, Breaking Changes가 포함될 수 있습니다.
현재의 가상 프로젝트와 달리 실제 프로젝트에선 lodash의 Breaking Change로 인해 동작 오류가 발생할 가능성이 높습니다. 따라서 버전 업데이트 후 모든 기능을 테스트하여 변경된 API나 동작을 확인해야 합니다.
--legacy-peer-deps 사용해보기
이번에는 --legacy-peer-deps로 설치합니다.
$ npm install ../fake-dependency/fake-dependency-1.0.0.tgz --legacy-peer-deps

설치는 완료되지만 peer dependency 요구 사항과 실제 설치 버전은 여전히 어긋나 있습니다.

lock 파일에서도 peer dependency는 4.0.0 이상을 요구하지만 실제로 참조하는 버전은 3.10.1입니다.
이 작은 예제의 설치 결과만 보면 두 옵션의 차이가 드러나지 않습니다. 그러나 --force는 npm의 여러 보호 장치를 해제하고 충돌하는 peer 설치를 허용하는 반면, --legacy-peer-deps는 peer dependency를 npm 6 방식처럼 설치 판단에서 제외합니다.
패키지 매니저 간의 동작 차이
npm, pnpm, yarn에서 각 옵션은 조금씩 차이가 있습니다.
npm
- --force와 --legacy-peer-deps 모두 지원
pnpm
- 엄격한 종속성 관리를 특징으로 하지만 peer dependency 불일치는 경고로 표시합니다.
- --force를 사용할 수 있지만 --legacy-peer-deps는 지원하지 않습니다.
- 대신 strict-peer-dependencies=false(기본값), auto-install-peers, peerDependencyRules(allowAny/allowedVersions/ignoreMissing) 설정으로 peer 이슈를 제어합니다.
yarn
- yarn v2 이상에서는 --legacy-peer-deps를 지원하지 않습니다.
- pnpm과 마찬가지로 설치 중단 대신 경고로 표시합니다.
- Yarn 버전에 따라 지원 플래그가 다릅니다. Yarn Classic의
--force는 패키지를 다시 가져오는 옵션으로, npm의 충돌 허용 플래그와 뜻이 다릅니다. resolutions로 선택할 버전을 지정할 수 있습니다. 버전 범위를 맞춘 뒤 install로 yarn.lock을 재생성하며, 이것만으로 런타임 호환성이 보장되지는 않습니다.
가능하면 요구 버전을 맞추고 호환성을 검증해야 합니다. 우회 옵션이 필요하다면 어떤 검사를 건너뛰는지와 설치 뒤 확인할 범위를 먼저 정합니다.
동작 차이 검증하기
1) yarn v1.x
Yarn 1에서 fake-dependency를 설치하면 incorrect peer dependency 경고를 표시하고 설치를 계속합니다.

yarn.lock 파일을 살펴보아도 설치는 완료된 것으로 보입니다.

yarn v1.x는 peer dependency를 이렇게 다룹니다.
- peer dependency 불일치를 검사해 경고하지만 설치를 중단하지는 않습니다.
- 충돌이 있는 패키지는 경고만 표시하고 설치를 계속합니다.
- npm v7이 peer dependencies를 엄격하게 검증하는 것과 다릅니다.
- 충돌 시 호환성 문제가 발생할 수 있으나, yarn v1.x는 이를 강제로 막지 않습니다.
2) pnpm v8.x
pnpm 8도 기본 설정에서는 peer dependency 불일치를 경고하고 설치를 계속합니다.

이 예제에서 확인한 동작은 다음과 같습니다.
- pnpm은 엄격한 의존성 관리와 독립된 node_modules 구조를 사용하는 것이 특징입니다.
- 그러나, peer dependencies 충돌은 경고만 출력하고 설치는 진행합니다.
.npmrc나 실행 플래그에서 strict-peer-dependencies를 활성화하면 충돌 시 설치를 중단할 수 있습니다.
# npmrc
strict-peer-dependencies = true

프로젝트에서 peer dependency 불일치를 허용하지 않으려면 이 설정을 사용할 수 있습니다.
