- Published on
Rust 생태계 2026 완벽 가이드 - Rust 1.85+ · tokio · axum · actix-web · sqlx · bevy · tauri · leptos · dioxus · embassy · cargo 심층 분석
- Authors

- Name
- Youngju Kim
- @fjvbn20031
들어가며 — 2026년 5월, Rust는 "시스템 언어 표준"으로 굳어졌다
2024년 말 rust-for-linux 패치가 메인라인에 머지되면서 Rust는 "유망한 언어"에서 "리눅스 커널이 채택한 언어"로 신분이 바뀌었다. 2026년 5월 현재, 그 변화는 더 또렷하다. Cloudflare의 Pingora(Rust로 작성된 차세대 프록시)는 하루 40조 건이 넘는 요청을 처리하며, Discord의 Read States 서비스는 Rust 마이그레이션 이후 99.99% 안정성과 GC 폴 제거를 동시에 달성했고, Dropbox의 Magic Pocket 스토리지 엔진, Figma의 멀티플레이어 HTTP 레이어, Mozilla의 Servo(2024년 Linux Foundation으로 이관)가 Rust 기반으로 굳어졌다.
이 글은 마케팅 매트릭스가 아니라 "2026년 5월 현재 Rust 프로덕션 스택은 어떻게 구성되는가"를 정직하게 짚는다. 1.85 에디션의 변화부터 tokio/axum/sqlx의 실제 코드, Tauri 2의 모바일 GA, Bevy 0.15의 ECS, Embassy의 no_std async, 그리고 한국·일본의 채택 사례까지 비교한다.
Rust 2026 스택 — 8개 레이어로 분해하기
먼저 큰 그림이다. 2026년 표준 Rust 스택은 다음 8개 레이어로 나뉜다.
- 언어/툴체인: rustc, cargo, rustup, rust-analyzer
- async 런타임: tokio가 사실상 표준, async-std/smol/embassy가 특수 영역
- 웹/네트워크: axum, actix-web 5, poem, rocket 0.5, warp, salvo, loco-rs
- 데이터/ORM: sqlx, sea-orm, diesel 2.x, rbatis, sqlite-rs
- 에러/관측: anyhow, thiserror, eyre, snafu, miette, tracing
- GUI/렌더: Tauri 2, egui, Iced, Slint, Dioxus, Yew, Leptos, Sycamore
- 시스템/임베디드/게임: Embassy, RTIC, embedded-hal, Bevy, winit, gtk-rs
- 도구 생태계: cargo-nextest, cargo-mutants, cargo-deny, cargo-audit, cross, binstall
각 레이어를 한두 크레이트가 담당하면 됐던 시대는 끝났다. 지금은 "axum + tokio + sqlx + tracing"이 사실상 백엔드 표준 조합이고, GUI는 "Tauri 2 + Leptos/Dioxus" 또는 "egui/Slint"로 갈라진다. 아래부터 한 레이어씩 본다.
Rust 1.85와 2024 에디션 — async fn in trait, let-else, GAT 안정화
Rust 1.85는 2024 에디션의 첫 번째 안정 릴리스다. 핵심 변화는 다음과 같다.
- async fn in trait (RPITIT): 더 이상
async-trait매크로가 필수가 아니다. dyn-safe 제한은 여전히 있지만, 정적 디스패치 시나리오에서 매크로 없이 작성 가능. - GAT(Generic Associated Types): 1.65에서 안정화됐지만 2024 에디션에서 다양한 lints가 정리됐다.
- let-else 패턴: 1.65부터 사용 가능. 2024 에디션에서
if let임시 수명(temporary lifetime) 규칙이 직관적으로 변경됐다. - never type fallback:
!타입의 폴백이()에서!로 바뀌어 더 엄격해졌다. 일부 코드는 명시적 타입 어노테이션 필요. - raw lifetime/
r#: 식별자 충돌 해소.
대표적인 1.85 스타일 트레이트 정의는 다음과 같다.
use std::error::Error;
// 2024 에디션: async fn in trait, 매크로 불필요
pub trait UserRepo {
async fn find_by_id(&self, id: u64) -> Result<Option<User>, Box<dyn Error + Send + Sync>>;
async fn create(&self, user: NewUser) -> Result<User, Box<dyn Error + Send + Sync>>;
}
pub struct User { pub id: u64, pub email: String }
pub struct NewUser { pub email: String }
pub struct PgUserRepo { pool: sqlx::PgPool }
impl UserRepo for PgUserRepo {
async fn find_by_id(&self, id: u64) -> Result<Option<User>, Box<dyn Error + Send + Sync>> {
let row = sqlx::query_as::<_, (i64, String)>("SELECT id, email FROM users WHERE id = $1")
.bind(id as i64)
.fetch_optional(&self.pool)
.await?;
Ok(row.map(|(id, email)| User { id: id as u64, email }))
}
async fn create(&self, user: NewUser) -> Result<User, Box<dyn Error + Send + Sync>> {
let row: (i64,) = sqlx::query_as("INSERT INTO users(email) VALUES ($1) RETURNING id")
.bind(&user.email)
.fetch_one(&self.pool)
.await?;
Ok(User { id: row.0 as u64, email: user.email })
}
}
tokio 런타임 내부 — 워크 스틸링, LIFO 슬롯, 협력적 스케줄링
tokio는 멀티스레드(M:N) 워크 스틸링 스케줄러를 쓴다. 핵심 메커니즘은 세 가지다.
- 로컬 큐 + 글로벌 큐: 각 워커는 자신의 로컬 큐(LIFO 슬롯 1개 + FIFO 256개)를 가진다. 가득 차면 절반을 글로벌 큐로 옮긴다.
- LIFO 슬롯: 가장 최근에
wake()된 태스크를 다음 폴 대상으로 우선 처리해 캐시 지역성을 유지한다. - 워크 스틸링: 자기 큐가 비면 무작위 워커의 큐 절반을 훔쳐온다.
tokio 1.40 이후로는 협력적 스케줄링(cooperative scheduling) 예산 기본값이 128로 줄었다. 즉, 한 태스크가 128번 폴(poll) 되면 자동으로 양보(yield) 한다. 무한 루프에서 tokio::task::yield_now().await를 명시할 필요가 사실상 사라졌다.
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
#[tokio::main(flavor = "multi_thread", worker_threads = 8)]
async fn main() {
let (tx, mut rx) = mpsc::channel::<u64>(1024);
// 프로듀서 100개
for i in 0..100u64 {
let tx = tx.clone();
tokio::spawn(async move {
for j in 0..1000u64 {
let _ = tx.send(i * 1_000 + j).await;
if j % 100 == 0 { sleep(Duration::from_micros(10)).await; }
}
});
}
drop(tx);
// 컨슈머: 단일 태스크
let mut count = 0u64;
while let Some(_v) = rx.recv().await { count += 1; }
println!("received: {count}");
}
#[tokio::main]은 단순한 매크로 설탕이고, 실제로는 tokio::runtime::Builder::new_multi_thread()를 부른다. 라이브러리에서는 매크로 대신 직접 빌더를 쓰는 게 일반적이다.
async 런타임 비교 — tokio vs async-std vs smol vs embassy
| 런타임 | 타깃 | 스레딩 | I/O 백엔드 | 비고 |
|---|---|---|---|---|
| tokio | 서버/네트워크 표준 | multi-thread/current-thread | mio(epoll/kqueue/IOCP) | 2026년 사실상 표준 |
| async-std | 단순 API 지향 | multi-thread | smol-based | 유지보수 모드, 신규 채택 비추 |
| smol | 경량 (1k LOC) | single/multi | polling/async-io | 임베디드, 학습 목적 |
| embassy | no_std/펌웨어 | cooperative, no-alloc 가능 | HAL 통합 | RP2040/STM32/ESP32 |
| glommio | Linux-only io_uring | thread-per-core | io_uring | 데이터베이스/스토리지 |
2026년 5월 기준 "라이브러리는 런타임 무관(runtime-agnostic)으로, 애플리케이션은 tokio로"가 표준 패턴이다. tokio::main을 라이브러리 크레이트에 박지 않는 게 매너다.
axum — async 웹 프레임워크의 사실상 표준
tokio 팀이 직접 만드는 axum은 0.7~0.8을 거치며 사실상 표준이 됐다. 특징은 tower::Service 기반 미들웨어, 강력한 추출기(extractor) 시스템, hyper 위에 얇게 얹은 디자인이다.
use axum::{
Router,
extract::{Path, State},
response::Json,
routing::{get, post},
http::StatusCode,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use sqlx::PgPool;
#[derive(Clone)]
struct AppState { pool: PgPool }
#[derive(Serialize)]
struct User { id: i64, email: String }
#[derive(Deserialize)]
struct CreateUser { email: String }
async fn get_user(
State(state): State<Arc<AppState>>,
Path(id): Path<i64>,
) -> Result<Json<User>, StatusCode> {
let row = sqlx::query_as::<_, (i64, String)>(
"SELECT id, email FROM users WHERE id = $1"
)
.bind(id)
.fetch_optional(&state.pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.ok_or(StatusCode::NOT_FOUND)?;
Ok(Json(User { id: row.0, email: row.1 }))
}
async fn create_user(
State(state): State<Arc<AppState>>,
Json(payload): Json<CreateUser>,
) -> Result<(StatusCode, Json<User>), StatusCode> {
let row: (i64,) = sqlx::query_as("INSERT INTO users(email) VALUES ($1) RETURNING id")
.bind(&payload.email)
.fetch_one(&state.pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok((StatusCode::CREATED, Json(User { id: row.0, email: payload.email })))
}
#[tokio::main]
async fn main() {
let pool = PgPool::connect(&std::env::var("DATABASE_URL").unwrap()).await.unwrap();
let state = Arc::new(AppState { pool });
let app = Router::new()
.route("/users/:id", get(get_user))
.route("/users", post(create_user))
.with_state(state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
Path, State, Json, Query 같은 추출기는 모두 FromRequestParts 또는 FromRequest 트레이트로 확장 가능하다. 미들웨어는 tower::Layer로 합성해 Router::layer()에 꽂는다.
actix-web 5와 다른 웹 프레임워크들 — poem · rocket · warp · salvo · loco-rs
axum이 표준이라고 해도 다른 프레임워크도 살아 있다.
- actix-web 5: 액터 모델은 점점 옅어지고 일반 async-fn 핸들러 중심으로 자리 잡았다. TechEmpower 벤치마크에서 여전히 최상위권. 마이크로벤치 성능을 짜내는 팀이 선호.
- poem: tokio 위에 axum과 유사한 추출기 디자인. OpenAPI 자동 생성(poem-openapi)에 강점.
- rocket 0.5: 1.0 트랙으로 안정화됐지만 async 적응이 느려 신규 채택은 감소.
- warp: 필터 콤비네이터 디자인. 유지보수 활동은 줄었다.
- salvo: 중국 커뮤니티에서 활발. 트리 기반 라우팅.
- loco-rs: "Rust의 Rails"를 표방. 풀스택 어셈블리(axum + sea-orm + 백그라운드 작업 + 메일러).
| 프레임워크 | 핵심 특징 | 추천 시나리오 |
|---|---|---|
| axum | tower 생태계, 추출기 | 일반 백엔드 표준 |
| actix-web 5 | 성능 최상위 | 초고성능 게이트웨이 |
| poem | OpenAPI 1급 시민 | 스키마 중심 API |
| loco-rs | 풀스택 컨벤션 | "Rails of Rust" 빠른 스타트 |
| salvo | 트리 라우팅 | 중국 시장/대형 라우팅 트리 |
sqlx · sea-orm · diesel 2.x — 데이터베이스 레이어 비교
DB 레이어는 세 갈래로 정리됐다.
- sqlx 0.8+: async, 컴파일타임 쿼리 검사(
query!매크로), Postgres/MySQL/SQLite/MSSQL. 마이그레이션은sqlx-cli. 2026년 표준. - sea-orm: 풀 ORM, ActiveRecord 패턴, 마이그레이션 DSL, 엔티티 코드 생성(
sea-orm-cli). loco-rs의 기본 ORM. - diesel 2.x: 동기 ORM.
diesel-async로 async 어댑터 지원. 컴파일타임 안전성 최강, 다만 매크로 학습곡선이 가파르다. - rbatis: 중국 커뮤니티 발 async ORM. Mybatis-스타일 XML/매크로.
sqlx의 컴파일타임 쿼리 검사 예시.
use sqlx::PgPool;
#[derive(sqlx::FromRow)]
struct Order { id: i64, user_id: i64, total_cents: i64, status: String }
async fn list_user_orders(pool: &PgPool, user_id: i64) -> sqlx::Result<Vec<Order>> {
// 매크로가 컴파일타임에 DB에 접속해 SELECT 컬럼 타입을 검사한다
let orders = sqlx::query_as!(
Order,
r#"SELECT id, user_id, total_cents, status
FROM orders
WHERE user_id = $1
ORDER BY id DESC
LIMIT 100"#,
user_id
)
.fetch_all(pool)
.await?;
Ok(orders)
}
sqlx::query_as!는 DATABASE_URL 환경 변수 또는 sqlx-data.json(오프라인 모드)으로 DB 스키마를 미리 확인한다. CI에서 오프라인 모드를 쓰는 게 표준이다.
| 라이브러리 | 스타일 | 안전성 | async | 추천 |
|---|---|---|---|---|
| sqlx | SQL 우선 + 매크로 검사 | 컴파일타임 검증 | 네이티브 | 백엔드 표준 |
| sea-orm | ActiveRecord ORM | 런타임 | 네이티브 | 풀스택/loco-rs |
| diesel | 타입드 DSL | 컴파일타임 최강 | adapter 필요 | 안전성 우선 |
| rbatis | XML/매크로 SQL | 런타임 | 네이티브 | 중국 시장 호환 |
에러 처리 — anyhow · thiserror · eyre · snafu · miette
에러 처리 크레이트는 두 축으로 갈린다.
- 라이브러리용 에러 타입 정의:
thiserror(파생 매크로),snafu(컨텍스트 추가 강조). - 애플리케이션용 에러 합치기:
anyhow(anyhow::Error로 모두 흡수),eyre(anyhow + 리포터 커스터마이즈),miette(예쁜 진단 출력).
전형적인 라이브러리 + 애플리케이션 분리.
// 라이브러리 측: thiserror로 명시적 에러 타입
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BillingError {
#[error("invoice not found: {0}")]
NotFound(i64),
#[error("database error")]
Db(#[from] sqlx::Error),
#[error("payment provider error: {0}")]
Provider(String),
}
pub async fn charge(invoice_id: i64) -> Result<(), BillingError> {
Err(BillingError::Provider("stripe timeout".into()))
}
// 애플리케이션 측: anyhow로 컨텍스트 누적
use anyhow::Context;
pub async fn process_batch(ids: &[i64]) -> anyhow::Result<()> {
for &id in ids {
charge(id).await
.with_context(|| format!("failed to charge invoice {id}"))?;
}
Ok(())
}
miette는 컴파일러 진단처럼 소스 위치를 가리키는 출력을 만든다. CLI 사용자 친화적 도구를 만들 때 자주 쓴다.
tracing + tracing-subscriber — 비동기 관측의 표준
println!/log/env_logger를 지나, 2026년 Rust의 관측 표준은 tracing이다. tokio 팀이 만들었고 비동기 코드에 자연스럽다.
use tracing::{info, instrument, error};
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
#[instrument(skip(pool))]
async fn handle_request(pool: &sqlx::PgPool, user_id: i64) -> anyhow::Result<()> {
info!(user_id, "processing request");
let _row = sqlx::query("SELECT 1").fetch_one(pool).await?;
Ok(())
}
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()))
.with(fmt::layer().json())
.init();
info!("server starting");
}
tracing-opentelemetry 어댑터를 추가하면 OTEL 트레이서로 그대로 흘려보낼 수 있고, tracing-loki/tracing-bunyan-formatter 같은 익스포터도 풍부하다.
serde 2026 — 표준의 자리, miniserde · postcard
serde는 사실상 표준 직렬화 프레임워크다. 2026년 5월 기준 1.0 라인업이 계속 유지되고 있고, "serde 2.0"의 RFC가 논의 중이지만 아직 안정 릴리스는 없다.
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct ApiResponse {
#[serde(rename = "userId")]
user_id: i64,
#[serde(default)]
tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
}
fn parse_response(s: &str) -> serde_json::Result<ApiResponse> {
serde_json::from_str(s)
}
대안군:
- miniserde: 매크로 컴파일 시간을 줄인 미니멀 직렬화 라이브러리. 단순 JSON 전용.
- postcard:
serde-기반 임베디드 친화적 바이너리 포맷.no_std호환. - bincode 2.0: 표준 빠른 바이너리. derive 매크로 분리.
- rkyv: zero-copy 역직렬화. 성능 극단 추구.
Tauri 2 — 데스크톱과 모바일을 같은 코드로
Tauri 1이 "Electron의 가벼운 대안"이었다면, Tauri 2(2024년 GA, 2026년 5월 안정 트랙)는 데스크톱 + 안드로이드 + iOS를 같은 Rust 코어로 묶는다.
// src-tauri/src/lib.rs
use tauri::command;
#[command]
async fn greet(name: String) -> Result<String, String> {
Ok(format!("Hello, {name}!"))
}
#[command]
async fn read_file(path: String) -> Result<String, String> {
tokio::fs::read_to_string(&path).await.map_err(|e| e.to_string())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet, read_file])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
플러그인 시스템이 정비돼 tauri-plugin-sql, tauri-plugin-store, tauri-plugin-deep-link, tauri-plugin-notification 같은 1급 플러그인이 모바일까지 커버한다. 프론트는 Vanilla/React/Vue/Svelte/Leptos/Dioxus 모두 가능.
Leptos · Dioxus · Yew · Sycamore — Rust 풀스택 프론트엔드
WASM + Rust 풀스택 프레임워크는 네 갈래다.
- Leptos 0.7+: 시그널 기반 fine-grained 반응성, SSR/CSR/하이드레이션/
server fn일급 지원.cargo-leptos로 풀스택 빌드. - Dioxus 0.6+: RSX(React 유사) 매크로, 데스크톱/모바일/웹/터미널 멀티 타깃. 2025년 D팀이 풀타임 OSS 회사로 자리 잡았다.
- Yew: VDOM 기반 클래식. 진입은 친숙하지만 풀스택 도구는 Leptos에 밀린다.
- Sycamore: Solid 영감 시그널 기반. 학습 목적/소규모 SPA에 적합.
Leptos의 풀스택 컴포넌트 예시.
use leptos::*;
use leptos::server_fn::ServerFn;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
pub struct Todo { pub id: i64, pub title: String, pub done: bool }
#[server]
pub async fn list_todos() -> Result<Vec<Todo>, ServerFnError> {
Ok(vec![Todo { id: 1, title: "ship".into(), done: false }])
}
#[component]
pub fn TodoList() -> impl IntoView {
let todos = create_resource(|| (), |_| async move { list_todos().await });
view! {
<Suspense fallback=move || view! { <p>"Loading..."</p> }>
{move || todos.get().map(|res| match res {
Ok(items) => view! {
<ul>{items.into_iter().map(|t| view!{ <li>{t.title}</li> }).collect_view()}</ul>
}.into_view(),
Err(_) => view! { <p>"error"</p> }.into_view(),
})}
</Suspense>
}
}
Dioxus의 같은 화면.
use dioxus::prelude::*;
#[derive(Clone, PartialEq, Props)]
struct Todo { id: i64, title: String, done: bool }
fn TodoList(cx: Scope) -> Element {
let todos = use_state(cx, || vec![Todo { id: 1, title: "ship".into(), done: false }]);
cx.render(rsx! {
ul {
todos.iter().map(|t| rsx!{
li { key: "{t.id}", "{t.title}" }
})
}
})
}
| 프레임워크 | 패러다임 | 멀티타깃 | 풀스택 | 추천 |
|---|---|---|---|---|
| Leptos | 시그널, SSR 1급 | 웹 | 강함 | 풀스택 웹 표준 |
| Dioxus | RSX, 멀티 렌더러 | 웹/데스크톱/모바일/TUI | 진행 중 | 멀티 타깃 |
| Yew | VDOM | 웹 | 약함 | 학습/단순 SPA |
| Sycamore | 시그널 | 웹 | 약함 | 학습 |
egui · Iced · Slint — 네이티브 GUI 라이브러리
Tauri가 "웹뷰 기반"이라면, 네이티브 GUI는 세 갈래다.
- egui: 즉시 모드(immediate mode) GUI. 자체 렌더러(
eframe), 짧은 코드, 게임/내부 도구에 적합. - Iced: Elm 영감, Cmd/Sub 패턴. 데스크톱 앱.
- Slint: DSL(
.slint파일) 기반. 임베디드 친화, 상용 라이선스도 제공.
use eframe::egui;
struct App { name: String, age: u32 }
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("My egui Application");
ui.horizontal(|ui| {
ui.label("Your name: ");
ui.text_edit_singleline(&mut self.name);
});
ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
if ui.button("Increment").clicked() { self.age += 1; }
ui.label(format!("Hello '{}', age {}", self.name, self.age));
});
}
}
fn main() -> eframe::Result<()> {
let opts = eframe::NativeOptions::default();
eframe::run_native(
"demo",
opts,
Box::new(|_| Ok(Box::new(App { name: "Arthur".into(), age: 42 }))),
)
}
| 라이브러리 | 패러다임 | 렌더 | 적합 시나리오 |
|---|---|---|---|
| egui | immediate mode | wgpu/glow | 게임 내 디버그, 내부 도구 |
| Iced | Elm-like retained | wgpu/tiny-skia | 데스크톱 앱 |
| Slint | DSL retained | software/skia/femtovg | 임베디드, 키오스크 |
| Dioxus desktop | 웹뷰 | OS WebView | 웹 자산 재사용 |
| Tauri 2 | 웹뷰 | OS WebView | 풀 데스크톱 + 모바일 |
Bevy 0.15+ — Rust 게임 엔진의 사실상 표준
Bevy는 ECS 기반 데이터 지향 게임 엔진이다. 2025년 0.15에서 BSN(Bevy Scene Notation)·렌더 그래프 v2·UI 개편이 들어갔고, 2026년 0.16/0.17 라인에서 에디터·핫리로드·머티리얼 추가가 진행 중이다.
use bevy::prelude::*;
#[derive(Component)]
struct Player { speed: f32 }
fn spawn_player(mut commands: Commands) {
commands.spawn((
Player { speed: 250.0 },
Sprite::from_color(Color::srgb(0.2, 0.7, 1.0), Vec2::new(40.0, 40.0)),
Transform::default(),
));
}
fn move_player(
time: Res<Time>,
keys: Res<ButtonInput<KeyCode>>,
mut q: Query<(&Player, &mut Transform)>,
) {
for (p, mut t) in &mut q {
let mut dir = Vec3::ZERO;
if keys.pressed(KeyCode::KeyW) { dir.y += 1.0; }
if keys.pressed(KeyCode::KeyS) { dir.y -= 1.0; }
if keys.pressed(KeyCode::KeyA) { dir.x -= 1.0; }
if keys.pressed(KeyCode::KeyD) { dir.x += 1.0; }
t.translation += dir.normalize_or_zero() * p.speed * time.delta_secs();
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, spawn_player)
.add_systems(Update, move_player)
.run();
}
Bevy의 매력은 "ECS가 일급 시민이고, 시스템이 그냥 함수"라는 점이다. 게임뿐 아니라 시뮬레이션·툴 백엔드로도 채택이 늘고 있다.
Embassy · RTIC · embedded-hal — 임베디드/펌웨어 Rust
no_std 임베디드 영역에서 2026년의 흐름은 명확하다. Embassy가 사실상 표준이고, 인터럽트 우선순위 기반의 결정론이 필요한 곳은 RTIC가 자리를 지킨다.
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_rp::gpio::{Level, Output};
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::task]
async fn blink(mut led: Output<'static>) {
loop {
led.set_high();
Timer::after(Duration::from_millis(500)).await;
led.set_low();
Timer::after(Duration::from_millis(500)).await;
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let led = Output::new(p.PIN_25, Level::Low);
spawner.spawn(blink(led)).unwrap();
}
embedded-hal 1.0이 2023년 말 안정화되면서 드라이버 생태계가 일원화됐다. STM32/RP2040/nRF52/ESP32 가릴 것 없이 embedded-hal 트레이트를 구현한 HAL 위에서 같은 드라이버가 돈다.
CLI · 시스템 도구 — clap 4.5 · argh · ripgrep · bat · fd
CLI 파서는 사실상 clap이 표준이다.
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "blogctl", version, about = "Blog management CLI")]
struct Cli {
#[command(subcommand)]
command: Cmd,
}
#[derive(Subcommand)]
enum Cmd {
/// Create a new post
New { title: String, #[arg(short, long)] draft: bool },
/// Publish a post
Publish { slug: String },
}
fn main() {
let cli = Cli::parse();
match cli.command {
Cmd::New { title, draft } => println!("new post: {title} (draft={draft})"),
Cmd::Publish { slug } => println!("publish {slug}"),
}
}
argh(Google)는 derive 매크로 컴파일 시간이 짧아 작은 도구에 적합. 시스템 명령 라인 도구 중 Rust 작성이 사실상 표준이 된 사례는 ripgrep, fd, bat, eza, zoxide, bottom, dust, procs, hyperfine, tokei, mdbook이다.
Cargo 워크스페이스 · build.rs · cross-compile
대형 Rust 프로젝트는 워크스페이스로 묶어 크레이트들을 함께 관리한다.
# 워크스페이스 루트의 Cargo.toml
[workspace]
resolver = "3"
members = ["crates/api", "crates/domain", "crates/storage"]
[workspace.dependencies]
tokio = { version = "1.40", features = ["full"] }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres"] }
axum = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
anyhow = "1"
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"
크로스 컴파일은 cross 크레이트가 표준이다. cross build --target aarch64-unknown-linux-musl --release 한 줄이면 Docker 기반 툴체인이 정적 바이너리를 만들어 준다.
Cargo 도구 생태 — nextest · mutants · deny · audit · machete · expand · binstall
표준 cargo test만으로는 부족하다. 2026년 표준 보조 도구는 다음과 같다.
| 도구 | 용도 |
|---|---|
| cargo-nextest | 병렬 테스트 러너, 멋진 출력, 격리 |
| cargo-mutants | 변이 테스트, 테스트 품질 검증 |
| cargo-deny | 라이선스/취약점/중복 의존 검사 |
| cargo-audit | RUSTSEC DB 기반 취약점 스캔 |
| cargo-machete | 사용되지 않는 의존성 탐지 |
| cargo-expand | 매크로 펼친 결과 확인 |
| cargo-binstall | 사전 빌드 바이너리 설치 |
| cargo-watch | 파일 변경 감지 자동 재빌드 |
| cargo-leptos | Leptos 풀스택 빌드 |
# 표준 CI 한 묶음
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo nextest run --workspace --all-features
cargo deny check
cargo audit
cargo machete
WebAssembly — wasm-bindgen · wasm-pack · wasmtime · wasmer
Rust + WASM은 두 진영이다.
- 브라우저용:
wasm-bindgen+wasm-pack. JS와 Rust 간 자동 바인딩. - 서버/엣지용:
wasmtime(Bytecode Alliance, Fastly·Microsoft 후원),wasmer. WASI Preview 2가 2024년 정착하면서 컴포넌트 모델 기반 모듈화가 진행 중.
Cloudflare Workers는 Rust+WASM을 1급 시민으로 지원하고, Fermyon·Spin·WasmCloud 같은 회사가 WASM 기반 서버리스를 끌어가고 있다.
rust-for-linux · Cloudflare Pingora · Discord · Dropbox · Figma — 프로덕션 사례
Rust 채택은 더 이상 "스타트업의 실험"이 아니다.
| 회사/프로젝트 | 채택 시점 | Rust 사용처 | 임팩트 |
|---|---|---|---|
| Linux Kernel | 2022 머지, 2024 mainline 확장 | 드라이버, Android Binder | 시스템 언어 인증 |
| Cloudflare Pingora | 2022 발표, 2024 OSS | 차세대 HTTP 프록시 (NGINX 대체) | 일 40T 요청 처리 |
| Discord | 2020~ | Read States, 게이트웨이 | GC pause 제거, 99.99% 안정성 |
| Dropbox | 2014~ | Magic Pocket 스토리지 | 엑사바이트급 |
| Figma | 2022~ | 멀티플레이어 HTTP 레이어 | 동시 편집 성능 |
| Mozilla / Servo | 2024 LF 이관 | 차세대 브라우저 엔진 | 임베디드 브라우저 |
| AWS | 전사 | Firecracker, Bottlerocket, S3 일부 | 컨테이너/마이크로VM |
| 안드로이드 | Binder, Crash Subsystem | 시스템 메모리 안전 | |
| Microsoft | Windows | 일부 시스템 컴포넌트 | 메모리 안전 강화 |
| Meta | 백엔드 | 서비스 메시 일부 | Rust 공식 채택 |
함정 · 트레이드오프 — Rust를 도입할 때 빠지기 쉬운 함정
Rust를 새 프로젝트에 도입할 때 자주 보는 함정 다섯 가지.
- 컴파일 시간: 큰 워크스페이스에서 풀 빌드는 길다.
cargo check위주 워크플로,sccache,mold링커, debug-info=line-tables-only 같은 셋업이 표준이다. - async + Send 경계:
Box<dyn Trait>을 도처에 쓰지 말 것. 라이브러리 트레이트는 가급적Send + 'static경계를 명시. - dyn 호환성과 RPITIT: 모든 async-trait를 RPITIT로 옮기면 dyn-trait가 깨진다. dyn 사용 트레이트는 여전히
async-trait매크로가 필요할 수 있다. - 에러 타입 폭주: 라이브러리 한 곳에 거대한
enum Error박지 말 것. 모듈마다 작은 에러 타입을 두고From으로 끌어올린다. - unsafe 남용: 99% 의 코드는 unsafe가 필요 없다. FFI/임베디드/SIMD가 아니면 멈춰서 다시 본다.
한국 · 일본의 Rust 채택 — Naver · Kakao · CyberAgent · Mercari · Sansan
한국·일본도 적극적이다.
- 네이버: 검색/광고 일부 백엔드, 미디어 처리 파이프라인에 Rust 도입. 사내 Rust 스터디·세미나가 정기 운영된다.
- 카카오 브레인 / 카카오엔터프라이즈: ML 추론 서버, 모델 게이트웨이에 Rust 활용. PyO3로 파이썬과 결합.
- Discord Korea / 트위치 코리아 그룹: 글로벌 Rust 스택을 그대로 활용.
- CyberAgent: 광고·게임 인프라 일부를 Rust로 재작성. AbemaTV의 일부 마이크로서비스.
- Mercari: 검색·인프라 도구·CLI 일부를 Rust로 작성. Rust 채용 공고 상시.
- Sansan: 명함 인식·OCR 후처리 파이프라인에 Rust 채택.
- LINE Yahoo: 일부 시스템 데몬, 빌드 도구를 Rust로 마이그레이션.
도쿄와 서울 모두 Rust 밋업이 분기마다 열린다. 한국은 "Rust Korea" 슬랙·디스코드·페이스북 그룹이, 일본은 "Rust.Tokyo" 컨퍼런스가 정기 행사로 자리 잡았다.
Rust 배우는 순서 — 2026년 추천 로드맵
처음 Rust를 만지는 개발자에게 추천하는 흐름이다.
- The Rust Programming Language("the book") 1~10장: 소유권·빌림·라이프타임의 직관을 잡는다.
- rustlings: 작은 빈칸 채우기로 컴파일러 메시지에 익숙해진다.
cargo new로 CLI 만들기: clap + serde + anyhow 조합.- tokio + axum으로 작은 API: sqlx + Postgres + tracing까지.
- 2024 에디션 변경점 + async fn in trait 학습.
- 도메인 선택:
- 웹/풀스택: Leptos 또는 Dioxus.
- 데스크톱: Tauri 2 또는 egui.
- 게임: Bevy.
- 임베디드: Embassy + RP2040 또는 STM32 Discovery.
- 시스템: rust-for-linux, Firecracker, Pingora 소스 읽기.
"빨리 잘 짜기"보다 "컴파일러와 대화하면서 모델을 잡기"가 핵심이다. 컴파일러 에러는 친구다.
마무리 — 2026년의 Rust는 "선택의 언어"가 아닌 "기본의 언어"
2024년 rust-for-linux 머지, 2024년 Tauri 2 GA, 2025년 Pingora 오픈소스화, 2025년 axum 0.8, 2026년 Rust 1.85(2024 에디션)까지 흐름이 모이면, 2026년 5월 Rust는 더 이상 "쓰면 좋은 언어"가 아니다. 백엔드는 tokio + axum + sqlx, 데스크톱·모바일은 Tauri 2, 게임은 Bevy, 임베디드는 Embassy, 풀스택 웹은 Leptos/Dioxus가 표준이 됐다.
핵심은 더 이상 "Rust를 써야 하는가?"가 아니라 "어디에 Rust를 쓰는가?"다. 그리고 그 답은 점점 더 많은 영역으로 확장되고 있다.
References
- Rust 공식 — https://www.rust-lang.org/
- The Rust Book — https://doc.rust-lang.org/book/
- Rust 블로그 — https://blog.rust-lang.org/
- tokio — https://tokio.rs/
- axum — https://github.com/tokio-rs/axum
- actix-web — https://actix.rs/
- sqlx — https://github.com/launchbadge/sqlx
- Diesel — https://diesel.rs/
- SeaORM — https://www.sea-ql.org/SeaORM/
- Bevy — https://bevyengine.org/
- Tauri — https://tauri.app/
- Leptos — https://leptos.dev/
- Dioxus — https://dioxuslabs.com/
- Yew — https://yew.rs/
- Embassy — https://embassy.dev/
- crates.io — https://crates.io/
- rust-for-linux — https://rust-for-linux.com/
- Cloudflare Pingora — https://github.com/cloudflare/pingora
- Servo (Linux Foundation) — https://servo.org/
- Discord Rust 사례 — https://discord.com/blog/why-discord-is-switching-from-go-to-rust