@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.
@@ -0,0 +1,64 @@
1
+ # Security Guide
2
+
3
+ > Tauri v2 보안 상세 가이드
4
+
5
+ ---
6
+
7
+ ## Capabilities
8
+
9
+ ```json
10
+ // src-tauri/capabilities/default.json
11
+ {
12
+ "identifier": "default",
13
+ "description": "메인 윈도우 기본 권한",
14
+ "windows": ["main"],
15
+ "permissions": [
16
+ "core:default",
17
+ "core:window:allow-set-title",
18
+ "core:window:allow-close",
19
+ "shell:allow-open"
20
+ ]
21
+ }
22
+ ```
23
+
24
+ ```json
25
+ // 플랫폼별 Capability
26
+ {
27
+ "identifier": "mobile-features",
28
+ "description": "모바일 전용 권한",
29
+ "windows": ["main"],
30
+ "platforms": ["iOS", "android"],
31
+ "permissions": [
32
+ "nfc:default",
33
+ "biometric:default"
34
+ ]
35
+ }
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Permission 패턴
41
+
42
+ ```
43
+ core:default # Core 기본 권한
44
+ core:window:allow-<command> # Window 개별 허용
45
+ core:event:allow-listen # Event 리스닝 허용
46
+ <plugin>:default # Plugin 기본 권한
47
+ <plugin>:allow-<command> # Plugin 개별 허용
48
+ <plugin>:deny-<command> # Plugin 개별 거부
49
+ ```
50
+
51
+ ---
52
+
53
+ ## CSP 설정
54
+
55
+ ```json
56
+ // tauri.conf.json > app > security > csp
57
+ {
58
+ "default-src": "'self' customprotocol: asset:",
59
+ "connect-src": "ipc: http://ipc.localhost",
60
+ "font-src": ["https://fonts.gstatic.com"],
61
+ "img-src": "'self' asset: http://asset.localhost blob: data:",
62
+ "style-src": "'unsafe-inline' 'self'"
63
+ }
64
+ ```
@@ -0,0 +1,49 @@
1
+ # State Management Guide
2
+
3
+ > Tauri Rust 상태 관리 상세 가이드
4
+
5
+ ---
6
+
7
+ ## State 패턴
8
+
9
+ ```rust
10
+ use std::sync::Mutex;
11
+ use tauri::{Builder, Manager, State};
12
+
13
+ struct AppState {
14
+ counter: u32,
15
+ db_url: String,
16
+ }
17
+
18
+ #[tauri::command]
19
+ async fn increment(state: State<'_, Mutex<AppState>>) -> Result<u32, String> {
20
+ let mut s = state.lock().map_err(|e| e.to_string())?;
21
+ s.counter += 1;
22
+ Ok(s.counter)
23
+ }
24
+
25
+ fn main() {
26
+ Builder::default()
27
+ .setup(|app| {
28
+ // 불변 상태
29
+ app.manage(String::from("config-value"));
30
+ // 가변 상태
31
+ app.manage(Mutex::new(AppState { counter: 0, db_url: String::new() }));
32
+ Ok(())
33
+ })
34
+ .invoke_handler(tauri::generate_handler![increment])
35
+ .run(tauri::generate_context!())
36
+ .unwrap();
37
+ }
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 규칙
43
+
44
+ | 규칙 | 설명 |
45
+ |------|------|
46
+ | **Arc 불필요** | State<T>가 내부 처리 |
47
+ | **가변 상태** | `Mutex<T>` 사용 (std::sync::Mutex 권장) |
48
+ | **await 제한** | await 포인트 넘어 lock 유지 금지 |
49
+ | **타입 별칭** | 잘못된 타입 접근 → 런타임 패닉 |
@@ -0,0 +1,66 @@
1
+ # Async Execution Model
2
+
3
+ > Tauri Command의 비동기 실행 원리
4
+
5
+ ---
6
+
7
+ ## async vs non-async Command
8
+
9
+ | 유형 | 실행 위치 | 사용 시점 |
10
+ |------|----------|----------|
11
+ | `async fn` | Tokio async runtime | IO 작업 (DB, HTTP, File) |
12
+ | `fn` (non-async) | 별도 스레드풀 | CPU 작업 (계산, 변환) |
13
+
14
+ ```rust
15
+ // ✅ async Command: Tokio runtime에서 실행
16
+ // - await 사용 가능
17
+ // - IO 작업에 적합 (non-blocking)
18
+ #[tauri::command]
19
+ async fn fetch_data(url: String) -> Result<String, String> {
20
+ let response = reqwest::get(&url).await.map_err(|e| e.to_string())?;
21
+ response.text().await.map_err(|e| e.to_string())
22
+ }
23
+
24
+ // ✅ non-async Command: 스레드풀에서 실행
25
+ // - CPU 집약적 작업에 적합
26
+ // - 메인 스레드 블로킹 방지
27
+ #[tauri::command]
28
+ fn heavy_computation(data: Vec<u8>) -> Vec<u8> {
29
+ // CPU 집약적 작업
30
+ data.iter().map(|x| x.wrapping_mul(2)).collect()
31
+ }
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Tokio Runtime 동작
37
+
38
+ ```
39
+ ┌─────────────────────────────────────────────────────────────┐
40
+ │ Tauri Application │
41
+ │ ┌─────────────────────────────────────────────────────┐ │
42
+ │ │ Tokio Runtime │ │
43
+ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
44
+ │ │ │ async Task 1│ │ async Task 2│ │ async Task 3│ │ │
45
+ │ │ │ (fetch_data)│ │ (read_file) │ │ (db_query) │ │ │
46
+ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
47
+ │ └─────────────────────────────────────────────────────┘ │
48
+ │ ┌─────────────────────────────────────────────────────┐ │
49
+ │ │ Thread Pool (sync commands) │ │
50
+ │ │ ┌─────────────┐ ┌─────────────┐ │ │
51
+ │ │ │ sync Task 1 │ │ sync Task 2 │ │ │
52
+ │ │ │ (compute) │ │ (transform) │ │ │
53
+ │ │ └─────────────┘ └─────────────┘ │ │
54
+ │ └─────────────────────────────────────────────────────┘ │
55
+ └─────────────────────────────────────────────────────────────┘
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 주의사항
61
+
62
+ | 패턴 | 설명 |
63
+ |------|------|
64
+ | **async에서 blocking 금지** | `std::thread::sleep()` 대신 `tokio::time::sleep()` 사용 |
65
+ | **sync에서 await 불가** | non-async 함수에서는 `.await` 사용 불가 |
66
+ | **spawn_blocking** | async 내에서 blocking 작업 필요 시 `tokio::task::spawn_blocking` |
@@ -0,0 +1,46 @@
1
+ # Data Flow
2
+
3
+ > Tauri 데이터 흐름 패턴
4
+
5
+ ---
6
+
7
+ ## Command Flow (읽기)
8
+
9
+ ```
10
+ ┌─────────┐ invoke ┌─────────┐ validate ┌─────────┐ execute ┌─────────┐
11
+ │ React │───────────▶│ IPC │─────────────▶│Capability│────────────▶│ Command │
12
+ │ Hook │ │ Bridge │ │ Check │ │ Handler │
13
+ └─────────┘ └─────────┘ └─────────┘ └────┬────┘
14
+ ▲ │
15
+ │ ▼
16
+ │ ┌─────────┐
17
+ │◀────────────────────────────────────────────────────────────────│ State │
18
+ │ Result<T> │ / DB │
19
+ └─────────┘
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Event Flow (실시간 업데이트)
25
+
26
+ ```
27
+ ┌─────────┐ ┌─────────┐
28
+ │ Rust │ app.emit("event", data) │ Frontend│
29
+ │ Core │─────────────────────────────▶│ listen()│
30
+ └─────────┘ └─────────┘
31
+
32
+ │ Background Task (download, sync, etc.)
33
+
34
+ └──▶ Progress Event ──▶ UI Update
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Plugin Data Flow
40
+
41
+ ```
42
+ Frontend ──▶ invoke("plugin:fs|read") ──▶ Capability Check ──▶ Plugin Handler ──▶ OS API
43
+
44
+ ◀─────────────────────┘
45
+ Result/Error
46
+ ```
@@ -0,0 +1,113 @@
1
+ # Error Handling
2
+
3
+ > Rust Error → JSON → TypeScript Error 전파 메커니즘
4
+
5
+ ---
6
+
7
+ ## 커스텀 Error 타입 정의
8
+
9
+ ```rust
10
+ // src-tauri/src/error.rs
11
+ use serde::Serialize;
12
+
13
+ #[derive(Debug, thiserror::Error)]
14
+ pub enum AppError {
15
+ #[error("Not found: {0}")]
16
+ NotFound(String),
17
+
18
+ #[error("Unauthorized: {0}")]
19
+ Unauthorized(String),
20
+
21
+ #[error("Database error: {0}")]
22
+ Database(String),
23
+
24
+ #[error("Internal error: {0}")]
25
+ Internal(String),
26
+ }
27
+
28
+ // serde::Serialize 구현 필수 (JSON 직렬화)
29
+ impl Serialize for AppError {
30
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31
+ where
32
+ S: serde::Serializer,
33
+ {
34
+ serializer.serialize_str(self.to_string().as_ref())
35
+ }
36
+ }
37
+
38
+ // From 변환 구현 (다른 에러 타입 자동 변환)
39
+ impl From<sqlx::Error> for AppError {
40
+ fn from(e: sqlx::Error) -> Self {
41
+ AppError::Database(e.to_string())
42
+ }
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Command에서 에러 반환
49
+
50
+ ```rust
51
+ #[tauri::command]
52
+ async fn get_user(id: u32, db: State<'_, DbPool>) -> Result<User, AppError> {
53
+ let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
54
+ .fetch_optional(&**db)
55
+ .await? // sqlx::Error → AppError 자동 변환
56
+ .ok_or_else(|| AppError::NotFound(format!("User {} not found", id)))?;
57
+
58
+ Ok(user)
59
+ }
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Frontend 에러 처리
65
+
66
+ ```typescript
67
+ import { invoke } from '@tauri-apps/api/core';
68
+
69
+ interface User {
70
+ id: number;
71
+ name: string;
72
+ }
73
+
74
+ async function getUser(id: number): Promise<User | null> {
75
+ try {
76
+ return await invoke<User>('get_user', { id });
77
+ } catch (error) {
78
+ // error는 Rust에서 직렬화된 문자열
79
+ const errorMessage = error as string;
80
+
81
+ if (errorMessage.includes('Not found')) {
82
+ console.warn('User not found:', id);
83
+ return null;
84
+ }
85
+
86
+ if (errorMessage.includes('Unauthorized')) {
87
+ // 인증 에러 처리
88
+ window.location.href = '/login';
89
+ return null;
90
+ }
91
+
92
+ // 기타 에러
93
+ console.error('Failed to get user:', errorMessage);
94
+ throw new Error(errorMessage);
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## 에러 전파 흐름
102
+
103
+ ```
104
+ ┌─────────────┐ Result<T,E> ┌─────────────┐ JSON ┌─────────────┐
105
+ │ Rust │ ─────────────────▶│ IPC │ ──────────▶ │ Frontend │
106
+ │ Command │ Err(AppError) │ Bridge │ serialize │ catch() │
107
+ └─────────────┘ └─────────────┘ └─────────────┘
108
+ │ │
109
+ │ #[error("...")] 메시지 │ error as string
110
+ │ │
111
+ ▼ ▼
112
+ "Not found: User 1 not found" "Not found: User 1 not found"
113
+ ```
@@ -0,0 +1,64 @@
1
+ # Mobile Architecture
2
+
3
+ > Tauri v2 모바일 아키텍처 상세 가이드
4
+
5
+ ---
6
+
7
+ ## 엔트리포인트 구조
8
+
9
+ ```rust
10
+ // lib.rs - 공유 로직 (데스크톱 + 모바일)
11
+ #[cfg_attr(mobile, tauri::mobile_entry_point)]
12
+ pub fn run() {
13
+ tauri::Builder::default()
14
+ .invoke_handler(tauri::generate_handler![...])
15
+ .run(tauri::generate_context!())
16
+ .expect("error");
17
+ }
18
+
19
+ // main.rs - 데스크톱 전용
20
+ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
21
+ fn main() {
22
+ my_app_lib::run()
23
+ }
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Mobile Plugin 구조
29
+
30
+ ```
31
+ plugin/
32
+ ├── src/
33
+ │ ├── lib.rs # 공유 로직
34
+ │ ├── desktop.rs # 데스크톱 구현
35
+ │ └── mobile.rs # 모바일 브릿지 (Kotlin/Swift 호출)
36
+ ├── android/
37
+ │ └── src/.../Plugin.kt # Kotlin 구현
38
+ └── ios/
39
+ └── Sources/Plugin.swift # Swift 구현
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 플랫폼 설정
45
+
46
+ | 파일 | 용도 |
47
+ |------|------|
48
+ | `tauri.conf.json` | 공통 설정 |
49
+ | `tauri.android.conf.json` | Android 오버라이드 |
50
+ | `tauri.ios.conf.json` | iOS 오버라이드 |
51
+ | `tauri.windows.conf.json` | Windows 오버라이드 |
52
+ | `tauri.macos.conf.json` | macOS 오버라이드 |
53
+ | `tauri.linux.conf.json` | Linux 오버라이드 |
54
+
55
+ ---
56
+
57
+ ## 모바일 개발 주의사항
58
+
59
+ | 항목 | 설명 |
60
+ |------|------|
61
+ | **iOS 개발** | macOS에서만 가능 |
62
+ | **Android 메인 스레드** | 블로킹 I/O → UI 프리징, Coroutine 사용 필수 |
63
+ | **최소 Android SDK** | 24 (Android 7.0) |
64
+ | **lib.rs 엔트리포인트** | 모바일은 라이브러리로 로드 |
@@ -0,0 +1,85 @@
1
+ # Security Model
2
+
3
+ > Tauri v2 보안 모델 상세 가이드
4
+
5
+ ---
6
+
7
+ ## Trust Boundaries
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────┐
11
+ │ UNTRUSTED ZONE │
12
+ │ ┌───────────────────────────────────────────────────────┐ │
13
+ │ │ WebView │ │
14
+ │ │ • HTML/CSS/JS │ │
15
+ │ │ • 사용자 입력 │ │
16
+ │ │ • 외부 콘텐츠 (제한적) │ │
17
+ │ └───────────────────────────────────────────────────────┘ │
18
+ └─────────────────────────┬───────────────────────────────────┘
19
+ │ IPC (Capabilities 검증)
20
+
21
+ ┌─────────────────────────────────────────────────────────────┐
22
+ │ TRUSTED ZONE │
23
+ │ ┌───────────────────────────────────────────────────────┐ │
24
+ │ │ Rust Core │ │
25
+ │ │ • 시스템 API 접근 │ │
26
+ │ │ • 파일 시스템 │ │
27
+ │ │ • 네트워크 │ │
28
+ │ │ • 민감 데이터 │ │
29
+ │ └───────────────────────────────────────────────────────┘ │
30
+ └─────────────────────────────────────────────────────────────┘
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Capabilities System
36
+
37
+ ```json
38
+ // src-tauri/capabilities/default.json
39
+ {
40
+ "identifier": "default",
41
+ "description": "메인 윈도우 기본 권한",
42
+ "windows": ["main"],
43
+ "permissions": [
44
+ "core:default",
45
+ "core:window:allow-set-title",
46
+ "shell:allow-open"
47
+ ]
48
+ }
49
+ ```
50
+
51
+ | Permission 패턴 | 설명 |
52
+ |----------------|------|
53
+ | `core:default` | Core 기본 권한 |
54
+ | `core:window:allow-<cmd>` | Window 개별 허용 |
55
+ | `<plugin>:default` | Plugin 기본 권한 |
56
+ | `<plugin>:allow-<cmd>` | Plugin 개별 허용 |
57
+ | `<plugin>:deny-<cmd>` | Plugin 개별 거부 |
58
+
59
+ ---
60
+
61
+ ## CSP (Content Security Policy)
62
+
63
+ ```json
64
+ // tauri.conf.json > app > security > csp
65
+ {
66
+ "default-src": "'self' customprotocol: asset:",
67
+ "connect-src": "ipc: http://ipc.localhost",
68
+ "img-src": "'self' asset: http://asset.localhost blob: data:",
69
+ "style-src": "'unsafe-inline' 'self'"
70
+ }
71
+ ```
72
+
73
+ ### CSP 프로토콜 설명
74
+
75
+ | 프로토콜 | 설명 |
76
+ |---------|------|
77
+ | `'self'` | 같은 origin 리소스만 허용 |
78
+ | `customprotocol:` | Tauri 커스텀 프로토콜 (앱 내부 통신) |
79
+ | `asset:` | 로컬 파일 접근 프로토콜 (Tauri v2) |
80
+ | `ipc:` | Frontend ↔ Rust IPC 통신 프로토콜 |
81
+ | `http://ipc.localhost` | IPC 브릿지 localhost 접근 |
82
+ | `http://asset.localhost` | 로컬 에셋 서빙 |
83
+ | `'unsafe-inline'` | 인라인 스타일 허용 (CSS-in-JS 라이브러리 호환, style-src만) |
84
+ | `blob:` | Blob URL 허용 (동적 이미지 생성) |
85
+ | `data:` | Data URL 허용 (Base64 이미지) |
@@ -0,0 +1,117 @@
1
+ # State Management
2
+
3
+ > Tauri Rust 상태 관리 상세 가이드
4
+
5
+ ---
6
+
7
+ ## Rust State 패턴
8
+
9
+ ```rust
10
+ use std::sync::Mutex;
11
+ use tauri::{Builder, Manager, State};
12
+
13
+ // 상태 정의
14
+ struct AppState {
15
+ counter: u32,
16
+ db_url: String,
17
+ }
18
+
19
+ // Command에서 사용
20
+ #[tauri::command]
21
+ async fn increment(state: State<'_, Mutex<AppState>>) -> Result<u32, String> {
22
+ let mut s = state.lock().map_err(|e| e.to_string())?;
23
+ s.counter += 1;
24
+ Ok(s.counter)
25
+ }
26
+
27
+ // 등록
28
+ fn main() {
29
+ Builder::default()
30
+ .setup(|app| {
31
+ app.manage(Mutex::new(AppState { counter: 0, db_url: String::new() }));
32
+ Ok(())
33
+ })
34
+ .invoke_handler(tauri::generate_handler![increment])
35
+ .run(tauri::generate_context!())
36
+ .unwrap();
37
+ }
38
+ ```
39
+
40
+ ---
41
+
42
+ ## State 규칙
43
+
44
+ | 규칙 | 설명 |
45
+ |------|------|
46
+ | **Arc 불필요** | State<T>가 내부적으로 Arc 처리 |
47
+ | **가변 상태** | `Mutex<T>` 사용 |
48
+ | **await 제한** | await 포인트 넘어 lock 유지 금지 (데드락) |
49
+ | **타입 별칭** | 잘못된 타입 접근 → 런타임 패닉 |
50
+
51
+ ---
52
+
53
+ ## Mutex 선택 기준
54
+
55
+ | Mutex 종류 | 사용 시점 | 예시 |
56
+ |-----------|----------|------|
57
+ | `std::sync::Mutex` | CPU 작업 (빠른 연산) | counter, config, cache |
58
+ | `tokio::sync::Mutex` | IO 작업 (await 필요) | DB, HTTP, File |
59
+
60
+ ---
61
+
62
+ ## await 걸쳐 lock 유지 금지
63
+
64
+ ```rust
65
+ // ❌ 잘못된 예시: await 걸쳐 lock 유지 → 데드락 위험
66
+ #[tauri::command]
67
+ async fn bad_example(state: State<'_, std::sync::Mutex<AppState>>) -> Result<(), String> {
68
+ let s = state.lock().unwrap(); // lock 획득
69
+ some_async_operation().await; // await 포인트 → 다른 스레드 블로킹
70
+ s.counter += 1; // lock 아직 유지 중
71
+ Ok(())
72
+ }
73
+
74
+ // ✅ 올바른 예시 1: lock 범위 최소화
75
+ #[tauri::command]
76
+ async fn good_example_1(state: State<'_, std::sync::Mutex<AppState>>) -> Result<(), String> {
77
+ let data = {
78
+ let s = state.lock().unwrap();
79
+ s.get_data() // lock 범위 내에서 필요한 작업만
80
+ }; // lock 해제
81
+ some_async_operation(data).await; // await는 lock 밖에서
82
+ Ok(())
83
+ }
84
+
85
+ // ✅ 올바른 예시 2: IO 작업 시 tokio::sync::Mutex
86
+ #[tauri::command]
87
+ async fn good_example_2(state: State<'_, tokio::sync::Mutex<DbConnection>>) -> Result<(), String> {
88
+ let mut db = state.lock().await; // tokio Mutex는 await 가능
89
+ db.query("SELECT * FROM users").await?;
90
+ Ok(())
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Frontend State (Zustand)
97
+
98
+ ```typescript
99
+ // stores/app.ts
100
+ import { create } from 'zustand';
101
+ import { persist } from 'zustand/middleware';
102
+
103
+ export const useAppStore = create(
104
+ persist(
105
+ (set) => ({
106
+ theme: 'dark' as 'light' | 'dark',
107
+ setTheme: (theme) => set({ theme }),
108
+ }),
109
+ { name: 'app-store' }
110
+ )
111
+ );
112
+ ```
113
+
114
+ | 상태 유형 | 위치 | 사용 |
115
+ |----------|------|------|
116
+ | **시스템 상태** | Rust State | DB 연결, 설정, 캐시 |
117
+ | **UI 상태** | Zustand | 테마, 사이드바, 임시 데이터 |
@@ -0,0 +1,31 @@
1
+ # Technology Stack
2
+
3
+ > Tauri v2 기술 스택
4
+
5
+ ---
6
+
7
+ ## Core Technologies
8
+
9
+ | Layer | Technology | Version |
10
+ |-------|------------|---------|
11
+ | Framework | Tauri | 2.x |
12
+ | Backend | Rust | stable |
13
+ | Frontend | React | 19.x |
14
+ | Bundler | Vite | 6.x |
15
+ | TypeScript | TS | 5.x |
16
+ | State (Client) | Zustand | latest |
17
+ | IPC API | @tauri-apps/api | 2.x |
18
+ | Window | TAO | - |
19
+ | WebView | WRY | - |
20
+
21
+ ---
22
+
23
+ ## WebView Engine
24
+
25
+ | 플랫폼 | WebView |
26
+ |--------|---------|
27
+ | Windows | WebView2 (Chromium) |
28
+ | macOS | WKWebView (WebKit) |
29
+ | Linux | webkitgtk (WebKit) |
30
+ | iOS | WKWebView (WebKit) |
31
+ | Android | WebView (Chromium) |