Rust로 CLI 도구 만들어보며 배운 것들

배경

팀에서 사용하는 로그 파싱 스크립트가 Node.js로 작성되어 있는데, 파일 크기가 커지면서 속도 문제가 생겼다. 성능 개선이 필요한 시점에 Rust를 학습 삼아 시도해봤다.

구현 과정

기본적인 파일 읽기와 정규식 매칭 정도의 간단한 도구였다.

use std::fs::File;
use std::io::{BufRead, BufReader};
use regex::Regex;

fn parse_log(path: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let re = Regex::new(r"ERROR.*")?;
    
    let mut errors = Vec::new();
    for line in reader.lines() {
        let line = line?;
        if re.is_match(&line) {
            errors.push(line);
        }
    }
    
    Ok(errors)
}

소유권 시스템

처음엔 소유권 개념이 낯설어서 컴파일러 에러와 씨름했다. 특히 문자열을 여러 곳에서 참조하려 할 때 &strString의 차이를 이해하는 데 시간이 걸렸다. 하지만 일단 컴파일되면 런타임 에러가 거의 없다는 점은 인상적이었다.

성능

200MB 로그 파일 기준으로 Node.js 버전은 약 3.2초, Rust 버전은 0.4초 정도 걸렸다. 단순 비교지만 체감할 수 있는 차이였다.

에러 핸들링

Result<T, E> 타입과 ? 연산자를 사용한 에러 처리가 명시적이어서 좋았다. try-catch보다 어떤 에러가 발생할 수 있는지 타입 레벨에서 알 수 있었다.

배포

싱글 바이너리로 빌드되어 배포가 간단했다. Node.js처럼 런타임 설치가 필요 없어서 CI/CD 이미지 크기도 줄었다.

한계

학습 곡선이 가파르고, 간단한 스크립트 작성에는 과한 면이 있다. 팀원들이 수정하기 어려울 수 있어서 당장 프로덕션 코드로 전환하긴 망설여진다.

결론

성능이 중요한 도구나 CLI에는 Rust가 좋은 선택지라고 느꼈다. 당장 메인 언어로 쓰긴 어렵지만, 특정 영역에선 충분히 활용 가치가 있다.