본문으로 건너뛰기
← 블로그로 돌아가기
개발 2026년 3월 10일

Tauri IPC 통신 — 프론트엔드에서 Rust 호출하기

Tauri의 invoke 시스템으로 React에서 Rust 함수를 타입 안전하게 호출하는 방법.

Tauri v2 Rust React IPC

Tauri의 IPC 구조

Tauri에서 프론트엔드(WebView)와 백엔드(Rust)는 IPC(Inter-Process Communication)로 통신한다. 웹에서 API를 호출하는 것과 비슷하지만, 네트워크를 거치지 않고 프로세스 내부에서 직접 데이터를 주고받는다.

흐름은 단순하다:

  1. Rust에서 #[tauri::command]로 함수를 정의
  2. 프론트엔드에서 invoke("함수명", { 인자 })로 호출
  3. 결과를 Promise로 받음

Rust 커맨드 정의

#[tauri::command]
fn get_secrets(master_password: String) -> Result<Vec<Secret>, String> {
    let key = crypto::derive_key(&master_password);
    db::get_all_secrets(&key)
        .map_err(|e| e.to_string())
}

#[tauri::command]
fn save_secret(
    master_password: String,
    name: String,
    value: String,
) -> Result<(), String> {
    let key = crypto::derive_key(&master_password);
    db::insert_secret(&key, &name, &value)
        .map_err(|e| e.to_string())
}

Result<T, String>을 반환하면 프론트엔드에서 try-catch로 에러를 잡을 수 있다.

프론트엔드에서 호출

import { invoke } from '@tauri-apps/api/core';

interface Secret {
  id: number;
  name: string;
  value: string;
}

async function loadSecrets(password: string): Promise<Secret[]> {
  return invoke<Secret[]>('get_secrets', {
    masterPassword: password,
  });
}

주의할 점: Rust에서 master_password(snake_case)로 정의했지만, 프론트엔드에서는 masterPassword(camelCase)로 전달한다. Tauri가 자동 변환한다.

타입 안전성 확보

Tauri v2에서는 specta 크레이트로 Rust 타입에서 TypeScript 타입을 자동 생성할 수 있다.

use specta::Type;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Type)]
pub struct Secret {
    pub id: i64,
    pub name: String,
    pub value: String,
}

빌드 시 TypeScript 타입 파일이 생성되므로, 프론트엔드에서 수동으로 인터페이스를 관리할 필요가 없다. Rust 구조체를 변경하면 TypeScript 타입도 자동으로 바뀐다.

비동기 커맨드

DB 조회나 파일 I/O처럼 시간이 걸리는 작업은 비동기로 처리해야 UI가 멈추지 않는다.

#[tauri::command]
async fn export_secrets(
    master_password: String,
    path: String,
) -> Result<(), String> {
    let key = crypto::derive_key(&master_password);
    let secrets = db::get_all_secrets(&key)
        .map_err(|e| e.to_string())?;

    tokio::fs::write(&path, serde_json::to_string_pretty(&secrets).unwrap())
        .await
        .map_err(|e| e.to_string())
}

Tauri v2는 내부적으로 tokio를 사용하므로 async fn을 바로 쓸 수 있다.

이벤트 시스템

단방향 호출이 아니라 Rust에서 프론트엔드로 푸시가 필요한 경우, 이벤트를 사용한다.

// Rust → Frontend
app_handle.emit("sync-complete", payload)?;
// Frontend에서 수신
import { listen } from '@tauri-apps/api/event';

listen<string>('sync-complete', (event) => {
  console.log('동기화 완료:', event.payload);
});

KeyBox에서는 암호화 진행률을 이벤트로 프론트엔드에 전달하는 데 사용했다.

정리

Tauri의 IPC는 REST API를 설계하는 것과 비슷한 멘탈 모델로 접근하면 된다. 커맨드가 엔드포인트고, invoke가 fetch다. 다만 네트워크 지연이 없고, Rust의 타입 시스템이 프론트엔드까지 연결된다는 점이 다르다.