Skip to main content
블로그로 돌아가기
블로그

Electron 대신 Tauri로 MadoHub을 만든 이유

데스크톱 개발 도구에 Tauri v2를 선택한 기술적 이유를 설명합니다. 성능, 바이너리 크기, 시스템 통합에서 Rust가 가진 장점까지 다룹니다.

MadoHub을 만들기 시작했을 때 요구사항은 분명했습니다. 웹 기반 UI를 가지면서 PTY 세션을 관리하고, 파일시스템을 읽고, 시스템 프로세스와 깊게 연동하는 데스크톱 애플리케이션이 필요했습니다. 진지한 선택지는 Electron과 Tauri 두 가지였습니다.

우리는 Tauri를 선택했습니다. 이유와 배운 점은 다음과 같습니다.

바이너리 크기 논쟁

Tauri의 가장 자주 언급되는 장점은 바이너리 크기입니다. 빈 Electron 앱은 Chromium 브라우저 전체를 함께 싣기 때문에 약 150MB입니다. Tauri 앱은 macOS에서는 WebKit, Windows에서는 WebView2, Linux에서는 WebKitGTK 같은 시스템의 네이티브 웹뷰를 사용하기 때문에 5MB 이하로 시작할 수 있습니다.

개발자 도구에서는 바이너리 크기가 생각보다 중요합니다. 개발자들은 설치할 대상을 꽤 까다롭게 고릅니다. "생산성 도구"가 200MB 다운로드라면 의심부터 듭니다. 10MB 다운로드는 "가볍고 목적이 분명하구나"라는 인상을 줍니다.

하지만 바이너리 크기는 결정에서 가장 덜 중요한 요소였습니다.

Rust 백엔드

Tauri를 고른 진짜 이유는 Rust 백엔드였습니다.

MadoHub은 PTY 세션을 관리하고, tmux 패널을 모니터링하고, JSONL 파일을 파싱하고, 라우팅용 API를 호출하고, 파일시스템 작업을 모두 동시에 처리합니다. 이런 시스템 수준 작업은 다음의 이점을 얻습니다.

가비지 컬렉션 없이 메모리 안전성: PTY 관리는 프로세스 생명주기, 파일 디스크립터 읽기, 시그널 처리를 다룹니다. Node.js(Electron의 백엔드)에서는 프로세스 관리 버그가 파일 디스크립터 누수나 좀비 프로세스를 만들 수 있습니다. Rust의 소유권 모델은 이런 버그를 컴파일 타임에 잡아냅니다.

진정한 동시성: MadoHub은 여러 터미널 프로세스를 동시에 모니터링하고, 백그라운드 스레드에서 tmux 패널을 폴링하고, 라우팅용 API를 병렬 호출합니다. Rust의 async 런타임(tokio)은 이걸 적은 오버헤드로 처리합니다. Node.js도 async I/O는 잘하지만, 큰 JSONL 파일 파싱 같은 CPU 바운드 작업은 이벤트 루프를 막습니다.

구조화된 에러 처리: 시스템 작업은 예측 가능한 방식으로 실패합니다. 파일이 없거나, 프로세스가 종료되거나, API 호출이 타임아웃됩니다. Rust의 Result 타입은 모든 실패 경로를 처리하게 강제합니다. 라우팅 코드에서는 "세션 파일을 찾지 못해 CWD 해석으로 fallback"과 "원인 불명의 undefined 에러로 파이프라인이 죽는 것"의 차이를 만듭니다.

우리의 Rust 워크스페이스

MadoHub의 백엔드는 7개의 집중된 crate로 나뉩니다.

Crate책임
madohub-ptyPTY 세션 관리, tmux 통합
madohub-pane팀 모니터링용 tmux 패널 폴링
madohub-agent에이전트 라우팅, JSONL 파싱, API 호출
madohub-gitGit 작업(status, commit, push)
madohub-team~/.claude/teams/에서 팀 감지
madohub-persistence캔버스 상태를 ~/.madohub/에 직렬화
madohub-core공유 이벤트 emitter trait

이 모듈식 구조 덕분에 각 crate를 독립적으로 테스트할 수 있고, 변경되지 않은 crate는 다시 빌드되지 않아 컴파일 시간도 합리적으로 유지됩니다.

프론트엔드 이야기

Tauri의 프론트엔드는 표준 웹 애플리케이션을 렌더링하는 웹뷰입니다. 우리는 React 19 + TypeScript + Tailwind CSS + Zustand를 상태 관리에 사용합니다. 웹 앱을 만들 때 쓰는 것과 같은 스택입니다.

프론트엔드와 백엔드의 통신은 Tauri의 command 시스템을 통해 이뤄집니다. 본질적으로 타입이 있는 IPC입니다. 프론트엔드가 Tauri command를 호출하면 Rust 백엔드가 실행하고 결과를 돌려줍니다. 스트리밍 데이터(터미널 출력, 라우팅 이벤트)는 Tauri의 이벤트 시스템이 처리합니다.

과소평가되기 쉬운 장점 하나는, 웹뷰가 시스템의 네이티브 렌더링 엔진을 사용한다는 점입니다. 즉 플랫폼의 텍스트 렌더링, 스크롤 물리, 접근성 기능을 그대로 물려받습니다. MadoHub의 텍스트가 네이티브처럼 보이는 이유는 실제로 네이티브 렌더링이기 때문입니다.

우리가 포기한 것

Tauri에도 당연히 트레이드오프가 있습니다.

크로스 플랫폼 웹뷰 차이: WebKit(macOS)과 WebView2(Windows)는 엣지 케이스에서 서로 다른 렌더링 동작을 보입니다. CSS backdrop-filter가 다르게 동작하고, 일부 스크롤 동작이 달라지고, 최신 Web API는 플랫폼마다 도착 시점이 다릅니다. 우리는 주로 애니메이션과 블러 효과에서 이런 문제를 몇 번 겪었습니다.

작은 생태계: Electron은 10년에 걸친 플러그인, 디버깅 도구, 커뮤니티 지식을 가진 생태계가 있습니다. Tauri의 생태계는 빠르게 성장 중이지만 아직 더 젊습니다. Electron이었다면 npm 패키지로 끝났을 일을 직접 구현해야 했던 적도 있습니다.

Rust 학습 곡선: 프론트엔드 팀은 주로 TypeScript 개발자입니다. 백엔드를 위해 Rust를 쓰려면 학습 시간이 필요했습니다. 시스템 수준 코드에는 그만한 가치가 있지만, 초기 투자는 분명합니다.

실제 성능

MadoHub 같은 도구에서는 5개의 터미널 타일이 열려 있고, 각각 PTY 출력을 스트리밍하면서, 라우팅 시스템이 완료를 감시하고 tmux 패널을 폴링하는 상황이 흔합니다. 이런 상황에서는 성능이 이론이 아닙니다. 캔버스에서 프레임이 떨어지거나, 터미널 렌더링이 느리거나, 라우팅 트리거가 지연되면 사용성에 바로 영향이 옵니다.

우리 벤치마크에서 8개의 활성 터미널 타일을 가진 MadoHub은 RAM 약 120MB를 사용합니다. 동등한 Electron 앱은 터미널 하나도 열기 전에 Chromium 오버헤드만으로 300MB 이상에서 시작합니다.

평상시 동작(터미널 활성, 라우팅 유휴) 중 CPU 사용률은 5% 이하입니다. 활성 라우팅(API 호출 + JSONL 파싱 + 콘텐츠 전달) 중에는 잠깐 15-20%까지 올라갔다가 다시 기본값으로 돌아옵니다.

다시 Tauri를 선택할까?

이런 종류의 애플리케이션이라면 망설임 없이 그렇습니다. 시스템 수준의 Rust 백엔드와 웹 기반 프론트엔드의 조합은 깊은 OS 통합이 필요한 개발자 도구에 매우 잘 맞습니다.

기본적인 파일 I/O 외에 시스템 상호작용이 거의 없는 더 단순한 앱이라면 선택이 덜 분명할 수 있습니다. 그런 경우에는 Electron의 성숙도와 생태계 크기가 더 실용적인 선택일 수 있습니다.

하지만 MadoHub처럼 백엔드가 동시에 PTY 세션을 관리하고, 시스템 프로세스를 모니터링하고, 구조화된 데이터를 고처리량으로 파싱하는 경우에는 Rust가 단순한 성능 최적화가 아닙니다. 자신 있게 배포할 수 있게 해주는 정확성 보장입니다.