timsquad 2.1.0 → 3.4.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/README.ko.md +288 -0
- package/README.md +170 -763
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +170 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/daemon.d.ts +7 -0
- package/dist/commands/daemon.d.ts.map +1 -0
- package/dist/commands/daemon.js +229 -0
- package/dist/commands/daemon.js.map +1 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +235 -14
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/full.js +2 -2
- package/dist/commands/full.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +118 -22
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/knowledge.d.ts +3 -0
- package/dist/commands/knowledge.d.ts.map +1 -0
- package/dist/commands/knowledge.js +316 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/log.d.ts +27 -0
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +965 -0
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/meta-index.d.ts +3 -0
- package/dist/commands/meta-index.d.ts.map +1 -0
- package/dist/commands/meta-index.js +401 -0
- package/dist/commands/meta-index.js.map +1 -0
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +239 -4
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/retro.js +154 -6
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +231 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/upgrade.d.ts +8 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +292 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/workflow.d.ts +3 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +607 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/daemon/context-writer.d.ts +16 -0
- package/dist/daemon/context-writer.d.ts.map +1 -0
- package/dist/daemon/context-writer.js +35 -0
- package/dist/daemon/context-writer.js.map +1 -0
- package/dist/daemon/entry.d.ts +7 -0
- package/dist/daemon/entry.d.ts.map +1 -0
- package/dist/daemon/entry.js +17 -0
- package/dist/daemon/entry.js.map +1 -0
- package/dist/daemon/event-queue.d.ts +52 -0
- package/dist/daemon/event-queue.d.ts.map +1 -0
- package/dist/daemon/event-queue.js +255 -0
- package/dist/daemon/event-queue.js.map +1 -0
- package/dist/daemon/file-watcher.d.ts +19 -0
- package/dist/daemon/file-watcher.d.ts.map +1 -0
- package/dist/daemon/file-watcher.js +87 -0
- package/dist/daemon/file-watcher.js.map +1 -0
- package/dist/daemon/index.d.ts +29 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +296 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/jsonl-watcher.d.ts +49 -0
- package/dist/daemon/jsonl-watcher.d.ts.map +1 -0
- package/dist/daemon/jsonl-watcher.js +258 -0
- package/dist/daemon/jsonl-watcher.js.map +1 -0
- package/dist/daemon/meta-cache.d.ts +63 -0
- package/dist/daemon/meta-cache.d.ts.map +1 -0
- package/dist/daemon/meta-cache.js +249 -0
- package/dist/daemon/meta-cache.js.map +1 -0
- package/dist/daemon/session-state.d.ts +19 -0
- package/dist/daemon/session-state.d.ts.map +1 -0
- package/dist/daemon/session-state.js +132 -0
- package/dist/daemon/session-state.js.map +1 -0
- package/dist/daemon/shutdown.d.ts +21 -0
- package/dist/daemon/shutdown.d.ts.map +1 -0
- package/dist/daemon/shutdown.js +164 -0
- package/dist/daemon/shutdown.js.map +1 -0
- package/dist/index.js +24 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-composer.d.ts +38 -0
- package/dist/lib/agent-composer.d.ts.map +1 -0
- package/dist/lib/agent-composer.js +128 -0
- package/dist/lib/agent-composer.js.map +1 -0
- package/dist/lib/agent-generator.d.ts +22 -0
- package/dist/lib/agent-generator.d.ts.map +1 -0
- package/dist/lib/agent-generator.js +150 -0
- package/dist/lib/agent-generator.js.map +1 -0
- package/dist/lib/ast-parser.d.ts +11 -0
- package/dist/lib/ast-parser.d.ts.map +1 -0
- package/dist/lib/ast-parser.js +282 -0
- package/dist/lib/ast-parser.js.map +1 -0
- package/dist/lib/compile-rules.d.ts +66 -0
- package/dist/lib/compile-rules.d.ts.map +1 -0
- package/dist/lib/compile-rules.js +114 -0
- package/dist/lib/compile-rules.js.map +1 -0
- package/dist/lib/compiler.d.ts +105 -0
- package/dist/lib/compiler.d.ts.map +1 -0
- package/dist/lib/compiler.js +368 -0
- package/dist/lib/compiler.js.map +1 -0
- package/dist/lib/config.d.ts +7 -2
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +34 -3
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/meta-index.d.ts +19 -0
- package/dist/lib/meta-index.d.ts.map +1 -0
- package/dist/lib/meta-index.js +573 -0
- package/dist/lib/meta-index.js.map +1 -0
- package/dist/lib/project.js +1 -1
- package/dist/lib/project.js.map +1 -1
- package/dist/lib/skill-generator.d.ts +32 -0
- package/dist/lib/skill-generator.d.ts.map +1 -0
- package/dist/lib/skill-generator.js +187 -0
- package/dist/lib/skill-generator.js.map +1 -0
- package/dist/lib/template.d.ts +16 -2
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +115 -20
- package/dist/lib/template.js.map +1 -1
- package/dist/lib/ui-index.d.ts +12 -0
- package/dist/lib/ui-index.d.ts.map +1 -0
- package/dist/lib/ui-index.js +239 -0
- package/dist/lib/ui-index.js.map +1 -0
- package/dist/lib/ui-parser.d.ts +12 -0
- package/dist/lib/ui-parser.d.ts.map +1 -0
- package/dist/lib/ui-parser.js +472 -0
- package/dist/lib/ui-parser.js.map +1 -0
- package/dist/lib/update-check.d.ts +6 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +121 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/upgrade-backup.d.ts +33 -0
- package/dist/lib/upgrade-backup.d.ts.map +1 -0
- package/dist/lib/upgrade-backup.js +101 -0
- package/dist/lib/upgrade-backup.js.map +1 -0
- package/dist/lib/version.d.ts +19 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +35 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/lib/workflow-state.d.ts +48 -0
- package/dist/lib/workflow-state.d.ts.map +1 -0
- package/dist/lib/workflow-state.js +67 -0
- package/dist/lib/workflow-state.js.map +1 -0
- package/dist/types/config.d.ts +103 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +184 -9
- package/dist/types/config.js.map +1 -1
- package/dist/types/feedback.d.ts +7 -0
- package/dist/types/feedback.d.ts.map +1 -1
- package/dist/types/feedback.js +1 -1
- package/dist/types/feedback.js.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/meta-index.d.ts +146 -0
- package/dist/types/meta-index.d.ts.map +1 -0
- package/dist/types/meta-index.js +7 -0
- package/dist/types/meta-index.js.map +1 -0
- package/dist/types/project.d.ts +19 -3
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +23 -0
- package/dist/types/project.js.map +1 -1
- package/dist/types/task-log.d.ts +208 -0
- package/dist/types/task-log.d.ts.map +1 -0
- package/dist/types/task-log.js +6 -0
- package/dist/types/task-log.js.map +1 -0
- package/dist/types/ui-meta.d.ts +118 -0
- package/dist/types/ui-meta.d.ts.map +1 -0
- package/dist/types/ui-meta.js +7 -0
- package/dist/types/ui-meta.js.map +1 -0
- package/package.json +12 -4
- package/templates/base/agents/base/tsq-architect.md +68 -0
- package/templates/base/agents/base/tsq-dba.md +56 -0
- package/templates/base/agents/base/tsq-designer.md +72 -0
- package/templates/base/agents/base/tsq-developer.md +67 -0
- package/templates/base/agents/base/tsq-qa.md +55 -0
- package/templates/base/agents/base/tsq-security.md +65 -0
- package/templates/base/agents/overlays/domain/general-web/_common.md +11 -0
- package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
- package/templates/base/agents/overlays/platform/claude-code.md +12 -0
- package/templates/base/config.template.yaml +213 -0
- package/templates/base/knowledge/checklists/accessibility.md +37 -0
- package/templates/base/knowledge/checklists/architecture-review.md +28 -0
- package/templates/base/knowledge/checklists/database-standards.md +84 -0
- package/templates/base/knowledge/checklists/design-reference.md +97 -0
- package/templates/base/knowledge/checklists/security.md +50 -0
- package/templates/base/knowledge/checklists/ssot-validation.md +19 -0
- package/templates/base/knowledge/domains/_template.md +16 -0
- package/templates/base/knowledge/platforms/_template.md +16 -0
- package/templates/base/knowledge/templates/sequence-report.md +44 -0
- package/templates/base/knowledge/templates/task-result.md +105 -0
- package/templates/base/skills/_template/SKILL.md +59 -0
- package/templates/base/skills/_template/references/_template.md +35 -0
- package/templates/base/skills/_template/rules/_sections.md +34 -0
- package/templates/base/skills/_template/rules/_template.md +32 -0
- package/templates/base/skills/_template/scripts/_template.sh +31 -0
- package/templates/base/skills/architecture/SKILL.md +54 -0
- package/templates/base/skills/architecture/references/adr-template.md +50 -0
- package/templates/base/skills/architecture/references/api-design.md +64 -0
- package/templates/base/skills/backend/node/SKILL.md +81 -0
- package/templates/base/skills/backend/node/rules/async-patterns.md +81 -0
- package/templates/base/skills/backend/node/rules/deployment.md +33 -0
- package/templates/base/skills/backend/node/rules/env-config.md +41 -0
- package/templates/base/skills/backend/node/rules/error-handling.md +83 -0
- package/templates/base/skills/backend/node/rules/hono-app-setup.md +98 -0
- package/templates/base/skills/backend/node/rules/jwt-auth.md +76 -0
- package/templates/base/skills/backend/node/rules/middleware.md +56 -0
- package/templates/base/skills/backend/node/rules/testing.md +82 -0
- package/templates/base/skills/coding/SKILL.md +47 -0
- package/templates/base/skills/coding/rules/patterns.md +81 -0
- package/templates/base/skills/controller/SKILL.md +111 -0
- package/templates/base/skills/controller/references/README.md +35 -0
- package/templates/base/skills/controller/rules/README.md +18 -0
- package/templates/base/skills/database/SKILL.md +98 -0
- package/templates/base/skills/database/prisma/SKILL.md +57 -0
- package/templates/base/skills/database/prisma/rules/queries.md +133 -0
- package/templates/base/skills/database/prisma/rules/schema-design.md +80 -0
- package/templates/base/skills/frontend/nextjs/SKILL.md +59 -0
- package/templates/base/skills/frontend/nextjs/rules/app-router.md +138 -0
- package/templates/base/skills/frontend/react/SKILL.md +86 -0
- package/templates/base/skills/frontend/react/rules/_sections.md +88 -0
- package/templates/base/skills/frontend/react/rules/anti-patterns.md +67 -0
- package/templates/base/skills/frontend/react/rules/async-api-routes.md +38 -0
- package/templates/base/skills/frontend/react/rules/async-defer-await.md +80 -0
- package/templates/base/skills/frontend/react/rules/async-dependencies.md +36 -0
- package/templates/base/skills/frontend/react/rules/async-parallel.md +28 -0
- package/templates/base/skills/frontend/react/rules/async-suspense-boundaries.md +99 -0
- package/templates/base/skills/frontend/react/rules/bundle-barrel-imports.md +59 -0
- package/templates/base/skills/frontend/react/rules/bundle-defer-third-party.md +49 -0
- package/templates/base/skills/frontend/react/rules/bundle-dynamic-imports.md +35 -0
- package/templates/base/skills/frontend/react/rules/component-conventions.md +74 -0
- package/templates/base/skills/frontend/react/rules/js-combine-iterations.md +32 -0
- package/templates/base/skills/frontend/react/rules/js-early-exit.md +50 -0
- package/templates/base/skills/frontend/react/rules/js-index-maps.md +37 -0
- package/templates/base/skills/frontend/react/rules/js-set-map-lookups.md +24 -0
- package/templates/base/skills/frontend/react/rules/rendering-conditional-render.md +40 -0
- package/templates/base/skills/frontend/react/rules/rendering-content-visibility.md +38 -0
- package/templates/base/skills/frontend/react/rules/rendering-hoist-jsx.md +46 -0
- package/templates/base/skills/frontend/react/rules/rerender-defer-reads.md +39 -0
- package/templates/base/skills/frontend/react/rules/rerender-derived-state.md +29 -0
- package/templates/base/skills/frontend/react/rules/rerender-memo.md +44 -0
- package/templates/base/skills/frontend/react/rules/rerender-transitions.md +40 -0
- package/templates/base/skills/frontend/react/rules/server-after-nonblocking.md +73 -0
- package/templates/base/skills/frontend/react/rules/server-cache-react.md +26 -0
- package/templates/base/skills/frontend/react/rules/server-parallel-fetching.md +79 -0
- package/templates/base/skills/frontend/react/rules/state-location.md +55 -0
- package/templates/base/skills/methodology/bdd/SKILL.md +69 -0
- package/templates/base/skills/methodology/bdd/rules/gherkin-patterns.md +113 -0
- package/templates/base/skills/methodology/ddd/SKILL.md +74 -0
- package/templates/base/skills/methodology/ddd/rules/strategic-patterns.md +98 -0
- package/templates/base/skills/methodology/debugging/SKILL.md +60 -0
- package/templates/base/skills/methodology/debugging/references/root-cause-tracing.md +84 -0
- package/templates/base/skills/methodology/tdd/SKILL.md +66 -0
- package/templates/base/skills/methodology/tdd/rules/real-world-example.md +88 -0
- package/templates/base/skills/methodology/tdd/rules/techniques.md +185 -0
- package/templates/base/skills/mobile/dart/SKILL.md +69 -0
- package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
- package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
- package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
- package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
- package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
- package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
- package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
- package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
- package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
- package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
- package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
- package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
- package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
- package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
- package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
- package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
- package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
- package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
- package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
- package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
- package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
- package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
- package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
- package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
- package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
- package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
- package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
- package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
- package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
- package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
- package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
- package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
- package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
- package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
- package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
- package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
- package/templates/base/skills/planning/SKILL.md +58 -0
- package/templates/base/skills/planning/references/prd-guide.md +47 -0
- package/templates/base/skills/planning/references/requirements-guide.md +46 -0
- package/templates/base/skills/prompt-engineering/SKILL.md +103 -0
- package/templates/base/skills/retrospective/SKILL.md +102 -0
- package/templates/base/skills/security/SKILL.md +55 -0
- package/templates/base/skills/security/rules/owasp-examples.md +119 -0
- package/templates/base/skills/security/scripts/check-secrets.sh +55 -0
- package/templates/base/skills/testing/SKILL.md +63 -0
- package/templates/base/skills/testing/references/testing-patterns.md +103 -0
- package/templates/base/skills/tsq-protocol/SKILL.md +51 -0
- package/templates/base/skills/typescript/SKILL.md +67 -0
- package/templates/base/skills/typescript/rules/type-patterns.md +135 -0
- package/templates/base/skills/typescript/rules/utility-types.md +76 -0
- package/templates/base/skills/ui-design/SKILL.md +70 -0
- package/templates/{common → base}/timsquad/feedback/routing-rules.yaml +1 -1
- package/templates/{common → base}/timsquad/retrospective/metrics/metrics-schema.json +46 -1
- package/templates/platforms/claude-code/CLAUDE.md.template +89 -0
- package/templates/platforms/claude-code/rules/adr-rules.md +32 -0
- package/templates/platforms/claude-code/rules/feedback-routing.md +18 -0
- package/templates/platforms/claude-code/rules/phase-management.md +23 -0
- package/templates/platforms/claude-code/rules/reporting-format.md +26 -0
- package/templates/platforms/claude-code/rules/sequence-management.md +72 -0
- package/templates/platforms/claude-code/rules/workspace-sync.md +33 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +57 -0
- package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
- package/templates/platforms/claude-code/settings.json +98 -0
- package/templates/project-types/api-backend/config.yaml +227 -0
- package/templates/project-types/api-backend/process/workflow.xml +214 -0
- package/templates/project-types/fintech/config.yaml +151 -0
- package/templates/project-types/fintech/process/workflow.xml +316 -0
- package/templates/project-types/infra/config.yaml +327 -0
- package/templates/project-types/infra/process/workflow.xml +296 -0
- package/templates/project-types/mobile-app/config.yaml +123 -0
- package/templates/project-types/mobile-app/process/workflow.xml +191 -0
- package/templates/project-types/platform/config.yaml +254 -0
- package/templates/project-types/platform/process/workflow.xml +254 -0
- package/templates/project-types/web-app/config.yaml +198 -0
- package/templates/project-types/web-app/process/workflow.xml +210 -0
- package/templates/project-types/web-service/config.yaml +136 -0
- package/templates/project-types/web-service/process/workflow.xml +184 -0
- package/templates/common/CLAUDE.md.template +0 -254
- package/templates/common/claude/agents/tsq-dba.md +0 -311
- package/templates/common/claude/agents/tsq-designer.md +0 -323
- package/templates/common/claude/agents/tsq-developer.md +0 -177
- package/templates/common/claude/agents/tsq-planner.md +0 -190
- package/templates/common/claude/agents/tsq-prompter.md +0 -356
- package/templates/common/claude/agents/tsq-qa.md +0 -168
- package/templates/common/claude/agents/tsq-retro.md +0 -193
- package/templates/common/claude/agents/tsq-security.md +0 -221
- package/templates/common/claude/hooks/auto-metrics.sh +0 -165
- package/templates/common/claude/hooks/auto-worklog.sh +0 -245
- package/templates/common/claude/hooks/event-logger.sh +0 -208
- package/templates/common/claude/settings.json +0 -86
- package/templates/common/claude/skills/architecture/SKILL.md +0 -123
- package/templates/common/claude/skills/backend/node/SKILL.md +0 -1015
- package/templates/common/claude/skills/coding/SKILL.md +0 -171
- package/templates/common/claude/skills/database/prisma/SKILL.md +0 -357
- package/templates/common/claude/skills/frontend/nextjs/SKILL.md +0 -279
- package/templates/common/claude/skills/frontend/react/SKILL.md +0 -1729
- package/templates/common/claude/skills/methodology/bdd/SKILL.md +0 -234
- package/templates/common/claude/skills/methodology/ddd/SKILL.md +0 -311
- package/templates/common/claude/skills/methodology/tdd/SKILL.md +0 -512
- package/templates/common/claude/skills/planning/SKILL.md +0 -90
- package/templates/common/claude/skills/security/SKILL.md +0 -234
- package/templates/common/claude/skills/testing/SKILL.md +0 -146
- package/templates/common/claude/skills/typescript/SKILL.md +0 -435
- package/templates/common/config.template.yaml +0 -132
- /package/templates/{common → base}/timsquad/architectures/clean/ARCHITECTURE.md +0 -0
- /package/templates/{common → base}/timsquad/architectures/clean/backend.xml +0 -0
- /package/templates/{common → base}/timsquad/architectures/clean/frontend.xml +0 -0
- /package/templates/{common → base}/timsquad/architectures/fsd/ARCHITECTURE.md +0 -0
- /package/templates/{common → base}/timsquad/architectures/fsd/frontend.xml +0 -0
- /package/templates/{common → base}/timsquad/architectures/hexagonal/ARCHITECTURE.md +0 -0
- /package/templates/{common → base}/timsquad/architectures/hexagonal/backend.xml +0 -0
- /package/templates/{common → base}/timsquad/constraints/competency-framework.xml +0 -0
- /package/templates/{common → base}/timsquad/constraints/ssot-schema.xml +0 -0
- /package/templates/{common → base}/timsquad/feedback/feedback-router.sh +0 -0
- /package/templates/{common → base}/timsquad/generators/data-design.xml +0 -0
- /package/templates/{common → base}/timsquad/generators/prd.xml +0 -0
- /package/templates/{common → base}/timsquad/generators/requirements.xml +0 -0
- /package/templates/{common → base}/timsquad/generators/service-spec.xml +0 -0
- /package/templates/{common → base}/timsquad/logs/_example.md +0 -0
- /package/templates/{common → base}/timsquad/logs/_template.md +0 -0
- /package/templates/{common → base}/timsquad/patterns/cqrs.xml +0 -0
- /package/templates/{common → base}/timsquad/patterns/event-sourcing.xml +0 -0
- /package/templates/{common → base}/timsquad/patterns/repository.xml +0 -0
- /package/templates/{common → base}/timsquad/process/phase-checklist.yaml +0 -0
- /package/templates/{common → base}/timsquad/process/state-machine.xml +0 -0
- /package/templates/{common → base}/timsquad/process/validation-rules.xml +0 -0
- /package/templates/{common → base}/timsquad/process/workflow-base.xml +0 -0
- /package/templates/{common → base}/timsquad/retrospective/cycle-report.template.md +0 -0
- /package/templates/{common → base}/timsquad/retrospective/patterns/failure-patterns.md +0 -0
- /package/templates/{common → base}/timsquad/retrospective/patterns/success-patterns.md +0 -0
- /package/templates/{common → base}/timsquad/retrospective/retrospective-config.xml +0 -0
- /package/templates/{common → base}/timsquad/retrospective/retrospective-state.xml +0 -0
- /package/templates/{common → base}/timsquad/ssot/adr/ADR-000-template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/adr/ADR-001-example.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/data-design.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/deployment-spec.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/env-config.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/error-codes.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/functional-spec.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/glossary.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/integration-spec.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/planning.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/prd.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/requirements.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/security-spec.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/service-spec.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/test-spec.template.md +0 -0
- /package/templates/{common → base}/timsquad/ssot/ui-ux-spec.template.md +0 -0
- /package/templates/{common → base}/timsquad/state/workspace.xml +0 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Sentry Integration & Breadcrumbs
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: "에러 컨텍스트 부족 → 재현 불가, 크래시 원인 추적 지연"
|
|
5
|
+
tags: sentry, breadcrumb, scope, dsn, error-tracking
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Sentry Integration & Breadcrumbs
|
|
9
|
+
|
|
10
|
+
**Impact: MEDIUM (에러 컨텍스트 부족 → 재현 불가, 크래시 원인 추적 지연)**
|
|
11
|
+
|
|
12
|
+
sentry_flutter 초기화, DSN 환경별 분리, breadcrumbs 자동/수동 기록,
|
|
13
|
+
scope 설정, Crashlytics와의 공존 전략.
|
|
14
|
+
|
|
15
|
+
### 의존성
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
# pubspec.yaml
|
|
19
|
+
dependencies:
|
|
20
|
+
sentry_flutter: ^8.12.0
|
|
21
|
+
sentry_dio: ^8.12.0 # dio HTTP breadcrumbs
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 초기화
|
|
25
|
+
|
|
26
|
+
**Incorrect (하드코딩된 DSN, 기본 설정만):**
|
|
27
|
+
```dart
|
|
28
|
+
await SentryFlutter.init((options) {
|
|
29
|
+
options.dsn = 'https://key@sentry.io/123'; // 하드코딩 → 환경 구분 불가
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Correct (환경별 DSN + 상세 설정):**
|
|
34
|
+
```dart
|
|
35
|
+
Future<void> main() async {
|
|
36
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
37
|
+
await Firebase.initializeApp();
|
|
38
|
+
|
|
39
|
+
await SentryFlutter.init(
|
|
40
|
+
(options) {
|
|
41
|
+
// DSN 환경별 분리 (--dart-define으로 주입)
|
|
42
|
+
options.dsn = const String.fromEnvironment(
|
|
43
|
+
'SENTRY_DSN',
|
|
44
|
+
defaultValue: '', // 빈 문자열 → Sentry 비활성화
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// 환경 태그
|
|
48
|
+
options.environment = const String.fromEnvironment(
|
|
49
|
+
'ENV',
|
|
50
|
+
defaultValue: 'development',
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// 샘플링 (프로덕션에서 100%는 비용 과다)
|
|
54
|
+
options.tracesSampleRate = kDebugMode ? 1.0 : 0.3;
|
|
55
|
+
options.profilesSampleRate = kDebugMode ? 1.0 : 0.1;
|
|
56
|
+
|
|
57
|
+
// 릴리스 버전 (소스맵 매핑)
|
|
58
|
+
options.release = '${packageInfo.version}+${packageInfo.buildNumber}';
|
|
59
|
+
|
|
60
|
+
// 디버그 모드
|
|
61
|
+
options.debug = kDebugMode;
|
|
62
|
+
|
|
63
|
+
// 자동 breadcrumb 수집
|
|
64
|
+
options.enableAutoNativeBreadcrumbs = true;
|
|
65
|
+
options.enableAutoPerformanceTracing = true;
|
|
66
|
+
|
|
67
|
+
// PII 전송 비활성화
|
|
68
|
+
options.sendDefaultPii = false;
|
|
69
|
+
|
|
70
|
+
// 에러 필터링 (무시할 에러 타입)
|
|
71
|
+
options.beforeSend = (event, hint) {
|
|
72
|
+
// 네트워크 끊김 에러는 무시
|
|
73
|
+
if (event.throwable is SocketException) return null;
|
|
74
|
+
return event;
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
appRunner: () => runApp(
|
|
78
|
+
const ProviderScope(child: MyApp()),
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Breadcrumbs
|
|
85
|
+
|
|
86
|
+
```dart
|
|
87
|
+
class SentryBreadcrumbService {
|
|
88
|
+
/// 네비게이션 breadcrumb (수동 — go_router 연동)
|
|
89
|
+
static void navigationBreadcrumb({
|
|
90
|
+
required String from,
|
|
91
|
+
required String to,
|
|
92
|
+
}) {
|
|
93
|
+
Sentry.addBreadcrumb(Breadcrumb(
|
|
94
|
+
type: 'navigation',
|
|
95
|
+
category: 'navigation',
|
|
96
|
+
data: {'from': from, 'to': to},
|
|
97
|
+
));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// 사용자 액션 breadcrumb
|
|
101
|
+
static void userActionBreadcrumb({
|
|
102
|
+
required String action,
|
|
103
|
+
required String target,
|
|
104
|
+
Map<String, dynamic>? data,
|
|
105
|
+
}) {
|
|
106
|
+
Sentry.addBreadcrumb(Breadcrumb(
|
|
107
|
+
type: 'user',
|
|
108
|
+
category: 'user.action',
|
|
109
|
+
message: '$action on $target',
|
|
110
|
+
data: data,
|
|
111
|
+
));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// 상태 변경 breadcrumb
|
|
115
|
+
static void stateBreadcrumb({
|
|
116
|
+
required String category,
|
|
117
|
+
required String message,
|
|
118
|
+
Map<String, dynamic>? data,
|
|
119
|
+
}) {
|
|
120
|
+
Sentry.addBreadcrumb(Breadcrumb(
|
|
121
|
+
type: 'info',
|
|
122
|
+
category: category,
|
|
123
|
+
message: message,
|
|
124
|
+
data: data,
|
|
125
|
+
level: SentryLevel.info,
|
|
126
|
+
));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// go_router NavigatorObserver로 자동 네비게이션 breadcrumb
|
|
131
|
+
class SentryNavigatorObserver extends NavigatorObserver {
|
|
132
|
+
@override
|
|
133
|
+
void didPush(Route route, Route? previousRoute) {
|
|
134
|
+
SentryBreadcrumbService.navigationBreadcrumb(
|
|
135
|
+
from: previousRoute?.settings.name ?? 'unknown',
|
|
136
|
+
to: route.settings.name ?? 'unknown',
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@override
|
|
141
|
+
void didPop(Route route, Route? previousRoute) {
|
|
142
|
+
SentryBreadcrumbService.navigationBreadcrumb(
|
|
143
|
+
from: route.settings.name ?? 'unknown',
|
|
144
|
+
to: previousRoute?.settings.name ?? 'unknown',
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Scope 설정
|
|
151
|
+
|
|
152
|
+
```dart
|
|
153
|
+
/// 사용자 정보 + 태그 설정
|
|
154
|
+
class SentryScopeManager {
|
|
155
|
+
/// 로그인 시 사용자 설정
|
|
156
|
+
static Future<void> setUser({
|
|
157
|
+
required String id,
|
|
158
|
+
String? email,
|
|
159
|
+
String? username,
|
|
160
|
+
Map<String, String>? extras,
|
|
161
|
+
}) async {
|
|
162
|
+
Sentry.configureScope((scope) {
|
|
163
|
+
scope.setUser(SentryUser(
|
|
164
|
+
id: id,
|
|
165
|
+
email: email, // PII — sendDefaultPii: true 필요
|
|
166
|
+
username: username,
|
|
167
|
+
));
|
|
168
|
+
// 태그 (필터링/검색용)
|
|
169
|
+
scope.setTag('subscription_tier', extras?['tier'] ?? 'free');
|
|
170
|
+
scope.setTag('region', extras?['region'] ?? 'unknown');
|
|
171
|
+
// 추가 데이터 (상세 컨텍스트)
|
|
172
|
+
extras?.forEach((key, value) {
|
|
173
|
+
scope.setExtra(key, value);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/// 로그아웃 시 정리
|
|
179
|
+
static Future<void> clearUser() async {
|
|
180
|
+
Sentry.configureScope((scope) {
|
|
181
|
+
scope.setUser(null);
|
|
182
|
+
scope.removeTag('subscription_tier');
|
|
183
|
+
scope.removeTag('region');
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Dio HTTP Breadcrumbs
|
|
190
|
+
|
|
191
|
+
```dart
|
|
192
|
+
/// sentry_dio로 HTTP 요청/응답 자동 breadcrumb
|
|
193
|
+
final dio = Dio()
|
|
194
|
+
..addSentry(); // sentry_dio 확장 메서드
|
|
195
|
+
|
|
196
|
+
// 또는 수동 interceptor
|
|
197
|
+
class SentryDioInterceptor extends Interceptor {
|
|
198
|
+
@override
|
|
199
|
+
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
|
200
|
+
Sentry.addBreadcrumb(Breadcrumb(
|
|
201
|
+
type: 'http',
|
|
202
|
+
category: 'http',
|
|
203
|
+
data: {
|
|
204
|
+
'url': response.requestOptions.uri.toString(),
|
|
205
|
+
'method': response.requestOptions.method,
|
|
206
|
+
'status_code': response.statusCode,
|
|
207
|
+
'duration_ms': response.requestOptions.extra['start_time'] != null
|
|
208
|
+
? DateTime.now().difference(
|
|
209
|
+
response.requestOptions.extra['start_time'] as DateTime,
|
|
210
|
+
).inMilliseconds
|
|
211
|
+
: null,
|
|
212
|
+
},
|
|
213
|
+
));
|
|
214
|
+
handler.next(response);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Crashlytics + Sentry 공존 전략
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
역할 분리:
|
|
223
|
+
┌──────────────┬────────────────────────┬──────────────────────┐
|
|
224
|
+
│ │ Firebase Crashlytics │ Sentry │
|
|
225
|
+
├──────────────┼────────────────────────┼──────────────────────┤
|
|
226
|
+
│ 강점 │ 크래시 집계, 안정성 % │ 에러 컨텍스트, 검색 │
|
|
227
|
+
│ 크래시 │ O (primary) │ O (backup) │
|
|
228
|
+
│ 비치명 에러 │ O (recordError) │ O (captureException) │
|
|
229
|
+
│ Breadcrumbs │ X (log만 가능) │ O (풍부한 컨텍스트) │
|
|
230
|
+
│ 알림 │ Firebase Console │ Slack/Email 통합 │
|
|
231
|
+
│ 비용 │ 무료 │ 무료 (5K 이벤트/월) │
|
|
232
|
+
└──────────────┴────────────────────────┴──────────────────────┘
|
|
233
|
+
|
|
234
|
+
공존 시 주의:
|
|
235
|
+
- 에러를 양쪽 모두에 전송하면 이벤트 쿼터 소모 → 역할 기반 필터링
|
|
236
|
+
- Crashlytics = 크래시 대시보드 (안정성 %)
|
|
237
|
+
- Sentry = 에러 디버깅 (breadcrumb, context, 검색)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 규칙
|
|
241
|
+
|
|
242
|
+
- DSN → `--dart-define`으로 환경별 주입, 하드코딩 금지
|
|
243
|
+
- `tracesSampleRate` → 프로덕션 0.1~0.3 (비용 절감)
|
|
244
|
+
- `beforeSend` → 불필요한 에러 (SocketException 등) 필터링
|
|
245
|
+
- Breadcrumb → 네비게이션, HTTP, 사용자 액션 자동 기록
|
|
246
|
+
- Scope → 로그인/로그아웃 시 사용자 정보 설정/해제
|
|
247
|
+
- PII → `sendDefaultPii: false` 기본, 필요 시 명시적 활성화
|
|
248
|
+
- Crashlytics 공존 → 각 도구의 강점에 맞는 역할 분리
|
|
249
|
+
- 릴리스 → `options.release` 설정 + 소스맵/디버그 심볼 업로드
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: networking
|
|
3
|
+
description: |
|
|
4
|
+
Flutter 네트워크 통신 가이드라인.
|
|
5
|
+
Dio HTTP 클라이언트, Retrofit 코드 생성, 인터셉터 체인,
|
|
6
|
+
에러 핸들링, 연결 상태 관리, 캐시 전략.
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
tags: [flutter, dio, retrofit, http, api, interceptor, connectivity]
|
|
9
|
+
user-invocable: false
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Networking
|
|
13
|
+
|
|
14
|
+
Flutter Dio + Retrofit + Interceptor + Connectivity 통합 가이드.
|
|
15
|
+
HTTP 클라이언트 설정부터 오프라인 캐시까지, API 통신 전체 파이프라인.
|
|
16
|
+
|
|
17
|
+
## Philosophy
|
|
18
|
+
|
|
19
|
+
- API 클라이언트는 서비스 — Feature-first 구조에서 `core/networking/` 중앙화
|
|
20
|
+
- 에러는 도메인 타입 — DioException을 앱 도메인 에러(NetworkFailure)로 변환
|
|
21
|
+
- 오프라인은 기본 — connectivity 감지 + 캐시 + 재시도를 기본 탑재
|
|
22
|
+
- 인터셉터는 레이어 — 인증, 재시도, 로깅, 에러 변환을 체인으로 분리
|
|
23
|
+
|
|
24
|
+
## Resources
|
|
25
|
+
|
|
26
|
+
6개 규칙 + 1개 참조. 네트워크 통신 전체를 커버.
|
|
27
|
+
|
|
28
|
+
| Priority | Type | Resource | Description |
|
|
29
|
+
|----------|------|----------|-------------|
|
|
30
|
+
| CRITICAL | rule | [dio-setup](rules/dio-setup.md) | Dio 싱글톤, BaseOptions, Riverpod Provider |
|
|
31
|
+
| CRITICAL | rule | [interceptors](rules/interceptors.md) | Interceptor chain 순서: Auth → Retry → Logging → Error |
|
|
32
|
+
| CRITICAL | rule | [error-handling](rules/error-handling.md) | DioException → NetworkFailure sealed class, Result 패턴 |
|
|
33
|
+
| HIGH | rule | [retrofit-patterns](rules/retrofit-patterns.md) | @RestApi, 코드 생성, freezed 모델 연동 |
|
|
34
|
+
| HIGH | rule | [connectivity](rules/connectivity.md) | connectivity_plus, 오프라인 큐, 자동 재시도 |
|
|
35
|
+
| MEDIUM | rule | [caching](rules/caching.md) | dio_cache_interceptor, ETag, 오프라인 fallback |
|
|
36
|
+
| — | ref | [api-client-architecture](references/api-client-architecture.md) | 디렉토리 구조, Provider 구성, Repository 연동, Mock 테스트 |
|
|
37
|
+
|
|
38
|
+
## Quick Rules
|
|
39
|
+
|
|
40
|
+
### Dio 설정
|
|
41
|
+
- `Dio(BaseOptions(...))` 싱글톤 — Riverpod Provider로 전역 관리
|
|
42
|
+
- `connectTimeout: 15s`, `receiveTimeout: 15s` 기본값
|
|
43
|
+
- `baseUrl` 은 환경별 분리 (dev/staging/prod)
|
|
44
|
+
- `contentType: 'application/json'` 기본 헤더
|
|
45
|
+
|
|
46
|
+
### 인터셉터 체인
|
|
47
|
+
- 순서: Auth → Retry → Logging → Error Transform
|
|
48
|
+
- Auth: 토큰 주입 + 401 시 리프레시
|
|
49
|
+
- Retry: 지수 백오프, 최대 3회, 5xx/timeout만
|
|
50
|
+
- Logging: debug 빌드에서만 활성화
|
|
51
|
+
|
|
52
|
+
### 에러 핸들링
|
|
53
|
+
- `DioException` → `NetworkFailure` sealed class 변환
|
|
54
|
+
- `connectionTimeout/sendTimeout` → `TimeoutFailure`
|
|
55
|
+
- `badResponse` → `ServerFailure(statusCode, message)`
|
|
56
|
+
- `connectionError` → `NoConnectionFailure`
|
|
57
|
+
- Repository에서 `Result<T, Failure>` 패턴 반환
|
|
58
|
+
|
|
59
|
+
### Retrofit 코드 생성
|
|
60
|
+
- `@RestApi(baseUrl: '')` — baseUrl은 Dio에서 관리
|
|
61
|
+
- 응답 모델은 freezed + json_serializable
|
|
62
|
+
- `build_runner watch` 로 개발 중 자동 생성
|
|
63
|
+
|
|
64
|
+
### 연결 상태
|
|
65
|
+
- `connectivity_plus` → 네트워크 상태 실시간 감지
|
|
66
|
+
- 오프라인 시 요청 큐잉, 연결 복구 시 자동 재시도
|
|
67
|
+
- UI에 오프라인 배너 표시
|
|
68
|
+
|
|
69
|
+
### 캐시
|
|
70
|
+
- `dio_cache_interceptor` → GET 응답 캐시
|
|
71
|
+
- `ETag`/`Last-Modified` 헤더 활용
|
|
72
|
+
- 오프라인 시 캐시 fallback
|
|
73
|
+
|
|
74
|
+
## Checklist
|
|
75
|
+
|
|
76
|
+
| Priority | Item |
|
|
77
|
+
|----------|------|
|
|
78
|
+
| CRITICAL | Dio 싱글톤 인스턴스 Riverpod Provider로 관리 |
|
|
79
|
+
| CRITICAL | Interceptor 순서: Auth → Retry → Logging → Error Transform |
|
|
80
|
+
| CRITICAL | DioException → 도메인 NetworkFailure 변환 처리 |
|
|
81
|
+
| HIGH | Auth 인터셉터에서 401 → 토큰 리프레시 → 재요청 |
|
|
82
|
+
| HIGH | Retry 인터셉터: 지수 백오프, 5xx/timeout만, 최대 3회 |
|
|
83
|
+
| HIGH | Retrofit @RestApi 응답 타입을 freezed 모델로 정의 |
|
|
84
|
+
| HIGH | connectivity_plus로 오프라인 감지 + UI 배너 |
|
|
85
|
+
| MEDIUM | 오프라인 요청 큐 + 연결 복구 시 자동 재시도 |
|
|
86
|
+
| MEDIUM | GET 응답 캐시 (dio_cache_interceptor) |
|
|
87
|
+
| MEDIUM | 환경별 baseUrl 분리 (dev/staging/prod) |
|
|
88
|
+
| MEDIUM | Release 빌드에서 Logging 인터셉터 비활성화 |
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: API Client Architecture
|
|
3
|
+
category: reference
|
|
4
|
+
source: internal
|
|
5
|
+
tags: architecture, directory, provider, repository, mock, testing
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# API Client Architecture
|
|
9
|
+
|
|
10
|
+
네트워크 레이어 전체 아키텍처. 디렉토리 구조, Provider 그래프, Repository 패턴, Mock 테스트 전략.
|
|
11
|
+
|
|
12
|
+
## Key Concepts
|
|
13
|
+
|
|
14
|
+
- **중앙화**: 네트워크 관련 코드를 `core/networking/` 에 집중 (feature 횡단 관심사)
|
|
15
|
+
- **계층 분리**: Dio → Interceptor → API Client → Repository → Provider → UI
|
|
16
|
+
- **테스트 가능성**: API Client 추상화로 Mock 구현 용이
|
|
17
|
+
- **오프라인 우선**: 캐시 + 큐 + connectivity 감지를 기본 탑재
|
|
18
|
+
|
|
19
|
+
## Directory Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
lib/
|
|
23
|
+
├── core/
|
|
24
|
+
│ └── networking/
|
|
25
|
+
│ ├── dio_provider.dart # Dio 싱글톤 Provider
|
|
26
|
+
│ ├── app_config.dart # 환경 설정 (baseUrl 등)
|
|
27
|
+
│ ├── interceptors/
|
|
28
|
+
│ │ ├── auth_interceptor.dart # 토큰 주입 + 401 리프레시
|
|
29
|
+
│ │ ├── retry_interceptor.dart # 지수 백오프 재시도
|
|
30
|
+
│ │ ├── logging_interceptor.dart # 요청/응답 로깅
|
|
31
|
+
│ │ └── error_transform_interceptor.dart # DioException → NetworkFailure
|
|
32
|
+
│ ├── errors/
|
|
33
|
+
│ │ ├── network_failure.dart # sealed class 에러 타입
|
|
34
|
+
│ │ └── result.dart # Result<T> 타입
|
|
35
|
+
│ ├── cache/
|
|
36
|
+
│ │ ├── cache_config.dart # 캐시 정책 팩토리
|
|
37
|
+
│ │ ├── cache_store_provider.dart # HiveCacheStore Provider
|
|
38
|
+
│ │ └── cache_manager.dart # 캐시 클리어/무효화
|
|
39
|
+
│ ├── connectivity/
|
|
40
|
+
│ │ ├── connectivity_notifier.dart # 연결 상태 스트림
|
|
41
|
+
│ │ ├── offline_queue.dart # 오프라인 요청 큐
|
|
42
|
+
│ │ └── offline_banner.dart # 오프라인 UI 위젯
|
|
43
|
+
│ └── api/
|
|
44
|
+
│ ├── user_api.dart # @RestApi 클라이언트
|
|
45
|
+
│ ├── match_api.dart # @RestApi 클라이언트
|
|
46
|
+
│ └── ...
|
|
47
|
+
│
|
|
48
|
+
├── features/
|
|
49
|
+
│ └── user/
|
|
50
|
+
│ ├── data/
|
|
51
|
+
│ │ ├── models/
|
|
52
|
+
│ │ │ ├── user_response.dart # freezed 응답 모델
|
|
53
|
+
│ │ │ └── create_user_request.dart # freezed 요청 모델
|
|
54
|
+
│ │ └── repositories/
|
|
55
|
+
│ │ └── user_repository_impl.dart # API 호출 + 에러 변환
|
|
56
|
+
│ ├── domain/
|
|
57
|
+
│ │ ├── entities/
|
|
58
|
+
│ │ │ └── user.dart # 도메인 엔티티
|
|
59
|
+
│ │ └── repositories/
|
|
60
|
+
│ │ └── user_repository.dart # abstract
|
|
61
|
+
│ └── presentation/
|
|
62
|
+
│ └── providers/
|
|
63
|
+
│ └── user_provider.dart # UI 상태 관리
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Provider Dependency Graph
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
AppConfig
|
|
70
|
+
│
|
|
71
|
+
▼
|
|
72
|
+
DioProvider ──────────────────────────────────┐
|
|
73
|
+
│ │
|
|
74
|
+
├─ AuthInterceptor ← TokenStorage │
|
|
75
|
+
├─ RetryInterceptor │
|
|
76
|
+
├─ LoggingInterceptor (dev only) │
|
|
77
|
+
├─ ErrorTransformInterceptor │
|
|
78
|
+
└─ DioCacheInterceptor ← CacheStore │
|
|
79
|
+
│
|
|
80
|
+
┌──────────────────────────────────────────┘
|
|
81
|
+
│
|
|
82
|
+
▼
|
|
83
|
+
API Clients (Retrofit)
|
|
84
|
+
│ UserApi, MatchApi, ...
|
|
85
|
+
│
|
|
86
|
+
▼
|
|
87
|
+
Repositories
|
|
88
|
+
│ UserRepository, MatchRepository, ...
|
|
89
|
+
│ (API 호출 + Result<T> 반환)
|
|
90
|
+
│
|
|
91
|
+
▼
|
|
92
|
+
Notifiers / Providers
|
|
93
|
+
│ UserNotifier, MatchNotifier, ...
|
|
94
|
+
│ (비즈니스 로직 + UI 상태)
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
Widgets (UI)
|
|
98
|
+
|
|
99
|
+
ConnectivityNotifier ────► OfflineQueueManager
|
|
100
|
+
│ │
|
|
101
|
+
└── OfflineBanner (UI) └── 연결 복구 시 큐 처리
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Provider 코드
|
|
105
|
+
|
|
106
|
+
```dart
|
|
107
|
+
// core/networking/providers.dart — 네트워크 레이어 Provider 모음
|
|
108
|
+
|
|
109
|
+
/// 앱 설정
|
|
110
|
+
final appConfigProvider = Provider<AppConfig>((ref) => AppConfig.dev);
|
|
111
|
+
|
|
112
|
+
/// Dio 싱글톤
|
|
113
|
+
final dioProvider = Provider<Dio>((ref) {
|
|
114
|
+
final config = ref.watch(appConfigProvider);
|
|
115
|
+
final dio = Dio(BaseOptions(
|
|
116
|
+
baseUrl: config.baseUrl,
|
|
117
|
+
connectTimeout: const Duration(seconds: 15),
|
|
118
|
+
receiveTimeout: const Duration(seconds: 15),
|
|
119
|
+
sendTimeout: const Duration(seconds: 15),
|
|
120
|
+
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
|
|
121
|
+
));
|
|
122
|
+
|
|
123
|
+
dio.interceptors.addAll([
|
|
124
|
+
ref.watch(authInterceptorProvider),
|
|
125
|
+
ref.watch(retryInterceptorProvider),
|
|
126
|
+
if (config.environment == AppEnvironment.dev)
|
|
127
|
+
ref.watch(loggingInterceptorProvider),
|
|
128
|
+
ref.watch(errorTransformInterceptorProvider),
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
return dio;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/// API Clients
|
|
135
|
+
final userApiProvider = Provider<UserApi>((ref) {
|
|
136
|
+
return UserApi(ref.watch(dioProvider));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
/// Repositories
|
|
140
|
+
final userRepositoryProvider = Provider<UserRepository>((ref) {
|
|
141
|
+
return UserRepositoryImpl(api: ref.watch(userApiProvider));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/// Connectivity
|
|
145
|
+
final connectivityProvider =
|
|
146
|
+
StreamNotifierProvider<ConnectivityNotifier, NetworkStatus>(
|
|
147
|
+
ConnectivityNotifier.new,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
final isOnlineProvider = Provider<bool>((ref) {
|
|
151
|
+
return ref.watch(connectivityProvider).valueOrNull == NetworkStatus.online;
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Repository Pattern
|
|
156
|
+
|
|
157
|
+
```dart
|
|
158
|
+
/// Repository 추상 (domain 레이어)
|
|
159
|
+
abstract class UserRepository {
|
|
160
|
+
Future<Result<User>> getUser(String id);
|
|
161
|
+
Future<Result<List<User>>> getUsers({int page = 1});
|
|
162
|
+
Future<Result<User>> createUser(CreateUserRequest request);
|
|
163
|
+
Future<Result<void>> deleteUser(String id);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// Repository 구현 (data 레이어)
|
|
167
|
+
class UserRepositoryImpl implements UserRepository {
|
|
168
|
+
final UserApi _api;
|
|
169
|
+
|
|
170
|
+
UserRepositoryImpl({required UserApi api}) : _api = api;
|
|
171
|
+
|
|
172
|
+
@override
|
|
173
|
+
Future<Result<User>> getUser(String id) async {
|
|
174
|
+
try {
|
|
175
|
+
final response = await _api.getUser(id);
|
|
176
|
+
return Success(response.toDomain());
|
|
177
|
+
} on DioException catch (e) {
|
|
178
|
+
return Failure(NetworkFailure.fromDioException(e));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@override
|
|
183
|
+
Future<Result<List<User>>> getUsers({int page = 1}) async {
|
|
184
|
+
try {
|
|
185
|
+
final response = await _api.getUsers(page, 20);
|
|
186
|
+
return Success(response.data.map((r) => r.toDomain()).toList());
|
|
187
|
+
} on DioException catch (e) {
|
|
188
|
+
return Failure(NetworkFailure.fromDioException(e));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ... createUser, deleteUser 동일 패턴
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Mock Testing Strategy
|
|
197
|
+
|
|
198
|
+
```dart
|
|
199
|
+
/// Mock API Client
|
|
200
|
+
class MockUserApi implements UserApi {
|
|
201
|
+
@override
|
|
202
|
+
Future<UserResponse> getUser(String id) async {
|
|
203
|
+
return UserResponse(
|
|
204
|
+
id: id,
|
|
205
|
+
name: 'Test User',
|
|
206
|
+
email: 'test@example.com',
|
|
207
|
+
createdAt: DateTime.now(),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@override
|
|
212
|
+
Future<PaginatedResponse<UserResponse>> getUsers(int page, int limit) async {
|
|
213
|
+
return PaginatedResponse(
|
|
214
|
+
data: List.generate(limit, (i) => UserResponse(
|
|
215
|
+
id: 'user_$i',
|
|
216
|
+
name: 'User $i',
|
|
217
|
+
email: 'user$i@example.com',
|
|
218
|
+
createdAt: DateTime.now(),
|
|
219
|
+
)),
|
|
220
|
+
total: 100,
|
|
221
|
+
page: page,
|
|
222
|
+
lastPage: 5,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ...
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// 테스트에서 Provider 오버라이드
|
|
230
|
+
void main() {
|
|
231
|
+
group('UserNotifier', () {
|
|
232
|
+
late ProviderContainer container;
|
|
233
|
+
|
|
234
|
+
setUp(() {
|
|
235
|
+
container = ProviderContainer(overrides: [
|
|
236
|
+
userApiProvider.overrideWithValue(MockUserApi()),
|
|
237
|
+
]);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
tearDown(() => container.dispose());
|
|
241
|
+
|
|
242
|
+
test('loads user successfully', () async {
|
|
243
|
+
final repository = container.read(userRepositoryProvider);
|
|
244
|
+
final result = await repository.getUser('user_1');
|
|
245
|
+
|
|
246
|
+
expect(result, isA<Success<User>>());
|
|
247
|
+
expect((result as Success).data.name, 'Test User');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// 에러 시나리오 Mock
|
|
253
|
+
class ErrorUserApi implements UserApi {
|
|
254
|
+
@override
|
|
255
|
+
Future<UserResponse> getUser(String id) async {
|
|
256
|
+
throw DioException(
|
|
257
|
+
requestOptions: RequestOptions(path: '/users/$id'),
|
|
258
|
+
type: DioExceptionType.connectionTimeout,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ...
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/// 에러 테스트
|
|
266
|
+
test('returns TimeoutFailure on connection timeout', () async {
|
|
267
|
+
final container = ProviderContainer(overrides: [
|
|
268
|
+
userApiProvider.overrideWithValue(ErrorUserApi()),
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
final repository = container.read(userRepositoryProvider);
|
|
272
|
+
final result = await repository.getUser('user_1');
|
|
273
|
+
|
|
274
|
+
expect(result, isA<Failure<User>>());
|
|
275
|
+
expect((result as Failure).failure, isA<TimeoutFailure>());
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Initialization Flow
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
앱 시작 (main.dart)
|
|
283
|
+
│
|
|
284
|
+
├─ 1. WidgetsFlutterBinding.ensureInitialized()
|
|
285
|
+
├─ 2. AppConfig 결정 (환경 변수 / 빌드 플래그)
|
|
286
|
+
├─ 3. CacheStore 초기화 (HiveCacheStore)
|
|
287
|
+
├─ 4. ProviderScope(overrides: [appConfig, cacheStore])
|
|
288
|
+
└─ 5. runApp()
|
|
289
|
+
│
|
|
290
|
+
└─ App 위젯 build
|
|
291
|
+
├─ DioProvider 자동 생성 (lazy)
|
|
292
|
+
├─ ConnectivityNotifier 스트림 시작
|
|
293
|
+
└─ OfflineBanner 조건부 표시
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Common Pitfalls
|
|
297
|
+
|
|
298
|
+
1. **Dio 다중 인스턴스**: Feature마다 Dio 생성 → 인터셉터 미적용, 토큰 누락
|
|
299
|
+
2. **인터셉터 순서**: Error Transform이 Auth 앞 → 401 리프레시 불가
|
|
300
|
+
3. **토큰 리프레시 순환**: Auth 인터셉터가 메인 Dio로 리프레시 → 무한 루프
|
|
301
|
+
4. **캐시 + 인증 에러**: 401 응답 캐시 → 로그인 후에도 에러 반환
|
|
302
|
+
5. **오프라인 큐 영속성**: 메모리 큐만 사용 → 앱 재시작 시 유실
|
|
303
|
+
6. **connectTimeout vs receiveTimeout**: connect는 TCP 연결, receive는 데이터 수신 — 둘 다 설정 필수
|
|
304
|
+
7. **validateStatus 미설정**: 4xx도 DioException → 정상 에러 응답 처리 불가
|
|
305
|
+
8. **build_runner 미실행**: Retrofit `.g.dart` 미생성 → 컴파일 에러
|