본문 바로가기

개발/javascript

Node.js 에서 Bash Script 출력(stdout, stderr) 문자열로 읽기 + 한글(EUC-KR) 인코딩

DC 코믹스의 Spawn 이라는 다크히어로. (이미지 클릭 - 나무위키 링크)

Spawn 함수가 오늘의 주인공이기에 모셔봤당 ㅎㅎ


Focus Point

매번 TeleditClient 를 포팅하는 작업은 "SClient 모듈 실행, 결과값(stdout) 받아오기"가 문제다.

jar, c 실행파일 2가지 선택지 중 골라 어떻게든 SClient를 호출 해야한다.

javascript 는 기본적으로 동기적 실행방식을 갖지만,

수많은 함수들이 비동기적으로 실행된다. (결과를 기다리지 않고 넘어간다)

 

Bash 스크립트를 실행시킬 수 있는 exec, spawn 함수 둘 다 실행 방식은 비동기적이다.

따라서 spawnSync, execSync 와 같은 함수들이 같이 제공된다.

 

child_process.spawn, spawnSync

const spawn = require("child_process").spawn;
const proc = spawn('sh', [
  '-c',
  'netstat -nat | grep ESTABLISHED'
]);

proc.stdout.on('data', (chunk) => {
  output += chunk.toString();
});

proc.stderr.on('data', (chunk) => {
  console.log(chunk.toString());
});

proc.on('error', (err) => {
  console.error(err);
});

proc.on('exit', (code) => {
  console.log(code);
  console.log(output);
});

 

output 변수에 출력 문자열이 담긴다.

spawn은 비동기 함수이다. 결과를 기다리지 않는다.

proc.on('exit', (code) => {

이 구문에서 한 프로세스가 끝나면서 할일을 하기 때문에 핵심적이라 할 수 있다.

이 구문을 통해 어느정도 동기처리가 가능하다.

하지만 exit 블록 안에 모든 이후 프로세스가 갇힌다는 제한적인 상황이 발생한다.

 

spawnSync 를 사용해 근본적인 동기 처리를 할 수 있다.

나는 Bash 스크립트 실행 결과 STDOUT 값을 읽어들여 다음 단계를 진행해야 하기 때문에

동기 처리가 꼭 필요했다.

마찬가지로 exec 에 상응하는 execSync 도 존재한다.

 

 

`spawn` vs `exec`

`spawn`과 `exec`가 하는 작업은 동일하지만, 
 
`spawn`은 스트림(stream)을 리턴하고, `exec`는 버퍼(buffer)를 리턴한다.
 
`spawn`은 `stdout`과 `stderr` 스트림을 포함한 객체를 리턴한다.
자식 프로세스의 표준 출력을 `stdout` 객체로 받아 처리할 수 있다.
`stdout` 객체는 `data`와 `end` 등의 이벤트를 발생한다.
`spawn`은 자식 프로세스로부터 많은 양의 데이터를 받는 경우에 유용한다.
예) 이미지 프로세싱이나 바이너리 데이터를 읽어오는 등
 
`exec`는 자식 프로세스 버퍼의 아웃풋을 리턴한다.
버퍼의 사이즈는 기본값은 200k이다.
만약, 자식 프로세스가 버퍼 사이즈보다 더 큰 값을 리턴하면,
"Error: maxBuffer exceeded" 오류가 나면서 프로그램이 종료될 것이다.
버퍼 사이즈를 크게 늘리면 해결할 수는 있으나,
`exec`는 큰 사이즈의 버퍼 처리를 위한 것이 아니다.
이런 경우라면 `spawn`을 사용하는 게 적합하다.
`exec`는 데이터 대신 상태 정도의 작은 결과를 출력하는 프로그램을 실행하는 용도로 사용한다.
 
또 하나,
`spawn`은 비동기로 실행하고, 결과도 비동기로 받는다.
`exec`는 동기로 실행하고, 결과는 비동기로 받는다.
 
정리: 자식 프로세스로부터 큰 바이너리 데이터를 리턴받는 경우라면 `spawn`을,
간단한 상태 메시지만 받는 것이라면 `exec`를 쓴다.
 
 

stdout 한글 인코딩 (EUC-KR)

EUC-KR 로 결과값을 주는 SClient 소켓통신 모듈의 인코딩 방식으로 인해,

기존 사용중인 iconv 라이브러리와 함께 사용가능한 spawn 함수를 썼다. (exec는 iconv로 인코딩 안됨)

let Input = TeleditBinPath+"/"+bin+" \""+arg+"\""
let Output = ""
let Ret = ""

const spawn = require("child_process").spawnSync;
const proc = spawn('sh', [
  '-c',
  Input
]);

Output = iconv.decode(proc.stdout, 'euc-kr').toString()
let MapOutput = Parsor(Output)
Debug ? console.log('MapOutput',MapOutput) : null
return MapOutput

 

EJS 형식

Node 에서 프론트를 제공하는 라이브러리가 EJS 다.

 

ejs 파일 내부에서는 html 과 함께

<%=Somevalue%> 형태로 변수를 전달,

server.js 에서는

app.get('/Success', (req, res) => {
	res.render("public/Success.ejs"), {
    	Somevalue : "someString",
        ...
    }
}

형태로 전달된다.

 

JSON 으로 변수 분리하기

원래 휴대폰결제는 여러 변수 ( 상품 금액, ID, PWD 등) 가 몇몇 파일에 산재되어있다.

이번 기회에 conf.json 으로 모두 빼내어,

앞으로 연동하는 가맹점은 conf.json 만 변경해주면 되도록 했다. (크어어 시원타 옛날부터 하고 싶었다)

yaml 과 json 을 고민했는데, yaml 은 비교적 최신 언어이며, 언어들 버전이 최신버전이 아니면 내장되지 않은 경우가 많고,

공백을 많이 사용하는 문법 특성상 직접 파싱코드를 만들기도 어려워 배제했다.

json 은 주석을 달 수가 없어서, "_comment" 변수로 코멘트를 넣었다.

 

Ref :
https://ohgyun.com/453
https://stackoverflow.com/questions/52864241/node-js-easiest-way-to-capture-stdout-into-string-variable

반응형