@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.
- package/dist/index.js +7 -1
- package/package.json +1 -1
- package/templates/.claude/agents/analyst.md +5 -0
- package/templates/.claude/agents/architect.md +5 -0
- package/templates/.claude/agents/build-fixer.md +1 -0
- package/templates/.claude/agents/code-reviewer.md +1 -0
- package/templates/.claude/agents/critic.md +4 -0
- package/templates/.claude/agents/deep-executor.md +1 -0
- package/templates/.claude/agents/dependency-manager.md +2 -0
- package/templates/.claude/agents/deployment-validator.md +2 -0
- package/templates/.claude/agents/designer.md +2 -0
- package/templates/.claude/agents/document-writer.md +3 -0
- package/templates/.claude/agents/explore.md +1 -0
- package/templates/.claude/agents/git-operator.md +2 -0
- package/templates/.claude/agents/implementation-executor.md +2 -0
- package/templates/.claude/agents/ko-to-en-translator.md +3 -0
- package/templates/.claude/agents/lint-fixer.md +2 -0
- package/templates/.claude/agents/planner.md +3 -0
- package/templates/.claude/agents/pm.md +349 -0
- package/templates/.claude/agents/qa-tester.md +1 -0
- package/templates/.claude/agents/refactor-advisor.md +4 -0
- package/templates/.claude/agents/researcher.md +9 -1
- package/templates/.claude/agents/scientist.md +1 -0
- package/templates/.claude/agents/security-reviewer.md +1 -0
- package/templates/.claude/agents/tdd-guide.md +1 -0
- package/templates/.claude/agents/vision.md +1 -0
- package/templates/.claude/instructions/agent-patterns/agent-teams-usage.md +376 -0
- package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
- package/templates/.claude/scripts/agent-teams/check-availability.sh +238 -0
- package/templates/.claude/scripts/agent-teams/setup-tmux.sh +125 -0
- package/templates/.claude/skills/agent-teams-setup/SKILL.md +460 -0
- package/templates/.claude/skills/brainstorm/SKILL.md +1 -0
- package/templates/.claude/skills/bug-fix/SKILL.md +1 -0
- package/templates/.claude/skills/crawler/SKILL.md +2 -0
- package/templates/.claude/skills/docs-creator/SKILL.md +1 -0
- package/templates/.claude/skills/docs-fetch/SKILL.md +6 -4
- package/templates/.claude/skills/docs-refactor/SKILL.md +1 -0
- package/templates/.claude/skills/elon-musk/SKILL.md +1 -0
- package/templates/.claude/skills/execute/SKILL.md +1 -0
- package/templates/.claude/skills/feedback/SKILL.md +1 -0
- package/templates/.claude/skills/figma-to-code/SKILL.md +1 -0
- package/templates/.claude/skills/genius-thinking/SKILL.md +1 -0
- package/templates/.claude/skills/global-uiux-design/SKILL.md +1 -0
- package/templates/.claude/skills/korea-uiux-design/SKILL.md +1 -0
- package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/plan/SKILL.md +1 -0
- package/templates/.claude/skills/prd/SKILL.md +1 -0
- package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
- package/templates/.claude/skills/project-optimizer/SKILL.md +375 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-config-centralize.md +66 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-hot-path.md +35 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-interface-segregation.md +51 -0
- package/templates/.claude/skills/project-optimizer/rules/arch-module-boundary.md +42 -0
- package/templates/.claude/skills/project-optimizer/rules/build-cache.md +57 -0
- package/templates/.claude/skills/project-optimizer/rules/build-code-split.md +56 -0
- package/templates/.claude/skills/project-optimizer/rules/build-incremental.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/build-minify.md +61 -0
- package/templates/.claude/skills/project-optimizer/rules/build-tree-shake.md +60 -0
- package/templates/.claude/skills/project-optimizer/rules/code-complexity.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/code-dead-elimination.md +32 -0
- package/templates/.claude/skills/project-optimizer/rules/code-duplication.md +54 -0
- package/templates/.claude/skills/project-optimizer/rules/code-error-handling.md +75 -0
- package/templates/.claude/skills/project-optimizer/rules/code-naming.md +52 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-defer-await.md +54 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-parallel.md +90 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-pipeline.md +68 -0
- package/templates/.claude/skills/project-optimizer/rules/concurrency-pool.md +68 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-lightweight-alt.md +37 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-peer-align.md +44 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-security-audit.md +45 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-unused-removal.md +25 -0
- package/templates/.claude/skills/project-optimizer/rules/deps-version-pin.md +40 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-ci-speed.md +47 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-dev-server.md +35 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-lint-config.md +36 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-test-coverage.md +34 -0
- package/templates/.claude/skills/project-optimizer/rules/dx-type-safety.md +49 -0
- package/templates/.claude/skills/project-optimizer/rules/io-batch-queries.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-cache-layer.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-connection-reuse.md +67 -0
- package/templates/.claude/skills/project-optimizer/rules/io-serialize-minimal.md +61 -0
- package/templates/.claude/skills/project-optimizer/rules/io-stream.md +75 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-bounded-cache.md +65 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-large-data.md +64 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-lazy-init.md +78 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-leak-prevention.md +79 -0
- package/templates/.claude/skills/project-optimizer/rules/memory-pool-reuse.md +70 -0
- package/templates/.claude/skills/ralph/SKILL.md +1 -0
- package/templates/.claude/skills/refactor/SKILL.md +1 -0
- package/templates/.claude/skills/research/SKILL.md +1 -0
- package/templates/.claude/skills/sql-optimizer/SKILL.md +438 -0
- package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -0
- package/templates/.claude/skills/startup-validator/SKILL.md +1 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +53 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +94 -27
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +42 -19
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-optimistic-updates.md +109 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-suspense-query.md +74 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-use-hook.md +81 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-react-compiler.md +81 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-beforeload-auth.md +121 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-file-conventions.md +104 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-link-navigation.md +119 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-nested-layouts.md +155 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-path-params.md +89 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-pending-component.md +110 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-preload-strategy.md +91 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-router-context.md +120 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-search-params.md +114 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +1 -1
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-error-boundaries.md +79 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-middleware.md +85 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +56 -21
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-streaming.md +84 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-validator.md +71 -0
- package/templates/.claude/skills/tauri-react-best-practices/AGENTS.md +527 -0
- package/templates/.claude/skills/tauri-react-best-practices/SKILL.md +571 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-barrel-imports.md +140 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-cargo-profile.md +96 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-frontend-treeshake.md +242 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-lazy-components.md +255 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-remove-unused-commands.md +160 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-ci-pipeline.md +269 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-signing.md +207 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-updater.md +226 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-async-commands.md +172 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-batch-commands.md +133 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-binary-response.md +198 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-channel-streaming.md +186 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-error-handling.md +250 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-type-safe.md +227 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-derived-state.md +231 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-functional-setstate.md +191 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-index-maps.md +276 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/perf-lazy-state-init.md +196 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-lifecycle.md +265 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-mobile-compat.md +199 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-permission-scope.md +193 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-error-boundary.md +239 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-event-listener.md +151 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-file-src.md +155 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-invoke-hook.md +139 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/react-optimistic-update.md +211 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-capability-split.md +205 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-csp.md +207 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-least-privilege.md +106 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-no-wildcard.md +253 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/security-scope-paths.md +160 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-async-mutex.md +270 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-mutex-pattern.md +265 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-react-sync.md +375 -0
- package/templates/.claude/skills/tauri-react-best-practices/rules/state-single-container.md +275 -0
- package/templates/tanstack-start/docs/architecture.md +238 -167
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +777 -38
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +549 -37
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +895 -111
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +641 -43
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +889 -38
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +891 -29
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +972 -36
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +1525 -881
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +1099 -20
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +796 -30
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +953 -35
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +371 -15
- package/templates/tauri/CLAUDE.md +189 -0
- package/templates/tauri/docs/guides/distribution.md +261 -0
- package/templates/tauri/docs/guides/getting-started.md +302 -0
- package/templates/tauri/docs/guides/mobile.md +288 -0
- package/templates/tauri/docs/library/tauri/index.md +510 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# 자동 업데이트 서명 키 관리
|
|
2
|
+
|
|
3
|
+
## 왜 중요한가
|
|
4
|
+
|
|
5
|
+
Tauri의 자동 업데이트는 서명된 업데이트 파일만 설치합니다. 서명 키를 올바르게 생성하고 환경 변수로 관리하지 않으면 업데이트가 실패하거나 보안 취약점이 발생합니다. 키를 하드코딩하면 소스 코드 유출 시 악의적인 업데이트 배포가 가능합니다.
|
|
6
|
+
|
|
7
|
+
## ❌ 잘못된 패턴
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
// tauri.conf.json
|
|
11
|
+
{
|
|
12
|
+
"bundle": {
|
|
13
|
+
"updater": {
|
|
14
|
+
"active": true,
|
|
15
|
+
"endpoints": ["https://example.com/updates/{{target}}/{{current_version}}"],
|
|
16
|
+
// ❌ 공개 키를 하드코딩 (private 키는 더 위험)
|
|
17
|
+
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDEyMzQ1Njc4OTBBQkNERUYKUldRZEdzV3RQNjNiUEsxL0ZyQWt4SUVGQldRcVFRSkRYRFFRWENnPT0K"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# ❌ private 키를 프로젝트 디렉토리에 보관 (Git에 커밋 위험)
|
|
25
|
+
ls ~/.tauri/myapp.key
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**문제점:**
|
|
29
|
+
- 서명 키 미설정 또는 하드코딩
|
|
30
|
+
- private 키가 Git에 커밋될 위험
|
|
31
|
+
- CI/CD에서 키 관리 없음
|
|
32
|
+
- 업데이트 서명 실패
|
|
33
|
+
|
|
34
|
+
## ✅ 올바른 패턴
|
|
35
|
+
|
|
36
|
+
### 1. 서명 키 생성
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Tauri CLI로 서명 키 생성
|
|
40
|
+
pnpm tauri signer generate -- -w ~/.tauri/myapp.key
|
|
41
|
+
|
|
42
|
+
# 출력:
|
|
43
|
+
# Private key: ~/.tauri/myapp.key
|
|
44
|
+
# Public key: dW50cnVzdGVkIGNvbW1lbnQ6...
|
|
45
|
+
# Password: (비워두면 비밀번호 없음)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. 환경 변수로 키 관리
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# .env (Git에 커밋하지 않음)
|
|
52
|
+
TAURI_SIGNING_PRIVATE_KEY="dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5T0ZraGtUcjVVU28ybmNaNFN0Y1lHSzc0TVh4MkJiUGREN2xrPQo="
|
|
53
|
+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" # 비밀번호가 있으면 설정
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# .gitignore
|
|
58
|
+
.env
|
|
59
|
+
*.key
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. tauri.conf.json 설정
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"bundle": {
|
|
67
|
+
"identifier": "com.example.app",
|
|
68
|
+
"version": "1.0.0",
|
|
69
|
+
"updater": {
|
|
70
|
+
"active": true,
|
|
71
|
+
"endpoints": [
|
|
72
|
+
"https://example.com/updates/{{target}}/{{current_version}}"
|
|
73
|
+
],
|
|
74
|
+
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDEyMzQ1Njc4OTBBQkNERUYKUldRZEdzV3RQNjNiUEsxL0ZyQWt4SUVGQldRcVFRSkRYRFFRWENnPT0K"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4. 빌드 시 서명
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# private 키 환경 변수로 주입
|
|
84
|
+
export TAURI_SIGNING_PRIVATE_KEY=$(cat ~/.tauri/myapp.key)
|
|
85
|
+
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD=""
|
|
86
|
+
|
|
87
|
+
# 빌드 (자동으로 서명됨)
|
|
88
|
+
pnpm tauri build
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 5. 업데이트 서버 응답 형식
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"version": "1.0.1",
|
|
96
|
+
"date": "2024-02-09T12:00:00Z",
|
|
97
|
+
"platforms": {
|
|
98
|
+
"darwin-aarch64": {
|
|
99
|
+
"url": "https://example.com/releases/MyApp-1.0.1-arm64.app.tar.gz",
|
|
100
|
+
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUldRZEdzV3RQNjNiUEsxL0ZyQWt4SUVGQldRcVFRSkRYRFFRWENnPT0K"
|
|
101
|
+
},
|
|
102
|
+
"darwin-x86_64": {
|
|
103
|
+
"url": "https://example.com/releases/MyApp-1.0.1-x64.app.tar.gz",
|
|
104
|
+
"signature": "..."
|
|
105
|
+
},
|
|
106
|
+
"windows-x86_64": {
|
|
107
|
+
"url": "https://example.com/releases/MyApp-1.0.1-x64.msi.zip",
|
|
108
|
+
"signature": "..."
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 6. GitHub Actions CI/CD
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
name: Release
|
|
118
|
+
on:
|
|
119
|
+
push:
|
|
120
|
+
tags:
|
|
121
|
+
- 'v*'
|
|
122
|
+
|
|
123
|
+
jobs:
|
|
124
|
+
build:
|
|
125
|
+
runs-on: ${{ matrix.platform }}
|
|
126
|
+
strategy:
|
|
127
|
+
matrix:
|
|
128
|
+
platform: [macos-latest, windows-latest, ubuntu-latest]
|
|
129
|
+
|
|
130
|
+
steps:
|
|
131
|
+
- uses: actions/checkout@v4
|
|
132
|
+
- uses: pnpm/action-setup@v2
|
|
133
|
+
- uses: actions/setup-node@v4
|
|
134
|
+
|
|
135
|
+
- name: Install dependencies
|
|
136
|
+
run: pnpm install
|
|
137
|
+
|
|
138
|
+
- name: Build with signing
|
|
139
|
+
env:
|
|
140
|
+
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
141
|
+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
142
|
+
run: pnpm tauri build
|
|
143
|
+
|
|
144
|
+
- name: Upload artifacts
|
|
145
|
+
uses: actions/upload-artifact@v4
|
|
146
|
+
with:
|
|
147
|
+
name: app-${{ matrix.platform }}
|
|
148
|
+
path: ./target/release/bundle/**/*
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**GitHub Secrets 설정:**
|
|
152
|
+
1. GitHub 저장소 → Settings → Secrets and variables → Actions
|
|
153
|
+
2. New repository secret:
|
|
154
|
+
- `TAURI_SIGNING_PRIVATE_KEY`: `cat ~/.tauri/myapp.key` 출력값
|
|
155
|
+
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`: 비밀번호 (없으면 빈 문자열)
|
|
156
|
+
|
|
157
|
+
### 7. 프론트엔드에서 업데이트 확인
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
import { check, installUpdate } from '@tauri-apps/plugin-updater'
|
|
161
|
+
import { relaunch } from '@tauri-apps/plugin-process'
|
|
162
|
+
import { useEffect, useState } from 'react'
|
|
163
|
+
|
|
164
|
+
function UpdateChecker() {
|
|
165
|
+
const [updateAvailable, setUpdateAvailable] = useState(false)
|
|
166
|
+
const [loading, setLoading] = useState(false)
|
|
167
|
+
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
check().then(update => {
|
|
170
|
+
if (update?.available) {
|
|
171
|
+
setUpdateAvailable(true)
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
}, [])
|
|
175
|
+
|
|
176
|
+
const handleUpdate = async () => {
|
|
177
|
+
setLoading(true)
|
|
178
|
+
try {
|
|
179
|
+
await installUpdate()
|
|
180
|
+
await relaunch()
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error('Update failed:', error)
|
|
183
|
+
setLoading(false)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!updateAvailable) return null
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div className="update-banner">
|
|
191
|
+
<p>새 버전이 있습니다!</p>
|
|
192
|
+
<button onClick={handleUpdate} disabled={loading}>
|
|
193
|
+
{loading ? 'Updating...' : 'Update Now'}
|
|
194
|
+
</button>
|
|
195
|
+
</div>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## 추가 컨텍스트
|
|
201
|
+
|
|
202
|
+
**서명 키 관리 원칙:**
|
|
203
|
+
1. private 키는 절대 Git에 커밋하지 않음
|
|
204
|
+
2. 환경 변수로만 관리 (`.env`, GitHub Secrets)
|
|
205
|
+
3. public 키는 `tauri.conf.json`에 하드코딩 가능
|
|
206
|
+
4. private 키는 팀 내 안전한 저장소에 백업
|
|
207
|
+
|
|
208
|
+
**Updater 엔드포인트 변수:**
|
|
209
|
+
- `{{target}}`: 플랫폼 식별자 (darwin-aarch64, windows-x86_64 등)
|
|
210
|
+
- `{{current_version}}`: 현재 앱 버전
|
|
211
|
+
- `{{arch}}`: CPU 아키텍처
|
|
212
|
+
|
|
213
|
+
**서명 파일 위치:**
|
|
214
|
+
- macOS: `*.app.tar.gz.sig`
|
|
215
|
+
- Windows: `*.msi.zip.sig`
|
|
216
|
+
- Linux: `*.AppImage.tar.gz.sig`
|
|
217
|
+
|
|
218
|
+
**권장 업데이트 서버:**
|
|
219
|
+
- GitHub Releases (무료, 간단)
|
|
220
|
+
- AWS S3 + CloudFront
|
|
221
|
+
- Cloudflare R2
|
|
222
|
+
- 자체 서버 (Nginx/Caddy)
|
|
223
|
+
|
|
224
|
+
**참고:** [Tauri Updater Guide](https://beta.tauri.app/plugin/updater/)
|
|
225
|
+
|
|
226
|
+
영향도: CRITICAL - 보안, 자동 업데이트 기능, 배포 신뢰성
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use async Commands for Non-Blocking Operations
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: Prevent UI freezes with async Rust commands
|
|
5
|
+
tags: ipc, async, tokio, performance, tauri-v2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# async 커맨드로 UI 차단 방지
|
|
9
|
+
|
|
10
|
+
## 왜 중요한가
|
|
11
|
+
|
|
12
|
+
Rust 커맨드가 동기(blocking) 작업을 수행하면 Tauri 런타임이 차단되어 UI가 멈춥니다. `async` 커맨드를 사용하면 I/O, 네트워크, 파일 작업을 비차단(non-blocking) 방식으로 실행하여 UI 응답성을 유지할 수 있습니다.
|
|
13
|
+
|
|
14
|
+
**영향도:**
|
|
15
|
+
- UI 응답성: 차단 방지
|
|
16
|
+
- 동시성: 여러 작업 병렬 처리
|
|
17
|
+
- 사용자 경험: 부드러운 인터랙션
|
|
18
|
+
|
|
19
|
+
## ❌ 잘못된 패턴
|
|
20
|
+
|
|
21
|
+
**동기 커맨드에서 blocking 작업:**
|
|
22
|
+
|
|
23
|
+
```rust
|
|
24
|
+
use std::fs;
|
|
25
|
+
use std::thread::sleep;
|
|
26
|
+
use std::time::Duration;
|
|
27
|
+
|
|
28
|
+
// ❌ 동기 커맨드에서 파일 I/O (UI 차단)
|
|
29
|
+
#[tauri::command]
|
|
30
|
+
fn read_large_file(path: String) -> Result<String, String> {
|
|
31
|
+
// 메인 스레드 차단
|
|
32
|
+
let content = fs::read_to_string(&path)
|
|
33
|
+
.map_err(|e| e.to_string())?;
|
|
34
|
+
Ok(content)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ❌ 동기 커맨드에서 sleep (UI 멈춤)
|
|
38
|
+
#[tauri::command]
|
|
39
|
+
fn slow_operation() -> String {
|
|
40
|
+
sleep(Duration::from_secs(5)); // 5초 동안 UI 멈춤
|
|
41
|
+
"Done".to_string()
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**문제점:**
|
|
46
|
+
- 커맨드 실행 중 UI 완전히 멈춤
|
|
47
|
+
- 다른 IPC 요청도 차단됨
|
|
48
|
+
- 사용자 경험 저하
|
|
49
|
+
|
|
50
|
+
## ✅ 올바른 패턴
|
|
51
|
+
|
|
52
|
+
**async 커맨드로 비차단 실행:**
|
|
53
|
+
|
|
54
|
+
```rust
|
|
55
|
+
use tokio::fs;
|
|
56
|
+
use tokio::time::{sleep, Duration};
|
|
57
|
+
|
|
58
|
+
// ✅ async 커맨드로 파일 I/O (UI 차단 없음)
|
|
59
|
+
#[tauri::command]
|
|
60
|
+
async fn read_large_file(path: String) -> Result<String, String> {
|
|
61
|
+
// tokio 비동기 파일 I/O
|
|
62
|
+
let content = fs::read_to_string(&path)
|
|
63
|
+
.await
|
|
64
|
+
.map_err(|e| e.to_string())?;
|
|
65
|
+
Ok(content)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ✅ async 커맨드로 지연 작업 (UI 응답 유지)
|
|
69
|
+
#[tauri::command]
|
|
70
|
+
async fn slow_operation() -> String {
|
|
71
|
+
sleep(Duration::from_secs(5)).await; // 다른 작업 진행 가능
|
|
72
|
+
"Done".to_string()
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**HTTP 요청 예시:**
|
|
77
|
+
|
|
78
|
+
```rust
|
|
79
|
+
use reqwest;
|
|
80
|
+
|
|
81
|
+
// ✅ 비동기 HTTP 요청
|
|
82
|
+
#[tauri::command]
|
|
83
|
+
async fn fetch_data(url: String) -> Result<String, String> {
|
|
84
|
+
let response = reqwest::get(&url)
|
|
85
|
+
.await
|
|
86
|
+
.map_err(|e| e.to_string())?
|
|
87
|
+
.text()
|
|
88
|
+
.await
|
|
89
|
+
.map_err(|e| e.to_string())?;
|
|
90
|
+
Ok(response)
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**병렬 작업 실행:**
|
|
95
|
+
|
|
96
|
+
```rust
|
|
97
|
+
use tokio::task::spawn;
|
|
98
|
+
|
|
99
|
+
#[tauri::command]
|
|
100
|
+
async fn process_multiple_files(paths: Vec<String>) -> Result<Vec<String>, String> {
|
|
101
|
+
let mut tasks = vec![];
|
|
102
|
+
|
|
103
|
+
for path in paths {
|
|
104
|
+
// 각 파일을 별도 태스크로 처리
|
|
105
|
+
let task = spawn(async move {
|
|
106
|
+
fs::read_to_string(&path).await
|
|
107
|
+
});
|
|
108
|
+
tasks.push(task);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 모든 태스크 완료 대기
|
|
112
|
+
let results = futures::future::try_join_all(tasks)
|
|
113
|
+
.await
|
|
114
|
+
.map_err(|e| e.to_string())?;
|
|
115
|
+
|
|
116
|
+
results
|
|
117
|
+
.into_iter()
|
|
118
|
+
.collect::<Result<Vec<_>, _>>()
|
|
119
|
+
.map_err(|e| e.to_string())
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## async에서 &str 사용 제한
|
|
124
|
+
|
|
125
|
+
**❌ async 함수에서 &str 파라미터 불가:**
|
|
126
|
+
|
|
127
|
+
```rust
|
|
128
|
+
// ❌ 컴파일 에러: async 함수는 'static 라이프타임 필요
|
|
129
|
+
#[tauri::command]
|
|
130
|
+
async fn process_text(text: &str) -> String {
|
|
131
|
+
// Error: `text` has an incompatible lifetime
|
|
132
|
+
text.to_uppercase()
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**✅ String 사용:**
|
|
137
|
+
|
|
138
|
+
```rust
|
|
139
|
+
// ✅ String으로 소유권 확보
|
|
140
|
+
#[tauri::command]
|
|
141
|
+
async fn process_text(text: String) -> String {
|
|
142
|
+
text.to_uppercase()
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**이유:**
|
|
147
|
+
- async 함수는 Future로 변환되어 나중에 실행됨
|
|
148
|
+
- 참조(&str)는 원본 데이터가 유효함을 보장할 수 없음
|
|
149
|
+
- String은 소유권을 가져 'static 라이프타임 요구사항 충족
|
|
150
|
+
|
|
151
|
+
## 추가 컨텍스트
|
|
152
|
+
|
|
153
|
+
**언제 async 커맨드를 사용해야 하는가:**
|
|
154
|
+
- 파일 I/O (`tokio::fs`)
|
|
155
|
+
- 네트워크 요청 (`reqwest`, `tokio::net`)
|
|
156
|
+
- 데이터베이스 쿼리 (`sqlx`, `tokio-postgres`)
|
|
157
|
+
- 타이머/지연 (`tokio::time::sleep`)
|
|
158
|
+
- CPU 집약적이지 않은 모든 작업
|
|
159
|
+
|
|
160
|
+
**동기 커맨드를 사용해도 되는 경우:**
|
|
161
|
+
- 즉시 완료되는 간단한 계산
|
|
162
|
+
- 상태 읽기/쓰기 (Mutex)
|
|
163
|
+
- 메모리 작업만 수행하는 경우
|
|
164
|
+
|
|
165
|
+
**주의사항:**
|
|
166
|
+
- `Cargo.toml`에 `tokio` 의존성 추가 필요: `tokio = { version = "1", features = ["full"] }`
|
|
167
|
+
- CPU 집약적 작업은 `tokio::task::spawn_blocking` 사용
|
|
168
|
+
- async 커맨드 내부에서 std 동기 함수 호출 시 주의
|
|
169
|
+
|
|
170
|
+
**참고:**
|
|
171
|
+
- [Tauri Async Commands](https://tauri.app/develop/calling-rust/#async-commands)
|
|
172
|
+
- [Tokio Documentation](https://tokio.rs/)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Batch Multiple invoke Calls into Single Command
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: Reduce IPC round-trips from N to 1
|
|
5
|
+
tags: ipc, performance, batch, tauri-v2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 배치 커맨드로 IPC 왕복 최소화
|
|
9
|
+
|
|
10
|
+
## 왜 중요한가
|
|
11
|
+
|
|
12
|
+
여러 개의 `invoke` 호출을 순차적으로 실행하면 각각 IPC 왕복(round-trip)이 발생하여 성능이 저하됩니다. 특히 루프 안에서 `invoke`를 호출하면 N번의 IPC 오버헤드가 발생합니다. 배치 커맨드로 통합하면 IPC 왕복을 1회로 줄여 처리 속도를 크게 향상시킬 수 있습니다.
|
|
13
|
+
|
|
14
|
+
**영향도:**
|
|
15
|
+
- IPC 왕복: N회 → 1회
|
|
16
|
+
- 지연 시간: 누적 감소
|
|
17
|
+
- 처리량: 5-10배 향상 (데이터 크기에 따라)
|
|
18
|
+
|
|
19
|
+
## ❌ 잘못된 패턴
|
|
20
|
+
|
|
21
|
+
**프론트엔드에서 개별 호출:**
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// ❌ 루프에서 N번 invoke 호출
|
|
25
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
26
|
+
|
|
27
|
+
async function processItems(items: string[]) {
|
|
28
|
+
const results = [];
|
|
29
|
+
for (const item of items) {
|
|
30
|
+
// 각 호출마다 IPC 왕복 발생
|
|
31
|
+
const result = await invoke<string>('process_item', { item });
|
|
32
|
+
results.push(result);
|
|
33
|
+
}
|
|
34
|
+
return results;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**문제점:**
|
|
39
|
+
- 100개 아이템 = 100번 IPC 왕복
|
|
40
|
+
- 각 호출마다 직렬화/역직렬화 오버헤드
|
|
41
|
+
- 네트워크 레이턴시와 유사한 누적 지연
|
|
42
|
+
|
|
43
|
+
## ✅ 올바른 패턴
|
|
44
|
+
|
|
45
|
+
**배치 커맨드로 통합:**
|
|
46
|
+
|
|
47
|
+
```rust
|
|
48
|
+
// ✅ Rust: 배치 처리 커맨드
|
|
49
|
+
#[tauri::command]
|
|
50
|
+
fn process_batch(items: Vec<String>) -> Result<Vec<String>, String> {
|
|
51
|
+
items
|
|
52
|
+
.iter()
|
|
53
|
+
.map(|item| process_item(item))
|
|
54
|
+
.collect::<Result<Vec<_>, _>>()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn process_item(item: &str) -> Result<String, String> {
|
|
58
|
+
// 실제 처리 로직
|
|
59
|
+
Ok(item.to_uppercase())
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// ✅ 프론트엔드: 1번 호출
|
|
65
|
+
import { invoke } from '@tauri-apps/api/core';
|
|
66
|
+
|
|
67
|
+
async function processItems(items: string[]) {
|
|
68
|
+
// 단일 IPC 왕복
|
|
69
|
+
const results = await invoke<string[]>('process_batch', { items });
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 사용 예시
|
|
74
|
+
const items = ['apple', 'banana', 'cherry'];
|
|
75
|
+
const results = await processItems(items); // ['APPLE', 'BANANA', 'CHERRY']
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**병렬 처리가 필요한 경우:**
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
use rayon::prelude::*;
|
|
82
|
+
|
|
83
|
+
#[tauri::command]
|
|
84
|
+
fn process_batch_parallel(items: Vec<String>) -> Result<Vec<String>, String> {
|
|
85
|
+
items
|
|
86
|
+
.par_iter() // 병렬 반복자
|
|
87
|
+
.map(|item| process_item(item))
|
|
88
|
+
.collect::<Result<Vec<_>, _>>()
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**부분 실패 처리:**
|
|
93
|
+
|
|
94
|
+
```rust
|
|
95
|
+
use serde::{Deserialize, Serialize};
|
|
96
|
+
|
|
97
|
+
#[derive(Serialize, Deserialize)]
|
|
98
|
+
struct BatchResult {
|
|
99
|
+
success: Vec<String>,
|
|
100
|
+
failed: Vec<(String, String)>, // (item, error)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#[tauri::command]
|
|
104
|
+
fn process_batch_resilient(items: Vec<String>) -> BatchResult {
|
|
105
|
+
let mut success = Vec::new();
|
|
106
|
+
let mut failed = Vec::new();
|
|
107
|
+
|
|
108
|
+
for item in items {
|
|
109
|
+
match process_item(&item) {
|
|
110
|
+
Ok(result) => success.push(result),
|
|
111
|
+
Err(err) => failed.push((item, err)),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
BatchResult { success, failed }
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 추가 컨텍스트
|
|
120
|
+
|
|
121
|
+
**언제 배치 커맨드를 사용해야 하는가:**
|
|
122
|
+
- 같은 커맨드를 3회 이상 호출해야 할 때
|
|
123
|
+
- 루프 안에서 `invoke`를 호출할 때
|
|
124
|
+
- 배열 데이터를 개별 처리해야 할 때
|
|
125
|
+
|
|
126
|
+
**주의사항:**
|
|
127
|
+
- 배치 크기가 매우 큰 경우(10,000+ 아이템) 청크 단위로 분할 호출
|
|
128
|
+
- 메모리 사용량 모니터링 필요
|
|
129
|
+
- 진행률 표시가 필요하면 [Channel 스트리밍](./ipc-channel-streaming.md) 패턴 사용
|
|
130
|
+
|
|
131
|
+
**참고:**
|
|
132
|
+
- [Tauri IPC Guide](https://tauri.app/concept/inter-process-communication/)
|
|
133
|
+
- [Calling Rust from Frontend](https://tauri.app/develop/calling-rust/)
|