package.json, node_modules, 그리고 package-lock.json간의 관계

npm이란 무엇인가?


npm(node package manager)은 노드 패키지 매니저의 약자로써, 모듈(패키지) 관리(설치, 업데이트, 삭제)를 하기위한 매니저이다.

노드의 정의는 크게 2가지로 나눌 수 있다.

  • 서버: 네트워크를 통해 클라이언트에 정보나 서비스를 제공하는 컴퓨터 또는 프로그램을 말한다.
  • 자바스크립트 런타임: 자바스크립트 프로그램을 컴퓨터에서 실행할 수 있게 하는 환경을 말한다.

npm에서의 노드는 자바스크립트 런타임을 의미한다. 즉, 대부분의 자바스크립트 프로그램은 패키지라는 이름으로 npm에 등록되어 특정 기능을 하는 패키지가 필요하면, npm에서 찾아 설치하면 된다. npm에 업로드된 노드 모듈(자바스크립트 기반으로 만들어진 프로그램 파일)을 패키지라고 부른다. 그래서, npm을 자바스크립트 패키지 매니저라고도 부른다.

모듈이 다른 모듈을 사용할 수 있는 것처럼, 어떤 패키지가 다른 패키지를 사용할 수 있다. 이런 관계를 의존 관계(dependencies)라고 부른다.

이런 패키지 매니저가 npm 말고도, 페이스북에서 만든 yarn도 있다.


npm은 왜 사용하는가?


npm에는 약 60만 개(2018년 6월 기준)의 패키지가 등록되어 있는데, 이는 대부분 오픈소스로 등록되어 있어 웹개발을 할 때 많은 도움이 된다.

또한, 새로운 모듈(패키지)를 설치하거나 관리를 위해, npm은 npm cli를 제공해서 cli 명령어로 설치, 업데이트 및 삭제를 쉽게 할 수 있다.


package.json이란 무엇인가?


package.json이란, 생성한 프로젝트의 메타정보와 이 프로젝트가 의존하고 있는(설치한) 모듈들에 대한 정보들을 json 형태로 모아놓은 파일이다.

package.json 파일내에 있는 각 속성들에 대해서는 다음 링크를 참고하면 좋을 것 같다.
package.json에 대한 자세한 설명


package.json은 왜 사용하는가?


만약 이 package.json 파일을 사용하지 않을 경우 다음과 같은 문제가 발생할 수 있다.

  • 프로젝트에서 사용하는 외부 모듈들이 많아지게 되면, 관리하기가 어려워진다.
  • 각 패키지들은 고유한 버전이 있기 때문에, 따로 기록해 두어야 한다. 왜냐하면, 패키지의 버전도 빈번하게 업데이트가 되기 때문이다.
  • 새로운 프로젝트를 진행할 때, 필요한 모듈들이 많다면 매번 npm 명령으로 설치해야 하는 번거로움이 있다.

이런 경우, 필요한 패키지들의 목록을 파일로 정리해놓고, 목록 파일을 이용하여 단 한번의 명령어로 필요한 패키지들을 모두 설치할 수 있다. 이러한 패키지 정의 파일을 package.json 파일이라고 한다. 즉, package.json은 프로젝트에 대한 메타정보, 그리고 설치한 패키지의 의존성 및 버전을 관리하는 파일이다.

vue cli로 Vue 프로젝트를 생성하면, 자동으로 package.json 파일이 만들어 지지만, npm init을 사용하면, package.json 파일을 직접 만들 수도 있다. 그리고, 팀 내에서 동일한 개발환경을 구축하려고 할 때, 이미 작성된 package.json 파일이 있다면, 팀 내에 배포하여 동일한 개발환경을 빠르게 구축할 수 있다.


node_modules란 무엇인가?


Vue 프로젝트를 생성하면, package.json 파일 뿐만 아니라, node_modules 디렉토리가 같이 생성된다. package.json에는 프로젝트가 의존하고 있는 모듈들에 대한 정보가 나와있고, node_modules 디렉토리에는 package.json에 있는 모듈 뿐만 아니라, package.json에 있는 모듈이 의존하고 있는 모듈 전부를 포함하고 있다. 그래서 node_modules 디렉토리안에는 정말 많은 모듈들이 들어가 있다. npm으로 새로운 모듈(패키지)를 설치하게 되면, package.json과 node_modules에 추가된다.

참고로, git에 커밋할 때, node_modules을 제외해도 된다. 왜냐하면, node_modules가 없어도, package.json에 설치한 패키지들이 모두 있기 때문에, npm install로 node_modules를 언제든지 설치가 가능하기 때문이다.


package-lock.json이란 무엇인가?


package-lock.json은 이 package-lock.json이 생성되는 시점의 의존성 트리(node_modules)에 대한 정보를 가지고 있는 파일을 말한다. 의존성 트리는 package.json에 등록된 모듈과 그 모듈들이 의존하고 있는 모듈 전부를 포함하고 있기 때문에, 결국 package-lock.json도 이 모든 모듈들을 가지고 있다. npm을 사용해서 node_modules나 package.json을 수정하게 되면, package-lock.json 또한 자동으로 업데이트가 된다.


package-lock.json은 왜 사용하는가?


package.json 파일에는 의존성 모듈(dependencies)의 version range가 사용된다. version range란, 특정 버전이 아닌, 버전의 범위를 의미한다. 예를 들어, npm install express로 express를 설치하면, package.json에는 ‘^4.10.3’(Caret Ranges)과 같이 버전 범위가 추가된다. 이 버전의 express가 추가된 package.json을 가지고 npm install을 실행하면, 현재는 4.10.3 버전이 설치되지만, express의 버전이 업데이트된 상태로 publish가 된 후에, 동일한 package.json 파일로 npm install을 실행했을 경우, 원래 버전이 아닌, 새로 업데이트된 버전으로 express가 변경된다. 이럴 경우, 기존에 가지고 있던 node_modules(의존성 트리)에 있던 모듈의 버전과 충돌이 일어나, 오류를 발생시킬 수 있다. 이 문제를 해결하기 위해, package-lock.json을 사용하는 것이다.

package-lock.json은 node_modules(의존성 트리)에 대한 정보를 가지고 있는데, package-lock.json이 업데이트가 되는 시점에 node_modules(의존성 트리)을 재생성할 수 있다. 그래서, package-lock.json 파일이 있다면, npm install로 package.json과 package-lock.json에 있는 모듈이 새로 업데이트되는 동시에, node_modules(의존성 트리)가 새로 생성되어, 각 파일이 가지고 있는 모듈의 버전을 동일하게 맞출 수가 있게 된다.

즉, package.json에 있는 모듈의 버전은 npm install을 수행하는 시점에 따라 달라진다. 이 말은, npm install을 수행하는 시점에 publish 되어있는 모듈의 버전으로 업데이트가 된다는 뜻이다. 이렇게 되면, package.json과 package-lock.json에 있는 모듈이 같은 버전으로 업데이트가 되고, 이때 package-lock.json 때문에 node_modules(의존성 트리)가 재생성되어, 3개의 파일에 있는 모듈이 모두 같은 버전으로 맞춰지게 되어 오류가 안나게 된다.

이런 이유로, git에 커밋할 때, package.json 파일 뿐만 아니라, packge-lock.json 파일 또한 같이 커밋을 해야 한다.


패키지 version


노드 패키지들의 버전은 세 자리로 되어있는데, 이는 SemVer 방식의 버전 넘버링을 따르기 때문이다. SemVer는 Semantic Versioning(유의적 버전)의 약어인데, 이는 버전을 구성하는 세 자리가 모두 의미가 있다는 뜻이다.

서비스를 개발하다 보면, 정말 많은 패키지들을 사용하게 되는데, 이런 많은 패키지들이 서로 얽히다 보면 문제가 생길 수 있다. 예를 들어, 어떤 패키지의 버전을 업그레이드 했는데, 그것을 사용하는 다른 패키지에서 에러가 발생한다면 문제가 된다. 따라서 버전 번호를 어떻게 정하고, 올려야 하는지를 명시하는 규칙을 만들었는데, 이것이 바로 SemVer 이다.

버전은 세 자리로 구성되어 있다.

  • major 버전: 하위 호환이 안될 정도로 패키지의 내용이 수정되었을 때 올린다.
    • 주 버전이 0이면, 초기 개발 중이라는 뜻이다.
    • 1부터는 정식 버전이다.
    • 만약, 1.7.1에서 2.0.0으로 올렸다면, 1.7.1 버전 패키지를 사용하고 있던 사람들이 2.0.0으로 업데이트 했을 때, 에러가 발생할 확률이 크다.
  • minor 버전: 하위 호환이 되는 업데이트 시에 올린다.
    • 만약, 1.7.1에서 1.8.0으로 올렸다면, 1.7.1 사용자가 1.8.0으로 업데이트 했을 때, 아무 문제가 없어야 한다.
  • patch 버전: 새로운 기능이 추가되었다기 보다는, 기존 기능에 문제가 있어 수정한 것을 내놓았을 때, patch 버전을 올린다.
    • 1.7.0에서 1.7.1으로 올렸다면, 업데이트 후 문제가 없어야 한다.

새 버전을 배포한 후에는, 그 버전의 내용을 절대 수정하면 안된다. 만약, 수정 사항이 생기면, major버전, minor 버전, patch버전 중 하나를 의미에 맞게 올려서 새로운 버전으로 배포해야 한다.

버전의 숫자마다 의미가 부여되어 있기 때문에, 다른 패키지를 사용할 때도 버전만 보고 에러 발생 여부를 판단할 수 있다.

만약, 의존하는 패키지의 major 버전이 업데이트 되었다면, 기존 코드와 호환이 되지 않을 확률이 크기 때문에, 미리 주의를 기울여야 한다. 만약, minor나 patch 버전으로 업데이트 되었다면, 상대적으로 안심하고 버전을 올리 수 있다.

package.json에는 버전 말고도, 다른 기호(‘^’, ‘~’ 등)들이 있다. 이런 기호들은 버전에는 포함되지 않지만, 설치 또는 업데이트 시 어떤 버전의 범위를 설치해야 하는지 알 수 있다.

  • ^(캐럿): minor 버전까지만 설치 또는 업데이트한다.
    • 예를 들어, npm install express@^1.7.1 이라면, 1.7.1 <= 버전 < 2.0.0까지 설치가 되고, 2.0.0은 설치되지 않는다.
  • ~(틸트): patch 버전까지만 설치 또는 업데이트한다.
    • npm install express@~1.7.1 이라면, 1.7.1 <= 버전 < 1.8.0까지 설치가 된다.

~ 보다 ^가 많이 사용되는 이유는, minor 버전까지는 하위 호환이 되기 때문이다.

npm semver calculator에 방문하면, 패키지 별로 버전 표기법을 사용하여, 업데이터 버전 범위를 확인 가능하다.

참고로, npm install 명령어의 패키명 뒤에 @버전을 추가하면 패키지 버전을 지정하여 설치할 수 있다.


nodemon이란?


nodemon은 프로젝트 폴더의 파일들을 모니터링하고 있다가, 파일이 수정될 경우 자동으로 서버를 재시작을 해준다. nodemon을 위해서 소스에 다른 설정을 추가할 필요도 없기 때문에 상당히 편리하게 사용할 수 있습니다.


–save와 –dev


npm install 모듈명 --save 을 입력하면, 설치하는 모듈을 package.json에 등록할 수 있다.(npm@5 부터는 –save는 기본옵션이다.)
그래서, npm@5 부터는 –save 옵션을 사용하지 않더라도, 모든 install 명령은 package.json의 dependencies에 설치되어 관리된다.
그리고, npm install 모듈명 --save --dev을 입력하면, 설치하는 모듈을 package.json에 등록할 수 있을 뿐만 아니라, –dev 때문에, package.json 파일에서 devDepencies에 등록된다. devDepencies에서 관리하는 모듈들은 개발용 모듈들이고, depencies에서 관리하는 모듈들은 배포용 모듈들이다.

package.json에 명시된 모든 의존 패키지를 한번에 설치하기 위해서는, npm install 명령어를 사용하면 된다.


자주 사용하는 npm 명령어


목적 npm 명령어
package.json 생성 npm init
패키지 로컬 설치 npm install package-name
패키지 전역 설치 npm install -g package-name
패키지 개발 설치 npm install –save –dev package-name
package.json의 모든 패키지 설치 npm install
package.json의 모든 패키지 설치 npm install
로컬/개발 패키지 제거 npm uninstall package-name
전역 패키지 제거 npm uninstall -g package-name
패키지 업데이트 npm update package-name
버전 확인 npm -v
npm 명령어 설명 참조 npm help