macOS를 기준으로 Rust GUI 라이브러리의 사용성, 입력기 지원, 접근성을 QR 코드 생성기 예제로 비교한 설문조사입니다.
boringcactus의 2025년 Rust GUI 라이브러리 설문조사가 나온 지 1년이 넘었다. 정말 흥미로운 글이었고, 나도 직접 해 보고 싶을 만큼 흥미로웠다. 그래서 Are We GUI Yet? 웹사이트에 나열된 라이브러리를 각각 테스트하고 검토해 보려 한다.
내가 고른 과제는 QR 코드 생성기다. 인터페이스에는 텍스트 상자가 있고, 텍스트를 입력하면 Rust 백엔드가 해당 텍스트에 대응하는 QR 코드를 계산해 텍스트 상자 아래에 표시한다.

이 과제는 GUI 프레임워크가 고려해야 하는 여러 측면을 다룰 수 있다. 예를 들어 텍스트 상자는 IME 지원과 관련되고, 백엔드에서 이미지를 표시하는 일은 기존 Rust 생태계와 프레임워크의 호환성을 시험한다. 또한 이 과제는 올해 처음 Rust로 GUI 프로그램을 개발하면서 실제로 마주친 사례를 단순화한 버전이기도 하다.
기본 기능의 완성도 외에도 상태 관리, 스타일링, 초기 프로젝트 뼈대 구성의 복잡성, 편집기 경험을 아우르는 다소 주관적인 사용성 등급도 매기겠다.
현실적인 감각을 얻기 위해 각 과제는 가능한 한 직접 손으로 작성해 보겠다. 물론 2026년의 큰 차이점은 코딩 에이전트가 실용적으로 채택되었다는 것이다. 이 설문 중 내가 올바른 사용법을 스스로 알아내지 못하는 프레임워크를 만났지만 코딩 에이전트가 대신 올바른 코드를 작성할 수 있다면, 그 프레임워크의 사용성에도 어느 정도 점수를 주겠다.
나는 macOS를 사용하므로 설문은 macOS 플랫폼을 기준으로 한다. 일부 Windows 전용 프레임워크는 Windows 가상 머신에서도 실행해 보겠다.
Azul의 홈페이지를 열자 Azlin Workspace, Azlin UI Toolkit, Azlin OS를 소개하는 꽤 야심 찬 페이지가 나를 맞았다. Azlin Workspace는 사실상 "곧 출시"라는 안내문이 잔뜩인 상태였고 Azlin이 Azul과 어떤 관계인지도 확신할 수 없었지만, 홈페이지를 지나 GitHub에서 올바른 사용자 문서를 찾는 데는 성공했다.
Azul은 막 0.2.0 버전을 출시한 것으로 보인다. 문서에 따르면 Homebrew로 런타임 라이브러리를 설치하면 된다. 그리고 Azul은 crates.io에 게시되어 있지 않아서 Git 저장소를 복제하고 실행하여 Rust API 바인딩을 생성해야 했다.
첫 번째 난관은 즉시 나타났다. rust-analyzer가 Azul이 생성한 Rust 코드를 인식하지 못했다. 컴파일과 실행은 잘됐지만 관련 타입에 대한 편집기 힌트가 모두 사라졌다.
그래도 큰 문제는 아니다. 적어도 cargo doc은 사용할 수 있다. LSP 이전 시대로 돌아가 프로그래밍하는 느낌이다.
cargo 문서를 뒤지고 컴파일러를 반복 실행하며 10분을 보낸 끝에, 마침내 원래 예제에 텍스트 상자를 넣는 데 성공했다. 그러나 무슨 짓을 해도 상자에 입력한 텍스트가 실제로 표시되게 할 수 없었다. 그래서 Codex에게 이 문제의 디버깅을 부탁했는데, Azul은 시스템에 설치된 글꼴을 읽지 못하는 듯했다. 이 용도로 Azul을 배제하기에 거의 충분한 이유다. 더 이상 시간과 토큰을 낭비하고 싶지 않아 다음 프레임워크로 넘어가기로 했다.
Blinc는 아주 새로운 프레임워크다. 첫 버전이 2026년 초에야 출시됐다. 많은 Rust GUI 프레임워크와 마찬가지로 Blinc는 렌더링 백엔드로 wgpu를 사용하고 반응형 프로그래밍 모델을 채택한다. 빠르게 반복 개발되는 탓에 공식 문서의 일부 예제는 이미 오래되었다.
TextInput 컴포넌트는 미완성처럼 느껴진다. 글꼴을 설정할 수 없고 반응형 동작을 위해 on_change 이벤트에서 수동으로 트리거해야 한다. 한편 TextArea는 업데이트될 때 트리거할 대상으로 시그널을 지정할 수 있다. 하지만 이 두 방식 어느 쪽도 반응형의 기준에 진정으로 부합한다고 생각하지 않는다. 진정한 반응성이라면 이벤트나 시그널을 수동으로 처리할 필요 없이 입력 컨트롤의 변경 사항이 상태가 사용되는 모든 곳으로 직접 전파되어야 한다.
TextInput의 기본 글꼴은 CJK 문자 표시를 지원하지 않는다. IME는 잘 작동하지만 조합 중인 텍스트의 위치가 텍스트 상자의 위치와 맞지 않는다.
macOS의 접근성 기능도 작동하지 않아 화면 읽기 프로그램이 창의 내용을 읽을 수 없다.
전체 코드
use base64::Engine as _;
use blinc_app::prelude::*;
use blinc_app::windowed::{WindowedApp, WindowedContext};
fn qr_encode(text: &str) -> anyhow::Result<Vec<u8>> {
let code = qrcode::QrCode::new(text.as_bytes())?;
let img = code.render::<image::Luma<u8>>().build();
let mut buf = Vec::new();
img.write_to(&mut std::io::Cursor::new(&mut buf), image::ImageFormat::Png)?;
Ok(buf)
}
fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
let config = WindowConfig {
title: "QRCode Generator".to_string(),
width: 400,
height: 400,
resizable: false,
..Default::default()
};
WindowedApp::run(config, build_ui)
}
fn build_ui(ctx: &mut WindowedContext) -> impl ElementBuilder + use<> {
let text = ctx.use_state_keyed("text", || {
text_input_state_with_placeholder("https://example.com")
});
div()
.w(ctx.width)
.h(ctx.height)
.padding(Length::Px(10.0))
.flex_col()
.bg_surface()
.gap(24.0)
.child(label("Enter text to generate QR code:").text_center())
.child(text_input(&text.get()).on_change({
let text = text.clone();
move |_| text.update(|text| text)
}))
.child(stateful::<NoState>().deps([text.signal_id()]).on_state({
let text = text.clone();
move |_| {
let text = text.get().lock().unwrap().value.clone();
let img = qr_encode(&text).unwrap_or_else(|_| Vec::new());
let b64 = base64::engine::general_purpose::STANDARD.encode(&img);
div()
.w_full()
.flex_grow()
.flex_col()
.child(image(format!("data:image/png;base64,{b64}")).self_center())
}
}))
}
이 긴 여정을 마무리하며, 이 설문에서의 승자, 혹은 좀 더 겸손하게 말해 내가 실제로 사용해 볼 의향이 있는 프레임워크를 꼽을 때가 되었다.
승자 그룹에는 slint와 egui가 있다. 이들은 명백한 마찰이나 함정이 없는 API를 제공할 뿐 아니라 IME와 접근성도 탄탄하게 지원한다. 이들은 각각 보존형 UI와 즉시 모드 UI의 두 왕좌를 차지한다.
특정 틈새 영역에서 매력적인 프레임워크도 있다. 예를 들어 SwiftUI와 결합한 Crux, Flutter와 결합한 rinf가 그렇다. WebView를 기꺼이 채택할 수 있다면 Dioxus 또는 Tauri + tauri-spectra도 합리적인 선택이다.
cushy, Freya, Floem, Iced, Relm4, Xilem처럼 사소한 단점 때문에 승자 그룹에 약간 못 미치는 프레임워크들도 있다. 이들의 API 설계도 상당히 마음에 든다. 안타깝게도 입력기나 접근성 지원이 다소 부족하다. 이 영역에서 계속 개선한다면 미래가 밝다.
물론 이 설문은 하나의 시점에 대한, 그리고 상당히 주관적인 스냅샷일 뿐이다. 이 단순한 시나리오에서 뛰어난 프레임워크가 더 복잡한 프로그램도 반드시 잘 처리하는 것은 아니다. 그럼에도 이 실험은 GUI 개발에서 가장 흉내 내기 어려운 부분을 드러냈다. Rust에는 유망한 GUI 프로젝트가 많지만, 생태계는 아직 누구나 받아들이는 평범한 선택지 하나로 수렴하지 않았다.
개인적인 간과로 인해 이 설문에 오류가 있을 수도 있다. 내가 쓴 내용 중 사실과 맞지 않는 것을 발견한다면 연락해 알려 주기 바란다.
라이브러리사용성접근성****IME 지원 Azul😭 글꼴을 읽을 수 없음 blinc🟡 API 마찰❌ 없음🟡 조합 위치 불량, CJK 글꼴 미지원 cacao✅ 양호(macOS 전용)✅ 양호✅ 양호 Core Foundation😭 저수준 API Crux✅ 양호(SwiftUI)✅ 양호✅ 양호 cushy✅ 양호❌ 없음🟡 조합 텍스트 숨김 CXX-Qt🟡 환경 설정✅ 양호✅ 양호 Dioxus✅ 양호(WebView)✅ 양호✅ 양호 dominator 웹 전용 egui✅ 양호✅ 양호🟡 CJK 글꼴 설정 필요 floem✅ 양호❌ 없음❌ 없음 FLTK✅ 양호✅ 양호(추가 설정 필요)✅ 양호 Flutter Rust Bridge✅ 양호(Flutter)✅ 양호✅ 양호 Freya✅ 양호❌ 없음✅ 양호 Fui macOS 미지원 gemgui✅ 양호(pywebview)✅ 양호✅ 양호 GPUI🟡 텍스트 입력 위젯 없음❌ 작동시키는 방법을 모름🟡 충돌 GPUI Component✅ 양호✅ 양호✅ 양호 GTK 3🟡 특정 커밋 사용❌ 없음❌ 없음 GTK 4✅ 양호❌ 없음✅ 양호 iced✅ 양호❌ 없음✅ 양호 imgui🟡 상용구 코드❌ 없음❌ 없음 KAS🟡 API 마찰❌ 없음❌ 없음 kittest GUI 프레임워크 아님 Leptos 웹 전용 lvgl🟡 임베디드 전용❌ 없음❌ 없음 Makepad🟡 부실한 문서❌ 없음🟡 조합 텍스트 숨김 masonry🟡 저수준✅ 양호✅ 양호 Maycoon 사용 중단 Pane UI😭 이미지를 불러올 수 없음 Pax😭 컴파일 실패 ply🟡 이상한 기본값✅ 양호(추가 설정 필요)❌ 없음 QMetaObject🟡 환경 설정✅ 양호✅ 양호 Relm✅ 양호❌ 없음❌ 없음 Relm4✅ 양호❌ 없음✅ 양호 Ribir🟡 난해한 매크로❌ 없음✅ 양호 rinf✅ 양호(Flutter)✅ 양호✅ 양호 rosin🟡 부실한 위젯 라이브러리✅ 양호(추가 설정 필요)✅ 양호 rui🟡 부실한 위젯 라이브러리❌ 없음❌ 없음 SDL3 GUI 프레임워크 아님 slint✅ 양호✅ 양호✅ 양호 Tauri✅ 양호(WebView)✅ 양호✅ 양호 Tessera macOS 미지원 tinyfiledialogs GUI 프레임워크 아님 Tk🟡 API 마찰❌ 없음✅ 양호 undoredo GUI 프레임워크 아님 Vizia✅ 양호❌ 충돌✅ 양호 WebRender GUI 프레임워크 아님 Windows😭 저수준 API Windows Reactor🟡 API 마찰(Windows 전용)🟡 텍스트 상자만✅ 양호 WinSafe✅ 양호(Windows 전용)✅ 양호✅ 양호 WxDragon✅ 양호✅ 양호✅ 양호 Xilem✅ 양호✅ 양호🟡 CJK 글꼴 미지원 Yew 웹 전용