⚡ 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는 트레이트 기반 익스트랙터

다음 단계

참고: 전체 Axum + Ranvier 예제는 향후 마일스톤에 계획되어 있습니다. 지금은 위의 코드 스니펫을 시작점으로 사용하세요.

통합 패턴에 대해 자세히 알아보려면: