npm install시 —force와 —legacy-peer-deps은 왜 사용할까?
이 글에서 다루는 내용
이 글에서는 npm과 같은 패키지 관리 도구에서 의존성 충돌 문제를 해결하기 위해 사용되는 --force
와 --legacy-peer-deps
옵션의 차이점과 사용 사례를 다룹니다. 특히, 레거시 프로젝트에서 의존성 충돌로 인해 발생하는 문제를 어떻게 우회하거나 해결할 수 있는지에 대한 구체적인 방법을 살펴봅니다.
이를 위해 가상의 프로젝트를 만들어 의존성 충돌 상황을 재현하고, 각 옵션을 적용했을 때의 결과와 동작 방식을 비교합니다. 또한, 옵션 사용 시의 주의사항과 함께, 다른 패키지 관리 도구인 pnpm과 yarn에서 유사한 상황을 처리하는 방법도 설명합니다.
—force와 —legacy-peer-deps는 언제 사용해야 할까?
레거시 프로젝트에서 작업을 해야 할 때 의존성 충돌 문제를 자주 만나게 됩니다. 상시 프로젝트 마이그레이션은 현실적으로 어렵고, 당장 수정 배포는 나가야 하는 상황에서 우회 방법을 찾게 됩니다.
—force와 —legacy-peer-deps는 npm, pnpm, yarn 등 패키지 관리 도구에서 의존성 문제를 해결하거나 우회하기 위해 사용되는 플래그입니다.
—force 옵션에 대해
모든 peer dependencies 충돌을 무시하고 강제로 패키지를 설치합니다.
언제 필요할까?
- 테스트를 위해 로컬에서 빠르게 설치할 필요가 있을 때
- 중요한 작업 중 의존성 문제가 blocker일 경우 설치를 강제로 이행해야 할 경우
- 패키지간 의존성 버전이 맞지 않지만 어쨌든 설치하려 시도하는 경우
이 점 주의하세요
- 설치 이후 런타임 오류가 계속해서 발생할 가능성이 높습니다.
- 종속성 트리가 깨지거나 예기치 못한 충돌 발생 우려가 있습니다.
설치가 성공하더라도 peer dependency conflict 경고가 출력되며 패키지 작동 여부를 반드시 테스트해야 합니다.
—legacy-peer-deps 옵션에 대해
npm v7 이상에서 도입된 엄격한 peer dependencies 검증을 비활성화 합니다. npm v6 이전 방식처럼 peer dependencies 충돌을 무시하고 설치합니다.
언제 필요할까?
- 레거시 패키지 등 오래된 프로젝트가 특정 버전에 의존하는 경우
- 새로운 패키지와 기존 프로젝트가 충돌하는 상황에서 기존 규칙대로 설치하고 진행하고 싶은 경우
이 점 주의하세요
- 종속성 트리가 예상대로 설치되지 않을 경우가 높아요.
- 향후 업데이트 과정에서 다시 충돌 문제가 발생할 가능성이 있어요.
옵션 | 동작 방식 | 사용 상황 |
---|---|---|
--force | 모든 충돌을 무시하고 최신 버전 설치 | 테스트 환경, 당장 실행이 필요할 때 |
--legacy-peer-deps | npm v6처럼 peer dependencies 검증 비활성화 | 레거시 프로젝트 |
간단한 상황 재현으로 동작 이해하기
가상 프로젝트로 dependency conflict 상황을 재현하여 의존성 충돌 우회 옵션에 대해 이해해봅 니다. 재현하는 상황을 정확히 표현하자면 **업스트림 디펜던시 컨플릭트(Upstream Dependency Conflict)**라고 합니다.
프로젝트 셋업
.
├── 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
—force 옵션을 사용하여 설치했을때와 마찬가지로 종속성 충돌에 대해 설명하고 있습니다.
lock파일 내부에서도 peerDependency로 4.0.0 이상이 요구되는 반면 참조되는 종속성은 3.10.1으로 어긋남을 알 수 있습니다.
단순한 프로젝트라서 육안으로 —force와 —legacy-peer-deps의 동작 차이는 보이지 않지만, 종속성 충돌에 대한 우회 동작은 동일한 것으로 보입니다.
패키지 매니저 간의 동작 차이
npm, pnpm, yarn에서 각 옵션은 조금씩 차이가 있습니다.
npm
- —force와 —legacy-peer-deps 모두 지원
pnpm
- 엄격한 종속성 관리를 특징으로 하지만 peer dependency 불일치는 경고로 표시합니다.
- —force를 사용할 수 있지만 —legacy-peer-deps는 지원하지 않습니다.
- 대신 —shamefully-hoist 또는 —no-peer-deps 옵션을 통해 peer dependency 문제를 우회할 수 있습니다.
yarn
- yarn v2 이상에서는 —legacy-peer-deps를 지원하지 않습니다.
- pnpm과 마찬가지로 설치 중단 대신 경고로 표시합니다.
- —force와 유사한 플래그는 있지만, 엄격한 dependency 관리 철학으로 잘 사용되지 않는다고 하네요.
- resolution 필드를 활용하거나 yarn.lock을 수동으로 수정하는 방법도 있습니다.