LabHub

블로그

Rust 웹 백엔드 프레임워크 2026 완벽 가이드 - Axum · Actix-web · Rocket · Poem · Loco.rs · Pavex · Salvo · Warp · Hyper 심층 분석

한국어English日本語

1. 왜 2026년에 Rust 백엔드인가

2026년 Rust 백엔드는 더 이상 "유망한 신생 언어" 카테고리에 속하지 않습니다. Cloudflare가 Pingora로 NGINX를 대체하고, Discord가 읽기 API를 Go에서 Rust로 옮겼으며, Anthropic이 AI 인프라의 일부를 Rust로 작성하면서 "메모리 안전성과 GC 없는 성능"이라는 두 마리 토끼가 실제로 잡힌다는 점이 검증됐습니다.

Rust 웹의 핵심 가치는 세 가지입니다. 컴파일 타임에 잡히는 메모리 안전성으로 새벽 3시의 segfault 페이저 호출을 거의 0에 가깝게 줄이고, Tokio 기반 async/await로 Go에 필적하거나 일부 워크로드에서는 더 빠른 처리량을 내며, 풍부한 타입 시스템으로 API 계약을 컴파일러가 검증합니다.

2. Tokio 1.42: Rust 비동기의 기반

Tokio는 사실상 Rust 웹의 표준 런타임입니다. 2026년 5월 기준 Tokio 1.42는 안정성과 성능의 정점을 보여줍니다.

// Cargo.toml
[dependencies]
tokio = { version = "1.42", features = ["full"] }

// main.rs
#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() {
    println!("Hello from Tokio runtime");
}

핵심 컴포넌트는 멀티 스레드 워크 스틸링 스케줄러, tokio::net TCP/UDP, tokio::sync 채널/뮤텍스, tokio::time 타이머, tokio::fs 비차단 파일 I/O입니다. Axum, Actix-web, Poem, Salvo, Loco.rs 모두 Tokio 위에서 동작합니다.

3. Hyper 1.x: 모든 것의 토대

Hyper 1.x는 Rust 웹 스택의 가장 낮은 층입니다. HTTP/1.1과 HTTP/2를 직접 다루는 라이브러리로, Axum과 Actix-web을 비롯한 거의 모든 상위 프레임워크가 내부적으로 Hyper를 사용합니다.

use hyper::{body::Incoming, Request, Response};
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper_util::rt::TokioIo;

async fn handle(req: Request<Incoming>) -> Result<Response<String>, hyper::Error> {
    Ok(Response::new(format!("Hello {}", req.uri().path())))
}

직접 Hyper로 서버를 작성하는 경우는 드물지만, 디버깅이나 극한 최적화 시 내부 구조를 이해해두면 큰 도움이 됩니다.

4. Axum 0.8: 사실상 표준

Axum은 Tokio 팀이 직접 만든 프레임워크로 2026년 현재 Rust 웹의 사실상 표준입니다. tower 미들웨어 생태계를 그대로 활용하고, 추출자(Extractor) 패턴으로 핸들러 시그니처만 봐도 입력이 무엇인지 알 수 있습니다.

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

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

async fn get_user(Path(id): Path<u64>) -> Json<User> {
    Json(User { id, name: format!("User-{id}") })
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/users/:id", get(get_user));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Axum 0.8의 강점은 매크로를 거의 쓰지 않고도 함수 시그니처에 추출자를 명시하는 것만으로 라우팅이 완성된다는 점입니다. 매개변수 타입이 Path, Query, Json, State 등 추출자를 구현하기만 하면 됩니다.

5. Actix-web 4.x: 성능 챔피언

Actix-web 4.x는 TechEmpower 벤치마크에서 수년간 상위권을 차지해온 성능 챔피언입니다. Actor 모델 기반이었지만 4.x부터는 일반적인 async/await로도 자연스럽게 작동합니다.

use actix_web::{get, web, App, HttpServer, Responder};

#[get("/hello/{name}")]
async fn hello(name: web::Path<String>) -> impl Responder {
    format!("Hello {}", name)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(hello))
        .bind("0.0.0.0:8080")?
        .run()
        .await
}

Actix-web의 매력은 raw throughput입니다. 초당 수십만 요청을 단일 인스턴스에서 처리하는 사례가 흔합니다. 다만 internal API가 종종 변경되어 마이너 버전 업그레이드 시 마이그레이션 비용이 발생할 수 있습니다.

6. Rocket 0.5: 타입 안전 마법

Rocket은 Sergio Benitez가 만든 프레임워크로, 매크로 기반의 선언적 API가 특징입니다. 라우팅을 #[get("/users/<id>")] 같은 속성으로 표현해 가독성이 매우 높습니다.

#[macro_use] extern crate rocket;

#[get("/users/<id>")]
fn user(id: u64) -> String {
    format!("User {}", id)
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![user])
}

Rocket 0.5에서 드디어 stable Rust와 async를 모두 지원하기 시작했지만, Axum과 Actix-web의 인기에 밀려 채택률은 정체 상태입니다. 단순한 API 서버, 학습용 프로젝트, 가독성을 극대화하고 싶을 때 여전히 매력적인 선택지입니다.

7. Warp 0.3과 필터 패턴의 쇠퇴

Warp는 Hyper의 메인테이너 Sean McArthur가 만든 필터 기반 프레임워크입니다. warp::path!, warp::filters::body::json 같은 필터를 조합해 라우트를 만드는 방식이 신선했지만, 컴파일 시간이 길어지고 타입 시그니처가 복잡해지는 문제로 2024-2025년을 거치며 사용자가 Axum으로 대거 이동했습니다.

use warp::Filter;

#[tokio::main]
async fn main() {
    let hello = warp::path!("hello" / String)
        .map(|name| format!("Hello {}", name));
    warp::serve(hello).run(([0, 0, 0, 0], 3030)).await;
}

Warp는 여전히 유지보수되고 있지만 신규 프로젝트에서는 거의 선택되지 않습니다.

8. Tide: async-std와 함께 저무는 별

Tide는 async-std 기반의 프레임워크였습니다. async-std가 2024년 이후 사실상 개발 동력을 잃으면서 Tide 역시 신규 채택이 사라졌습니다. 기존 프로젝트는 Tokio + Axum으로 마이그레이션하는 것이 일반적입니다.

9. Poem 3.x: 쉬우면서 강력하게

Poem은 중국에서 시작된 프레임워크로 "쉬우면서 강력하게"가 모토입니다. Axum과 유사한 추출자 패턴을 쓰면서도 OpenAPI 생성(poem-openapi)이 1급 시민으로 통합돼 있어 API 우선 개발에 강합니다.

use poem::{get, handler, listener::TcpListener, web::Path, Route, Server};

#[handler]
fn hello(Path(name): Path<String>) -> String {
    format!("hello: {}", name)
}

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    let app = Route::new().at("/hello/:name", get(hello));
    Server::new(TcpListener::bind("0.0.0.0:3000")).run(app).await
}

Poem 3.x는 WebSocket, SSE, GraphQL 통합이 매우 매끄러워 한 프레임워크에서 다양한 통신 방식을 다루고 싶을 때 좋은 선택입니다.

10. Salvo: 신흥 강자

Salvo는 비교적 새로운 프레임워크로 매우 간결한 API를 지향합니다. 라우팅, 미들웨어, OpenAPI 모두 일관된 인터페이스로 제공돼 학습 곡선이 낮습니다.

use salvo::prelude::*;

#[handler]
async fn hello() -> &'static str { "Hello from Salvo" }

#[tokio::main]
async fn main() {
    let router = Router::new().get(hello);
    let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
    Server::new(acceptor).serve(router).await;
}

Salvo는 2025년 들어 GitHub 별이 빠르게 늘었고, 특히 아시아권 개발자 커뮤니티에서 인기를 얻고 있습니다.

11. Loco.rs: Rails 영감의 풀스택

Loco.rs는 "Rust의 Rails"를 표방하는 풀스택 프레임워크입니다. CLI로 모델, 컨트롤러, 마이그레이션을 생성하고, 백그라운드 잡, 메일러, 세션, 인증까지 박스에 포함돼 있습니다.

cargo install loco-cli
loco new my-app
cd my-app
cargo loco start

내부적으로 Axum과 SeaORM을 묶어 사용합니다. 개별 부품을 직접 조립하기보다는 "내일 출시할 사이드 프로젝트"를 빠르게 만들고 싶은 개발자에게 적합합니다.

12. Pavex: 컴파일 타임 DI

Pavex는 Luca Palmieri(Zero To Production In Rust 저자)가 만든 신규 프레임워크입니다. 가장 큰 차별점은 컴파일 타임 의존성 주입입니다. 별도의 코드 생성 단계를 거쳐 모든 라우트 핸들러의 의존성을 컴파일러가 보장하고, 런타임 오버헤드가 0에 가깝습니다.

// Pavex blueprint
let mut bp = Blueprint::new();
bp.constructor(f!(crate::deps::db_pool), Lifecycle::Singleton);
bp.route(GET, "/users/:id", f!(crate::routes::get_user));

빌드 단계가 추가되는 단점이 있지만, "한 번 컴파일되면 절대 깨지지 않는다"는 보장은 대규모 팀에 매력적입니다.

13. gotham과 Rouille: 잊혀가는 선택지

gotham은 한때 안정성을 강조한 프레임워크였지만 활발한 개발이 멈췄습니다. Rouille는 비동기를 쓰지 않는 동기 프레임워크로, 단순한 사이드카나 학습용에는 여전히 유효하지만 프로덕션 도입은 드뭅니다.

14. 비동기 런타임 4파전

2026년 신규 프로젝트는 99% Tokio를 선택합니다. Glommio/monoio는 매우 특수한 I/O 워크로드(데이터베이스 엔진, 메시지 브로커)에서 고려할 만합니다.

15. HTTP 클라이언트 라이브러리

let body = reqwest::get("https://httpbin.org/get")
    .await?
    .text()
    .await?;

16. 템플릿 엔진

Askama 예시는 다음과 같습니다.

#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate<'a> { name: &'a str }

Vec<u8>, Result<T, E> 같은 제네릭 타입을 인라인 코드로 안전하게 표현할 수 있다는 점도 Askama의 매력입니다.

17. WebSocket: 실시간 통신

WebSocket 구현은 보통 tungstenite와 tokio-tungstenite로 시작하지만, Axum 사용자는 axum::extract::ws를 직접 쓰는 것이 자연스럽습니다.

use axum::extract::ws::{Message, WebSocket, WebSocketUpgrade};

async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
    ws.on_upgrade(handle_socket)
}

async fn handle_socket(mut socket: WebSocket) {
    while let Some(Ok(Message::Text(t))) = socket.recv().await {
        let _ = socket.send(Message::Text(format!("echo: {t}"))).await;
    }
}

18. GraphQL: async-graphql 7

async-graphql 7은 Axum, Actix-web, Poem, Salvo에 모두 어댑터를 제공합니다. 코드 우선 스키마와 매크로 기반 정의가 강력합니다.

use async_graphql::{Object, Schema, EmptyMutation, EmptySubscription};

struct Query;
#[Object]
impl Query {
    async fn hello(&self) -> &str { "world" }
}

19. ORM 4파전

신규 프로젝트라면 SQLx 0.8(타입 안전 raw SQL)이나 SeaORM 1(고수준 추상화)이 가장 무난한 선택입니다.

20. 데이터베이스 드라이버

21. 인증, OpenAPI, 직렬화, 관측

22. 테스트와 벤치마크

빌트인 #[test]로 단위 테스트, tokio::test로 비동기 테스트, axum-test와 tower-test로 핸들러를 직접 호출하는 통합 테스트를 작성합니다. TechEmpower 벤치마크는 매 라운드마다 Actix-web과 Axum이 상위권을 유지하며 일부 시나리오에서는 Go보다 빠른 처리량을 보입니다.

23. 배포 옵션

소규모 프로젝트는 Shuttle, 글로벌 서비스는 Fly.io 또는 Cloudflare, AWS 종속 환경은 Lambda Rust를 추천합니다.

24. 글로벌 도입 사례

25. 한국과 일본의 도입 현황

26. 프레임워크 선택 의사결정 트리

27. References

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다