aiwf 0.3.7 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/.aiwf/00_PROJECT_MANIFEST.md +226 -2
  2. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/S03_M02_sprint_meta.md +112 -0
  3. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T01_S03_new_test_task.md +39 -0
  4. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T02_S03_first_task.md +39 -0
  5. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T03_S03_test_task.md +39 -0
  6. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T04_S03_task_with_special_characters.md +39 -0
  7. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T05_S03_test.md +39 -0
  8. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T06_S03_test_task.md +39 -0
  9. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T07_S03_next_task.md +39 -0
  10. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T08_S03_next_task.md +39 -0
  11. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T09_S03_new_test_task.md +39 -0
  12. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T100_S03_task_with_special_characters.md +39 -0
  13. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T101_S03_test.md +39 -0
  14. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T102_S03_test_task.md +39 -0
  15. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T103_S03_next_task.md +39 -0
  16. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T104_S03_next_task.md +39 -0
  17. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T105_S03_new_test_task.md +39 -0
  18. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T106_S03_first_task.md +39 -0
  19. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T107_S03_test_task.md +39 -0
  20. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T108_S03_task_with_special_characters.md +39 -0
  21. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T109_S03_test.md +39 -0
  22. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T10_S03_first_task.md +39 -0
  23. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T110_S03_test_task.md +39 -0
  24. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T111_S03_next_task.md +39 -0
  25. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T112_S03_next_task.md +39 -0
  26. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T11_S03_test_task.md +39 -0
  27. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T12_S03_task_with_special_characters.md +39 -0
  28. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T13_S03_test.md +39 -0
  29. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T14_S03_test_task.md +39 -0
  30. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T15_S03_next_task.md +39 -0
  31. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T16_S03_next_task.md +39 -0
  32. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T17_S03_new_test_task.md +39 -0
  33. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T18_S03_first_task.md +39 -0
  34. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T19_S03_test_task.md +39 -0
  35. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T20_S03_task_with_special_characters.md +39 -0
  36. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T21_S03_test.md +39 -0
  37. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T22_S03_test_task.md +39 -0
  38. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T23_S03_next_task.md +39 -0
  39. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T24_S03_next_task.md +39 -0
  40. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T25_S03_new_test_task.md +39 -0
  41. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T26_S03_first_task.md +39 -0
  42. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T27_S03_test_task.md +39 -0
  43. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T28_S03_task_with_special_characters.md +39 -0
  44. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T29_S03_test.md +39 -0
  45. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T30_S03_test_task.md +39 -0
  46. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T31_S03_next_task.md +39 -0
  47. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T32_S03_next_task.md +39 -0
  48. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T33_S03_new_test_task.md +39 -0
  49. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T34_S03_first_task.md +39 -0
  50. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T35_S03_test_task.md +39 -0
  51. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T36_S03_task_with_special_characters.md +39 -0
  52. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T37_S03_test.md +39 -0
  53. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T38_S03_test_task.md +39 -0
  54. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T39_S03_next_task.md +39 -0
  55. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T40_S03_next_task.md +39 -0
  56. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T41_S03_new_test_task.md +39 -0
  57. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T42_S03_first_task.md +39 -0
  58. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T43_S03_test_task.md +39 -0
  59. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T44_S03_task_with_special_characters.md +39 -0
  60. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T45_S03_test.md +39 -0
  61. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T46_S03_test_task.md +39 -0
  62. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T47_S03_next_task.md +39 -0
  63. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T48_S03_next_task.md +39 -0
  64. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T49_S03_new_test_task.md +39 -0
  65. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T50_S03_first_task.md +39 -0
  66. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T51_S03_test_task.md +39 -0
  67. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T52_S03_task_with_special_characters.md +39 -0
  68. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T53_S03_test.md +39 -0
  69. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T54_S03_test_task.md +39 -0
  70. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T55_S03_next_task.md +39 -0
  71. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T56_S03_next_task.md +39 -0
  72. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T57_S03_new_test_task.md +39 -0
  73. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T58_S03_first_task.md +39 -0
  74. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T59_S03_test_task.md +39 -0
  75. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T60_S03_task_with_special_characters.md +39 -0
  76. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T61_S03_test.md +39 -0
  77. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T62_S03_test_task.md +39 -0
  78. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T63_S03_next_task.md +39 -0
  79. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T64_S03_next_task.md +39 -0
  80. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T65_S03_new_test_task.md +39 -0
  81. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T66_S03_first_task.md +39 -0
  82. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T67_S03_test_task.md +39 -0
  83. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T68_S03_task_with_special_characters.md +39 -0
  84. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T69_S03_test.md +39 -0
  85. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T70_S03_test_task.md +39 -0
  86. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T71_S03_next_task.md +39 -0
  87. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T72_S03_next_task.md +39 -0
  88. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T73_S03_new_test_task.md +39 -0
  89. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T74_S03_first_task.md +39 -0
  90. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T75_S03_test_task.md +39 -0
  91. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T76_S03_task_with_special_characters.md +39 -0
  92. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T77_S03_test.md +39 -0
  93. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T78_S03_test_task.md +39 -0
  94. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T79_S03_next_task.md +39 -0
  95. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T80_S03_next_task.md +39 -0
  96. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T81_S03_new_test_task.md +39 -0
  97. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T82_S03_first_task.md +39 -0
  98. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T83_S03_test_task.md +39 -0
  99. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T84_S03_task_with_special_characters.md +39 -0
  100. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T85_S03_test.md +39 -0
  101. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T86_S03_test_task.md +39 -0
  102. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T87_S03_next_task.md +39 -0
  103. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T88_S03_next_task.md +39 -0
  104. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T89_S03_new_test_task.md +39 -0
  105. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T90_S03_first_task.md +39 -0
  106. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T91_S03_test_task.md +39 -0
  107. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T92_S03_task_with_special_characters.md +39 -0
  108. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T93_S03_test.md +39 -0
  109. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T94_S03_test_task.md +39 -0
  110. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T95_S03_next_task.md +39 -0
  111. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T96_S03_next_task.md +39 -0
  112. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T97_S03_new_test_task.md +39 -0
  113. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T98_S03_first_task.md +39 -0
  114. package/.aiwf/03_SPRINTS/S03_M02_ecosystem_integration/T99_S03_test_task.md +39 -0
  115. package/.aiwf/test-results-s02.json +5 -5
  116. package/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +3 -15
  117. package/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +2 -15
  118. package/.claude/commands/aiwf/aiwf_compress_stats.md +2 -51
  119. package/.claude/commands/aiwf/aiwf_compress_with_persona.md +5 -18
  120. package/.claude/commands/aiwf/aiwf_create_feature_ledger.md +1 -1
  121. package/.claude/commands/aiwf/aiwf_default_mode.md +1 -1
  122. package/.claude/commands/aiwf/aiwf_evaluate.md +3 -16
  123. package/.claude/commands/aiwf/aiwf_get_feature_details.md +1 -1
  124. package/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +1 -1
  125. package/.claude/commands/aiwf/aiwf_list_features.md +1 -1
  126. package/.claude/commands/aiwf/aiwf_switch_language.md +48 -120
  127. package/.claude/commands/aiwf/aiwf_update_feature_status.md +1 -1
  128. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_aggressive_persona.md +2 -2
  129. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_minimal_persona.md +2 -2
  130. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_stats.md +10 -47
  131. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_compress_with_persona.md +2 -2
  132. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_create_feature_ledger.md +1 -1
  133. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_default_mode.md +1 -1
  134. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_evaluate.md +2 -2
  135. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_get_feature_details.md +1 -1
  136. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_link_feature_to_milestone.md +1 -1
  137. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_list_features.md +1 -1
  138. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_architect.md +1 -1
  139. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_backend.md +1 -1
  140. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_data_analyst.md +1 -1
  141. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_frontend.md +1 -1
  142. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_security.md +1 -1
  143. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_persona_status.md +1 -1
  144. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_update_feature_status.md +1 -1
  145. package/package.json +1 -1
  146. package/src/cli/index.js +174 -0
  147. package/src/commands/compress.js +60 -0
  148. package/src/commands/create-project.js +23 -17
  149. package/src/commands/evaluate.js +459 -0
  150. package/src/commands/feature.js +365 -0
  151. package/src/commands/persona.js +37 -16
  152. package/src/commands/token.js +312 -0
  153. package/src/lib/resource-loader.js +250 -0
  154. package/src/lib/resources/commands/ai-persona.js +602 -0
  155. package/src/lib/resources/commands/compress-context.js +389 -0
  156. package/src/lib/resources/commands/evaluate.js +246 -0
  157. package/src/lib/resources/commands/feature-ledger.js +565 -0
  158. package/src/lib/resources/commands/feature_commit_report.js +370 -0
  159. package/src/lib/resources/commands/persona-context-apply.js +562 -0
  160. package/src/lib/resources/commands/scan_git_history.js +234 -0
  161. package/src/lib/resources/commands/sync_feature_commits.js +154 -0
  162. package/src/lib/resources/commands/token-tracking.js +443 -0
  163. package/src/lib/resources/config/commit-patterns.js +103 -0
  164. package/src/lib/resources/config/language.json +6 -0
  165. package/src/lib/resources/personas/PERSONA_INDEX.md +57 -0
  166. package/src/lib/resources/personas/analyst.json +44 -0
  167. package/src/lib/resources/personas/architect/best_practices.md +136 -0
  168. package/src/lib/resources/personas/architect/knowledge_base.md +377 -0
  169. package/src/lib/resources/personas/architect.json +44 -0
  170. package/src/lib/resources/personas/backend/best_practices.md +766 -0
  171. package/src/lib/resources/personas/backend/knowledge_base.md +1070 -0
  172. package/src/lib/resources/personas/data_analyst/best_practices.md +594 -0
  173. package/src/lib/resources/personas/data_analyst/knowledge_base.md +1057 -0
  174. package/src/lib/resources/personas/developer.json +44 -0
  175. package/src/lib/resources/personas/developer.md +37 -0
  176. package/src/lib/resources/personas/evaluation_criteria.json +137 -0
  177. package/src/lib/resources/personas/frontend/best_practices.md +445 -0
  178. package/src/lib/resources/personas/frontend/knowledge_base.md +729 -0
  179. package/src/lib/resources/personas/persona-index.json +31 -0
  180. package/src/lib/resources/personas/reviewer.json +44 -0
  181. package/src/lib/resources/personas/security/best_practices.md +307 -0
  182. package/src/lib/resources/personas/security/knowledge_base.md +498 -0
  183. package/src/lib/resources/personas/tester.json +44 -0
  184. package/src/lib/resources/templates/README.md +78 -0
  185. package/src/lib/resources/templates/api-server/config.json +64 -0
  186. package/src/lib/resources/templates/api-server/template/.aiwf/config.json +56 -0
  187. package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +42 -0
  188. package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +40 -0
  189. package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +111 -0
  190. package/src/lib/resources/templates/api-server/template/.env.example +29 -0
  191. package/src/lib/resources/templates/api-server/template/.eslintrc.json +23 -0
  192. package/src/lib/resources/templates/api-server/template/README.md +171 -0
  193. package/src/lib/resources/templates/api-server/template/jest.config.js +26 -0
  194. package/src/lib/resources/templates/api-server/template/nodemon.json +9 -0
  195. package/src/lib/resources/templates/api-server/template/package.json +61 -0
  196. package/src/lib/resources/templates/api-server/template/src/app.ts +60 -0
  197. package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +38 -0
  198. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +68 -0
  199. package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +128 -0
  200. package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +28 -0
  201. package/src/lib/resources/templates/api-server/template/src/index.ts +28 -0
  202. package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +50 -0
  203. package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +41 -0
  204. package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +42 -0
  205. package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +14 -0
  206. package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +20 -0
  207. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +42 -0
  208. package/src/lib/resources/templates/api-server/template/src/routes/index.ts +13 -0
  209. package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +67 -0
  210. package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +37 -0
  211. package/src/lib/resources/templates/api-server/template/tests/app.test.ts +40 -0
  212. package/src/lib/resources/templates/api-server/template/tsconfig.json +43 -0
  213. package/src/lib/resources/templates/npm-library/config.json +61 -0
  214. package/src/lib/resources/templates/npm-library/template/LICENSE +21 -0
  215. package/src/lib/resources/templates/npm-library/template/README.md +201 -0
  216. package/src/lib/resources/templates/npm-library/template/package.json +78 -0
  217. package/src/lib/resources/templates/web-app/config.json +53 -0
  218. package/src/lib/resources/templates/web-app/template/.aiwf/config.json +54 -0
  219. package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +37 -0
  220. package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +40 -0
  221. package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +110 -0
  222. package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +20 -0
  223. package/src/lib/resources/templates/web-app/template/README.md +151 -0
  224. package/src/lib/resources/templates/web-app/template/index.html +14 -0
  225. package/src/lib/resources/templates/web-app/template/package.json +44 -0
  226. package/src/lib/resources/templates/web-app/template/postcss.config.js +6 -0
  227. package/src/lib/resources/templates/web-app/template/public/vite.svg +1 -0
  228. package/src/lib/resources/templates/web-app/template/src/App.tsx +21 -0
  229. package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +57 -0
  230. package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +107 -0
  231. package/src/lib/resources/templates/web-app/template/src/components/aiwf/FeatureLedger.tsx +93 -0
  232. package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +71 -0
  233. package/src/lib/resources/templates/web-app/template/src/index.css +60 -0
  234. package/src/lib/resources/templates/web-app/template/src/main.tsx +10 -0
  235. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +60 -0
  236. package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +89 -0
  237. package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +25 -0
  238. package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +126 -0
  239. package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +9 -0
  240. package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +1 -0
  241. package/src/lib/resources/templates/web-app/template/tailwind.config.js +30 -0
  242. package/src/lib/resources/templates/web-app/template/tsconfig.json +37 -0
  243. package/src/lib/resources/templates/web-app/template/tsconfig.node.json +10 -0
  244. package/src/lib/resources/templates/web-app/template/vite.config.ts +23 -0
  245. package/src/lib/resources/utils/background-monitor.js +223 -0
  246. package/src/lib/resources/utils/compression-metrics.js +1031 -0
  247. package/src/lib/resources/utils/compression-strategies.js +557 -0
  248. package/src/lib/resources/utils/content-normalizer.js +455 -0
  249. package/src/lib/resources/utils/context-compressor.js +583 -0
  250. package/src/lib/resources/utils/context-rule-parser.js +180 -0
  251. package/src/lib/resources/utils/context-token-monitor.js +463 -0
  252. package/src/lib/resources/utils/context-update-manager.js +502 -0
  253. package/src/lib/resources/utils/feature-updater.js +271 -0
  254. package/src/lib/resources/utils/git-utils.js +220 -0
  255. package/src/lib/resources/utils/importance-classifier.js +645 -0
  256. package/src/lib/resources/utils/information-filter.js +710 -0
  257. package/src/lib/resources/utils/persona-aware-compressor.js +598 -0
  258. package/src/lib/resources/utils/prompt-injector.js +242 -0
  259. package/src/lib/resources/utils/simplified-evaluator.js +119 -0
  260. package/src/lib/resources/utils/text-summarizer.js +492 -0
  261. package/src/lib/resources/utils/token-counter.js +177 -0
  262. package/src/lib/resources/utils/token-monitor.js +484 -0
  263. package/src/lib/resources/utils/token-reporter.js +453 -0
  264. package/src/lib/resources/utils/token-storage.js +433 -0
  265. package/src/lib/resources/utils/token-tracker.js +283 -0
  266. package/claude-code/aiwf/ko/.claude/commands/aiwf/aiwf_switch_language.md +0 -160
  267. /package/claude-code/aiwf/ko/.claude/commands/aiwf/{aiwf_update_docs_kr.md → aiwf_update_docs.md} +0 -0
@@ -0,0 +1,766 @@
1
+ # Backend 페르소나 Best Practices
2
+
3
+ ## 개요
4
+ Backend 페르소나는 서버 사이드 로직, 데이터베이스 설계, API 개발에 중점을 둡니다. 확장성, 성능, 데이터 무결성을 최우선으로 고려합니다.
5
+
6
+ ## 핵심 원칙
7
+
8
+ ### 1. API 설계 원칙
9
+ - RESTful 원칙 준수
10
+ - GraphQL 스키마 최적화
11
+ - API 버전 관리
12
+ - 일관된 응답 형식
13
+
14
+ ### 2. 데이터베이스 설계
15
+ ```sql
16
+ -- 정규화와 성능의 균형
17
+ CREATE TABLE users (
18
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
19
+ email VARCHAR(255) UNIQUE NOT NULL,
20
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
21
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
22
+ );
23
+
24
+ -- 인덱스 전략
25
+ CREATE INDEX idx_users_email ON users(email);
26
+ CREATE INDEX idx_users_created_at ON users(created_at DESC);
27
+
28
+ -- 파티셔닝 (대용량 테이블)
29
+ CREATE TABLE orders (
30
+ id BIGSERIAL,
31
+ user_id UUID REFERENCES users(id),
32
+ order_date DATE NOT NULL,
33
+ total_amount DECIMAL(10,2),
34
+ PRIMARY KEY (id, order_date)
35
+ ) PARTITION BY RANGE (order_date);
36
+ ```
37
+
38
+ ### 3. 마이크로서비스 아키텍처
39
+ ```javascript
40
+ // 서비스 간 통신 패턴
41
+ class OrderService {
42
+ constructor(eventBus, userService, inventoryService) {
43
+ this.eventBus = eventBus;
44
+ this.userService = userService;
45
+ this.inventoryService = inventoryService;
46
+ }
47
+
48
+ async createOrder(orderData) {
49
+ // Saga 패턴으로 분산 트랜잭션 처리
50
+ const saga = new OrderSaga();
51
+
52
+ try {
53
+ // 1. 사용자 검증
54
+ const user = await this.userService.validateUser(orderData.userId);
55
+ saga.addCompensation(() => this.userService.rollback(user));
56
+
57
+ // 2. 재고 확인 및 예약
58
+ const reservation = await this.inventoryService.reserve(orderData.items);
59
+ saga.addCompensation(() => this.inventoryService.release(reservation));
60
+
61
+ // 3. 주문 생성
62
+ const order = await this.createOrderRecord(orderData);
63
+ saga.addCompensation(() => this.deleteOrder(order.id));
64
+
65
+ // 4. 이벤트 발행
66
+ await this.eventBus.publish('order.created', {
67
+ orderId: order.id,
68
+ userId: user.id,
69
+ items: orderData.items
70
+ });
71
+
72
+ return order;
73
+ } catch (error) {
74
+ await saga.compensate();
75
+ throw error;
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## API 개발 Best Practices
82
+
83
+ ### RESTful API 설계
84
+ ```javascript
85
+ // Express.js 라우터
86
+ class UserController {
87
+ // GET /api/v1/users?page=1&limit=20&sort=created_at:desc
88
+ async getUsers(req, res) {
89
+ const { page = 1, limit = 20, sort, filter } = req.query;
90
+
91
+ try {
92
+ const users = await this.userService.findAll({
93
+ pagination: { page, limit },
94
+ sorting: this.parseSortParam(sort),
95
+ filters: this.parseFilterParam(filter)
96
+ });
97
+
98
+ res.json({
99
+ success: true,
100
+ data: users,
101
+ meta: {
102
+ page: parseInt(page),
103
+ limit: parseInt(limit),
104
+ total: users.total,
105
+ totalPages: Math.ceil(users.total / limit)
106
+ }
107
+ });
108
+ } catch (error) {
109
+ this.handleError(error, res);
110
+ }
111
+ }
112
+
113
+ // POST /api/v1/users
114
+ async createUser(req, res) {
115
+ try {
116
+ // 입력 검증
117
+ const validatedData = await this.validator.validate(req.body, UserSchema);
118
+
119
+ // 비즈니스 로직
120
+ const user = await this.userService.create(validatedData);
121
+
122
+ // 201 Created with Location header
123
+ res.status(201)
124
+ .location(`/api/v1/users/${user.id}`)
125
+ .json({
126
+ success: true,
127
+ data: user
128
+ });
129
+ } catch (error) {
130
+ this.handleError(error, res);
131
+ }
132
+ }
133
+
134
+ // Error handling
135
+ handleError(error, res) {
136
+ if (error instanceof ValidationError) {
137
+ return res.status(400).json({
138
+ success: false,
139
+ error: {
140
+ code: 'VALIDATION_ERROR',
141
+ message: error.message,
142
+ details: error.details
143
+ }
144
+ });
145
+ }
146
+
147
+ if (error instanceof NotFoundError) {
148
+ return res.status(404).json({
149
+ success: false,
150
+ error: {
151
+ code: 'NOT_FOUND',
152
+ message: error.message
153
+ }
154
+ });
155
+ }
156
+
157
+ // 500 Internal Server Error
158
+ console.error('Unexpected error:', error);
159
+ res.status(500).json({
160
+ success: false,
161
+ error: {
162
+ code: 'INTERNAL_ERROR',
163
+ message: 'An unexpected error occurred'
164
+ }
165
+ });
166
+ }
167
+ }
168
+ ```
169
+
170
+ ### GraphQL 스키마 설계
171
+ ```graphql
172
+ # Schema Definition
173
+ type User {
174
+ id: ID!
175
+ email: String!
176
+ profile: Profile!
177
+ orders(
178
+ first: Int = 10
179
+ after: String
180
+ filter: OrderFilter
181
+ ): OrderConnection!
182
+ createdAt: DateTime!
183
+ updatedAt: DateTime!
184
+ }
185
+
186
+ type Profile {
187
+ firstName: String!
188
+ lastName: String!
189
+ avatar: String
190
+ preferences: UserPreferences!
191
+ }
192
+
193
+ # Pagination using Relay specification
194
+ type OrderConnection {
195
+ edges: [OrderEdge!]!
196
+ pageInfo: PageInfo!
197
+ totalCount: Int!
198
+ }
199
+
200
+ type OrderEdge {
201
+ node: Order!
202
+ cursor: String!
203
+ }
204
+
205
+ # Input types
206
+ input CreateUserInput {
207
+ email: String!
208
+ password: String!
209
+ profile: ProfileInput!
210
+ }
211
+
212
+ # Queries
213
+ type Query {
214
+ user(id: ID!): User
215
+ users(
216
+ first: Int
217
+ after: String
218
+ filter: UserFilter
219
+ orderBy: UserOrderBy
220
+ ): UserConnection!
221
+ me: User @auth
222
+ }
223
+
224
+ # Mutations
225
+ type Mutation {
226
+ createUser(input: CreateUserInput!): CreateUserPayload!
227
+ updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
228
+ deleteUser(id: ID!): DeleteUserPayload!
229
+ }
230
+
231
+ # Subscriptions
232
+ type Subscription {
233
+ userUpdated(userId: ID!): User!
234
+ orderCreated(userId: ID!): Order!
235
+ }
236
+ ```
237
+
238
+ ## 데이터베이스 최적화
239
+
240
+ ### 쿼리 최적화
241
+ ```javascript
242
+ // Query Builder Pattern
243
+ class UserRepository {
244
+ constructor(db) {
245
+ this.db = db;
246
+ }
247
+
248
+ // N+1 문제 방지
249
+ async findWithOrders(userId) {
250
+ const query = `
251
+ SELECT
252
+ u.id, u.email, u.created_at,
253
+ o.id as order_id, o.total_amount, o.order_date,
254
+ oi.product_id, oi.quantity, oi.price
255
+ FROM users u
256
+ LEFT JOIN orders o ON u.id = o.user_id
257
+ LEFT JOIN order_items oi ON o.id = oi.order_id
258
+ WHERE u.id = $1
259
+ ORDER BY o.order_date DESC
260
+ `;
261
+
262
+ const result = await this.db.query(query, [userId]);
263
+ return this.mapResultToUser(result.rows);
264
+ }
265
+
266
+ // Batch loading
267
+ async findByIds(userIds) {
268
+ if (userIds.length === 0) return [];
269
+
270
+ const query = `
271
+ SELECT * FROM users
272
+ WHERE id = ANY($1::uuid[])
273
+ `;
274
+
275
+ const result = await this.db.query(query, [userIds]);
276
+
277
+ // 순서 보장
278
+ const userMap = new Map(
279
+ result.rows.map(row => [row.id, row])
280
+ );
281
+
282
+ return userIds.map(id => userMap.get(id)).filter(Boolean);
283
+ }
284
+
285
+ // 동적 쿼리 빌더
286
+ async search(criteria) {
287
+ let query = 'SELECT * FROM users WHERE 1=1';
288
+ const params = [];
289
+ let paramIndex = 1;
290
+
291
+ if (criteria.email) {
292
+ query += ` AND email ILIKE $${paramIndex}`;
293
+ params.push(`%${criteria.email}%`);
294
+ paramIndex++;
295
+ }
296
+
297
+ if (criteria.createdAfter) {
298
+ query += ` AND created_at >= $${paramIndex}`;
299
+ params.push(criteria.createdAfter);
300
+ paramIndex++;
301
+ }
302
+
303
+ if (criteria.sortBy) {
304
+ const validColumns = ['email', 'created_at'];
305
+ if (validColumns.includes(criteria.sortBy)) {
306
+ query += ` ORDER BY ${criteria.sortBy} ${
307
+ criteria.sortOrder === 'desc' ? 'DESC' : 'ASC'
308
+ }`;
309
+ }
310
+ }
311
+
312
+ query += ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
313
+ params.push(criteria.limit || 20, criteria.offset || 0);
314
+
315
+ return this.db.query(query, params);
316
+ }
317
+ }
318
+ ```
319
+
320
+ ### 캐싱 전략
321
+ ```javascript
322
+ // Redis 캐싱 레이어
323
+ class CacheService {
324
+ constructor(redis, ttl = 3600) {
325
+ this.redis = redis;
326
+ this.ttl = ttl;
327
+ }
328
+
329
+ // Cache-Aside Pattern
330
+ async get(key, fetchFn) {
331
+ // 1. 캐시 확인
332
+ const cached = await this.redis.get(key);
333
+ if (cached) {
334
+ return JSON.parse(cached);
335
+ }
336
+
337
+ // 2. 캐시 미스 시 데이터 조회
338
+ const data = await fetchFn();
339
+
340
+ // 3. 캐시 저장
341
+ await this.redis.setex(
342
+ key,
343
+ this.ttl,
344
+ JSON.stringify(data)
345
+ );
346
+
347
+ return data;
348
+ }
349
+
350
+ // Write-Through Pattern
351
+ async set(key, data, ttl = this.ttl) {
352
+ await this.redis.setex(
353
+ key,
354
+ ttl,
355
+ JSON.stringify(data)
356
+ );
357
+ return data;
358
+ }
359
+
360
+ // Cache Invalidation
361
+ async invalidate(pattern) {
362
+ const keys = await this.redis.keys(pattern);
363
+ if (keys.length > 0) {
364
+ await this.redis.del(...keys);
365
+ }
366
+ }
367
+
368
+ // Distributed Lock (분산 환경)
369
+ async acquireLock(lockKey, ttl = 10) {
370
+ const identifier = uuidv4();
371
+ const result = await this.redis.set(
372
+ `lock:${lockKey}`,
373
+ identifier,
374
+ 'NX',
375
+ 'EX',
376
+ ttl
377
+ );
378
+
379
+ return result === 'OK' ? identifier : null;
380
+ }
381
+
382
+ async releaseLock(lockKey, identifier) {
383
+ const script = `
384
+ if redis.call("get", KEYS[1]) == ARGV[1] then
385
+ return redis.call("del", KEYS[1])
386
+ else
387
+ return 0
388
+ end
389
+ `;
390
+
391
+ return this.redis.eval(
392
+ script,
393
+ 1,
394
+ `lock:${lockKey}`,
395
+ identifier
396
+ );
397
+ }
398
+ }
399
+ ```
400
+
401
+ ## 메시지 큐와 이벤트 처리
402
+
403
+ ### RabbitMQ 패턴
404
+ ```javascript
405
+ // Publisher/Subscriber Pattern
406
+ class EventPublisher {
407
+ constructor(channel) {
408
+ this.channel = channel;
409
+ }
410
+
411
+ async publish(exchange, routingKey, message) {
412
+ const messageBuffer = Buffer.from(JSON.stringify({
413
+ ...message,
414
+ timestamp: new Date().toISOString(),
415
+ messageId: uuidv4()
416
+ }));
417
+
418
+ await this.channel.publish(
419
+ exchange,
420
+ routingKey,
421
+ messageBuffer,
422
+ {
423
+ persistent: true,
424
+ contentType: 'application/json',
425
+ headers: {
426
+ 'x-retry-count': 0
427
+ }
428
+ }
429
+ );
430
+ }
431
+ }
432
+
433
+ // Consumer with Error Handling
434
+ class EventConsumer {
435
+ constructor(channel, options = {}) {
436
+ this.channel = channel;
437
+ this.maxRetries = options.maxRetries || 3;
438
+ }
439
+
440
+ async consume(queue, handler) {
441
+ await this.channel.consume(queue, async (msg) => {
442
+ if (!msg) return;
443
+
444
+ try {
445
+ const content = JSON.parse(msg.content.toString());
446
+ await handler(content);
447
+
448
+ // Acknowledge message
449
+ this.channel.ack(msg);
450
+ } catch (error) {
451
+ console.error('Error processing message:', error);
452
+
453
+ // Retry logic
454
+ const retryCount = (msg.properties.headers['x-retry-count'] || 0) + 1;
455
+
456
+ if (retryCount <= this.maxRetries) {
457
+ // Requeue with delay
458
+ setTimeout(() => {
459
+ this.channel.sendToQueue(
460
+ queue,
461
+ msg.content,
462
+ {
463
+ ...msg.properties,
464
+ headers: {
465
+ ...msg.properties.headers,
466
+ 'x-retry-count': retryCount
467
+ }
468
+ }
469
+ );
470
+ }, Math.pow(2, retryCount) * 1000); // Exponential backoff
471
+ } else {
472
+ // Send to DLQ
473
+ await this.sendToDeadLetter(msg, error);
474
+ }
475
+
476
+ // Reject without requeue
477
+ this.channel.nack(msg, false, false);
478
+ }
479
+ });
480
+ }
481
+
482
+ async sendToDeadLetter(msg, error) {
483
+ const dlq = `${msg.fields.routingKey}.dlq`;
484
+ await this.channel.sendToQueue(
485
+ dlq,
486
+ msg.content,
487
+ {
488
+ headers: {
489
+ 'x-death-reason': error.message,
490
+ 'x-original-queue': msg.fields.routingKey
491
+ }
492
+ }
493
+ );
494
+ }
495
+ }
496
+ ```
497
+
498
+ ## 보안 구현
499
+
500
+ ### 인증/인가 미들웨어
501
+ ```javascript
502
+ // JWT Authentication
503
+ class AuthMiddleware {
504
+ constructor(jwtService, userService) {
505
+ this.jwtService = jwtService;
506
+ this.userService = userService;
507
+ }
508
+
509
+ authenticate() {
510
+ return async (req, res, next) => {
511
+ try {
512
+ const token = this.extractToken(req);
513
+ if (!token) {
514
+ return res.status(401).json({
515
+ error: 'No authentication token provided'
516
+ });
517
+ }
518
+
519
+ const payload = await this.jwtService.verify(token);
520
+
521
+ // Token blacklist check
522
+ if (await this.isTokenBlacklisted(payload.jti)) {
523
+ return res.status(401).json({
524
+ error: 'Token has been revoked'
525
+ });
526
+ }
527
+
528
+ req.user = await this.userService.findById(payload.sub);
529
+ next();
530
+ } catch (error) {
531
+ if (error.name === 'TokenExpiredError') {
532
+ return res.status(401).json({
533
+ error: 'Token has expired'
534
+ });
535
+ }
536
+
537
+ return res.status(401).json({
538
+ error: 'Invalid authentication token'
539
+ });
540
+ }
541
+ };
542
+ }
543
+
544
+ authorize(...roles) {
545
+ return (req, res, next) => {
546
+ if (!req.user) {
547
+ return res.status(401).json({
548
+ error: 'Authentication required'
549
+ });
550
+ }
551
+
552
+ if (roles.length > 0 && !roles.includes(req.user.role)) {
553
+ return res.status(403).json({
554
+ error: 'Insufficient permissions'
555
+ });
556
+ }
557
+
558
+ next();
559
+ };
560
+ }
561
+
562
+ extractToken(req) {
563
+ const authHeader = req.headers.authorization;
564
+ if (authHeader && authHeader.startsWith('Bearer ')) {
565
+ return authHeader.substring(7);
566
+ }
567
+
568
+ return req.cookies?.accessToken;
569
+ }
570
+ }
571
+ ```
572
+
573
+ ## 테스팅 전략
574
+
575
+ ### 단위 테스트
576
+ ```javascript
577
+ describe('UserService', () => {
578
+ let userService;
579
+ let userRepository;
580
+ let emailService;
581
+
582
+ beforeEach(() => {
583
+ userRepository = {
584
+ findByEmail: jest.fn(),
585
+ create: jest.fn(),
586
+ update: jest.fn()
587
+ };
588
+
589
+ emailService = {
590
+ sendWelcomeEmail: jest.fn()
591
+ };
592
+
593
+ userService = new UserService(userRepository, emailService);
594
+ });
595
+
596
+ describe('createUser', () => {
597
+ it('should create user with hashed password', async () => {
598
+ const userData = {
599
+ email: 'test@example.com',
600
+ password: 'password123'
601
+ };
602
+
603
+ userRepository.findByEmail.mockResolvedValue(null);
604
+ userRepository.create.mockResolvedValue({
605
+ id: 'user-123',
606
+ ...userData,
607
+ password: 'hashed-password'
608
+ });
609
+
610
+ const user = await userService.createUser(userData);
611
+
612
+ expect(userRepository.findByEmail).toHaveBeenCalledWith(userData.email);
613
+ expect(userRepository.create).toHaveBeenCalledWith({
614
+ email: userData.email,
615
+ password: expect.stringMatching(/^\$2[aby]\$/)
616
+ });
617
+ expect(emailService.sendWelcomeEmail).toHaveBeenCalledWith(userData.email);
618
+ expect(user).not.toHaveProperty('password');
619
+ });
620
+
621
+ it('should throw error if email already exists', async () => {
622
+ userRepository.findByEmail.mockResolvedValue({ id: 'existing-user' });
623
+
624
+ await expect(
625
+ userService.createUser({ email: 'test@example.com', password: 'pass' })
626
+ ).rejects.toThrow('Email already registered');
627
+ });
628
+ });
629
+ });
630
+ ```
631
+
632
+ ### 통합 테스트
633
+ ```javascript
634
+ describe('API Integration Tests', () => {
635
+ let app;
636
+ let db;
637
+
638
+ beforeAll(async () => {
639
+ // Test database setup
640
+ db = await createTestDatabase();
641
+ app = createApp(db);
642
+ });
643
+
644
+ afterAll(async () => {
645
+ await db.close();
646
+ });
647
+
648
+ describe('POST /api/users', () => {
649
+ it('should create user and return 201', async () => {
650
+ const response = await request(app)
651
+ .post('/api/users')
652
+ .send({
653
+ email: 'newuser@example.com',
654
+ password: 'SecurePass123!',
655
+ profile: {
656
+ firstName: 'John',
657
+ lastName: 'Doe'
658
+ }
659
+ });
660
+
661
+ expect(response.status).toBe(201);
662
+ expect(response.headers.location).toMatch(/\/api\/users\/[\w-]+/);
663
+ expect(response.body).toMatchObject({
664
+ success: true,
665
+ data: {
666
+ email: 'newuser@example.com',
667
+ profile: {
668
+ firstName: 'John',
669
+ lastName: 'Doe'
670
+ }
671
+ }
672
+ });
673
+
674
+ // Verify database
675
+ const user = await db.query(
676
+ 'SELECT * FROM users WHERE email = $1',
677
+ ['newuser@example.com']
678
+ );
679
+ expect(user.rows).toHaveLength(1);
680
+ });
681
+ });
682
+ });
683
+ ```
684
+
685
+ ## 모니터링과 로깅
686
+
687
+ ### 구조화된 로깅
688
+ ```javascript
689
+ class Logger {
690
+ constructor(service) {
691
+ this.service = service;
692
+ this.winston = winston.createLogger({
693
+ format: winston.format.combine(
694
+ winston.format.timestamp(),
695
+ winston.format.errors({ stack: true }),
696
+ winston.format.json()
697
+ ),
698
+ defaultMeta: { service },
699
+ transports: [
700
+ new winston.transports.Console({
701
+ format: winston.format.simple()
702
+ }),
703
+ new winston.transports.File({
704
+ filename: 'error.log',
705
+ level: 'error'
706
+ }),
707
+ new winston.transports.File({
708
+ filename: 'combined.log'
709
+ })
710
+ ]
711
+ });
712
+ }
713
+
714
+ log(level, message, meta = {}) {
715
+ this.winston.log({
716
+ level,
717
+ message,
718
+ ...meta,
719
+ timestamp: new Date().toISOString()
720
+ });
721
+ }
722
+
723
+ // Request logging middleware
724
+ requestLogger() {
725
+ return (req, res, next) => {
726
+ const start = Date.now();
727
+
728
+ res.on('finish', () => {
729
+ const duration = Date.now() - start;
730
+
731
+ this.log('info', 'HTTP Request', {
732
+ method: req.method,
733
+ path: req.path,
734
+ statusCode: res.statusCode,
735
+ duration,
736
+ ip: req.ip,
737
+ userAgent: req.get('user-agent'),
738
+ userId: req.user?.id
739
+ });
740
+ });
741
+
742
+ next();
743
+ };
744
+ }
745
+ }
746
+ ```
747
+
748
+ ## 배포 전 체크리스트
749
+
750
+ ### 성능
751
+ - [ ] 데이터베이스 인덱스 최적화
752
+ - [ ] N+1 쿼리 제거
753
+ - [ ] 캐싱 전략 구현
754
+ - [ ] 비동기 처리 적용
755
+
756
+ ### 보안
757
+ - [ ] 환경 변수로 민감 정보 관리
758
+ - [ ] SQL Injection 방지
759
+ - [ ] Rate Limiting 설정
760
+ - [ ] CORS 정책 구성
761
+
762
+ ### 안정성
763
+ - [ ] 에러 처리 및 복구 메커니즘
764
+ - [ ] 서킷 브레이커 패턴
765
+ - [ ] 헬스체크 엔드포인트
766
+ - [ ] 우아한 종료(Graceful Shutdown)