Rust의 소유권과 라이프타임을 익혔다면, 이제 비동기 프로그래밍을 배울 차례입니다. Tokio 런타임과 async/await로 고성능 동시성 코드를 작성하세요.
After learning Rust's ownership and lifetimes, it's time to learn async programming. Write high-performance concurrent code with Tokio runtime and async/await.
async/await 기초 async/await Basics
// async 함수 정의
async fn fetch_data(url: &str) -> Result<String, Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
// Tokio 런타임에서 실행
#[tokio::main]
async fn main() {
let data = fetch_data("https://api.example.com").await;
println!("{:?}", data);
}
async fn fetch_data(url: &str) -> Result<String, Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
// Tokio 런타임에서 실행
#[tokio::main]
async fn main() {
let data = fetch_data("https://api.example.com").await;
println!("{:?}", data);
}
// Define async function
async fn fetch_data(url: &str) -> Result<String, Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
// Run in Tokio runtime
#[tokio::main]
async fn main() {
let data = fetch_data("https://api.example.com").await;
println!("{:?}", data);
}
async fn fetch_data(url: &str) -> Result<String, Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
// Run in Tokio runtime
#[tokio::main]
async fn main() {
let data = fetch_data("https://api.example.com").await;
println!("{:?}", data);
}
동시 실행 (Concurrent Execution) Concurrent Execution
use tokio::join;
async fn fetch_all() -> Result<(), Error> {
// 동시에 실행
let (users, posts, comments) = join!(
fetch_users(),
fetch_posts(),
fetch_comments()
);
println!("Users: {:?}", users?);
println!("Posts: {:?}", posts?);
Ok(())
}
// 여러 Future 중 하나만 필요할 때
use tokio::select;
async fn race() {
select! {
result = fast_api() => println!("Fast: {:?}", result),
result = slow_api() => println!("Slow: {:?}", result),
}
}
async fn fetch_all() -> Result<(), Error> {
// 동시에 실행
let (users, posts, comments) = join!(
fetch_users(),
fetch_posts(),
fetch_comments()
);
println!("Users: {:?}", users?);
println!("Posts: {:?}", posts?);
Ok(())
}
// 여러 Future 중 하나만 필요할 때
use tokio::select;
async fn race() {
select! {
result = fast_api() => println!("Fast: {:?}", result),
result = slow_api() => println!("Slow: {:?}", result),
}
}
use tokio::join;
async fn fetch_all() -> Result<(), Error> {
// Run concurrently
let (users, posts, comments) = join!(
fetch_users(),
fetch_posts(),
fetch_comments()
);
println!("Users: {:?}", users?);
println!("Posts: {:?}", posts?);
Ok(())
}
// When you need only one of multiple Futures
use tokio::select;
async fn race() {
select! {
result = fast_api() => println!("Fast: {:?}", result),
result = slow_api() => println!("Slow: {:?}", result),
}
}
async fn fetch_all() -> Result<(), Error> {
// Run concurrently
let (users, posts, comments) = join!(
fetch_users(),
fetch_posts(),
fetch_comments()
);
println!("Users: {:?}", users?);
println!("Posts: {:?}", posts?);
Ok(())
}
// When you need only one of multiple Futures
use tokio::select;
async fn race() {
select! {
result = fast_api() => println!("Fast: {:?}", result),
result = slow_api() => println!("Slow: {:?}", result),
}
}
Tokio 채널 Tokio Channels
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel(32);
// 송신자 태스크
tokio::spawn(async move {
for i in 0..10 {
tx.send(i).await.unwrap();
}
});
// 수신자
while let Some(msg) = rx.recv().await {
println!("Received: {}", msg);
}
}
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel(32);
// 송신자 태스크
tokio::spawn(async move {
for i in 0..10 {
tx.send(i).await.unwrap();
}
});
// 수신자
while let Some(msg) = rx.recv().await {
println!("Received: {}", msg);
}
}
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel(32);
// Sender task
tokio::spawn(async move {
for i in 0..10 {
tx.send(i).await.unwrap();
}
});
// Receiver
while let Some(msg) = rx.recv().await {
println!("Received: {}", msg);
}
}
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel(32);
// Sender task
tokio::spawn(async move {
for i in 0..10 {
tx.send(i).await.unwrap();
}
});
// Receiver
while let Some(msg) = rx.recv().await {
println!("Received: {}", msg);
}
}
에러 처리 패턴 Error Handling Patterns
use anyhow::{Result, Context};
async fn process() -> Result<()> {
let config = load_config().await
.context("설정 파일 로드 실패")?;
let conn = connect_db(&config.db_url).await
.context("데이터베이스 연결 실패")?;
Ok(())
}
// thiserror로 커스텀 에러
use thiserror::Error;
#[derive(Error, Debug)]
enum ApiError {
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("Not found: {0}")]
NotFound(String),
}
async fn process() -> Result<()> {
let config = load_config().await
.context("설정 파일 로드 실패")?;
let conn = connect_db(&config.db_url).await
.context("데이터베이스 연결 실패")?;
Ok(())
}
// thiserror로 커스텀 에러
use thiserror::Error;
#[derive(Error, Debug)]
enum ApiError {
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("Not found: {0}")]
NotFound(String),
}
use anyhow::{Result, Context};
async fn process() -> Result<()> {
let config = load_config().await
.context("Failed to load config")?;
let conn = connect_db(&config.db_url).await
.context("Failed to connect database")?;
Ok(())
}
// Custom error with thiserror
use thiserror::Error;
#[derive(Error, Debug)]
enum ApiError {
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("Not found: {0}")]
NotFound(String),
}
async fn process() -> Result<()> {
let config = load_config().await
.context("Failed to load config")?;
let conn = connect_db(&config.db_url).await
.context("Failed to connect database")?;
Ok(())
}
// Custom error with thiserror
use thiserror::Error;
#[derive(Error, Debug)]
enum ApiError {
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("Not found: {0}")]
NotFound(String),
}
💡 Rust 비동기 에코시스템 💡 Rust Async Ecosystem
- tokio: 가장 널리 쓰이는 비동기 런타임
- tokio: Most widely used async runtime
- reqwest: HTTP 클라이언트
- reqwest: HTTP client
- sqlx: 비동기 SQL 쿼리
- sqlx: Async SQL queries
- axum: Tokio 기반 웹 프레임워크
- axum: Tokio-based web framework
⚠️ async 블로킹 주의 ⚠️ Beware of Blocking in async
비동기 함수 안에서 std::thread::sleep 같은 블로킹 호출을 하면 안 됩니다.
대신 tokio::time::sleep을 사용하세요.
Don't use blocking calls like std::thread::sleep inside async functions.
Use tokio::time::sleep instead.
