Tokio(토키오)는 Rust에서 비동기(async) 프로그래밍을 위한 런타임이다.
Actix Web은 액터 모델을 사용하지만, Axum은 Tokio 기반 비동기 모델을 사용한다.
그럼, "Tokio 모델이 대체 뭐고, 왜 중요한가?"를 자세히 알아보자.
Tokio는 Rust에서 비동기 동작을 처리하는 이벤트 기반(Runtime) 시스템이다.
✅ Rust에서 "Node.js"처럼 비동기 방식으로 동작하는 런타임이라고 보면 된다.
💡 Tokio = 비동기 실행 엔진 + 태스크 스케줄러
Tokio의 핵심 목표:
✅ 최소한의 스레드 사용으로 많은 요청 처리 (고성능)
✅ 안전하고 예측 가능한 동시성
✅ 네트워크, DB 연결, 타이머 등 다양한 비동기 기능 지원
동기 방식에서는 한 작업이 끝나야 다음 작업을 실행할 수 있음.
fn sync_function() {
let data = get_data(); // 3초 걸림 (DB 조회)
println!("Data: {}", data); // 데이터 출력
}
💥 단점:
get_data()가 실행될 동안 CPU가 아무것도 하지 않고 대기함 (비효율적) Tokio를 사용하면, 비동기 작업이 완료될 때까지 다른 작업을 수행할 수 있음.
async fn async_function() {
let data = get_data().await; // 3초 걸려도 다른 작업 가능
println!("Data: {}", data);
}
✅ 장점:
await을 사용하면 get_data()가 실행되는 동안 다른 요청을 처리 가능 Tokio의 핵심 구조는 이벤트 루프(Event Loop) + Future(태스크) 기반이다.
1️⃣ 사용자가 async 함수를 호출하면, Future가 생성됨
2️⃣ Tokio 런타임이 Future를 실행 큐(Task Queue)에 추가
3️⃣ 멀티스레드 환경에서 여러 Future를 동시에 실행
4️⃣ Future가 완료되면 결과를 반환 (await 사용)
💡 간단한 예제
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
println!("Start");
let task = async {
sleep(Duration::from_secs(2)).await;
println!("Task done");
};
tokio::spawn(task); // 비동기 태스크 실행
println!("End");
}
🔍 출력 결과:
Start
End
Task done // (2초 후 실행됨)
✅ tokio::spawn()을 사용하면, sleep(2초) 동안 대기하지 않고 다른 작업을 먼저 수행 가능!
Rust에서 async 함수는 Future<T> 타입을 반환함.
await으로 기다려야 함. async fn hello() -> String {
"Hello, world!".to_string()
}
#[tokio::main]
async fn main() {
let result = hello().await;
println!("{}", result);
}
Tokio에서 Future는 실행되지 않음 → Tokio 런타임이 Task로 관리해야 실행됨.
tokio::spawn()을 사용하면 백그라운드에서 실행되는 비동기 태스크가 됨. use tokio::task;
#[tokio::main]
async fn main() {
let handle = task::spawn(async {
"Hello from task"
});
let result = handle.await.unwrap();
println!("{}", result);
}
Rust 표준 라이브러리는 기본적으로 비동기 실행 환경이 없음.
Tokio는 Rust에서 비동기 작업을 실행하기 위한 런타임 역할을 함.
#[tokio::main] → 단일 스레드 또는 멀티 스레드 런타임 실행 #[tokio::test] → 비동기 테스트 실행 #[tokio::main]
async fn main() {
println!("Running with Tokio runtime!");
}
Tokio는 Reactor와 Executor를 조합한 구조를 가짐.
| 개념 | 설명 |
|---|---|
| Reactor | OS 비동기 I/O 이벤트 감지 (예: 네트워크, 파일, DB) |
| Executor | Future를 실행하는 태스크 스케줄러 |
🔍 Tokio 내부 동작 과정
1. Reactor가 "이벤트 발생"을 감지 (예: 네트워크 요청 수신)
2. Executor가 Future를 실행 큐(Task Queue)에 추가
3. Future가 실행되다가 I/O 작업이 필요하면 다시 Reactor에 넘김
4. I/O 작업이 완료되면 Reactor가 다시 Executor로 전달
5. Executor가 Future를 계속 실행하여 최종 결과 반환
| 비교 항목 | Tokio (Axum 기반) | Actix Web (Actor 모델) |
|---|---|---|
| 동작 방식 | Future 기반 비동기 실행 | Actor 기반 메시지 전달 |
| Rust 생태계 호환성 | ✅ 표준 (Tokio 기반) | ❌ Actix 독자적인 구조 |
| 성능 | 🚀 빠름 (하지만 Actix보다 약간 느림) | 🚀🚀 초고속 (Rust에서 가장 빠름) |
| 동시성 처리 | 멀티태스킹 기반 | 멀티 액터 기반 |
| 코드 직관성 | ✅ 쉬움 (async/await) | ❌ 어려움 (Actor 메시지 패싱) |
| 유지보수성 | ✅ 높음 | ❌ 상대적으로 낮음 |
| 개념 | 설명 |
|---|---|
| Tokio 모델 | 비동기(async/await) 기반 실행 모델 |
| Actix Web | 액터 모델 기반 동시성 처리 |
| Tokio의 장점 | Rust 표준, 유지보수 용이, 높은 확장성 |
| Actix Web의 장점 | 초고속 성능, 액터 기반 멀티태스킹 |
💡 결론:
🚀 Rust로 서버 개발을 처음 한다면 Axum(Tokio)부터 시작하는 것이 좋다!