masterror란 무엇인가요?
masterror는 Display와 source()만으로는 부족한 Rust 서비스를 위한 오류 처리 워크스페이스입니다. thiserror가 트레이트 구현 파생에서 멈추고 anyhow가 타입이 소거된 전파에서 멈추는 반면, masterror는 오류를 전송 경계까지 끝까지 운반합니다:
AppError— 의미론적 범주(AppErrorKind), 안정적인 기계 판독 가능 코드(AppCode), 선택적인 안전한 공개 메시지, 구조화된 메타데이터, 전송 힌트(Retry-After,WWW-Authenticate)를 갖춘 풍부한 오류 값입니다.- 보수적인 HTTP 및 gRPC 매핑 — 모든 종류와 코드는 HTTP 상태,
tonic::Code판별값, RFC 7807typeURI에 결정론적으로 매핑됩니다. - 타입 기반 텔레메트리 — 메타데이터는 임시방편의
String맵이 아니라 필드별 리덕션 정책을 갖춘 타입 기반 필드(문자열, 정수, 부동 소수점, 기간, IP, UUID, JSON)로 저장됩니다. - 네이티브 파생 —
#[derive(Error)]는thiserror구문을 미러링하며,#[app_error(...)]와#[derive(Masterror)]는 코드, 범주, 리덕션 및 매핑 테이블과 함께 도메인 오류를AppError에 연결합니다. - 설계 단계부터의 리덕션 — 소스는 절대 클라이언트에 직렬화되지 않으며, 메시지, 세부 정보 및 메타데이터 필드는 경계에서 리덕션, 해시 또는 마스킹될 수 있습니다.
unsafe 코드가 없고, MSRV가 고정되어 있으며, 기본 std 기능을 비활성화하면 no_std를 지원합니다.
해결하는 문제
| 관심사 | thiserror | anyhow | masterror |
|---|---|---|---|
Display / source() 파생 | 지원 | — | 지원 (동일한 구문) |
| 컨텍스트를 포함한 타입 소거 전파 | — | 지원 | 지원 (.ctx() / .context()) |
| 안정적인 기계 판독 가능 오류 코드 | 수동 | 수동 | AppCode, 와이어 계약의 일부 |
| HTTP 상태 매핑 | 수동 | 수동 | AppErrorKind::http_status(), 안정적인 테이블 |
| gRPC 상태 매핑 | 수동 | 수동 | CODE_MAPPINGS, tonic::Status 변환 |
RFC 7807 problem+json | 수동 | 수동 | ProblemJson::from_app_error |
| 구조화된 타입 기반 메타데이터 | — | — | Metadata + field::* 빌더 |
| 경계에서의 비밀 정보 리덕션 | — | — | MessageEditPolicy, FieldRedaction |
| tracing / metrics / backtrace 발행 | — | — | 기능 게이트, 생성 시 자동 |
thiserror로 파생한 열거형은 무슨 일이 일어났는지 알려줍니다. masterror는 여기에 더해 클라이언트가 무엇을 보는지(상태, 코드, 안전한 메시지, problem+json), 운영자가 무엇을 보는지(구조화된 필드, tracing 이벤트, 카운터), 무엇이 절대 유출되지 않는지(소스, 리덕션된 필드)까지 결정합니다.
주요 기능
| 영역 | 제공 내용 |
|---|---|
| 코어 분류 체계 | AppError, AppErrorKind (23개의 안정적인 범주), AppCode (SCREAMING_SNAKE_CASE 코드, 커스텀 코드 지원), AppResult<T> |
| 파생 | #[derive(Error)], #[derive(Masterror)], #[app_error(...)], #[masterror(...)], #[provide(...)] 텔레메트리 프로바이더 |
| 제어 흐름 | ensure! / fail! — 타입 기반의 할당 없는 조기 반환 |
| 컨텍스트 | ResultExt::ctx / ResultExt::context, 호출자 추적을 갖춘 Context 빌더 |
| 와이어 페이로드 | ErrorResponse (레거시 JSON), 재시도 및 인증 힌트를 갖춘 ProblemJson (RFC 7807) |
| 전송 | Axum IntoResponse, Actix ResponseError/Responder, tonic::Status, WASM JsValue, OpenAPI 스키마 |
| 통합 | sqlx, redis, reqwest, validator, config, tokio, teloxide, Telegram Mini Apps init data, Turnkey |
| 관측성 | tracing 이벤트, metrics 카운터, 지연 backtrace 캡처, 컬러 터미널 출력, DisplayMode (prod/staging/local) |
빠른 예제
use masterror::{AppError, AppErrorKind, AppResult, ProblemJson, field};
fn find_user(id: u64) -> AppResult<()> {
masterror::ensure!(id != 0, AppError::bad_request("id must be non-zero"));
Err(AppError::not_found("user not found")
.with_field(field::u64("user_id", id))
.with_field(field::str("request_id", "abc123")))
}
let err = find_user(42).unwrap_err();
assert_eq!(err.kind, AppErrorKind::NotFound);
assert_eq!(err.kind.http_status(), 404);
let problem = ProblemJson::from_app_error(err);
assert_eq!(problem.status, 404);
assert_eq!(problem.code.as_str(), "NOT_FOUND");
assert_eq!(problem.grpc.expect("grpc").name, "NOT_FOUND");
또는 도메인 오류를 한 번만 선언하고 매핑은 파생 매크로에 맡길 수 있습니다:
use masterror::{AppCode, AppError, AppErrorKind, Error};
#[derive(Debug, Error)]
#[error("missing flag: {name}")]
#[app_error(kind = AppErrorKind::BadRequest, code = AppCode::BadRequest, message)]
struct MissingFlag {
name: &'static str
}
let app: AppError = MissingFlag { name: "feature" }.into();
assert!(matches!(app.kind, AppErrorKind::BadRequest));
워크스페이스 크레이트
| 크레이트 | 역할 |
|---|---|
masterror | 코어 오류 타입, 메타데이터, 전송, 통합, 프렐루드 |
masterror-derive | #[derive(Error)]와 #[derive(Masterror)]를 지원하는 프로시저 매크로 (자동으로 가져옴) |
masterror-template | 공유 #[error("...")] 템플릿 파서 |
문서
시작하기
핵심 개념
- 오류 종류와 코드 — 분류 체계, HTTP/gRPC 테이블, problem+json
- Derive 매크로 —
#[derive(Error)],#[derive(Masterror)]와 해당 속성 - 컨텍스트와 메타데이터 —
Context,ResultExt, 필드, 리덕션, 체인
통합
- 웹 프레임워크 — Axum, Actix, tonic
- 통합 — sqlx, redis, reqwest, validator 등
- 관측성 — tracing, metrics, 백트레이스, 디스플레이 모드
고급