{ "source": "doc-ko/api/addons.markdown", "modules": [ { "textRaw": "Addons", "name": "addons", "desc": "
애드온은 동적으로 공유 객체를 연결합니다. 애드온은 C나 C++ 라이브러리에 연결할 수 있다.\nAPI는(현 시점에) 여러 가지 라이브러리에 대한 지식을 포함해서 상당히 복잡하다.\n\n
\nV8 자바스트립트, C++ 라이브러리. 자바스크립트와 인터페이스로 연결하는데 사용한다:\n객체를 생성하고 함수를 호출하는 등. v8.h
헤더파일(Node 소스트리의 \ndeps/v8/include/v8.h
)에 주로 문서화되어 있고 \nonline에서도 확인할 수 있다.
libuv, C 이벤트루프 라이브러리. 파일 디스크립터가\n읽을 수 있게 되기를 기다린다거나 타이머를 기다리거나 받아야할 신호를 기다려야 할 때는 \n언제든지 libuv와 인터페이스로 연결할 필요가 있을 것이다. 즉 어떤 I/O라도 수행한다면 \nlibuv를 사용해야 한다.
\n내부 Node 라이브러리. 가장 중요한 것은 node::ObjectWrap
클래스이다. 아마 \n이 클래스에서 파생하기를 원할 것이다.
그 밖에. 무엇을 사용할 수 있는지 알고 싶으면 deps/
를 봐라.
Node는 실행가능하도록 모든 의존성을 정적으로 컴파일한다. 모듈을 컴파일할 때 이러한 \n라이브러리의 연결에 대해서 걱정할 필요가 없다.\n\n\n
\n", "modules": [ { "textRaw": "Hello world", "name": "hello_world", "desc": "다음 자바스크립트 코드와 동일한 작은 애드온을 C++로 작성하면서 시작해 보자.\n\n
\nexports.hello = function() { return 'world'; };
\n우선 hello.cc
파일을 생성한다.\n\n
#include <node.h>\n#include <v8.h>\n\nusing namespace v8;\n\nHandle<Value> Method(const Arguments& args) {\n HandleScope scope;\n return scope.Close(String::New("world"));\n}\n\nvoid init(Handle<Object> target) {\n target->Set(String::NewSymbol("hello"),\n FunctionTemplate::New(Method)->GetFunction());\n}\nNODE_MODULE(hello, init)
\n모든 Node 애드온은 초기화 함수를 외부에 노출해야 한다.\n\n
\nvoid Initialize (Handle<Object> target);\nNODE_MODULE(module_name, Initialize)
\n함수가 아니므로 NODE_MODULE
뒤에 세미콜론이 없다.(node.h
를 봐라.)\n\n
module_name
은 최종 바이너리의 파일명과 일치시켜야 한다.(.node 접미사는 제외하고)\n\n
소스코드는 바이너리 애드온인 hello.node
로 빌드되어야 한다. 이를 위해서 \nJSON과 유사한 형식으로 모듈의 빌드 설정을 나타내는 binding.gyp
파일을 생성해야 한다.\n이 파일은 node-gyp가 컴파일한다.\n\n
{\n "targets": [\n {\n "target_name": "hello",\n "sources": [ "hello.cc" ]\n }\n ]\n}
\n다음 과정은 현재 플랫폼에 적절한 프로젝트 빌드 파일을 생성하는 것이다.\n빌드파일을 생성하기 위해서 node-gyp configure
를 사용해라.\n\n
이제 build/
디렉토리에 Makefile
(Unix 플랫폼)과 vcxproj
(Windows 플랫폼)가 \n있을 것이다. 그 다음 node-gyp build
명령어를 실행한다.\n\n
컴파일된 .node
바인딩 파일을 얻었다! 컴파일된 파인딩 파일들은 build/Release/
에 있다.\n\n
최근에 빌드한 hello.node
모듈을 require
함으로써 Node 프로젝트 hello.js
에서 바이너리 \n애드온을 사용할 수 있다.\n\n
var addon = require('./build/Release/hello');\n\nconsole.log(addon.hello()); // 'world'
\n더 많은 정보는 아래의 패턴들을 보거나 실사용 예제를 보려면 \n
\nhttps://github.com/pietern/hiredis-node를 봐라.\n\n\n
\n", "type": "module", "displayName": "Hello world" }, { "textRaw": "Addon patterns", "name": "addon_patterns", "desc": "다음은 애드온 개발을 시작할 때 도움이 될만한 애드폰 패턴들이다. 여러 가지 v8 호출에 대해서는 \n온라인 v8 reference를 참고하고 핸들, 범위, 함수 템플릿 \n등과 같이 사용된 여러가지 개념에 대한 설명은 v8의 \nEmbedder's Guide를 참고해라.\n\n
\n이 예제를 사용하려면 node-gyp
를 사용해서 컴파일해야 한다.\n다음과 같은 binding.gyp
파일을 생성해라.\n\n
{\n "targets": [\n {\n "target_name": "addon",\n "sources": [ "addon.cc" ]\n }\n ]\n}
\n하나 이상의 .cc
파일이 있는 경우에 sources
배열에 파일명을 다음과 같이 추가해라.\n\n
"sources": ["addon.cc", "myexample.cc"]
\nbinding.gyp
파일을 준비했으면 애드온을 설정하고 빌드할 수 있다.\n\n
$ node-gyp configure build
\n",
"modules": [
{
"textRaw": "Function arguments",
"name": "function_arguments",
"desc": "다음 패턴은 자바스크립트 함수 호출에서 어떻게 아규먼트들을 읽고 결과를 리턴하는 지 보여준다.\n다음 파일이 메인파일이고 소스파일인 addon.cc
만 필요하다.\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n\nusing namespace v8;\n\nHandle<Value> Add(const Arguments& args) {\n HandleScope scope;\n\n if (args.Length() < 2) {\n ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));\n return scope.Close(Undefined());\n }\n\n if (!args[0]->IsNumber() || !args[1]->IsNumber()) {\n ThrowException(Exception::TypeError(String::New("Wrong arguments")));\n return scope.Close(Undefined());\n }\n\n Local<Number> num = Number::New(args[0]->NumberValue() +\n args[1]->NumberValue());\n return scope.Close(num);\n}\n\nvoid Init(Handle<Object> target) {\n target->Set(String::NewSymbol("add"),\n FunctionTemplate::New(Add)->GetFunction());\n}\n\nNODE_MODULE(addon, Init)
\n다음 자바스크립트 코드로 이를 테스트할 수 있다:\n\n
\nvar addon = require('./build/Release/addon');\n\nconsole.log( 'This should be eight:', addon.add(3,5) );
\n",
"type": "module",
"displayName": "Function arguments"
},
{
"textRaw": "Callbacks",
"name": "callbacks",
"desc": "C++ 함수에 자바스크립트 함수를 전달해서 C++ 함수에서 자바스크립트 함수를 실행할 수 \n있다. 다음은 addon.cc
이다:\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n\nusing namespace v8;\n\nHandle<Value> RunCallback(const Arguments& args) {\n HandleScope scope;\n\n Local<Function> cb = Local<Function>::Cast(args[0]);\n const unsigned argc = 1;\n Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };\n cb->Call(Context::GetCurrent()->Global(), argc, argv);\n\n return scope.Close(Undefined());\n}\n\nvoid Init(Handle<Object> target) {\n target->Set(String::NewSymbol("runCallback"),\n FunctionTemplate::New(RunCallback)->GetFunction());\n}\n\nNODE_MODULE(addon, Init)
\n다음 자바스크립트 코드를 실행해서 이를 테스트 할 수 있다:\n\n
\nvar addon = require('./build/Release/addon');\n\naddon.runCallback(function(msg){\n console.log(msg); // 'hello world'\n});
\n",
"type": "module",
"displayName": "Callbacks"
},
{
"textRaw": "Object factory",
"name": "object_factory",
"desc": "createObject()
에 전달된 문자열을 출력하는 msg
프로퍼티를 가진 객체를 리턴하는 \n이 addon.cc
패턴과 함께 C++ 함수내에서 새로운 객체를 생성해서 리턴할 수 있다. \n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n\nusing namespace v8;\n\nHandle<Value> CreateObject(const Arguments& args) {\n HandleScope scope;\n\n Local<Object> obj = Object::New();\n obj->Set(String::NewSymbol("msg"), args[0]->ToString());\n\n return scope.Close(obj);\n}\n\nvoid Init(Handle<Object> target) {\n target->Set(String::NewSymbol("createObject"),\n FunctionTemplate::New(CreateObject)->GetFunction());\n}\n\nNODE_MODULE(addon, Init)
\n자바스크립트에서 다음과 같이 테스트한다:\n\n
\nvar addon = require('./build/Release/addon');\n\nvar obj1 = addon.createObject('hello');\nvar obj2 = addon.createObject('world');\nconsole.log(obj1.msg+' '+obj2.msg); // 'hello world'
\n",
"type": "module",
"displayName": "Object factory"
},
{
"textRaw": "Function factory",
"name": "function_factory",
"desc": "이 패턴은 C++ 함수를 감싸는 자바스크립트 함수를 어떻게 생성하고 리턴하는지 보여준다:\n\n
\n#define BUILDING_NODE_EXTENSION\n#include <node.h>\n\nusing namespace v8;\n\nHandle<Value> MyFunction(const Arguments& args) {\n HandleScope scope;\n return scope.Close(String::New("hello world"));\n}\n\nHandle<Value> CreateFunction(const Arguments& args) {\n HandleScope scope;\n\n Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);\n Local<Function> fn = tpl->GetFunction();\n fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous\n\n return scope.Close(fn);\n}\n\nvoid Init(Handle<Object> target) {\n target->Set(String::NewSymbol("createFunction"),\n FunctionTemplate::New(CreateFunction)->GetFunction());\n}\n\nNODE_MODULE(addon, Init)
\n다음과 같이 테스트한다:\n\n
\nvar addon = require('./build/Release/addon');\n\nvar fn = addon.createFunction();\nconsole.log(fn()); // 'hello world'
\n",
"type": "module",
"displayName": "Function factory"
},
{
"textRaw": "Wrapping C++ objects",
"name": "wrapping_c++_objects",
"desc": "new
오퍼레이터로 자바스크립트에서 인스턴스화할 수 있는 MyObject
C++ 객체/클래스에 대한\n랩퍼(wrapper)를 생성할 것이다. 우선 메인 모듈 addon.cc
를 준비하자.\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n#include "myobject.h"\n\nusing namespace v8;\n\nvoid InitAll(Handle<Object> target) {\n MyObject::Init(target);\n}\n\nNODE_MODULE(addon, InitAll)
\n그 다음 myobject.h
는 랩퍼가 node::ObjectWrap
를 상속받도록 한다:\n\n
#ifndef MYOBJECT_H\n#define MYOBJECT_H\n\n#include <node.h>\n\nclass MyObject : public node::ObjectWrap {\n public:\n static void Init(v8::Handle<v8::Object> target);\n\n private:\n MyObject();\n ~MyObject();\n\n static v8::Handle<v8::Value> New(const v8::Arguments& args);\n static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);\n double counter_;\n};\n\n#endif
\n그리고 myobject.cc
에서 노출할 다양한 메서드를 구현한다.\n여기서 생성자의 프로토타입에 추가해서 plusOne
메서드를 노출했다.\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n#include "myobject.h"\n\nusing namespace v8;\n\nMyObject::MyObject() {};\nMyObject::~MyObject() {};\n\nvoid MyObject::Init(Handle<Object> target) {\n // Prepare constructor template\n Local<FunctionTemplate> tpl = FunctionTemplate::New(New);\n tpl->SetClassName(String::NewSymbol("MyObject"));\n tpl->InstanceTemplate()->SetInternalFieldCount(1);\n // Prototype\n tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),\n FunctionTemplate::New(PlusOne)->GetFunction());\n\n Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction());\n target->Set(String::NewSymbol("MyObject"), constructor);\n}\n\nHandle<Value> MyObject::New(const Arguments& args) {\n HandleScope scope;\n\n MyObject* obj = new MyObject();\n obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();\n obj->Wrap(args.This());\n\n return args.This();\n}\n\nHandle<Value> MyObject::PlusOne(const Arguments& args) {\n HandleScope scope;\n\n MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());\n obj->counter_ += 1;\n\n return scope.Close(Number::New(obj->counter_));\n}
\n다음 코드로 테스트한다:\n\n
\nvar addon = require('./build/Release/addon');\n\nvar obj = new addon.MyObject(10);\nconsole.log( obj.plusOne() ); // 11\nconsole.log( obj.plusOne() ); // 12\nconsole.log( obj.plusOne() ); // 13
\n",
"type": "module",
"displayName": "Wrapping C++ objects"
},
{
"textRaw": "Factory of wrapped objects",
"name": "factory_of_wrapped_objects",
"desc": "이는 자바스크립트에서 new
오퍼레이터로 명시적인 인스턴스화 없이 네이티브 객체를 \n생성할 수 있도록 하고 싶을 때 유용하다.\n\n
var obj = addon.createObject();\n// 대신에:\n// var obj = new addon.Object();
\naddon.cc
에 createObject
메서드를 등록하자:\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n#include "myobject.h"\n\nusing namespace v8;\n\nHandle<Value> CreateObject(const Arguments& args) {\n HandleScope scope;\n return scope.Close(MyObject::NewInstance(args));\n}\n\nvoid InitAll(Handle<Object> target) {\n MyObject::Init();\n\n target->Set(String::NewSymbol("createObject"),\n FunctionTemplate::New(CreateObject)->GetFunction());\n}\n\nNODE_MODULE(addon, InitAll)
\nmyobject.h
에서 객체의 인스턴스화를 처리하는 정적 메서드 NewInstance
를 도입한다.\n(예를 들어 자바스크립트에서 new
가 하는 일이다.)\n\n
#define BUILDING_NODE_EXTENSION\n#ifndef MYOBJECT_H\n#define MYOBJECT_H\n\n#include <node.h>\n\nclass MyObject : public node::ObjectWrap {\n public:\n static void Init();\n static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);\n\n private:\n MyObject();\n ~MyObject();\n\n static v8::Persistent<v8::Function> constructor;\n static v8::Handle<v8::Value> New(const v8::Arguments& args);\n static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);\n double counter_;\n};\n\n#endif
\nmyobject.cc
에서 구현체는 위와 유사하다:\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n#include "myobject.h"\n\nusing namespace v8;\n\nMyObject::MyObject() {};\nMyObject::~MyObject() {};\n\nPersistent<Function> MyObject::constructor;\n\nvoid MyObject::Init() {\n // 생성자 템플릿 준비\n Local<FunctionTemplate> tpl = FunctionTemplate::New(New);\n tpl->SetClassName(String::NewSymbol("MyObject"));\n tpl->InstanceTemplate()->SetInternalFieldCount(1);\n // 프로토타입\n tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),\n FunctionTemplate::New(PlusOne)->GetFunction());\n\n constructor = Persistent<Function>::New(tpl->GetFunction());\n}\n\nHandle<Value> MyObject::New(const Arguments& args) {\n HandleScope scope;\n\n MyObject* obj = new MyObject();\n obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();\n obj->Wrap(args.This());\n\n return args.This();\n}\n\nHandle<Value> MyObject::NewInstance(const Arguments& args) {\n HandleScope scope;\n\n const unsigned argc = 1;\n Handle<Value> argv[argc] = { args[0] };\n Local<Object> instance = constructor->NewInstance(argc, argv);\n\n return scope.Close(instance);\n}\n\nHandle<Value> MyObject::PlusOne(const Arguments& args) {\n HandleScope scope;\n\n MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());\n obj->counter_ += 1;\n\n return scope.Close(Number::New(obj->counter_));\n}
\n다음으로 테스트한다:\n\n
\nvar addon = require('./build/Release/addon');\n\nvar obj = addon.createObject(10);\nconsole.log( obj.plusOne() ); // 11\nconsole.log( obj.plusOne() ); // 12\nconsole.log( obj.plusOne() ); // 13\n\nvar obj2 = addon.createObject(20);\nconsole.log( obj2.plusOne() ); // 21\nconsole.log( obj2.plusOne() ); // 22\nconsole.log( obj2.plusOne() ); // 23
\n",
"type": "module",
"displayName": "Factory of wrapped objects"
},
{
"textRaw": "Passing wrapped objects around",
"name": "passing_wrapped_objects_around",
"desc": "C++ 객체를 감싸고 리턴하는 부분에 대해서 추가적으로 Node의 node::ObjectWrap::Unwrap
\n헬퍼 함수로 이 객체들을 풀어줌으로써(unwrapping) 전달할 수 있다.\n다음 addon.cc
에서 두 MyObject
객체받을 수 있는 add()
함수를 도입한다.\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n#include "myobject.h"\n\nusing namespace v8;\n\nHandle<Value> CreateObject(const Arguments& args) {\n HandleScope scope;\n return scope.Close(MyObject::NewInstance(args));\n}\n\nHandle<Value> Add(const Arguments& args) {\n HandleScope scope;\n\n MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(\n args[0]->ToObject());\n MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(\n args[1]->ToObject());\n\n double sum = obj1->Val() + obj2->Val();\n return scope.Close(Number::New(sum));\n}\n\nvoid InitAll(Handle<Object> target) {\n MyObject::Init();\n\n target->Set(String::NewSymbol("createObject"),\n FunctionTemplate::New(CreateObject)->GetFunction());\n\n target->Set(String::NewSymbol("add"),\n FunctionTemplate::New(Add)->GetFunction());\n}\n\nNODE_MODULE(addon, InitAll)
\n흥미롭게 myobject.h
에서 퍼블릭 메서드를 도입해서 객체를 풀어버린(unwrapping) 후 \nprivate 값을 자세히 조사할 수 있다:\n\n
#define BUILDING_NODE_EXTENSION\n#ifndef MYOBJECT_H\n#define MYOBJECT_H\n\n#include <node.h>\n\nclass MyObject : public node::ObjectWrap {\n public:\n static void Init();\n static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);\n double Val() const { return val_; }\n\n private:\n MyObject();\n ~MyObject();\n\n static v8::Persistent<v8::Function> constructor;\n static v8::Handle<v8::Value> New(const v8::Arguments& args);\n double val_;\n};\n\n#endif
\nmyobject.cc
의 구현체는 이전과 유사하다:\n\n
#define BUILDING_NODE_EXTENSION\n#include <node.h>\n#include "myobject.h"\n\nusing namespace v8;\n\nMyObject::MyObject() {};\nMyObject::~MyObject() {};\n\nPersistent<Function> MyObject::constructor;\n\nvoid MyObject::Init() {\n // 생성자 템플릿 준비\n Local<FunctionTemplate> tpl = FunctionTemplate::New(New);\n tpl->SetClassName(String::NewSymbol("MyObject"));\n tpl->InstanceTemplate()->SetInternalFieldCount(1);\n\n constructor = Persistent<Function>::New(tpl->GetFunction());\n}\n\nHandle<Value> MyObject::New(const Arguments& args) {\n HandleScope scope;\n\n MyObject* obj = new MyObject();\n obj->val_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();\n obj->Wrap(args.This());\n\n return args.This();\n}\n\nHandle<Value> MyObject::NewInstance(const Arguments& args) {\n HandleScope scope;\n\n const unsigned argc = 1;\n Handle<Value> argv[argc] = { args[0] };\n Local<Object> instance = constructor->NewInstance(argc, argv);\n\n return scope.Close(instance);\n}
\n다음으로 테스트한다:\n\n
\nvar addon = require('./build/Release/addon');\n\nvar obj1 = addon.createObject(10);\nvar obj2 = addon.createObject(20);\nvar result = addon.add(obj1, obj2);\n\nconsole.log(result); // 30
\n",
"type": "module",
"displayName": "Passing wrapped objects around"
}
],
"type": "module",
"displayName": "Addon patterns"
}
],
"type": "module",
"displayName": "Addons"
}
]
}