본문 바로가기
JavaScript & TypeScript

CI 환경에서 `npm ci` 명령어를 사용해야 하는 이유

by 대박플머 2024. 11. 25.

CI(Continuous Integration) 환경에서 의존성 관리는 매우 중요한 부분입니다. CI 환경에서 안정적이고 예측 가능한 빌드를 만들기 위해 npm install 대신 npm ci를 사용하는 것이 일반적입니다. 이번 글에서는 npm ci 명령어의 필요성, 차이점, 그리고 이를 사용하지 않을 때 발생할 수 있는 문제점에 대해 상세히 알아보겠습니다.


1. npm installnpm ci의 차이점

먼저 npm installnpm ci의 차이점에 대해 이해해야 합니다.

1.1 npm install

npm install은 우리가 일반적으로 로컬 개발 환경에서 사용하는 명령어입니다. package.json 파일에 명시된 패키지를 설치하며, 필요 시 새로운 버전의 패키지를 설치하거나 package-lock.json 파일을 업데이트합니다. 만약 package-lock.json 파일에 정의된 버전과 package.json에 정의된 버전이 다를 경우, 패키지의 버전 충돌이 발생할 수 있습니다.

사용 예시:
npm install

위 명령어를 실행하면, 프로젝트에서 필요한 패키지들을 설치하고, 만약 새로운 버전이 있다면 이를 반영해 package-lock.json을 업데이트합니다.

1.2 npm ci

반면, npm ci는 CI 환경에서의 패키지 설치를 위한 명령어입니다. 이 명령어는 package-lock.json 파일에 명시된 정확한 버전 의 패키지만을 설치합니다. npm install과는 다르게 npm cipackage-lock.json을 절대로 수정하지 않습니다. 만약 package-lock.jsonpackage.json의 의존성 정보가 맞지 않으면, 오류를 발생시키고 설치를 중단합니다.

사용 예시:
npm ci

이 명령어는 CI 환경에서 불필요한 패키지 업데이트나 의존성 충돌을 방지하며, 동일한 패키지 버전으로 빌드를 보장합니다.


2. npm ci의 필요성

CI 환경에서는 일관성 있고 안정적인 빌드가 매우 중요합니다. 로컬 개발 환경에서는 개발자들이 패키지 버전을 유동적으로 관리할 수 있지만, CI 환경에서는 빌드마다 동일한 의존성을 보장해야만 버그나 예기치 않은 오류를 방지할 수 있습니다. npm ci 명령어는 이러한 필요성을 충족시키기 위해 만들어졌으며, 주된 장점은 다음과 같습니다.

2.1 일관된 패키지 버전 관리

CI 환경에서는 특정 빌드마다 의존성이 정확하게 같아야 합니다. npm install을 사용하면 패키지 버전이 다르게 설치될 가능성이 있는데, 이는 package-lock.json이 업데이트되거나 패키지의 새로운 버전이 존재할 때 발생합니다. 하지만 npm cipackage-lock.json 파일에 명시된 버전만 설치하기 때문에 일관성을 유지할 수 있습니다.

2.2 빌드 속도 향상

npm cinode_modules 폴더를 완전히 삭제한 후, 패키지를 다시 설치합니다. 따라서, 기존의 패키지를 확인하고 업데이트하는 npm install보다 더 빠르게 빌드를 수행할 수 있습니다. CI 시스템에서는 빌드 시간을 단축하는 것이 매우 중요한데, npm ci는 이 부분에서도 이점을 제공합니다.

2.3 에러 감지

npm cipackage-lock.jsonpackage.json 사이의 불일치가 있을 경우 빌드를 중단하고 오류를 발생시킵니다. 이는 개발자가 의존성 충돌을 미리 발견하고 수정할 수 있도록 도와줍니다. 반면에 npm install은 이러한 불일치를 감지하지 못하고 넘어갈 수 있으며, 이는 잠재적인 문제로 이어질 수 있습니다.


3. npm ci를 사용하지 않으면 발생할 수 있는 문제점

npm ci를 사용하지 않고 npm install을 CI 환경에서 사용하면 다양한 문제가 발생할 수 있습니다.

3.1 의존성 버전 불일치

가장 큰 문제는 패키지 버전 불일치입니다. npm installpackage.json에 정의된 범위 내에서 패키지를 설치하므로, 만약 새로운 버전이 릴리즈되었다면 그 버전이 자동으로 설치될 수 있습니다. 이렇게 되면 각 빌드마다 패키지의 버전이 달라질 수 있으며, 이는 빌드 실패나 기능적 오류를 초래할 수 있습니다.

예시:

로컬 환경에서 개발자가 npm install을 실행했을 때, 패키지의 최신 버전이 설치되고 package-lock.json이 업데이트되었다고 가정해봅시다. 이때 CI 환경에서는 이전의 package-lock.json 파일을 사용하여 빌드를 시도하는데, 이 과정에서 패키지 버전이 다르게 설치되면서 빌드 실패 또는 테스트 오류가 발생할 수 있습니다.

3.2 테스트 및 배포 실패

CI 환경에서 중요한 테스트 단계에서 패키지 버전이 변경되면 테스트 결과가 일관되지 않을 수 있습니다. 만약 특정 버전에서만 발생하는 버그가 있다면, 패키지 버전의 변경으로 인해 발견되지 못할 가능성이 큽니다. 그 결과, 버그가 포함된 코드가 프로덕션 환경으로 배포되어 심각한 문제를 일으킬 수 있습니다.

예시:

어떤 프로젝트가 패키지의 새로운 버전에서 성능 저하가 발생하는 버그를 가지고 있다고 가정해봅시다. npm install을 사용하여 새로운 버전의 패키지가 설치된다면, CI에서의 테스트는 통과하더라도 프로덕션 환경에서 성능 문제가 발생할 수 있습니다. 이 문제는 npm ci를 사용하여 동일한 버전을 유지했다면 예방할 수 있었을 것입니다.

3.3 보안 취약점 발생

npm install은 패키지의 새로운 버전을 자동으로 설치하기 때문에, 의도하지 않은 보안 취약점이 포함된 버전이 설치될 수 있습니다. 만약 새로운 버전의 패키지가 충분히 검증되지 않은 상태에서 CI 환경에 도입된다면, 보안상의 문제가 발생할 위험이 있습니다.


4. CI 환경에서의 npm ci 사용 방법

CI 환경에서 npm ci를 사용하려면 간단하게 npm install 대신 npm ci 명령어를 실행하면 됩니다. 대부분의 CI 도구(예: Jenkins, Travis CI, GitHub Actions 등)에서 이 명령어를 쉽게 설정할 수 있습니다.

4.1 GitHub Actions 예시

GitHub Actions를 사용하는 경우, .github/workflows 폴더에 있는 YAML 파일에서 다음과 같이 설정할 수 있습니다.

name: Node.js CI

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [14.x, 16.x]

    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm test

위 예시에서 npm ci 명령어를 사용하여 패키지를 설치하고 있습니다. 이렇게 설정하면 CI 빌드 시마다 정확한 패키지 버전을 설치할 수 있으며, 빌드 실패를 방지할 수 있습니다.


5. 결론

npm ci는 CI 환경에서 일관성 있고 안정적인 빌드를 보장하기 위해 매우 중요한 명령어입니다. npm install과는 다르게, npm ci는 정확히 package-lock.json에 명시된 버전만 설치하여 패키지 버전의 불일치로 인한 문제를 방지합니다. 이를 통해 빌드 시간 단축, 테스트의 안정성 향상, 예기치 않은 의존성 충돌 예방 등 여러 가지 이점을 얻을 수 있습니다.따라서, CI 환경에서 npm ci를 사용하는 것은 프로젝트의 안정성을 높이고, 예상치 못한 문제를 방지하는 데 필수적입니다. 이를 통해 더욱 신뢰할 수 있는 배포 파이프라인을 구축할 수 있습니다.