Rust로 Node.js Native Addon 작성하기
문제 상황
사용자 업로드 이미지를 여러 크기로 리사이징하는 API가 느렸다. Sharp 라이브러리를 사용했지만, 동시에 100개 이상의 요청이 들어오면 응답 시간이 3초를 넘었다.
Rust 선택 이유
- C/C++보다 메모리 안전성 보장
- neon 또는 napi-rs로 Node.js 바인딩 가능
- 이미지 처리 라이브러리(image crate) 성능 우수
구현
napi-rs를 사용해 Rust 함수를 Node.js에서 호출할 수 있도록 했다.
use image::imageops::FilterType;
use napi::bindgen_prelude::*;
use napi_derive::napi;
#[napi]
pub fn resize_image(input: Buffer, width: u32, height: u32) -> Result<Buffer> {
let img = image::load_from_memory(&input)
.map_err(|e| Error::from_reason(e.to_string()))?;
let resized = img.resize_exact(width, height, FilterType::Lanczos3);
let mut output = Vec::new();
resized.write_to(&mut std::io::Cursor::new(&mut output), image::ImageFormat::Jpeg)
.map_err(|e| Error::from_reason(e.to_string()))?;
Ok(output.into())
}
빌드 후 Node.js에서 사용:
const { resizeImage } = require('./index.node');
app.post('/resize', async (req, res) => {
const buffer = await resizeImage(req.file.buffer, 800, 600);
res.send(buffer);
});
결과
- 평균 응답 시간: 3.2초 → 0.8초
- CPU 사용률 40% 감소
- 메모리 누수 없음
배운 점
Rust의 학습 곡선은 가파르지만, 성능이 중요한 부분에 선택적으로 도입하면 효과적이다. 전체를 Rust로 재작성할 필요는 없고, 병목 지점만 개선해도 충분했다.