Rust Async/Await ์๋ฒฝ ๊ฐ์ด๋: Tokio, Futures, ๋น๋๊ธฐ ๋์์ฑ ์ฌ์ธต ๋ถ์
Rust์ Async/Await๋ฅผ Tokio ๋ฐํ์, Future ํธ๋ ์ดํธ, ํ์คํฌ ์คํฐ, ๊ตฌ์กฐํ๋ ๋์์ฑ, ์ค์ ํจํด๊น์ง ๊น์ด ์๊ฒ ๋ถ์ํฉ๋๋ค.

Rust๋ ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ๊ณผ ์ฑ๋ฅ์ ๋์์ ์ ๊ณตํ๋ ์์คํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก, ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์์๋ ๋ ํนํ ์ ๊ทผ ๋ฐฉ์์ ์ทจํฉ๋๋ค. JavaScript์ Promise๋ Python์ asyncio์ ๋ฌ๋ฆฌ, Rust์ async/await๋ ์ ๋ก ์ฝ์คํธ ์ถ์ํ ์์น์ ๋ฐ๋ฅด๋ฉฐ, ๋ฐํ์ ์ค๋ฒํค๋ ์์ด ๊ณ ์ฑ๋ฅ ๋น๋๊ธฐ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ๋ณธ ๊ธ์์๋ Rust์ Future ํธ๋ ์ดํธ๋ถํฐ Tokio ๋ฐํ์, ๊ทธ๋ฆฌ๊ณ ์ค์ ๋์์ฑ ํจํด๊น์ง ์ฌ์ธต์ ์ผ๋ก ์ดํด๋ด ๋๋ค.
Rust์ Future๋ ์์ฑ ์์ ์ ์คํ๋์ง ์๊ณ , .await๋ฅผ ํธ์ถํ๊ฑฐ๋ ๋ฐํ์์ ์ํด poll๋ ๋๊น์ง ์๋ฌด ์์
๋ ์ํํ์ง ์์ต๋๋ค. ์ด๋ JavaScript์ Promise๊ฐ ์์ฑ ์ฆ์ ์คํ๋๋ ๊ฒ๊ณผ ๊ทผ๋ณธ์ ์ผ๋ก ๋ค๋ฅธ ๋์ ๋ฐฉ์์
๋๋ค.
Rust Future์ ๋ค๋ฅธ ์ธ์ด์ ์ฐจ์ด์
Rust์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ Future ํธ๋ ์ดํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค. ์ด ํธ๋ ์ดํธ๋ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ์๋์ด ์์ผ๋ฉฐ, ๋ชจ๋ ๋น๋๊ธฐ ์์
์ ๊ทผ๊ฐ์ด ๋ฉ๋๋ค.
// core::future::Future
trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}poll ๋ฉ์๋๋ Future์ ์ํ๋ฅผ ํ์ธํ๊ณ , ์๋ฃ๋์์ผ๋ฉด Poll::Ready(value)๋ฅผ, ์์ง ์งํ ์ค์ด๋ฉด Poll::Pending์ ๋ฐํํฉ๋๋ค. ์ค์ํ ์ ์ Rust์ Future๊ฐ pull ๊ธฐ๋ฐ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ค๋ ๊ฒ์
๋๋ค. ๋ฐํ์์ด Future๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก pollํ์ฌ ์งํ ์ํฉ์ ํ์ธํ๋ฉฐ, Future๊ฐ ์ค๋น๋์ง ์์์ ๋๋ waker๋ฅผ ๋ฑ๋กํ์ฌ ๋์ค์ ๋ค์ poll๋ ์ ์๋๋ก ํฉ๋๋ค.
JavaScript๋ Python์ ๋น๋๊ธฐ ๋ชจ๋ธ๊ณผ ๋น๊ตํ์ ๋ ์ฃผ์ ์ฐจ์ด์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. ์ฒซ์งธ, Rust Future๋ ํ ํ ๋น ์์ด ์คํ์ ๋ฐฐ์น๋ ์ ์์ด ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ์ด ๋์ต๋๋ค. ๋์งธ, ์ปดํ์ผ๋ฌ๊ฐ async ํจ์๋ฅผ ์ํ ๋จธ์ ์ผ๋ก ๋ณํํ์ฌ ๋ฐํ์ ์ค๋ฒํค๋๋ฅผ ์ต์ํํฉ๋๋ค. ์ ์งธ, ๋ฐํ์์ด ์ธ์ด์ ๋ด์ฅ๋์ด ์์ง ์์ Tokio, async-std ๋ฑ ์ฌ๋ฌ ๋ฐํ์ ์ค ์ ํํ ์ ์์ต๋๋ค.
Tokio ๋ฐํ์ ์ค์
Tokio๋ Rust ์ํ๊ณ์์ ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๋น๋๊ธฐ ๋ฐํ์์ ๋๋ค. ํ๋ก๋์ ํ๊ฒฝ์์ ๊ฒ์ฆ๋ ์์ ์ฑ๊ณผ ํ๋ถํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ฉฐ, ๋ฉํฐ์ค๋ ๋ ์์ ์ค์ผ์ค๋ง, ํ์ด๋จธ, I/O ๋๋ผ์ด๋ฒ ๋ฑ์ ํฌํจํฉ๋๋ค.
# Cargo.toml
[dependencies]
tokio = { version = "2", features = ["rt-multi-thread", "macros", "net", "time"] }rt-multi-thread ํผ์ฒ๋ ์ฌ๋ฌ OS ์ค๋ ๋์์ ํ์คํฌ๋ฅผ ์คํํ๋ ์์
์คํธ๋ง ์ค์ผ์ค๋ฌ๋ฅผ ํ์ฑํํฉ๋๋ค. macros ํผ์ฒ๋ #[tokio::main]๊ณผ #[tokio::test] ๊ฐ์ ํธ์ ๋งคํฌ๋ก๋ฅผ ์ ๊ณตํ๋ฉฐ, net๊ณผ time์ ๊ฐ๊ฐ ๋คํธ์ํฌ I/O์ ํ์ด๋จธ ๊ธฐ๋ฅ์ ํ์ฑํํฉ๋๋ค.
#[tokio::main]
async fn main() {
let result = fetch_data().await;
println!("Got: {result}");
}
async fn fetch_data() -> String {
// Simulate async I/O
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
String::from("data loaded")
}#[tokio::main] ๋งคํฌ๋ก๋ Tokio ๋ฐํ์์ ์ด๊ธฐํํ๊ณ async main ํจ์๋ฅผ ์คํํฉ๋๋ค. ๋ด๋ถ์ ์ผ๋ก ์ด ๋งคํฌ๋ก๋ Runtime::new()๋ฅผ ํธ์ถํ๊ณ block_on์ผ๋ก Future๋ฅผ ์๋ฃ๊น์ง ์คํํ๋ ์ฝ๋๋ก ํ์ฅ๋ฉ๋๋ค.
ํ์คํฌ ์คํฐ๊ณผ ๊ตฌ์กฐํ๋ ๋์์ฑ
๋จ์ผ Future๋ฅผ ์์ฐจ์ ์ผ๋ก ์คํํ๋ ๊ฒ๋ง์ผ๋ก๋ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ ํ ์ด์ ์ ์ป์ ์ ์์ต๋๋ค. tokio::spawn์ ์ฌ์ฉํ๋ฉด ๋
๋ฆฝ์ ์ธ ํ์คํฌ๋ฅผ ์์ฑํ์ฌ ๋์์ ์คํํ ์ ์์ต๋๋ค.
use tokio::task::JoinHandle;
#[tokio::main]
async fn main() {
// Spawn two independent tasks
let handle_a: JoinHandle<u32> = tokio::spawn(async {
expensive_computation("dataset_a").await
});
let handle_b: JoinHandle<u32> = tokio::spawn(async {
expensive_computation("dataset_b").await
});
// Await both results
let (result_a, result_b) = (
handle_a.await.expect("task A panicked"),
handle_b.await.expect("task B panicked"),
);
println!("Results: {result_a}, {result_b}");
}
async fn expensive_computation(name: &str) -> u32 {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
println!("{name} done");
42
}JoinHandle์ ์คํฐ๋ ํ์คํฌ์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ํธ๋ค์
๋๋ค. ํ์คํฌ๊ฐ ํจ๋ํ๋ฉด JoinError๋ฅผ ๋ฐํํ๋ฏ๋ก ์ ์ ํ ์๋ฌ ์ฒ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค.
tokio::spawn์ ์ ๋ฌ๋๋ Future๋ Send์ 'static ํธ๋ ์ดํธ ๋ฐ์ด๋๋ฅผ ๋ง์กฑํด์ผ ํฉ๋๋ค. ์ด๋ ํ์คํฌ๊ฐ ๋ค๋ฅธ ์ค๋ ๋๋ก ์ด๋ํ ์ ์๊ณ , ์๋ ์ค์ฝํ๋ณด๋ค ์ค๋ ์ด ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค. ๋ก์ปฌ ์ฐธ์กฐ๋ฅผ ์บก์ฒํด์ผ ํ๋ค๋ฉด tokio::task::spawn_local์ด๋ ์ค์ฝํ๋ ํ์คํฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ณ ๋ คํด์ผ ํฉ๋๋ค.
tokio::join!๊ณผ tokio::select!๋ก ์ฌ๋ฌ Future ๊ฒฐํฉํ๊ธฐ
์ฌ๋ฌ ๋น๋๊ธฐ ์์
์ ์กฐํฉํ๋ ๋ฐฉ๋ฒ์ ๋ฐ๋ผ ํ๋ก๊ทธ๋จ์ ๋์์ด ํฌ๊ฒ ๋ฌ๋ผ์ง๋๋ค. tokio::join!์ ๋ชจ๋ Future๋ฅผ ๋์์ ์คํํ๊ณ ์ ๋ถ ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค.
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
// All three run concurrently, total time ~200ms (not 600ms)
let (users, orders, inventory) = tokio::join!(
fetch_users(),
fetch_orders(),
fetch_inventory()
);
println!("Users: {}, Orders: {}, Stock: {}", users.len(), orders.len(), inventory);
}
async fn fetch_users() -> Vec<String> {
sleep(Duration::from_millis(200)).await;
vec!["Alice".into(), "Bob".into()]
}
async fn fetch_orders() -> Vec<String> {
sleep(Duration::from_millis(150)).await;
vec!["ORD-001".into()]
}
async fn fetch_inventory() -> u32 {
sleep(Duration::from_millis(100)).await;
84
}์ ์์ ์์ ์ธ ๊ฐ์ fetch ํจ์๊ฐ ๋์์ ์คํ๋์ด ์ด ์์ ์๊ฐ์ ๊ฐ์ฅ ์ค๋ ๊ฑธ๋ฆฌ๋ ์์ ์ธ 200ms์ ์๋ ดํฉ๋๋ค. ์์ฐจ ์คํ์ด์๋ค๋ฉด 450ms๊ฐ ์์๋์์ ๊ฒ์ ๋๋ค.
๋ฐ๋ฉด tokio::select!๋ ์ฌ๋ฌ Future ์ค ๊ฐ์ฅ ๋จผ์ ์๋ฃ๋๋ ํ๋๋ง ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ ํ์์์ ๊ตฌํ, ์ทจ์, ๋๋ ์ฌ๋ฌ ์์ค ์ค ๊ฐ์ฅ ๋น ๋ฅธ ์๋ต์ ์ ํํ ๋ ์ ์ฉํฉ๋๋ค.
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
tokio::select! {
val = fetch_from_cache() => {
println!("Cache hit: {val}");
}
val = fetch_from_database() => {
println!("DB result: {val}");
}
}
}
async fn fetch_from_cache() -> String {
sleep(Duration::from_millis(5)).await;
"cached_value".into()
}
async fn fetch_from_database() -> String {
sleep(Duration::from_millis(50)).await;
"db_value".into()
}์บ์ ์กฐํ๊ฐ 5ms๋ก ๋ ๋น ๋ฅด๋ฏ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๋ ์ทจ์๋๊ณ ์บ์ ๊ฐ์ด ์ฌ์ฉ๋ฉ๋๋ค. select!๋ ์ ํ๋์ง ์์ ๋ธ๋์น์ Future๋ฅผ ๋๋กญํ๋ฏ๋ก ๋ฆฌ์์ค ์ ๋ฆฌ๊ฐ ์๋์ผ๋ก ์ด๋ฃจ์ด์ง๋๋ค.
Rust ๋ฉด์ ์ค๋น๊ฐ ๋์ จ๋์?
์ธํฐ๋ํฐ๋ธ ์๋ฎฌ๋ ์ดํฐ, flashcards, ๊ธฐ์ ํ ์คํธ๋ก ์ฐ์ตํ์ธ์.
๋น๋๊ธฐ Rust์์์ ์๋ฌ ์ฒ๋ฆฌ
๋น๋๊ธฐ ์ฝ๋์์์ ์๋ฌ ์ฒ๋ฆฌ๋ ๋๊ธฐ ์ฝ๋์ ํฌ๊ฒ ๋ค๋ฅด์ง ์์ต๋๋ค. Result ํ์
๊ณผ ? ์ฐ์ฐ์๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ์ปค์คํ
์๋ฌ ํ์
์ ์ ์ํ์ฌ ๋ค์ํ ์๋ฌ ์์ค๋ฅผ ํตํฉํ ์ ์์ต๋๋ค.
use std::io;
#[derive(Debug)]
enum AppError {
Network(reqwest::Error),
Parse(serde_json::Error),
Io(io::Error),
}
async fn load_config(url: &str) -> Result<Config, AppError> {
let response = reqwest::get(url)
.await
.map_err(AppError::Network)?;
let text = response.text()
.await
.map_err(AppError::Network)?;
let config: Config = serde_json::from_str(&text)
.map_err(AppError::Parse)?;
Ok(config)
}
#[derive(serde::Deserialize)]
struct Config {
db_url: String,
port: u16,
}์ค๋ฌด์์๋ thiserror ํฌ๋ ์ดํธ๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฌ ํ์
์ ๋ ๊ฐ๊ฒฐํ๊ฒ ์ ์ํ๊ฑฐ๋, anyhow ํฌ๋ ์ดํธ๋ก ์๋ฌ๋ฅผ ๋์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ๋ง์ด ์ฌ์ฉํฉ๋๋ค. ํนํ ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋์์๋ anyhow::Result๊ฐ ํธ๋ฆฌํ๊ณ , ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฝ๋์์๋ ๋ช
์์ ์ธ ์๋ฌ ํ์
์ด ์ ํธ๋ฉ๋๋ค.
ํ์คํฌ ๊ฐ ๋น๋๊ธฐ ํต์ ์ฑ๋
๋์์ ์คํ๋๋ ํ์คํฌ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ์ ๋ฌํ๋ ค๋ฉด ์ฑ๋์ ์ฌ์ฉํฉ๋๋ค. Tokio๋ ์ฌ๋ฌ ์ข
๋ฅ์ ์ฑ๋์ ์ ๊ณตํ๋ฉฐ, ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๊ฒ์ mpsc(๋ค์ค ์์ฐ์, ๋จ์ผ ์๋น์) ์ฑ๋์
๋๋ค.
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
// Bounded channel with capacity 32
let (tx, mut rx) = mpsc::channel::<String>(32);
// Producer task
let producer = tokio::spawn(async move {
for i in 0..5 {
tx.send(format!("message-{i}")).await.unwrap();
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
}
// tx dropped here, closing the channel
});
// Consumer reads until channel closes
while let Some(msg) = rx.recv().await {
println!("Received: {msg}");
}
producer.await.unwrap();
}์ํฉ์ ๋ฐ๋ผ ์ ์ ํ ๋์์ฑ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ์ ํํด์ผ ํฉ๋๋ค. ์ฌ๋ฌ ์์ฐ์์์ ํ ์๋น์๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ๋๋ mpsc๋ฅผ, ๋ธ๋ก๋์บ์คํธ๊ฐ ํ์ํ๋ฉด broadcast๋ฅผ, ํ ๋ฒ๋ง ๊ฐ์ ์ ๋ฌํ๋ค๋ฉด oneshot์, ์ต์ ๊ฐ๋ง ์ค์ํ๋ค๋ฉด watch ์ฑ๋์ ์ฌ์ฉํฉ๋๋ค. ๊ณต์ ์ํ๊ฐ ํ์ํ๋ฉด RwLock์ด๋ Mutex๋ฅผ ๊ณ ๋ คํ๋, ๊ฐ๋ฅํ๋ฉด ์ฑ๋์ ํตํ ๋ฉ์์ง ์ ๋ฌ ๋ฐฉ์์ ์ ํธํฉ๋๋ค.
์ค์ ํจํด: ์๋ ์ ํ์ด ์๋ HTTP ์์ฒญ
์ค์ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ์ธ๋ถ API ํธ์ถ ์ ๋์ ์์ฒญ ์๋ฅผ ์ ํํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. Semaphore๋ฅผ ์ฌ์ฉํ๋ฉด ๋์ ์คํ ํ์คํฌ ์๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ ์ดํ ์ ์์ต๋๋ค.
use std::sync::Arc;
use tokio::sync::Semaphore;
async fn fetch_all(urls: Vec<String>, max_concurrent: usize) -> Vec<Result<String, String>> {
let semaphore = Arc::new(Semaphore::new(max_concurrent));
let mut handles = Vec::new();
for url in urls {
let sem = Arc::clone(&semaphore);
let handle = tokio::spawn(async move {
// Acquire permit before making request
let _permit = sem.acquire().await.unwrap();
reqwest::get(&url)
.await
.map(|r| r.status().to_string())
.map_err(|e| e.to_string())
// permit dropped here, allowing next task to proceed
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.await.unwrap());
}
results
}์ธ๋งํฌ์ด permit์ _permit ๋ณ์๊ฐ ์ค์ฝํ๋ฅผ ๋ฒ์ด๋ ๋ ์๋์ผ๋ก ๋ฐํ๋ฉ๋๋ค. ์ด ํจํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ, ํ์ผ ๋์คํฌ๋ฆฝํฐ ์ ํ, API ๋ ์ดํธ ๋ฆฌ๋ฐ ์ค์ ๋ฑ ๋ค์ํ ์ํฉ์์ ํ์ฉ๋ฉ๋๋ค.
Pin๊ณผ Unpin: ๋น๋๊ธฐ Rust์ ํ์ํ ์ด์
Future ํธ๋ ์ดํธ์ poll ๋ฉ์๋ ์๊ทธ๋์ฒ์์ Pin<&mut Self>๊ฐ ๋ฑ์ฅํฉ๋๋ค. ์ด๋ ์๊ธฐ ์ฐธ์กฐ(self-referential) ๊ตฌ์กฐ์ฒด์ ๊ด๋ จ์ด ์์ต๋๋ค. async ๋ธ๋ก์ด ์ปดํ์ผ๋ฌ์ ์ํด ์ํ ๋จธ์ ์ผ๋ก ๋ณํ๋ ๋, ์ง์ญ ๋ณ์์ ๊ทธ์ ๋ํ ์ฐธ์กฐ๊ฐ ๊ฐ์ ๊ตฌ์กฐ์ฒด ์์ ์ ์ฅ๋ ์ ์์ต๋๋ค.
๋ง์ฝ ์ด ๊ตฌ์กฐ์ฒด๊ฐ ๋ฉ๋ชจ๋ฆฌ ๋ด์์ ์ด๋ํ๋ฉด, ๋ด๋ถ ์ฐธ์กฐ๊ฐ ๋ฌดํจํ๋์ด ์ ์๋์ง ์์ ๋์์ด ๋ฐ์ํฉ๋๋ค. Pin์ ๊ฐ์ด ๋ฉ๋ชจ๋ฆฌ์์ ์ด๋ํ์ง ์์์ ๋ณด์ฅํ๋ ํ์
์์คํ
์์ค์ ์ฅ์น์
๋๋ค. ๋๋ถ๋ถ์ ํ์
์ Unpin ํธ๋ ์ดํธ๋ฅผ ์๋์ผ๋ก ๊ตฌํํ์ฌ ์ด๋์ด ์์ ํจ์ ๋ํ๋ด๋ฉฐ, ์ผ๋ฐ์ ์ธ ์ฌ์ฉ์์๋ Pin์ ์ง์ ๋ค๋ฃฐ ํ์๊ฐ ๊ฑฐ์ ์์ต๋๋ค.
์ ์์ค Future ๊ตฌํ์ด๋ ์ปค์คํ combinator๋ฅผ ์์ฑํ ๋ Pin์ ์ดํดํด์ผ ํ์ง๋ง, ์ผ๋ฐ์ ์ธ async/await ์ฌ์ฉ์์๋ ์ปดํ์ผ๋ฌ๊ฐ ๋ชจ๋ ๊ฒ์ ์ฒ๋ฆฌํฉ๋๋ค.
์ฑ๋ฅ ํน์ฑ๊ณผ ๋น๋๊ธฐ ์ฌ์ฉ ์์
๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ด ํญ์ ์ต์ ์ ์ ํ์ ์๋๋๋ค. CPU ๋ฐ์ด๋ ์์ ์๋ ๋น๋๊ธฐ๊ฐ ์คํ๋ ค ์ค๋ฒํค๋๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค. ๋น๋๊ธฐ๋ I/O ๋ฐ์ด๋ ์์ , ํนํ ๋ง์ ๋์ ์ฐ๊ฒฐ์ ์ฒ๋ฆฌํ๋ ๋คํธ์ํฌ ์๋น์ค์์ ์ง๊ฐ๋ฅผ ๋ฐํํฉ๋๋ค.
#[tokio::main]
async fn main() {
let hash = tokio::task::spawn_blocking(|| {
// CPU-intensive work runs on a blocking thread
compute_hash(b"large dataset")
})
.await
.unwrap();
println!("Hash: {hash}");
}
fn compute_hash(data: &[u8]) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
format!("{:x}", hasher.finish())
}spawn_blocking์ CPU ์ง์ฝ์ ์ธ ์์
์ ๋ณ๋์ ์ค๋ ๋ ํ์์ ์คํํ์ฌ ๋น๋๊ธฐ ๋ฐํ์์ ๋ธ๋กํนํ์ง ์๋๋ก ํฉ๋๋ค. ํ์ผ ์์คํ
์์
, ์ํธํ ์ฐ์ฐ, ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๋ฑ ์ค๋ ๊ฑธ๋ฆฌ๋ ๋๊ธฐ ์์
์ ์ ํฉํฉ๋๋ค.
๊ฒฐ๋ก
Rust์ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ์ฒ์์๋ ๋ณต์กํด ๋ณด์ผ ์ ์์ง๋ง, ๊ทธ ์ค๊ณ ์ฒ ํ์ ์ดํดํ๋ฉด ๊ฐ๋ ฅํ๊ณ ํจ์จ์ ์ธ ๋์์ฑ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ํต์ฌ ๋ด์ฉ์ ์ ๋ฆฌํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- Future ํธ๋ ์ดํธ๋ Rust ๋น๋๊ธฐ์ ๊ธฐ๋ฐ์ด๋ฉฐ, poll ๊ธฐ๋ฐ์ ๊ฒ์ผ๋ฅธ ์คํ ๋ชจ๋ธ์ ์ฌ์ฉํฉ๋๋ค
- Tokio ๋ฐํ์์ ๋ฉํฐ์ค๋ ๋ ์ค์ผ์ค๋ง, ํ์ด๋จธ, I/O ๋๋ผ์ด๋ฒ๋ฅผ ์ ๊ณตํ๋ ํ๋ก๋์ ๋ ๋ ์๋ฃจ์ ์ ๋๋ค
- tokio::spawn์ผ๋ก ๋ ๋ฆฝ์ ์ธ ํ์คํฌ๋ฅผ ์์ฑํ๊ณ , **join!**๊ณผ **select!**๋ก ์ฌ๋ฌ Future๋ฅผ ์กฐํฉํฉ๋๋ค
- ์ฑ๋์ ํตํด ํ์คํฌ ๊ฐ ์์ ํ ํต์ ์ด ๊ฐ๋ฅํ๋ฉฐ, Semaphore๋ก ๋์์ฑ์ ์ ์ดํฉ๋๋ค
- CPU ๋ฐ์ด๋ ์์ ์ spawn_blocking์ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ ๋ฐํ์์ ๋ธ๋กํนํ์ง ์๋๋ก ํด์ผ ํฉ๋๋ค
- Pin์ ์๊ธฐ ์ฐธ์กฐ ๊ตฌ์กฐ์ฒด์ ์์ ์ฑ์ ๋ณด์ฅํ์ง๋ง, ์ผ๋ฐ์ ์ธ ์ฌ์ฉ์์๋ ์ง์ ๋ค๋ฃฐ ํ์๊ฐ ์์ต๋๋ค
Rust์ ๋น๋๊ธฐ ์ํ๊ณ๋ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ผ๋ฉฐ, Tokio๋ฅผ ๋น๋กฏํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋์ฑ ์ฌ์ฉํ๊ธฐ ์ฌ์์ง๊ณ ์์ต๋๋ค. ํ์ ์์คํ ์ ๋์์ผ๋ก ๋ฐ์ดํฐ ๋ ์ด์ค์ ๊ฐ์ ๋์์ฑ ๋ฒ๊ทธ๋ฅผ ์ปดํ์ผ ํ์์ ๋ฐฉ์งํ ์ ์๋ค๋ ์ ์ด Rust ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ ๋๋ค.
์ฐ์ต์ ์์ํ์ธ์!
๋ฉด์ ์๋ฎฌ๋ ์ดํฐ์ ๊ธฐ์ ํ ์คํธ๋ก ์ง์์ ํ ์คํธํ์ธ์.
ํ๊ทธ
๊ณต์
๊ด๋ จ ๊ธฐ์ฌ

Rust 2026 ํธ๋ ์ดํธ์ ์ ๋ค๋ฆญ ์๋ฒฝ ๊ฐ์ด๋: ์ ์บ์คํ , AsyncFn, ๊ณ ๊ธ ๋ฉด์ ๋๋น
Rust 2024 Edition์ ํธ๋ ์ดํธ ์ค๋ธ์ ํธ ์ ์บ์คํ , AsyncFn ํธ๋ ์ดํธ, RPITIT ๋ฑ ์ ๊ธฐ๋ฅ์ ์ปดํ์ผ ๊ฐ๋ฅํ ์ฝ๋์ ํจ๊ป ์ค๋ช ํฉ๋๋ค. 2026๋ ๊ธฐ์ ๋ฉด์ ์์ ์์ฃผ ์ถ์ ๋๋ ๊ณ ๊ธ ์ง๋ฌธ๋ ๋ค๋ฃน๋๋ค.

Rust ์น ํ๋ ์์ํฌ ๋น๊ต: Actix Web vs Axum โ 2026๋ ๊ฐ์ด๋์ ๋ฉด์ ๋๋น
Actix Web 4.13๊ณผ Axum 0.8์ ์ํคํ ์ฒ, ์ฑ๋ฅ, ๋ฏธ๋ค์จ์ด ์ค๊ณ๋ฅผ ๋น๊ตํฉ๋๋ค. 2026๋ Rust ๋ฐฑ์๋ ๋ฉด์ ์์ ์์ฃผ ๋์ค๋ ์ง๋ฌธ๊ณผ ๋ชจ๋ฒ ๋ต๋ณ๋ ํจ๊ป ๋ค๋ฃน๋๋ค.

Rust ์์ ๊ถ๊ณผ ๋น๋ฆผ: ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ์ ํต์ฌ ์๋ฆฌ ์๋ฒฝ ํด์ค
Rust์ ์์ ๊ถ(Ownership), ๋น๋ฆผ(Borrowing), ๋ผ์ดํํ์(Lifetime) ๊ฐ๋ ์ ์ฒด๊ณ์ ์ผ๋ก ๋ถ์ํฉ๋๋ค. ๋น๋ฆผ ๊ฒ์ฌ๊ธฐ์ ๋์ ์๋ฆฌ์ ์ค๋ฌด ํจํด, ๊ธฐ์ ๋ฉด์ ๋๋น ํต์ฌ ํฌ์ธํธ๋ฅผ ๋ค๋ฃน๋๋ค.