3P by GN⁺ | ★ favorite | 댓글 1개
  • Rust로 GUI 프로그램을 만들고 싶은 개발자를 위해 Rust 상태·로직은 그대로 두고 Flutter UI를 붙이는 방식을 제안함
  • Flutter의 성숙한 크로스플랫폼 SDK와 위젯 생태계, 픽셀 단위 제어, hot reload가 UI 반복 작업을 빠르게 만듦
  • 100% 순수 Rust 접근은 아니지만, HTML/CSS/Slint나 매크로 기반 DSL처럼 UI 계층을 분리하는 기존 Rust UI 절충안과 비슷함
  • flutter_rust_bridge는 임의 타입, &mut, async, traits, results, closure, lifetimes 등을 자동 변환해 Rust와 Flutter 사이의 브리지가 됨
  • 카운터 앱과 todo-list 앱 예제로 구조를 확인할 수 있으며, 전체 코드는 flutter_rust_bridge 저장소의 예제 디렉터리에 있음

Rust GUI에 Flutter를 붙이는 방식

  • Rust는 StackOverflow와 GitHub 기준으로 8년 동안 “가장 원하는 프로그래밍 언어”로 꼽혔고, Rust로 GUI 프로그램을 만들고 싶어 하는 수요가 큼
  • 제안된 방식은 Flutter와 flutter_rust_bridge를 사용해 Rust 프로그램에 GUI를 붙이는 것임
  • 직접 실행하려면 GitHub 저장소나 데모 폴더를 활용할 수 있음

Flutter를 쓰는 이유

  • Flutter는 StackOverflow 기준 “가장 인기 있는 크로스플랫폼 모바일 SDK”로 꼽혔고, 여러 개발자와 브랜드가 사용 중임
  • 풍부한 위젯 생태계 덕분에 원하는 UI 기능을 구현하기 쉬움
    • confetti 애니메이션 같은 패키지도 존재함
    • 다양한 위젯과 기능, 픽셀 단위 제어 유연성을 제공함
  • hot reload는 UI 조정이 잦은 개발 과정에서 반복 속도를 높임
    • 코드 변경 후 상태를 잃지 않고 거의 즉시 업데이트된 UI를 볼 수 있음
    • 재컴파일을 기다리는 시간이 줄어듦
  • 같은 코드베이스를 Android, iOS뿐 아니라 Linux, MacOS, Windows, Web에서도 실행할 수 있음

순수 Rust가 아니라는 절충

  • 이 접근은 100% 순수 Rust가 아님
    • Rust가 상태와 로직을 담당하고, Flutter가 UI를 담당하는 구조임
    • 매크로로 만든 커스텀 DSL, HTML/CSS/Slint 같은 다른 언어를 쓰는 Rust UI 방식과 유사함
  • 이런 분리는 관심사 분리에 맞고, 다른 사례에서도 채택된 방식임
  • Flutter는 Rust를 이해하고 있다면 배우기 쉽다고 봄
  • Web 플랫폼에는 일부 비판이 있으며, 정적 웹페이지보다는 Google Earth나 Rive 애니메이션 에디터 같은 “앱” 형태에 더 적합해 보임
  • Flutter에는 보일러플레이트와 스캐폴드 코드가 많음
    • 작은 프로젝트에서는 대개 해당 파일들을 바꾸지 않아 없는 것과 비슷하게 볼 수 있음
    • 큰 프로젝트에서는 수정 가능성이 곧 커스터마이즈 가능성으로 이어짐

flutter_rust_bridge가 연결하는 것

  • flutter_rust_bridge의 목표는 Rust와 Flutter 사이를 하나의 언어처럼 자연스럽게 연결하는 것임
  • 여러 요소를 자동 변환함
    • 임의 타입
    • &mut
    • async
    • traits
    • results
    • closure(callback)
    • lifetimes
  • “Flutter를 통한 Rust GUI”는 가능한 사용 사례 중 하나임
  • 다른 사용 예로는 Flutter에서 임의의 Rust 라이브러리를 쓰거나, 알고리듬 같은 코드는 Rust로 작성하고 나머지는 Flutter로 작성하는 방식이 있음

카운터 앱 예제

  • 예제는 Rust와 Flutter를 통합하는 여러 방법 중 하나임
  • flutter_rust_bridge는 특정 구조를 강제하지 않는 범용 도구라서 Redux식 또는 Elm식 접근도 가능함
  • Rust 쪽은 #[frb(ui_state)]로 상태를 정의하고, #[frb(ui_mutation)]으로 변경 메서드를 표시함
    • RustStatecount: i32를 가짐
    • new()count100으로 초기화함
    • increment()count를 1 증가시킴
  • #[frb(ui_state)]#[frb(ui_mutation)]은 매우 가벼우며, 내부에 숨겨진 마법은 없고 코드도 10여 줄 정도라고 함
  • Flutter UI는 선언형으로 작성됨
    • 현재 카운트를 보여주는 Text
    • state.increment를 호출하는 TextButton
    • 두 요소를 컬럼으로 묶고 padding을 적용함
  • 실행 중 UI를 수정하면 hot reload로 변경 사항을 즉시 확인할 수 있음

Todo-list 앱 예제

  • todo-list 앱은 완결성을 위한 선택 섹션이며, flutter_rust_bridge가 지원할 수 있는 여러 접근 중 하나임
  • Rust 상태는 todo 항목과 입력 텍스트, 필터, 다음 ID를 포함함
    • items: Vec<Item>
    • input_text: String
    • filter: Filter
    • next_id: i32
  • Itemid, content, completed를 가짐
  • FilterAll, Active, Completed를 가짐
  • 상태 변경 동작은 #[frb(ui_mutation)] 아래에 구현됨
    • add()는 현재 입력 텍스트로 항목을 추가하고 입력을 비움
    • remove(id)는 해당 ID의 항목을 제거함
    • toggle(id)는 완료 상태를 반전함
  • 비즈니스 로직은 filtered_items()Filter::check()로 구성됨
    • All은 모든 항목을 통과시킴
    • Active는 완료되지 않은 항목만 통과시킴
    • Completed는 완료된 항목만 통과시킴
  • Flutter UI는 텍스트 필드, 리스트 뷰, 필터 버튼 row를 컬럼으로 배치함
    • SyncTextField는 입력 변경과 제출을 Rust 상태 변경에 연결함
    • 각 todo 항목은 체크박스, 텍스트, 삭제 버튼으로 구성됨

코드 위치와 실행

  • 전체 코드는 flutter_rust_bridge 저장소에 있음
    • frb_example/rust_ui_counter
    • frb_example/rust_ui_todo_list
  • 대부분은 Flutter 기능 때문에 생기는 자동 생성 보일러플레이트 파일임
  • 핵심 파일은 src/app.rsui/lib/main.dart
  • 데모 실행은 ui 디렉터리에서 다음 명령을 실행함
    • flutter_rust_bridge_codegen generate && flutter run

댓글과 토론

Hacker News 의견들
  • 지난 몇 년간 이걸로 앱[0]을 만들고 있는데, 약간 거친 부분은 있어도 전반적으로 쓰기 즐거웠고 만족스러웠음
    v1에서 v2로 올리는 것도 어렵지 않았고, v2는 유용한 기능이 많아 큰 업그레이드였음
    특히 코드 생성 경험 개선tokio 비동기 지원이 큰 전환점이었음
    앱의 비즈니스 로직은 Rust로 쓰고 프런트엔드는 Dart로 두는 구성이 잘 맞았고, HN에서 Flutter/Dart가 별로 사랑받지 못하는 건 알지만 React 같은 시스템보다 추론하기 훨씬 쉽다고 봄
    React는 Flutter처럼 전체 위젯 트리를 렌더링하는 접근에 비해 추상화 수준이 맞지 않는다고 느낌
    [0]: https://saveoursecrets.com

    • Flutter 자체는 나쁘지 않지만, Google이 통제하는 제품이라는 점 때문에 깊게 들어가기가 망설여짐
      Google 제품을 쓰면 언제 사라질지 모른다는 위협을 계속 받는 느낌임
      또 여전히 모바일과 손가락 중심의 흔적이 남아 있고, 데스크톱과 마우스 지원 개선은 내 경험상 거의 없었음
    • Flutter와 React가 컴포넌트 접근에서 꽤 비슷하다고 생각했는데, 이 부분을 조금 더 설명해 줄 수 있는지 궁금함
      Flutter 경험이 많지 않아서 실제로 어떻게 느꼈는지 듣고 싶음
    • Flutter와 React는 둘 다 UI가 상태의 함수라는 방식으로 동작함
      사실 Flutter도 처음에는 명령형으로 시작했지만 React 출시 이후 선언형으로 바뀌었고, Android도 Jetpack Compose를 통해 같은 원칙의 선언형으로 가는 중임
      flutter_hooks나 ReArch 같은 패키지를 쓰면 상태 캡슐화가 훨씬 쉬워지고, 기능마다 initState를 쓰고 dispose를 잊지 않아야 하는 방식은 견디기 어려웠음
    • Flutter/Dart가 정말 마음에 듦
      React 자체보다 HTML/CSS/JavaScript 생태계와 도구 체인이 더 큰 문제였고, Flutter는 UI를 위해 목적에 맞게 만들어진 생태계라 신선함
      SDK를 설치하면 핫 리로드 같은 기능이 기본으로 준비되어 있고, 여러 도구를 조합해 동작 방식을 파악할 필요가 없음
      HTML + CSS가 필요 없다는 점도 큼
      더 인기가 없는 이유는 이미 HTML/CSS/JS에 익숙한 프런트엔드 개발자가 너무 많기 때문으로 보이고, 전통적인 GUI 패러다임에 익숙한 사람에게는 Flutter가 처음부터 훨씬 단순하고 쉽다고 봄
  • Rust로 UI를 쓰는 게 Dart보다 어떤 장점이 있는지는 잘 모르겠지만, flutter_rust_bridge는 매우 좋아함
    fzyzcjy와 커뮤니티가 Dart에서 Rust 코드를 매끄럽게 호출할 수 있게 만든 작업은 Flutter 앱에 큰 자산임
    webp가 지원되지 않아서 인기 이미지 압축 앱 ImageOptim을 Flutter로 주말 동안 다시 만들었는데, 초기 설정에서 약간 고생한 뒤 flutter_rust_bridge와 작은 래퍼[0]로 성숙한 Rust 이미지 라이브러리를 호출할 수 있었고 병렬 처리도 알아서 처리해 줬음
    결과적으로 앱은 ImageOptim보다 기능도 많고 더 빨랐는데, 대부분 Rust 통합 덕분이었음
    [0]: https://github.com/blopker/alic/blob/main/rust/src/api/compr...

    • flutter_rust_bridge를 처음 개발할 때의 개인 사용 시나리오도 비슷했음
      일부 고성능 알고리즘에는 Rust를 쓰는 방식이었음
  • 훌륭한 노력임
    지금 프로젝트에서 Tauri를 쓰고 있는데, 둘 사이의 장단점을 아는 사람이 있는지 궁금함

    • JavaScript를 쓰는 앱에서는 Tauri가 Electron을 대체해 가고 있고 요즘 인기가 있다는 얘기를 들었음
      다만 Tauri는 아직 Rust와 JavaScript가 아주 매끄럽게 통신하게 해 주지는 못하는 듯하고, 예를 들어 https://tauri.app/v1/guides/features/command/도 주로 기본 기능 위주로 보임
      아래에서 언급된 것처럼 Tauri는 아직 모바일을 일급 지원 대상으로 보지 않는다는 점도 있음
      앞으로 Tauri의 브리지가 개선되고 모바일 지원도 좋아지길 기대함
    • 내가 보기에는 Tauri를 써 보지는 않았고 Flutter도 몇 년 전이 마지막이지만, Tauri는 현재 모바일과 Linux 지원이 약한 편임
      Linux 쪽은 WebkitGtk 상태 때문임
      Flutter는 Dart를 쓰는데, Dart는 다른 곳에서 널리 쓰이지 않음
      MDN, w3schools, 표준 명세 같은 학습 기준이 없어 배우기 더 어렵고, Flutter Web은 DOM 대신 Canvas를 쓰기 때문에 문제가 있음
      개인적으로는 크로스 플랫폼 앱에는 여전히 Electron이 맞다고 봄
  • 흥미로움
    내가 이해한 게 맞다면 이건 소스 간 변환으로 동작하는 건가 싶음
    문서만 봐서는 기술적 접근을 이해하기 조금 어렵지만, 사용자 대상으로 잘 쓰였고 인상적임
    더 깊이 보기 전에 접근 방식과 wasm 기반 Rust 웹 프레임워크와의 비교를 알고 싶음
    Rust와 Flutter를 결합하는 장점 중 하나는 Flutter가 이미 완성된 프레임워크라서 서버와 클라이언트 사이에서 코드와 자료 구조를 공유할 수 있다는 점으로 보임

    • Rust 코드를 파싱해서 “여기에 함수가 있고 저기에 구조체가 있다”는 식으로 이해한 뒤, 일부 Rust 코드와 Flutter 코드를 생성하는 방식임
      비교에 대해서는 블로그에서 몇 가지 장단점을 다루고 있음
  • 잘 만들었음
    rust_flutter_bridge에 대해 좋은 얘기만 들었음
    다만 Flutter 질문에 가깝지만, 간단한 앱 기준으로 모바일 네이티브(Java, Swift?)와 비교했을 때 Flutter의 최종 앱 크기가 얼마나 커지는지, 그리고 UI 성능은 어떤지 궁금함

    • 앱 크기는 구체적인 시나리오에 따라 달라짐
      예를 들어 Flutter는 사용하지 않는 라이브러리 코드를 자동으로 제거함
      기억이 흐릿하지만 단순 앱은 대략 5MB 정도였던 것 같고, ABI별로 .so 파일을 나누는 것도 기억해야 함
      UI 성능은 개인적으로 꽤 좋다고 느꼈고, Flutter(Dart)는 JIT나 인터프리터가 아니라 AOT로 어셈블리 코드로 컴파일되므로 이론적으로도 빠름
      둘 다 정확히 보려면 특정 사례에 맞는 데모를 만들어 보는 게 좋고, Flutter 커뮤니티에 물어보는 것도 도움이 될 듯함
  • 이 경우 접근성은 어떤지 궁금함
    문서에서 관련 정보를 찾지 못했는데, 2024년에 광범위한 접근성 지원 없이 GUI 키트를 내놓는 건 상상하기 어려우니 왜 언급이 없는지 의아함

    • Flutter 전반을 말하는 거라면 꽤 좋다고 봄
      위젯 단위로 쉽게 넣을 수 있고, 빌드를 실행하면서 확인하고 테스트하기도 매우 쉬움
  • 데스크톱 UI에는 Flutter를 쓰고 백엔드에는 Rust를 쓰지만, 브리지 대신 gRPC로 둘을 분리했음
    이렇게 하면 양쪽 언어에 덜 묶이고, 프런트엔드에서 백엔드를 모킹하기 위한 인터페이스도 더 깔끔해질 것 같음
    UI와 백엔드를 다른 머신에 두기도 쉬워져 진짜 클라이언트/서버 구조를 만들 수 있음
    단점은 인터페이스가 더 장황해질 가능성이 크다는 점임

    • UI가 실행되는 데스크톱에서 gRPC 서비스를 띄우고 둘을 localhost로 연결하는 방식인지 궁금함
  • Chrome 셸이나 웹 위젯에 기대는 것보다 훨씬 나아 보이고, 노력에 박수를 보냄

  • 좋음
    Flutter의 UI 구축 방식은 정말 즐거웠지만, Dart 자체는 그다지 마음에 들지 않았음
    이론적으로는 UI 전용 프로그래밍 언어를 만들 수 있지 않을까 싶음
    주요 프로그래밍 언어 어디서든 연결할 수 있고, protobuf의 IDL 형식처럼 데이터를 정의하는 대신 사용자 인터페이스를 선언하는 식임
    말도 안 되거나 멍청한 아이디어일까?
    QT와 XAML이 떠오르지만, QT는 닫힌 소스라고 알고 있고 틀렸을 수도 있음
    XAML은 오래전부터 정체된 것처럼 보이는데 이것도 틀렸을 수 있음

  • Google이 시간이 지나면서 Flutter 전체 프레임워크를 종료할 계획이 있는지 아는 사람이 있나?
    내부에서 작업하던 사람들을 거의 다 해고했다면, 3~5년 뒤에도 Flutter가 Google 지원을 받을 거라는 확신이 크지 않음
    장기 생존성을 보장하기 위해 포크해서 독립 재단 아래에 두자는 얘기가 있었는지도 궁금함
    Google이 여러 기술을 장기 지원해 온 기록을 보면, Flutter가 너무 일찍 역사 속 폐기물로 들어가면 아쉬울 것 같음

    • 무슨 얘기인지 모르겠음
      Flutter 팀에서는 아무도 해고되지 않았음
      잠재적으로 관련이 있었던 건 Flutter 빌드 파이프라인을 운영하던 Google Cloud 팀 사람들이었을 뿐이고, 그들도 Flutter를 직접 작업한 건 아니었음
      게다가 역할이 유럽으로 외주화된 것에 가까워서 Flutter 자체가 실제로 영향을 받은 건 아님
    • 대체 무슨 소리를 하는 건지 모르겠음
      그런 일은 말 그대로 일어난 적이 없음