12-23 00:00
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

[Programming / Web App] Node.js의 Express Framework에서 RScript 실행하기 본문

Programming/Web App

[Programming / Web App] Node.js의 Express Framework에서 RScript 실행하기

cinema4dr12 2016. 9. 25. 21:02

by Geol Choi | 


이번 글에서는 Node.js의 Web Framework인 Express에서 R Script를 실행하여 결과를 출력하는 방법에 대하여 알아보도록 하겠다.

Data Flow의 개념은 다음 그림과 같다.



이를 구현하기 위하여 몇가지 기본 선행 지식이 필요하며, 아래 링크를 간단하게 파악하면 도움이 될 것이다.

Rscript 명령을 이용하여 Command Line에서 R 스크립트 파일 실행하기
Node.js에서 Command Line 명령 실행하기
Node.js Child Process

실행환경

본 튜토리얼의 설명을 위해 구성한 실행환경은 다음과 같으나, Mac OS 및 Linux 계열의 OS에서도 가능하며, R 및 Node.js의 버전은 이보다 낮은 버전에서도 실행 가능하다.

  • OS : Windows 7 64

  • Node.js : 6.6.0

  • R : ver.3.3.1 Bug in Your Hair


R Scripts 작성

Express 프로젝트 경로의 public/rscripts 경로 내에 다음 두 개의 R Script 파일들을 작성한다.

[{EXPRESS_PROJ_ROOT}/public/rscripts/log_a_b.R]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
log_a_b <- function(a, b) {
  if (b <= 0) {
    ret = "Error: b must be greater than 0";
    return(ret);
  }
 
  if (a <= 0) {
    ret = "Error: a must be greater than 0";
    return(ret);
  }
  else if (a == 1) {
    ret = "Error: a must not be 1";
    return(ret);
  }
 
  ret <- log10(b) / log10(a);
  return(ret);
}
cs


[{EXPRESS_PROJ_ROOT}/public/rscripts/log_wrapper.R]

1
2
3
4
5
6
7
8
args <- commandArgs(TRUE)
<- as.double(args[1])
<- as.double(args[2])
 
source("./public/rscripts/log_a_b.R")
 
val <- log_a_b(a, b)
cat(val)
cs


Client 페이지 작성

본 튜토리얼은 Node.js 서버와 R Script 실행을 통해 \(\mathrm{log}_{a}{b}\)를 계산하는 것이므로, 두 개의 입력 파라미터들(a, b)가 필요하므로, Client 페이지는 이 두 개의 입력 파라미터를 받을 수 있도록 작성하였다.

Express View Template Engine을 Pug를 사용하였으므로 이에 대하여 사전 지식이 필요하다.
Express Template Engine / Pug

다음과 같이 {EXPRESS_PROJ_ROOT}/views/logarithm.pug 파일을 작성한다.

[{EXPRESS_PROJ_ROOT}/views/logarithm.pug]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
html
    head
        title #{title}
    body
        form(action="/rscript", method="POST")
            div
                h2 Calculate Log_a_b
            div
                label(for="a") a : &nbsp
                input(name="a" type="number")
            br
            div
                label(for="b") b : &nbsp
                input(name="b" type="number")
            br
            button(type="submit") Calculate
cs

코드에서 보는 바와 같이, "/rscript" POST HTTP Method의 요청을 통해 a, b 두 개의 입력 파라미터 값을 전달하는 것을 알 수 있다.

작성된 페이지의 모양은 다음과 같다.

Router JavaScript 파일 작성

"/rscript"의 GET 및 POST HTTP Method를 통한 요청을 처리할 수 있도록 Router JavaScript 파일을 작성한다.

[{EXPRESS_PROJ_ROOT}/routes/rscripts.js]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
'use strict';
 
var express = require('express');
var router = express.Router();
 
var child_process = require('child_process');
var exec = child_process.exec;
 
/* GET : "/rscript" */
router.get('/', (req, res) => {
 
    res.render( 'logarithm', {
      title: 'Calculate Logarithm'
    } );
 
} );
 
/* POST : "/rscript" */
router.post('/', (req, res) => {
 
    var a = req.body.a;
    var b = req.body.b;
 
    var cmd = 'Rscript ./public/rscripts/log_wrapper.R ' + a.toString() + " " + b.toString();
 
    exec(cmd, (error, stdout, stderr) => {
        if(error) {
            console.error(error);
            return;
        }
 
        res.send("<h2>Log<sub>" + a.toString() + "</sub>" + b.toString() + " = " + stdout + "</h2>");
    } );
 
} );
 
module.exports = router;
cs


POST Method 요청을 처리하는 코드를 살펴보도록 하자.
Client 페이지로부터 POST Method를 통해 전달된 파라미터는 req.body라는 Payload에 실려 전달된다.
전달받은 값들은 ab 변수에 저장한다.

1
2
var a = req.body.a;
var b = req.body.b;
cs


다음 코드는 Command Line을 통해 실행할 명령을 정의한다.

1
var cmd = 'Rscript ./public/rscripts/log_wrapper.R ' + a.toString() + " " + b.toString();
cs

  
Command Line 명령을 통해 실행된 R Script 결과는 stdout에 저장된다. 이 값을 이용하여 Client 측으로 전달한다.

1
2
3
4
5
6
7
8
exec(cmd, function(error, stdout, stderr) {
    if (error == null) {
        res.send("Log_" + a.toString() + "_" + b.toString() + " = " + stdout);
    }
    else {
        res.send(error);
    }
} );
cs


app.js에 코드 추가

이제 app.js에 router를 추가한다.

[{EXPRESS_PROJ_ROOT}/views/app.js]

1
2
3
4
5
6
7
var routes = require('./routes/index');
var rscript = require('./routes/rscript');
 
...
 
app.use('/', routes);
app.use('/rscript', rscript);
cs


결과 확인

\(\mathrm{log}_{3}{2}\)를 계산해 본다.

결과 페이지는 다음과 같다.

Comments