npm install시 —force와 —legacy-peer-deps은 왜 사용할까?

Wonkook Lee·2025년 1월 18일
5

Libraries and Frameworks

목록 보기
2/3
post-thumbnail

이 글에서 다루는 내용

이 글에서는 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-depsnpm 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


에러 메시지 살펴보기

  1. npm ERR! code ERESOLVE
    1. ERESOLVE는 npm이 의존성 트리를 해결하지 못했다는 뜻입니다.
  2. Found: lodash@3.10.1
    1. 현재 프로젝트의 node_modules에 lodash@3.10.1이 설치되어 있습니다.
  3. Could not resolve dependency: peer lodash@"^4.0.0" from fake-dependency@1.0.0
    1. 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

출력되는 메시지를 요약하면 아래와 같습니다.

  1. --force로 인해 종속성 충돌을 무시하고 설치가 완료되었습니다.
  2. lodash@3.10.1이 설치된 상태에서 fake-dependency가 요구하는 lodash@^4.0.0은 무시되었습니다.
  3. 이로 인해 런타임에서 fake-dependency가 올바르게 동작하지 않을 가능성이 있습니다.
  4. 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을 수동으로 수정하는 방법도 있습니다.

가장 이상적인 시나리오는 최신화, 버전 검증, 엄격한 의존성 관리 정책 등이 있겠으나 현실적으로 어려울때가 많으니 적어도 어떤 trade-off가 있을지 알고 사용하는게 좋겠다는 생각입니다.




동작 차이 검증하기

1) yarn v1.x

npm 대신 yarn을 사용하여 fake-dependency를 설치했을때 incorrect peer dependency 경고만 표시될 뿐 그대로 설치됩니다.

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

이는 yarn v1.x의 특성에 기인합니다.

  • peer dependencies가 충돌해도 검증 없이 설치를 진행합니다.
  • 충돌이 있는 패키지는 경고만 표시하고 설치를 계속합니다.
  • 이는 npm v7의 엄격한 peer dependencies 검증과 대조됩니다.
  • 충돌 시 호환성 문제가 발생할 수 있으나, yarn v1.x는 이를 강제로 막지 않습니다.



2) pnpm v8.x

pnpm 패키지 매니저도 peer dependencies 이슈에 대해 경고만 할 뿐 설치를 막진 않습니다.

pnpm의 Peer Dependencies 처리 방식을 살펴보면,

  • pnpm은 엄격한 의존성 관리와 독립된 node_modules 구조를 사용하는 것이 특징입니다.
  • 그러나, peer dependencies 충돌은 경고만 출력하고 설치는 진행합니다.

이 동작은 아래와 같은 이유로 설계되었다고 합니다.

  1. 중단 없이 설치: 프로젝트를 멈추지 않고 설치를 진행해 개발자가 직접 충돌을 해결할 수 있도록 유도.
  2. 최대한 독립적 구조 유지: 다른 패키지의 영향을 최소화하면서도 의존성을 설치.

만약 .npmrc 또는 실행시 플래그로 --strict-peer-dependencies를 활성화하면 충돌을 그냥 넘어가지 않습니다.

# npmrc
strict-peer-dependencies = true

이 옵션을 사용하면 좀 더 엄격하게 종속성을 관리할 수 있겠습니다.

profile
Software Engineer | Former Industrial Designer

0개의 댓글