@kood/claude-code 0.7.2 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/.claude/instructions/agent-patterns/agent-teams-usage.md +152 -278
- package/templates/hono/docs/architecture.md +759 -98
- package/templates/tauri/CLAUDE.md +1 -0
- package/templates/tauri/docs/architecture.md +300 -0
- package/templates/tauri/docs/library/tauri/index.md +44 -356
- package/templates/tauri/docs/library/tauri/references/configuration.md +46 -0
- package/templates/tauri/docs/library/tauri/references/ipc-guide.md +131 -0
- package/templates/tauri/docs/library/tauri/references/plugins-guide.md +47 -0
- package/templates/tauri/docs/library/tauri/references/security-guide.md +64 -0
- package/templates/tauri/docs/library/tauri/references/state-guide.md +49 -0
- package/templates/tauri/docs/references/async-execution.md +66 -0
- package/templates/tauri/docs/references/data-flow.md +46 -0
- package/templates/tauri/docs/references/error-handling.md +113 -0
- package/templates/tauri/docs/references/mobile-architecture.md +64 -0
- package/templates/tauri/docs/references/security-model.md +85 -0
- package/templates/tauri/docs/references/state-management.md +117 -0
- package/templates/tauri/docs/references/tech-stack.md +31 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
> **Version 2.x** | Cross-platform Desktop & Mobile Application Framework
|
|
4
4
|
|
|
5
|
+
<instructions>
|
|
6
|
+
@references/ipc-guide.md
|
|
7
|
+
@references/security-guide.md
|
|
8
|
+
@references/state-guide.md
|
|
9
|
+
@references/plugins-guide.md
|
|
10
|
+
@references/configuration.md
|
|
11
|
+
</instructions>
|
|
12
|
+
|
|
5
13
|
---
|
|
6
14
|
|
|
7
15
|
<context>
|
|
@@ -30,16 +38,13 @@
|
|
|
30
38
|
| **v1 API** | `Window` 타입 | `WebviewWindow` 사용 |
|
|
31
39
|
| **v1 API** | `get_window()` | `get_webview_window()` 사용 |
|
|
32
40
|
| **v1 API** | allowlist 설정 | Capabilities/Permissions 사용 |
|
|
33
|
-
| **v1 API** | `.validator()` | v2에서 제거됨 |
|
|
34
41
|
| **보안** | Permission 없이 Command 노출 | Capabilities 필수 |
|
|
35
42
|
| **보안** | CSP 비활성화 | 항상 CSP 설정 |
|
|
36
43
|
| **보안** | `'unsafe-eval'` 무분별 사용 | WASM만 `'wasm-unsafe-eval'` 허용 |
|
|
37
|
-
| **보안** | CDN에서 원격 스크립트 로드 | 공격 벡터, 로컬 번들 사용 |
|
|
38
44
|
| **IPC** | Rust 함수 직접 FFI 호출 | `invoke()` 메시지 패싱 사용 |
|
|
39
45
|
| **IPC** | async command에 &str 인자 | `String`으로 변환 필수 |
|
|
40
46
|
| **상태** | `State<T>`에 Arc 래핑 | Tauri가 내부적으로 처리 |
|
|
41
|
-
| **상태** | async에서 await 걸쳐 Mutex lock | 데드락
|
|
42
|
-
| **Plugin** | v1 built-in API 직접 사용 | v2 plugin으로 설치 필요 |
|
|
47
|
+
| **상태** | async에서 await 걸쳐 Mutex lock | 데드락 위험 |
|
|
43
48
|
|
|
44
49
|
</forbidden>
|
|
45
50
|
|
|
@@ -83,19 +88,13 @@ npx tauri init
|
|
|
83
88
|
|
|
84
89
|
## Prerequisites
|
|
85
90
|
|
|
86
|
-
**모든 플랫폼:**
|
|
87
|
-
- Rust (rustup 설치)
|
|
88
|
-
- Node.js
|
|
91
|
+
**모든 플랫폼:** Rust (rustup), Node.js
|
|
89
92
|
|
|
90
|
-
**macOS:**
|
|
91
|
-
- Xcode Command Line Tools
|
|
93
|
+
**macOS:** Xcode Command Line Tools
|
|
92
94
|
|
|
93
|
-
**Windows:**
|
|
94
|
-
- Microsoft C++ Build Tools (Desktop development with C++)
|
|
95
|
-
- WebView2 Runtime (Windows 10 v1803+ 기본 포함)
|
|
95
|
+
**Windows:** Microsoft C++ Build Tools, WebView2 Runtime
|
|
96
96
|
|
|
97
|
-
**Linux:**
|
|
98
|
-
- webkit2gtk, build-essential, libssl-dev, librsvg2-dev
|
|
97
|
+
**Linux:** webkit2gtk, build-essential, libssl-dev, librsvg2-dev
|
|
99
98
|
|
|
100
99
|
## 초기 설정
|
|
101
100
|
|
|
@@ -124,353 +123,17 @@ npx tauri init
|
|
|
124
123
|
}
|
|
125
124
|
```
|
|
126
125
|
|
|
127
|
-
```typescript
|
|
128
|
-
// vite.config.ts
|
|
129
|
-
import { defineConfig } from 'vite';
|
|
130
|
-
import react from '@vitejs/plugin-react';
|
|
131
|
-
|
|
132
|
-
const host = process.env.TAURI_DEV_HOST;
|
|
133
|
-
|
|
134
|
-
export default defineConfig({
|
|
135
|
-
plugins: [react()],
|
|
136
|
-
clearScreen: false,
|
|
137
|
-
server: {
|
|
138
|
-
port: 5173,
|
|
139
|
-
strictPort: true,
|
|
140
|
-
host: host || false,
|
|
141
|
-
hmr: host ? { protocol: 'ws', host, port: 1421 } : undefined,
|
|
142
|
-
watch: { ignored: ['**/src-tauri/**'] },
|
|
143
|
-
},
|
|
144
|
-
envPrefix: ['VITE_', 'TAURI_ENV_*'],
|
|
145
|
-
build: {
|
|
146
|
-
target: process.env.TAURI_ENV_PLATFORM === 'windows' ? 'chrome105' : 'safari13',
|
|
147
|
-
minify: !process.env.TAURI_ENV_DEBUG ? 'esbuild' : false,
|
|
148
|
-
sourcemap: !!process.env.TAURI_ENV_DEBUG,
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
```
|
|
152
|
-
|
|
153
126
|
</setup>
|
|
154
127
|
|
|
155
128
|
---
|
|
156
129
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
### Commands (프론트엔드 → Rust)
|
|
160
|
-
|
|
161
|
-
```rust
|
|
162
|
-
// src-tauri/src/commands/mod.rs
|
|
163
|
-
use serde::{Deserialize, Serialize};
|
|
164
|
-
use tauri::State;
|
|
165
|
-
use std::sync::Mutex;
|
|
166
|
-
|
|
167
|
-
#[derive(Serialize, Deserialize)]
|
|
168
|
-
pub struct User {
|
|
169
|
-
pub id: u32,
|
|
170
|
-
pub name: String,
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// 기본 Command
|
|
174
|
-
#[tauri::command]
|
|
175
|
-
fn greet(name: String) -> String {
|
|
176
|
-
format!("Hello, {}!", name)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// 비동기 Command + State
|
|
180
|
-
#[tauri::command]
|
|
181
|
-
async fn get_user(id: u32, db: State<'_, Mutex<Database>>) -> Result<User, String> {
|
|
182
|
-
let db = db.lock().map_err(|e| e.to_string())?;
|
|
183
|
-
db.find_user(id).ok_or("User not found".into())
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// 에러 처리 (thiserror 권장)
|
|
187
|
-
#[derive(Debug, thiserror::Error)]
|
|
188
|
-
pub enum AppError {
|
|
189
|
-
#[error("Not found: {0}")]
|
|
190
|
-
NotFound(String),
|
|
191
|
-
#[error("Internal error: {0}")]
|
|
192
|
-
Internal(String),
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
impl serde::Serialize for AppError {
|
|
196
|
-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
197
|
-
where S: serde::Serializer {
|
|
198
|
-
serializer.serialize_str(self.to_string().as_ref())
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
#[tauri::command]
|
|
203
|
-
async fn delete_user(id: u32) -> Result<(), AppError> {
|
|
204
|
-
// ...
|
|
205
|
-
Err(AppError::NotFound(format!("User {} not found", id)))
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
```typescript
|
|
210
|
-
// 프론트엔드 invoke
|
|
211
|
-
import { invoke } from '@tauri-apps/api/core';
|
|
212
|
-
|
|
213
|
-
// 기본 호출
|
|
214
|
-
const greeting = await invoke<string>('greet', { name: 'World' });
|
|
215
|
-
|
|
216
|
-
// 타입 안전 호출
|
|
217
|
-
interface User { id: number; name: string; }
|
|
218
|
-
const user = await invoke<User>('get_user', { id: 1 });
|
|
219
|
-
|
|
220
|
-
// 에러 처리
|
|
221
|
-
try {
|
|
222
|
-
await invoke('delete_user', { id: 999 });
|
|
223
|
-
} catch (error) {
|
|
224
|
-
console.error('Command failed:', error);
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### Events (양방향 통신)
|
|
229
|
-
|
|
230
|
-
```rust
|
|
231
|
-
// Rust → 프론트엔드 Event 발행
|
|
232
|
-
use tauri::{AppHandle, Emitter};
|
|
233
|
-
|
|
234
|
-
#[derive(Clone, Serialize)]
|
|
235
|
-
struct DownloadProgress {
|
|
236
|
-
url: String,
|
|
237
|
-
progress: f64,
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
fn download_file(app: AppHandle, url: String) {
|
|
241
|
-
// 모든 윈도우에 Global Event
|
|
242
|
-
app.emit("download-progress", DownloadProgress {
|
|
243
|
-
url, progress: 0.5,
|
|
244
|
-
}).unwrap();
|
|
245
|
-
|
|
246
|
-
// 특정 윈도우에만
|
|
247
|
-
app.emit_to("main", "download-complete", ()).unwrap();
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
// 프론트엔드 Event 리스닝
|
|
253
|
-
import { listen, once } from '@tauri-apps/api/event';
|
|
254
|
-
|
|
255
|
-
// 지속 리스닝
|
|
256
|
-
const unlisten = await listen<{ url: string; progress: number }>(
|
|
257
|
-
'download-progress',
|
|
258
|
-
(event) => console.log(`${event.payload.url}: ${event.payload.progress * 100}%`)
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
// 단발 리스닝
|
|
262
|
-
await once('download-complete', () => console.log('Done!'));
|
|
263
|
-
|
|
264
|
-
// 리스너 해제
|
|
265
|
-
unlisten();
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Channels (고속 스트리밍)
|
|
269
|
-
|
|
270
|
-
```rust
|
|
271
|
-
use tauri::ipc::Channel;
|
|
272
|
-
|
|
273
|
-
#[tauri::command]
|
|
274
|
-
fn stream_data(channel: Channel<String>) {
|
|
275
|
-
for i in 0..100 {
|
|
276
|
-
channel.send(format!("chunk {}", i)).unwrap();
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
## Security (보안)
|
|
284
|
-
|
|
285
|
-
### Capabilities
|
|
286
|
-
|
|
287
|
-
```json
|
|
288
|
-
// src-tauri/capabilities/default.json
|
|
289
|
-
{
|
|
290
|
-
"identifier": "default",
|
|
291
|
-
"description": "메인 윈도우 기본 권한",
|
|
292
|
-
"windows": ["main"],
|
|
293
|
-
"permissions": [
|
|
294
|
-
"core:default",
|
|
295
|
-
"core:window:allow-set-title",
|
|
296
|
-
"core:window:allow-close",
|
|
297
|
-
"shell:allow-open"
|
|
298
|
-
]
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
```json
|
|
303
|
-
// 플랫폼별 Capability
|
|
304
|
-
{
|
|
305
|
-
"identifier": "mobile-features",
|
|
306
|
-
"description": "모바일 전용 권한",
|
|
307
|
-
"windows": ["main"],
|
|
308
|
-
"platforms": ["iOS", "android"],
|
|
309
|
-
"permissions": [
|
|
310
|
-
"nfc:default",
|
|
311
|
-
"biometric:default"
|
|
312
|
-
]
|
|
313
|
-
}
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### Permission 패턴
|
|
317
|
-
|
|
318
|
-
```
|
|
319
|
-
core:default # Core 기본 권한
|
|
320
|
-
core:window:allow-<command> # Window 개별 허용
|
|
321
|
-
core:event:allow-listen # Event 리스닝 허용
|
|
322
|
-
<plugin>:default # Plugin 기본 권한
|
|
323
|
-
<plugin>:allow-<command> # Plugin 개별 허용
|
|
324
|
-
<plugin>:deny-<command> # Plugin 개별 거부
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### CSP 설정
|
|
328
|
-
|
|
329
|
-
```json
|
|
330
|
-
// tauri.conf.json > app > security > csp
|
|
331
|
-
{
|
|
332
|
-
"default-src": "'self' customprotocol: asset:",
|
|
333
|
-
"connect-src": "ipc: http://ipc.localhost",
|
|
334
|
-
"font-src": ["https://fonts.gstatic.com"],
|
|
335
|
-
"img-src": "'self' asset: http://asset.localhost blob: data:",
|
|
336
|
-
"style-src": "'unsafe-inline' 'self'"
|
|
337
|
-
}
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
---
|
|
341
|
-
|
|
342
|
-
## State Management (상태 관리)
|
|
343
|
-
|
|
344
|
-
```rust
|
|
345
|
-
use std::sync::Mutex;
|
|
346
|
-
use tauri::{Builder, Manager, State};
|
|
347
|
-
|
|
348
|
-
struct AppState {
|
|
349
|
-
counter: u32,
|
|
350
|
-
db_url: String,
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
#[tauri::command]
|
|
354
|
-
async fn increment(state: State<'_, Mutex<AppState>>) -> Result<u32, String> {
|
|
355
|
-
let mut s = state.lock().map_err(|e| e.to_string())?;
|
|
356
|
-
s.counter += 1;
|
|
357
|
-
Ok(s.counter)
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
fn main() {
|
|
361
|
-
Builder::default()
|
|
362
|
-
.setup(|app| {
|
|
363
|
-
// 불변 상태
|
|
364
|
-
app.manage(String::from("config-value"));
|
|
365
|
-
// 가변 상태
|
|
366
|
-
app.manage(Mutex::new(AppState { counter: 0, db_url: String::new() }));
|
|
367
|
-
Ok(())
|
|
368
|
-
})
|
|
369
|
-
.invoke_handler(tauri::generate_handler![increment])
|
|
370
|
-
.run(tauri::generate_context!())
|
|
371
|
-
.unwrap();
|
|
372
|
-
}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
**규칙:**
|
|
376
|
-
- Arc 불필요 (State<T>가 내부 처리)
|
|
377
|
-
- 가변 상태 → `Mutex<T>` (std::sync::Mutex 권장)
|
|
378
|
-
- 비동기 코드에서 await 포인트 넘어 lock 유지 금지
|
|
379
|
-
- 잘못된 타입 접근 → 런타임 패닉 (타입 별칭 권장)
|
|
380
|
-
|
|
381
|
-
---
|
|
382
|
-
|
|
383
|
-
## Plugins (v2)
|
|
384
|
-
|
|
385
|
-
### v1 Built-in → v2 Plugin 전환
|
|
386
|
-
|
|
387
|
-
| 기능 | v2 Plugin 패키지 |
|
|
388
|
-
|------|-----------------|
|
|
389
|
-
| Clipboard | `@tauri-apps/plugin-clipboard-manager` |
|
|
390
|
-
| Dialog | `@tauri-apps/plugin-dialog` |
|
|
391
|
-
| File System | `@tauri-apps/plugin-fs` |
|
|
392
|
-
| HTTP | `@tauri-apps/plugin-http` |
|
|
393
|
-
| Shell | `@tauri-apps/plugin-shell` |
|
|
394
|
-
| Process | `@tauri-apps/plugin-process` |
|
|
395
|
-
| OS Info | `@tauri-apps/plugin-os` |
|
|
396
|
-
| Store | `@tauri-apps/plugin-store` |
|
|
397
|
-
| Notification | `@tauri-apps/plugin-notification` |
|
|
398
|
-
| Global Shortcut | `@tauri-apps/plugin-global-shortcut` |
|
|
399
|
-
|
|
400
|
-
### Plugin 사용 패턴
|
|
401
|
-
|
|
402
|
-
```bash
|
|
403
|
-
# 설치 (Rust + JS 바인딩)
|
|
404
|
-
npm add @tauri-apps/plugin-shell
|
|
405
|
-
cargo add tauri-plugin-shell -F tauri-plugin-shell/build
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
```rust
|
|
409
|
-
// main.rs에 등록
|
|
410
|
-
fn main() {
|
|
411
|
-
tauri::Builder::default()
|
|
412
|
-
.plugin(tauri_plugin_shell::init())
|
|
413
|
-
.run(tauri::generate_context!())
|
|
414
|
-
.unwrap();
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
```json
|
|
419
|
-
// capabilities에 Permission 추가
|
|
420
|
-
{
|
|
421
|
-
"permissions": ["shell:allow-open"]
|
|
422
|
-
}
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
---
|
|
426
|
-
|
|
427
|
-
## Configuration
|
|
428
|
-
|
|
429
|
-
### 플랫폼별 설정
|
|
430
|
-
|
|
431
|
-
```
|
|
432
|
-
src-tauri/
|
|
433
|
-
├── tauri.conf.json # 공통 설정
|
|
434
|
-
├── tauri.windows.conf.json # Windows 오버라이드
|
|
435
|
-
├── tauri.macos.conf.json # macOS 오버라이드
|
|
436
|
-
├── tauri.linux.conf.json # Linux 오버라이드
|
|
437
|
-
├── tauri.android.conf.json # Android 오버라이드
|
|
438
|
-
└── tauri.ios.conf.json # iOS 오버라이드
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
### 핵심 설정 필드
|
|
442
|
-
|
|
443
|
-
```json
|
|
444
|
-
{
|
|
445
|
-
"productName": "My App",
|
|
446
|
-
"version": "1.0.0",
|
|
447
|
-
"identifier": "com.example.myapp",
|
|
448
|
-
"build": {
|
|
449
|
-
"beforeDevCommand": "npm run dev",
|
|
450
|
-
"beforeBuildCommand": "npm run build",
|
|
451
|
-
"devUrl": "http://localhost:5173",
|
|
452
|
-
"frontendDist": "../dist"
|
|
453
|
-
},
|
|
454
|
-
"app": {
|
|
455
|
-
"windows": [{ "title": "My App", "width": 1024, "height": 768 }],
|
|
456
|
-
"security": { "csp": "..." }
|
|
457
|
-
},
|
|
458
|
-
"bundle": {
|
|
459
|
-
"active": true,
|
|
460
|
-
"targets": ["deb", "rpm", "appimage", "msi", "nsis", "dmg", "app"],
|
|
461
|
-
"icon": ["icons/32x32.png", "icons/icon.icns", "icons/icon.ico"],
|
|
462
|
-
"android": { "minSdkVersion": 24 },
|
|
463
|
-
"macOS": { "minimumSystemVersion": "10.13" }
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
```
|
|
130
|
+
<quick_reference>
|
|
467
131
|
|
|
468
|
-
|
|
132
|
+
## Quick Reference
|
|
469
133
|
|
|
470
|
-
|
|
134
|
+
### Command 기본 패턴
|
|
471
135
|
|
|
472
136
|
```rust
|
|
473
|
-
// Command 기본 패턴
|
|
474
137
|
#[tauri::command]
|
|
475
138
|
async fn my_command(arg: String, state: State<'_, Mutex<AppState>>) -> Result<MyResponse, MyError> {
|
|
476
139
|
let s = state.lock().map_err(|e| MyError::Lock(e.to_string()))?;
|
|
@@ -478,16 +141,26 @@ async fn my_command(arg: String, state: State<'_, Mutex<AppState>>) -> Result<My
|
|
|
478
141
|
}
|
|
479
142
|
```
|
|
480
143
|
|
|
144
|
+
### invoke 기본 패턴
|
|
145
|
+
|
|
481
146
|
```typescript
|
|
482
|
-
|
|
147
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
483
148
|
const result = await invoke<ResponseType>('my_command', { arg: 'value' });
|
|
484
149
|
```
|
|
485
150
|
|
|
151
|
+
### Capability 기본 패턴
|
|
152
|
+
|
|
486
153
|
```json
|
|
487
|
-
// Capability 기본 패턴
|
|
488
154
|
{ "identifier": "main", "windows": ["main"], "permissions": ["core:default", "plugin:allow-command"] }
|
|
489
155
|
```
|
|
490
156
|
|
|
157
|
+
### Event 리스닝
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { listen } from '@tauri-apps/api/event';
|
|
161
|
+
const unlisten = await listen<PayloadType>('event-name', (e) => console.log(e.payload));
|
|
162
|
+
```
|
|
163
|
+
|
|
491
164
|
</quick_reference>
|
|
492
165
|
|
|
493
166
|
---
|
|
@@ -505,6 +178,21 @@ const result = await invoke<ResponseType>('my_command', { arg: 'value' });
|
|
|
505
178
|
- Window: `Window` → `WebviewWindow`, `get_window()` → `get_webview_window()`
|
|
506
179
|
- Security: allowlist → Capabilities/Permissions 시스템
|
|
507
180
|
- Plugins: 모든 시스템 API가 별도 plugin으로 분리
|
|
508
|
-
- Events: source-based → target-based 필터링
|
|
509
181
|
|
|
510
182
|
</version_info>
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
<references_index>
|
|
187
|
+
|
|
188
|
+
## References
|
|
189
|
+
|
|
190
|
+
| 문서 | 내용 |
|
|
191
|
+
|------|------|
|
|
192
|
+
| **@references/ipc-guide.md** | Commands, Events, Channels 상세 |
|
|
193
|
+
| **@references/security-guide.md** | Capabilities, CSP 상세 |
|
|
194
|
+
| **@references/state-guide.md** | State 패턴, Mutex 규칙 |
|
|
195
|
+
| **@references/plugins-guide.md** | v2 Plugin 목록, 사용법 |
|
|
196
|
+
| **@references/configuration.md** | 플랫폼별 설정 상세 |
|
|
197
|
+
|
|
198
|
+
</references_index>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
> Tauri v2 설정 상세 가이드
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 플랫폼별 설정
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src-tauri/
|
|
11
|
+
├── tauri.conf.json # 공통 설정
|
|
12
|
+
├── tauri.windows.conf.json # Windows 오버라이드
|
|
13
|
+
├── tauri.macos.conf.json # macOS 오버라이드
|
|
14
|
+
├── tauri.linux.conf.json # Linux 오버라이드
|
|
15
|
+
├── tauri.android.conf.json # Android 오버라이드
|
|
16
|
+
└── tauri.ios.conf.json # iOS 오버라이드
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 핵심 설정 필드
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"productName": "My App",
|
|
26
|
+
"version": "1.0.0",
|
|
27
|
+
"identifier": "com.example.myapp",
|
|
28
|
+
"build": {
|
|
29
|
+
"beforeDevCommand": "npm run dev",
|
|
30
|
+
"beforeBuildCommand": "npm run build",
|
|
31
|
+
"devUrl": "http://localhost:5173",
|
|
32
|
+
"frontendDist": "../dist"
|
|
33
|
+
},
|
|
34
|
+
"app": {
|
|
35
|
+
"windows": [{ "title": "My App", "width": 1024, "height": 768 }],
|
|
36
|
+
"security": { "csp": "..." }
|
|
37
|
+
},
|
|
38
|
+
"bundle": {
|
|
39
|
+
"active": true,
|
|
40
|
+
"targets": ["deb", "rpm", "appimage", "msi", "nsis", "dmg", "app"],
|
|
41
|
+
"icon": ["icons/32x32.png", "icons/icon.icns", "icons/icon.ico"],
|
|
42
|
+
"android": { "minSdkVersion": 24 },
|
|
43
|
+
"macOS": { "minimumSystemVersion": "10.13" }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# IPC Guide
|
|
2
|
+
|
|
3
|
+
> Inter-Process Communication 상세 가이드
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Commands (프론트엔드 → Rust)
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
// src-tauri/src/commands/mod.rs
|
|
11
|
+
use serde::{Deserialize, Serialize};
|
|
12
|
+
use tauri::State;
|
|
13
|
+
use std::sync::Mutex;
|
|
14
|
+
|
|
15
|
+
#[derive(Serialize, Deserialize)]
|
|
16
|
+
pub struct User {
|
|
17
|
+
pub id: u32,
|
|
18
|
+
pub name: String,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 기본 Command
|
|
22
|
+
#[tauri::command]
|
|
23
|
+
fn greet(name: String) -> String {
|
|
24
|
+
format!("Hello, {}!", name)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 비동기 Command + State
|
|
28
|
+
#[tauri::command]
|
|
29
|
+
async fn get_user(id: u32, db: State<'_, Mutex<Database>>) -> Result<User, String> {
|
|
30
|
+
let db = db.lock().map_err(|e| e.to_string())?;
|
|
31
|
+
db.find_user(id).ok_or("User not found".into())
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 에러 처리 (thiserror 권장)
|
|
35
|
+
#[derive(Debug, thiserror::Error)]
|
|
36
|
+
pub enum AppError {
|
|
37
|
+
#[error("Not found: {0}")]
|
|
38
|
+
NotFound(String),
|
|
39
|
+
#[error("Internal error: {0}")]
|
|
40
|
+
Internal(String),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
impl serde::Serialize for AppError {
|
|
44
|
+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
45
|
+
where S: serde::Serializer {
|
|
46
|
+
serializer.serialize_str(self.to_string().as_ref())
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[tauri::command]
|
|
51
|
+
async fn delete_user(id: u32) -> Result<(), AppError> {
|
|
52
|
+
// ...
|
|
53
|
+
Err(AppError::NotFound(format!("User {} not found", id)))
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// 프론트엔드 invoke
|
|
59
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
60
|
+
|
|
61
|
+
// 기본 호출
|
|
62
|
+
const greeting = await invoke<string>('greet', { name: 'World' });
|
|
63
|
+
|
|
64
|
+
// 타입 안전 호출
|
|
65
|
+
interface User { id: number; name: string; }
|
|
66
|
+
const user = await invoke<User>('get_user', { id: 1 });
|
|
67
|
+
|
|
68
|
+
// 에러 처리
|
|
69
|
+
try {
|
|
70
|
+
await invoke('delete_user', { id: 999 });
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error('Command failed:', error);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Events (양방향 통신)
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
// Rust → 프론트엔드 Event 발행
|
|
82
|
+
use tauri::{AppHandle, Emitter};
|
|
83
|
+
|
|
84
|
+
#[derive(Clone, Serialize)]
|
|
85
|
+
struct DownloadProgress {
|
|
86
|
+
url: String,
|
|
87
|
+
progress: f64,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fn download_file(app: AppHandle, url: String) {
|
|
91
|
+
// 모든 윈도우에 Global Event
|
|
92
|
+
app.emit("download-progress", DownloadProgress {
|
|
93
|
+
url, progress: 0.5,
|
|
94
|
+
}).unwrap();
|
|
95
|
+
|
|
96
|
+
// 특정 윈도우에만
|
|
97
|
+
app.emit_to("main", "download-complete", ()).unwrap();
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// 프론트엔드 Event 리스닝
|
|
103
|
+
import { listen, once } from '@tauri-apps/api/event';
|
|
104
|
+
|
|
105
|
+
// 지속 리스닝
|
|
106
|
+
const unlisten = await listen<{ url: string; progress: number }>(
|
|
107
|
+
'download-progress',
|
|
108
|
+
(event) => console.log(`${event.payload.url}: ${event.payload.progress * 100}%`)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// 단발 리스닝
|
|
112
|
+
await once('download-complete', () => console.log('Done!'));
|
|
113
|
+
|
|
114
|
+
// 리스너 해제
|
|
115
|
+
unlisten();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Channels (고속 스트리밍)
|
|
121
|
+
|
|
122
|
+
```rust
|
|
123
|
+
use tauri::ipc::Channel;
|
|
124
|
+
|
|
125
|
+
#[tauri::command]
|
|
126
|
+
fn stream_data(channel: Channel<String>) {
|
|
127
|
+
for i in 0..100 {
|
|
128
|
+
channel.send(format!("chunk {}", i)).unwrap();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Plugins Guide
|
|
2
|
+
|
|
3
|
+
> Tauri v2 Plugin 사용 가이드
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## v1 Built-in → v2 Plugin 전환
|
|
8
|
+
|
|
9
|
+
| 기능 | v2 Plugin 패키지 |
|
|
10
|
+
|------|-----------------|
|
|
11
|
+
| Clipboard | `@tauri-apps/plugin-clipboard-manager` |
|
|
12
|
+
| Dialog | `@tauri-apps/plugin-dialog` |
|
|
13
|
+
| File System | `@tauri-apps/plugin-fs` |
|
|
14
|
+
| HTTP | `@tauri-apps/plugin-http` |
|
|
15
|
+
| Shell | `@tauri-apps/plugin-shell` |
|
|
16
|
+
| Process | `@tauri-apps/plugin-process` |
|
|
17
|
+
| OS Info | `@tauri-apps/plugin-os` |
|
|
18
|
+
| Store | `@tauri-apps/plugin-store` |
|
|
19
|
+
| Notification | `@tauri-apps/plugin-notification` |
|
|
20
|
+
| Global Shortcut | `@tauri-apps/plugin-global-shortcut` |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Plugin 사용 패턴
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 설치 (Rust + JS 바인딩)
|
|
28
|
+
npm add @tauri-apps/plugin-shell
|
|
29
|
+
cargo add tauri-plugin-shell -F tauri-plugin-shell/build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```rust
|
|
33
|
+
// main.rs에 등록
|
|
34
|
+
fn main() {
|
|
35
|
+
tauri::Builder::default()
|
|
36
|
+
.plugin(tauri_plugin_shell::init())
|
|
37
|
+
.run(tauri::generate_context!())
|
|
38
|
+
.unwrap();
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
// capabilities에 Permission 추가
|
|
44
|
+
{
|
|
45
|
+
"permissions": ["shell:allow-open"]
|
|
46
|
+
}
|
|
47
|
+
```
|