ππ» 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.
1.μ½λμμ± / Write code
1.1 sersver.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`);
});
1.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)
1.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" ]
}
]
}
2.1.μμ‘΄μ± μ€μΉ / install dependencies
npm install node-gyp node-addon-api express --save
2.2.C++ μ λμ¨ λΉλ / C++ add-on build
npx node-gyp configure
npx node-gyp build
2.3.μλ²μ€ν / server run
node server.js
2.4 ν°λ―Έλμμ ν μ€νΈ / Test in terminal
curl http://localhost:3000/calculate?a=123&b=4.5
2.5 λΈλΌμ°μ ν μ€νΈ κ²°κ³Ό / Browser test results
{
"input_a": 123,
"input_b": 4.5,
"source": "C++ Addon",
"result": 553.5
}
3. μ€ν / Run


4.μ€μ νμΌμ μμ ν κ²½μ° / If you have modified the settings file
4.1 λΉλνμΌμ 리 / Build file organization
npx node-gyp clean
4.2 κ΅¬μ± μ¬μ€ν / Rerun configuration
npx node-gyp configure
4.3 λΉλ μ¬μ€ν / Rerun the build
npx node-gyp build