[node.js] npm이 사용하는 폴더 구조

원문: https://docs.npmjs.com/cli/v8/configuring-npm/folders

번역: 양정석 (dasomoli@gmailREMOVETHIS.com)

설명

npm은 여러분의 컴퓨터 상에 다양한 것들을 둡니다. 이게 그게 하는 일이예요.

이 문서는 이게 어디에 무엇을 두는지 설명합니다.

요약

  • 로컬 설치 (기본): 현재 패키지의 루트 디렉토리의 ./node_modules 에 설치
  • 글로벌 설치 (-g 옵션): /usr/local 또는 노드가 설치된 곳에 설치
  • require()를 한다면 로컬에 설치
  • 커맨드 라인에서 실행한다면 글로벌 설치
  • 로컬과 글로벌 둘 다 필요하다면 양쪽 모두에 설치하거나 npm link를 사용

prefix 설정

기본 prefix 설정은 노드가 설치된 곳입니다. 대부분 /usr/local 입니다. 윈도우즈에서는 %AppData%\npm 입니다. {prefix}/node.exe 가 아니라 {prefix}/bin/node에 보통 설치되므로 유닉스 시스템에서는 한 레벨 더 들어갑니다.

global 플래그가 설정되면, npm은 이 prefix 내에 설치합니다. 설정되지 않았다면, 패키지 내에 없으면 현재 패키지 또는 현재 작업 디렉토리의 루트 디렉토리를 사용합니다.

노드 모듈

패키지들은 prefix 아래의 node_modules 폴더 내에 들어갑니다. 로컬에 설치될 때, 이는 main 모듈을 로드하기 위해 여러분이 require("packagename")을 사용하거나 다른 모듈을 사용하기 위해서 require("packagename/lib/path/to/sub/module")을 사용할 수 있다는 뜻입니다.

유닉스 시스템 상에서의 글로벌 설치는 {prefix}/lib/node_modules로 진행됩니다. 윈도우즈 상에서 글로벌 설치는 {prefix}/node_modules로 진행(즉, lib가 없음)됩니다.

범위가 지정된 패키지들은 @ 기호가 붙는 범위 prefix의 이름과 연관된 node_modules 폴더의 하위 폴더 내에 함께 그룹지어지는 것을 빼면 같은 방식으로 설치됩니다. 예를 들어, npm install @myorg/package는 그 패키지를 {prefix}/node_modules/@myorg/package 내에 설치합니다. 더 자세한 사항은 scope을 참고하세요.

패키지를 require()하길 원한다면, 로컬에 설치하세요.

실행 파일

글로벌 모드에서 유닉스 상에서는 {prefix}/bin, 윈도우즈 상에서는 {prefix}에 직접 실행 파일이 링크됩니다. 여러분의 터미널의 PATH 환경 변수에 이를 실행할 수 있도록 이 경로가 들어가 있는지 꼭 확인하세요.

로컬 모드에서 실행파일은 ./node_modules/.bin 내에 링크되기 때문에 npm을 통해 스크립트 실행이 가능합니다. (예를 들어 npm test를 실행할 때 그 경로 내에 테스트 러너가 있을 겁니다)

Man Pages

글로벌 모드에서 man page는 {prefix}/share/man 내로 링크됩니다.

로컬 모드에서는 man page는 설치되지 않습니다.

Man page는 윈도우즈 시스템 상에서 설치되지 않습니다.

캐시

npm cache를 보세요. 캐시 파일들은 Posix 상에서는 ~/.npm 내에, 윈도우즈 상에서는 %AppData%/npm-cache 내에 저장됩니다.

이는 cache 설정 파라미터에 의해 제어됩니다.

임시 파일

임시 파일은 기본 값인 TMPDIR, TMP, TEMP 환경변수, 또는 유닉스 상에서 /tmp나 윈도우즈 상에서 c:\windows\temptmp 설정으로 지정된 폴더 내에 저장됩니다.

임시 파일들은 이 루트 디렉토리 안에 프로그램의 각각의 실행에 대한 유일한 폴더가 지정되고, 성공적으로 끝나면 지워집니다.

더 자세한 정보

로컬 설치 시에 npm은 먼저 적절한 prefix 폴더를 찾으려고 시도합니다. 이는 그래서 npm install foo@1.2.3은 여러분이 다른 폴더로 cd를 이용해서 이동했었다 하더라도 여러분의 패키지의 합리적인 루트 디렉토리에 설치할 겁니다.

$PWD에서 시작해서 npm은 package.json 파일 아니면 node_modules 폴더를 갖는 폴더를 확인하면서 탐색합니다. 그런 것을 찾으면 npm 명령을 실행하는 목적에 맞는 실제적인 “현재 디렉토리”로 취급합니다. (이 동작은 git의 작업 디렉토리 내에서 git 명령을 실행했을 때 .git 폴더를 찾는 로직에서 영감을 받아 비슷합니다).

아무 패키지 루트도 찾지 못했다면, 현재 폴더가 사용됩니다.

npm install foo@1.2.3을 실행하면, 그 패키지는 캐시 내에 로드된 후, ./node_modules/foo내에 풀게 됩니다. 그 후 foo의 디펜던시들은 비슷하게 ./node_modules/foo/node_modules/...에 풀게 됩니다.

./node_modules/.bin에 심볼릭 링크된 바이너리 파일들은 npm 스크립트가 필요할 때 찾을 수 있습니다.

글로벌 설치

global 설정이 true로 설정되면, npm은 “글로벌”로 패키지를 설치합니다.

글로벌 설치에서 패키지들은 위에서 설명된 폴더를 사용해서 거의 같은 방식으로 설치됩니다.

순환, 충돌, 그리고 폴더의 중복 제거

순환은 node_modules 폴더를 찾는 디렉토리를 탐색하는 노드의 모듈 시스템 속성을 사용해서 처리됩니다.모든 단계에서 어느 패키지가 이미 그 상위의 node_modules 폴더에 설치되어 있다면 현재 위치에는 설치하지 않습니다.

foo -> bar -> baz에서 위의 경우를 생각해 봅시다. baz가 bar에 의존한다고 상상해보면, foo -> bar -> baz -> bar -> baz ... 가 될 겁니다. 그러나 폴더 구조가 foo/node_modules/bar/node_modules/baz가 되기 때문에 require("bar")를 부르면, foo/node_modules/bar 내에 설치된 복사본을 얻을 것이므로 bar의 다른 복사본을 .../baz/node_modules 내에 둘 필요가 없습니다.

이 손쉬운 방법은 정확히 같은 버전이 중첩된 node_modules 폴더 내에 설치되었을 때만 사용됩니다. 두 “a” 패키지가 다른 버전이라면 a/node_modules/b/node_modules/a가 되는 것이 여전히 가능합니다. 그러나 정확히 같은 패키지를 여러번 반복하는 일 없이 무한 회귀는 언제나 방지됩니다.

다른 최적화는 아래의 로컬라이즈된 “target” 폴더 아래에 가능한 한 가장 높은 레벨에 의존 모듈들을 설치해서 구성할 수 있습니다.

예제

이런 의존 그래프를 생각해보죠.

foo
+-- blerg@1.2.5
+-- bar@1.2.3
|   +-- blerg@1.x (latest=1.3.7)
|   +-- baz@2.x
|   |   `-- quux@3.x
|   |       `-- bar@1.2.3 (순환)
|   `-- asdf@*
`-- baz@1.2.3
    `-- quux@3.x
        `-- bar

이 경우, 우리는 다음과 같은 폴더 구조를 기대할 수 있을 겁니다.

foo
+-- node_modules
    +-- blerg (1.2.5) <---[A]
    +-- bar (1.2.3) <---[B]
    |   `-- node_modules
    |       +-- baz (2.0.2) <---[C]
    |       |   `-- node_modules
    |       |       `-- quux (3.2.0)
    |       `-- asdf (2.3.4)
    `-- baz (1.2.3) <---[D]
        `-- node_modules
            `-- quux (3.2.0) <---[E]

foo가 bar@1.2.3baz@1.2.3에 직접 의존하므로, 이들은 foo의 node_modules 폴더 내에 설치됩니다.

blerg의 최신본이 1.3.7임에도 불구하고, foo는 1.2.5 버전으로 지정된 의존을 갖습니다. 그래서 이는 [A]에 설치됩니다. blerg의 상위 설치가 bar의 blerg@1.x 의존을 만족하므로, [B] 아래에 다른 복사본을 설치하지 않습니다.

bar [B] 또한 baz와 asdf에 의존을 갖습니다. 그래서 이들은 bar의 node_modules 폴더에 설치됩니다. 이게 baz@2.x에 의존하므로 그 상위의 node_modules 폴더 [D] 에 설치된 baz@1.2.3을 재사용할 수는 없고, 그 자신의 복사본 [C]를 설치해야만 합니다.

bar 아래에 baz -> quux -> bar 의존은 순환을 만듭니다. 그러나 bar가 이미 quux의 조상 [B] 이므로, 그 폴더 내에 bar의 다른 복사본을 풀지 않습니다.

foo -> baz [D] 아래에, quux의 [E] 폴더 트리는 비게 됩니다. bar 상의 그 의존이 [B] 에 설치된 상위 폴더 복사본에 의해 만족되기 때문입니다.

무엇이 어디에 설치되는지의 그래프 분석은 npm ls를 사용하세요.

퍼블리싱

퍼블리싱 상에서 npm은 node_modules 폴더 안을 들여다 봅니다. 항목 들 중 어느 것도 bundledDependencies 배열에 들어있지 않으면, 그들은 패키지 tarball 내에 포함되지 않을 겁니다.

이는 패키지 메인테이너가 그들의 의존 모두를 로컬에 설치할 수 있도록 하지만, 다른 곳에서 찾을 수 없는 이들 항목만 다시 퍼블리싱 합니다. 더 자세한 정보는 package.json을 보세요.

더 읽을 거리

[node.js] node.js 설치는 nvm으로

node.js 설치를 위해서 nodejs.org의 다운로드에서 다운로드 받아 설치할 수도 있지만(이런 방식을 Node installer라고 한다), nvm을 이용하는 것이 좋다.

nvm은 node version manager의 약자로, node 버전을 필요에 따라 바꿔가며 사용할 수 있다. nvm의 설치는 https://github.com/nvm-sh/nvm#installing-and-updating를 참고하자.

nvm을 설치하고 나면 이 nvm을 통해 node.js의 특정 버전을 설치하면 된다. 현재 기준 LTS인 16을 설치하려면 다음과 같이 한다.

$ nvm install 16

그러면 해당 버전의 node.js와 함께 npm이 설치된다. npm은 node package manager의 약자이다.

최신 LTS 버전을 설치하고 싶을 때는 그냥 다음과 같이 하면 된다.

$ nvm install --lts

최신 npm을 다운로드하고 싶을 때는 npm 그 자체를 이용한다. 다음과 같이 하면 된다.

$ npm install --location=global npm

yarn 을 사용하고 싶다면 그냥 다음과 같이 하면 된다.

$ corepack enable