본문 바로가기
JavaScript/Node.js

Node) 패키지 매니저

by 박채니 2022. 12. 28.
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[Node.js 교과서]의 책을 참고하여 포스팅한 개인 공부 내용입니다.

 

패키지 매니저

 

npm 알아보기

 

npm (Node Package Manager)

  • 대부분의 자바스크립트 프로그램은 '패키지'라는 이름으로 npm에 등록 되어 있음
  • 특정 기능을 하는 패키지가 필요하다면 npm에서 찾아 설치
  • npm에 업로드된 노드 모듈을 패키지라고 부름
  • 패키지가 다른 패키지를 사용할 수도 있음 (의존 관계)

package.json으로 패키지 관리하기

서비스에 필요한 패키지를 추가하다보면, 관리가 어려워지고 버전별로 기능이 다를 수 있으므로 프로젝트 설치 시 동일한 버전을 사용해야 합니다.

 

package.json

  • 설치한 패키지의 버전을 관리하는 파일
  • 노드 프로젝트를 시작하기 전에는 폴더 내부에 반드시 package.json 파일을 생성하고 시작
  • npm에서 package.json을 만드는 명령어 제공

package.json 설치 - 콘솔로 프로젝트를 시작할 폴더로 이동

npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (playground-cepark) npmtest
version: (1.0.0) 0.0.1
description: hello package.json
entry point: (helloWorld.js) index.js
test command: 
git repository: (https://github.com/codewritz-repo/playground-cepark.git) 
keywords: 
author: chany
license: (ISC) 
About to write to /Users/parkchaeeun/project/playground-cepark/package.json:

{
  "name": "npmtest", // 프로젝트 이름 입력
  "version": "0.0.1", // 프로젝트 버전 입력
  "description": "hello package.json", // 프로젝트 설명 입력
  "main": "index.js",
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/codewritz-repo/playground-cepark.git"
  },
  "author": "chany", // 이름 입력
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/codewritz-repo/playground-cepark/issues"
  },
  "homepage": "https://github.com/codewritz-repo/playground-cepark#readme"
}


Is this OK? (yes) yes

필요없는 명령어들은 Enter로 넘겼고, 추후 필요하다면 package.json에서 수정하면 됩니다.

 

  • package name
    : 패키지의 이름, package.json의 name 속성에 저장
  • version
    : 패키지 버전
  • entry point
    : 자바스크립트 실행 파일 진입점, package.json의 main 속성에 저장
    : 보통 마지막으로 module.exports 하는 파일 저장
  • test command
    : 코드를 테스트할 때 입력할 명령어 의미, package.json scripts 속성 안의 test 속성에 저장
  • git repository
    : 코드를 저장해둔 깃 저장소 주소를 의미, package.json repository 속성에 저장
    : 나중에 소스에 문제가 생겼을 때 사용자들이 해당 저장소에 방문에 문제 제기 혹은 코드 수정본을 올릴 수 있음
  • keywords
    : package.json의 keywords 속성에 저장
  • license
    : 해당 패키지의 라이선스를 넣음

npm init 명령어가 실행되면 아래와 같은 파일이 생성됩니다.

package.json

{
  "name": "npmtest",
  "version": "0.0.1",
  "description": "hello package.json",
  "main": "index.js",
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/codewritz-repo/playground-cepark.git"
  },
  "author": "chany",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/codewritz-repo/playground-cepark/issues"
  },
  "homepage": "https://github.com/codewritz-repo/playground-cepark#readme"
}

scripts 부분은 npm 명령어를 저장하는 부분입니다.

콘솔에서 npm run [스크립트 명령어]를 입력하면 해당 스크립트가 실행됩니다.

 

@콘솔출력값
$ npm run test

> npmtest@0.0.1 test
> echo "Error: no test specified" && exit 1

Error: no test specified
  • Error: no test specified
    • 콘솔에 해당 문자열을 출력하라는 뜻
  • exit 1
    • 에러와 함께 종료하라는 것을 의미
  • npmtest@0.0.1
    • npmtest 패키지의 0.0.1 버전을 의미

test 스크립트 외에도 scripts 속성에 명령어를 여러 개 등록해두고 사용할 수 있습니다.

보통 start 명령어에 node [파일명]을 저장해두고 npm start로 실행합니다. (start, test 같은 스크립트는 run을 붙이지 않아도 됨)

 

패키지 설치

package.json이 있는 폴더의 콘솔

$ npm install express

added 57 packages, and audited 58 packages in 676ms

7 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

express 패키지를 설치하였고, 설치한 패키지가 package.json에 기록됩니다.

 

package.json

{
  "name": "npmtest",
  "version": "0.0.1",
  "description": "hello package.json",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "chany",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2"
  }
}

dependencies 속성이 생성되고, express라는 이름과 함께 설치된 버전이 저장되었습니다.

추가로 node_modules라는 폴더도 생성되었고, 설치한 패키지들이 들어있는 것을 확인할 수 있습니다.

실제론 express 패키지 하나만 설치하였지만, Express가 의존하는 패키지들까지 설치된 것을 확인할 수 있습니다.

패키지 하나가 다른 여러 패키지에 의존하고, 그 패키지들은 또 다른 패키지들에 의존하기 때문입니다.

 

또한, package-lock.json 파일도 생성되었습니다.

node_modules에 들어있는 패키지들의 정확한 버전과 의존 관계가 담겨있습니다.

npm으로 패키지를 설치, 수정, 삭제할 때마다 패키지들 간의 정확한 내부 의존 관계를 해당 파일에 저장하게 됩니다.

 

**정리

  • package.json
    • 직접 설치한 패키지를 기록하는 파일
  • package-lock.json
    • 패키지 간의 의존 관계를 명시한 파일

 

모듈 여러 개를 동시에 설치할 때는 npm install [패키지1], [패키지2], [...]와 같이 패키지들을 나열합니다.

@콘솔출력값

$ npm install morgan cookie-parser express-session
Debugger attached.

added 11 packages, and audited 69 packages in 764ms

7 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

 

package.json

...

  "dependencies": {
    "cookie-parser": "^1.4.6",
    "express": "^4.18.2",
    "express-session": "^1.17.3",
    "morgan": "^1.10.0"
  }

...

 

실제 배포 시에는 사용되지 않고, 개발 중에만 사용되는 개발용 패키지를 설치할 수도 있습니다.

npm install --save-dev [패키지] [...]로 설치합니다.

@콘솔출력값

$ npm install --save-dev nodemon
Debugger attached.

added 33 packages, and audited 102 packages in 1s

10 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

 

package.json

...

"devDependencies": {
    "nodemon": "^2.0.20"
  }

...

dependencies가 아닌 devDependencies 속성이 생성되었고, 개발용 패키지만을 따로 관리해줍니다.

 

간혹 peerDependencies가 존재하는 경우도 있습니다.

아래와 같이 A라는 라이브러리의 package.json이 다음과 같다고 가정하였을 때

"peerDependencies": {
	"jQeury": "^3.0.0"
}

A라는 라이브러리가 jQuery 3 버전을 직접적으로 사용하진 않지만, jQuery 3 버전이 설치되어 있다고 생각하고 코드를 작성했다는 의미입니다.

따라서 A라는 라이브러리를 사용하는 입장에서 jQuery를 미리 설치하지 않았거나, jQuery 3 버전이 아닌 다른 버전을 설치한 경우 에러가 발생하게 됩니다.

peerDependencies와 다른 버전이 설치 되어있다면, ERESOLVE unable to resolve rependency tree에러 메세지가 나타납니다.

  • 해결방법
    • peerDependencies에 맞게 설치하기
    • npm i --force로 강제로 모든 버전 설치 (A는 jQuery 3 버전이고, B는 jQuery 2 버전일 때..)
    • npm i --legacy-peer-deps로 peerDependencies 무시
    • 애초에 peerDependencies가 서로 충돌하지 않게 패키지 설치하기..^^ (패키지 설치 시 peerDependencies 있는지 확인)

npm 전역(global) 설치

  • 패키지를 현재 폴더의 node_modules에 설치하는 것이 아닌 npm이 설치되어 있는 폴더에 설치
  • 해당 경로는 보통 시스템 환경 변수에 등록되어 있으므로 전역 설치한 패키지는 콘솔 명령어로 사용 가능
  • package.json에 기록되지 않음
  • package.json에 기록을 원한다면 npx 명령어 사용
// 맥은 권한 때메 sudo로 설치
// rimraf는 rm -rf 명령어를 윈도에서도 사용할 수 있게 해주는 패키지

$ sudo npm install --global rimraf
Password:

added 12 packages, and audited 13 packages in 343ms

2 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

 

node_modules 폴더 삭제

$ rimraf node_modules

node_modules 폴더를 지웠기 때문에 package.json과 package-lock.json 만 남아있습니다.

하지만 package.json에 설치한 패키지 내역이 남아있으므로 문제가 되진 않습니다. npm install 한다면 알아서 다시 설치되기 때문이죠.

따라서 node_modules보다 package.json, package-lock.json이 훨씬 중요!!!합니다.

 

* npm i 를 할 때마다 package.json과 package-lock.json이 변하므로, 실제 서비스를 배포할 때는 npm ci 명령어를 사용하길 권장

 


패키지 버전 이해하기

노드 패키지는 SemVer 방식의 버전의 넘버링을 이용하므로 항상 세 자리입니다.

 

SemVer (Semantic Versioning)

- 버전을 구성하는 세 자리가 모두 의미를 가지고 있음

- 각 패키지 간의 의존 관계가 복잡하고, 버전이 다르므로 버전 번호를 어떻게 정하고 올려야 하는지를 명시하는 규칙

 

  • 버전의 첫 번째 자리 - 메이저 버전
    • 메이저 버전이 0이면 초기 개발 중이라는 뜻
    • 메이저 버전이 1이면 정식 버전이라는 뜻
    • 하위 호환이 안 될 정도로 패키지의 내용이 수정되었을 때 주로 올림
    • 1.5.0 사용자 → 2.0.0 업데이트 : 에러 발생 확률 ↑
  • 버전의 두 번째 자리 - 마이너 버전
    • 하위 호환이 되는 기능 업데이트를 할 때 주로 올림
    • 1.5.0 사용자 → 1.6.0 업데이트 : 사용 가능
  • 버전의 세 번째 자리 - 패치 버전
    • 기존 기능에 문제가 있어 수정한 것을 내놓았을 때 주로 사용

새 버전 배포 후 버전 내용을 수정하면 안됩니다.

만일 수정사항이 생긴다면 메이저/마이너/패치 버전 중 하나를 의미에 맞게 올려서 새로운 버전을 배포해야 합니다.

(패키지 간의 의존 관계와 사용자 편리를 위해)

 

이 외에도 ^ 혹은 ~, >, <와 같은 문자를 볼 수도 있는데, 이는 설치하거나 업데이트할 때 어떤 버전을 설치해야 하는지 알려줍니다.

  • ^ 기호
    • 마이너 버전까지만 설치하거나 업데이트
    • npm i express@^1.1.1 → 1.1.1 이상부터 2.0.0 미만 버전까지 설치 (1.x.x로 표현)
  • ~ 기호
    • 패치 버전까지만 설치하거나 업데이트
    • npm i express@~1.1.1 → 1.1.1 이상부터 1.2.0 미만 버전까지 설치 (1.1.x로 표현)
  • >, <, >=, <=, = 기호
    • 초과, 미만, 이상, 이하, 동일을 뜻
    • npm i express@>1.1.1 → 1.1.1 버전보다 높은 버전 설치
  • @latest
    • 아전된 최신 버전의 패키지 설치
    • npm i express@x 로도 표현
  • @next
    • 가장 최근 배포판 사용
    • 안정되지 않은 알파나 배타 버전의 패키지를 설치할 수 있음
  • 알파나 배타 버전
    • 1.1.1-alpha.0 나 2.0.0-beta.1 처럼 표시

기타 npm 명령어

  • npm outdated
    • 업데이트할 수 있는 패키지가 있는지 확인
    • npm으로 설치한 패키지 사용 시 새로운 기능이 추가되거나 버그를 고친 새로운 버전이 나올 때가 있음
    • Current와 Wanted가 다르다면 업데이트가 필요
    • Latest는 해당 패키지의 최신 버전이지만 package.json에 적힌 버전 범위와 다르면 설치하지 않음
  • npm update
    • 업데이트 가능한 모든 패키지가 Wanted에 적힌 버전으로 업데이트
  • npm uninstall [패키지 이름]
    • 해당 패키지를 제거
    • node_modules 폴더와 package.json에서 사라짐
    • npm rm [패키지 이름]으로 줄여쓸 수 있음
  • npm search [검색어]
    • npm의 패키지 검색
    • package.json의 keywords가 사용됨
  • npm info [패키지 이름]
    • 패키지의 세부 정보를 파악하고자 할 때 사용
    • package.json의 내용과 의존 관계, 설치 가능한 버전 정보 등이 표시
  • npm login
    • npm 로그인을 위한 명령어
    • 패키지를 배포할 때 로그인이 필요
  • npm whoami
    • 로그인한 사용자가 누구인지 알림
    • 로그인된 상태가 아니라면 에러 발생
  • npm logout
    • npm login으로 로그인한 계정을 로그아웃할 때 사용
  • npm version [버전]
    • package.json의 버전을 올림
    • 원하는 숫자를 넣거나 major, minor, patch라는 문자열을 넣어 해당 부분의 숫자를 1 올림
    • 예) npm version 5.3.2, npm version minor
  • npm deprecate [패키지 이름] [버전] [메시지]
    • 해당 패키지를 설치할 때 경고 메시지를 띄우게 함
    • 자신의 패키지에만 해당 명령어 사용 가능
    • 다른 사용자들이 버그가 있는 버전의 패키지를 설치할 때 경고 메세지 출력
  • npm publish
    • 자신이 만든 패키지를 배포할 때 사용
  • npm unpublish
    • 배포한 패키지를 제거할 때 사용
    • 24시간 이내에 배포한 패키지만 제거 가능 (의존성 관계 때문)

 


패키지 배포하기

 

npm 계정 만들기

  1. npm 웹 사이트 (https://www.npmjs.com/) 우측 상단의 Sign Up을 통해 회원가입
  2. 회원 가입 confirm 메일 확인
  3. npm login 명령어를 입력해 생성한 계정으로 로그인. 이메일 발송된 OTP 코드도 입력해야 로그인됨

index.js

module.exports = () => {
    return 'hello package';
}

 

npm은 패키지 이름이 겹치는 것을 허용하지 않으므로 사용하지 않는 패키지 이름으로 배포해야 합니다.

// 패키지 배포
$ npm publish
  • npm info [패키지 이름]
    • 해당 패키지 명을 가지고 있는 패키지 정보

해당 명령어로 이름을 사용하고 있는지 확인할 수 있습니다.

정보가 나온다면 누군가가 사용하고 있는 패키지 명이고, npm ERR! code E404 에러가 발생하면 사용 가능합니다.

 

버전을 올려서 출시해보기

$ npm publish

...
npm ERR! code E403
...

해당 메세지가 나온다면 이미 출시한 버전이라는 의미이고, 버전을 올리기 위해 npm version 명령어를 사용합니다.

 

$ npm version patch

버전을 올려 배포하는데에 성공하였습니다.

실무에서는 버전을 올려 배포할 때는 release-it이라는 패키지를 주로 사용합니다. (https://github.com/release-it/release-it)

 

배포한 패키지 삭제해보기

// npm unpublish [패키지 이름] --force
$ npm unpublish npmtest-1234 --force

삭제 후 npm info 명령어를 사용해 404가 뜬다면 정상적으로 지워진 것입니다.