-
-
Notifications
You must be signed in to change notification settings - Fork 0
시작하기
이 페이지에서는 masterror 설치, 첫 AppError 반환, ensure!/fail!을 통한 단락 처리, 프렐루드 사용, 첫 파생 작성을 차례로 살펴봅니다.
기본 빌드는 std 기능만 활성화합니다 — 웹 프레임워크도, 텔레메트리 백엔드도 포함되지 않습니다:
[dependencies]
masterror = "0.28"필요한 통합을 그때그때 활성화하세요 (전체 목록은 기능 플래그 참조):
[dependencies]
masterror = { version = "0.28", features = ["axum", "serde_json", "tracing"] }MSRV는 1.96입니다. 이 크레이트는 unsafe를 금지하며 default-features = false로 빌드하면 no_std를 지원합니다.
AppError는 의미론적 범주(AppErrorKind)와 선택적인 공개 메시지를 결합합니다. AppResult<T>는 Result<T, AppError>의 별칭입니다:
use masterror::{AppError, AppErrorKind, AppResult};
fn do_work(flag: bool) -> AppResult<()> {
if !flag {
return Err(AppError::new(AppErrorKind::BadRequest, "Flag must be set"));
}
Ok(())
}
let err = do_work(false).unwrap_err();
assert!(matches!(err.kind, AppErrorKind::BadRequest));
assert_eq!(err.kind.http_status(), 400);모든 종류에는 이름 있는 생성자가 있으므로 호출 지점에서 AppErrorKind를 직접 쓸 일은 거의 없습니다:
use masterror::AppError;
let _ = AppError::not_found("user not found"); // 404
let _ = AppError::validation("invalid email"); // 422
let _ = AppError::unauthorized("token expired"); // 401
let _ = AppError::forbidden("no access"); // 403
let _ = AppError::conflict("already exists"); // 409
let _ = AppError::rate_limited("slow down"); // 429
let _ = AppError::internal("unexpected failure"); // 500
let _ = AppError::service("orchestration failed"); // 500
let _ = AppError::timeout("upstream timed out"); // 504
let _ = AppError::bare(masterror::AppErrorKind::NotFound); // no message타입을 포기하지 않고도 구조화된 메타데이터와 업스트림 소스를 첨부할 수 있습니다:
use masterror::{AppError, field};
let err = AppError::service("downstream degraded")
.with_field(field::str("request_id", "abc123"))
.with_field(field::i64("attempt", 2))
.with_context(std::io::Error::other("connection reset"));
assert_eq!(err.metadata().len(), 2);
assert!(err.source_ref().is_some());소스는 로그와 chain() 순회에서 사용할 수 있지만 절대 클라이언트에 직렬화되지 않습니다.
ensure!와 fail!은 anyhow::ensure!/anyhow::bail!의 타입 기반 대안입니다. 오류 표현식은 지연 평가되므로 성공 경로에서는 포매팅도 할당도 수행하지 않습니다:
use masterror::{AppError, AppErrorKind, AppResult};
fn guard(flag: bool) -> AppResult<()> {
masterror::ensure!(flag, AppError::bad_request("flag must be set"));
Ok(())
}
fn bail() -> AppResult<()> {
masterror::fail!(AppError::unauthorized("token expired"));
}
assert!(guard(true).is_ok());
assert!(matches!(guard(false).unwrap_err().kind, AppErrorKind::BadRequest));
assert!(matches!(bail().unwrap_err().kind, AppErrorKind::Unauthorized));ensure!는 복잡한 조건을 위한 상세 형식도 지원합니다:
use masterror::{AppError, AppResult};
fn bounded(value: i32, max: i32) -> AppResult<()> {
masterror::ensure!(
cond = value <= max,
else = AppError::service("value too large")
);
Ok(())
}masterror::prelude는 코어 타입만 재수출합니다 (AppError, AppErrorKind, AppCode, AppResult, ErrorResponse, 그리고 해당 기능이 켜져 있을 때의 turnkey 헬퍼):
use masterror::prelude::*;
fn handler(flag: bool) -> AppResult<()> {
if !flag {
return Err(AppError::bad_request("Flag must be set"));
}
Ok(())
}프레임워크 트레이트 구현(Axum IntoResponse, Actix Responder)은 기능 플래그로 활성화되며 별도의 임포트가 필요하지 않습니다.
ResultExt는 임의의 Result<T, E: Error>를 AppResult<T>로 승격합니다:
use masterror::{AppErrorKind, Context, ResultExt, field};
fn read_config() -> Result<String, std::io::Error> {
Err(std::io::Error::from(std::io::ErrorKind::NotFound))
}
// Simple, anyhow-style message:
let err = read_config().context("Failed to read config file").unwrap_err();
assert!(err.source_ref().is_some());
// Full control over category, code, metadata and redaction:
let err = read_config()
.ctx(|| Context::new(AppErrorKind::Config).with(field::str("path", "app.toml")))
.unwrap_err();
assert_eq!(err.kind, AppErrorKind::Config);전체 Context API는 컨텍스트와 메타데이터를 참조하세요.
#[derive(Error)]는 thiserror 구문을 미러링하고, #[app_error(...)]는 AppError로의 변환을 추가합니다:
use masterror::{AppCode, AppError, AppErrorKind, Error};
#[derive(Debug, Error)]
#[error("I/O failed: {source}")]
#[app_error(kind = AppErrorKind::Internal, code = AppCode::Internal, message)]
pub struct DomainError {
#[from]
#[source]
source: std::io::Error
}
fn load() -> Result<(), DomainError> {
Err(std::io::Error::other("disk offline").into())
}
let err = load().unwrap_err();
assert_eq!(err.to_string(), "I/O failed: disk offline");
let app: AppError = err.into();
assert!(matches!(app.kind, AppErrorKind::Internal));-
#[error("...")]는{field}플레이스홀더를 사용하는Display템플릿을 정의합니다. -
#[from]은 래퍼에 대한From<std::io::Error>를 생성합니다. -
#[source]는 내부 오류를source()를 통해 전달합니다. -
#[app_error(kind = ..., code = ..., message)]는From<DomainError> for AppError(그리고for AppCode)를 생성합니다.message플래그는Display출력을 공개 메시지로 노출합니다.
열거형도 변형별 #[error]와 #[app_error] 속성으로 동일하게 동작합니다. 메타데이터, 리덕션 정책, gRPC/problem+json 매핑 테이블까지 필요하다면 #[derive(Masterror)]를 사용하세요 — Derive 매크로에서 다룹니다.
- Axum 또는 Actix에서 오류를 HTTP 응답으로 매핑하기 — 웹 프레임워크
- 분류 체계와 와이어 계약 이해하기 — 오류 종류와 코드
- sqlx, redis, reqwest 통합 활성화하기 — 기능 플래그
함께 보기: 기능 플래그 · 오류 종류와 코드 · Derive 매크로 · 컨텍스트와 메타데이터 · 마이그레이션
🇬🇧 English | 🇷🇺 Русский | 🇰🇷 한국어
Getting Started
Core Concepts
Integrations
Advanced
Начало работы
Основы
Интеграции
Продвинутое
시작하기
핵심 개념
통합
고급