⚡ Axum 통합 가이드
Axum의 인체공학적 API와 Ranvier의 Schematic 워크플로우 결합
개요
Axum은 인체공학과 모듈성에 중점을 둔 웹 프레임워크로, Tower 위에 구축되었습니다. Ranvier 트랜지션을 Axum 핸들러와 통합하여 다음을 결합할 수 있습니다:
- Axum: 타입 안전 익스트랙터, 상태 관리, Tower 미들웨어
- Ranvier: Schematic 시각화, Bus 전파, 조합 가능한 로직
통합 패턴
Axum 핸들러에서 Ranvier 트랜지션을 호출하는 것이 권장 접근법입니다:
예제: Axum 핸들러에서 Ranvier 트랜지션 호출
use axum::{
extract::{State, Json},
http::StatusCode,
response::IntoResponse,
Router,
};
use ranvier_core::{Bus, Outcome};
use ranvier_macros::transition;
use ranvier_runtime::Axon;
use std::sync::Arc;
// 공유 앱 상태
#[derive(Clone)]
struct AppState {
pipeline: Axon<CreateUser, User, AppError, ()>,
}
// Ranvier 트랜지션
#[transition]
async fn create_user(
input: CreateUser,
_res: &(),
bus: &mut Bus
) -> Outcome<User, AppError> {
// 비즈니스 로직
Outcome::Next(User { id: 1, name: input.name })
}
// Axum 핸들러
async fn create_user_handler(
State(state): State<Arc<AppState>>,
Json(payload): Json<CreateUser>
) -> impl IntoResponse {
let mut bus = Bus::new();
match state.pipeline.execute(payload, &(), &mut bus).await {
Outcome::Next(user) => (StatusCode::CREATED, Json(user)).into_response(),
Outcome::Fault(err) => (StatusCode::BAD_REQUEST, Json(err)).into_response(),
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}
// Axum 앱
#[tokio::main]
async fn main() {
let pipeline = Axon::simple::<AppError>("create-user")
.then(create_user);
let state = Arc::new(AppState { pipeline });
let app = Router::new()
.route("/users", axum::routing::post(create_user_handler))
.with_state(state);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}예제: Ranvier와 함께 Axum 익스트랙터 사용
use axum::{
extract::{Extension, TypedHeader},
headers::Authorization,
headers::authorization::Bearer,
};
async fn protected_handler(
TypedHeader(auth): TypedHeader<Authorization<Bearer>>, // Axum 익스트랙터
State(state): State<Arc<AppState>>,
Json(data): Json<RequestData>
) -> impl IntoResponse {
let mut bus = Bus::new();
// 추출한 토큰을 Bus에 추가하여 Ranvier 트랜지션에서 사용
bus.insert(auth.token().to_string());
match state.pipeline.execute(data, &(), &mut bus).await {
Outcome::Next(result) => (StatusCode::OK, Json(result)).into_response(),
Outcome::Fault(err) => (StatusCode::UNAUTHORIZED, Json(err)).into_response(),
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}예제: Axum과 Ranvier 간 상태 공유
// Axum 상태에 앱 설정 및 Ranvier 파이프라인 포함
#[derive(Clone)]
struct AppState {
db: Arc<DatabasePool>,
order_pipeline: Axon<Order, OrderResult, AppError, ()>,
user_pipeline: Axon<CreateUser, User, AppError, ()>,
}
// 핸들러에서 Axum 상태 접근 및 Ranvier 파이프라인 실행
async fn process_order(
State(state): State<Arc<AppState>>,
Json(order): Json<Order>
) -> impl IntoResponse {
let mut bus = Bus::new();
// Axum 관리 리소스를 Bus에 추가하여 Ranvier 트랜지션에서 사용
bus.insert(state.db.clone());
state.order_pipeline.execute(order, &(), &mut bus).await
// ... 결과 처리
}이 접근법을 사용해야 하는 경우
✅ 적합한 경우:
- 복잡한 워크플로우에 Ranvier를 추가하는 기존 Axum 앱
- Axum의 타입 안전 익스트랙터에 익숙한 팀
- Ranvier 비즈니스 로직과 함께 Tower 미들웨어가 필요한 경우
- 다단계 프로세스를 위한 Ranvier의 Schematic 시각화가 필요한 경우
⚠️ 고려사항:
- 상태 브리징: Axum State를 Ranvier Bus로 수동으로 브리징해야 함
- 미들웨어 가시성: Axum 미들웨어(Tower 레이어)는 Ranvier Schematic에 표시되지 않음
- 테스팅: Axum 핸들러는 통합 테스트, Ranvier 트랜지션은 단위 테스트
트레이드오프
| 측면 | 장점 | 제한사항 |
|---|---|---|
| 익스트랙터 | Axum의 타입 안전 익스트랙터 (Json, State, TypedHeader) | Bus로의 수동 브리징 필요 |
| Tower 미들웨어 | Axum 레이어를 통한 전체 Tower 생태계 | Ranvier Schematic에 보이지 않음 |
| 상태 관리 | Axum의 인체공학적 상태 공유 | State + Bus 이중 관리 |
| 비즈니스 로직 | Schematic 시각화가 있는 Ranvier 트랜지션 | Axum ↔ Ranvier 통합을 위한 추가 보일러플레이트 |
대안과의 비교
- 순수 Ranvier와 비교: Axum 통합은 타입 안전 익스트랙터와 Tower 미들웨어를 제공하지만 일부 Schematic 가시성을 잃음
- Tower 통합과 비교: Axum은 Tower 위에서 더 높은 수준의 인체공학 제공 (익스트랙터, 라우팅)
- actix 통합과 비교: Axum은 타입 기반 익스트랙터, actix는 트레이트 기반 익스트랙터