pkg는 zeit에서 만든 Node.js 바이너리 컴파일러이다. 보통 Node.js로 애플리케이션을 만들면 실행 머신에 애플리케이션에 맞는 Node.js가 설치되어 있어야 하고 npm installl
로 관련 모듈을 설치하고 npm start
나 다른 실행방식으로 애플리케이션을 실행하거나 사용해야 한다. 사용자가 다 Node.js 개발자라면 큰 문제가 아니지만 타 언어 개발자나 일반 사용자를 생각하면 편한 환경은 아니라고 할 수 있다. pkg
가 Node.js 컴파일러라는 의미는 Node.js까지 내장해서 하나의 실행 파일로 다른 의존성 없이 실행할 수 있게 만들어 준다.
이런 방식이 편하다고 느끼기 시작한 건 Go 언어로 만들어진 프로그램을 사용하면서부터다. 나 같은 경우는 HashiCorp에서 만든 Vault, Consul, Terraform 등을 사용하면 이런 방식이 편하다고 생각하게 되었다. 내 맥북에 Go 언어 환경이 없어도 바이너리 파일 하나만 다운로드 받아서 실행하면 되었고 하나의 파일로 서버와 클라이언트를 모두 사용할 수 있어서 꽤 편했다. 보통 다른 언어의 프로그램을 다운받아서 사용하려면 한두 번씩은 의존성 문제나 버전이 일치하지 않는 문제로 고생했던 터라 더 편하게 느껴진 것 같다.
pkg를 위한 환경 구성
pkg를 사용하기 위한 간단한 예제를 만들어 보자. 애플리케이션의 종류는 여기서 크게 중요하지 않으므로 Express.js의 제너레이터를 사용해서 기본 Express 앱을 만들어 보자.
바이너리를 만들기 전에 의존성을 설치되어 있어야 하므로 npm install
로 의존성을 설치한다.
pkg
를 사용하기 위해 npm install --save-dev pkg
로 설치한다. pkg
는 빌드용 모듈이므로 devDependencies
로 추가했다.
pkg
명령어를 사용하려고 package.json
의 스크립트로 추가했다. 이렇게 하면 npm run build
로 pkg .
명령어를 실행할 수 있다.(요즘은 npm으로 전역 모듈은 거의 설치하지 않는 편이다) 그리고 pkg
가 바이너리를 만들기 위한 엔트리 포인트를 지정해야 하는데 bin
프로퍼티를 이용해서 지정한다. 여기서 express
애플리케이션의 실행하는 파일, 즉 시작 파일이 ./bin/www
이므로 이 파일을 지정했다. 이 시작 파일을 기준으로 pkg
가 require()
를 추적하고 필요한 파일을 찾아서 컴파일한다.
추가로 Express 예제 같은 경우 템플릿으로 Jade를 사용하고 있다. 이 뷰 엔진 같은 경우는 require()
로 불러오는 것이 아니라 아래와 같이 사용하게 된다.
그래서 pkg
가 이 views
디렉터리를 자동으로 바이너리 파일에 포함하지 않는다. 이런 경우 바이너리 파일에 넣어줄 파일(다른 수정 없이)을 직접 지정해 주어야 하는데 package.json
에 다음과 같이 지정할 수 있다.
pkg로 바이너리 생성하기
기본 타겟 플랫폼인 Linux와 macOS, Windows를 위한 바이너리가 생성되었다.
위처럼 생성된다. 기본 express 예제는 포함된 의존성이 많지 않음에도 Node.js가 바이너리 안에 포함했기 때문에 용량이 적지는 않다. 여기서 example
이라는 이름은 package.json
의 name
에서 온 것이다.
실제 이 파일이 Linux에서 다른 의존성 없이도 실행될 수 있는지 확인해 보자. Docker로 Ubuntu에서 실행해 보려고 Dockerfile
을 다음과 같이 만들었다.
이 도커 이미지를 만들어서 실행하면 다음과 같이 잘 실행되는 것을 알 수 있다.
당연히 여기서는 ubuntu:18.04
도커 이미지를 사용했으므로 Node.js나 다른 환경은 전혀 설정되어 있지 않으므로 pkg
로 컴파일한 바이너리 하나만으로 Express 애플리케이션을 실행할 수 있는 걸 알 수 있다.(사실 Windows에서는 직접 테스트해보지 않았다.)
pkg 옵션
위에서는 pkg .
로 컴파일할 위치만 지정했지만 몇 가지 옵션을 지정할 수 있다.
위처럼 --out-path
를 지정하면 dist
디렉토리 하에 바이너리가 생성된다.
타겟 플랫폼을 지정하고 싶다면 --targets
옵션을 사용하면 된다. 타겟 플랫폼의 지정 방식은 node8-linux-x64
처럼 node${n}
로 Node.js 버전, freebsd
, linux
, macos
, win
의 플랫폼 이름, x64
, x86
, armv6
, armv7
로 아키텍처를 지정해 주면 된다.
직접 서버에서 실행하는 경우에는 이렇게 컴파일하는 것에 큰 이점이 없지만 배포하는 프로그램의 경우에는 사용자가 쉽게 사용할 수 있어서 충분히 장점이 된다고 생각한다. Node.js가 포함되어 용량이 큰 것은 좀 문제이지만 상황에 따라서는 그 부분을 무시할 수 있는 이점을 준다고 본다.
감사합니다~