$PATH를 직접 갈아끼우는 데 있다. 거의 모든 JS 개발자가 매일 쓰지만 안을 들여다본 사람은 드문, "내가 매일 쓰는 도구는 대체 어떻게 동작하나"를 배우기에 더없이 좋은 교과서다. (저장소: nvm-sh/nvm · Shell/POSIX · MIT · ⭐ 약 93.7k · v0.40.5 · 2010년 Tim Caswell 시작)한 문장으로 이 프로젝트가 뭘 하는지부터.
nvm = "한 PC에 Node.js 여러 버전을 동시에 설치해 두고, nvm use 20 같은 한 줄로 '지금 쓸 버전'을 즉시 바꿔 주는 셸 도구". 프로젝트 A는 Node 18, 프로젝트 B는 Node 22를 요구할 때, 매번 Node를 지웠다 깔 필요 없이 디렉터리만 옮겨 다니며 버전을 갈아탈 수 있다. 핵심 통찰은 "Node를 바꾼다 = 사실은 셸의 $PATH에서 가리키는 node 위치를 바꾸는 것"이라는 점이다. nvm은 새 Node를 설치하는 도구이자, 더 본질적으로는 PATH를 갈아끼우는 도구다.
작업마다 드릴(=Node 버전)을 뽑았다 꽂았다 할 필요가 없다. 드릴 여러 개를 멀티탭(=$NVM_DIR 폴더)에 미리 다 꽂아 두고, 스위치(=$PATH의 맨 앞)를 어느 드릴로 돌리느냐만 바꾸면 손에 잡히는 드릴이 즉시 달라진다.
그래서 nvm use 22의 실체는 "Node 22를 새로 설치"가 아니라 "이미 꽂혀 있는 드릴들 중 22번으로 스위치를 돌리는 것"이다. 설치는 nvm install 22가 따로 하고, use는 전원 스위치만 옮긴다.
node라고 치면 셸이 "이 이름의 실행 파일을 어느 폴더에서 찾을지" 순서대로 적어 둔 목록이 PATH다. 콜론(:)으로 폴더들이 이어져 있고, 맨 앞 폴더부터 먼저 찾는다. nvm은 자기 Node 폴더를 이 목록 맨 앞에 끼워 넣어, node가 "nvm이 고른 버전"을 가리키게 만든다.. nvm.sh)./script.sh는 자식 프로세스에서 돌지만, . script.sh(또는 source)는 내 셸 자신이 그 내용을 실행한다. nvm이 함수로 심어지는 이유가 바로 이것 — 자식 프로세스는 부모 셸의 PATH를 못 바꾸기 때문이다(2·4장에서 자세히).한 번 더 못 박자면, nvm이 하는 일은 크게 둘이다. ① 설치: nodejs.org에서 원하는 버전의 바이너리(또는 소스)를 받아 ~/.nvm/versions/node/<버전>/에 풀어 둔다. ② 전환: $PATH 맨 앞을 그 버전의 bin 폴더로 갈아끼운다. 이 단순한 두 동작을, 수십 종의 OS·CPU·셸·엣지케이스를 견디며 안전하게 해내려고 4,800줄이 쓰인 것이다.
반짝 신상이 아니라, '계속' 트렌딩에 오르는 스테디셀러의 이유.
nvm은 어제 나온 화제작이 아니다. 2010년부터 살아 있고, TrendShift의 'GitHub 트렌딩 역대 누적' 집계에서 61번이나 트렌딩에 올랐을 정도로 "끊임없이 다시 인기를 끄는" 스테디셀러다. 새 Node가 나올 때마다, 새 개발자가 환경을 세팅할 때마다 다시 검색되기 때문이다. 이 딥다이브의 가치는 "신기술 따라잡기"가 아니라 "내가 매일 무심코 쓰는 도구의 속을 처음으로 열어 보는" 학습에 있다.
Node는 버전 간 호환이 자주 깨진다. 회사 레거시는 Node 16, 신규 프로젝트는 22, 어떤 오픈소스는 18에서만 빌드된다. nvm은 이 문제를 "OS에 Node를 하나만 깐다"는 전제 자체를 버리는 것으로 풀었다. 버전별로 폴더를 따로 두고 PATH만 바꾸는 발상은 지금은 당연해 보이지만, 당시엔 신선했고 그 단순함 덕에 표준이 됐다.
nvm은 Node로 만들어지지 않았다 — Node를 깔기 전에 동작해야 하니 당연하다. Go·Rust 같은 컴파일 런타임도, 별도 패키지 매니저도 필요 없이 시스템에 거의 항상 있는 bash/zsh와 curl 혹은 wget만으로 돈다. "최소 의존성으로 어디서나 도는 도구"의 교본이다.
설치법이 curl ... | bash(인터넷에서 받은 스크립트를 바로 실행)라 보안 비판을 받기 쉬운 부류다. 그런데 nvm 저장소에는 SECURITY.md, THREAT_MODEL.md(위협 모델), INCIDENT_RESPONSE_PLAN.md(사고 대응 계획)가 있고, 다운로드 무결성(체크섬), 입력값 새니타이즈, 코드 인젝션 방지 테스트까지 갖췄다. 셸 도구의 보안을 어떻게 설계하는지 보여 주는 드문 사례다.
nvm = 가장 보편적이고 의존성 없는 '표준'. 거의 모든 튜토리얼이 nvm을 전제한다. 단점은 셸 시작이 느려질 수 있다는 것(셸을 열 때마다 4,800줄을 읽어 들임).
fnm·volta = Rust로 짠 '빠른' 대안(셸 시작 부담이 작다). asdf = Node 말고 다른 런타임까지 한꺼번에 관리. n = 더 미니멀. 그래도 nvm이 1위인 건 호환성·문서·생태계의 두께 때문이다. (자세한 비교는 9장)
| 지표 | 값 / 의미 |
|---|---|
| 스타 | 약 93,700개 (Shell 카테고리 최상위권) |
| 나이 | 2010년 시작 — 15년 넘게 유지되는 장수 프로젝트 |
| 언어 | Shell 100% (POSIX 호환을 목표로 한 bash 스크립트) |
| 코드 규모 | nvm.sh 4,846줄 · 함수 116개 · install.sh 507줄 |
| 라이선스 | MIT (자유롭게 쓰고 고칠 수 있음) |
| 지원 셸 | bash · zsh · ksh · dash · sh (테스트로 보증) |
'백엔드/프론트엔드'가 없는 도구다 — 대신 셸·네트워크·테스트/CI 3축으로 본다.
nvm은 웹 서버가 아니라 로컬 셸 도구다. 그래서 보통의 "백엔드·프론트엔드·인프라" 대신, ① 셸 런타임(핵심 로직), ② 네트워크·파일(설치·다운로드), ③ 테스트·CI(품질) 세 축으로 보는 게 정확하다.
| 요소 | 역할 |
|---|---|
| bash / POSIX sh | nvm의 '구현 언어'. 특정 셸 전용 문법(bashism)을 피해 zsh·ksh·dash에서도 동일하게 돌도록 작성. |
셸 함수 nvm() | 모든 명령의 진입점. nvm.sh를 소싱하면 이 함수가 셸에 등록된다. 실행 파일이 아니라 '함수'인 게 핵심. |
awk / sed / grep | PATH 같은 콜론 구분 문자열을 자르고 끼워 넣는 '텍스트 수술' 도구. nvm_change_path의 핵심 재료. |
command 빌트인 | command grep처럼 앞에 붙여 사용자가 만든 alias·함수를 우회하고 '진짜 원본 명령'을 부른다. 환경 오염 방어. |
| 요소 | 역할 |
|---|---|
curl 또는 wget | nodejs.org에서 바이너리/소스를 받는 다운로더. nvm은 둘 중 있는 걸 자동으로 골라 쓴다(nvm_download). |
| tar / xz / gzip | 받은 압축 파일을 푼다. nvm_supports_xz로 더 작은 xz 압축을 쓸 수 있는지 판단. |
| sha256 체크섬 | 받은 파일이 중간에 변조·손상되지 않았는지 검증(nvm_compute_checksum / nvm_compare_checksum). |
| gcc / g++ / make | 소스 설치 시에만 필요. 바이너리가 없는 OS/CPU에서 Node를 직접 컴파일(nvm_install_source). |
| 미러(mirror) 변수 | NVM_NODEJS_ORG_MIRROR 등으로 다운로드 서버를 사내/지역 미러로 바꿀 수 있다. |
| 요소 | 역할 |
|---|---|
| urchin | 셸 스크립트용 테스트 러너(devDependency). test/ 폴더의 실행 파일들을 돌려 통과/실패를 센다. |
| Makefile | 테스트 실행 오케스트레이터. make test-bash, make TEST_SUITE=fast ...처럼 셸·범위별로 묶어 실행. |
| ShellCheck | 셸 스크립트 정적 분석기(린터). 흔한 버그·이식성 문제를 자동 검출. CI에서 강제. |
| GitHub Actions | tests.yml·shellcheck.yml·설치 테스트 등 다수 워크플로로 여러 셸·OS에서 자동 검증. |
| Dockerfile | Ubuntu 22.04에 bash·zsh·ksh·dash·shellcheck를 깐 멀티 셸 개발/테스트 환경을 재현. |
nvm의 스택은 "특별한 게 없다는 게 특별하다". 화려한 프레임워크 없이 모든 유닉스에 이미 깔린 도구(bash·curl·awk·sed·tar)만으로 작동하도록 설계됐다. 그래서 어디서나 돌고, 그래서 표준이 됐다.
"왜 함수여야만 하는가"부터 PATH 수술까지 — nvm의 설계 급소 5가지.
가장 먼저 깨야 할 오해. nvm은 /usr/bin/ 어딘가에 있는 실행 파일이 아니다. 터미널에서 type nvm을 쳐 보면 "nvm is a function"이라고 나온다. 이건 버그가 아니라 nvm 설계의 심장이다.
유닉스에서 자식 프로세스는 부모의 환경(PATH 등)을 바꿀 수 없다. 만약 nvm이 일반 실행 파일이라면, 그건 내 터미널의 '자식'으로 잠깐 떴다 사라지는 셈이라 — 내 터미널의 PATH를 바꿔 봤자 자기 안에서만 바뀌고, 끝나면 원상복구된다. 마치 "옆방(자식)에서 우리 집(부모) 가구를 옮겨도, 우리 집은 그대로"인 셈.
그래서 nvm은 자식이 아니라 내 셸 자신이 되어야 한다. . nvm.sh로 소싱하면 nvm() 함수가 내 셸 안에 등록되고, 이 함수가 export PATH=...를 실행하면 내 터미널의 PATH가 진짜로 바뀐다.
# 설치 시 ~/.bashrc(또는 ~/.zshrc)에 nvm이 심는 3줄
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # nvm 함수를 셸에 로드
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" # 탭 자동완성
새 터미널을 열 때마다 이 3줄이 nvm.sh를 '소싱'하기 때문에 nvm이 늘 손에 잡히는 것이다. 동시에 이게 nvm의 유명한 단점 — 셸을 열 때마다 4,800줄을 읽어 들여 시작이 느려질 수 있다(해법은 6·9장 '지연 로딩').
nvm.sh 전체는 중괄호 { ... } 하나로 감싸여 있고, 파일 맨 끝은 이렇게 닫힌다:
} # this ensures the entire script is downloaded #
이유가 영리하다. curl ... | bash로 받다가 네트워크가 끊겨 파일이 절반만 도착하면, 셸은 받은 데까지만 실행하려 든다. 그러면 위험한 중간 상태가 될 수 있다. 그런데 전체를 { }로 감싸 두면, 셸은 닫는 }를 만나기 전까지 블록 전체를 실행하지 않는다. 즉 마지막 줄까지 무사히 도착해야 비로소 실행된다 — "끝까지 다 받았을 때만 돌아라"를 한 줄로 보장하는 관용구다.
nvm() 디스패처와 116개의 nvm_ 함수공개 명령은 nvm() 함수 하나(약 1,500줄, 전체의 1/3)뿐이고, 내부는 거대한 case문으로 서브커맨드를 가른다. 실제 로직은 nvm_ 접두사가 붙은 116개의 헬퍼 함수에 흩어져 있다.
nvm_)nvm_를 붙여(예: nvm_change_path) 충돌을 피한다. 셸에서 흉내 내는 일종의 '비공개 모듈' 기법.nvm() {
case "$1" in
"install"|"i") ... ;; # 버전 설치 (바이너리 or 소스)
"use") ... ;; # PATH 갈아끼우기 (전환)
"ls"|"list") ... ;; # 설치/원격 버전 목록
"alias"|"unalias")... ;; # default·lts/* 같은 별칭
"exec"|"run") ... ;; # 특정 버전으로 명령/스크립트 실행
"uninstall") ... ;; # 버전 삭제
"deactivate") ... ;; # PATH에서 nvm 흔적 제거
...
esac
}
nvm use의 실체 — PATH 수술버전 전환의 핵심은 단 몇 줄로 압축된다. nvm_change_path로 PATH를 다시 조립하고, hash -r로 셸의 명령 위치 캐시를 비우고, 환경변수를 내보낸다:
# nvm.sh 'use' 케이스의 심장부 (요약)
NVM_VERSION_DIR="$(nvm_version_path "$VERSION")" # ~/.nvm/versions/node/v22.x.x
PATH="$(nvm_change_path "$PATH" "/bin" "$NVM_VERSION_DIR")" # PATH 맨 앞에 이 버전의 bin 삽입
export PATH
\hash -r # 셸의 '명령→경로' 캐시 비우기(필수!)
export NVM_BIN="$NVM_VERSION_DIR/bin"
export NVM_INC="$NVM_VERSION_DIR/include/node"
hash -r (해시 리셋)node는 여기 있더라"를 외워 둔다(해시 캐시). PATH만 바꾸고 이 캐시를 안 비우면 옛 버전 위치를 계속 가리킨다. hash -r가 그 기억을 지워, 다음 node 호출이 새 PATH를 다시 탐색하게 만든다. 이걸 빠뜨리면 "분명 use 했는데 버전이 그대로"인 유령 버그가 생긴다.그럼 nvm_change_path는 PATH 문자열에 어떻게 끼워 넣을까? awk/sed로 콜론 구분 목록을 훑으며 ① nvm 경로가 없으면 맨 앞에 추가, ② 이미 있으면 그 자리를 새 버전으로 '치환'(순서 보존), ③ 시스템 경로가 nvm 경로보다 앞서면 그냥 앞에 덧붙이는 등 엣지케이스별로 다르게 처리한다. "텍스트 한 줄을 안전하게 갈아끼우기"가 생각보다 어렵다는 걸 보여 주는 코드다.
.nvmrc — 디렉터리를 거슬러 올라가며 찾기프로젝트 폴더에 .nvmrc(예: 내용이 22) 파일을 두면, 그 폴더에서 nvm use만 쳐도 알맞은 버전이 선택된다. 찾는 방식은 nvm_find_up — 현재 폴더에서 위로 한 칸씩 올라가며 .nvmrc가 나올 때까지 탐색한다(git이 .git을 찾는 방식과 똑같다):
nvm_find_up() { # 상위로 거슬러 올라가며 파일 찾기
path_="$PWD"
while [ "$path_" != "" ] && [ ! -f "$path_/$1" ]; do
path_=${path_%/*} # 경로 끝 한 칸 제거 = 한 단계 위로
done
nvm_echo "$path_"
}
폴더를 옮길 때 .nvmrc를 읽어 자동으로 nvm use를 해 주는 건 nvm 기본 기능이 아니다. README의 'Deeper Shell Integration'에 나온 셸 훅(bash cdnvm 함수, zsh add-zsh-hook chpwd load-nvmrc)을 직접 프로필에 추가해야 한다(실습 8장).
저장소의 핵심 파일과, 설치 후 내 PC에 생기는 ~/.nvm 폴더 둘 다 본다.
| 파일 | 역할 / 배울 점 |
|---|---|
| nvm.sh | 전부의 본체. nvm() 디스패처 + 설치·전환·alias·ls·체크섬·OS/arch 감지 등 모든 로직. |
| install.sh | 설치 단계 분리의 모범. git clone 또는 스크립트 다운로드 → 셸 프로필 자동 감지·주입. |
| nvm-exec | 20줄. "특정 버전 골라 명령 실행"을 exec "$@" 한 줄로 끝내는 깔끔한 래퍼. |
| bash_completion | nvm use <탭> 같은 자동완성. 셸 컴플리션 작성법 학습용. |
| Makefile + package.json | 셸 프로젝트의 테스트/CI를 어떻게 구성하는가의 예. |
~/.nvm설치한 모든 버전은 ~/.nvm/versions/node/ 아래 나란히 공존한다. nvm이 하는 일은 이 중 하나의 bin 폴더를 PATH 맨 앞에 놓는 것뿐. 그래서 전환이 즉시·무손실이고, nvm uninstall은 그냥 해당 폴더를 지우는 일이다.
test/는 fast·slow·install_script·installation_node·sourcing·xenial 등으로 나뉜다. 특이한 점: 테스트 파일의 '이름'이 곧 테스트 설명이다 — 예컨대 test/fast/Listing versions/Running 'nvm ls' should display all installed versions 라는 긴 문장이 그대로 실행 가능한 테스트 파일명이다(urchin 방식). 또 uname·isainfo 같은 시스템 명령을 가짜로 바꾼 목(mock)으로 "이 OS/CPU인 척"하며 크로스플랫폼 로직을 검증한다.
nvm 한 저장소에서 뽑아낼 수 있는 '진짜 배울 것'들.
nvm을 이해하면 셸의 가장 헷갈리는 개념이 한 번에 풀린다. 실행(./x.sh)은 자식 프로세스, 소싱(. x.sh)은 내 셸 자신. 환경변수를 바꾸는 도구(nvm·rbenv·conda activate 등)가 왜 죄다 "source 하세요"라고 하는지, 그 이유가 곧 nvm의 존재 이유다.
echo 'export FOO=1' > t.sh 후 bash t.sh 하고 echo $FOO → 비어 있음(자식에서만 설정). 다시 . t.sh 하고 echo $FOO → 1. 이 차이가 nvm 전부의 출발점이다.
PATH가 어떻게 검색되는지(맨 앞부터), which·type·command -v의 차이, hash -r의 필요성, 경로를 '앞에 추가'할지 '치환'할지의 트레이드오프. nvm_change_path/nvm_strip_path는 이 주제의 살아 있는 교재다.
bash 전용 문법을 피하고, 파라미터 확장(${var%/*}, ${1-}), case문, command 접두사를 쓰는 법. nvm이 zsh·ksh·dash에서까지 도는 비결을 따라가다 보면 이식성 감각이 길러진다. 짝꿍 도구는 ShellCheck — 이식성·버그를 자동으로 잡아 준다.
nvm은 PATH·버전 목록·체크섬 출력 같은 텍스트를 awk/sed/grep로 가공한다. "콜론으로 이어진 문자열에서 특정 토막만 빼거나 바꾸기"를 awk로 푸는 패턴은 데이터 정리·로그 처리 등 어디든 쓰인다.
nvm_get_os/nvm_get_arch가 uname 출력을 파싱해 linux/macos/alpine(musl)·arm/x64·Rosetta 등을 가려낸다. 더 멋진 건 테스트 방식 — 가짜 uname을 PATH에 끼워 넣어 "내가 ARM64 맥인 척"하게 만들어 검증한다. 외부 의존(시스템·네트워크)을 어떻게 테스트하는가의 모범.
다운로드 무결성(sha256), 입력 새니타이즈(nvm_sanitize_auth_header가 위험 문자를 sed로 제거), 코드 인젝션 방지(테스트 케이스 nvm_download no eval injection·security_wget_injection), 다운로드 완결 가드({ }), 그리고 THREAT_MODEL.md. "신뢰할 수 없는 입력을 다루는 셸 도구"를 어떻게 방어적으로 짜는지 배운다.
eval이나 따옴표 처리 실수로 잘 터진다. nvm은 이를 막는 전용 테스트까지 둬서, 받은 값이 '데이터'로만 다뤄지고 '명령'으로 새지 않도록 한다.urchin 러너, "파일명=테스트설명" 규약, Makefile로 스위트 분리, package.json의 test가 부모 셸을 자동 감지해 그 셸로 테스트를 돌리는 트릭, GitHub Actions 멀티 셸/OS 매트릭스, Dockerfile로 재현 가능한 멀티 셸 환경. 컴파일 언어가 아닌 셸도 이렇게 진지하게 테스트할 수 있음을 보여 준다.
무겁지 않다 — 거의 모든 유닉스 계열 환경에서 돈다.
| 항목 | 요구사항 |
|---|---|
| OS | Linux · macOS · WSL(Windows Subsystem for Linux). 네이티브 Windows는 미지원 — 별도 프로젝트인 nvm-windows를 쓰거나 WSL 사용. |
| 셸 | bash · zsh · ksh · dash · sh 중 하나(POSIX 호환). 로그인 셸의 프로필(.bashrc/.zshrc)에 로드 줄이 들어가야 함. |
| 필수 명령 | curl 또는 wget(둘 중 하나면 됨), git(git 방식 설치 시), 기본 유틸(tar·sed·awk·grep). |
| 빌드 도구 | 바이너리 설치면 불필요. 소스 컴파일(nvm install -s) 시에만 gcc/g++/make 필요(시간·CPU 많이 듦). |
| 디스크 | nvm 자체는 수 MB. 단, Node 버전마다 수십~수백 MB가 쌓이므로 여러 버전을 두면 누적 용량 주의. |
| 네트워크 | 설치 시 nodejs.org 접근 필요. 폐쇄망은 NVM_NODEJS_ORG_MIRROR로 사내 미러 지정 가능. |
nvm: command not found거의 항상 프로필 파일에 로드 3줄이 안 들어갔거나, 셸을 새로 안 띄운 것이다. 같은 터미널에서 즉시 쓰려면 . ~/.bashrc(또는 .zshrc)로 다시 소싱해야 한다. zsh인데 nvm이 .bash_profile에만 줄을 넣었을 때도 발생 — 그래서 install.sh가 셸을 감지해 올바른 프로필을 고르려 애쓴다.
새 터미널마다 nvm.sh(4,800줄)를 소싱하느라 시작이 0.1~0.5초 느려질 수 있다. 해법: 지연 로딩(lazy load) — 처음 nvm/node를 부를 때만 nvm을 로드하도록 프로필을 손보거나, 더 가벼운 대안(fnm 등)을 검토(9장).
읽기만 하지 말고 손으로 — 난이도순 5선.
nvm install 20, nvm install 22로 둘을 깐 뒤 nvm use 20 → node -v, nvm use 22 → node -v로 바뀌는지 확인. 사이사이 which node를 쳐서 경로가 ~/.nvm/versions/node/...로 바뀌는 것을 눈으로 보라. 마지막에 nvm alias default 22로 기본값 고정.
grep -n nvm ~/.bashrc ~/.zshrc 2>/dev/null로 nvm이 추가한 줄을 찾아, export NVM_DIR·. "$NVM_DIR/nvm.sh"·bash_completion 세 줄이 각각 무슨 일을 하는지 한 줄씩 주석으로 설명해 보라(답은 4장).
.nvmrc + cd 자동 전환 훅 달기 난이도 ★★☆프로젝트 폴더에 echo "20" > .nvmrc를 만들고 nvm use(버전 인자 없이)가 20을 고르는지 확인. 그다음 폴더만 옮겨도 자동 전환되도록 zsh 훅을 .zshrc 맨 끝(nvm 로드 줄 뒤)에 추가:
# zsh: cd 할 때마다 .nvmrc 읽어 자동 use
autoload -U add-zsh-hook
load-nvmrc() {
local p="$(nvm_find_nvmrc)"
if [ -n "$p" ]; then
[ "$(nvm version)" != "$(nvm version "$(cat "$p")")" ] && nvm use
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
nvm을 30줄로 흉내 내 보라. mytool.sh에 두 함수를 정의하고 source 한다: addpath <dir>(받은 폴더를 PATH 맨 앞에 추가 후 hash -r), rmpath <dir>(PATH에서 그 폴더 제거). 그리고 ./binA·./binB 두 가짜 hello 스크립트를 만들어 addpath로 전환해 보라.
nvm_change_path 읽고 테스트 케이스 짜기 난이도 ★★★nvm.sh에서 nvm_change_path를 정독한 뒤, 세 시나리오의 기대 출력을 손으로 예측하고 실제로 함수를 불러 비교하라: ① 빈 PATH, ② nvm 경로가 없는 PATH, ③ 시스템 경로가 nvm 경로보다 앞에 있는 PATH(이슈 #1652 케이스). 가능하면 urchin 스타일로 test/ 한 칸을 만들어 자동화.
nvm을 출발점 삼아 '셸 마스터'로 가는 6주 코스.
| 주차 | 주제 · 할 일 |
|---|---|
| 1주차 | 셸 기초 멘탈모델 — 프로세스/환경변수, 소싱 vs 실행, PATH 검색 순서, type/command -v/which 차이. (과제 1·2) |
| 2주차 | POSIX 셸 스크립팅 — 파라미터 확장, case, 함수, 따옴표/이스케이프, 이식성. ShellCheck 설치해 내 스크립트 린트. |
| 3주차 | nvm 소스 정독 — nvm() 디스패처 → use/install 경로 → alias 시스템 순으로 따라 읽기. (과제 5) |
| 4주차 | 텍스트 처리 — awk/sed로 PATH·CSV·로그 다루기. nvm_change_path를 직접 재구현. (과제 4) |
| 5주차 | 셸 테스트 & CI — urchin 써 보기, uname 목으로 크로스플랫폼 테스트, GitHub Actions 매트릭스, Dockerfile로 멀티 셸 환경 만들기. |
| 6주차 | 대안 비교 & 최적화 — fnm·asdf·volta·n과 nvm을 직접 써 보고 셸 시작 시간 측정·지연 로딩 적용. corepack(yarn/pnpm 버전 관리)까지. |
| 도구 | 특징 / 언제 |
|---|---|
| nvm | 표준·최대 생태계·무의존. 단점은 셸 시작 부담. "가장 보편적인 정답". |
| fnm | Rust로 작성돼 빠르다. .nvmrc 호환. 셸 시작 무거움이 싫을 때. |
| volta | Rust. 프로젝트의 package.json에 도구 버전을 '고정(pin)'하는 철학. |
| asdf / mise | Node뿐 아니라 Python·Ruby·Go 등 다중 런타임을 하나로 관리. |
| n | 아주 미니멀한 Node 전용 버전 매니저. |
| nvm-windows | 이름만 닮은 별개 프로젝트(Go 작성). 네이티브 Windows용. |
나오면 막히는 용어들 — 한 줄 정의로 정리.
| 키워드 | 뜻 |
|---|---|
| nvm | Node Version Manager. 여러 Node 버전을 깔아 두고 전환하는 셸 도구. |
| $NVM_DIR | nvm이 모든 걸 보관하는 폴더(보통 ~/.nvm). 버전·alias·캐시가 여기 산다. |
소싱 / source / . | 스크립트를 자식 프로세스가 아니라 현재 셸 안에서 읽어 실행. nvm이 함수로 사는 이유. |
| $PATH | 실행 파일을 찾을 폴더 목록(콜론 구분, 앞에서부터 검색). nvm이 갈아끼우는 대상. |
hash -r | 셸의 '명령→경로' 캐시 초기화. PATH 변경을 즉시 반영시키는 필수 동작. |
| .nvmrc | 프로젝트 폴더에 둔 "이 폴더는 Node 몇 버전" 표식 파일. nvm use가 읽는다. |
| alias (default / lts/* / node / stable) | 버전에 붙인 별칭. default=새 셸 기본, lts/*=장기지원 버전, node=최신. |
| LTS | Long-Term Support. 오래 보안패치되는 안정 버전 라인(짝수 메이저). 운영용 권장. |
| 바이너리 vs 소스 설치 | 미리 컴파일된 파일을 받느냐(빠름) vs 내 PC에서 직접 컴파일하느냐(-s, 느림·빌드도구 필요). |
| POSIX | 유닉스 계열 공통 표준. "POSIX 호환 셸 코드" = 어느 표준 셸에서나 동일하게 동작. |
shebang (#!) | 스크립트 첫 줄의 #!/usr/bin/env bash — 이 파일을 어떤 인터프리터로 돌릴지 지정. |
| curl | bash | 인터넷 스크립트를 받아 바로 실행하는 설치 관용구. 편하지만 보안에 주의가 필요한 방식. |
| 체크섬(checksum) / sha256 | 파일의 '지문'. 받은 파일이 변조·손상 없이 원본과 같은지 비교하는 무결성 검증값. |
command 빌트인 | 사용자 alias·함수를 무시하고 '원본 명령'을 호출. 환경 오염을 막는 방어 기법. |
| ShellCheck | 셸 스크립트 정적 분석기(린터). 버그·이식성 문제를 자동 검출. |
| urchin | nvm이 쓰는 셸 테스트 러너. 디렉터리·파일명 규약으로 테스트를 조직. |
| MANPATH | man 문서를 찾는 경로 목록. nvm은 PATH와 함께 이것도 버전에 맞춰 바꾼다. |
| iojs | 2014~2015년 Node에서 갈라졌다 다시 합쳐진 포크. nvm에 그 시절 호환 코드가 남아 있다. |
| 지연 로딩(lazy load) | nvm을 셸 시작 때 전부 읽지 않고, 처음 호출될 때만 로드해 시작을 빠르게 하는 기법. |
원문으로 더 깊이 파고들 때.
· GitHub 저장소: github.com/nvm-sh/nvm
· README(설치·.nvmrc·셸 통합·트러블슈팅 총정리): nvm-sh/nvm#readme
· Deeper Shell Integration(cd 자동 전환 훅): #deeper-shell-integration
· 본체 소스 nvm.sh: blob/master/nvm.sh
· 설치 스크립트 install.sh: blob/master/install.sh
· 보안 정책 SECURITY.md: .github/SECURITY.md
· 위협 모델 THREAT_MODEL.md: .github/THREAT_MODEL.md
· fnm(Rust, 빠름): github.com/Schniz/fnm
· volta: github.com/volta-cli/volta
· asdf(다중 런타임): github.com/asdf-vm/asdf
· n: github.com/tj/n
· nvm-windows(별개 프로젝트): github.com/coreybutler/nvm-windows
· ShellCheck: shellcheck.net
· Node.js 릴리스 일정(LTS 확인): nodejs.org/about/previous-releases
· corepack(yarn/pnpm 버전 관리): github.com/nodejs/corepack