Rust로 간단한 CLI 도구 만들어보기
배경
사내에서 사용하는 로그 파싱 CLI 도구가 있었다. Node.js로 작성되어 있었는데, 실행 속도도 느리고 팀원들이 각자 node 환경을 설정해야 하는 불편함이 있었다.
Rust를 배운 김에 실전 프로젝트로 이 도구를 재작성해보기로 했다.
구현
기본적인 파일 읽기와 정규식 매칭 정도만 필요한 간단한 도구였다.
use std::fs::File;
use std::io::{BufRead, BufReader};
use regex::Regex;
fn parse_log(path: &str, pattern: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let re = Regex::new(pattern)?;
let mut results = Vec::new();
for line in reader.lines() {
let line = line?;
if re.is_match(&line) {
results.push(line);
}
}
Ok(results)
}
clap crate로 CLI 인터페이스를 구성했다. derive 매크로를 사용하니 보일러플레이트가 많이 줄어들었다.
use clap::Parser;
#[derive(Parser)]
struct Args {
#[clap(short, long)]
file: String,
#[clap(short, long)]
pattern: String,
}
결과
Node.js 버전 대비 실행 속도가 약 10배 빨라졌다. 500MB 로그 파일 처리 시간이 3초에서 0.3초로 단축됐다.
단일 바이너리로 배포되니 팀원들이 별도 설치 없이 바로 사용할 수 있게 됐다. GitHub Actions에서 크로스 컴파일로 Mac/Linux/Windows 바이너리를 자동 빌드하도록 설정했다.
느낀 점
소유권 시스템에 익숙해지는 데 시간이 걸렸다. 특히 문자열 처리할 때 &str와 String의 차이, lifetime 개념이 헷갈렸다.
하지만 컴파일만 되면 런타임 에러가 거의 없다는 점이 인상적이었다. 에러 핸들링도 Result 타입으로 명시적으로 처리하게 되어 있어서 견고한 코드를 작성하게 됐다.
간단한 CLI 도구 정도는 Rust로 작성하는 게 충분히 실용적이라는 결론을 내렸다.