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 참조
Modules: ECMAScript modules | Node.js v18.16.0 Documentation 참조
Node.js에서 패키지란 재사용 가능한 코드를 모듈화하여 다른 프로젝트에서도 사용할 수 있도록 하는 방법입니다. 즉, 서드 파티 모듈입니다. 패키지는 일반적으로 Node.js의 패키지 매니저인 npm(Node.js Package Manager)을 사용하여 설치, 관리 및 공유됩니다.
패키지는 일반적으로 여러 모듈로 구성되며, 이러한 모듈은 하나의 파일 또는 여러 파일로 구성될 수 있습니다. 모듈은 일반적으로 특정 기능을 수행하거나 데이터를 제공하는 함수, 클래스, 객체 또는 변수와 같은 것입니다.
패키지는 다른 패키지의 모듈을 사용할 수 있으며, 이를 위해 package.json
파일에 의존성(dependencies)을 명시합니다. package.json
파일은 패키지의 이름, 버전, 의존성 및 스크립트와 같은 정보를 제공하여 npm이 패키지를 관리하는 데 사용됩니다.
Node.js에서 패키지를 사용하면 코드의 재사용성을 높일 수 있으며, 개발자는 직접 모든 기능을 작성하지 않고도 다른 패키지에서 제공하는 기능을 쉽게 사용할 수 있습니다.
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은 소프트웨어 버전 관리의 일종으로, 버전 번호를 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는 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
은 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
는 Node.js 패키지를 npm 레지스트리에 공개하는 명령어입니다. 이 명령어는 package.json
파일을 기반으로 패키지를 빌드하고, npm 레지스트리에 업로드하여 다른 사용자들이 패키지를 설치하고 사용할 수 있도록 합니다.
npm publish
명령어를 실행하기 전에, 먼저 npm 레지스트리에 계정을 만들고 npm login
명령어로 로그인해야 합니다.
npm publish
명령어를 실행하면 다음과 같은 일이 발생합니다.
package.json
파일을 기반으로 패키지를 빌드합니다.npm install
명령어를 사용하여 다른 프로젝트에서 쉽게 설치할 수 있습니다.npm version [Semantic Version]
명령어로 패키지의 버전을 업데이트를 할 수 있습니다. 업데이트 후에 npm publish
명령어로 업데이트된 버전을 npm 레지스트리에 업로드할 수 있습니다.
npm unpublish
는 npm 레지스트리에서 패키지를 삭제하는 명령어입니다. 이 명령어는 이미 배포된 패키지를 취소하고, npm 레지스트리에서 삭제하는 데 사용됩니다.
npm unpublish
명령어는 다음과 같은 두 가지 방법으로 사용할 수 있습니다.
npm unpublish package@version
--force
옵션을 사용하여 모든 버전의 패키지를 삭제하는 방법:npm unpublish package --force
npm unpublish
명령어를 실행하면 다음과 같은 일이 발생합니다.
패키지를 퍼블리싱할 때, 용량의 문제로 node_modules 디렉토리는 보통 포함하지 않습니다.
대신 패키지를 설치하면 npm이 package.json 파일의 dependencies 필드에 적힌 의존 패키지들의 이름과 Semantic Version과 Version Range Syntax를 보고 알맞은 패키지들을 자동으로 설치해줍니다.
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 update
는 package.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
이라는 커맨드를 실행하면, npm이 현재 설치된 패키지들의 이름과 버전을 보고, 발표된 취약점이 있는 것들은 그 정보를 출력해줍니다. npm install fix
라는 명령어를 실행하면 npm이 Version Range Syntax를 준수하면서도, 취약점이 해결된 더 최신 버전의 패키지를 자동으로 설치해줍니다. npm install fix
만으로는 문제를 해결하지 못하는 경우, More info에 있는 URL로 들어가서 필요한 해결 조치를 보고 직접 수행해주면 됩니다.
세 번째로 문제점은 사용 중이던 패키지가 갑자기 사라져버리거나 관리되지 않고 방치될 수도 있다는 점입니다. 실제로 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)처럼 존재하는 패키지가 있는 경우라면 상관없지만, 그렇지 않은 경우에 어떤 패키지를 사용하려고 한다면, 위에서 설명한 기준들을 충족하는지 잘 판단하고 사용 여부를 결정해야 합니다.
npm 패키지 매니저는 로컬 모드(Local mode)와 글로벌 모드(Global mode) 두 가지 모드로 패키지를 설치할 수 있습니다.
로컬 모드는 프로젝트 폴더 내에서 패키지를 설치하는 것을 의미합니다. 즉, 특정 프로젝트에서만 사용할 패키지를 설치할 때 사용합니다. 로컬 모드로 패키지를 설치하면 해당 패키지는 해당 프로젝트 내에서만 사용 가능하며, 프로젝트 루트 폴더 내의 node_modules 폴더에 저장됩니다.
글로벌 모드는 시스템 전체에서 사용할 패키지를 설치하는 것을 의미합니다. 즉, 여러 프로젝트에서 공통으로 사용할 패키지를 설치할 때 사용합니다. 글로벌 모드로 패키지를 설치하면 모든 프로젝트에서 해당 패키지를 사용할 수 있으며 패키지를 하나의 실행 파일인 것 처럼 사용할 수 있습니다..
글로벌 모드로 패키지를 설치하려면 npm install -g nodemon
처럼 -g
옵션을 주고 설치하면 됩니다. 이때 sudo
키워드로 관리자 모드를 사용해야할 수 있습니다.
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를 사용하여 설치합니다.
/usr/local/lib/node_modules
디렉토리에 설치합니다.bin/nodemon.js
는 nodemon 패키지 안의 bin 디렉토리에 있는 nodemon.js라는 파일을 나타냅니다. 실제로 이런 파일이 있는지 확인해보면
정말로 존재하는 파일이라는 걸 알 수 있습니다. nodemon.js 파일의 내용을 잠깐 살펴보면,
이렇게 몇 줄의 자바스크립트 코드가 적혀있는 파일이라는 것을 알 수 있습니다. 여기서 가장 맨 위의 #!/usr/bin/env node
이 표시는 이 파일은 node
키워드로 실행해야 한다는 뜻입니다.
화살표 표시는 /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 를 실행해주는 겁니다.
/usr/local/bin/nodemon
이라고 타이핑하지 않고 nodemon main.js
라고 간단하게 써도 실행되는 부분은 Node.js가 아니라 운영체제와 관련된 부분입니다.
운영체제에는 환경 변수(Environment Variables)라는 것이 있습니다. 환경 변수란 시스템을 위해 필요한 각종 기본값들을 저장하고 있는 변수들입니다. 이 중에는 $PATH
라는 환경 변수가 있습니다. 이 $PATH
환경변수는 실행 파일들이 존재하는 디렉토리의 경로들이 저장되어있는 변수입니다.
$PATH
환경 변수의 값으로 다양한 디렉토리들의 경로가 콜론(:)을 기준으로 나열되어 있습니다. /usr/local/bin
디렉토리가 nodemon 파일이 있던 디렉토리입니다.
운영체제는 우리가 어떤 경로를 주지 않고 파일 이름만 적고 엔터를 치면, 이 $PATH
에 있는 모든 디렉토리를 확인하면서 그런 이름의 파일을 찾아서 실행합니다.
(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 search package
npm 레지스트리에서 패키지 이름과 일치하는 패키지를 검색합니다. 이 명령어는 npm 웹 사이트에서 수행하는 검색과 유사한 결과를 출력합니다.
npm info package
npm 레지스트리에서 특정 패키지에 대한 정보를 표시합니다. 이 명령어를 사용하면 패키지의 최신 버전, 라이선스, 의존성 및 메인 파일 등과 같은 정보를 확인할 수 있습니다.
npm list
현재 프로젝트에서 설치된 모든 패키지를 나열합니다. 이 명령어는 프로젝트 내의 모든 패키지와 해당 패키지가 의존하는 패키지들을 재귀적으로 표시합니다.
npm uninstall package
지정된 패키지를 제거합니다. 이 명령어는 현재 프로젝트에서 패키지를 삭제하며, --global 옵션을 추가하면 글로벌 패키지에서 패키지를 삭제할 수 있습니다. 또한, --save 옵션을 추가하면 package.json 파일에서 해당 패키지를 자동으로 제거할 수 있습니다.
npm Docs 참조
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이 2010년에 출시된 패키지 관리 프로그램이라면, Yarn은 2016년 페이스북에서 출시한 패키지 관리 프로그램입니다. 당시 npm의 느린 속도 등을 해결하기 위해 페이스북에서 Yarn을 만들었는데, 출시 당시에 Yarn은 npm보다 더 빠른 패키지 설치 속도를 자랑했고, 오프라인 캐시(offline cache - 한번 설치한 패키지를 계속 보관해두는 기능, 패키지를 다시 삭제하고 인터넷이 끊긴 상태에서도 재설치 가능) 기능 등의 추가 기능을 탑재했습니다.
하지만 npm 또한 그 이후로 속도가 향상되고 캐시 기능 등도 비슷하게 도입되면서 많은 발전을 이루었는데요. 오늘날 개발자들은 자신의 취향에 따라 npm 또는 Yarn을 쓰고 있습니다. 그리고 Yarn 자체가 npm으로부터 많은 핵심 개념을 그대로 본떠서 만든 패키지 매니저 프로그램이기 때문에 npm만 잘 사용할 줄 알면 Yarn도 금방 사용할 수 있습니다.
사용하는 입장에서 알아두면 좋을 Yarn과 npm의 차이는 다음과 같습니다.
npm은 Node.js를 설치할 때 함께 설치되지만, Yarn은 별도로 설치해줘야 하는 패키지입니다. 따라서 아래와 같이 Yarn을 npm에서 설치해줘야 합니다.
npm install -g yarn
우리가 배운 전역 설치 방식으로 설치해주면 됩니다.
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
워킹 디렉토리의 패키지에 설치된 패키지들의 정확한 버전 정보가 package-lock.json
파일에 있는데, Yarn을 사용하는 경우에도 이것과 같은 역할을 하는 파일인 yarn.lock
을 사용합니다.
두 파일은 동일한 내용을 갖고 있지만, 그 형식이 약간 다릅니다.
package-lock.json
yarn.lock
Yarn으로 패키지 관련 작업을 하면 npm 때와 조금은 다른 형식으로 터미널에서 그 결과들이 출력됩니다. Yarn은 npm에 비해 조금은 더 정돈된 디자인과, 귀여운 이모티콘을 출력한다는 것이 특징입니다.
Reference