티스토리 뷰

Node.js의 명성과 장점은 굳이 제가 언급할 필요도 없을 것입니다.

획기적인 생산성을 제공하기로 유명한데,
그래도 저같은 C 개발자가 처음 Node.js 프로젝트에 합류했을 때에는 
여전히 일부 모듈만이라도 C로 개발하고 싶은 욕구가 마음 속 깊은 곳에서 부터 꿈틀꿈틀댑니다.

또는 내가 만든 C/C++ 라이브러리를 Javascript Language에도 binding해서 제공하고 싶다면 비슷한 욕구가 생길 것입니다.

Node.js에서 require로 load해 쓸 수 있는 이런 C/C++로 제작된 Shared Object를 Addon이라 부릅니다.


Addon을 처음 개발할 때, 간단히 따라할 수 있는 예제 코드와 node-gyp 라는 빌딩 툴이
Node.js 홈페이지에 매우 쉽게 정리되어 있습니다.

C++ Addons : https://nodejs.org/api/addons.html

node-gyp : https://github.com/nodejs/node-gyp


그러나, 저는 vi와 Makefile을 좋아하는 고전(?) 개발자이기 때문에 node-gyp 사용이 달갑지 않습니다.

다른 Library linking은 어떻게 하지?

기존 컴파일 방법을 유지하고 싶은데...

등과 같은 고민이 시작됩니다.


다른 Opensource들의 선행 사례를 참조하다 보니,
그냥 Makefile로 컴파일 하고도 Node.js에서 읽을 수 있는 Addon을 생성하는 것을 발견했습니다.

시행착오를 거쳐 알아낸 내용을 결론부터 말씀드리자면,

약속된 Function Symbol이 존재하는 Shared Library 파일이면 모두 Node.js의 Addon이 될 수 있습니다.


이는 흔히 우리가 Library를 dlopen 후 약속된 Symbol을 찾아 약속된 형식으로 Call하는 로직을
Node.js에서도 그대로 사용한 듯 싶습니다.

그러면 한번 위 Node.js 사이트에 소개된 예제코드를 약간 변형하면서 gcc로 빌드하는 방법을 보여드리겠습니다.


1. Node.js 설치

 저는 Node.js v5.12.0 을 이용했습니다.
일반적으로 Ubuntu에서 apt-get으로 nodejs 및 nodejs-dev를 설치할 경우,
LTS 버젼인 4.x 가 설치되는데 버젼 별로 헤더파일의 위치가 달라집니다.
본 예제를 따라하시려면 5.x를 설치하시기 바랍니다.

설치 방법 : https://github.com/nodesource/distributions#deb 에서
Node.js v5.x 항목 중 본인 OS에 맞는 설치 방법 실행


2. 코드 작성

#include <node/node.h> // 중요 1. header
#include <iostream>

namespace demo {

using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Number;
using v8::Value;

void Method1(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world"));
}

void Method2(const FunctionCallbackInfo<Value>& args) {
  if (args.Length() != 2) {
    std::cout<<"Invalid Parameters"<<std::endl;
    return;
  }
  if (args[0]->IsNumber() && args[1]->IsNumber()) {
    double value = args[0]->NumberValue() + args[1]->NumberValue();
    Isolate* isolate = args.GetIsolate();
    Local<Number> num = Number::New(isolate, value);
    args.GetReturnValue().Set(num);
  }
}

void init(Local<Object> exports) { // 중요 3. Method 정의
  NODE_SET_METHOD(exports, "hello", Method1);
  NODE_SET_METHOD(exports, "sum", Method2);
}

NODE_MODULE(addon, init) // 중요 2. 모듈 정의

}

 코드에서 중요한 부분은

  1) 첫 라인의 header 참조입니다. Node.js 5.x 의 경우 node/node.h를 참조하고 있습니다.
(물론 컴파일 인자에 따라 폴더명은 생략할 수도 있겠지요)

  2) 가장 하단부 입니다. NODE_MODULE 이라는 매크로를 반드시 사용해야 하고,
첫 란에 모듈 이름을, 두번째 란에 초기화 함수를 지정합니다.

  3) 위의 초기화 함수 안에서 NODE_SET_METHOD 매크로를 이용해, 위와 같이 원하는 만큼 method를 지정해 줍니다.

   첫 인자는 init()의 파라미터, 두번째 인자는 Node.js에서 호출할 때 쓸 Method명,
세번째 인자는 실제 C++ 코드에서 호출되는 Method명 입니다.


Method1은 그냥 "world"라는 String을 리턴하는 내용이고,

Method2는 두 파라미터 Number의 합을 리턴하는 내용입니다.


3. 컴파일 하기

g++ -std=c++11 --shared -fPIC -o test.node module.cc

  컴파일은 매우 단순합니다.
module.cc 라는 코드를 test.node 라는 이름의 shared library 로 컴파일 하였습니다.


4. 실행 해보기

$ node

> test = require('./test.node')

{ hello: [Function: hello], sum: [Function: sum] }

[ require() 로 모듈 로드 시 Method들 정보를 볼 수 있습니다. ]


> var str = test.hello()

undefined

> console.log('Str : ' + str)

Str : world

undefined

[ hello() method의 return 값을 str에 담은 뒤 console에 print해서 확인해 보았습니다. 'world'라는 string이 정상적으로 획득되었습니다. ]


> test.sum(1)

Invalid Parameters

undefined

[ sum() method에 parameter 갯수를 한개만 주었을 때. ]


> test.sum(1, 2,2)

Invalid Parameters

undefined

[ sum() method에 parameter 갯수를 세개 주었을 때. ]


> test.sum(1, 2.2)

3.2

[ sum() method에 parameter 갯수를 두개(정상) 주었을 떄. 더한 값 3이 화면에 출력 됨. ]

그 외, 파라미터 핸들링 등에 관한 내용은 V8의 Class들을 공부해야 합니다.

https://developers.google.com/v8

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함