Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

소스에서 pkg로 패키징되었는지 확인하기

pkg이전에도 소개한 적이 있는데 Node.js 프로그램을 바로 실행할 수 있는 하나의 바이너리로 만들어 주는 프로그램이다. 나는 CLI처럼 사용자도 Node.js가 설치되는 것을 가정할 수 없을 때 종종 사용하고 있다. Node.js까지 패키징되기 때문에 용량은 좀 크지만 그래도 얻을 수 있는 편의성이 있다고 생각한다.

pkg로 패키징할 프로그램을 작성하다 보니 소스 코드에서 pkg 내에서 실행되고 있는지 그냥 node로 실행되고 있는지 구분할 필요가 있었다. 배포하면 사용자는 pkg로 패키징된 프로그램을 쓰겠지만 개발할 때는 편의상 로컬에서 Node.js로 바로 코드를 실행하는데 (내 경우에서는) 로그를 다른 식으로 출력하고 싶었다. 개발할 때는 개발에 유리하게 로그를 남기지만 pkg된 후에는 사용자가 편한 로그를 남기고 싶었다.

그러다 보니 실행될 때 지금 패키징된 상태인지 구분할 필요가 있어서 방법을 찾아봤다.

간단한 예제 프로그램

일단 예제용 node.js 프로그램을 만들기 위해 cli.js 파일을 만들었다. 이 코드는 console.log()로 메시지만 출력한다.

// cli.js
console.log('This is CLI');

pkg에 실행파일의 엔트리 포인트를 알려줘야 하므로 package.jsonbin을 지정한다.

{
  "name": "pkg-example",
  "version": "1.0.0",
  "bin": "cli.js"
}

이 간단한 프로그램을 pkg로 패키징 해보자. 보통은 pkg를 의존성에 추가하고 npm script로 빌드 명령어를 추가하겠지만 여기선 간단하게 npxpkg를 실행했다.

$ npx pkg .
> pkg@5.8.0
> Targets not specified. Assuming:
  node16-linux-arm64, node16-macos-arm64, node16-win-arm64

따로 옵션을 주지 않았기 때문에 Linux, macOS, Windows용이 만들어졌다.(Apple M1을 쓰고 있어서 arm64로 빌드되었다) 아래처럼 pkg-example-linux, pkg-example-macos, pkg-example-win.exe 3개의 바이너리가 생성되었다.

├── cli.js
├── package.json
├── pkg-example-linux
├── pkg-example-macos
└── pkg-example-win.exe

macOS를 쓰고 있어서 macOS용 바이너리를 실행하면 앞에서 작성한 메시지가 출력되는 것을 볼 수 있다.

$ ./pkg-example-macos
This is CLI


pkg로 패키징되었는지 구분하기

구분할 수 있게 내부에 환경변수를 주입해 주려나 했는데 그렇지는 않았고 Node.js의 process를 확인하면 기존 Node.js로 실행했을 때와 다른 부분을 볼 수 있었다.

process {
  versions: {
    ...
    pkg: '5.8.0'
  },
  title: './pkg-example-macos',
  pkg: {
    mount: [Function: createMountpoint],
    entrypoint: '/snapshot/pkg-example/cli.js',
    defaultEntrypoint: '/snapshot/pkg-example/cli.js',
    path: { resolve: [Function: resolve] }
  },
  ...
}

process 객체에서 다른 부분만 적었다. process.versions.pkg에 버전이 들어가 있어서 사용된 pkg 버전을 알 수 있었고 process.title에 실행한 프로그램 이름이 들어 있었다. node cli.js로 실행한 경우에는 process.titlenode가 들어 있었다. 하지만 title은 사용자가 얼마든지 바꿀 수 있어서 신뢰할 수 없었다. process.pkg에는 pkg가 몇 가지 정보와 함수를 넣어주고 있다.

process.versions.pkg도 가능은 하겠지만 process.pkg가 더 확실하고 간결해서 process.pkg를 사용했다.

// cli.js
if (process.pkg) {
  console.log('Run as packaged');
} else {
  console.log('Run by Node.js');
}

예제라서 process.pkg로 분기했다.

$ node cli.js
Run by Node.js

$ ./pkg-example-macos
Run as packaged

패키징되었을 때와 아닐 때를 잘 구분해서 실행되는 것을 볼 수 있다.

2022/08/25 00:12 2022/08/25 00:12