슬기로운 개발자생활/Rust

Rust와 Axum을 활용한 웹 백엔드 개발 - Axum 프레임워크 소개

개발자 소신 2024. 9. 24. 07:35
반응형

1-1. Axum의 특징과 설치

Axum의 주요 특징

Axum은 Rust로 작성된 경량의 웹 애플리케이션 프레임워크로, Tokio 비동기 런타임을 기반으로 합니다. 주요 특징은 다음과 같습니다:

  • 간단한 라우팅: 함수와 경로를 간단하게 매핑하여 핸들러를 정의할 수 있습니다.
  • 비동기 지원: Tokio 런타임과 함께 비동기 프로그래밍을 효율적으로 지원합니다.
  • 확장성: 다른 라이브러리와의 통합이 용이하며, 필요한 기능을 모듈화하여 사용할 수 있습니다.
  • 상태 관리 용이성: 애플리케이션 상태를 쉽게 공유하고 관리할 수 있습니다.

Axum 설치하기

Axum을 사용하려면 Cargo.toml 파일에 의존성을 추가해야 합니다.

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
  • axum: 웹 프레임워크
  • tokio: 비동기 런타임

1-2. 기본 라우팅 설정 및 핸들러 작성

"Hello, World!" 예제

가장 기본적인 Axum 웹 서버를 만들어 보겠습니다.

src/main.rs:

use axum::{
    routing::get,
    Router,
};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    // 라우터 생성: "/" 경로에 대한 요청을 `hello_world` 핸들러에 연결
    let app = Router::new().route("/", get(hello_world));

    // 서버를 바인딩할 주소 설정
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("서버가 {}에서 실행 중입니다.", addr);

    // 서버 실행
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

// 핸들러 함수 정의
async fn hello_world() -> &'static str {
    "Hello, World!"
}

 

실행하기:

cargo run

 

웹 브라우저에서 http://127.0.0.1:3000/에 접속하면 "Hello, World!" 메시지를 볼 수 있습니다.

라우팅 설정

다양한 경로와 HTTP 메서드를 설정하여 라우팅을 구성할 수 있습니다.

use axum::routing::{get, post};

let app = Router::new()
    .route("/get", get(get_handler))
    .route("/post", post(post_handler));

1-3. 요청과 응답 처리 방법

요청 데이터 추출

경로 매개변수 추출

경로에서 변수를 추출하여 핸들러 함수의 매개변수로 사용할 수 있습니다.

use axum::{extract::Path, routing::get, Router};

async fn greet(Path(name): Path<String>) -> String {
    format!("Hello, {}!", name)
}

let app = Router::new().route("/greet/:name", get(greet));

 

예를 들어, /greet/Alice로 요청하면 "Hello, Alice!"를 반환합니다.

쿼리 문자열 추출

use axum::{extract::Query, routing::get, Router};
use std::collections::HashMap;

async fn query_params(Query(params): Query<HashMap<String, String>>) -> String {
    format!("쿼리 파라미터: {:?}", params)
}

let app = Router::new().route("/search", get(query_params));

/search?keyword=rust&sort=asc로 요청하면 쿼리 파라미터를 추출하여 반환합니다.

JSON 바디 추출

use axum::{extract::Json, routing::post, Router};
use serde::Deserialize;

#[derive(Deserialize)]
struct CreateUser {
    username: String,
    email: String,
}

async fn create_user(Json(payload): Json<CreateUser>) -> String {
    format!("사용자 생성: {} ({})", payload.username, payload.email)
}

let app = Router::new().route("/users", post(create_user));

클라이언트에서 JSON 형식의 데이터를 POST로 전송하면 구조체로 매핑하여 사용할 수 있습니다.

응답 생성

문자열 응답

핸들러 함수에서 String 또는 &'static str을 반환하여 텍스트 응답을 보낼 수 있습니다.

async fn simple_response() -> &'static str {
    "간단한 응답 메시지"
}

JSON 응답

use axum::{response::Json, routing::get, Router};
use serde::Serialize;

#[derive(Serialize)]
struct User {
    id: u32,
    username: String,
}

async fn get_user() -> Json<User> {
    let user = User {
        id: 1,
        username: "Alice".to_string(),
    };
    Json(user)
}

let app = Router::new().route("/user", get(get_user));

응답 데이터를 JSON으로 직렬화하여 반환합니다.


1-4. 상태 관리 및 공유 데이터 처리

애플리케이션 상태 공유

애플리케이션에서 공통으로 사용하는 상태를 공유하려면 Extension을 사용합니다.

use axum::{
    extract::Extension,
    routing::get,
    Router,
};
use std::sync::Arc;

struct AppState {
    app_name: String,
}

async fn handler(Extension(state): Extension<Arc<AppState>>) -> String {
    format!("앱 이름: {}", state.app_name)
}

#[tokio::main]
async fn main() {
    let state = Arc::new(AppState {
        app_name: "My Axum App".to_string(),
    });

    let app = Router::new()
        .route("/", get(handler))
        .layer(Extension(state));

    axum::Server::bind(&"127.0.0.1:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}
  • Arc를 사용하여 다중 스레드 환경에서 안전하게 상태를 공유합니다.
  • Extension 레이어를 통해 상태를 애플리케이션에 주입합니다.

상태 사용 예시: 카운터 증가

use axum::{
    extract::Extension,
    routing::get,
    Router,
};
use std::sync::{Arc, Mutex};

struct AppState {
    counter: Mutex<u32>,
}

async fn increment_counter(Extension(state): Extension<Arc<AppState>>) -> String {
    let mut counter = state.counter.lock().unwrap();
    *counter += 1;
    format!("현재 카운터 값: {}", counter)
}

#[tokio::main]
async fn main() {
    let state = Arc::new(AppState {
        counter: Mutex::new(0),
    });

    let app = Router::new()
        .route("/count", get(increment_counter))
        .layer(Extension(state));

    axum::Server::bind(&"127.0.0.1:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}
  • Mutex를 사용하여 공유 자원의 동시 접근을 제어합니다.
  • /count 엔드포인트에 접근할 때마다 카운터가 증가합니다.

결론

Axum은 Rust로 웹 애플리케이션을 개발할 때 간단하면서도 강력한 기능을 제공합니다. 이번 글에서는 Axum의 특징과 설치 방법부터 기본적인 라우팅 설정, 요청과 응답 처리, 그리고 상태 관리 및 공유 데이터 처리까지 살펴보았습니다. 이러한 기본 개념을 바탕으로 더 복잡한 웹 애플리케이션을 구축할 수 있습니다.


연습 과제:

  1. 추가 엔드포인트 구현하기
    • 여러 개의 엔드포인트를 추가하여 다양한 HTTP 메서드와 경로를 연습해보세요.
  2. 상태를 활용한 간단한 애플리케이션 만들기
    • 상태를 사용하여 방문자 수를 추적하거나, 간단한 메모장 기능을 구현해보세요.
  3. 에러 처리 추가하기
    • 요청 처리 중 발생할 수 있는 에러를 적절히 처리하고 사용자에게 의미 있는 응답을 반환하도록 해보세요.

참고 자료


Note: 이 글은 Axum 프레임워크의 기본 사용법을 소개하기 위한 것으로, 더 깊은 이해를 위해서는 공식 문서와 추가 자료를 참고하시기 바랍니다.

반응형