서드파티 모듈과 npm 제대로 배우기

Jeris·2023년 5월 6일
0

코드잇 부트캠프 0기

목록 보기
86/107

모듈 검색 순서(CommonJS)

Node.js에서 모듈이 검색되는 순서

패키지가 로드되는 두 가지 방법

package.json 파일이 존재하지 않는 경우뿐만 아니라 package.json 파일이 존재하는 경우라도 그 내용 중에 main 필드가 없으면, index.js 파일이 로드됩니다.

패키지 안에 package.json 파일이 있을 때, package.json 파일의 내용 중
1. main 필드가 존재하면 거기에 적힌 파일을 로드합니다.
2. main 필드가 존재하지 않으면 index.js 파일을 로드합니다.

실제 모듈 로드 과정

실제로 Node.js에서 모듈이 로드되는 과정은 조금 더 복잡합니다. 이미지에서 '모듈 로드 실패'라고 표시했던 부분이더라도 반드시 모듈 로드가 실패하는 것은 아니고 그 안에는 생략된 과정들이 포함되어 있습니다.

Modules: CommonJS modules | Node.js v18.16.0 Documentation 참조

ES 모듈이 검색되는 순서

  1. 현재 모듈에서 이름을 검색합니다.
  2. 현재 모듈에서 import 한 다른 모듈에서 이름을 검색합니다.
  3. 현재 모듈이 속한 패키지의 다른 모듈에서 이름을 검색합니다.
  4. 전역 객체(global/window)에서 이름을 검색합니다.

Modules: ECMAScript modules | Node.js v18.16.0 Documentation 참조


패키지와 package.json

패키지(package)

Node.js에서 패키지란 재사용 가능한 코드를 모듈화하여 다른 프로젝트에서도 사용할 수 있도록 하는 방법입니다. 즉, 서드 파티 모듈입니다. 패키지는 일반적으로 Node.js의 패키지 매니저인 npm(Node.js Package Manager)을 사용하여 설치, 관리 및 공유됩니다.

패키지는 일반적으로 여러 모듈로 구성되며, 이러한 모듈은 하나의 파일 또는 여러 파일로 구성될 수 있습니다. 모듈은 일반적으로 특정 기능을 수행하거나 데이터를 제공하는 함수, 클래스, 객체 또는 변수와 같은 것입니다.

패키지는 다른 패키지의 모듈을 사용할 수 있으며, 이를 위해 package.json 파일에 의존성(dependencies)을 명시합니다. package.json 파일은 패키지의 이름, 버전, 의존성 및 스크립트와 같은 정보를 제공하여 npm이 패키지를 관리하는 데 사용됩니다.

Node.js에서 패키지를 사용하면 코드의 재사용성을 높일 수 있으며, 개발자는 직접 모든 기능을 작성하지 않고도 다른 패키지에서 제공하는 기능을 쉽게 사용할 수 있습니다.

package.json

package.json 파일은 Node.js에서 패키지를 관리하는 데 필요한 정보를 제공합니다. 이 파일은 패키지의 이름, 버전, 의존성 및 스크립트와 같은 정보를 포함하며, npm(Node.js Package Manager)이 패키지를 설치, 관리 및 배포하는 데 사용됩니다.

package.json 파일은 다음과 같은 정보를 포함합니다.

name 패키지의 이름을 나타냅니다. 이 이름은 npm에 등록된 패키지의 이름과 일치해야 합니다.
version 패키지의 버전을 나타냅니다. 이 버전은 Major.Minor.Patch 형식으로 표기됩니다.
description 패키지에 대한 설명을 나타냅니다. 패키지 검색 기준으로 활용됩니다.
keywords 패키지에 대한 키워드들을 나타냅니다. 패키지 검색 기준으로 활용됩니다.
homepage 패키지 관련 사이트의 URL을 나타냅니다. 패키지 관련 커뮤니티의 홈페이지 주소가 있는 경우가 많습니다.
bugs 패키지를 사용하다가 발생하는 버그들을 신고할 수 있는 URL이나 이메일 주소를 나타냅니다.
license 패키지의 라이센스 정보를 나타냅니다. 패키지가 가질 수 있는 라이센스의 종류 참조
author, contributors author는 패키지를 만든 사람, contributors는 패키지를 만드는데 기여하는 사람들을 나타냅니다.
main 패키지에서 기본으로 사용하는 파일을 나타냅니다. 이 파일은 패키지를 불러올 때 사용됩니다.
man 패키지의 사용 설명서가 담긴 파일들의 경로를 나타냅니다.
repository 패키지의 코드가 관리되고 있는 repository의 주소를 나타냅니다.
scripts 패키지에서 사용할 수 있는 스크립트 목록을 나타냅니다. 이 스크립트는 패키지를 빌드하거나 실행하는 데 사용됩니다. scripts | npm Docs 참조
dependencies 패키지가 의존하는 다른 패키지의 목록을 나타냅니다. 이 목록은 패키지를 설치할 때 npm이 이용합니다.
devDependencies 패키지가 개발 과정에서 의존하는 다른 패키지의 목록을 나타냅니다. 이 목록은 패키지를 개발할 때만 필요합니다.

package.json 파일은 패키지를 관리하는 데 필수적인 파일이며, 패키지의 이름, 버전 및 의존성과 같은 정보를 포함하여 패키지를 설치, 업데이트, 관리 및 배포하는 데 사용됩니다. package.json | npm Docs 참조


Semantic Version과 Version Range Syntax

Semantic Version

Semantic Version은 소프트웨어 버전 관리의 일종으로, 버전 번호를 Major, Minor, Patch 세 가지 성분으로 구분하여 정하는 규칙입니다. Semantic Version을 우리말로 해석하면 '의미론적 버전' 정도로 해석할 수 있습니다.

X.Y.Z에서 X를 메이저 버전(major version), Y를 마이너 버전(minor version), Z를 패치 버전(patch version)이라고 합니다. Semantic Version에서는 API의 변화를 기준으로 버전을 업데이트해야 합니다.

패치 버전은 API에 변화를 주지 않는 범위 내에서의 변화가 이루어진 경우에 업데이트합니다. 겉으로 공개된 API는 바뀌지 않았지만, 코드에 존재하던 버그를 해결하거나, 알고리즘을 바꿔서 그 효율성을 향상시킨 등의 경우에 해당합니다. 하위 호완성이 보장됩니다.

마이너 버전은 이전 버전의 API와 호환되는(backward-compatible) API 상의 변화가 발생했을 때 업데이트합니다. 새로운 API를 추가하는 경우에 해당합닌다. 하위 호환성이 보장됩니다.

메이저 버전은 이전 버전의 API와 호환되지 않는(not backward-compatible) API 상의 변화가 발생했을 때 업데이트합니다. 기존의 API를 아예 삭제했거나 그 이름을 바꾸는 등의 경우에 해당합니다. 만약 사용하던 패키지의 업데이트된 메이저 버전을 사용하고 싶다면, 원래 사용하던 이전 버전 패키지의 API에서 어떤 부분들이 바뀐 건지를 체크하고, 코드를 재수정해야 할 가능성이 높습니다.

Version Range Syntax

Version Range Syntax는 Semantic Version을 기반으로 패키지가 다른 패키지의 어느 버전들을 요구하는지를 나타낼 때 사용되는 구문입니다.

Basic Syntax

"codeit" : "2.3.1"

정확히 2.3.1 버전의 codeit 패키지가 필요하다는 뜻입니다.

"codeit" : ">2.3.1"

2.3.1보다 높은 버전의 codeit 패키지가 필요하다는 뜻입니다.

"codeit" : "2.3.1 || ≥2.5.0 <3.1.2"

2.3.1 버전의 codeit 패키지 또는 2.5.0 버전 이상이면서 3.1.2 버전 미만의 codeit 패키지가 필요하다는 뜻입니다. ||(or)는 왼쪽 조건과 오른쪽 조건 중 하나를 만족해야 한다는 뜻이고, ≥2.5.0 <3.1.2 사이의 공백 하나는 왼쪽과 오른쪽 조건 둘 다(&, and)를 만족해야 한다는 뜻입니다.

다음은 Advanced Syntax 입니다.

Hyphen Range

"codeit" : "2.3.1 - 3.1.2"

2.3.1 버전 이상 3.1.2 버전 이하의 codeit 패키지가 필요하다는 뜻입니다. 이것은 ≥2.3.1 ≤3.1.2 을 줄여서 표시한 것과 동일합니다.

이때 패치 버전이나 마이너 버전을 표시하지 않는 경우도 있습니다.
왼쪽에서 생략할 경우, 2.3 - 3.1.2이면 자동으로 ≥2.3.0 ≤3.1.2 으로 빈자리에 0이 붙어서 해석됩니다. 하지만 오른쪽에서 생략할 경우, 2.3.1 - 3.1 이면 ≥2.3.1 <3.2.0 으로 해석됩니다. 같은 원리로 2.3.1 - 3 이면 ≥2.3.1 <4.0.0 으로 해석됩니다.

X-range

"codeit" : "\*"

어느 버전의 codeit 패키지도 상관없다는 뜻입니다.

"codeit" : "3.x"

x에는 어떤 버전이 들어가도 상관없다는 뜻입니다. 즉, ≥3.0.0 <4.0.0 과 같은 뜻입니다.

참고로 그냥 3이라고만 써있어도 3.x 으로 해석이 되어서 ≥3.0.0 <4.0.0 라는 뜻입니다.

"codeit" : "3.1.x"

같은 원리로 ≥3.1.0 <3.2.0 라는 뜻입니다.

참고로 그냥 3.1이라고만 써있어도 3.1.x 으로 해석이 되어서 ≥3.1.0 <3.2.0 라는 뜻입니다.

Tilde Range
물결 모양 기호(~, Tilde)를 사용한 표기법입니다. 마이너 버전이 표시된 경우는 패치 버전 업데이트까지만 허용하고, 마이너 버전이 없으면 마이너 버전의 업데이트까지 허용합니다.

"codeit" : "~3.1.2"

이런 경우는 ≥3.1.2 <3.2.0 으로 해석이 됩니다. 지금 3.1.2에 마이너 버전이 존재하므로 패치 버전이 업데이트된 것들만 허용합니다.

"codeit" : "~3.1"

이렇게 패치 버전이 적혀있지 않다면 ≥3.1.0 <3.2.0 으로 해석됩니다. 생략된 패치 버전은 0부터 시작합니다.

"codeit" : "~3"

마이너 버전도 적혀있지 않다면 ≥3.0.0 <4.0.0 으로 해석됩니다. 즉, 마이너 버전이나 패치 버전이 업데이트된 버전들만 허용합니다.

Caret Range
메이저 버전, 마이너 버전, 패치 버전 중에서 현재 보이는 가장 왼쪽의 0이 아닌 버전이 바뀌지 않는 선에서의 버전 업데이트만을 허용합니다.

"codeit" : "^1.2.3"

가장 왼쪽의 0이 아닌 버전, 즉, 1이 바뀌지 않는 선에서의 버전 업데이트만 허용됩니다. 여기서는 ≥1.2.3 <2.0.0 이라는 뜻입니다.

"codeit" : "^0.2.3"

가장 왼쪽의 0이 아닌 버전, 2가 바뀌지 않는 선에서의 버전 업데이트만 허용됩니다. 여기서는 ≥0.2.3 <0.3.0 이라는 뜻입니다.

"codeit" : "^0.0.3"

가장 왼쪽의 0이 아닌 버전, 3이 바뀌지 않는 선에서의 버전 업데이트만 허용됩니다. 여기서는 ≥0.0.3 <0.0.4 이라는 뜻입니다.


패키지 생성하기

npm init

npm init은 Node.js 패키지를 생성할 때 사용하는 명령어입니다. 이 명령어는 새로운 프로젝트를 시작할 때 package.json 파일을 만들기 위해 사용됩니다.

npm init 명령어를 실행하면, 사용자는 프로젝트의 이름, 버전, 설명 등과 같은 정보를 입력하여 package.json 파일을 생성할 수 있습니다. 이 파일은 패키지의 이름, 버전, 의존성 및 스크립트와 같은 정보를 포함하며, Node.js 패키지를 설치, 관리 및 배포하는 데 사용됩니다.

npm init 명령어는 다음과 같은 옵션을 제공합니다.

-y 모든 프롬프트를 건너뛰고 기본값으로 package.json 파일을 생성합니다.
-f 이미 package.json 파일이 있는 경우 덮어쓰기를 하여 새로운 package.json 파일을 생성합니다.
--scope 패키지의 스코프(scope)를 지정합니다.

npm init 명령어를 사용하면 package.json 파일을 손쉽게 생성할 수 있습니다.

npm publish

npm publish는 Node.js 패키지를 npm 레지스트리에 공개하는 명령어입니다. 이 명령어는 package.json 파일을 기반으로 패키지를 빌드하고, npm 레지스트리에 업로드하여 다른 사용자들이 패키지를 설치하고 사용할 수 있도록 합니다.

npm publish 명령어를 실행하기 전에, 먼저 npm 레지스트리에 계정을 만들고 npm login 명령어로 로그인해야 합니다.

npm publish 명령어를 실행하면 다음과 같은 일이 발생합니다.

  1. package.json 파일을 기반으로 패키지를 빌드합니다.
  2. 패키지를 npm 레지스트리에 업로드합니다.
  3. 업로드된 패키지는 npm install 명령어를 사용하여 다른 프로젝트에서 쉽게 설치할 수 있습니다.

npm version

npm version [Semantic Version] 명령어로 패키지의 버전을 업데이트를 할 수 있습니다. 업데이트 후에 npm publish 명령어로 업데이트된 버전을 npm 레지스트리에 업로드할 수 있습니다.

npm unpublish

npm unpublish는 npm 레지스트리에서 패키지를 삭제하는 명령어입니다. 이 명령어는 이미 배포된 패키지를 취소하고, npm 레지스트리에서 삭제하는 데 사용됩니다.

npm unpublish 명령어는 다음과 같은 두 가지 방법으로 사용할 수 있습니다.

  1. 패키지 이름과 버전을 지정하여 패키지를 삭제하는 방법:
npm unpublish package@version
  1. --force 옵션을 사용하여 모든 버전의 패키지를 삭제하는 방법:
npm unpublish package --force

npm unpublish 명령어를 실행하면 다음과 같은 일이 발생합니다.

  1. 지정된 패키지를 npm 레지스트리에서 삭제합니다.
  2. 삭제된 패키지를 다시 npm 레지스트리에 업로드하는 것을 방지합니다.
  3. 이미 설치된 패키지를 사용할 수는 있지만, 더 이상 패키지를 설치할 수는 없습니다.

package.json과 package-lock.json의 차이

node_modules을 공유하는 방식

패키지를 퍼블리싱할 때, 용량의 문제로 node_modules 디렉토리는 보통 포함하지 않습니다.

대신 패키지를 설치하면 npm이 package.json 파일의 dependencies 필드에 적힌 의존 패키지들의 이름과 Semantic Version과 Version Range Syntax를 보고 알맞은 패키지들을 자동으로 설치해줍니다.

package-lock.json

package-lock.json 파일은 패키지 의존성 관리를 위한 파일입니다. 이 파일은 패키지의 의존성 트리를 포함하며, 패키지가 의존하는 다른 패키지의 정확한 버전과 의존성 관리 방식을 포함합니다.

package.json 파일의 dependencies 필드에는 Version Range Syntax만 적혀있기 때문에 패키지를 설치할 때 의존하는 다른 패키지의 버전이 달라질 수 있습니다. package-lock.json 파일의 dependencies 필드에는 현재 패키지에 설치되어 있는 다른 패키지들의 정확한 버전이 적혀있기 때문에 package.json 파일만 전달했을 때 생길 수 있는 문제를 해결할 수 있습니다.


패키지 간 의존 관계의 위험성

악성코드 문제

첫 번째로 악성 코드 문제가 있습니다. 패키지가 의존하는 수많은 하위 패키지 중에 악성코드가 있을 수도 있다는 뜻입니다. 실제로 2017년에는 cross-env라는 유명 패키지와 이름이 비슷한 crossenv라는 패키지에 악성 코드가 들어 있는 경우가 발견된 적도 있습니다. 사용자들이 패키지를 설치할 때 오타를 별로 신경 쓰지 않고 패키지를 설치해버리는 취약점을 공격한 일명 typo-squatting 기법을 사용한 경우입니다.

이 뿐만 아니라 2018년에는 Event-Stream이라는 유명 패키지가 의존하던 Flatmap-Stream이라는 패키지에 비트코인 관련 악성 코드가 포함된 사건도 있었고, 2020년에는 유닉스 시스템의 중요 정보를 빼가는 악성 패키지가 발견되는 사건도 있었습니다.

물론 패키지들의 보안 검사를 위해 npm 커뮤니티에서 많은 노력을 하고는 있지만, 특정 패키지, 그리고 그것이 의존하는 패키지들이 사용해도 괜찮은 것인지 확인하는 것은 본질적으로 그것을 사용해서 서비스를 만드는 개발자의 책임입니다. 모든 패키지를 본인이 직접 검사하는 것은 현실적으로 어렵겠지만 악성 코드가 있는 패키지를 설치하지 않으려면, 되도록 누구나 알 정도로 공신력있는 패키지들만을 골라서 사용하는 것이 좋습니다.

패키지 내 코드의 취약점 문제

두 번째는 취약점 문제입니다. 어떤 패키지들의 코드에는 보안 측면에서 취약한 부분이 있을 수 있습니다. npm 측과 각종 보안 회사들은 어떤 패키지의 어떤 점이 취약하다고 주기적으로 발표를 하는데, 이 문제에 관해 할 수 있는 것은 다음과 같습니다.

npm outdated, npm update 정기적으로 실행하기

먼저, 현재 작업 중인 패키지 안에서 npm outdated, npm update 라는 명령어를 자주 실행해주는 겁니다. npm outdated는 현재 패키지에 설치된 하위 패키지들 중에 버전이 최신이 아닌 것들이 무엇이 있는지 보여주는 명령어입니다. 그리고 npm update는 현재 자신의 패키지에 설치된 모든 패키지들을 최신 패키지로 업데이트해주는 명령어입니다.

npm updatepackage.json 파일의 dependencies 필드에 표시된 해당 패키지의 Version Range Syntax가 허용하는 범위 내에서만 업데이트를 해줍니다. 예를 들어 현재 설치된 버전이 1.5.2고, 최신 버전은 3.0.0인 패키지가 있다고 해도 이 패키지에 의존 중인 패키지의 package.json 파일에서 dependencies 필드에 ~1.8.3 이라고 써있다면 1.9.0 미만의 최신 버전까지로만 업데이트해줍니다.

최신 버전의 패키지들을 사용할수록 일반적으로 보안상 더 안전하기 때문에 위 작업을 주기적으로 실행해주는 게 좋습니다. 이때 일부 패키지의 경우, 최신 버전으로 업데이트하기 전에 별도의 검토가 필요해서 모든 패키지를 최신의 것으로 바꾸면 안 되는 경우에는 npm update pakage 명령어를 실행해서 원하는 패키지만 업데이트해줘도 됩니다. 만약 최신보다 약간 이전의 버전을 원하면 npm update package@version 이런 식으로 특정 버전을 지정해주고 실행하면 됩니다.

npm audit, npm audit fix으로 취약점 점검하기

패키지 내의 보안을 유지하는 또 다른 방법은 npm audit이라는 커맨드를 사용하는 것입니다. 현재 자신의 패키지 안에서 npm audit이라는 커맨드를 실행하면, npm이 현재 설치된 패키지들의 이름과 버전을 보고, 발표된 취약점이 있는 것들은 그 정보를 출력해줍니다. npm install fix라는 명령어를 실행하면 npm이 Version Range Syntax를 준수하면서도, 취약점이 해결된 더 최신 버전의 패키지를 자동으로 설치해줍니다. npm install fix 만으로는 문제를 해결하지 못하는 경우, More info에 있는 URL로 들어가서 필요한 해결 조치를 보고 직접 수행해주면 됩니다.

패키지의 가용성(Availability) 문제

세 번째로 문제점은 사용 중이던 패키지가 갑자기 사라져버리거나 관리되지 않고 방치될 수도 있다는 점입니다. 실제로 2016년에는 left-pad라고 하는 패키지의 주인이 npm 저장소에서 그 패키지를 삭제해버린 사건이 있었습니다. 이 때문에 당시 left-pad에 의존하고 있던, React나 Babel 같은 대형 프로젝트들에도 큰 문제가 발생했었는데요. 다행히 이 문제는 잘 해결되었지만, 이 사건은 당시 수많은 패키지들의 의존 생태계가 단 한 순간에 무너져버릴 수도 있다는 공포감을 개발자들에게 심어주었습니다. 그리고 단순한 기능 정도는 패키지를 굳이 쓰지 말자는 의식의 전환을 가져다주기도 했죠. 왜냐하면 당시 left-pad의 코드는 아래와 같이 단순한 코드였기 때문입니다.

module.exports = leftpad;

function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;
  while (++i < len) {
    str = ch + str;
 }
  return str;
}

현재, npm 저장소에 한 번 올린 패키지는, 올리고 나서 72시간이 지나면 함부로 삭제할 수 없도록 되어 있습니다. 그래서 이제 그런 문제는 발생할 수 없겠지만 내가 사용하는 패키지가 앞으로도 잘 관리될 패키지인지를 확인하는 것은 여전히 중요합니다. 이 부분은 패키지의 인기도, 패키지를 관리하는 주체 등을 보고 잘 판단해야 합니다.

결론

사용하는 패키지에 대해 이상한 코드는 없는지, 외부의 공격에 취약한 부분은 없는지, 앞으로도 꾸준한 업데이트가 이루어질지를 잘 고민하고 사용해야 합니다. 특정 분야에서 사실상 표준(de facto standard)처럼 존재하는 패키지가 있는 경우라면 상관없지만, 그렇지 않은 경우에 어떤 패키지를 사용하려고 한다면, 위에서 설명한 기준들을 충족하는지 잘 판단하고 사용 여부를 결정해야 합니다.


nodemon 패키지를 전역 설치해보기

local mode와 global mode

npm 패키지 매니저는 로컬 모드(Local mode)와 글로벌 모드(Global mode) 두 가지 모드로 패키지를 설치할 수 있습니다.

로컬 모드는 프로젝트 폴더 내에서 패키지를 설치하는 것을 의미합니다. 즉, 특정 프로젝트에서만 사용할 패키지를 설치할 때 사용합니다. 로컬 모드로 패키지를 설치하면 해당 패키지는 해당 프로젝트 내에서만 사용 가능하며, 프로젝트 루트 폴더 내의 node_modules 폴더에 저장됩니다.

글로벌 모드는 시스템 전체에서 사용할 패키지를 설치하는 것을 의미합니다. 즉, 여러 프로젝트에서 공통으로 사용할 패키지를 설치할 때 사용합니다. 글로벌 모드로 패키지를 설치하면 모든 프로젝트에서 해당 패키지를 사용할 수 있으며 패키지를 하나의 실행 파일인 것 처럼 사용할 수 있습니다..

글로벌 모드로 패키지를 설치하려면 npm install -g nodemon처럼 -g 옵션을 주고 설치하면 됩니다. 이때 sudo 키워드로 관리자 모드를 사용해야할 수 있습니다.

nodemon

nodemon Node.js 애플리케이션을 개발하는 동안 코드 변경을 감지하고 자동으로 서버를 재시작해주는 유용한 도구입니다. nodemon은 Node.js의 기본적인 파일 시스템 이벤트를 감지하고, 파일이 수정되면 자동으로 Node.js 애플리케이션을 재시작합니다. 이렇게 하면 개발자는 코드를 변경할 때마다 수동으로 서버를 재시작하지 않아도 되므로 개발 시간을 절약할 수 있습니다.

nodemon을 사용하려면, npm을 통해 전역으로 설치한 다음, Node.js 애플리케이션을 실행할 때 node 대신 nodemon 명령어를 사용하면 됩니다. 이러한 명령어는 애플리케이션이 실행되면서 파일 변경 사항을 감지하고, 자동으로 서버를 재시작해줍니다.

전역 설치의 원리

global mode로 설치하는 경우에는 패키지가 node_modules 디렉토리가 아닌 {prefix}/lib/node_modules 경로에 설치됩니다. 여기서 prefix는 npm에 관한 여러 기본 설정값 중 하나입니다.

npm은 동작할 때 참조하는 여러 가지 설정값들이 있습니다. 그 설정값들은 npm config list --json
이라는 명령어로 아래 이미지처럼 확인해볼 수 있습니다.

각각의 설정값이 의미 참조

prefix라는 설정의 값이 /usr/local이라는 걸 알 수 있습니다. {prefix}/lib/node_modules 경로는 /usr/local/lib/node_modules 이고, 전역 설치된 패키지가 여기에 설치됩니다. prefix의 값은 OS에 따라 달라질 수 있습니다.

이렇게 전역 설치를 한 패키지 또한 코드에서 로드할 수는 있습니다. 하지만 이렇게 되면 나중에 패키지를 외부로 공유할 때 상대방의 컴퓨터에서는 경로가 달라질 수 있기 때문에(prefix의 값은 OS마다 다르기 때문에) 이렇게 코드에서 로드할 목적의 패키지를 굳이 전역 설치하지는 않습니다.

대신 패키지를 마치 하나의 실행 파일처럼 사용하고자 할 때 global mode를 사용하여 설치합니다.

npm install -g nodemon을 실행하면 발생하는 일

1. npm은 nodemon 패키지를 /usr/local/lib/node_modules 디렉토리에 설치합니다.

2. 그리고 nodemon 패키지 안에 있는 package.json 파일의 내용 중에서 bin이라는 필드를 찾습니다.

bin/nodemon.js는 nodemon 패키지 안의 bin 디렉토리에 있는 nodemon.js라는 파일을 나타냅니다. 실제로 이런 파일이 있는지 확인해보면

정말로 존재하는 파일이라는 걸 알 수 있습니다. nodemon.js 파일의 내용을 잠깐 살펴보면,

이렇게 몇 줄의 자바스크립트 코드가 적혀있는 파일이라는 것을 알 수 있습니다. 여기서 가장 맨 위의 #!/usr/bin/env node
이 표시는 이 파일은 node 키워드로 실행해야 한다는 뜻입니다.

3. 그다음 npm은 {prefix}/bin 이라는 디렉토리에 nodemon이라는 파일을 생성합니다.

화살표 표시는 /usr/local/bin/nodemon 파일이 /usr/local/lib/node_modules/nodemon/bin/nodemon.js에 대한 바로가기 파일이라는 뜻입니다. 리눅스에서 이 표시는 심볼릭 링크(Symbolic Link)라는 별도의 명칭과 기능을 갖고 있습니다.

/usr/local/bin/nodemon을 실행하면 이것이 가리키는 원본 파일인 nodemon.js이 실행되는 것이고 nodemon.js 파일은 아까 본 내용처럼

이렇게 node로 실행되어야 한다는 표시가 맨 윗줄에 쓰여 있기 때문에 시스템이 자동으로 node nodemon.js 를 실행해주는 겁니다.

5. nodemon main.js

/usr/local/bin/nodemon이라고 타이핑하지 않고 nodemon main.js라고 간단하게 써도 실행되는 부분은 Node.js가 아니라 운영체제와 관련된 부분입니다.

운영체제에는 환경 변수(Environment Variables)라는 것이 있습니다. 환경 변수란 시스템을 위해 필요한 각종 기본값들을 저장하고 있는 변수들입니다. 이 중에는 $PATH라는 환경 변수가 있습니다. 이 $PATH 환경변수는 실행 파일들이 존재하는 디렉토리의 경로들이 저장되어있는 변수입니다.

$PATH 환경 변수의 값으로 다양한 디렉토리들의 경로가 콜론(:)을 기준으로 나열되어 있습니다. /usr/local/bin 디렉토리가 nodemon 파일이 있던 디렉토리입니다.

운영체제는 우리가 어떤 경로를 주지 않고 파일 이름만 적고 엔터를 치면, 이 $PATH에 있는 모든 디렉토리를 확인하면서 그런 이름의 파일을 찾아서 실행합니다.

6. 결론

(1) nodemon 실행
(2) $PATH 변수 참조해서 /usr/local/bin/nodemon 파일 실행
(3) 원본 파일인 /user/local/lib/node_modules/nodemon.js을 실행
(4) 가장 맨 윗 줄의 #!/usr/bin/env node를 확인하고 시스템이 자동으로 node /user/local/lib/node_modules/nodemon.js 실행

이런 순서를 통해 nodemon 패키지를 마치 하나의 실행 파일인 것처럼 사용할 수 있었습니다.

참고로 npm 또한 {prefix}/lib/node_modules 디렉토리에 전역 설치되는 패키지입니다.


그밖의 npm 명령어

npm search package npm 레지스트리에서 패키지 이름과 일치하는 패키지를 검색합니다. 이 명령어는 npm 웹 사이트에서 수행하는 검색과 유사한 결과를 출력합니다.

npm info package npm 레지스트리에서 특정 패키지에 대한 정보를 표시합니다. 이 명령어를 사용하면 패키지의 최신 버전, 라이선스, 의존성 및 메인 파일 등과 같은 정보를 확인할 수 있습니다.

npm list 현재 프로젝트에서 설치된 모든 패키지를 나열합니다. 이 명령어는 프로젝트 내의 모든 패키지와 해당 패키지가 의존하는 패키지들을 재귀적으로 표시합니다.

npm uninstall package 지정된 패키지를 제거합니다. 이 명령어는 현재 프로젝트에서 패키지를 삭제하며, --global 옵션을 추가하면 글로벌 패키지에서 패키지를 삭제할 수 있습니다. 또한, --save 옵션을 추가하면 package.json 파일에서 해당 패키지를 자동으로 제거할 수 있습니다.

npm Docs 참조


npm의 중요성과 Yarn

웹 프론트엔드 개발 세계에서도 중요한 npm

npm은 오늘날 웹 프론트엔드 개발 영역에서도 중요한 역할을 하는 프로그램입니다.

오늘날의 웹 프론트엔드 개발은 단순히 HTML, CSS, Javascript를 잘 작성하고, 조합하는 정도에 그치지 않습니다. 왜냐하면 Javascript의 표준이 발전하고 있고, 웹 프론트엔드에서 요구되는 UI/UX 상의 기능 난이도가 높아지고 있으며, 성능 문제, 개발 생산성 향상 문제, 브라우저 호환 문제 등이 더욱 중요한 이슈가 되면서 훨씬 고도화된 작업들이 추가되었기 때문입니다.

예를 들어, 오늘날 웹 프론트엔드 개발을 할 때는

(1) 코드가 잘 작동하는지를 검사하는 테스트 작업(testing)

(2) 자바스크립트 코드가 가독성 좋은 포맷으로 잘 작성되었는지를 검사하고 수정하는 작업(code formatting)

(3) 작성한 자바스크립트 코드가 자바스크립트 최신 표준을 지원하지 않는, 오래된 브라우저(특히, 인터넷 익스플로러 등)에서도 문제없이 동작할 수 있도록 변환하거나 자바스크립트의 단점을 보완한 언어(Typescript 등)로 작성한 코드를 다시 자바스크립트로 변환하는 트랜스파일 작업(transpiling)

(4) 여러 자바스크립트 파일들과 CSS 파일 등을 하나의 파일로 묶는 번들링 작업(bundling),

(5) 번들링된 결과를 더 작은 용량으로 압축해주는 작업(minifying),

(6) 이런 작업들을 한 번에 자동으로 실행할 수 있도록 설정하는 작업(Task Runner)

등의 작업들이 필요합니다. 그리고 개발자들은 보통 이런 작업들을 이미 공신력있는 유명 툴들을 사용해서 수행하는데, 이때 각각의 작업을 수행할 수 있는 대표적인 도구들의 이름은 다음과 같습니다.

(1) testing 작업 - Mocha

(2) code formatting 작업 - ESLint

(3) transpiling 작업 - Babel

(4) bundling 작업 - Webpack

(5) minifying 작업 - Uglify-JS

(6) Task Runner - Gulp

각 작업에서 사용할 수 있는 툴들은 다양하지만 일단 대표적인 것들만 이름을 나열해보았습니다. 이런 툴들은 각자가 독립적으로 사용되기도 하고, 하나의 툴이 플러그인처럼 개발되어 다른 툴에 삽입되어서 쓰이기도 합니다.

웹 프론트엔드 개발자들도 이런 툴들을 모두 npm 저장소에서 패키지로 다운로드받아 사용합니다. 실제로 그런지 npm 공식 웹사이트에서 각 툴의 이름을 검색해보면,

(1) Mocha

(2) ESLint

(3) Babel

(4) Webpack

(5) Uglify-JS

(6) Gulp

이렇게 모두 npm 저장소에 존재하는 것을 볼 수 있습니다.

정리하자면 Node.js 개발자뿐만 아니라 수많은 웹 프론트엔드 개발자들도 npm을 사용해서 이런 툴들을 설치하고 사용한다는 뜻입니다.

npm과 Yarn

패키지를 관리하는 프로그램에는 npm 뿐만 아니라 Yarn이라는 것도 있습니다.

npm이 2010년에 출시된 패키지 관리 프로그램이라면, Yarn은 2016년 페이스북에서 출시한 패키지 관리 프로그램입니다. 당시 npm의 느린 속도 등을 해결하기 위해 페이스북에서 Yarn을 만들었는데, 출시 당시에 Yarn은 npm보다 더 빠른 패키지 설치 속도를 자랑했고, 오프라인 캐시(offline cache - 한번 설치한 패키지를 계속 보관해두는 기능, 패키지를 다시 삭제하고 인터넷이 끊긴 상태에서도 재설치 가능) 기능 등의 추가 기능을 탑재했습니다.

하지만 npm 또한 그 이후로 속도가 향상되고 캐시 기능 등도 비슷하게 도입되면서 많은 발전을 이루었는데요. 오늘날 개발자들은 자신의 취향에 따라 npm 또는 Yarn을 쓰고 있습니다. 그리고 Yarn 자체가 npm으로부터 많은 핵심 개념을 그대로 본떠서 만든 패키지 매니저 프로그램이기 때문에 npm만 잘 사용할 줄 알면 Yarn도 금방 사용할 수 있습니다.

사용하는 입장에서 알아두면 좋을 Yarn과 npm의 차이는 다음과 같습니다.

1. 설치 방법의 차이

npm은 Node.js를 설치할 때 함께 설치되지만, Yarn은 별도로 설치해줘야 하는 패키지입니다. 따라서 아래와 같이 Yarn을 npm에서 설치해줘야 합니다.

npm install -g yarn

우리가 배운 전역 설치 방식으로 설치해주면 됩니다.

2. 명령어의 차이

npm와 Yarn은 사용하는 명령어에도 차이가 있습니다. npm의 각 명령어별로, 같은 기능을 하는 Yarn의 명령어들을 옆에 나열해보면 아래와 같습니다.

패키지 생성 : npm init - yarn init

패키지 업로드 : npm publish - yarn publish

패키지 설치 : npm install package - yarn add package

패키지 삭제 : npm uninstall package - yarn remove package

패키지 업그레이드 : npm update package - yarn upgrade package

패키지 정보 조회 : npm info package - yarn info package

현재 패키지의 dependencies 조회 : npm list - yarn list

3. package-lock.json vs. yarn.lock

워킹 디렉토리의 패키지에 설치된 패키지들의 정확한 버전 정보가 package-lock.json 파일에 있는데, Yarn을 사용하는 경우에도 이것과 같은 역할을 하는 파일인 yarn.lock을 사용합니다.

두 파일은 동일한 내용을 갖고 있지만, 그 형식이 약간 다릅니다.

package-lock.json

yarn.lock

4. 명령어 실행 시 출력 내용의 차이

Yarn으로 패키지 관련 작업을 하면 npm 때와 조금은 다른 형식으로 터미널에서 그 결과들이 출력됩니다. Yarn은 npm에 비해 조금은 더 정돈된 디자인과, 귀여운 이모티콘을 출력한다는 것이 특징입니다.


Reference

profile
job's done

0개의 댓글