💻 프로그래밍

Actix

Rust의 고성능 웹 프레임워크

📖 상세 설명

Actix는 Rust 언어로 작성된 고성능 웹 프레임워크로, 액터 모델(Actor Model)을 기반으로 설계되었습니다. 2017년 Nikolay Kim이 개발을 시작했으며, TechEmpower 웹 프레임워크 벤치마크에서 꾸준히 상위권을 차지하며 세계에서 가장 빠른 웹 프레임워크 중 하나로 인정받고 있습니다.

Actix-web은 Actix 액터 프레임워크 위에 구축된 웹 프레임워크입니다. 비동기 I/O를 위해 Tokio 런타임을 사용하며, Rust의 소유권 시스템과 타입 안전성을 활용해 메모리 안전하면서도 C/C++ 수준의 성능을 제공합니다. HTTP/1.x와 HTTP/2를 모두 지원하고, WebSocket도 내장되어 있습니다.

액터 모델은 각 액터가 독립적인 상태를 가지며 메시지를 통해서만 통신하는 동시성 모델입니다. 이 방식은 공유 상태로 인한 데이터 레이스를 원천적으로 방지하며, 수천 개의 동시 연결도 효율적으로 처리할 수 있습니다. Actix는 이를 통해 확장성 높은 마이크로서비스 구축에 이상적입니다.

실무에서 Actix는 높은 처리량이 필요한 API 서버, 실시간 데이터 스트리밍, 고성능 프록시 서버 등에 사용됩니다. Discord, Cloudflare 등 대규모 트래픽을 처리하는 기업들이 Rust 기반 서비스에 Actix를 채택하고 있으며, 특히 초당 수십만 요청을 처리해야 하는 환경에서 Go나 Node.js 대비 현저히 낮은 메모리 사용량과 지연 시간을 보여줍니다.

💻 코드 예제

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

// 1. JSON 요청/응답을 위한 구조체 정의
#[derive(Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

// 2. 핸들러 함수 정의
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("안녕하세요! Actix-web입니다.")
}

async fn create_user(user: web::Json<CreateUser>) -> impl Responder {
    let new_user = User {
        id: 1,
        name: user.name.clone(),
        email: user.email.clone(),
    };
    HttpResponse::Created().json(new_user)
}

async fn get_user(path: web::Path<u64>) -> impl Responder {
    let user_id = path.into_inner();
    let user = User {
        id: user_id,
        name: "홍길동".to_string(),
        email: "hong@example.com".to_string(),
    };
    HttpResponse::Ok().json(user)
}

// 3. 서버 시작
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("서버 시작: http://127.0.0.1:8080");

    HttpServer::new(|| {
        App::new()
            .route("/", web::get().to(hello))
            .route("/users", web::post().to(create_user))
            .route("/users/{id}", web::get().to(get_user))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

🗣️ 실무에서 이렇게 말하세요

💬 회의에서
"이 API 서버는 초당 10만 요청을 처리해야 합니다. Node.js로는 메모리 사용량이 너무 높아지는데, Actix-web으로 전환하면 동일 스펙에서 3배 이상의 처리량과 70% 낮은 메모리 사용량을 기대할 수 있습니다."
💬 면접에서
"Actix-web은 액터 모델을 기반으로 하며, Tokio 런타임 위에서 비동기 I/O를 처리합니다. Rust의 소유권 시스템 덕분에 데이터 레이스 없이 안전한 동시성을 구현할 수 있고, TechEmpower 벤치마크에서 상위권 성능을 보여줍니다."
💬 코드 리뷰에서
"이 핸들러에서 blocking I/O를 직접 호출하면 안 됩니다. web::block으로 감싸서 별도 스레드풀에서 실행하거나, 비동기 버전의 라이브러리를 사용해주세요. 그렇지 않으면 전체 런타임이 블록됩니다."

⚠️ 흔한 실수 & 주의사항

비동기 핸들러에서 blocking 작업 수행

sync 데이터베이스 드라이버나 파일 I/O를 직접 호출하면 전체 런타임이 멈춥니다. `web::block()`을 사용하거나 sqlx, tokio-postgres 같은 async 드라이버를 사용하세요.

에러 핸들링 누락

Rust의 Result 타입을 제대로 처리하지 않고 `.unwrap()`을 남용하면 프로덕션에서 패닉이 발생합니다. `?` 연산자와 커스텀 에러 타입을 활용해 graceful한 에러 처리를 구현하세요.

App::app_data()로 상태 공유하기

핸들러 간 상태(DB 커넥션 풀 등)를 공유할 때 `App::app_data()`와 `web::Data<T>`를 사용하세요. Arc로 감싸면 여러 워커 스레드에서 안전하게 접근할 수 있습니다.

🔗 관련 용어

📚 더 배우기