슬기로운 개발자생활/Backend

Rust AXUM+MongoDB로 RestAPI 서버 개발하기 - 프로젝트 설정

개발자 소신 2024. 3. 10. 14:59
반응형

Rust AXUM을 활용한 RestfulAPI 웹 애플리케이션 서버를 개발해봅시다.
전체적인 폴더구조, 흐름은 다음과 같습니다.

프로젝트 시작

cargo new {project_name} --bin

0. 프로젝트 폴더 구조

src/
|-- main.rs
|-- .env
|-- {domain}/
    |-- mod.rs
    |-- entity.rs
    |-- handler.rs
    |-- usecase.rs
    |-- repository.rs
    |-- dto/
        |-- mod.rs
        |-- req.rs
        |-- res.rs
|-- config/
    |-- mod.rs
    |-- database.rs

1. 필수 패키지 설치

먼저, 프로젝트를 시작하기 전에 Cargo.toml 파일에 다음 패키지들을 추가해야 합니다:

axum: 웹 애플리케이션을 구축하기 위한 주요 웹 프레임워크입니다.
tokio: 비동기 런타임, Axum은 Tokio 위에서 작동합니다.
tracing: 로깅과 추적을 위한 유틸리티입니다.
tracing-subscriber: 로깅과 추적을 위한 유틸리티입니다.
mongodb: MongoDB 데이터베이스와의 통신을 위한 드라이버입니다.
serde, serde_json: JSON 직렬화 및 역직렬화를 위한 크레이트입니다.
dotenv: 환경 변수 관리를 쉽게 해줍니다.

  • Cargo.toml 예시:
[dependencies]
axum = "0.7.3"
tokio = { version = "1.35.1", features = ["full"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
mongodb = "2.8.0"
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
dotenv = "0.15"

2. 프로젝트 구조 설정

위에서 제시된 폴더 구조를 기반으로 각 파일과 모듈을 만들고 기본 구조를 설정해야 합니다. 각 모듈의 역할은 다음과 같습니다:

main.rs: 애플리케이션의 진입점이며, Axum 라우터를 설정하고 서버를 시작합니다.
{domain}/mod.rs: 도메인 별로 분리하여 모듈화된 비즈니스 로직을 포함합니다.
entity.rs: 데이터베이스 엔티티를 정의합니다.
handler.rs: HTTP 요청을 처리하는 핸들러 함수를 정의합니다.
usecase.rs: 비즈니스 로직을 구현합니다.
repository.rs: 데이터베이스 연산을 추상화합니다.
dto/mod.rs, req.rs, res.rs: Data Transfer Object(DTO)를 정의하여 클라이언트와 서버 간의 데이터 교환을 관리합니다.
config/mod.rs, database.rs: 데이터베이스 연결 설정과 관련된 구성을 관리합니다.

3. MongoDB 설정

config/database.rs 파일에 MongoDB 연결 설정을 구현합니다. dotenv 크레이트를 사용하여 환경 변수에서 데이터베이스 연결 정보를 가져올 수 있습니다.

use std::env;

use mongodb::{Client, options::ClientOptions, Database};
use tracing::log::info;

pub async fn dbconnect() -> mongodb::error::Result<Database> {
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    let db_name = env::var("MONGO_DB").expect("MONGO_DB must be set");
    let mut client_options = ClientOptions::parse(database_url).await?;

    client_options.app_name = Some("App Name".to_string());
    let client = Client::with_options(client_options)?;
    let db = client.database("db_name");

    info!("Database has been connected");

    Ok(db)
}
  • .env
MONGO_DB=tc_db
DATABASE_URL=mongodb://root:123456@0.0.0.0:27017
  • config/mod.rs
pub mod database;

4. RESTful API 엔드포인트 구현, Main.rs

각 도메인의 handler.rs 파일에서 필요한 API 엔드포인트를 구현합니다. axum의 라우팅 기능을 사용하여 각 엔드포인트를 구현하고, 요청을 처리하는 로직을 작성합니다.

  • {domain}/handler.rs
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde_json::json;

// 시작하기
pub async fn start() -> impl IntoResponse {
    (
        StatusCode::OK,
        Json(json!({
            "message": "hello Rust AXUM",
        })).into_response()
    )
}
  • {domain}/mod.rs
mod entity;
pub mod handler;
mod repository;
mod usecase;
  • main.rs
pub mod api;
pub mod config;

use api::handler::start;

use axum::{
    routing::{delete, get, patch, post},
    Router,
};

#[tokio::main]
async fn main() {
    // get `.env` secrets
    dotenv().ok();
    // initialize tracing
    tracing_subscriber::fmt::init();

    // build our application with a route
    let app = Router::new()
        .route("/", get(|| async { "OK" }))
        .route("/", get(start));

    let _ = config::database::dbconnect().await;

    // run our app with hyper, listening globally on port 3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3001").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

5. 서버 실행

마지막으로, main.rs 파일에서 애플리케이션의 메인 함수를 구성하여 서버를 실행합니다. Tokio 비동기 런타임을 초기화하고, Axum 라우터를 사용하여 HTTP 서버를 구성합니다.

cargo run

 

 

반응형