본문 바로가기
Backend2024년 11월 25일9분 읽기

Rust Actix Web으로 고성능 REST API 구축

YS
김영삼
조회 580

Actix Web 소개

Actix Web은 Rust의 대표적인 웹 프레임워크로, TechEmpower 벤치마크에서 상위권을 유지하는 고성능 프레임워크입니다. Rust의 소유권 시스템과 타입 안전성 덕분에 메모리 안전한 서버를 구축할 수 있으며, async/await 기반의 비동기 처리를 지원합니다.

프로젝트 설정

# Cargo.toml
[package]
name = "api-server"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "4"
actix-rt = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] }
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
env_logger = "0.10"
dotenv = "0.15"

기본 서버와 라우팅

use actix_web::{web, App, HttpServer, HttpResponse, middleware};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct User {
    id: uuid::Uuid,
    name: String,
    email: String,
    created_at: chrono::NaiveDateTime,
}

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

#[derive(Deserialize)]
struct QueryParams {
    page: Option<u32>,
    limit: Option<u32>,
}

async fn get_users(
    pool: web::Data<sqlx::PgPool>,
    query: web::Query<QueryParams>,
) -> HttpResponse {
    let limit = query.limit.unwrap_or(20).min(100);
    let offset = (query.page.unwrap_or(1) - 1) * limit;

    let users = sqlx::query_as!(
        User,
        "SELECT id, name, email, created_at FROM users ORDER BY created_at DESC LIMIT $1 OFFSET $2",
        limit as i64,
        offset as i64,
    )
    .fetch_all(pool.get_ref())
    .await
    .unwrap();

    HttpResponse::Ok().json(users)
}

async fn create_user(
    pool: web::Data<sqlx::PgPool>,
    body: web::Json<CreateUser>,
) -> HttpResponse {
    let user = sqlx::query_as!(
        User,
        "INSERT INTO users (id, name, email, created_at) VALUES ($1, $2, $3, NOW()) RETURNING *",
        uuid::Uuid::new_v4(),
        body.name,
        body.email,
    )
    .fetch_one(pool.get_ref())
    .await
    .unwrap();

    HttpResponse::Created().json(user)
}

에러 처리와 미들웨어

use actix_web::error::ResponseError;
use std::fmt;

#[derive(Debug)]
enum ApiError {
    NotFound(String),
    BadRequest(String),
    Internal(String),
}

impl fmt::Display for ApiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ApiError::NotFound(msg) => write!(f, "Not Found: {}", msg),
            ApiError::BadRequest(msg) => write!(f, "Bad Request: {}", msg),
            ApiError::Internal(msg) => write!(f, "Internal Error: {}", msg),
        }
    }
}

impl ResponseError for ApiError {
    fn error_response(&self) -> HttpResponse {
        match self {
            ApiError::NotFound(msg) =>
                HttpResponse::NotFound().json(serde_json::json!({"error": msg})),
            ApiError::BadRequest(msg) =>
                HttpResponse::BadRequest().json(serde_json::json!({"error": msg})),
            ApiError::Internal(msg) =>
                HttpResponse::InternalServerError().json(serde_json::json!({"error": msg})),
        }
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv::dotenv().ok();
    env_logger::init();

    let pool = sqlx::PgPool::connect(&std::env::var("DATABASE_URL").unwrap())
        .await.unwrap();

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .wrap(middleware::Logger::default())
            .wrap(middleware::Compress::default())
            .service(
                web::scope("/api/v1")
                    .route("/users", web::get().to(get_users))
                    .route("/users", web::post().to(create_user))
            )
    })
    .bind("0.0.0.0:8080")?
    .workers(num_cpus::get())
    .run()
    .await
}
  • Actix Web의 Extractor 패턴으로 Path, Query, Json, Data 등을 타입 안전하게 추출합니다
  • sqlx::query_as! 매크로는 컴파일 타임에 SQL 쿼리를 검증합니다
  • middleware::Compress로 자동 gzip/brotli 압축을 적용합니다
  • Rust의 zero-cost abstractions 덕분에 Go, Java 대비 메모리 사용량이 매우 적습니다
  • 프로덕션에서는 shuttle.rs 또는 Docker로 쉽게 배포할 수 있습니다

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록