[Rust 가이드] 12.8. 오류 메시지를 표준 오류에 쓰기

발행: (2026년 5월 24일 AM 07:19 GMT+9)
6 분 소요
원문: Dev.to

출처: Dev.to

12.8.0 시작하기 전에

12장에서는 샘플 프로젝트를 구축합니다: 명령줄 프로그램. 이 프로그램은 grep(Global Regular Expression Print)이며, 전역 정규식 검색 및 출력 도구입니다. 지정된 파일에서 지정된 텍스트를 검색하는 것이 목적입니다.
이 프로젝트는 다음 단계로 나뉩니다:

  • 명령줄 인수 받기
  • 파일 읽기
  • 리팩터링: 모듈 및 오류 처리 개선
  • TDD(테스트 주도 개발)를 이용한 라이브러리 기능 개발
  • 환경 변수 사용
  • 오류 메시지를 표준 출력이 아닌 표준 오류에 쓰기(본 글)

이 글이 도움이 되었다면 좋아요, 북마크, 팔로우를 눌러 주세요. 시리즈를 따라가며 계속 학습하시길 바랍니다.

아래는 이전 글까지 작성된 전체 코드입니다.

lib.rs

use std::error::Error;
use std::fs;

pub struct Config {
    pub query: String,
    pub filename: String,
    pub case_sensitive: bool,
}

impl Config {
    pub fn new(args: &[String]) -> Result {
        if args.len()  Result> {
    let contents = fs::read_to_string(config.filename)?;
    let results = if config.case_sensitive {
        search(&config.query, &contents)
    } else {
        search_case_insensitive(&config.query, &contents)
    };
    for line in results {
        println!("{}", line);
    }
    Ok(())
}

pub fn search(query: &str, contents: &'a str) -> Vec {
    let mut results = Vec::new();
    for line in contents.lines() {
        if line.contains(query) {
            results.push(line);
        }
    }
    results
}

pub fn search_case_insensitive(query: &str, contents: &'a str) -> Vec {
    let mut results = Vec::new();
    let query = query.to_lowercase();
    for line in contents.lines() {
        if line.to_lowercase().contains(&query) {
            results.push(line);
        }
    }
    results
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn case_sensitive() {
        let query = "duct";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";

        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
    }

    #[test]
    fn case_insensitive() {
        let query = "rUsT";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";

        assert_eq!(
            vec!["Rust:", "Trust me."],
            search_case_insensitive(query, contents)
        );
    }
}

main.rs

use std::env;
use std::process;
use minigrep::Config;

fn main() {
    let args: Vec = env::args().collect();
    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    if let Err(e) = minigrep::run(config) {
        println!("Application error: {}", e);
        process::exit(1);
    }
}

현재 코드는 오류 메시지를 포함한 모든 정보를 터미널에 출력합니다. 대부분의 터미널은 두 개의 출력 스트림을 제공합니다: 표준 출력(stdout)과 표준 오류(stderr).
일반적인 정보는 표준 출력으로, 오류 메시지는 표준 오류로 보내는 것이 좋습니다. 이렇게 하면 정상 출력은 파일로 리다이렉트하면서도 오류 메시지는 화면에 그대로 남게 됩니다.

println! 매크로는 표준 출력에만 쓸 수 있고, eprintln! 매크로는 표준 오류에 쓸 수 있습니다.

현재 코드를 그대로 사용하고 터미널에서 다음 명령을 실행해 보세요:

cargo run > output.txt

이 명령은 출력을 output.txt 파일로 리다이렉트합니다. 하지만 인수를 전달하지 않았기 때문에 프로그램은 오류를 발생시켜야 합니다. 오류 메시지도 표준 출력에 쓰이기 때문에 output.txt에 들어가 버립니다.

오류 메시지를 표준 오류에 출력하도록 바꾸면 표준 출력은 깨끗하게 유지할 수 있습니다. 변경은 매우 간단합니다. 오류를 출력하는 모든 println!eprintln!으로 바꾸면 됩니다. 오류 처리는 main.rs에만 있기 때문에 여기만 수정하면 되고, lib.rs는 그대로 두어도 됩니다:

use std::env;
use std::process;
use minigrep::Config;

fn main() {
    let args: Vec = env::args().collect();
    let config = Config::new(&args).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    if let Err(e) = minigrep::run(config) {
        eprintln!("Application error: {}", e);
        process::exit(1);
    }
}

다시 한 번 이전 명령을 실행해 보세요. 인수가 없으므로 프로그램은 오류를 내지만 이번에는 오류 메시지가 output.txt에 들어가지 않고 터미널에 바로 표시됩니다:

$ cargo run > output.txt
Problem parsing arguments: not enough arguments

그 다음 정상적인 인수를 넣고 실행해 보세요:

$ cargo run -- to poem.txt > output.txt

출력이 output.txt로 리다이렉트됩니다. 파일을 열어 보면:

Are you nobody, too?
How dreary to be somebody!

바라던 대로, 오류는 터미널에 직접 출력되고 정상 출력은 파일에 저장됩니다.

0 조회
Back to Blog

관련 글

더 보기 »

내 스킬

프로젝트를 위한 AI 지시문을 만들고, 설치하고, 관리하세요 — 코딩이 필요 없습니다. CREATE 이름을 정하고, 카테고리를 선택하고, 원하는 것을 설명하세요 — 마법사가 자동으로 구성합니다.