@kood/claude-code 0.6.6 → 0.7.0

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.
Files changed (170) hide show
  1. package/dist/index.js +7 -1
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/analyst.md +5 -0
  4. package/templates/.claude/agents/architect.md +5 -0
  5. package/templates/.claude/agents/build-fixer.md +1 -0
  6. package/templates/.claude/agents/code-reviewer.md +1 -0
  7. package/templates/.claude/agents/critic.md +4 -0
  8. package/templates/.claude/agents/deep-executor.md +1 -0
  9. package/templates/.claude/agents/dependency-manager.md +2 -0
  10. package/templates/.claude/agents/deployment-validator.md +2 -0
  11. package/templates/.claude/agents/designer.md +2 -0
  12. package/templates/.claude/agents/document-writer.md +3 -0
  13. package/templates/.claude/agents/explore.md +1 -0
  14. package/templates/.claude/agents/git-operator.md +2 -0
  15. package/templates/.claude/agents/implementation-executor.md +2 -0
  16. package/templates/.claude/agents/ko-to-en-translator.md +3 -0
  17. package/templates/.claude/agents/lint-fixer.md +2 -0
  18. package/templates/.claude/agents/planner.md +3 -0
  19. package/templates/.claude/agents/pm.md +349 -0
  20. package/templates/.claude/agents/qa-tester.md +1 -0
  21. package/templates/.claude/agents/refactor-advisor.md +4 -0
  22. package/templates/.claude/agents/researcher.md +9 -1
  23. package/templates/.claude/agents/scientist.md +1 -0
  24. package/templates/.claude/agents/security-reviewer.md +1 -0
  25. package/templates/.claude/agents/tdd-guide.md +1 -0
  26. package/templates/.claude/agents/vision.md +1 -0
  27. package/templates/.claude/instructions/agent-patterns/agent-teams-usage.md +376 -0
  28. package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
  29. package/templates/.claude/scripts/agent-teams/check-availability.sh +238 -0
  30. package/templates/.claude/scripts/agent-teams/setup-tmux.sh +125 -0
  31. package/templates/.claude/skills/agent-teams-setup/SKILL.md +460 -0
  32. package/templates/.claude/skills/brainstorm/SKILL.md +1 -0
  33. package/templates/.claude/skills/bug-fix/SKILL.md +1 -0
  34. package/templates/.claude/skills/crawler/SKILL.md +2 -0
  35. package/templates/.claude/skills/docs-creator/SKILL.md +1 -0
  36. package/templates/.claude/skills/docs-fetch/SKILL.md +6 -4
  37. package/templates/.claude/skills/docs-refactor/SKILL.md +1 -0
  38. package/templates/.claude/skills/elon-musk/SKILL.md +1 -0
  39. package/templates/.claude/skills/execute/SKILL.md +1 -0
  40. package/templates/.claude/skills/feedback/SKILL.md +1 -0
  41. package/templates/.claude/skills/figma-to-code/SKILL.md +1 -0
  42. package/templates/.claude/skills/genius-thinking/SKILL.md +1 -0
  43. package/templates/.claude/skills/global-uiux-design/SKILL.md +1 -0
  44. package/templates/.claude/skills/korea-uiux-design/SKILL.md +1 -0
  45. package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +1 -0
  46. package/templates/.claude/skills/plan/SKILL.md +1 -0
  47. package/templates/.claude/skills/prd/SKILL.md +1 -0
  48. package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
  49. package/templates/.claude/skills/project-optimizer/SKILL.md +375 -0
  50. package/templates/.claude/skills/project-optimizer/rules/arch-config-centralize.md +66 -0
  51. package/templates/.claude/skills/project-optimizer/rules/arch-hot-path.md +35 -0
  52. package/templates/.claude/skills/project-optimizer/rules/arch-interface-segregation.md +51 -0
  53. package/templates/.claude/skills/project-optimizer/rules/arch-module-boundary.md +42 -0
  54. package/templates/.claude/skills/project-optimizer/rules/build-cache.md +57 -0
  55. package/templates/.claude/skills/project-optimizer/rules/build-code-split.md +56 -0
  56. package/templates/.claude/skills/project-optimizer/rules/build-incremental.md +65 -0
  57. package/templates/.claude/skills/project-optimizer/rules/build-minify.md +61 -0
  58. package/templates/.claude/skills/project-optimizer/rules/build-tree-shake.md +60 -0
  59. package/templates/.claude/skills/project-optimizer/rules/code-complexity.md +65 -0
  60. package/templates/.claude/skills/project-optimizer/rules/code-dead-elimination.md +32 -0
  61. package/templates/.claude/skills/project-optimizer/rules/code-duplication.md +54 -0
  62. package/templates/.claude/skills/project-optimizer/rules/code-error-handling.md +75 -0
  63. package/templates/.claude/skills/project-optimizer/rules/code-naming.md +52 -0
  64. package/templates/.claude/skills/project-optimizer/rules/concurrency-defer-await.md +54 -0
  65. package/templates/.claude/skills/project-optimizer/rules/concurrency-parallel.md +90 -0
  66. package/templates/.claude/skills/project-optimizer/rules/concurrency-pipeline.md +68 -0
  67. package/templates/.claude/skills/project-optimizer/rules/concurrency-pool.md +68 -0
  68. package/templates/.claude/skills/project-optimizer/rules/deps-lightweight-alt.md +37 -0
  69. package/templates/.claude/skills/project-optimizer/rules/deps-peer-align.md +44 -0
  70. package/templates/.claude/skills/project-optimizer/rules/deps-security-audit.md +45 -0
  71. package/templates/.claude/skills/project-optimizer/rules/deps-unused-removal.md +25 -0
  72. package/templates/.claude/skills/project-optimizer/rules/deps-version-pin.md +40 -0
  73. package/templates/.claude/skills/project-optimizer/rules/dx-ci-speed.md +47 -0
  74. package/templates/.claude/skills/project-optimizer/rules/dx-dev-server.md +35 -0
  75. package/templates/.claude/skills/project-optimizer/rules/dx-lint-config.md +36 -0
  76. package/templates/.claude/skills/project-optimizer/rules/dx-test-coverage.md +34 -0
  77. package/templates/.claude/skills/project-optimizer/rules/dx-type-safety.md +49 -0
  78. package/templates/.claude/skills/project-optimizer/rules/io-batch-queries.md +67 -0
  79. package/templates/.claude/skills/project-optimizer/rules/io-cache-layer.md +67 -0
  80. package/templates/.claude/skills/project-optimizer/rules/io-connection-reuse.md +67 -0
  81. package/templates/.claude/skills/project-optimizer/rules/io-serialize-minimal.md +61 -0
  82. package/templates/.claude/skills/project-optimizer/rules/io-stream.md +75 -0
  83. package/templates/.claude/skills/project-optimizer/rules/memory-bounded-cache.md +65 -0
  84. package/templates/.claude/skills/project-optimizer/rules/memory-large-data.md +64 -0
  85. package/templates/.claude/skills/project-optimizer/rules/memory-lazy-init.md +78 -0
  86. package/templates/.claude/skills/project-optimizer/rules/memory-leak-prevention.md +79 -0
  87. package/templates/.claude/skills/project-optimizer/rules/memory-pool-reuse.md +70 -0
  88. package/templates/.claude/skills/ralph/SKILL.md +1 -0
  89. package/templates/.claude/skills/refactor/SKILL.md +1 -0
  90. package/templates/.claude/skills/research/SKILL.md +1 -0
  91. package/templates/.claude/skills/sql-optimizer/SKILL.md +438 -0
  92. package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -0
  93. package/templates/.claude/skills/startup-validator/SKILL.md +1 -0
  94. package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +53 -14
  95. package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +94 -27
  96. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +42 -19
  97. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-optimistic-updates.md +109 -0
  98. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-suspense-query.md +74 -0
  99. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-use-hook.md +81 -0
  100. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-react-compiler.md +81 -0
  101. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-beforeload-auth.md +121 -0
  102. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-file-conventions.md +104 -0
  103. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-link-navigation.md +119 -0
  104. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-nested-layouts.md +155 -0
  105. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-path-params.md +89 -0
  106. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-pending-component.md +110 -0
  107. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-preload-strategy.md +91 -0
  108. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-router-context.md +120 -0
  109. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-search-params.md +114 -0
  110. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +1 -1
  111. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-error-boundaries.md +79 -0
  112. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-middleware.md +85 -0
  113. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +56 -21
  114. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-streaming.md +84 -0
  115. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-validator.md +71 -0
  116. package/templates/.claude/skills/tauri-react-best-practices/AGENTS.md +527 -0
  117. package/templates/.claude/skills/tauri-react-best-practices/SKILL.md +571 -0
  118. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-barrel-imports.md +140 -0
  119. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-cargo-profile.md +96 -0
  120. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-frontend-treeshake.md +242 -0
  121. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-lazy-components.md +255 -0
  122. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-remove-unused-commands.md +160 -0
  123. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-ci-pipeline.md +269 -0
  124. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-signing.md +207 -0
  125. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-updater.md +226 -0
  126. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-async-commands.md +172 -0
  127. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-batch-commands.md +133 -0
  128. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-binary-response.md +198 -0
  129. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-channel-streaming.md +186 -0
  130. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-error-handling.md +250 -0
  131. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-type-safe.md +227 -0
  132. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-derived-state.md +231 -0
  133. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-functional-setstate.md +191 -0
  134. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-index-maps.md +276 -0
  135. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-lazy-state-init.md +196 -0
  136. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-lifecycle.md +265 -0
  137. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-mobile-compat.md +199 -0
  138. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-permission-scope.md +193 -0
  139. package/templates/.claude/skills/tauri-react-best-practices/rules/react-error-boundary.md +239 -0
  140. package/templates/.claude/skills/tauri-react-best-practices/rules/react-event-listener.md +151 -0
  141. package/templates/.claude/skills/tauri-react-best-practices/rules/react-file-src.md +155 -0
  142. package/templates/.claude/skills/tauri-react-best-practices/rules/react-invoke-hook.md +139 -0
  143. package/templates/.claude/skills/tauri-react-best-practices/rules/react-optimistic-update.md +211 -0
  144. package/templates/.claude/skills/tauri-react-best-practices/rules/security-capability-split.md +205 -0
  145. package/templates/.claude/skills/tauri-react-best-practices/rules/security-csp.md +207 -0
  146. package/templates/.claude/skills/tauri-react-best-practices/rules/security-least-privilege.md +106 -0
  147. package/templates/.claude/skills/tauri-react-best-practices/rules/security-no-wildcard.md +253 -0
  148. package/templates/.claude/skills/tauri-react-best-practices/rules/security-scope-paths.md +160 -0
  149. package/templates/.claude/skills/tauri-react-best-practices/rules/state-async-mutex.md +270 -0
  150. package/templates/.claude/skills/tauri-react-best-practices/rules/state-mutex-pattern.md +265 -0
  151. package/templates/.claude/skills/tauri-react-best-practices/rules/state-react-sync.md +375 -0
  152. package/templates/.claude/skills/tauri-react-best-practices/rules/state-single-container.md +275 -0
  153. package/templates/tanstack-start/docs/architecture.md +238 -167
  154. package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +777 -38
  155. package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +549 -37
  156. package/templates/tanstack-start/docs/library/tanstack-router/index.md +895 -111
  157. package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +641 -43
  158. package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +889 -38
  159. package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +891 -29
  160. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +972 -36
  161. package/templates/tanstack-start/docs/library/tanstack-start/index.md +1525 -881
  162. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +1099 -20
  163. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +796 -30
  164. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +953 -35
  165. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +371 -15
  166. package/templates/tauri/CLAUDE.md +189 -0
  167. package/templates/tauri/docs/guides/distribution.md +261 -0
  168. package/templates/tauri/docs/guides/getting-started.md +302 -0
  169. package/templates/tauri/docs/guides/mobile.md +288 -0
  170. package/templates/tauri/docs/library/tauri/index.md +510 -0
@@ -0,0 +1,270 @@
1
+ ---
2
+ title: Async Mutex for IO Resources
3
+ impact: MEDIUM
4
+ impactDescription: prevents blocking tokio runtime
5
+ tags: state, mutex, async, tokio, performance
6
+ ---
7
+
8
+ # IO 리소스만 Tokio Mutex, 나머지는 Std Mutex
9
+
10
+ ## 왜 중요한가
11
+
12
+ Rust의 `std::sync::Mutex`는 **CPU 작업**에 최적화되어 있고, `tokio::sync::Mutex`는 **비동기 IO 작업**에 최적화되어 있습니다. 잘못된 Mutex를 사용하면 **성능 저하**나 **데드락**이 발생할 수 있습니다.
13
+
14
+ ## ❌ 잘못된 패턴
15
+
16
+ **모든 곳에 Tokio Mutex 사용 (CPU 작업에서 오버헤드):**
17
+
18
+ ```rust
19
+ // ❌ src-tauri/src/main.rs
20
+ use tokio::sync::Mutex; // ❌ CPU 작업에는 비효율적
21
+ use tauri::State;
22
+
23
+ struct AppStateInner {
24
+ counter: i32, // CPU 작업
25
+ database: Database // IO 작업
26
+ }
27
+
28
+ type AppState = Mutex<AppStateInner>;
29
+
30
+ #[tauri::command]
31
+ async fn increment(state: State<'_, AppState>) -> Result<i32, String> {
32
+ let mut state = state.lock().await; // ❌ 불필요한 async
33
+ state.counter += 1; // CPU 작업인데 tokio::Mutex 사용
34
+ Ok(state.counter)
35
+ }
36
+
37
+ #[tauri::command]
38
+ async fn query_db(state: State<'_, AppState>) -> Result<String, String> {
39
+ let state = state.lock().await; // ❌ Database도 함께 락
40
+ let result = state.database.query("SELECT * FROM users").await?;
41
+ Ok(result)
42
+ }
43
+ ```
44
+
45
+ **문제:**
46
+ - `increment`는 CPU 작업인데 `tokio::Mutex`의 async 오버헤드 발생
47
+ - `database` IO 작업 중에도 `counter`가 락되어 병렬성 저하
48
+
49
+ ## ✅ 올바른 패턴
50
+
51
+ **CPU 작업과 IO 작업을 분리, 적절한 Mutex 사용:**
52
+
53
+ ```rust
54
+ // ✅ src-tauri/src/state.rs
55
+ use std::sync::Mutex as StdMutex;
56
+ use tokio::sync::Mutex as TokioMutex;
57
+
58
+ pub struct Database {
59
+ pool: sqlx::Pool<sqlx::Postgres>
60
+ }
61
+
62
+ impl Database {
63
+ pub async fn query(&self, sql: &str) -> Result<String, sqlx::Error> {
64
+ let rows = sqlx::query(sql)
65
+ .fetch_all(&self.pool)
66
+ .await?;
67
+ Ok(format!("{:?}", rows))
68
+ }
69
+ }
70
+
71
+ pub struct AppStateInner {
72
+ // CPU 작업: std::sync::Mutex
73
+ counter: StdMutex<i32>,
74
+ settings: StdMutex<AppSettings>,
75
+
76
+ // IO 작업: tokio::sync::Mutex
77
+ database: TokioMutex<Database>,
78
+ http_client: TokioMutex<reqwest::Client>
79
+ }
80
+
81
+ pub type AppState = AppStateInner;
82
+
83
+ impl AppStateInner {
84
+ pub fn new(db_pool: sqlx::Pool<sqlx::Postgres>) -> Self {
85
+ Self {
86
+ counter: StdMutex::new(0),
87
+ settings: StdMutex::new(AppSettings::default()),
88
+ database: TokioMutex::new(Database { pool: db_pool }),
89
+ http_client: TokioMutex::new(reqwest::Client::new())
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ ```rust
96
+ // ✅ src-tauri/src/main.rs
97
+ mod state;
98
+
99
+ use state::{AppState, AppStateInner};
100
+ use tauri::State;
101
+
102
+ // CPU 작업: 동기 함수, std::Mutex
103
+ #[tauri::command]
104
+ fn increment(state: State<'_, AppState>) -> Result<i32, String> {
105
+ let mut counter = state.counter.lock()
106
+ .map_err(|e| format!("Failed to lock counter: {}", e))?;
107
+ *counter += 1;
108
+ Ok(*counter)
109
+ }
110
+
111
+ // IO 작업: async 함수, tokio::Mutex
112
+ #[tauri::command]
113
+ async fn query_db(state: State<'_, AppState>) -> Result<String, String> {
114
+ let db = state.database.lock().await; // await 필요
115
+ let result = db.query("SELECT * FROM users")
116
+ .await
117
+ .map_err(|e| e.to_string())?;
118
+ Ok(result)
119
+ }
120
+
121
+ // IO 작업: async 함수, tokio::Mutex
122
+ #[tauri::command]
123
+ async fn fetch_data(state: State<'_, AppState>, url: String) -> Result<String, String> {
124
+ let client = state.http_client.lock().await;
125
+ let response = client.get(&url)
126
+ .send()
127
+ .await
128
+ .map_err(|e| e.to_string())?
129
+ .text()
130
+ .await
131
+ .map_err(|e| e.to_string())?;
132
+ Ok(response)
133
+ }
134
+
135
+ #[tokio::main]
136
+ async fn main() {
137
+ let db_pool = sqlx::postgres::PgPoolOptions::new()
138
+ .connect("postgres://localhost/mydb")
139
+ .await
140
+ .expect("Failed to connect to database");
141
+
142
+ tauri::Builder::default()
143
+ .manage(AppStateInner::new(db_pool))
144
+ .invoke_handler(tauri::generate_handler![
145
+ increment,
146
+ query_db,
147
+ fetch_data
148
+ ])
149
+ .run(tauri::generate_context!())
150
+ .expect("error while running tauri application");
151
+ }
152
+ ```
153
+
154
+ **RwLock 혼합 사용 (읽기 많은 경우):**
155
+
156
+ ```rust
157
+ use std::sync::RwLock as StdRwLock;
158
+ use tokio::sync::RwLock as TokioRwLock;
159
+
160
+ pub struct AppStateInner {
161
+ // 읽기 많은 CPU 작업: std::sync::RwLock
162
+ config: StdRwLock<AppConfig>,
163
+
164
+ // 읽기 많은 IO 작업: tokio::sync::RwLock
165
+ cache: TokioRwLock<Cache>
166
+ }
167
+
168
+ #[tauri::command]
169
+ fn get_config(state: State<'_, AppState>) -> Result<String, String> {
170
+ let config = state.config.read()
171
+ .map_err(|e| format!("Failed to read config: {}", e))?;
172
+ Ok(format!("{:?}", *config))
173
+ }
174
+
175
+ #[tauri::command]
176
+ async fn get_cached_data(state: State<'_, AppState>, key: String) -> Result<String, String> {
177
+ let cache = state.cache.read().await;
178
+ cache.get(&key)
179
+ .await
180
+ .ok_or_else(|| "Key not found".to_string())
181
+ }
182
+ ```
183
+
184
+ ## 추가 컨텍스트
185
+
186
+ **Mutex 선택 가이드:**
187
+
188
+ | 작업 유형 | Mutex 종류 | 이유 |
189
+ |-----------|-----------|------|
190
+ | CPU 계산 (counter, settings) | `std::sync::Mutex` | OS 레벨 락, 오버헤드 낮음 |
191
+ | IO 작업 (DB, HTTP, 파일) | `tokio::sync::Mutex` | async/await 호환, tokio runtime 차단 안함 |
192
+ | 읽기 많음 + CPU | `std::sync::RwLock` | 여러 스레드 동시 읽기 |
193
+ | 읽기 많음 + IO | `tokio::sync::RwLock` | 비동기 읽기 병렬화 |
194
+
195
+ **성능 비교:**
196
+
197
+ ```rust
198
+ // 벤치마크 예시 (단순 증가 연산 1,000,000회)
199
+ // std::sync::Mutex: 50ms
200
+ // tokio::sync::Mutex: 120ms (2.4배 느림)
201
+
202
+ // IO 작업 시 (100ms 네트워크 요청)
203
+ // std::sync::Mutex: tokio runtime 차단 → 다른 작업 대기
204
+ // tokio::sync::Mutex: 다른 작업 병렬 실행 가능
205
+ ```
206
+
207
+ **데드락 위험:**
208
+
209
+ ```rust
210
+ // ❌ std::sync::Mutex를 async 블록 안에서 .await 넘어 사용
211
+ async fn bad_example(state: State<'_, AppState>) {
212
+ let lock = state.counter.lock().unwrap(); // std::Mutex
213
+ some_async_function().await; // ❌ .await 동안 락 유지 → 데드락 위험
214
+ println!("{}", *lock);
215
+ }
216
+
217
+ // ✅ 락 범위 최소화
218
+ async fn good_example(state: State<'_, AppState>) {
219
+ let value = {
220
+ let lock = state.counter.lock().unwrap();
221
+ *lock // 값 복사 후 락 해제
222
+ };
223
+ some_async_function().await; // ✅ 락 해제 후 await
224
+ println!("{}", value);
225
+ }
226
+ ```
227
+
228
+ **Tauri 2.0+ 권장 패턴:**
229
+
230
+ ```rust
231
+ // Tauri 2.0부터는 async command가 기본
232
+ // IO 작업은 tokio::Mutex + async
233
+ // CPU 작업은 std::Mutex + 동기 함수 (Tauri가 tokio::spawn_blocking 자동 처리)
234
+
235
+ #[tauri::command]
236
+ fn cpu_intensive_task(state: State<'_, AppState>) -> Result<i32, String> {
237
+ // Tauri가 자동으로 blocking thread에서 실행
238
+ let mut data = state.cpu_data.lock().unwrap();
239
+ // 무거운 CPU 작업...
240
+ Ok(*data)
241
+ }
242
+ ```
243
+
244
+ **Arc는 언제 사용?**
245
+
246
+ ```rust
247
+ // ❌ Tauri State에서는 Arc 불필요
248
+ use std::sync::Arc;
249
+ type AppState = Arc<Mutex<AppStateInner>>; // ❌ 불필요한 Arc
250
+
251
+ // ✅ Tauri가 자동으로 State를 관리 (이미 Arc 내부 사용)
252
+ type AppState = Mutex<AppStateInner>; // ✅ 충분
253
+
254
+ // ✅ State 외부에서 공유 필요 시에만 Arc 사용
255
+ let shared_db = Arc::new(Database::new());
256
+ let db_clone = Arc::clone(&shared_db);
257
+ tokio::spawn(async move {
258
+ db_clone.query("...").await;
259
+ });
260
+ ```
261
+
262
+ **참고:**
263
+ - Tokio Mutex: [tokio::sync::Mutex](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html)
264
+ - Std Mutex: [std::sync::Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html)
265
+ - Tauri Async: [Async Commands](https://tauri.app/v2/guides/features/commands/#async-commands)
266
+
267
+ **영향도:**
268
+ - 성능: MEDIUM (CPU 작업 2-3배, IO 작업 병렬성 향상)
269
+ - 데드락 방지: HIGH (올바른 Mutex 선택)
270
+ - 복잡도: MEDIUM (작업 유형 판단 필요)
@@ -0,0 +1,265 @@
1
+ ---
2
+ title: Mutex for Mutable State
3
+ impact: HIGH
4
+ impactDescription: prevents panic from concurrent access
5
+ tags: state, mutex, thread-safety, tauri, rust
6
+ ---
7
+
8
+ # Mutex로 가변 상태 관리, 타입 별칭 사용
9
+
10
+ ## 왜 중요한가
11
+
12
+ Tauri의 상태 관리는 **여러 스레드에서 동시에 접근**할 수 있습니다. 가변 상태를 `Mutex`로 감싸지 않으면 **런타임 패닉**이 발생하며, 타입 불일치로 인한 버그를 방지하기 위해 **타입 별칭**을 사용해야 합니다.
13
+
14
+ ## ❌ 잘못된 패턴
15
+
16
+ **Mutex 없이 가변 상태 관리 (패닉 발생):**
17
+
18
+ ```rust
19
+ // ❌ src-tauri/src/main.rs
20
+ use tauri::State;
21
+
22
+ struct AppState {
23
+ count: i32
24
+ }
25
+
26
+ #[tauri::command]
27
+ fn increment(state: State<'_, AppState>) -> i32 {
28
+ state.count += 1; // ❌ 컴파일 에러: cannot mutate immutable field
29
+ state.count
30
+ }
31
+
32
+ fn main() {
33
+ tauri::Builder::default()
34
+ .manage(AppState { count: 0 })
35
+ .invoke_handler(tauri::generate_handler![increment])
36
+ .run(tauri::generate_context!())
37
+ .expect("error while running tauri application");
38
+ }
39
+ ```
40
+
41
+ **Mutex 사용하지만 타입 별칭 없음 (불일치 위험):**
42
+
43
+ ```rust
44
+ // ⚠️ src-tauri/src/main.rs
45
+ use std::sync::Mutex;
46
+ use tauri::State;
47
+
48
+ struct AppStateInner {
49
+ count: i32
50
+ }
51
+
52
+ #[tauri::command]
53
+ fn increment(state: State<'_, Mutex<AppStateInner>>) -> i32 {
54
+ let mut state = state.lock().unwrap();
55
+ state.count += 1;
56
+ state.count
57
+ }
58
+
59
+ #[tauri::command]
60
+ fn get_count(state: State<'_, AppStateInner>) -> i32 {
61
+ // ❌ 타입 불일치: Mutex<AppStateInner>를 등록했지만
62
+ // AppStateInner로 접근 시도 → 런타임 패닉!
63
+ state.count
64
+ }
65
+
66
+ fn main() {
67
+ tauri::Builder::default()
68
+ .manage(Mutex::new(AppStateInner { count: 0 }))
69
+ .invoke_handler(tauri::generate_handler![increment, get_count])
70
+ .run(tauri::generate_context!())
71
+ .expect("error while running tauri application");
72
+ }
73
+ ```
74
+
75
+ **런타임 에러:**
76
+ ```
77
+ thread 'main' panicked at 'Failed to get state: no state of type `AppStateInner` is managed'
78
+ ```
79
+
80
+ ## ✅ 올바른 패턴
81
+
82
+ **Mutex + 타입 별칭으로 타입 안전성 보장:**
83
+
84
+ ```rust
85
+ // ✅ src-tauri/src/main.rs
86
+ use std::sync::Mutex;
87
+ use tauri::State;
88
+
89
+ // 내부 상태 구조체
90
+ struct AppStateInner {
91
+ count: i32,
92
+ username: String
93
+ }
94
+
95
+ // 타입 별칭으로 Mutex 포함
96
+ type AppState = Mutex<AppStateInner>;
97
+
98
+ #[tauri::command]
99
+ fn increment(state: State<'_, AppState>) -> i32 {
100
+ let mut state = state.lock().unwrap();
101
+ state.count += 1;
102
+ state.count
103
+ }
104
+
105
+ #[tauri::command]
106
+ fn get_count(state: State<'_, AppState>) -> i32 {
107
+ let state = state.lock().unwrap();
108
+ state.count
109
+ }
110
+
111
+ #[tauri::command]
112
+ fn set_username(state: State<'_, AppState>, name: String) {
113
+ let mut state = state.lock().unwrap();
114
+ state.username = name;
115
+ }
116
+
117
+ fn main() {
118
+ tauri::Builder::default()
119
+ .manage(AppState::new(AppStateInner {
120
+ count: 0,
121
+ username: String::from("Guest")
122
+ }))
123
+ .invoke_handler(tauri::generate_handler![
124
+ increment,
125
+ get_count,
126
+ set_username
127
+ ])
128
+ .run(tauri::generate_context!())
129
+ .expect("error while running tauri application");
130
+ }
131
+ ```
132
+
133
+ **별도 파일로 상태 관리 분리:**
134
+
135
+ ```rust
136
+ // ✅ src-tauri/src/state.rs
137
+ use std::sync::Mutex;
138
+ use serde::{Deserialize, Serialize};
139
+
140
+ #[derive(Debug, Serialize, Deserialize)]
141
+ pub struct AppStateInner {
142
+ pub count: i32,
143
+ pub username: String,
144
+ pub is_logged_in: bool
145
+ }
146
+
147
+ impl Default for AppStateInner {
148
+ fn default() -> Self {
149
+ Self {
150
+ count: 0,
151
+ username: String::from("Guest"),
152
+ is_logged_in: false
153
+ }
154
+ }
155
+ }
156
+
157
+ pub type AppState = Mutex<AppStateInner>;
158
+
159
+ impl AppStateInner {
160
+ pub fn new() -> Self {
161
+ Self::default()
162
+ }
163
+ }
164
+ ```
165
+
166
+ ```rust
167
+ // ✅ src-tauri/src/main.rs
168
+ mod state;
169
+
170
+ use state::{AppState, AppStateInner};
171
+ use tauri::State;
172
+
173
+ #[tauri::command]
174
+ fn increment(state: State<'_, AppState>) -> i32 {
175
+ let mut state = state.lock().unwrap();
176
+ state.count += 1;
177
+ state.count
178
+ }
179
+
180
+ #[tauri::command]
181
+ fn login(state: State<'_, AppState>, username: String) {
182
+ let mut state = state.lock().unwrap();
183
+ state.username = username;
184
+ state.is_logged_in = true;
185
+ }
186
+
187
+ fn main() {
188
+ tauri::Builder::default()
189
+ .manage(AppState::new(AppStateInner::new()))
190
+ .invoke_handler(tauri::generate_handler![increment, login])
191
+ .run(tauri::generate_context!())
192
+ .expect("error while running tauri application");
193
+ }
194
+ ```
195
+
196
+ ## 추가 컨텍스트
197
+
198
+ **Mutex가 필요한 이유:**
199
+
200
+ | 상황 | Mutex 필요 여부 | 이유 |
201
+ |------|----------------|------|
202
+ | 읽기 전용 상태 | ❌ | `State<'_, T>` 자체가 읽기 전용 보장 |
203
+ | 가변 상태 | ✅ | 여러 스레드에서 동시 수정 가능 |
204
+ | 단일 스레드 | ⚠️ | Tauri는 멀티스레드 환경이므로 Mutex 필수 |
205
+
206
+ **타입 별칭의 장점:**
207
+
208
+ 1. **타입 안전성**: 모든 커맨드에서 동일한 타입 사용
209
+ 2. **가독성**: `State<'_, AppState>` vs `State<'_, Mutex<AppStateInner>>`
210
+ 3. **유지보수**: 상태 구조 변경 시 타입 별칭만 수정
211
+
212
+ **Mutex::lock() 에러 처리:**
213
+
214
+ ```rust
215
+ // ❌ unwrap() - 패닉 발생 가능
216
+ let mut state = state.lock().unwrap();
217
+
218
+ // ✅ Result 반환 - 에러 처리
219
+ #[tauri::command]
220
+ fn increment(state: State<'_, AppState>) -> Result<i32, String> {
221
+ let mut state = state.lock()
222
+ .map_err(|e| format!("Failed to lock state: {}", e))?;
223
+ state.count += 1;
224
+ Ok(state.count)
225
+ }
226
+ ```
227
+
228
+ **RwLock vs Mutex:**
229
+
230
+ ```rust
231
+ // 읽기가 많고 쓰기가 적은 경우 RwLock 사용
232
+ use std::sync::RwLock;
233
+
234
+ type AppState = RwLock<AppStateInner>;
235
+
236
+ #[tauri::command]
237
+ fn get_count(state: State<'_, AppState>) -> i32 {
238
+ let state = state.read().unwrap(); // 여러 스레드 동시 읽기 가능
239
+ state.count
240
+ }
241
+
242
+ #[tauri::command]
243
+ fn increment(state: State<'_, AppState>) -> i32 {
244
+ let mut state = state.write().unwrap(); // 배타적 쓰기
245
+ state.count += 1;
246
+ state.count
247
+ }
248
+ ```
249
+
250
+ **패턴 비교:**
251
+
252
+ | 패턴 | 장점 | 단점 | 권장 |
253
+ |------|------|------|------|
254
+ | `Mutex<T>` | 단순, 안전 | 읽기도 락 필요 | 쓰기 많을 때 |
255
+ | `RwLock<T>` | 읽기 병렬화 | 복잡, 오버헤드 | 읽기 많을 때 |
256
+ | `Arc<Mutex<T>>` | 복제 가능 | 불필요 (Tauri가 관리) | ❌ 사용 안함 |
257
+
258
+ **참고:**
259
+ - Tauri State Management: [Managing State](https://tauri.app/v2/guides/features/state-management/)
260
+ - Rust Mutex: [std::sync::Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html)
261
+
262
+ **영향도:**
263
+ - 안정성: HIGH (패닉 방지)
264
+ - 타입 안전성: HIGH (컴파일 타임 검증)
265
+ - 성능: NEUTRAL (Mutex 오버헤드 미미)