Published on

axum vs actix-web: 백엔드 개발자를 위한 선택 기준

Authors
  • avatar
    Name
    Indo Yoon
    Twitter

axum과 actix-web은 둘 다 Rust 비동기 서버를 운영할 수 있는 성숙한 선택지이며, 결론부터 말씀드리면 이미 tower 기반 구성을 쓰고 있거나 미들웨어를 직접 조합하는 편을 선호한다면 axum이, 프레임워크가 정한 실행 규약과 내장 기능을 그대로 활용하는 편을 선호한다면 actix-web이 운영 부담을 줄여 줍니다. 두 프레임워크 모두 실제 서비스에서 쓰이지만 설계 철학이 달라서 장애 대응 방식과 업그레이드 비용이 갈립니다. 이 글은 서버 운영과 배포를 담당하는 백엔드 개발자 관점에서 두 프레임워크를 같은 기준으로 비교해 드려요.

한눈에 보기

axum은 Tokio 진영에서 관리하는 웹 프레임워크로, HTTP 처리는 hyper에, 미들웨어와 서비스 조합은 tower에 맡기는 구조입니다. 라우터에 핸들러를 등록하고 추출자로 요청 데이터를 꺼내는 방식이 중심이며, 매크로 없이도 라우팅 코드가 그대로 읽히는 점이 특징이에요. 공유 상태는 State 추출자와 with_state로 연결하고, 미들웨어는 tower 계층을 쌓아서 구성합니다.

actix-web은 액터 계열 크레이트에서 출발해 웹 계층이 독립적으로 발전해 온 프레임워크입니다. App에 서비스와 미들웨어를 등록하고 HttpServer가 워커마다 앱을 만들어 실행하는 구조이며, 속성 매크로로 핸들러를 선언하는 방식이 널리 쓰여요. 로거, 압축, 기본 헤더, 경로 정규화처럼 운영에서 자주 쓰는 미들웨어를 프레임워크 차원에서 제공합니다.

두 프레임워크 모두 비동기 핸들러, JSON 직렬화, 경로 파라미터, 쿼리 파싱, 공유 상태, 정적 파일 제공, 테스트 유틸리티를 갖추고 있어요. 그래서 기능 목록만 나열하면 차이가 작아 보이지만, 실제 운영에서는 미들웨어를 조합하는 방식과 앱 인스턴스의 수명을 관리하는 지점에서 체감 차이가 크게 납니다.

비교 표

아래 표는 배포와 운영을 준비할 때 자주 확인하게 되는 항목만 추려 정리한 것입니다.

항목axumactix-web
기반hyper, tower, Tokio자체 런타임 계층과 웹 스택
라우팅 선언코드로 라우터에 등록코드 등록과 속성 매크로 모두 지원
미들웨어tower 및 tower-http 계층 조합내장 미들웨어와 wrap 등록
상태 공유State 추출자와 with_stateweb::Data와 app_data
앱 수명라우터를 그대로 서비스로 사용워커마다 앱 팩토리 호출
종료 처리종료 신호를 라우터에 연결서버 설정으로 종료 시간 제어
테스트서비스 호출 방식전용 테스트 모듈 제공
생태계tower 호환 라이브러리와 결합actix 계열 크레이트와 결합

표에서 눈여겨볼 지점은 미들웨어와 앱 수명 항목이에요. 이 두 가지가 배포 스크립트와 로그 파이프라인 설계에 직접 영향을 줍니다.

설계 차이: tower 계층과 앱 팩토리

axum은 요청 처리 과정을 tower의 서비스로 바라봅니다. 라우터 자체가 서비스이고, 그 바깥에 계층을 쌓아 타임아웃, 트레이싱, 압축, CORS를 붙이는 방식이에요. 계층은 바깥에서 안으로 적용되기 때문에 순서를 어떻게 두느냐에 따라 동작이 달라지고, 그 순서가 코드에 그대로 드러난다는 점이 운영할 때 도움이 됩니다. tower-http에 준비된 계층을 그대로 재사용할 수 있어서 다른 tower 기반 서비스와 미들웨어를 공유하기도 쉬워요.

actix-web은 HttpServer가 워커를 만들고, 각 워커가 앱 팩토리 클로저를 호출해 자기 앱 인스턴스를 갖는 구조입니다. 그래서 공유 상태는 web::Data로 감싸 넘기고 핸들러에서는 그 데이터를 참조만 하게 됩니다. 미들웨어는 wrap으로 등록하며, 로거와 압축, 기본 헤더 같은 기능이 프레임워크에 들어 있어서 필요한 것을 골라 붙이면 돼요.

이 차이는 장애 대응 장면에서 드러납니다. axum은 미들웨어 한 겹을 끼거나 빼는 일이 곧 계층을 조합하는 일이라 변경 범위를 예측하기 쉬워요. actix-web은 프레임워크가 정한 실행 흐름 안에서 설정을 조정하는 방식이라, 제공되는 기능을 그대로 쓰는 편이 안전합니다.

라우팅과 추출자 비교

axum은 Router에 경로와 핸들러를 등록하고, 핸들러 인자로 Path, Query, Json, State 같은 추출자를 받습니다. 추출자마다 요청 본문을 소비하는지 여부가 정해져 있어서, 본문을 읽는 추출자는 핸들러 인자에서 마지막에 두어야 해요. 경로 파라미터 표기는 버전에 따라 달라진 적이 있으므로 사용 중인 버전의 공식 문서에서 확인하는 편이 좋습니다.

actix-web은 App에 서비스를 등록하고 web::Path, web::Query, web::Json으로 값을 꺼냅니다. 속성 매크로를 쓰면 핸들러 위에 경로와 메서드를 함께 선언할 수 있어서 라우팅 목록이 한눈에 들어오는 장점이 있어요. 다만 매크로 방식과 직접 등록 방식이 한 파일 안에서 섞이면 일관성이 떨어지므로 팀 규칙을 미리 정해 두는 편이 좋습니다.

두 프레임워크 모두 경로 파라미터와 쿼리, JSON 본문을 다루는 기본기는 비슷합니다. 그래서 라우팅 문법보다는 추출자가 실패했을 때 어떤 응답이 나가는지, 에러를 어떤 타입으로 통일할지 쪽을 먼저 정하는 것이 실제 운영에서 더 중요해요.

단계별 도입 방법

두 프레임워크 모두 골격을 잡고 상태와 미들웨어를 붙인 뒤 종료 처리와 테스트를 더하는 순서로 진행하면 시행착오가 줄어듭니다.

axum 도입 순서

  • 라우터 골격 만들기: Router::new()에 상태가 필요 없는 헬스 체크 경로부터 등록합니다.
  • 공유 상태 붙이기: 커넥션 풀이나 설정 값을 with_state로 넣고 핸들러에서 State로 받습니다.
  • 미들웨어 쌓기: 트레이싱과 타임아웃 계층을 먼저 붙이고, 압축과 CORS는 필요해질 때 추가합니다.
  • 종료 처리 넣기: 종료 신호를 받아 with_graceful_shutdown으로 진행 중인 요청을 정리합니다.
  • 테스트 작성: 라우터를 서비스로 호출해 응답 코드와 본문을 검증합니다.

actix-web 도입 순서

  • 앱 팩토리 작성하기: App을 만드는 클로저를 분리해 워커마다 새 인스턴스가 만들어지게 합니다.
  • 상태 공유하기: web::Data로 감싼 값을 app_data에 등록하고 핸들러에서 꺼내 씁니다.
  • 미들웨어 등록하기: 로거와 기본 헤더를 먼저 붙이고, 압축과 CORS는 지표를 본 뒤 추가합니다.
  • 서버 설정하기: 바인딩 주소와 종료 타임아웃을 정하고 run으로 기동합니다.
  • 테스트 작성: 전용 테스트 모듈로 앱을 초기화한 뒤 요청을 보내 응답을 확인합니다.

두 경우 모두 배포 전에 종료 처리를 먼저 넣어 두는 것이 좋아요. 롤링 배포나 오토스케일 환경에서는 종료 신호를 받은 뒤 진행 중인 요청을 얼마나 정리하느냐가 사용자 체감 오류 수를 좌우합니다.

상황별 선택 기준

이미 tower 기반 미들웨어나 서비스 구성을 사내에서 쓰고 있다면 axum이 자연스럽습니다. 트레이싱, 타임아웃, 압축 같은 계층을 다른 서비스와 같은 방식으로 관리할 수 있어서 운영 도구와 로그 형식을 통일하기 쉬워요.

사내에 actix-web으로 만든 서비스가 이미 여러 개 있다면 actix-web을 유지하는 편이 낫습니다. 배포 스크립트, 대시보드, 로그 파이프라인, 장애 대응 런북이 그 프레임워크의 실행 모델에 맞춰져 있을 가능성이 크기 때문이에요.

프레임워크 내장 기능만으로 서비스를 빠르게 만들어야 하는 상황이라면 actix-web이 편합니다. 반대로 여러 서비스에 같은 미들웨어 정책을 강제해야 하는 조직이라면 axum 쪽이 조합으로 풀기 좋아요.

팀원의 Rust 웹 경험이 적다면 공식 문서와 예제의 일관성을 기준으로 고르는 것도 현실적인 방법입니다. 두 프레임워크 모두 문서가 잘 정리되어 있으니, 팀이 실제로 읽고 이해되는 쪽을 고르면 됩니다.

주의할 점

공개된 벤치마크 순위를 그대로 믿고 프레임워크를 고르는 것은 위험합니다. 워크로드 구성, 런타임 설정, 직렬화 비용, 데이터베이스 대기 시간에 따라 결과가 크게 달라지기 때문에, 실제 서비스와 비슷한 요청을 직접 측정하는 편이 정확해요.

버전을 올릴 때는 라우팅 문법과 추출자 시그니처 변경을 먼저 확인해야 합니다. 특히 경로 파라미터 표기나 미들웨어 등록 방식은 릴리스마다 조정된 이력이 있으므로, 업그레이드 노트를 읽고 컴파일 오류를 기준으로 대응 범위를 잡는 것이 안전해요.

워커 수와 스레드 설정, 데이터베이스 커넥션 풀 크기를 따로 튜닝하면 자원이 남거나 모자라는 상황이 생깁니다. 서버가 동시에 처리할 수 있는 요청량과 커넥션 풀이 감당할 수 있는 양을 함께 보고 설정해야 합니다.

미들웨어 순서도 자주 놓치는 부분입니다. 인증과 레이트 리밋은 로깅보다 안쪽에 두어야 거부된 요청이 로그에 어떻게 남길지 예측할 수 있고, 타임아웃은 어느 지점에서 끊길지 명확히 해 두어야 합니다.

에러 응답 형식은 초기에 통일해 두는 편이 좋아요. 두 프레임워크 모두 에러를 응답으로 바꾸는 지점이 여러 곳에 흩어지기 쉬워서, 공통 에러 타입과 변환 규칙을 정해 두지 않으면 클라이언트가 예외 처리를 하기 어려워집니다.

자주 묻는 질문

axum이 actix-web보다 항상 빠른가요?

그렇지 않습니다. 성능은 프레임워크 이름보다 워크로드, 직렬화 비용, 런타임 설정, 외부 의존성 대기 시간에 더 크게 좌우돼요. 비교가 필요하다면 두 프레임워크로 같은 기능의 최소 서비스를 만들고 실제 트래픽 형태로 측정하는 편이 신뢰할 수 있습니다.

러닝커브는 어느 쪽이 낮나요?

Rust 비동기와 타입 시스템에 익숙하다면 두 프레임워크 모두 빠르게 적응합니다. 다만 tower의 계층 개념이 낯설면 axum이 처음에 어렵게 느껴질 수 있고, 앱 팩토리와 워커 모델이 낯설면 actix-web 쪽이 헷갈릴 수 있어요. 팀이 이미 알고 있는 개념이 어느 쪽에 가까운지로 판단하면 됩니다.

actix-web 서비스를 axum으로 옮길 수 있나요?

기술적으로는 가능하지만 라우팅 선언, 상태 공유, 미들웨어 등록, 테스트 방식이 모두 달라서 부분 이관보다는 경계를 나눠 순차적으로 옮기는 편이 안전합니다. 특히 종료 처리와 에러 응답 형식은 이관 전후 동작이 같아야 하므로 먼저 계약을 정리해 두는 것이 좋아요.

미들웨어 생태계는 어떻게 다른가요?

axum은 tower와 tower-http 계층을 그대로 쓰는 방식이라 다른 tower 기반 서비스와 자산을 공유할 수 있습니다. actix-web은 프레임워크에 내장된 미들웨어와 actix 계열 크레이트를 조합하는 방식이라, 제공되는 기능을 그대로 쓰는 데 강점이 있어요.

출처

프레임워크를 고른 뒤에는 배포 단계에서 확인할 항목이 남습니다. 종료 처리와 헬스 체크, 로그 수집, 런타임 스레드 설정처럼 두 프레임워크 모두에 공통으로 적용되는 내용은 Rust 서버 배포 체크리스트와 Tokio 런타임 운영 가이드에서 이어서 확인하실 수 있어요.