`npm install` 이 내부적으로 하는 일
npm install 은 크게 두 종류의 의존성을 처리합니다:
| 순수 JS 패키지 | lodash, axios, moment 등 | .js 파일만 다운로드 | ❌ 영향 없음 |
| 네이티브 애드온 패키지 | node-sass, bcrypt, sqlite3, canvas, sharp 등 | C/C++ 소스를 node-gyp 로 빌드 → .node 바이너리 생성 | ✅ OS·Node·컴파일러·라이브러리 모두 영향받음 |
즉, 순수 JS 모듈은 어디서 설치해도 동일하지만,
네이티브 모듈은 빌드된 결과(.node)가 OS·CPU·Node 버전에 따라 다릅니다.
js 파일을 실행시키는 것은 문제가 안되지만, C/C++로 작성된 확장 모듈 (ex. glibc, OpenSSL, GCC, python) 은 OS와 런타임의 ABI에 직접 의존하기 때문에 로드하는 Node 프로세스는 순간 오류가 나거나 죽을 수 있다.
npm install 시 이 모듈들은 c++로 빌드가 된다.
즉, npm install을 어느 OS에서 진행하냐에 따라 애드온의 결과가 달라진다.
다만 모든 패키지가 그런 것은 아니고 'C/C++로 빌드가 필요한 모듈'만 OS에 영향을 받는다.
대표적인 네이티브 애드온
Node 프로젝트에는 **C/C++로 작성된 확장 모듈(네이티브 애드온)**이 종종 들어있습니다.
대표적인 예가 아래입니다.
| 모듈명 | 설명 | 특징 |
| node-sass | SCSS를 C++로 컴파일 | libsass.so 등 네이티브 코드 |
| bcrypt | 암호화 알고리즘 구현 | OpenSSL, glibc 의존 |
| canvas, sqlite3, fsevents 등 | 시스템 라이브러리 직접 호출 | OS/컴파일러와 강하게 묶임 |
빌드가 OS별로 달라지는 이유
(1) node-gyp 빌드는 OS의 “컴파일러 + 시스템 라이브러리”를 사용
- Linux → gcc, glibc, libstdc++, OpenSSL
- macOS → clang, libc++, CommonCrypto
- Windows → MSVC, msvcrt.dll
→ 각 플랫폼마다 ABI(Application Binary Interface) 와 심볼 이름이 다름.
따라서 동일한 C++ 코드라도 다른 바이너리 형태로 컴파일됩니다.
예:
파일 이름은 같지만 내부 구조가 완전히 다름.
(2) Node 버전에 따라 ABI 버전(NODE_MODULE_VERSION)이 다름
Node 10 = ABI 64, Node 12 = ABI 72, Node 16 = ABI 93 …
→ Node10에서 빌드한 모듈은 Node16에서 로드 불가.
에러 예시:
(3) OS 버전(glibc 차이)에 따라 심볼 링크 달라짐
- RHEL7 glibc 2.17
- RHEL9 glibc 2.34
→ RHEL9에서 빌드하면 GLIBC_2.28 이상 심볼 사용 → RHEL7에서는 “not found” 오류 발생
예시: bcrypt 설치 결과 비교
| RHEL7 + Node10 | bcrypt_lib.node (ELF, glibc 2.17) | 낮은 glibc 심볼 |
| RHEL9 + Node10 | bcrypt_lib.node (ELF, glibc 2.34) | 높은 glibc 심볼 |
| Windows + Node10 | bcrypt_lib.node (DLL, PE/COFF) | msvcrt 의존 |
→ 모양은 같지만 내부 코드 완전히 다름.
따라서 한 OS에서 만든 node_modules를 다른 OS에 옮기면 작동이 안 될 수 있음.
확인 방법
(1) 네이티브 모듈 찾기
(2) 빌드 환경 확인
OS별 빌드 차이를 피하는 방법
| 전략 | 설명 | 비고 |
| ① 사전 빌드(prebuild) | 패키지 제작자가 OS별로 미리 빌드해 배포 (prebuild-install 사용) | 가장 안전. 단, 폐쇄망에서는 다운로드 불가 가능 |
| ② 동일 OS 환경에서 빌드 | 운영환경(RHEL9 등)과 동일한 OS 버전에서 npm install 실행 | 내부망 배포 시 재현성↑ |
| ③ Docker / CI 빌드 이미지 고정 | node:10-buster 등 고정 이미지로 항상 동일 glibc에서 빌드 | RHEL9 서버에서도 호환 |
| ④ JS-only 대체 사용 | bcrypt → bcryptjs, node-sass → sass | 빌드 필요 없음 |