Rust AXUM+MongoDB로 RestAPI 서버 개발하기 - 프로젝트 설정
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