Tauri 심화 개발
Tauri는 Rust 기반 데스크톱 앱 프레임워크로, Electron 대비 10배 작은 번들 크기와 낮은 메모리 사용량이 특징입니다. Rust의 강력한 시스템 접근 능력을 활용한 플러그인 개발을 살펴봅니다.
Tauri 커맨드 (Rust → JS 브릿지)
use tauri::command;
use std::fs;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct FileInfo {
name: String,
size: u64,
is_dir: bool,
modified: String,
}
#[command]
fn list_directory(path: String) -> Result<Vec<FileInfo>, String> {
let entries = fs::read_dir(&path)
.map_err(|e| format!("디렉토리 읽기 실패: {}", e))?;
let mut files = Vec::new();
for entry in entries {
let entry = entry.map_err(|e| e.to_string())?;
let metadata = entry.metadata().map_err(|e| e.to_string())?;
files.push(FileInfo {
name: entry.file_name().to_string_lossy().to_string(),
size: metadata.len(),
is_dir: metadata.is_dir(),
modified: format!("{:?}", metadata.modified().unwrap_or(
std::time::SystemTime::UNIX_EPOCH
)),
});
}
files.sort_by(|a, b| a.name.cmp(&b.name));
Ok(files)
}
#[command]
async fn read_file_content(path: String) -> Result<String, String> {
tokio::fs::read_to_string(&path)
.await
.map_err(|e| format!("파일 읽기 실패: {}", e))
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
list_directory,
read_file_content,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
프론트엔드에서 호출
import { invoke } from '@tauri-apps/api/core';
interface FileInfo {
name: string;
size: number;
is_dir: boolean;
modified: string;
}
async function loadDirectory(path: string) {
try {
const files = await invoke<FileInfo[]>('list_directory', { path });
return files;
} catch (error) {
console.error('Error:', error);
return [];
}
}
Tauri 플러그인 작성
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
};
#[tauri::command]
fn copy_to_clipboard(text: String) -> Result<(), String> {
use clipboard::{ClipboardContext, ClipboardProvider};
let mut ctx: ClipboardContext = ClipboardProvider::new()
.map_err(|e| e.to_string())?;
ctx.set_contents(text)
.map_err(|e| e.to_string())
}
#[tauri::command]
fn read_clipboard() -> Result<String, String> {
use clipboard::{ClipboardContext, ClipboardProvider};
let mut ctx: ClipboardContext = ClipboardProvider::new()
.map_err(|e| e.to_string())?;
ctx.get_contents()
.map_err(|e| e.to_string())
}
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("clipboard")
.invoke_handler(tauri::generate_handler![
copy_to_clipboard,
read_clipboard,
])
.build()
}
시스템 트레이와 글로벌 단축키
use tauri::{
menu::{Menu, MenuItem},
tray::{TrayIconBuilder, TrayIconEvent},
Manager,
};
fn main() {
tauri::Builder::default()
.setup(|app| {
let quit = MenuItem::with_id(app, "quit", "종료", true, None::<&str>)?;
let show = MenuItem::with_id(app, "show", "보이기", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&show, &quit])?;
TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.menu(&menu)
.on_menu_event(|app, event| match event.id.as_ref() {
"quit" => app.exit(0),
"show" => {
if let Some(window) = app.get_webview_window("main") {
window.show().unwrap();
window.set_focus().unwrap();
}
}
_ => {}
})
.build(app)?;
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running application");
}
Electron vs Tauri 비교
| 항목 | Electron | Tauri |
|---|---|---|
| 번들 크기 | ~150MB+ | ~3-10MB |
| 메모리 사용 | ~150MB+ | ~30-50MB |
| 백엔드 언어 | Node.js | Rust |
| 렌더링 엔진 | Chromium | 시스템 WebView |
| 보안 모델 | 느슨함 | 권한 기반 (엄격) |
| 성숙도 | 높음 | 성장 중 |
- Tauri v2부터 iOS/Android 모바일 앱도 지원
- Rust의 메모리 안전성으로 보안 취약점 대폭 감소
- 권한 시스템(tauri.conf.json)으로 API 접근을 세밀하게 제어
- 시스템 WebView 사용으로 번들 크기와 메모리 사용량 대폭 절감
- Rust 학습 곡선이 있지만, JS/TS 프론트엔드는 그대로 사용 가능
댓글 0