@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
|
@@ -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,11 +5,12 @@ 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
|
|
|
12
12
|
@../../instructions/agent-patterns/read-parallelization.md
|
|
13
|
+
@../../instructions/agent-patterns/agent-teams-usage.md
|
|
13
14
|
@../../instructions/validation/forbidden-patterns.md
|
|
14
15
|
@../../instructions/validation/required-behaviors.md
|
|
15
16
|
@../../instructions/multi-agent/coordination-guide.md
|
|
@@ -17,7 +18,7 @@ metadata:
|
|
|
17
18
|
|
|
18
19
|
# TanStack Start React 베스트 프랙티스
|
|
19
20
|
|
|
20
|
-
React와 TanStack Start 애플리케이션 성능 최적화 가이드.
|
|
21
|
+
React 19와 TanStack Start v1 애플리케이션 성능 최적화 가이드. 8개 카테고리, 55개 규칙 포함. 영향도별 우선순위로 자동 리팩토링과 코드 생성 가이드 제공. TanStack Router 라우팅 패턴(Link, useNavigate, useSearch, useParams, beforeLoad, Outlet, pendingComponent, 프리로딩), React Compiler, use() hook, useOptimistic, Middleware, Streaming 등 최신 패턴 반영.
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
@@ -188,11 +189,12 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
188
189
|
|---------|---------|--------|--------|
|
|
189
190
|
| 1 | Waterfall 제거 | **CRITICAL** | `async-` |
|
|
190
191
|
| 2 | 번들 크기 최적화 | **CRITICAL** | `bundle-` |
|
|
191
|
-
| 3 |
|
|
192
|
-
| 4 |
|
|
193
|
-
| 5 |
|
|
194
|
-
| 6 |
|
|
195
|
-
| 7 |
|
|
192
|
+
| 3 | TanStack Router 라우팅 | **HIGH** | `routing-` |
|
|
193
|
+
| 4 | 서버 사이드 성능 | HIGH | `server-` |
|
|
194
|
+
| 5 | 클라이언트 데이터 페칭 | MEDIUM-HIGH | `client-` |
|
|
195
|
+
| 6 | Re-render 최적화 | MEDIUM | `rerender-` |
|
|
196
|
+
| 7 | 렌더링 성능 | MEDIUM | `rendering-` |
|
|
197
|
+
| 8 | JavaScript 성능 | LOW-MEDIUM | `js-` |
|
|
196
198
|
|
|
197
199
|
</categories>
|
|
198
200
|
|
|
@@ -221,35 +223,57 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
221
223
|
| `bundle-conditional` | 기능 활성화 시에만 모듈 로드 |
|
|
222
224
|
| `bundle-preload` | hover/focus 시 preload로 체감 속도 향상 |
|
|
223
225
|
|
|
224
|
-
### 3.
|
|
226
|
+
### 3. TanStack Router 라우팅 (HIGH)
|
|
227
|
+
|
|
228
|
+
| 규칙 | 설명 |
|
|
229
|
+
|------|------|
|
|
230
|
+
| `routing-file-conventions` | 파일 기반 라우팅 컨벤션 (__root, $param, _layout, lazy) |
|
|
231
|
+
| `routing-link-navigation` | Link 컴포넌트와 useNavigate로 타입 안전 네비게이션 |
|
|
232
|
+
| `routing-search-params` | useSearch + Zod 스키마로 search params 검증 |
|
|
233
|
+
| `routing-path-params` | useParams와 getRouteApi로 타입 안전 path params |
|
|
234
|
+
| `routing-beforeload-auth` | beforeLoad로 인증 가드, pathless layout 조합 |
|
|
235
|
+
| `routing-nested-layouts` | Outlet과 pathless layout으로 중첩 UI |
|
|
236
|
+
| `routing-router-context` | createRootRouteWithContext로 의존성 주입 |
|
|
237
|
+
| `routing-preload-strategy` | Link preload="intent"로 즉각적 네비게이션 |
|
|
238
|
+
| `routing-pending-component` | pendingComponent로 로딩 상태 (pendingMs/pendingMinMs) |
|
|
239
|
+
|
|
240
|
+
### 4. 서버 사이드 성능 (HIGH)
|
|
225
241
|
|
|
226
242
|
| 규칙 | 설명 |
|
|
227
243
|
|------|------|
|
|
228
244
|
| `server-cache-lru` | LRU 캐시로 요청 간 캐싱 |
|
|
229
|
-
| `server-serialization` |
|
|
245
|
+
| `server-serialization` | loader 데이터 직렬화 최소화 |
|
|
230
246
|
| `server-parallel-fetching` | loader에서 병렬 데이터 페칭 |
|
|
231
|
-
| `server-deferred-data` |
|
|
247
|
+
| `server-deferred-data` | Promise 반환으로 비차단 데이터 로딩 |
|
|
248
|
+
| `server-middleware` | createMiddleware()로 인증/로깅 중앙화 |
|
|
249
|
+
| `server-validator` | inputValidator()로 타입 안전한 Server Functions |
|
|
250
|
+
| `server-streaming` | async generator/ReadableStream으로 데이터 스트리밍 |
|
|
251
|
+
| `server-error-boundaries` | 라우트 레벨 errorComponent/notFoundComponent |
|
|
232
252
|
|
|
233
|
-
###
|
|
253
|
+
### 5. 클라이언트 데이터 페칭 (MEDIUM-HIGH)
|
|
234
254
|
|
|
235
255
|
| 규칙 | 설명 |
|
|
236
256
|
|------|------|
|
|
237
257
|
| `client-tanstack-query` | TanStack Query로 자동 캐싱/중복 제거 |
|
|
258
|
+
| `client-suspense-query` | useSuspenseQuery로 선언적 데이터 로딩 (v5) |
|
|
259
|
+
| `client-optimistic-updates` | useOptimistic으로 즉각적 UI 피드백 (React 19) |
|
|
260
|
+
| `client-use-hook` | use() hook으로 Promise 처리 (React 19) |
|
|
238
261
|
| `client-event-listeners` | 전역 이벤트 리스너 중복 제거 |
|
|
239
262
|
|
|
240
|
-
###
|
|
263
|
+
### 6. Re-render 최적화 (MEDIUM)
|
|
241
264
|
|
|
242
265
|
| 규칙 | 설명 |
|
|
243
266
|
|------|------|
|
|
244
267
|
| `rerender-defer-reads` | 콜백 전용 상태는 구독 안 함 |
|
|
245
268
|
| `rerender-memo` | 비싼 작업은 memoized 컴포넌트로 추출 |
|
|
269
|
+
| `rerender-react-compiler` | React Compiler로 자동 메모이제이션 (React 19) |
|
|
246
270
|
| `rerender-dependencies` | effect에 원시값 의존성 사용 |
|
|
247
271
|
| `rerender-derived-state` | 파생 boolean 구독, raw 값 구독 회피 |
|
|
248
272
|
| `rerender-functional-setstate` | 안정적 콜백용 함수형 setState |
|
|
249
273
|
| `rerender-lazy-state-init` | 비싼 초기값은 함수로 useState에 전달 |
|
|
250
274
|
| `rerender-transitions` | 비긴급 업데이트는 startTransition |
|
|
251
275
|
|
|
252
|
-
###
|
|
276
|
+
### 7. 렌더링 성능 (MEDIUM)
|
|
253
277
|
|
|
254
278
|
| 규칙 | 설명 |
|
|
255
279
|
|------|------|
|
|
@@ -259,7 +283,7 @@ Task(subagent_type="implementation-executor", model="sonnet",
|
|
|
259
283
|
| `rendering-svg-precision` | SVG 좌표 정밀도 감소 |
|
|
260
284
|
| `rendering-conditional-render` | 조건부 렌더링은 &&가 아닌 삼항 연산자 |
|
|
261
285
|
|
|
262
|
-
###
|
|
286
|
+
### 8. JavaScript 성능 (LOW-MEDIUM)
|
|
263
287
|
|
|
264
288
|
| 규칙 | 설명 |
|
|
265
289
|
|------|------|
|
|
@@ -400,26 +424,46 @@ const addItems = useCallback((newItems: Item[]) => {
|
|
|
400
424
|
import { createServerFn } from '@tanstack/react-start'
|
|
401
425
|
import { z } from 'zod'
|
|
402
426
|
|
|
403
|
-
// ✅ 기본 Server Function
|
|
427
|
+
// ✅ 기본 Server Function (GET)
|
|
404
428
|
const getUser = createServerFn().handler(async () => {
|
|
405
429
|
// 서버에서만 실행
|
|
406
430
|
return await db.user.findMany()
|
|
407
431
|
})
|
|
408
432
|
|
|
409
|
-
// ✅ POST + Validation
|
|
410
|
-
const
|
|
433
|
+
// ✅ POST + Validation (inputValidator는 함수 형태 필수)
|
|
434
|
+
const UserSchema = z.object({
|
|
411
435
|
name: z.string().min(1),
|
|
412
436
|
email: z.string().email()
|
|
413
437
|
})
|
|
414
438
|
|
|
415
439
|
const createUser = createServerFn({ method: 'POST' })
|
|
416
|
-
.inputValidator(
|
|
440
|
+
.inputValidator((d: unknown) => UserSchema.parse(d))
|
|
417
441
|
.handler(async ({ data }) => {
|
|
418
|
-
// data는
|
|
442
|
+
// data는 자동으로 { name: string; email: string } 타입
|
|
419
443
|
return await db.user.create({ data })
|
|
420
444
|
})
|
|
421
445
|
```
|
|
422
446
|
|
|
447
|
+
### Middleware로 인증 중앙화
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
import { createMiddleware } from '@tanstack/react-start'
|
|
451
|
+
|
|
452
|
+
const authMiddleware = createMiddleware()
|
|
453
|
+
.server(async ({ next }) => {
|
|
454
|
+
const session = await getSession()
|
|
455
|
+
if (!session?.user) throw redirect({ to: '/login' })
|
|
456
|
+
return next({ context: { user: session.user } })
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
// 보호된 Server Function
|
|
460
|
+
const getMyTodos = createServerFn()
|
|
461
|
+
.middleware([authMiddleware])
|
|
462
|
+
.handler(async ({ context }) => {
|
|
463
|
+
return await db.todo.findMany({ where: { userId: context.user.id } })
|
|
464
|
+
})
|
|
465
|
+
```
|
|
466
|
+
|
|
423
467
|
### Loader 최적화
|
|
424
468
|
|
|
425
469
|
```typescript
|
|
@@ -546,18 +590,41 @@ rules/server-deferred-data.md
|
|
|
546
590
|
|
|
547
591
|
## 참고 자료
|
|
548
592
|
|
|
549
|
-
### TanStack 공식 문서
|
|
593
|
+
### TanStack Start 공식 문서
|
|
550
594
|
1. [React 공식 문서](https://react.dev)
|
|
551
595
|
2. [TanStack Start Overview](https://tanstack.com/start/latest/docs/framework/react/overview)
|
|
552
596
|
3. [TanStack Start Quick Start](https://tanstack.com/start/latest/docs/framework/react/quick-start)
|
|
553
|
-
4. [TanStack
|
|
554
|
-
5. [
|
|
555
|
-
6. [
|
|
556
|
-
7. [
|
|
597
|
+
4. [TanStack Query](https://tanstack.com/query/latest/docs/framework/react/overview)
|
|
598
|
+
5. [Server Functions Guide](https://tanstack.com/start/latest/docs/framework/react/guide/server-functions)
|
|
599
|
+
6. [Middleware Guide](https://tanstack.com/start/latest/docs/framework/react/guide/middleware)
|
|
600
|
+
7. [Streaming Data Guide](https://tanstack.com/start/latest/docs/framework/react/guide/streaming-data-from-server-functions)
|
|
601
|
+
8. [Authentication Guide](https://tanstack.com/start/latest/docs/framework/react/guide/authentication)
|
|
602
|
+
|
|
603
|
+
### TanStack Router
|
|
604
|
+
9. [TanStack Router](https://tanstack.com/router)
|
|
605
|
+
10. [Navigation Guide](https://tanstack.com/router/latest/docs/framework/react/guide/navigation)
|
|
606
|
+
11. [Link Options](https://tanstack.com/router/latest/docs/framework/react/guide/link-options)
|
|
607
|
+
12. [Search Params](https://tanstack.com/router/latest/docs/framework/react/guide/search-params)
|
|
608
|
+
13. [Path Params](https://tanstack.com/router/latest/docs/framework/react/guide/path-params)
|
|
609
|
+
14. [Authenticated Routes](https://tanstack.com/router/latest/docs/framework/react/guide/authenticated-routes)
|
|
610
|
+
15. [Outlets](https://tanstack.com/router/latest/docs/framework/react/guide/outlets)
|
|
611
|
+
16. [Route Trees](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees)
|
|
612
|
+
17. [File-Based Routing](https://tanstack.com/router/latest/docs/framework/react/routing/file-based-routing)
|
|
613
|
+
18. [Router Context](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
|
|
614
|
+
19. [Preloading](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
615
|
+
20. [Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading)
|
|
616
|
+
21. [Deferred Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/deferred-data-loading)
|
|
617
|
+
|
|
618
|
+
### React 19
|
|
619
|
+
22. [React 19 Blog](https://react.dev/blog/2024/12/05/react-19)
|
|
620
|
+
23. [React use() API](https://react.dev/reference/react/use)
|
|
621
|
+
24. [React useOptimistic](https://react.dev/reference/react/useOptimistic)
|
|
622
|
+
25. [React useActionState](https://react.dev/reference/react/useActionState)
|
|
623
|
+
26. [React Compiler](https://react.dev/learn/react-compiler)
|
|
557
624
|
|
|
558
625
|
### 외부 자료
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
626
|
+
27. [better-all](https://github.com/shuding/better-all)
|
|
627
|
+
28. [node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
628
|
+
29. [Using Server Functions and TanStack Query](https://www.brenelz.com/posts/using-server-functions-and-tanstack-query/)
|
|
562
629
|
|
|
563
630
|
</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)
|