๐ Nodejs์์ C++ ์ ๋์จ(Native Addons)์ ์ฌ์ฉํ๋ ์ด์ ๋ Node.js์ย ์ฑ๋ฅ ์ ํ์ ๊ทน๋ณตํ๊ณ ,ย ๊ธฐ์กด์ C++ ์ํ๊ณ์ ์์์ ํ์ฉํ๊ธฐ ์ํจ์
๋๋ค.
The reason for using C++ addons (Native Addons) in nodejs is to overcome the performance limitations of Node.js and to leverage the existing C++ ecosystem and resources.
๐ ์๋๋ ๋งฅos์์ ์คํ๋์์ต๋๋ค.
Below was run on macOS.
โ๏ธ์ฝ๋์์ฑ / Write code
1)server.js
// server.js
const express = require("express");
const app = express();
const port = 3000;
// 1. C++ ์ ๋์จ ๋ก๋ / Loading C++ add-ons
// node-gyp ๋น๋ ํ ์์ฑ๋๋ .node ํ์ผ์ ๊ฒฝ๋ก๋ฅผ ์ง์ ํฉ๋๋ค.
// Specifies the path to the .node file generated after building node-gyp.
// (๋น๋๊ฐ ์ฑ๊ณตํ๋ฉด build/Release/addon_module.node ํ์ผ์ด ์์ฑ๋ฉ๋๋ค.)
// (If the build is successful, the build/Release/addon_module.node file will be created.)
const cppAddon = require("./build/Release/addon_module");
app.use(express.json());
// 2. C++ ์ ๋์จ์ ์ฌ์ฉํ๋ API ์๋ํฌ์ธํธ ์ ์
// Defining API endpoints using C++ add-ons
app.get("/calculate", (req, res) => {
// ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ์์ ๋ ์ซ์๋ฅผ ์ถ์ถ
// Extract two numbers from query parameters
const num1 = parseFloat(req.query.a);
const num2 = parseFloat(req.query.b);
if (isNaN(num1) || isNaN(num2)) {
return (
res
.status(400)
//Please enter valid numbers for query parameters 'a' and 'b'.
.send("์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ 'a'์ 'b'์ ์ ํจํ ์ซ์๋ฅผ ์
๋ ฅํ์ธ์.")
);
}
try {
// 3. C++ ์ ๋์จ์ ํจ์ ํธ์ถ / Calling functions in C++ add-ons
const result = cppAddon.multiply(num1, num2);
// 4. ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ต / Reply the results to the client
res.json({
input_a: num1,
input_b: num2,
source: "C++ Addon",
result: result,
});
} catch (error) {
console.error("C++ add-on call error:", error.message);
res.status(500).send("Server internal error occurred");
}
});
app.listen(port, () => {
console.log(`Node.js server is running at http://localhost:${port} .`);
console.log(`Test URL: http://localhost:${port}/calculate?a=123&b=4.5`);
});
2) addon.cpp
// addon.cpp
/*
* ์ด ์ฝ๋๋ "ํ์ฌ Node.js ํ๊ฒฝ(env)์์ C++ ๋ณ์ result๋ฅผ ์ด์ฉํ์ฌ ์๋ก์ด JavaScript ์ซ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฐํํ๋ผ"๋ ์๋ฏธ๊ฐ ๋ฉ๋๋ค.
* This code means "Create a new JavaScript numeric object
* using the C++ variable result in the current Node.js environment (env) and return it."
*
* C++ ์ ๋์จ์ mainํจ์๊ฐ ์๋ ์ด์ :Node.js ์์ฒด์ ์ด๋ฏธ ๊ฑฐ๋ํ main ํจ์๊ฐ ํฌํจ๋์ด ์๊ธฐ ๋๋ฌธ์
๋๋ค.
* Why C++ addons don't have a main function: Because Node.js itself already contains a huge main function.
*
* Napi::Number๋ node-addon-api ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ C++ ํด๋์ค๋ก, Node.js ํ๊ฒฝ์์ ํต์ ํ ๋ JavaScript์ number ํ์
์ ๋ํ๋
๋๋ค.
* Napi::Number is a C++ class provided by the node-addon-api library that represents the JavaScript number type when communicating in a Node.js environment.
*
* Napi::Number ํด๋์ค๋ ๋ด๋ถ์ ์ผ๋ก C++์ ๊ธฐ๋ณธ ์ซ์ ํ์
์ JavaScript๊ฐ ์ดํดํ๋ ์ซ์ ๊ฐ์ฒด ํฌ๋งท์ผ๋ก ๋ณํํด ์ค๋๋ค.
* The Napi::Number class internally converts C++'s basic number type into a numeric object format that JavaScript understands.
*/
// C++ ๊ฐ๋ฐ์๊ฐ Node.js์ ๊ณ ์ฑ๋ฅ ๋ค์ดํฐ๋ธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ์ํ ํค๋
// Header for C++ developers to use high-performance native features of Node.js
#include <napi.h>
#include <iostream>
// JavaScript์์ ํธ์ถ๋ C++ ํจ์ ์ ์
// Define a C++ function to be called from JavaScript
Napi::Number Multiply(const Napi::CallbackInfo& info) {
// env: ํ์ฌ Node.js์ ์คํ ํ๊ฒฝ(Napi::Env)์ ๋ํ๋
๋๋ค.
// env: Represents the current Node.js execution environment (Napi::Env).
Napi::Env env = info.Env();
// ์ธ์ ๊ฐ์ ํ์ธ / Check number of arguments
if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber()) {
Napi::TypeError::New(env, "๋ ๊ฐ์ ์ซ์๋ฅผ ์
๋ ฅํด์ผ ํฉ๋๋ค.").ThrowAsJavaScriptException();
return env.Null().As<Napi::Number>();
}
// JavaScript ์ธ์๋ฅผ C++ ์ซ์๋ก ๋ณํ
// Convert JavaScript arguments to C++ numbers
double a = info[0].As<Napi::Number>().DoubleValue();
double b = info[1].As<Napi::Number>().DoubleValue();
// C++ ๋ก์ง ์คํ
// Execute C++ logic
double result = a * b;
std::cout << "C++ ์ ๋์จ์์ ๊ณฑ์
๋ก์ง ์คํ๋จ: " << a << " * " << b << std::endl;
// ๊ฒฐ๊ณผ๋ฅผ JavaScript Number ํ์
์ผ๋ก ๋ณํํ์ฌ ๋ฐํ
// Convert the result to JavaScript Number type and return it
// New()๋ ์๋ก์ด JavaScript ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฉ์๋
// New() is a method that creates a new JavaScript object.
return Napi::Number::New(env, result);
}
// ๋ชจ๋ ์ด๊ธฐํ (exports ๊ฐ์ฒด์ ํจ์ ๋ฑ๋ก)
// Initialize module (register functions in exports object)
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(
// ์๋ฐ์คํฌ๋ฆฝํธ์์ addon.multiply(a, b);๋ก ์ฌ์ฉ๋จ.
// Used in JavaScript as addon.multiply(a, b);
Napi::String::New(env, "multiply"),
// Functionํ์
์ ์๋ฐ์คํฌ๋ฆฝํธ์์ ํจ์๋ก ์ฌ์ฉ ํ ์ ์๊ฒ ํด์ค.
// The Function type allows you to use it as a function in JavaScript.
// ์ค์ ๋ก ์คํ๋ c++ ํจ์์
๋๋ค. / This is the c++ function that will actually be executed.
Napi::Function::New(env, Multiply)
);
return exports;
}
// Node.js ๋ชจ๋๋ก ๋ฑ๋ก
// Register as a Node.js module
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
3) binding.gyp
# GYP (Generate Your Projects): ์ด ํ์์ Google์์ ๊ฐ๋ฐ๋์์ผ๋ฉฐ,
# ์๋๋ Chromium ํ๋ก์ ํธ๋ฅผ ์ํด ์ฌ์ฉ๋์์ต๋๋ค.
# ์ด๋ ๋ค์ํ ํ๋ซํผ(macOS, Linux, Windows)์์ C++ ํ๋ก์ ํธ๋ฅผ ๋น๋ํ๊ธฐ ์ํ ๋น๋ ์์คํ
์ค์ ์ธ์ด์
๋๋ค.
# GYP (Generate Your Projects): This format was developed by Google and was originally used for the Chromium project.
# It is a build system configuration language for building C++ projects on various platforms (macOS, Linux, Windows).
# binding.gyp
{
"targets": [
{
# target_name : ์ต์ข
์ ์ผ๋ก ์์ฑ๋ .node ํ์ผ์ ์ด๋ฆ / The name of the .node file that will ultimately be created
# sources : ๋น๋ํ ์์ค ์ฝ๋ ํ์ผ ๋ชฉ๋ก์ ์ง์ ํฉ๋๋ค. / Specifies a list of source code files to build.
# include_dirs : C++ ์ฝ๋์์ #include <napi.h>๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ํฉ๋๋ค. / Enables you to use #include <napi.h> in your C++ code.
"target_name": "addon_module",
"sources": [ "addon.cpp" ],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")"
],
"defines": [ "NAPI_CPP_EXCEPTIONS" ],
# C++ ์ปดํ์ผ๋ฌ ํ๋๊ทธ ์ค์ : ์์ธ ์ฒ๋ฆฌ ํ์ฑํ / Setting C++ Compiler Flags: Enable Exception Handling
# -- C++์ ํต์ฌ ๊ธฐ๋ฅ์ธ try, catch, throw ๊ตฌ๋ฌธ์ด ์ปดํ์ผ๋์ด ๋ฐํ์์ ์ ๋๋ก ์๋ํ ์ ์๋๋ก ๋ง๋ญ๋๋ค.
# The try, catch, and throw statements, which are core features of C++, are compiled so that they work properly at runtime.
"cflags_cc": [
"-fexceptions"
],
# (macOS ์ ์ฉ) XCode/Darwin ๋น๋ ์ค์ ์ค๋ฒ๋ผ์ด๋
# (macOS only) Override XCode/Darwin build settings
# ์์ธ์ฒ๋ฆฌ ๊ธฐ๋ฅ์ด ๋นํ์ฑํ๋๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
# ์๋์ ์ฝ๋๋ ๋งฅos์ ์์ธ์ฒ๋ฆฌ ๊ธฐ๋ฅ์ ํ์ฑํํ๋ ์ฝ๋ ์
๋๋ค.
# An error will occur if exception handling is disabled.
# The code below enables exception handling in macOS.
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES', # C++ ์์ธ ํ์ฑํ
'OTHER_CPLUSPLUSFLAGS': [ '-fexceptions' ] # ์ถ๊ฐ ํ๋๊ทธ
},
# ์์ ์ ์ํด ๊ธฐ๋ณธ -fno-exceptions๋ฅผ ์ ๊ฑฐ (๋ง์ฝ node-gyp ๊ธฐ๋ณธ ์ค์ ์ ์๋ค๋ฉด)
# Remove default -fno-exceptions for safety (if it's in node-gyp default config)
# cflags! : C ์ปดํ์ผ๋ฌ์ ์์ธ ๋นํ์ฑํ ์ต์
์ ๊ฑฐ / Removed the option to disable exceptions from the C compiler.
# cflags_cc! : C++ ์ปดํ์ผ๋ฌ์ ์์ธ ๋นํ์ฑํ ์ต์
์ ๊ฑฐ / Removed the option to disable exceptions from the C++ compiler.
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ]
}
]
}
โ๏ธ ์์กด์ฑ ์ค์น / install dependencies
npm install node-gyp node-addon-api express --save
โ๏ธ C++ ์ ๋์จ ๋น๋ / C++ add-on build
npx node-gyp configure
npx node-gyp build
โ๏ธ ์๋ฒ์คํ / server run
node server.js
โ๏ธ ํฐ๋ฏธ๋์์ ํ ์คํธ / Test in terminal
curl "http://localhost:3000/calculate?a=1203&b=4.5"
โ๏ธ ๋ธ๋ผ์ฐ์ ํ ์คํธ ๊ฒฐ๊ณผ / Browser test results
{
"input_a": 123,
"input_b": 4.5,
"source": "C++ Addon",
"result": 553.5
}
โ๏ธ ์คํ / Run


โ๏ธ์ค์ ํ์ผ์ ์์ ํ ๊ฒฝ์ฐ / If you have modified the settings file
1)๋น๋ํ์ผ์ ๋ฆฌ / Build file organization
npx node-gyp clean
2) ๊ตฌ์ฑ ์ฌ์คํ / Rerun configuration
npx node-gyp configure
3) ๋น๋ ์ฌ์คํ / Rerun the build
npx node-gyp build