개발 PC에서는 정상인데 CI 빌드나 운영 배포에서만 오류가 난다면, 애플리케이션 코드보다 설치된 의존성 조합이 서로 다른 경우를 먼저 확인해야 합니다. 팀이 배포하는 Node.js 프로젝트는 package-lock.json을 저장소에 함께 관리하고, 자동 빌드에서는 npm install 대신 npm ci를 사용해 같은 의존성 트리를 설치해야 합니다. 이 기준을 정하면 새 버전이 우연히 섞이는 문제와 잠금 파일 불일치를 배포 전에 발견할 수 있습니다.

같은 package.json인데 결과가 달라지는 이유

package.json의 버전 범위는 보통 ^, ~, 비교 연산자로 작성됩니다. 예를 들어 ^4.18.0은 4.x 범위의 더 새로운 버전을 허용합니다. 개발자가 오늘 npm install을 실행한 결과와 CI가 다음 주에 실행한 결과가 달라질 수 있는 이유입니다. 직접 설치한 패키지가 같아도 하위 의존성의 새 버전, 플랫폼별 선택 의존성, 동료가 갱신한 잠금 파일 때문에 실제 트리는 달라질 수 있습니다.

package-lock.json은 최상위 패키지뿐 아니라 하위 패키지의 정확한 버전과 무결성 정보를 기록합니다. 따라서 잠금 파일은 생성물이라서 제외하는 파일이 아니라, 재현 가능한 설치를 위한 배포 입력값입니다. 라이브러리와 애플리케이션의 정책은 다를 수 있지만, 서비스를 배포하는 저장소라면 잠금 파일 하나를 기준으로 개발·검증·배포가 같은 트리를 사용하도록 정하는 편이 안전합니다.

자동 빌드에서는 npm ci를 사용한다

npm ci는 기존 node_modules를 제거한 뒤 잠금 파일에 적힌 내용대로 설치합니다. 반대로 npm install은 package.json의 범위를 해석하고 필요하면 잠금 파일을 갱신할 수 있습니다. CI에서 잠금 파일을 조용히 바꾸는 동작은 검토되지 않은 의존성 변경을 만들 수 있으므로, 빌드 단계에는 npm ci가 맞습니다.

git status --short; npm ci; npm test; npm run build

첫 명령에서 잠금 파일과 package.json에 의도하지 않은 변경이 없는지 확인합니다. npm ci가 실패하면 package.json과 package-lock.json이 맞지 않거나, 잠금 파일이 현재 패키지 관리자 형식과 호환되지 않는 경우가 많습니다. 이 실패를 억지로 npm install로 통과시키지 말고, 개발 환경에서 변경 이유를 확인한 뒤 잠금 파일을 갱신해 커밋해야 합니다.

의존성을 바꾸는 작업 흐름

  • 기능 작업 브랜치에서 필요한 패키지를 추가·업데이트하고, 로컬에서 npm install을 한 번 실행합니다.
  • package.jsonpackage-lock.json의 변경을 함께 검토합니다. 두 파일 중 하나만 커밋하는 방식은 허용하지 않습니다.
  • 깨끗한 설치를 재현하려면 node_modules를 지운 뒤 npm ci, 테스트, 빌드를 순서대로 실행합니다.
  • 풀 리퀘스트에서는 변경된 직접 의존성, 버전 범위, 라이선스·보안 검토 필요 여부를 확인합니다.
  • CI는 같은 Node.js 메이저 버전에서 npm ci와 테스트를 실행하고, 성공한 커밋만 배포 대상으로 만듭니다.

의존성을 최신화하는 작업과 일반 기능 작업을 한 커밋에 섞지 않는 것도 중요합니다. 장애가 발생했을 때 코드 변경인지 의존성 변경인지 분리해서 되돌릴 수 있기 때문입니다. 긴급 수정이라도 잠금 파일의 대량 변경이 보이면 어떤 직접 패키지의 트리가 바뀌었는지 먼저 확인해야 합니다.

CI 설정에서 확인할 값

CI 환경은 Node.js 버전도 고정해야 합니다. 로컬은 20.x, CI는 22.x처럼 메이저 버전이 다르면 네이티브 모듈이나 빌드 도구의 결과가 달라질 수 있습니다. 프로젝트에서 지원할 버전을 문서와 CI 설정에 명시하고, 최소 한 개의 기준 버전으로 배포 빌드를 통일합니다. 환경 변수는 저장소에 비밀값을 넣지 말고 CI의 비밀 저장소에서 주입하되, 설치 단계와 애플리케이션 실행 단계를 구분합니다.

node-version: 22; cache: npm; install: npm ci; test: npm test; build: npm run build

캐시는 설치 결과를 신뢰하는 장치가 아니라 다운로드 시간을 줄이는 장치입니다. 캐시가 있어도 설치 명령은 npm ci여야 하며, 잠금 파일이 바뀌면 캐시 키도 새로 계산되어야 합니다. 캐시를 삭제했을 때만 빌드가 실패한다면 캐시가 누락 파일을 가리고 있었을 가능성을 점검해야 합니다.

실패 결과를 해석하는 방법

npm ci가 잠금 파일 불일치 메시지와 함께 종료되면, CI에서 파일을 수정하지 않습니다. 개발자가 사용 중인 npm과 Node.js 버전을 확인하고 npm install으로 잠금 파일을 의도적으로 다시 만든 뒤, 변경 내역을 검토해 커밋합니다. 설치는 성공하지만 테스트가 실패하면 현재 잠금 파일에 고정된 버전 조합에서 실제 문제가 재현된 것이므로, 로그와 실패 패키지 버전을 이슈에 남겨 수정합니다.

반대로 로컬에서만 성공한다면 node --version, npm --version, npm ls --depth=0의 결과를 CI 로그와 비교합니다. Node.js 메이저 버전, npm 버전, 직접 의존성 목록 세 가지가 같아도 문제가 남으면 운영체제와 환경 변수 차이를 확인합니다. 이 순서로 좁히면 막연히 node_modules를 지우는 대신 설치 조건을 증거로 비교할 수 있습니다.

소프트모아의 서비스와 포트폴리오는 softmoa.com에서 확인할 수 있습니다.

소프트모아는 해당 시스템을 구축합니다. 문의하기