프로젝트 클론하고 npm install 한 번에 되면 그날은 운이 좋은 날이다. 실무에서 이게 한 번에 되는 경우가 얼마나 되냐면, 팀이 클수록, 프로젝트 오래될수록 확률이 줄어든다.
문제는 에러 메시지를 제대로 읽지 않고 npm install --force 또는 npm install --legacy-peer-deps를 무지성으로 치는 경우다. 이러면 당장은 되는 것처럼 보이지만 나중에 런타임에서 이상한 에러로 돌아온다. 진짜 실무자는 에러 메시지를 읽는다.
에러 메시지별 원인과 해결
ERESOLVE: peer dependency 충돌
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: myapp@1.0.0
npm ERR! Found: react@18.2.0
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 some-library@2.1.0
이 에러는 some-library가 React 17을 요구하는데 프로젝트에는 React 18이 깔려 있다는 뜻이다. npm 7 버전부터 peer dependency를 엄격하게 검사하기 시작해서 이 에러가 갑자기 늘었다.
선택지는 세 가지다.
1. 라이브러리를 업데이트한다 (가장 좋음)
npm install some-library@latest
해당 라이브러리가 최신 버전에서 React 18을 지원하면 끝난다. 이게 제일 먼저 시도해야 할 옵션이다.
2. --legacy-peer-deps 옵션 (차선책)
npm install --legacy-peer-deps
npm 6 시절 방식으로 peer dependency를 그냥 무시하고 설치한다. 대부분의 경우 문제없이 동작하지만, 진짜 비호환 라이브러리가 섞이면 런타임 에러가 난다. 팀 전체가 이 옵션을 공유한다면 .npmrc에 박아두는 게 낫다.
# .npmrc
legacy-peer-deps=true
3. --force는 마지막 수단
npm install --force
--force는 캐시도 무시하고 버전 충돌도 무시하고 그냥 밀어붙인다. 개발 의존성이나 급할 때 쓰는 거고, CI/CD에서 이걸 쓰고 있다면 뭔가 잘못된 거다.
ENOENT: node_modules 꼬임
npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path /project/node_modules/.package-lock.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory
node_modules가 중간에 망가진 경우다. 삭제하고 다시 설치하면 된다.
# node_modules 통째로 삭제
rm -rf node_modules package-lock.json
# 클린 설치
npm install
Windows라면:
Remove-Item -Recurse -Force node_modules
Remove-Item package-lock.json
npm install
그냥 node_modules만 지우고 재설치하면 되는데, package-lock.json도 같이 지우는 게 나을 때가 있다. package-lock.json이 실제 package.json과 어긋나 있는 경우에 이 에러가 나기도 해서다. 단, package-lock.json을 지우면 의존성 버전이 최신으로 바뀔 수 있으니 팀 공유 프로젝트에선 조심해야 한다.
EACCES: 권한 문제
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied
글로벌 설치할 때 자주 나온다. sudo npm install -g로 해결했다면 잠깐은 괜찮지만, 이 방식이 습관되면 나중에 권한 문제가 더 복잡해진다. 올바른 방법은 npm 기본 디렉토리를 사용자 홈으로 옮기는 거다.
# npm 글로벌 디렉토리를 홈 폴더로 변경
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
# PATH에 추가 (~/.zshrc 또는 ~/.bashrc)
export PATH=~/.npm-global/bin:$PATH
# 적용
source ~/.zshrc
ETIMEDOUT / ECONNRESET: 네트워크 이슈
npm ERR! code ETIMEDOUT
npm ERR! errno ETIMEDOUT
npm ERR! network request to https://registry.npmjs.org/lodash failed
회사 네트워크나 VPN 환경에서 자주 나온다. 몇 가지 확인 포인트:
# npm 레지스트리 확인
npm config get registry
# 회사 사설 레지스트리 쓰고 있다면
npm config set registry https://your-company-registry.com/
# 원복
npm config set registry https://registry.npmjs.org/
# 프록시 환경이면
npm config set proxy http://proxy.company.com:8080
npm config set https-proxy http://proxy.company.com:8080
타임아웃 에러는 단순히 npm 서버 응답이 느린 경우도 있어서 재시도 먼저 해본다. 그래도 계속 나오면 레지스트리 설정 확인.
npm install vs npm ci — 이 차이 모르는 사람 많다
CI/CD 파이프라인에서 npm install을 쓰고 있다면 npm ci로 바꿔라.
| 구분 | npm install | npm ci |
|---|---|---|
| package-lock.json 없으면 | 새로 생성 | 에러로 중단 |
| 버전 범위(^, ~) | 최신 버전으로 설치 가능 | lock 파일 버전 그대로 |
| node_modules | 있으면 그대로 두고 업데이트 | 무조건 지우고 재설치 |
| 용도 | 개발 환경 | CI/CD, 배포 |
npm install은 package.json의 버전 범위 안에서 최신 버전을 가져올 수 있다. "react": "^18.0.0"이면 18.x 최신을 잡는다는 얘기다. CI에서 이걸 쓰면 빌드할 때마다 버전이 달라질 수 있다. npm ci는 package-lock.json에 적힌 정확한 버전만 설치한다. 재현 가능한 빌드를 원한다면 CI에서 npm ci가 맞다.
자주 보는 실수 모음
package-lock.json을 .gitignore에 넣는 경우
가끔 이렇게 된 프로젝트가 있다. package-lock.json은 반드시 커밋해야 한다. 이게 없으면 팀원마다 설치되는 패키지 버전이 달라지고, "내 로컬에선 되는데 왜 CI가 터지냐"는 상황이 만들어진다.
npm 버전이 팀마다 다른 경우
프로젝트 루트에 .nvmrc나 package.json의 engines 필드로 버전 명시해두는 습관을 들이자.
{
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0"
}
}
yarn/pnpm 섞어쓰기
package-lock.json(npm), yarn.lock(yarn), pnpm-lock.yaml(pnpm)이 동시에 존재하는 프로젝트가 있다. 이 상태로 팀에서 사람마다 다른 패키지 매니저 쓰면 lock 파일 충돌 지옥이 된다. package.json의 packageManager 필드로 통일해두자.
{
"packageManager": "npm@10.2.0"
}
정리
npm 에러는 대부분 세 가지 중 하나다: peer dependency 충돌, node_modules 꼬임, 네트워크/권한 문제. 에러 메시지 첫 줄에 나오는 code를 읽으면 원인이 나온다. --force는 진짜 마지막 수단이고, CI에선 npm ci 써라.
댓글 없음:
댓글 쓰기