Rust로 Node.js Native Addon 만들어보기

배경

이미지 리사이징 서비스에서 대량의 썸네일을 생성하는 작업이 있었다. Sharp 라이브러리를 사용했지만, 특정 필터 처리 로직이 순수 JavaScript로 구현되어 있어 느렸다. C++로 작성할까 고민하다가 최근 관심있던 Rust를 시도해봤다.

Neon 설치

npm install -g neon-cli
neon new image-processor

Neon은 Rust로 Node.js Native Addon을 만들 수 있게 해주는 바인딩 라이브러리다. 기존 C++ Addon보다 메모리 안전성을 보장받을 수 있다는 점이 매력적이었다.

Rust 코드 작성

use neon::prelude::*;

fn process_pixels(mut cx: FunctionContext) -> JsResult<JsNumber> {
    let buffer = cx.argument::<JsBuffer>(0)?;
    let width = cx.argument::<JsNumber>(1)?.value() as usize;
    let height = cx.argument::<JsNumber>(2)?.value() as usize;
    
    let data = cx.borrow(&buffer, |data| {
        data.as_slice::<u8>()
    });
    
    // 픽셀 처리 로직
    let result = apply_filter(data, width, height);
    
    Ok(cx.number(result as f64))
}

register_module!(mut cx, {
    cx.export_function("processPixels", process_pixels)
});

벤치마크 결과

  • Pure JS: 1,240ms
  • Sharp (C++): 180ms
  • Rust Addon: 165ms

성능은 C++ 수준으로 나왔고, 코드는 훨씬 안전하게 작성할 수 있었다. 다만 빌드 시간이 길고, neon-cli의 성숙도가 아직 낮다는 점이 아쉬웠다.

소감

Rust 자체의 러닝커브는 예상보다 높았다. Ownership과 Lifetime 개념이 익숙해지는데 시간이 걸렸다. 프로덕션 적용은 조금 더 고민이 필요하지만, 성능이 중요한 부분에서는 충분히 고려할만한 선택지라고 판단했다.