@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,375 @@
1
+ ---
2
+ title: Rust-React State Synchronization
3
+ impact: HIGH
4
+ impactDescription: ensures consistent state across boundaries
5
+ tags: state, react, events, synchronization, ipc
6
+ ---
7
+
8
+ # Rust 상태와 React 상태 동기화 패턴
9
+
10
+ ## 왜 중요한가
11
+
12
+ Tauri 앱에서 **Rust 백엔드 상태**와 **React 프론트엔드 상태**가 독립적으로 관리되면 **불일치**가 발생합니다. Tauri Events를 사용하여 양방향 동기화를 구현해야 합니다.
13
+
14
+ ## ❌ 잘못된 패턴
15
+
16
+ **Rust와 React 상태가 각각 독립적으로 관리:**
17
+
18
+ ```rust
19
+ // ❌ src-tauri/src/main.rs - Rust 상태만 업데이트
20
+ use std::sync::Mutex;
21
+ use tauri::State;
22
+
23
+ struct AppStateInner {
24
+ count: i32
25
+ }
26
+
27
+ type AppState = Mutex<AppStateInner>;
28
+
29
+ #[tauri::command]
30
+ fn increment(state: State<'_, AppState>) -> i32 {
31
+ let mut state = state.lock().unwrap();
32
+ state.count += 1;
33
+ state.count // React에 반환만 하고 끝
34
+ }
35
+ ```
36
+
37
+ ```typescript
38
+ // ❌ src/App.tsx - React 상태가 동기화되지 않음
39
+ import { useState } from 'react'
40
+ import { invoke } from '@tauri-apps/api/core'
41
+
42
+ function App() {
43
+ const [count, setCount] = useState(0)
44
+
45
+ const handleIncrement = async () => {
46
+ const newCount = await invoke<number>('increment')
47
+ setCount(newCount) // ✅ 이 창에서는 동기화됨
48
+ }
49
+
50
+ // ❌ 다른 창이나 백그라운드 업데이트는 감지 못함
51
+ return (
52
+ <div>
53
+ <p>Count: {count}</p>
54
+ <button onClick={handleIncrement}>Increment</button>
55
+ </div>
56
+ )
57
+ }
58
+ ```
59
+
60
+ **문제:**
61
+ - 다른 창에서 `increment` 호출 시 현재 창의 `count`가 업데이트되지 않음
62
+ - Rust 상태 변경이 React에 전파되지 않음
63
+ - 백그라운드 작업이 상태를 변경해도 UI가 업데이트 안됨
64
+
65
+ ## ✅ 올바른 패턴
66
+
67
+ **Tauri Events로 Rust → React 동기화:**
68
+
69
+ ```rust
70
+ // ✅ src-tauri/src/state.rs
71
+ use std::sync::Mutex;
72
+ use serde::{Deserialize, Serialize};
73
+
74
+ #[derive(Debug, Clone, Serialize, Deserialize)]
75
+ pub struct AppStateInner {
76
+ pub count: i32,
77
+ pub username: String
78
+ }
79
+
80
+ pub type AppState = Mutex<AppStateInner>;
81
+
82
+ impl AppStateInner {
83
+ pub fn new() -> Self {
84
+ Self {
85
+ count: 0,
86
+ username: String::from("Guest")
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ ```rust
93
+ // ✅ src-tauri/src/main.rs - 상태 변경 시 이벤트 발행
94
+ mod state;
95
+
96
+ use state::{AppState, AppStateInner};
97
+ use tauri::{State, Manager, Emitter};
98
+
99
+ #[tauri::command]
100
+ fn increment(state: State<'_, AppState>, app: tauri::AppHandle) -> Result<i32, String> {
101
+ let new_count = {
102
+ let mut state = state.lock().unwrap();
103
+ state.count += 1;
104
+ state.count
105
+ };
106
+
107
+ // ✅ 모든 창에 상태 변경 알림
108
+ app.emit("state-changed", &new_count)
109
+ .map_err(|e| format!("Failed to emit event: {}", e))?;
110
+
111
+ Ok(new_count)
112
+ }
113
+
114
+ #[tauri::command]
115
+ fn set_username(
116
+ state: State<'_, AppState>,
117
+ app: tauri::AppHandle,
118
+ username: String
119
+ ) -> Result<(), String> {
120
+ {
121
+ let mut state = state.lock().unwrap();
122
+ state.username = username.clone();
123
+ }
124
+
125
+ // ✅ username 변경 이벤트 발행
126
+ app.emit("username-changed", &username)
127
+ .map_err(|e| format!("Failed to emit event: {}", e))?;
128
+
129
+ Ok(())
130
+ }
131
+
132
+ #[tauri::command]
133
+ fn get_state(state: State<'_, AppState>) -> Result<AppStateInner, String> {
134
+ let state = state.lock()
135
+ .map_err(|e| format!("Failed to lock state: {}", e))?;
136
+ Ok(state.clone())
137
+ }
138
+
139
+ fn main() {
140
+ tauri::Builder::default()
141
+ .manage(AppState::new(AppStateInner::new()))
142
+ .invoke_handler(tauri::generate_handler![
143
+ increment,
144
+ set_username,
145
+ get_state
146
+ ])
147
+ .run(tauri::generate_context!())
148
+ .expect("error while running tauri application");
149
+ }
150
+ ```
151
+
152
+ ```typescript
153
+ // ✅ src/App.tsx - 이벤트 리스너로 동기화
154
+ import { useState, useEffect } from 'react'
155
+ import { invoke } from '@tauri-apps/api/core'
156
+ import { listen } from '@tauri-apps/api/event'
157
+
158
+ interface AppState {
159
+ count: number
160
+ username: string
161
+ }
162
+
163
+ function App() {
164
+ const [state, setState] = useState<AppState>({
165
+ count: 0,
166
+ username: 'Guest'
167
+ })
168
+
169
+ useEffect(() => {
170
+ // ✅ 초기 상태 로드
171
+ invoke<AppState>('get_state').then(setState)
172
+
173
+ // ✅ Rust 상태 변경 감지
174
+ const unlistenCount = listen<number>('state-changed', (event) => {
175
+ setState(prev => ({ ...prev, count: event.payload }))
176
+ })
177
+
178
+ const unlistenUsername = listen<string>('username-changed', (event) => {
179
+ setState(prev => ({ ...prev, username: event.payload }))
180
+ })
181
+
182
+ // 클린업
183
+ return () => {
184
+ unlistenCount.then(fn => fn())
185
+ unlistenUsername.then(fn => fn())
186
+ }
187
+ }, [])
188
+
189
+ const handleIncrement = async () => {
190
+ await invoke('increment') // 이벤트로 상태 업데이트됨
191
+ }
192
+
193
+ const handleSetUsername = async (name: string) => {
194
+ await invoke('set_username', { username: name })
195
+ }
196
+
197
+ return (
198
+ <div>
199
+ <p>Count: {state.count}</p>
200
+ <p>Username: {state.username}</p>
201
+ <button onClick={handleIncrement}>Increment</button>
202
+ <button onClick={() => handleSetUsername('Alice')}>
203
+ Set Username
204
+ </button>
205
+ </div>
206
+ )
207
+ }
208
+
209
+ export default App
210
+ ```
211
+
212
+ **React → Rust 동기화 (양방향):**
213
+
214
+ ```typescript
215
+ // ✅ src/App.tsx - React 상태 변경 시 Rust에 동기화
216
+ function App() {
217
+ const [localCount, setLocalCount] = useState(0)
218
+
219
+ const handleLocalIncrement = async () => {
220
+ // 낙관적 업데이트
221
+ const newCount = localCount + 1
222
+ setLocalCount(newCount)
223
+
224
+ // Rust 상태 동기화
225
+ try {
226
+ await invoke('increment')
227
+ } catch (error) {
228
+ // 실패 시 롤백
229
+ setLocalCount(localCount)
230
+ console.error('Failed to sync state:', error)
231
+ }
232
+ }
233
+
234
+ return (
235
+ <div>
236
+ <p>Local Count: {localCount}</p>
237
+ <button onClick={handleLocalIncrement}>Increment</button>
238
+ </div>
239
+ )
240
+ }
241
+ ```
242
+
243
+ **커스텀 훅으로 추상화:**
244
+
245
+ ```typescript
246
+ // ✅ src/hooks/useTauriState.ts
247
+ import { useState, useEffect } from 'react'
248
+ import { invoke } from '@tauri-apps/api/core'
249
+ import { listen } from '@tauri-apps/api/event'
250
+
251
+ export function useTauriState<T>(
252
+ getCommand: string,
253
+ eventName: string
254
+ ): [T | null, (newValue: T) => Promise<void>] {
255
+ const [state, setState] = useState<T | null>(null)
256
+
257
+ useEffect(() => {
258
+ // 초기 상태 로드
259
+ invoke<T>(getCommand).then(setState)
260
+
261
+ // 이벤트 리스너
262
+ const unlisten = listen<T>(eventName, (event) => {
263
+ setState(event.payload)
264
+ })
265
+
266
+ return () => {
267
+ unlisten.then(fn => fn())
268
+ }
269
+ }, [getCommand, eventName])
270
+
271
+ const updateState = async (newValue: T) => {
272
+ setState(newValue)
273
+ await invoke('update_state', { newValue })
274
+ }
275
+
276
+ return [state, updateState]
277
+ }
278
+
279
+ // 사용 예시
280
+ function App() {
281
+ const [count, setCount] = useTauriState<number>('get_count', 'count-changed')
282
+
283
+ return (
284
+ <div>
285
+ <p>Count: {count ?? 'Loading...'}</p>
286
+ <button onClick={() => setCount((count ?? 0) + 1)}>
287
+ Increment
288
+ </button>
289
+ </div>
290
+ )
291
+ }
292
+ ```
293
+
294
+ ## 추가 컨텍스트
295
+
296
+ **양방향 동기화 아키텍처:**
297
+
298
+ ```
299
+ ┌─────────────┐ invoke() ┌─────────────┐
300
+ │ React │ ───────────────────────> │ Rust │
301
+ │ State │ │ State │
302
+ │ │ <─────────────────────── │ │
303
+ └─────────────┘ emit() event └─────────────┘
304
+ ```
305
+
306
+ **동기화 패턴 비교:**
307
+
308
+ | 패턴 | 장점 | 단점 | 권장 |
309
+ |------|------|------|------|
310
+ | **Poll (주기적 조회)** | 간단 | 비효율적, 지연 | ❌ |
311
+ | **Event-driven** | 실시간, 효율적 | 복잡도 증가 | ✅ |
312
+ | **낙관적 업데이트** | 빠른 UX | 실패 시 롤백 필요 | ⚠️ 선택 |
313
+
314
+ **여러 창 동기화:**
315
+
316
+ ```rust
317
+ // 모든 창에 브로드캐스트
318
+ app.emit_all("state-changed", &new_count)?;
319
+
320
+ // 특정 창에만 전송
321
+ if let Some(window) = app.get_window("main") {
322
+ window.emit("state-changed", &new_count)?;
323
+ }
324
+ ```
325
+
326
+ **에러 처리:**
327
+
328
+ ```typescript
329
+ useEffect(() => {
330
+ const unlisten = listen<number>('state-changed', (event) => {
331
+ setState(event.payload)
332
+ }).catch((error) => {
333
+ console.error('Failed to listen to events:', error)
334
+ })
335
+
336
+ return () => {
337
+ unlisten.then(fn => fn()).catch(console.error)
338
+ }
339
+ }, [])
340
+ ```
341
+
342
+ **성능 최적화:**
343
+
344
+ ```rust
345
+ // 디바운싱으로 과도한 이벤트 방지
346
+ use std::time::{Duration, Instant};
347
+
348
+ struct StateEmitter {
349
+ last_emit: Mutex<Instant>
350
+ }
351
+
352
+ impl StateEmitter {
353
+ fn emit_throttled(&self, app: &tauri::AppHandle, count: i32) -> Result<(), String> {
354
+ let mut last = self.last_emit.lock().unwrap();
355
+ let now = Instant::now();
356
+
357
+ if now.duration_since(*last) > Duration::from_millis(100) {
358
+ app.emit("state-changed", &count)
359
+ .map_err(|e| e.to_string())?;
360
+ *last = now;
361
+ }
362
+
363
+ Ok(())
364
+ }
365
+ }
366
+ ```
367
+
368
+ **참고:**
369
+ - Tauri Events: [Inter-Process Communication](https://tauri.app/v2/guides/features/events/)
370
+ - React useEffect: [Synchronizing with Effects](https://react.dev/learn/synchronizing-with-effects)
371
+
372
+ **영향도:**
373
+ - 일관성: HIGH (상태 불일치 방지)
374
+ - 실시간성: HIGH (즉시 동기화)
375
+ - 복잡도: MEDIUM (이벤트 관리 필요)
@@ -0,0 +1,275 @@
1
+ ---
2
+ title: Single Container for Multiple States
3
+ impact: MEDIUM
4
+ impactDescription: prevents state registration conflicts
5
+ tags: state, container, tauri, type-map, rust
6
+ ---
7
+
8
+ # 동일 타입 중복 등록 방지
9
+
10
+ ## 왜 중요한가
11
+
12
+ Tauri의 상태 관리는 **타입 기반 의존성 주입** 시스템을 사용합니다. 동일한 타입을 여러 번 `manage()`로 등록하면 **두 번째 등록이 무시**되어 의도하지 않은 상태를 사용하게 됩니다.
13
+
14
+ ## ❌ 잘못된 패턴
15
+
16
+ **동일 타입을 여러 번 manage() 호출:**
17
+
18
+ ```rust
19
+ // ❌ src-tauri/src/main.rs
20
+ use std::sync::Mutex;
21
+ use tauri::State;
22
+
23
+ type DatabaseState = Mutex<String>;
24
+ type CacheState = Mutex<String>;
25
+
26
+ #[tauri::command]
27
+ fn get_db_connection(db: State<'_, DatabaseState>) -> String {
28
+ let db = db.lock().unwrap();
29
+ db.clone()
30
+ }
31
+
32
+ #[tauri::command]
33
+ fn get_cache_connection(cache: State<'_, CacheState>) -> String {
34
+ let cache = cache.lock().unwrap();
35
+ cache.clone()
36
+ }
37
+
38
+ fn main() {
39
+ tauri::Builder::default()
40
+ .manage(DatabaseState::new(String::from("postgres://localhost")))
41
+ .manage(CacheState::new(String::from("redis://localhost")))
42
+ // ❌ 두 번째 manage()가 무시됨! (둘 다 Mutex<String> 타입)
43
+ .invoke_handler(tauri::generate_handler![
44
+ get_db_connection,
45
+ get_cache_connection
46
+ ])
47
+ .run(tauri::generate_context!())
48
+ .expect("error while running tauri application");
49
+ }
50
+ ```
51
+
52
+ **프론트엔드에서 호출:**
53
+
54
+ ```typescript
55
+ // 둘 다 "postgres://localhost" 반환!
56
+ const db = await invoke('get_db_connection') // "postgres://localhost"
57
+ const cache = await invoke('get_cache_connection') // "postgres://localhost" (예상: "redis://localhost")
58
+ ```
59
+
60
+ **문제:**
61
+ - `CacheState`가 등록되지 않음
62
+ - `get_cache_connection`이 `DatabaseState`를 반환
63
+ - 런타임 에러는 발생하지 않지만 잘못된 상태 사용
64
+
65
+ ## ✅ 올바른 패턴
66
+
67
+ **컨테이너 구조체로 여러 상태 통합:**
68
+
69
+ ```rust
70
+ // ✅ src-tauri/src/state.rs
71
+ use std::sync::Mutex;
72
+
73
+ pub struct AppStateInner {
74
+ pub database_url: String,
75
+ pub cache_url: String,
76
+ pub api_key: String
77
+ }
78
+
79
+ pub type AppState = Mutex<AppStateInner>;
80
+
81
+ impl AppStateInner {
82
+ pub fn new() -> Self {
83
+ Self {
84
+ database_url: String::from("postgres://localhost"),
85
+ cache_url: String::from("redis://localhost"),
86
+ api_key: String::from("secret")
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ ```rust
93
+ // ✅ src-tauri/src/main.rs
94
+ mod state;
95
+
96
+ use state::{AppState, AppStateInner};
97
+ use tauri::State;
98
+
99
+ #[tauri::command]
100
+ fn get_db_connection(state: State<'_, AppState>) -> String {
101
+ let state = state.lock().unwrap();
102
+ state.database_url.clone()
103
+ }
104
+
105
+ #[tauri::command]
106
+ fn get_cache_connection(state: State<'_, AppState>) -> String {
107
+ let state = state.lock().unwrap();
108
+ state.cache_url.clone()
109
+ }
110
+
111
+ #[tauri::command]
112
+ fn get_api_key(state: State<'_, AppState>) -> String {
113
+ let state = state.lock().unwrap();
114
+ state.api_key.clone()
115
+ }
116
+
117
+ fn main() {
118
+ tauri::Builder::default()
119
+ .manage(AppState::new(AppStateInner::new())) // 단일 컨테이너 등록
120
+ .invoke_handler(tauri::generate_handler![
121
+ get_db_connection,
122
+ get_cache_connection,
123
+ get_api_key
124
+ ])
125
+ .run(tauri::generate_context!())
126
+ .expect("error while running tauri application");
127
+ }
128
+ ```
129
+
130
+ **newtype 패턴 (타입 래핑):**
131
+
132
+ ```rust
133
+ // ✅ src-tauri/src/state.rs - newtype 패턴
134
+ use std::sync::Mutex;
135
+
136
+ // 각각 고유한 타입으로 래핑
137
+ pub struct DatabaseState(pub Mutex<String>);
138
+ pub struct CacheState(pub Mutex<String>);
139
+
140
+ impl DatabaseState {
141
+ pub fn new(url: String) -> Self {
142
+ Self(Mutex::new(url))
143
+ }
144
+ }
145
+
146
+ impl CacheState {
147
+ pub fn new(url: String) -> Self {
148
+ Self(Mutex::new(url))
149
+ }
150
+ }
151
+ ```
152
+
153
+ ```rust
154
+ // ✅ src-tauri/src/main.rs
155
+ mod state;
156
+
157
+ use state::{DatabaseState, CacheState};
158
+ use tauri::State;
159
+
160
+ #[tauri::command]
161
+ fn get_db_connection(db: State<'_, DatabaseState>) -> String {
162
+ let db = db.0.lock().unwrap();
163
+ db.clone()
164
+ }
165
+
166
+ #[tauri::command]
167
+ fn get_cache_connection(cache: State<'_, CacheState>) -> String {
168
+ let cache = cache.0.lock().unwrap();
169
+ cache.clone()
170
+ }
171
+
172
+ fn main() {
173
+ tauri::Builder::default()
174
+ .manage(DatabaseState::new(String::from("postgres://localhost")))
175
+ .manage(CacheState::new(String::from("redis://localhost")))
176
+ // ✅ 이제 각각 고유한 타입이므로 둘 다 등록됨
177
+ .invoke_handler(tauri::generate_handler![
178
+ get_db_connection,
179
+ get_cache_connection
180
+ ])
181
+ .run(tauri::generate_context!())
182
+ .expect("error while running tauri application");
183
+ }
184
+ ```
185
+
186
+ ## 추가 컨텍스트
187
+
188
+ **Tauri의 타입 기반 상태 시스템:**
189
+
190
+ ```rust
191
+ // Tauri 내부적으로 TypeMap 사용
192
+ // TypeMap<TypeId, Box<dyn Any>>
193
+ // 각 타입당 하나의 값만 저장 가능
194
+
195
+ .manage(value1) // TypeId::of::<T>() → value1
196
+ .manage(value2) // TypeId::of::<T>() → value2 (value1 덮어쓰기 또는 무시)
197
+ ```
198
+
199
+ **패턴 비교:**
200
+
201
+ | 패턴 | 장점 | 단점 | 권장 |
202
+ |------|------|------|------|
203
+ | **단일 컨테이너** | 단순, 명확 | 큰 구조체 | ✅ 대부분의 경우 |
204
+ | **Newtype** | 타입 안전 | 보일러플레이트 | ⚠️ 필요 시만 |
205
+ | **여러 manage()** | - | ❌ 작동 안함 | ❌ 사용 금지 |
206
+
207
+ **컨테이너 구조체 설계:**
208
+
209
+ ```rust
210
+ // ✅ 도메인별 그룹화
211
+ pub struct AppStateInner {
212
+ // 데이터베이스 관련
213
+ pub db_pool: DatabasePool,
214
+ pub db_url: String,
215
+
216
+ // 캐시 관련
217
+ pub cache_pool: CachePool,
218
+ pub cache_url: String,
219
+
220
+ // 인증 관련
221
+ pub jwt_secret: String,
222
+ pub api_key: String,
223
+
224
+ // 설정 관련
225
+ pub config: AppConfig
226
+ }
227
+ ```
228
+
229
+ **env 변수 로드:**
230
+
231
+ ```rust
232
+ use std::env;
233
+
234
+ impl AppStateInner {
235
+ pub fn from_env() -> Self {
236
+ Self {
237
+ database_url: env::var("DATABASE_URL")
238
+ .unwrap_or_else(|_| String::from("postgres://localhost")),
239
+ cache_url: env::var("REDIS_URL")
240
+ .unwrap_or_else(|_| String::from("redis://localhost")),
241
+ api_key: env::var("API_KEY")
242
+ .expect("API_KEY must be set"),
243
+ }
244
+ }
245
+ }
246
+
247
+ fn main() {
248
+ tauri::Builder::default()
249
+ .manage(AppState::new(AppStateInner::from_env()))
250
+ .run(tauri::generate_context!())
251
+ .expect("error while running tauri application");
252
+ }
253
+ ```
254
+
255
+ **디버깅 팁:**
256
+
257
+ ```rust
258
+ // State를 찾을 수 없을 때 런타임 에러
259
+ #[tauri::command]
260
+ fn test_command(state: State<'_, WrongType>) {
261
+ // panic: no state of type `WrongType` is managed
262
+ }
263
+
264
+ // 등록된 상태 확인
265
+ println!("Registered state: {:?}", std::any::type_name::<AppState>());
266
+ ```
267
+
268
+ **참고:**
269
+ - Tauri State Management: [Managing State](https://tauri.app/v2/guides/features/state-management/)
270
+ - Rust TypeId: [std::any::TypeId](https://doc.rust-lang.org/std/any/struct.TypeId.html)
271
+
272
+ **영향도:**
273
+ - 타입 안전성: MEDIUM (타입 충돌 방지)
274
+ - 버그 가능성: HIGH (잘못된 상태 사용 방지)
275
+ - 유지보수: HIGH (명확한 구조)