Rust 첫 프로젝트: Node.js CLI 도구를 Rust로 재작성하기
배경
레거시 시스템에서 생성된 대용량 로그 파일(GB 단위)을 파싱해 JSON으로 변환하는 CLI 도구를 Node.js로 만들어 사용 중이었다. 파일 크기가 커지면서 메모리 사용량과 처리 시간이 문제가 되었고, Rust로 재작성을 결정했다.
첫 Rust 경험
공식 문서 The Book을 읽으며 시작했다. 소유권(Ownership) 개념이 가장 어려웠다.
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1은 더 이상 유효하지 않음
// println!("{}", s1); // 컴파일 에러
}
컴파일러가 친절하게 에러를 설명해주지만, JavaScript에서 넘어온 입장에서는 모든 것이 낯설었다.
구현 과정
기존 Node.js 버전은 스트림으로 파일을 읽었지만, 복잡한 파싱 로직 때문에 버퍼에 쌓이는 문제가 있었다.
Rust에서는 BufReader를 사용해 라인별 처리를 구현했다.
use std::fs::File;
use std::io::{BufRead, BufReader};
fn parse_log_file(path: &str) -> Result<Vec<LogEntry>, Box<dyn std::error::Error>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut entries = Vec::new();
for line in reader.lines() {
let line = line?;
if let Some(entry) = parse_line(&line) {
entries.push(entry);
}
}
Ok(entries)
}
결과
2GB 로그 파일 기준:
- Node.js: 약 45초, 메모리 ~1.2GB
- Rust: 약 4초, 메모리 ~80MB
성능 향상도 좋았지만, 컴파일 타임에 메모리 안전성이 보장된다는 점이 인상적이었다.
배운 점
- 러닝 커브가 가파르지만 컴파일러가 많은 것을 가르쳐준다
- 에러 핸들링이 타입 시스템에 통합되어 있어 안전하다
- 성능이 중요한 도구 제작에는 확실히 좋은 선택지다
당분간은 웹 개발에 집중하겠지만, CLI 도구나 시스템 프로그래밍이 필요할 때 Rust를 계속 활용할 예정이다.