Node.js 패키지 관리

2024. 12. 14. 01:34Programming Language/Node.js

01. npm이란?

npm이란

npm은 node package manager의 줄임말로 Node.js 환경에서 JavaScript 라이브러리와 패키지를 설치, 관리, 공유하는 도구이다.

npm은 웹사이트를 통해 패키지 검색과 관리 정보를 제공하고 CLI(Command Line Interface) 도구로 터미널에서 명령어를 입력해 패키지를 설치하거나 관리한다.

레지스트리는 전 세계 개발자들이 공유하는 패키지 저장소로, 여기에서 패키지를 다운로드하거나 업로드할 수 있다

 

02. npm package 설치하기

npm은 프로젝트를 생성하는 방법은 Visual Studio Code로 프로젝트를 하나 생성하고 터미널을 연 후에 

npm init -y

 

로 입력해주면 

이런 로그가 나오면서 

package.json파일이 생성된다.

이 내부에 위의 로그에 찍힌 내용이 작성되어 있는걸 볼 수 있다.

여기서 우리는 axios라는 패키지를 설치해서 사용해보려고 하는데 이 axios라는 패키지 JavaScript에서 HTTP 요청을 쉽게 처리하기 위해 사용하는 라이브러리다. 웹 브라우저나 Node.js 환경에서 서버와 데이터를 주고받을 때 유용하다. 예를 들어, API로부터 데이터를 가져오거나 서버에 데이터를 보낼 때 많이 사용된다.

axios라는 라이브러리를 설치하기 위해서는 npm을 이용하면 된다.

 

터미널에 

npm install axios

이렇게 입력하면 해당 패키지가 설치된다.

그러면 package.json 파일 내부의 dependencis라는 항목이 생성되면서 그 내부에 

이렇게 해당 패키지 명 : 버전으로 되어 있는 값이 추가된다.

 

이 axios라는 패키지를 사용하기 위해서 자바스크립트 파일을 하나 생성해주고

require를 통해서 axios 모듈을 가져와주자.

그리고 axios의 get 메서드(get 요청을 보내는 메서드)를 통해서 google에 요청을 보내보자.

그리고 이 결과를 사용하기 위해서 then을 통해서 응답을 받아서 콘솔에 찍어보자.

그리고 혹시 exception에 대해서 확인하기 위해서 catch를 통해서 error가 날 경우 error를 출력해보도록 하자.

이제 터미널을 통해서 해당 js파일을 실행해보면 

node axiosTest.js

그러면 응답으로 

이렇게 google에서 받아온 내용들을 출력해준다.

 

03. node_module 살펴보기

package.json이 생성되고 어떤 패키지를 하나 생성하면 아래와 같이 node_modules라는 폴더하나가 생성되는데 이전에 우리가 axios를 설치했을때 생성되었것이다.

해당 폴더에는 우리가 설치했던 axios를 제외한 다른 여러 라이브러리들이 같이 설치되었는데 

이건 axios와 의존관계에 있는 다른 라이브러리들이 같이 설치되어 있는 것이다.(axios가 굴러가게 하기 위해서 필요한 다른 부수적인 라이브러리들을 의미한다)

axios라이브러리의 내부를 보면 

이렇게 또 package.json이 있는데 이 내부를 보면 

이 axios를 사용하기 위한 dependencies가 존재한다.

 

node_modules를 보면 이 세 라이브러리 들이 같이 설치된 것을 볼 수 있다.

이 라이브러리의 내부에도 package.json이 있고 이렇게 서로 서로 필요로 한 라이브러리를 같이 설치하게 된다.

그게 모두 node_modules에 존재하고 있다고 보면 된다.

 

04. Sementic Versioning

Semantic Versioning(시맨틱 버저닝)은 소프트웨어 버전을 체계적으로 관리하기 위한 규칙이다. 보통 MAJOR.MINOR.PATCH 형식으로 버전을 표현한다. 이 숫자들은 각각 특정한 의미를 가진다.

  1. MAJOR (주 버전): 호환되지 않는 큰 변경이 있을 때 증가한다.
    예: 기존 기능이 삭제되거나 API 사용 방식이 완전히 바뀌는 경우.
  2. MINOR (부 버전): 새로운 기능 추가가 있지만, 기존 기능과 호환성은 유지될 때 증가한다.
    예: 새로운 옵션이 추가되거나 기존 코드를 그대로 사용해도 문제가 없는 경우.
  3. PATCH (수정 버전): 버그 수정과 같은 작은 변경이 있을 때 증가한다.
    예: 기능에는 변화가 없고, 기존 코드의 문제점만 고친 경우.

예시

  • 1.0.0: 처음 출시된 버전.
  • 1.1.0: 새로운 기능이 추가되었지만 기존과 호환된다.
  • 1.1.1: 버그가 수정된 버전.
  • 2.0.0: 큰 변화로 기존과 호환되지 않는 업데이트.

여기서 버전에 ^2.0.0 이라던가 ~2.0.0과 같이 특수 기호를 붙이는데 이것 또한 각각에 의미가 있다.

 

캐럿(^)

주 버전(MAJOR)이 바뀌지 않는 범위에서 최신 버전까지 허용한다.

  • 예를 들어, "^1.2.3"이라면 1.x.x 범위 내에서 가장 최신 버전을 허용한다.
    (1.3.0, 1.9.5 등은 허용되지만 2.0.0은 안 됨.)

 

  • "^1.2.3" → >=1.2.3 <2.0.0
  • "^0.3.4" → >=0.3.4 <0.4.0 (주의: 주 버전이 0일 때는 부 버전(MINOR)도 고정됨.)
  • "^0.0.5" → >=0.0.5 <0.0.6

안전한 업데이트를 위한 범위 설정. MAJOR가 바뀌면 호환성이 깨질 가능성이 있으므로 제한함.

 

틸드(~)

부 버전(MINOR)이 바뀌지 않는 범위에서 최신 버전까지 허용한다.

  • 예를 들어, "~1.2.3"이라면 1.2.x 범위 내에서 최신 버전을 허용한다.
    (1.2.4, 1.2.9 등은 허용되지만 1.3.0은 안 됨.)

 

  • "~1.2.3" → >=1.2.3 <1.3.0
  • "~0.3.4" → >=0.3.4 <0.4.0
  • "~0.0.5" → >=0.0.5 <0.0.6

작은 버그 수정(PATCH)만 업데이트하려고 할 때 주로 사용함.

 

이 두 특수 기호는 자동으로 버전을 업그레이드를 어디까지 할지에 대한 내용을 설정하는 기호라고 생각하면 된다.

package-lock.json 파일에는 설치된 정확한 버전이 기록된다.

따라서 npm install을 실행하면 기존에 기록된 package-lock.json 버전이 우선되지만, 새로운 환경에서 package.json만 있을 경우에는 지정된 범위 내에서 최신 버전으로 설치된다.

 

만약 호환성이 걱정된다면, 특정 버전을 고정하거나(예: "1.2.3"), 설치 전에 테스트를 거쳐야 한다.

 

05. package-lock.json

package-lock.json은 해당 파일이 생성되는 시점의 node_modules의 라이브러리들의 정보를 갖고 있는 파일이라고 보면 된다.

 

package-lock.json의 주요 목적과 역할은 여러가지가 있는데 나열해보자면

  • 의존성 버전 고정 (Locking Dependencies) - package.json에는 ^과 ~로 인해서 설치할때마다 해당 버전이 변경될 가능성이 있으나 package-lock.json 파일은 의존성 트리(직접 의존성과 하위 의존성)의 정확한 버전을 고정한다.
    이를 통해 협업 시 또는 CI/CD 환경에서 동일한 의존성 버전을 보장한다.
    ex) 다른 사용자가 만든 node_module가 없는 프로젝트에 사용자가 npm install을 실행하면, package-lock.json에 명시된 대로 의존성들이 설치된다
  • 단순히 최상위 package.json에 기록된 의존성 정보(직접 의존성)뿐 아니라, 모든 하위 의존성까지 포함한 의존성 트리를 저장한다.

요약하자면, package.json으로 패키지의 버전을 관리하나 정확한 버전으로 고정되어 있는게 아니기 때문에 package-lock.json을 통해서 명확한 의존성버전을 보장해주기 위해 사용하는 것이다라고 생각하면 될듯하다.

 

06. npm audit

npm audit이란 명령어를 사용하면 우리가 설치한 Node.js 프로젝트에서 사용하는 의존성(Dependencies) 패키지들 중

보안 취약점(Security Vulnerabilities)이 있는지 확인하고 해결하기 위한 정보를 제공하는 명령어이다.

이 명령어는 프로젝트에서 설치된 패키지와 그 하위 의존성(dependency tree)을 분석하고, 보안 취약점이 포함된 패키지를 알려준다.

 

지금 우리가 갖고 있는 node_modules와 package-lock.json을 제거해주고

 package.json에 있는 버전을 강제로 아주 낮게 변경한 다음에 

npm install

을 입력해주면

이렇게 출력 되는데 해당 내용은 axios@0.19.2 버전이 더 이상 권장되지 않으며(deprecated), 보안 취약점이 존재하다고 말한다.

Critical security vulnerability는 axios@0.19.2에서 발견된 보안 문제를 의미하며, 이는 0.21.1 이상 버전에서 수정되었다고 작성되어 있다.

그리고 그내용은 https://github.com/axios/axios/pull/3410 이 링크를 통해 볼 수 있다고 한다.

그리고 프로젝트 내의 의존성 패키지에서 2개의 심각도 높은 보안 취약점(High Severity)이 감지되었다고 한다.

그리고 아랫줄에 npm audit fix --force라는 코드를 실행시켜서 심각성에 대한 조치를 하라고 작성되어 있다.

 

그니까 적어도 심각한 취약성을 해결하기 위해선 0.21.1 버전 위로 설치해야한다고 알려주는 것이다.

그걸 해결할때 지금 우리가 보고 있는 npm audit 을 사용하면 되는데 먼저 fix 옵션을 넣지 않고 audit을 실행시켜보자.

그러면 이렇게 보안 취약성이 있는 패키지는 어떤거고 너가 설치한 버전은 몇버전 이하이기에 발생하는지 알리는 report가 날라온다.

각각 보면 axios에서는 Server-Side Request Forgery와 Inefficient Regular Expression Complexity, Cross-Site Request Forgery의 보안 취약점이 존재하고 follow-redirects라는 패키지에서는 인증 정보나 민감한 데이터를 의도치 않게 노출할 가능성이 있으며  URL 처리를 부적절하게 처리해 공격자가 악용할 수 있는 취약점이 존재하고 다른 호스트에 대해 프록시 인증 헤더가 유지되어 보안 문제가 발생할 수 있는 취약점이 있다고 한다.

 

물론 이런 취약점을 나열한건 별로 의미는 없지만 결국 각각 한줄 한줄마다 취약점에 대한 내용임을 확인할 수 있다.

 

그러면 fix 옵션과 --force옵션을 넣어서 수정을 요청해보자.

npm audit fix --force

 

이렇게 하면 

이렇게 버전을 최신으로 변경시켜주는 것을 볼 수 있다.

물론 axios말고 오류가 있었던 패키지도 

이렇게 버전을 올려준것을 볼수 있다.

 

이 방법이 해결을 해줄 순 있으나 하나 조심해야할 점은 Breaking Change를 발생시킬 수 있다는 점이다.

 

Breaking Change(브레이킹 체인지)란, 소프트웨어 또는 라이브러리의 업데이트에서 기존 코드와의 호환성(compatibility)을 깨는 변경 사항을 말한다.

위에서 --force를 사용했던 변경과 같은 변경으로 인해 이전 버전에서 동작하던 코드가 새로운 버전에서는 제대로 작동하지 않거나 에러가 발생할 수 있다.

 

Breaking Change가 발생되는 요인으론 

1) API나 함수의 제거

  • 예: axios의 특정 메서드나 옵션이 제거됨.
// 이전 버전
axios.get(url, { params: { id: 123 } });

// 새로운 버전 (변경된 API)
axios.get(url, { searchParams: { id: 123 } }); // 에러 발생

2) 기본 동작 변경

  • 라이브러리의 기본 설정 값이 변경되거나 함수가 다르게 동작함.
    예: 새 버전에서 HTTP 요청 타임아웃 기본값이 변경됨.

3) 기본 지원 환경 변경

  • 더 이상 특정 Node.js 버전이나 브라우저 버전을 지원하지 않을 수 있음.

4) 의존성 변경

  • 라이브러리가 의존하는 다른 패키지가 업데이트되면서 기존 동작에 영향을 줄 수 있음.

등이 있다.

 

이런 점들을 고려해가면서 버전을 올릴때(npm audit fix --force와 같이 버전의 호환성을 생각하지않고 강제로 버젼을 번경시키려고 할때)는 신중하게 선택해야만 한다.

 

07. npm instaill -g

npm install라는 npm 명령어에 -g라는 플래그를 주면 전역으로 패키지를 설치하게 된다.

보통

npm install 패키지명

이라는 명령어를 사용하면 로컬에 설치하게 되는데 이렇게 설치된 패키지는 현재 프로젝트 디렉토리의 node_modules 폴더에 저장되게 된다.

그리고 그렇게 설치된 패키지는 해당 프로젝트에서만 사용이 가능하다.

 

반면 

npm install -g 패키지명

을 통해서 패키지를 설치하면 글로벌로 설치하게 되는데 이렇게 설치된 패키지는 시스템 전체에서 사용이 가능하게 된다.

보통 CLI(Command Line Interface) 도구와 같은 명령줄에서 실행하는 유틸리티를 설치할 때 글로벌로 설치해서 사용하는 경우가 많다.

해당 패키지는 Node.js가 사용하는 글로벌 디렉토리(OS별로 다름)에 설치가 되고 이렇게 설치하면 모든 프로젝트 및 명령줄에서 사용 가능해진다. 

 

이렇게 글로벌 설치가 필요한 경우는 CLI 도구를 설치할때, 전역적으로 실행하고 싶은 명령어를 추가하고 싶을때인데

  • CLI 도구를 설치할때 - eslint, npm, nodemon, http-server 등의 패키지를 설치하는 경우 글로벌로 설치하는 경우가 많고 이 패키지들은 특정 프로젝트에 국한되지 않고 명령줄에서 직접 실행되는 경우가 많다.
  • 전역적으로 실행하고 싶은 명령어를 추가하고 싶을때 - 글로벌로 설치하면 명령어가 시스템의 환경변수(PATH)에 추가되어 어디서든 실행할 수 있기에 글로벌로 설치하게 된다.

로컬 설치를 하는 경우는 특정 프로젝트에서만 사용하고 싶은 패키지일때 이며 이 경우에는 다른 프로젝트에 영향을 주지 않고 독립적으로 관리할 수 있다.

 

글로벌 설치된 패키지 확인하고 싶을땐 

npm list -g --depth=0

명령어를 사용해서 확인할 수 있으며 --depth=0는 설치된 패키지의 최상위 목록만 표시하는 것으로 하위 의존성 말고 메인 패키지에 대한 리스트를 출력하라는 옵션이 된다.

 

위에서 이야기했던 글로벌 설치된 패키지가 설치되는 디렉토리는 OS에 따라서 다른 디렉토리에 저장되게 되는데 

 

  • Windows: %USERPROFILE%\AppData\Roaming\npm\node_modules
  • macOS / Linux: /usr/local/lib/node_modules 또는 /home/<사용자>/.npm-global/lib/node_modules (사용자 설정에 따라 다름)

에 설치되게 된다.

설치된 디렉토리는 NPM 설정으로 확인 가능하다.

npm config get prefix

 

명령어를 입력하면 글로벌 패키지가 설치되는 디렉토리를 출력한다.

 

그런데 글로벌 설치를 할 경우에는 글로벌 설치된 패키지프로젝트의 로컬 설치된 패키지와 버전 충돌을 일으킬 수 있고, 우리가 해당 프로젝트를 github같은 곳에 올려져 있을때 다른 사람이 내려 받아 사용할 경우 글로벌로 설치된 패키지는 package.json에 없기에 동일한 환경에서 프로젝트를 실행시킬 수 없다. 그렇기에  프로젝트 의존성으로 필요한 패키지로컬 설치를 권장한다.

 

08. yarm

yarn은 Facebook(현재 Meta)에서 개발한 패키지 관리자로 Node.js 환경에서 npm의 대안으로 사용된다.

JavaScript 프로젝트의 의존성 관리를 위해 개발되었으며 npm의 단점을 보완하고 추가 기능을 제공한다.

 

yarn은 의존성을 병렬로 설치하기 때문에(npm도 7 이상 버전부터 병렬화되었다 ), npm에 비해 설치 속도가 빠르고 캐싱(caching)을 사용해 한 번 설치한 패키지는 다시 다운로드하지 않고 재사용한다.

 

Yarn은 package-lock.json파일 대신 yarn.lock이란 파일 하나만 생성해서 사용한다.

그리고 이미 설치한 패키지를 캐싱해두고, 인터넷 연결 없이도 패키지를 설치할 수 있습니다.

 

Yarn은 체크섬 검증을 통해 패키지가 변조되지 않았는지 확인한다.

 

npm과 yarn중에 선택해서 사용하면 되는데 yarn은 따로 설치를 해줘야만 한다.

웃기게도 yarn은 npm을 사용해서 설치가 가능하다.

npm install -g yarn

yarn은 시스템 변수 설정이 되어야 하고 CLI에서 사용해야하기 때문에 -g로 전역 설치를 해준다.

 

이제 yarn을 사용할 수 있는데 먼저 package.json을 만드는 방법은 

yarn init

을 사용해서 package.json을 생성할 수 있다.

 

패키지를 설치하는 npm install [패키지명] 의 경우는 add 명령어로

yarn add [패키지이름]

사용된다.

하나의 패키지의 add 명령 혹은 install 을 사용하면 

yarn.lock파일과 node_modules 디렉토리가 생성된다.

그리고 설치한 패키지가 추가 된 것을 볼 수 있다.

 

패키지의 특정 버전을 설치할 때는 아래와 같은 명령어를 통해서 설치한다.

yarn add [패키지 이름]@[버전]

 

특정 버전을 설치하기 위해 먼저 특정 버전을 확인하려고 하는데 moment라는 패키지의 버전을 확인하기 위해서는 

npm view [패키지이름] versions --json

을 입력하면

이렇게 패키지의 버전 리스트가 나온다 우리는 2.3.1버전을 설치해보자.

이렇게 설치가 가능하고 

package.json에서도 moment의 특정 버전으로 설치된다.

그냥 add와 다른점은 package.json파일에 ^(캐럿)이나 ~(틸트)를 붙이지 않고 작성된다.

같은 패키지를 특정 버전을 설정하지 않는다면 

이렇게 ^이나 ~로 설정되는 것을 볼 수 있다.

 

패키지를 전역으로 설치할 때에는 

yarn global add [패키지이름]

으로 사용이 가능하다.

 

패키지를 제거하는 명령어는 

yarn remove [패키지 이름]

로 사용이 가능하고 

yarn upgrade [패키지 이름]

를 통해서 패키지를 업데이트 할 수 있다.

 

package.json과 yarn.lock을 기반으로 필요한 패키지를 설치할 때에는 

yarn install

명령어를 통해서 설치할 수 있다.

 

오프라인에서 해당 명령어를 설치할 때에는

yarn install --offline

와 같이 사용하면 된다.

 

그리고 특정 스크립트를 실행시키는 방법은 

package.json파일 내부에 가장 상위에 script라는 key값을 추가하고

내부에 어떤 key로 어떤 스크립트를 실행할지를 작성해주면 된다.

예를 들어 

와 같이 

이렇게 만들어진 app.js를 실행시키는 스크립트를 작성하고 터미널에서 

yarn start <-- 실행하고자 하는 스크립트 key값, value에 해당하는 스크립트가 실행된다.

을 입력하면 

해당 스크립트가 실행되어 파일이 실행되어 Starting이라는 문자열이 출력되는 걸 볼 수 있다.

 

더보기

나는 node.js의 버전을 22버전을 사용했는데 스크립트 출력하는데 실행이 되지 않았다.

해결하려고 

모든 파일을 제거해봣다가 설치도 해보고, node app.js로 생으로도 실행해봤는데도 그냥 실행이 안되었다.

그래서 nvm이라는 node version manager라는 프로그램을 추가로 설치해서 node.js를 다른 버전으로(18버전) 변경후 실행했더니 그때 부터 실행이 됏다...

 

NVM 설치

nvm은 node version manager로 python에서는 pyenv와 같은 역할을 하는 거라고 보면 된다.

 

https://github.com/coreybutler/nvm-windows/releases

 

Releases · coreybutler/nvm-windows

A node.js version management utility for Windows. Ironically written in Go. - coreybutler/nvm-windows

github.com

 

해당 URL을 통해서 원하는 버전의 nvm을 설치한 다음에 

nvm list

라는 명령어를 통해서 현재 설치된 node.js의 버전이 어떤것들이 있는지, 현재 사용하고 있는 버전이 뭔지를 확인할 수 있다.

새로운 node.js의 버전을 받기 위해선 

nvm install [버전 번호]

로 설치가 가능하며 18.20.5버전을 받기 위해서 18.20.5과 같이 풀 버전명을 넣어서 설치할 수 도 있으나 18만(major버전) 입력해도 해당 버전이 설치된다.

이는 18만 입력해도 18버전의 가장 최신 LTS버전을 자동으로 설치해주기 때문이다.

 

이렇게 설치되고 나서 

nvm use [버전]

 을 입력하면 해당 버전을 사용하게 된다.

nvm use 18 // 18.xx.xx 버전을 사용한다
nvm use 18.20.5 // 특정 버전을 명시적으로 사용한다

이렇게 사용하는 버전이 바뀌면 nvm list를 입력했을때 현재 버전의 가장 앞에 * 이 붙어서 현재 사용하고 있는 버전이 어떤것인지 알린다

 

nvm을 사용해 Node.js 버전 삭제하는 방법은 

nvm uninstall [버전]

이며 이때는 특정 버전을 지정해서 삭제해야 하는듯 보인

nvm uninstall 18.20.5

그러면 nvm list를 했을때 

삭제된걸 볼 수 있다.

 

 

아무튼 이렇게 해서 오류 해결되었음..