Rust 19. Tauri 첫 로컬 도구 만들기
요약
Tauri 첫 로컬 도구는 파일 읽기, shell 실행, 네트워크 요청부터 시작하지 않는 편이 좋다. 처음에는 frontend가 문자열을 보내고 Rust command가 계산 결과를 돌려주는 작은 도구가 가장 안전하다.
이 글의 예시는 text stats 도구다. 사용자가 textarea에 글을 넣으면 Rust command가 line, word, character 수를 계산해 반환한다. 이 예시는 OS 권한이 거의 필요 없어서 Tauri의 기본 흐름을 배우기에 좋다.
문서 정보
- 작성일: 2026-04-29
- 검증 기준일: 2026-04-29
-
문서 성격: tutorial analysis - 테스트 환경: 로컬 실행 테스트 없음. Tauri v2 project structure, command, capability 문서를 기준으로 예제를 구성했다.
- 테스트 버전: Tauri v2 문서 2026-04-29 확인. 실제 실행 시에는
tauri,@tauri-apps/api, Rust toolchain, frontend package manager 버전을 기록해야 한다. - 출처 성격: 공식 문서, 원 프로젝트 문서
확인한 사실
- Tauri project structure 문서는 일반적인 Tauri project가 top-level JavaScript project와
src-tauri/Rust project로 나뉜다고 설명한다. - Tauri command 문서는
#[tauri::command]로 Rust command를 만들고invoke_handler에 등록한 뒤 frontend에서invoke로 호출하는 흐름을 설명한다. - Tauri command 문서는 command가 인자, 반환값, 에러, async를 다룰 수 있다고 설명한다.
- Tauri capability 문서는 등록된 custom command가 기본적으로 모든 window와 WebView에서 허용된다고 설명한다. 더 좁은 제한이 필요하면
AppManifest::commands와 capability 설계를 검토해야 한다. - 이 글의 검증 기준일은 2026-04-29이다.
만들 도구
기능은 일부러 작게 잡는다.
- 입력: 사용자가 입력한 text
- 처리: line 수, word 수, character 수 계산
- 출력: JSON으로 직렬화 가능한 구조체
- 권한: 파일, shell, 네트워크, plugin permission 없음
- 실패 조건: 입력이 너무 크거나 비어 있는 경우
이렇게 시작하면 Tauri의 세 가지 핵심을 한 번에 볼 수 있다.
- frontend 입력
- Rust command
- frontend 결과 표시
Rust command
src-tauri/src/lib.rs에 반환 구조체와 command를 둔다.
use serde::Serialize;
#[derive(Serialize)]
struct TextStats {
lines: usize,
words: usize,
chars: usize,
}
#[tauri::command]
fn analyze_text(input: String) -> Result<TextStats, String> {
let text = input.trim();
if text.is_empty() {
return Err("text is required".to_string());
}
if text.len() > 100_000 {
return Err("text is too large".to_string());
}
Ok(TextStats {
lines: text.lines().count(),
words: text.split_whitespace().count(),
chars: text.chars().count(),
})
}
그리고 builder에 등록한다.
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![analyze_text])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Frontend 호출
frontend에서는 raw invoke를 작은 wrapper로 감싼다.
import { invoke } from "@tauri-apps/api/core";
type TextStats = {
lines: number;
words: number;
chars: number;
};
export async function analyzeText(input: string): Promise<TextStats> {
return await invoke<TextStats>("analyze_text", { input });
}
UI 이벤트에서는 성공과 실패를 모두 처리한다.
try {
const stats = await analyzeText(textarea.value);
result.textContent = `${stats.lines} lines, ${stats.words} words, ${stats.chars} chars`;
} catch (error) {
result.textContent = String(error);
}
검증할 것
| 입력 | 기대 결과 |
|---|---|
| 빈 문자열 | text is required 에러 |
hello world |
words 2 |
| 여러 줄 입력 | lines가 줄 수와 일치 |
| 한글 입력 | chars가 byte 수가 아니라 문자 수 기준 |
| 매우 긴 입력 | 크기 제한 에러 |
이 도구는 파일을 읽지 않기 때문에 filesystem plugin permission이 필요 없다. shell도 실행하지 않고 network도 쓰지 않는다. 첫 도구에서 이 점이 중요하다. 로컬 앱이라고 해서 곧바로 로컬 전체 권한을 열 필요는 없다.
다음에 붙일 수 있는 기능
기본 흐름이 확인되면 기능을 조금씩 넓힌다.
- 결과 복사 버튼
- 최근 입력을 local storage나 store plugin에 저장
- 파일 열기 기능
- markdown 분석
- export 기능
다만 파일 열기, 저장, 외부 command 실행을 붙이는 순간 capability와 permission 설계가 다시 필요하다. 기능이 늘 때마다 “어떤 window가 어떤 command를 왜 호출해야 하는가”를 다시 적는다.
한계와 예외
- 이 글은 실제 실행 로그가 아니라 첫 도구 설계 예제다.
- production 앱에서는 입력 크기, Unicode 처리, frontend state, error UX, 테스트를 더 정교하게 다뤄야 한다.
- 파일 접근, shell 실행, 외부 binary embedding은 다음 단계의 별도 주제로 보는 것이 안전하다.
참고자료
- Tauri: Project Structure
- Tauri: Calling Rust from the Frontend
- Tauri: Capabilities
- Tauri: Permissions
변경 이력
- 2026-04-29: 초안 작성.
- 2026-04-29: 첫 로컬 text stats 도구 예제, Rust command, frontend invoke, 검증 기준을 보강.
댓글남기기