10. 에러 처리 패턴 🟢
학습 목표:
- 라이브러리용
thiserror와 애플리케이션용anyhow를 구분하여 사용하는 법을 배웁니다.#[from]과.context()를 이용해 에러 변환 체인을 구축하는 기술을 익힙니다.?연산자의 내부 작동 방식과main()함수에서의 활용법을 이해합니다.- 예상된 에러(
Result)와 프로그래밍 버그(panic)를 명확히 구분합니다.
thiserror vs anyhow: 라이브러리냐 애플리케이션이냐
Rust의 에러 처리는 Result<T, E>를 중심으로 돌아갑니다. 상황에 따라 다음 두 크레이트 중 하나를 선택하세요.
thiserror(라이브러리용): 구체적인 에러 열거형을 정의할 때 사용합니다. 호출자가 에러 종류를match로 구분해야 하는 라이브러리에 적합합니다.anyhow(애플리케이션용): 에러의 구체적인 종류보다 "실패했다"는 사실과 원인 파악이 더 중요한 최종 실행 파일에 적합합니다..context()기능이 아주 강력합니다.
| 구분 | thiserror | anyhow |
|---|---|---|
| 권장 용도 | 공유 라이브러리, 크레이트 | 최종 바이너리, 애플리케이션 |
| 에러 타입 | 구체적인 열거형 (패턴 매칭 가능) | 불투명한 anyhow::Error |
| 장점 | 호출자가 정교한 에러 대응 가능 | 코드 작성이 빠르고 컨텍스트 추가 용이 |
에러 변환 체인 (#[from])
thiserror의 #[from] 속성을 사용하면 ? 연산자가 하위 에러를 상위 에러로 자동 변환해 줍니다.
#![allow(unused)] fn main() { #[derive(Error, Debug)] pub enum AppError { #[error("I/O 에러: {0}")] Io(#[from] std::io::Error), // io::Error를 AppError::Io로 자동 변환 #[error("JSON 에러: {0}")] Json(#[from] serde_json::Error), } }
컨텍스트과 에러 래핑 (anyhow)
anyhow는 에러가 발생한 지점의 상황을 설명하는 문구(Context)를 겹겹이 쌓아 올릴 수 있게 해줍니다.
#![allow(unused)] fn main() { let content = std::fs::read_to_string(path) .with_context(|| format!("{path} 파일을 읽는 데 실패했습니다"))?; }
출력 결과: "파일 읽기 실패" -> "원인: 권한 없음(OS Error)" 순서로 에러 원인들이 체인처럼 출력되어 디버깅이 매우 쉬워집니다.
? 연산자의 깊은 이해
?는 단순히 에러를 반환하는 것 이상의 일을 합니다. 내부적으로 From 트레이트를 호출하여 정의된 에러 타입으로 자동 변환까지 수행합니다.
#![allow(unused)] fn main() { // 이 코드는... let value = operation()?; // 내부적으로 다음과 같습니다: let value = match operation() { Ok(v) => v, Err(e) => return Err(From::from(e)), // 자동으로 에러 타입을 변환하여 반환 }; }
패닉(Panic) vs 에러(Result)
Result<T, E>: 파일 없음, 네트워크 타임아웃 등 충분히 발생할 수 있는 실패 상황에 사용하세요.panic!(): 인덱스 범위를 벗어남, 절대 일어나면 안 되는 논리적 모순 등 프로그래밍 버그를 나타낼 때만 사용하세요.catch_unwind: C/C++ 라이브러리와 통신(FFI)하거나 스레드 풀 경계에서 패닉이 프로그램 전체를 죽이지 않도록 격리할 때 사용합니다.
📝 연습 문제: thiserror를 활용한 에러 계층 설계 ★★ (~30분)
파일 처리 앱을 위한 에러 구조를 설계해 보세요. I/O 에러, 파싱 에러(JSON), 그리고 사용자 비즈니스 검증 에러가 포함되어야 합니다. ? 연산자가 어떻게 각 에러를 AppError로 변환하는지 코드로 구현해 보세요.
📌 요약
- 라이브러리는
thiserror, 애플리케이션은 **anyhow**를 쓰세요. - **
.context()**를 활용해 에러 발생 상황을 구체적으로 기록하세요. - **
?**는 에러 전파뿐 아니라 자동 타입 변환기이기도 합니다. - 패닉은 대처 가능한 에러 처리에 쓰는 도구가 아닙니다.