@kood/claude-code 0.6.6 → 0.6.7
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/researcher.md +8 -1
- package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
- package/templates/.claude/skills/docs-fetch/SKILL.md +5 -4
- package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
- package/templates/.claude/skills/project-optimizer/SKILL.md +374 -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/sql-optimizer/SKILL.md +437 -0
- package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -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 +93 -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 +570 -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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# TanStack Start React 베스트 프랙티스
|
|
2
2
|
|
|
3
|
-
**Version
|
|
4
|
-
TanStack Start 최적화 가이드
|
|
5
|
-
|
|
3
|
+
**Version 3.0.0**
|
|
4
|
+
TanStack Start v1 + TanStack Router + React 19 최적화 가이드
|
|
5
|
+
February 2026
|
|
6
6
|
|
|
7
7
|
> **참고:**
|
|
8
8
|
> 이 문서는 주로 에이전트와 LLM이 React 및 TanStack Start 코드베이스를 유지보수, 생성, 리팩토링할 때 따르기 위한 것입니다. 사람도 유용하게 사용할 수 있지만, AI 지원 워크플로의 자동화 및 일관성을 위해 최적화되어 있습니다.
|
|
@@ -11,7 +11,7 @@ January 2026
|
|
|
11
11
|
|
|
12
12
|
## 요약
|
|
13
13
|
|
|
14
|
-
AI 에이전트와 LLM을 위한 React 및 TanStack Start 애플리케이션 종합 성능 최적화 가이드.
|
|
14
|
+
AI 에이전트와 LLM을 위한 React 19 및 TanStack Start v1 애플리케이션 종합 성능 최적화 가이드. 8개 카테고리에 걸쳐 55개 규칙을 포함하며, 영향도별로 우선순위를 매겼습니다 (critical: waterfall 제거, 번들 크기 감소 → incremental: JavaScript 성능). TanStack Router 라우팅 패턴(Link, useNavigate, useSearch, useParams, beforeLoad, Outlet, pendingComponent, 프리로딩, 파일 컨벤션), React Compiler 자동 메모이제이션, use() hook, useOptimistic, createMiddleware, inputValidator, 데이터 스트리밍 등 최신 패턴 반영. 각 규칙은 자동 리팩토링 및 코드 생성을 위한 상세한 설명, 올바른 구현 대 잘못된 구현을 비교하는 실제 예시, 구체적인 영향 지표를 포함합니다.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -32,10 +32,27 @@ AI 에이전트와 LLM을 위한 React 및 TanStack Start 애플리케이션 종
|
|
|
32
32
|
@rules/server-serialization.md
|
|
33
33
|
@rules/server-parallel-fetching.md
|
|
34
34
|
@rules/server-deferred-data.md
|
|
35
|
+
@rules/server-middleware.md
|
|
36
|
+
@rules/server-validator.md
|
|
37
|
+
@rules/server-streaming.md
|
|
38
|
+
@rules/server-error-boundaries.md
|
|
35
39
|
@rules/client-tanstack-query.md
|
|
40
|
+
@rules/client-suspense-query.md
|
|
41
|
+
@rules/client-optimistic-updates.md
|
|
42
|
+
@rules/client-use-hook.md
|
|
36
43
|
@rules/client-event-listeners.md
|
|
44
|
+
@rules/routing-file-conventions.md
|
|
45
|
+
@rules/routing-link-navigation.md
|
|
46
|
+
@rules/routing-search-params.md
|
|
47
|
+
@rules/routing-path-params.md
|
|
48
|
+
@rules/routing-beforeload-auth.md
|
|
49
|
+
@rules/routing-nested-layouts.md
|
|
50
|
+
@rules/routing-router-context.md
|
|
51
|
+
@rules/routing-preload-strategy.md
|
|
52
|
+
@rules/routing-pending-component.md
|
|
37
53
|
@rules/rerender-defer-reads.md
|
|
38
54
|
@rules/rerender-memo.md
|
|
55
|
+
@rules/rerender-react-compiler.md
|
|
39
56
|
@rules/rerender-dependencies.md
|
|
40
57
|
@rules/rerender-derived-state.md
|
|
41
58
|
@rules/rerender-functional-setstate.md
|
|
@@ -71,11 +88,12 @@ AI 에이전트와 LLM을 위한 React 및 TanStack Start 애플리케이션 종
|
|
|
71
88
|
|---------|---------|--------|------|
|
|
72
89
|
| 1 | Waterfall 제거 | **CRITICAL** | 순차 await를 병렬로 전환. 가장 큰 성능 향상 제공 |
|
|
73
90
|
| 2 | 번들 크기 최적화 | **CRITICAL** | TTI와 LCP 개선. 초기 로딩 속도 향상 |
|
|
74
|
-
| 3 |
|
|
75
|
-
| 4 |
|
|
76
|
-
| 5 |
|
|
77
|
-
| 6 |
|
|
78
|
-
| 7 |
|
|
91
|
+
| 3 | TanStack Router 라우팅 | **HIGH** | 타입 안전 네비게이션, 인증 가드, 레이아웃, 프리로딩 |
|
|
92
|
+
| 4 | 서버 사이드 성능 | HIGH | 서버 사이드 waterfall 제거, 응답 시간 단축 |
|
|
93
|
+
| 5 | 클라이언트 데이터 페칭 | MEDIUM-HIGH | 자동 중복 제거, 효율적 데이터 페칭 |
|
|
94
|
+
| 6 | Re-render 최적화 | MEDIUM | 불필요한 re-render 최소화, UI 반응성 향상 |
|
|
95
|
+
| 7 | 렌더링 성능 | MEDIUM | 브라우저 렌더링 작업 최적화 |
|
|
96
|
+
| 8 | JavaScript 성능 | LOW-MEDIUM | Hot path 마이크로 최적화 |
|
|
79
97
|
|
|
80
98
|
</categories>
|
|
81
99
|
|
|
@@ -718,13 +736,34 @@ function UserList({ users }: { users: User[] }) {
|
|
|
718
736
|
2. [TanStack Start Overview](https://tanstack.com/start/latest/docs/framework/react/overview)
|
|
719
737
|
3. [TanStack Start Quick Start](https://tanstack.com/start/latest/docs/framework/react/quick-start)
|
|
720
738
|
4. [TanStack Router](https://tanstack.com/router)
|
|
721
|
-
5. [TanStack Router Deferred Data Loading](https://tanstack.com/router/
|
|
722
|
-
6. [TanStack Query](https://tanstack.com/query)
|
|
739
|
+
5. [TanStack Router Deferred Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/deferred-data-loading)
|
|
740
|
+
6. [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview)
|
|
723
741
|
7. [Server Functions Guide](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions)
|
|
742
|
+
8. [Middleware Guide](https://tanstack.com/start/latest/docs/framework/react/guide/middleware)
|
|
743
|
+
9. [Streaming Data Guide](https://tanstack.com/start/latest/docs/framework/react/guide/streaming-data-from-server-functions)
|
|
744
|
+
|
|
745
|
+
### TanStack Router
|
|
746
|
+
10. [Navigation Guide](https://tanstack.com/router/latest/docs/framework/react/guide/navigation)
|
|
747
|
+
11. [Link Options](https://tanstack.com/router/latest/docs/framework/react/guide/link-options)
|
|
748
|
+
12. [Search Params](https://tanstack.com/router/latest/docs/framework/react/guide/search-params)
|
|
749
|
+
13. [Path Params](https://tanstack.com/router/latest/docs/framework/react/guide/path-params)
|
|
750
|
+
14. [Authenticated Routes](https://tanstack.com/router/latest/docs/framework/react/guide/authenticated-routes)
|
|
751
|
+
15. [Outlets](https://tanstack.com/router/latest/docs/framework/react/guide/outlets)
|
|
752
|
+
16. [Route Trees](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees)
|
|
753
|
+
17. [File-Based Routing](https://tanstack.com/router/latest/docs/framework/react/routing/file-based-routing)
|
|
754
|
+
18. [Router Context](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
|
|
755
|
+
19. [Preloading](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
756
|
+
20. [Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading)
|
|
757
|
+
|
|
758
|
+
### React 19
|
|
759
|
+
21. [React 19 Blog](https://react.dev/blog/2024/12/05/react-19)
|
|
760
|
+
22. [React use() API](https://react.dev/reference/react/use)
|
|
761
|
+
23. [React useOptimistic](https://react.dev/reference/react/useOptimistic)
|
|
762
|
+
24. [React Compiler](https://react.dev/learn/react-compiler)
|
|
724
763
|
|
|
725
764
|
### 외부 자료
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
765
|
+
25. [better-all](https://github.com/shuding/better-all)
|
|
766
|
+
26. [node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
767
|
+
27. [Using Server Functions and TanStack Query](https://www.brenelz.com/posts/using-server-functions-and-tanstack-query/)
|
|
729
768
|
|
|
730
769
|
</references>
|
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
framework: tanstack-start
|
|
6
6
|
metadata:
|
|
7
7
|
author: vercel
|
|
8
|
-
version: "
|
|
8
|
+
version: "3.0.0"
|
|
9
9
|
adapted_for: tanstack-start
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ metadata:
|
|
|
17
17
|
|
|
18
18
|
# TanStack Start React 베스트 프랙티스
|
|
19
19
|
|
|
20
|
-
React와 TanStack Start 애플리케이션 성능 최적화 가이드.
|
|
20
|
+
React 19와 TanStack Start v1 애플리케이션 성능 최적화 가이드. 8개 카테고리, 55개 규칙 포함. 영향도별 우선순위로 자동 리팩토링과 코드 생성 가이드 제공. TanStack Router 라우팅 패턴(Link, useNavigate, useSearch, useParams, beforeLoad, Outlet, pendingComponent, 프리로딩), React Compiler, use() hook, useOptimistic, Middleware, Streaming 등 최신 패턴 반영.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -188,11 +188,12 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
188
188
|
|---------|---------|--------|--------|
|
|
189
189
|
| 1 | Waterfall 제거 | **CRITICAL** | `async-` |
|
|
190
190
|
| 2 | 번들 크기 최적화 | **CRITICAL** | `bundle-` |
|
|
191
|
-
| 3 |
|
|
192
|
-
| 4 |
|
|
193
|
-
| 5 |
|
|
194
|
-
| 6 |
|
|
195
|
-
| 7 |
|
|
191
|
+
| 3 | TanStack Router 라우팅 | **HIGH** | `routing-` |
|
|
192
|
+
| 4 | 서버 사이드 성능 | HIGH | `server-` |
|
|
193
|
+
| 5 | 클라이언트 데이터 페칭 | MEDIUM-HIGH | `client-` |
|
|
194
|
+
| 6 | Re-render 최적화 | MEDIUM | `rerender-` |
|
|
195
|
+
| 7 | 렌더링 성능 | MEDIUM | `rendering-` |
|
|
196
|
+
| 8 | JavaScript 성능 | LOW-MEDIUM | `js-` |
|
|
196
197
|
|
|
197
198
|
</categories>
|
|
198
199
|
|
|
@@ -221,35 +222,57 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
221
222
|
| `bundle-conditional` | 기능 활성화 시에만 모듈 로드 |
|
|
222
223
|
| `bundle-preload` | hover/focus 시 preload로 체감 속도 향상 |
|
|
223
224
|
|
|
224
|
-
### 3.
|
|
225
|
+
### 3. TanStack Router 라우팅 (HIGH)
|
|
226
|
+
|
|
227
|
+
| 규칙 | 설명 |
|
|
228
|
+
|------|------|
|
|
229
|
+
| `routing-file-conventions` | 파일 기반 라우팅 컨벤션 (__root, $param, _layout, lazy) |
|
|
230
|
+
| `routing-link-navigation` | Link 컴포넌트와 useNavigate로 타입 안전 네비게이션 |
|
|
231
|
+
| `routing-search-params` | useSearch + Zod 스키마로 search params 검증 |
|
|
232
|
+
| `routing-path-params` | useParams와 getRouteApi로 타입 안전 path params |
|
|
233
|
+
| `routing-beforeload-auth` | beforeLoad로 인증 가드, pathless layout 조합 |
|
|
234
|
+
| `routing-nested-layouts` | Outlet과 pathless layout으로 중첩 UI |
|
|
235
|
+
| `routing-router-context` | createRootRouteWithContext로 의존성 주입 |
|
|
236
|
+
| `routing-preload-strategy` | Link preload="intent"로 즉각적 네비게이션 |
|
|
237
|
+
| `routing-pending-component` | pendingComponent로 로딩 상태 (pendingMs/pendingMinMs) |
|
|
238
|
+
|
|
239
|
+
### 4. 서버 사이드 성능 (HIGH)
|
|
225
240
|
|
|
226
241
|
| 규칙 | 설명 |
|
|
227
242
|
|------|------|
|
|
228
243
|
| `server-cache-lru` | LRU 캐시로 요청 간 캐싱 |
|
|
229
|
-
| `server-serialization` |
|
|
244
|
+
| `server-serialization` | loader 데이터 직렬화 최소화 |
|
|
230
245
|
| `server-parallel-fetching` | loader에서 병렬 데이터 페칭 |
|
|
231
|
-
| `server-deferred-data` |
|
|
246
|
+
| `server-deferred-data` | Promise 반환으로 비차단 데이터 로딩 |
|
|
247
|
+
| `server-middleware` | createMiddleware()로 인증/로깅 중앙화 |
|
|
248
|
+
| `server-validator` | inputValidator()로 타입 안전한 Server Functions |
|
|
249
|
+
| `server-streaming` | async generator/ReadableStream으로 데이터 스트리밍 |
|
|
250
|
+
| `server-error-boundaries` | 라우트 레벨 errorComponent/notFoundComponent |
|
|
232
251
|
|
|
233
|
-
###
|
|
252
|
+
### 5. 클라이언트 데이터 페칭 (MEDIUM-HIGH)
|
|
234
253
|
|
|
235
254
|
| 규칙 | 설명 |
|
|
236
255
|
|------|------|
|
|
237
256
|
| `client-tanstack-query` | TanStack Query로 자동 캐싱/중복 제거 |
|
|
257
|
+
| `client-suspense-query` | useSuspenseQuery로 선언적 데이터 로딩 (v5) |
|
|
258
|
+
| `client-optimistic-updates` | useOptimistic으로 즉각적 UI 피드백 (React 19) |
|
|
259
|
+
| `client-use-hook` | use() hook으로 Promise 처리 (React 19) |
|
|
238
260
|
| `client-event-listeners` | 전역 이벤트 리스너 중복 제거 |
|
|
239
261
|
|
|
240
|
-
###
|
|
262
|
+
### 6. Re-render 최적화 (MEDIUM)
|
|
241
263
|
|
|
242
264
|
| 규칙 | 설명 |
|
|
243
265
|
|------|------|
|
|
244
266
|
| `rerender-defer-reads` | 콜백 전용 상태는 구독 안 함 |
|
|
245
267
|
| `rerender-memo` | 비싼 작업은 memoized 컴포넌트로 추출 |
|
|
268
|
+
| `rerender-react-compiler` | React Compiler로 자동 메모이제이션 (React 19) |
|
|
246
269
|
| `rerender-dependencies` | effect에 원시값 의존성 사용 |
|
|
247
270
|
| `rerender-derived-state` | 파생 boolean 구독, raw 값 구독 회피 |
|
|
248
271
|
| `rerender-functional-setstate` | 안정적 콜백용 함수형 setState |
|
|
249
272
|
| `rerender-lazy-state-init` | 비싼 초기값은 함수로 useState에 전달 |
|
|
250
273
|
| `rerender-transitions` | 비긴급 업데이트는 startTransition |
|
|
251
274
|
|
|
252
|
-
###
|
|
275
|
+
### 7. 렌더링 성능 (MEDIUM)
|
|
253
276
|
|
|
254
277
|
| 규칙 | 설명 |
|
|
255
278
|
|------|------|
|
|
@@ -259,7 +282,7 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
259
282
|
| `rendering-svg-precision` | SVG 좌표 정밀도 감소 |
|
|
260
283
|
| `rendering-conditional-render` | 조건부 렌더링은 &&가 아닌 삼항 연산자 |
|
|
261
284
|
|
|
262
|
-
###
|
|
285
|
+
### 8. JavaScript 성능 (LOW-MEDIUM)
|
|
263
286
|
|
|
264
287
|
| 규칙 | 설명 |
|
|
265
288
|
|------|------|
|
|
@@ -400,26 +423,46 @@ const addItems = useCallback((newItems: Item[]) => {
|
|
|
400
423
|
import { createServerFn } from '@tanstack/react-start'
|
|
401
424
|
import { z } from 'zod'
|
|
402
425
|
|
|
403
|
-
// ✅ 기본 Server Function
|
|
426
|
+
// ✅ 기본 Server Function (GET)
|
|
404
427
|
const getUser = createServerFn().handler(async () => {
|
|
405
428
|
// 서버에서만 실행
|
|
406
429
|
return await db.user.findMany()
|
|
407
430
|
})
|
|
408
431
|
|
|
409
|
-
// ✅ POST + Validation
|
|
410
|
-
const
|
|
432
|
+
// ✅ POST + Validation (inputValidator는 함수 형태 필수)
|
|
433
|
+
const UserSchema = z.object({
|
|
411
434
|
name: z.string().min(1),
|
|
412
435
|
email: z.string().email()
|
|
413
436
|
})
|
|
414
437
|
|
|
415
438
|
const createUser = createServerFn({ method: 'POST' })
|
|
416
|
-
.inputValidator(
|
|
439
|
+
.inputValidator((d: unknown) => UserSchema.parse(d))
|
|
417
440
|
.handler(async ({ data }) => {
|
|
418
|
-
// data는
|
|
441
|
+
// data는 자동으로 { name: string; email: string } 타입
|
|
419
442
|
return await db.user.create({ data })
|
|
420
443
|
})
|
|
421
444
|
```
|
|
422
445
|
|
|
446
|
+
### Middleware로 인증 중앙화
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
import { createMiddleware } from '@tanstack/react-start'
|
|
450
|
+
|
|
451
|
+
const authMiddleware = createMiddleware()
|
|
452
|
+
.server(async ({ next }) => {
|
|
453
|
+
const session = await getSession()
|
|
454
|
+
if (!session?.user) throw redirect({ to: '/login' })
|
|
455
|
+
return next({ context: { user: session.user } })
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
// 보호된 Server Function
|
|
459
|
+
const getMyTodos = createServerFn()
|
|
460
|
+
.middleware([authMiddleware])
|
|
461
|
+
.handler(async ({ context }) => {
|
|
462
|
+
return await db.todo.findMany({ where: { userId: context.user.id } })
|
|
463
|
+
})
|
|
464
|
+
```
|
|
465
|
+
|
|
423
466
|
### Loader 최적화
|
|
424
467
|
|
|
425
468
|
```typescript
|
|
@@ -546,18 +589,41 @@ rules/server-deferred-data.md
|
|
|
546
589
|
|
|
547
590
|
## 참고 자료
|
|
548
591
|
|
|
549
|
-
### TanStack 공식 문서
|
|
592
|
+
### TanStack Start 공식 문서
|
|
550
593
|
1. [React 공식 문서](https://react.dev)
|
|
551
594
|
2. [TanStack Start Overview](https://tanstack.com/start/latest/docs/framework/react/overview)
|
|
552
595
|
3. [TanStack Start Quick Start](https://tanstack.com/start/latest/docs/framework/react/quick-start)
|
|
553
|
-
4. [TanStack
|
|
554
|
-
5. [
|
|
555
|
-
6. [
|
|
556
|
-
7. [
|
|
596
|
+
4. [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview)
|
|
597
|
+
5. [Server Functions Guide](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions)
|
|
598
|
+
6. [Middleware Guide](https://tanstack.com/start/latest/docs/framework/react/guide/middleware)
|
|
599
|
+
7. [Streaming Data Guide](https://tanstack.com/start/latest/docs/framework/react/guide/streaming-data-from-server-functions)
|
|
600
|
+
8. [Authentication Guide](https://tanstack.com/start/latest/docs/framework/react/guide/authentication)
|
|
601
|
+
|
|
602
|
+
### TanStack Router
|
|
603
|
+
9. [TanStack Router](https://tanstack.com/router)
|
|
604
|
+
10. [Navigation Guide](https://tanstack.com/router/latest/docs/framework/react/guide/navigation)
|
|
605
|
+
11. [Link Options](https://tanstack.com/router/latest/docs/framework/react/guide/link-options)
|
|
606
|
+
12. [Search Params](https://tanstack.com/router/latest/docs/framework/react/guide/search-params)
|
|
607
|
+
13. [Path Params](https://tanstack.com/router/latest/docs/framework/react/guide/path-params)
|
|
608
|
+
14. [Authenticated Routes](https://tanstack.com/router/latest/docs/framework/react/guide/authenticated-routes)
|
|
609
|
+
15. [Outlets](https://tanstack.com/router/latest/docs/framework/react/guide/outlets)
|
|
610
|
+
16. [Route Trees](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees)
|
|
611
|
+
17. [File-Based Routing](https://tanstack.com/router/latest/docs/framework/react/routing/file-based-routing)
|
|
612
|
+
18. [Router Context](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
|
|
613
|
+
19. [Preloading](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
614
|
+
20. [Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading)
|
|
615
|
+
21. [Deferred Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/deferred-data-loading)
|
|
616
|
+
|
|
617
|
+
### React 19
|
|
618
|
+
22. [React 19 Blog](https://react.dev/blog/2024/12/05/react-19)
|
|
619
|
+
23. [React use() API](https://react.dev/reference/react/use)
|
|
620
|
+
24. [React useOptimistic](https://react.dev/reference/react/useOptimistic)
|
|
621
|
+
25. [React useActionState](https://react.dev/reference/react/useActionState)
|
|
622
|
+
26. [React Compiler](https://react.dev/learn/react-compiler)
|
|
557
623
|
|
|
558
624
|
### 외부 자료
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
625
|
+
27. [better-all](https://github.com/shuding/better-all)
|
|
626
|
+
28. [node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
627
|
+
29. [Using Server Functions and TanStack Query](https://www.brenelz.com/posts/using-server-functions-and-tanstack-query/)
|
|
562
628
|
|
|
563
629
|
</references>
|
|
@@ -13,37 +13,60 @@ tags: bundle, third-party, analytics, defer
|
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { Analytics } from '@vercel/analytics/react'
|
|
16
|
+
import { ErrorTracker } from './error-tracker'
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
function RootLayout({ children }: { children: React.ReactNode }) {
|
|
18
19
|
return (
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</html>
|
|
20
|
+
<div>
|
|
21
|
+
{children}
|
|
22
|
+
<Analytics />
|
|
23
|
+
<ErrorTracker />
|
|
24
|
+
</div>
|
|
25
25
|
)
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
**✅ 올바른 예시 (하이드레이션 후
|
|
29
|
+
**✅ 올바른 예시 (하이드레이션 후 lazy load):**
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
|
-
import
|
|
32
|
+
import { lazy, Suspense, useEffect, useState } from 'react'
|
|
33
33
|
|
|
34
|
-
const Analytics =
|
|
35
|
-
|
|
36
|
-
{ ssr: false }
|
|
34
|
+
const Analytics = lazy(() =>
|
|
35
|
+
import('@vercel/analytics/react').then(m => ({ default: m.Analytics }))
|
|
37
36
|
)
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
function RootLayout({ children }: { children: React.ReactNode }) {
|
|
39
|
+
const [hydrated, setHydrated] = useState(false)
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setHydrated(true)
|
|
43
|
+
}, [])
|
|
44
|
+
|
|
40
45
|
return (
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
<div>
|
|
47
|
+
{children}
|
|
48
|
+
{hydrated && (
|
|
49
|
+
<Suspense fallback={null}>
|
|
50
|
+
<Analytics />
|
|
51
|
+
</Suspense>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
47
54
|
)
|
|
48
55
|
}
|
|
49
56
|
```
|
|
57
|
+
|
|
58
|
+
**✅ 더 간단한 대안 (useEffect로 직접 초기화):**
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
function RootLayout({ children }: { children: React.ReactNode }) {
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
// 하이드레이션 후 비동기 로드
|
|
64
|
+
import('./analytics').then(mod => mod.init())
|
|
65
|
+
import('./error-tracker').then(mod => mod.init())
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
return <div>{children}</div>
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
TanStack Start에서는 `next/dynamic`을 사용할 수 없으므로, React의 `lazy()` + `Suspense` 또는 `useEffect` 내 동적 `import()`를 사용합니다.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use useOptimistic for Instant UI Feedback
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: eliminates perceived latency on mutations
|
|
5
|
+
tags: client, react-19, useOptimistic, mutations, optimistic-ui
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## useOptimistic으로 즉각적인 UI 피드백
|
|
9
|
+
|
|
10
|
+
React 19의 `useOptimistic`으로 서버 응답 대기 없이 UI를 즉시 업데이트하고, 실패 시 자동 롤백합니다.
|
|
11
|
+
|
|
12
|
+
**❌ 잘못된 예시 (서버 응답까지 대기):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function TodoList({ todos }: { todos: Todo[] }) {
|
|
16
|
+
const mutation = useMutation({
|
|
17
|
+
mutationFn: createTodo,
|
|
18
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<form onSubmit={(e) => {
|
|
23
|
+
e.preventDefault()
|
|
24
|
+
mutation.mutate({ title: inputValue })
|
|
25
|
+
// 사용자는 서버 응답까지 기다려야 아이템을 볼 수 있음
|
|
26
|
+
}}>
|
|
27
|
+
{mutation.isPending && <Spinner />}
|
|
28
|
+
{todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
|
|
29
|
+
</form>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**✅ 올바른 예시 (useOptimistic으로 즉시 반영):**
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { useOptimistic, startTransition } from 'react'
|
|
38
|
+
|
|
39
|
+
function TodoList({ todos }: { todos: Todo[] }) {
|
|
40
|
+
const [optimisticTodos, addOptimistic] = useOptimistic(
|
|
41
|
+
todos,
|
|
42
|
+
(current, newTodo: Todo) => [...current, { ...newTodo, pending: true }]
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const handleAdd = async (formData: FormData) => {
|
|
46
|
+
const newTodo = {
|
|
47
|
+
id: crypto.randomUUID(),
|
|
48
|
+
title: formData.get('title') as string,
|
|
49
|
+
completed: false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
startTransition(async () => {
|
|
53
|
+
addOptimistic(newTodo) // 즉시 UI에 반영
|
|
54
|
+
await createTodo({ data: newTodo }) // 서버에 저장
|
|
55
|
+
// 실패 시 자동으로 이전 상태로 롤백
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<form action={handleAdd}>
|
|
61
|
+
<input name="title" />
|
|
62
|
+
<button type="submit">추가</button>
|
|
63
|
+
{optimisticTodos.map(todo => (
|
|
64
|
+
<TodoItem
|
|
65
|
+
key={todo.id}
|
|
66
|
+
todo={todo}
|
|
67
|
+
style={{ opacity: todo.pending ? 0.5 : 1 }}
|
|
68
|
+
/>
|
|
69
|
+
))}
|
|
70
|
+
</form>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**TanStack Query와 조합:**
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
function LikeButton({ postId, liked, count }: Props) {
|
|
79
|
+
const [optimistic, setOptimistic] = useOptimistic(
|
|
80
|
+
{ liked, count },
|
|
81
|
+
(curr) => ({
|
|
82
|
+
liked: !curr.liked,
|
|
83
|
+
count: curr.liked ? curr.count - 1 : curr.count + 1
|
|
84
|
+
})
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const mutation = useMutation({
|
|
88
|
+
mutationFn: () => toggleLike({ data: { postId } }),
|
|
89
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['post', postId] })
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<button onClick={() => {
|
|
94
|
+
startTransition(async () => {
|
|
95
|
+
setOptimistic(null)
|
|
96
|
+
await mutation.mutateAsync()
|
|
97
|
+
})
|
|
98
|
+
}}>
|
|
99
|
+
{optimistic.liked ? '❤️' : '🤍'} {optimistic.count}
|
|
100
|
+
</button>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**사용 시점:** 좋아요/투표, 댓글 추가, 장바구니 아이템 추가/삭제, 토글 스위치 등 사용자가 즉각적인 피드백을 기대하는 모든 액션.
|
|
106
|
+
|
|
107
|
+
**주의:** `startTransition` 내에서 사용해야 자동 롤백이 작동합니다.
|
|
108
|
+
|
|
109
|
+
참고: [React 19 useOptimistic](https://react.dev/reference/react/useOptimistic)
|
package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-suspense-query.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use useSuspenseQuery for Declarative Data Loading
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: eliminates loading state boilerplate
|
|
5
|
+
tags: client, tanstack-query, suspense, data-fetching
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## useSuspenseQuery로 선언적 데이터 로딩
|
|
9
|
+
|
|
10
|
+
TanStack Query v5의 `useSuspenseQuery`는 Suspense를 기본 지원하여 로딩/에러 상태 보일러플레이트를 제거합니다.
|
|
11
|
+
|
|
12
|
+
**❌ 잘못된 예시 (수동 로딩/에러 처리):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { useQuery } from '@tanstack/react-query'
|
|
16
|
+
|
|
17
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
18
|
+
const { data: user, isLoading, error } = useQuery({
|
|
19
|
+
queryKey: ['user', userId],
|
|
20
|
+
queryFn: () => getUser({ data: { id: userId } })
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
if (isLoading) return <UserSkeleton />
|
|
24
|
+
if (error) return <ErrorMessage error={error} />
|
|
25
|
+
if (!user) return null
|
|
26
|
+
|
|
27
|
+
return <div>{user.name}</div>
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**✅ 올바른 예시 (Suspense + ErrorBoundary):**
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { useSuspenseQuery } from '@tanstack/react-query'
|
|
35
|
+
import { Suspense } from 'react'
|
|
36
|
+
import { ErrorBoundary } from 'react-error-boundary'
|
|
37
|
+
|
|
38
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
39
|
+
// data는 항상 존재 (undefined 아님), 타입 안전
|
|
40
|
+
const { data: user } = useSuspenseQuery({
|
|
41
|
+
queryKey: ['user', userId],
|
|
42
|
+
queryFn: () => getUser({ data: { id: userId } })
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return <div>{user.name}</div>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 부모에서 Suspense + ErrorBoundary로 감싸기
|
|
49
|
+
function UserPage({ userId }: { userId: string }) {
|
|
50
|
+
return (
|
|
51
|
+
<ErrorBoundary fallback={<ErrorMessage />}>
|
|
52
|
+
<Suspense fallback={<UserSkeleton />}>
|
|
53
|
+
<UserProfile userId={userId} />
|
|
54
|
+
</Suspense>
|
|
55
|
+
</ErrorBoundary>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**장점:**
|
|
61
|
+
- `data`가 `undefined`가 아닌 확정 타입 (타입 가드 불필요)
|
|
62
|
+
- 로딩/에러 처리가 컴포넌트 트리 상위로 위임 (관심사 분리)
|
|
63
|
+
- 여러 쿼리의 로딩 상태를 하나의 Suspense로 통합 가능
|
|
64
|
+
|
|
65
|
+
**TanStack Query v5 주요 변경 (v4 대비):**
|
|
66
|
+
|
|
67
|
+
| v4 | v5 |
|
|
68
|
+
|----|-----|
|
|
69
|
+
| `cacheTime` | `gcTime` |
|
|
70
|
+
| `keepPreviousData` | `placeholderData` |
|
|
71
|
+
| `isLoading` | `isPending` |
|
|
72
|
+
| `useSuspenseQuery` (실험적) | `useSuspenseQuery` (안정적) |
|
|
73
|
+
|
|
74
|
+
참고: [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use React 19 use() Hook for Promise Handling
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: replaces useEffect fetch patterns
|
|
5
|
+
tags: client, react-19, use-hook, promises, suspense
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## React 19 use() Hook으로 Promise 처리
|
|
9
|
+
|
|
10
|
+
React 19의 `use()` 훅은 렌더링 중 Promise를 읽어 Suspense와 자동 통합됩니다. `useEffect` + `useState` 조합을 대체합니다.
|
|
11
|
+
|
|
12
|
+
**❌ 잘못된 예시 (useEffect + useState 패턴):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function Comments({ postId }: { postId: string }) {
|
|
16
|
+
const [comments, setComments] = useState<Comment[] | null>(null)
|
|
17
|
+
const [loading, setLoading] = useState(true)
|
|
18
|
+
const [error, setError] = useState<Error | null>(null)
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
setLoading(true)
|
|
22
|
+
getComments({ data: { postId } })
|
|
23
|
+
.then(setComments)
|
|
24
|
+
.catch(setError)
|
|
25
|
+
.finally(() => setLoading(false))
|
|
26
|
+
}, [postId])
|
|
27
|
+
|
|
28
|
+
if (loading) return <CommentsSkeleton />
|
|
29
|
+
if (error) return <ErrorMessage error={error} />
|
|
30
|
+
return <CommentList comments={comments!} />
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**✅ 올바른 예시 (use() + Suspense):**
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { use, Suspense } from 'react'
|
|
38
|
+
import { ErrorBoundary } from 'react-error-boundary'
|
|
39
|
+
|
|
40
|
+
function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
|
|
41
|
+
const comments = use(commentsPromise)
|
|
42
|
+
return <CommentList comments={comments} />
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 부모에서 Promise 전달
|
|
46
|
+
function PostPage() {
|
|
47
|
+
const { post, deferredComments } = Route.useLoaderData()
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div>
|
|
51
|
+
<PostContent post={post} />
|
|
52
|
+
<ErrorBoundary fallback={<ErrorMessage />}>
|
|
53
|
+
<Suspense fallback={<CommentsSkeleton />}>
|
|
54
|
+
<Comments commentsPromise={deferredComments} />
|
|
55
|
+
</Suspense>
|
|
56
|
+
</ErrorBoundary>
|
|
57
|
+
</div>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**use()로 Context 읽기:**
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
// 조건부 Context 읽기 (useContext와 달리 조건부 호출 가능)
|
|
66
|
+
function ThemeButton({ override }: { override?: Theme }) {
|
|
67
|
+
if (override) return <Button theme={override} />
|
|
68
|
+
|
|
69
|
+
const theme = use(ThemeContext)
|
|
70
|
+
return <Button theme={theme} />
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**use() 사용 규칙:**
|
|
75
|
+
- ✅ 조건문/루프 내에서 호출 가능 (일반 Hook과 다름)
|
|
76
|
+
- ❌ 컴포넌트 내에서 `new Promise()` 생성 후 전달 금지 (매 렌더링마다 재생성)
|
|
77
|
+
- ✅ loader, server function, 부모 컴포넌트에서 생성된 Promise 전달
|
|
78
|
+
|
|
79
|
+
**TanStack Start와 조합:** loader에서 deferred Promise를 반환하고, 컴포넌트에서 `use()` 또는 `<Await>`로 처리합니다. 두 패턴 모두 유효하며, `<Await>`가 TanStack Router의 공식 패턴입니다.
|
|
80
|
+
|
|
81
|
+
참고: [React use() API](https://react.dev/reference/react/use)
|