sonamu 0.7.53 → 0.8.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.
Files changed (271) hide show
  1. package/dist/api/config.d.ts +9 -1
  2. package/dist/api/config.d.ts.map +1 -1
  3. package/dist/api/config.js +1 -1
  4. package/dist/api/sonamu.d.ts +21 -1
  5. package/dist/api/sonamu.d.ts.map +1 -1
  6. package/dist/api/sonamu.js +159 -65
  7. package/dist/auth/plugins/entity-definitions/anonymous.d.ts +10 -0
  8. package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -0
  9. package/dist/auth/plugins/entity-definitions/anonymous.js +23 -0
  10. package/dist/auth/plugins/entity-definitions/api-key.d.ts +9 -0
  11. package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -0
  12. package/dist/auth/plugins/entity-definitions/api-key.js +199 -0
  13. package/dist/auth/plugins/entity-definitions/index.d.ts +6 -0
  14. package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
  15. package/dist/auth/plugins/entity-definitions/index.js +20 -2
  16. package/dist/auth/plugins/entity-definitions/jwt.d.ts +9 -0
  17. package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -0
  18. package/dist/auth/plugins/entity-definitions/jwt.js +67 -0
  19. package/dist/auth/plugins/entity-definitions/organization.d.ts +9 -0
  20. package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -0
  21. package/dist/auth/plugins/entity-definitions/organization.js +424 -0
  22. package/dist/auth/plugins/entity-definitions/passkey.d.ts +10 -0
  23. package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -0
  24. package/dist/auth/plugins/entity-definitions/passkey.js +129 -0
  25. package/dist/auth/plugins/entity-definitions/sso.d.ts +10 -0
  26. package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -0
  27. package/dist/auth/plugins/entity-definitions/sso.js +110 -0
  28. package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
  29. package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
  30. package/dist/auth/plugins/entity-definitions/types.js +1 -1
  31. package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
  32. package/dist/auth/plugins/wrappers/admin.js +2 -4
  33. package/dist/auth/plugins/wrappers/anonymous.d.ts +18 -0
  34. package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -0
  35. package/dist/auth/plugins/wrappers/anonymous.js +26 -0
  36. package/dist/auth/plugins/wrappers/api-key.d.ts +18 -0
  37. package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -0
  38. package/dist/auth/plugins/wrappers/api-key.js +38 -0
  39. package/dist/auth/plugins/wrappers/index.d.ts +6 -0
  40. package/dist/auth/plugins/wrappers/index.d.ts.map +1 -1
  41. package/dist/auth/plugins/wrappers/index.js +7 -1
  42. package/dist/auth/plugins/wrappers/jwt.d.ts +18 -0
  43. package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -0
  44. package/dist/auth/plugins/wrappers/jwt.js +30 -0
  45. package/dist/auth/plugins/wrappers/organization.d.ts +18 -0
  46. package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -0
  47. package/dist/auth/plugins/wrappers/organization.js +67 -0
  48. package/dist/auth/plugins/wrappers/passkey.d.ts +18 -0
  49. package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -0
  50. package/dist/auth/plugins/wrappers/passkey.js +32 -0
  51. package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
  52. package/dist/auth/plugins/wrappers/phone-number.js +2 -4
  53. package/dist/auth/plugins/wrappers/sso.d.ts +853 -0
  54. package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -0
  55. package/dist/auth/plugins/wrappers/sso.js +36 -0
  56. package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
  57. package/dist/auth/plugins/wrappers/two-factor.js +2 -4
  58. package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
  59. package/dist/auth/plugins/wrappers/username.js +2 -4
  60. package/dist/bin/build-config.d.ts +2 -2
  61. package/dist/bin/build-config.js +6 -7
  62. package/dist/bin/cli.js +417 -32
  63. package/dist/bin/fixture.d.ts +27 -0
  64. package/dist/bin/fixture.d.ts.map +1 -0
  65. package/dist/bin/fixture.js +245 -0
  66. package/dist/cache/decorator.d.ts +4 -3
  67. package/dist/cache/decorator.d.ts.map +1 -1
  68. package/dist/cache/decorator.js +5 -4
  69. package/dist/cone/cone-generator.d.ts +33 -0
  70. package/dist/cone/cone-generator.d.ts.map +1 -0
  71. package/dist/cone/cone-generator.js +286 -0
  72. package/dist/database/_batch_update.d.ts.map +1 -1
  73. package/dist/database/_batch_update.js +16 -2
  74. package/dist/database/puri-subset.test-d.js +1 -1
  75. package/dist/database/puri-subset.types.d.ts +1 -1
  76. package/dist/database/puri-subset.types.d.ts.map +1 -1
  77. package/dist/database/puri-subset.types.js +1 -1
  78. package/dist/database/puri.d.ts +4 -0
  79. package/dist/database/puri.d.ts.map +1 -1
  80. package/dist/database/puri.js +20 -2
  81. package/dist/database/upsert-builder.d.ts.map +1 -1
  82. package/dist/database/upsert-builder.js +19 -3
  83. package/dist/dict/en.d.ts +15 -0
  84. package/dist/dict/en.d.ts.map +1 -1
  85. package/dist/dict/en.js +2 -1
  86. package/dist/dict/ko.d.ts +15 -0
  87. package/dist/dict/ko.d.ts.map +1 -1
  88. package/dist/dict/ko.js +2 -1
  89. package/dist/dict/rc-keys.d.ts +28 -0
  90. package/dist/dict/rc-keys.d.ts.map +1 -1
  91. package/dist/dict/rc-keys.js +31 -1
  92. package/dist/dict/sd.d.ts.map +1 -1
  93. package/dist/dict/sd.js +20 -4
  94. package/dist/entity/entity-manager.d.ts +298 -2
  95. package/dist/entity/entity-manager.d.ts.map +1 -1
  96. package/dist/entity/entity-manager.js +4 -1
  97. package/dist/entity/entity-template-cone.d.ts +14 -0
  98. package/dist/entity/entity-template-cone.d.ts.map +1 -0
  99. package/dist/entity/entity-template-cone.js +222 -0
  100. package/dist/entity/entity.d.ts +47 -2
  101. package/dist/entity/entity.d.ts.map +1 -1
  102. package/dist/entity/entity.js +161 -14
  103. package/dist/ssr/renderer.js +3 -3
  104. package/dist/syncer/api-parser.js +12 -1
  105. package/dist/syncer/checksum.d.ts +0 -14
  106. package/dist/syncer/checksum.d.ts.map +1 -1
  107. package/dist/syncer/checksum.js +1 -23
  108. package/dist/syncer/syncer-actions.d.ts.map +1 -1
  109. package/dist/syncer/syncer-actions.js +8 -2
  110. package/dist/syncer/syncer.d.ts +1 -1
  111. package/dist/syncer/syncer.d.ts.map +1 -1
  112. package/dist/syncer/syncer.js +17 -10
  113. package/dist/tasks/workflow-manager.d.ts +13 -1
  114. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  115. package/dist/tasks/workflow-manager.js +18 -1
  116. package/dist/template/entity-converter.js +4 -4
  117. package/dist/template/helpers.d.ts +10 -0
  118. package/dist/template/helpers.d.ts.map +1 -1
  119. package/dist/template/helpers.js +48 -1
  120. package/dist/template/implementations/entry-server.template.d.ts +1 -1
  121. package/dist/template/implementations/entry-server.template.js +7 -2
  122. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  123. package/dist/template/implementations/generated.template.js +5 -1
  124. package/dist/template/implementations/generated_http.template.d.ts +1 -0
  125. package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
  126. package/dist/template/implementations/generated_http.template.js +6 -2
  127. package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
  128. package/dist/template/implementations/generated_sso.template.js +29 -8
  129. package/dist/template/implementations/queries.template.d.ts.map +1 -1
  130. package/dist/template/implementations/queries.template.js +9 -1
  131. package/dist/template/implementations/sd.template.d.ts +1 -1
  132. package/dist/template/implementations/sd.template.d.ts.map +1 -1
  133. package/dist/template/implementations/sd.template.js +28 -4
  134. package/dist/template/implementations/services.template.d.ts.map +1 -1
  135. package/dist/template/implementations/services.template.js +12 -12
  136. package/dist/template/implementations/view_form.template.d.ts +11 -7
  137. package/dist/template/implementations/view_form.template.d.ts.map +1 -1
  138. package/dist/template/implementations/view_form.template.js +97 -87
  139. package/dist/template/implementations/view_list.template.d.ts +3 -3
  140. package/dist/template/implementations/view_list.template.d.ts.map +1 -1
  141. package/dist/template/implementations/view_list.template.js +115 -109
  142. package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
  143. package/dist/template/implementations/view_search_input.template.js +18 -14
  144. package/dist/template/zod-converter.d.ts.map +1 -1
  145. package/dist/template/zod-converter.js +95 -7
  146. package/dist/testing/_relation-graph.js +1 -1
  147. package/dist/testing/data-explorer.d.ts +61 -0
  148. package/dist/testing/data-explorer.d.ts.map +1 -0
  149. package/dist/testing/data-explorer.js +274 -0
  150. package/dist/testing/faker-mappings.d.ts +20 -0
  151. package/dist/testing/faker-mappings.d.ts.map +1 -0
  152. package/dist/testing/faker-mappings.js +421 -0
  153. package/dist/testing/fixture-generator.d.ts +161 -0
  154. package/dist/testing/fixture-generator.d.ts.map +1 -0
  155. package/dist/testing/fixture-generator.js +954 -0
  156. package/dist/testing/fixture-manager.d.ts +6 -1
  157. package/dist/testing/fixture-manager.d.ts.map +1 -1
  158. package/dist/testing/fixture-manager.js +72 -4
  159. package/dist/testing/index.d.ts +3 -0
  160. package/dist/testing/index.d.ts.map +1 -1
  161. package/dist/testing/index.js +4 -1
  162. package/dist/types/types.d.ts +1520 -26
  163. package/dist/types/types.d.ts.map +1 -1
  164. package/dist/types/types.js +136 -22
  165. package/dist/ui/ai-client.d.ts.map +1 -1
  166. package/dist/ui/ai-client.js +9 -4
  167. package/dist/ui/api.d.ts.map +1 -1
  168. package/dist/ui/api.js +303 -24
  169. package/dist/ui-web/assets/index-CsUr-_pV.js +254 -0
  170. package/dist/ui-web/assets/index-T42zzs1K.css +1 -0
  171. package/dist/ui-web/index.html +2 -2
  172. package/dist/utils/fs-utils.d.ts +2 -1
  173. package/dist/utils/fs-utils.d.ts.map +1 -1
  174. package/dist/utils/fs-utils.js +14 -3
  175. package/package.json +19 -11
  176. package/src/api/config.ts +12 -1
  177. package/src/api/sonamu.ts +179 -65
  178. package/src/auth/plugins/entity-definitions/anonymous.ts +17 -0
  179. package/src/auth/plugins/entity-definitions/api-key.ts +93 -0
  180. package/src/auth/plugins/entity-definitions/index.ts +18 -0
  181. package/src/auth/plugins/entity-definitions/jwt.ts +35 -0
  182. package/src/auth/plugins/entity-definitions/organization.ts +215 -0
  183. package/src/auth/plugins/entity-definitions/passkey.ts +64 -0
  184. package/src/auth/plugins/entity-definitions/sso.ts +62 -0
  185. package/src/auth/plugins/entity-definitions/types.ts +11 -1
  186. package/src/auth/plugins/wrappers/admin.ts +1 -3
  187. package/src/auth/plugins/wrappers/anonymous.ts +30 -0
  188. package/src/auth/plugins/wrappers/api-key.ts +42 -0
  189. package/src/auth/plugins/wrappers/index.ts +6 -0
  190. package/src/auth/plugins/wrappers/jwt.ts +34 -0
  191. package/src/auth/plugins/wrappers/organization.ts +73 -0
  192. package/src/auth/plugins/wrappers/passkey.ts +36 -0
  193. package/src/auth/plugins/wrappers/phone-number.ts +1 -3
  194. package/src/auth/plugins/wrappers/sso.ts +40 -0
  195. package/src/auth/plugins/wrappers/two-factor.ts +1 -3
  196. package/src/auth/plugins/wrappers/username.ts +1 -3
  197. package/src/bin/build-config.ts +6 -6
  198. package/src/bin/cli.ts +452 -31
  199. package/src/bin/fixture.ts +302 -0
  200. package/src/cache/decorator.ts +4 -3
  201. package/src/cone/cone-generator.ts +363 -0
  202. package/src/database/_batch_update.ts +11 -0
  203. package/src/database/puri-subset.test-d.ts +13 -13
  204. package/src/database/puri-subset.types.ts +1 -1
  205. package/src/database/puri.ts +43 -1
  206. package/src/database/upsert-builder.ts +16 -2
  207. package/src/dict/en.ts +1 -0
  208. package/src/dict/ko.ts +1 -0
  209. package/src/dict/rc-keys.ts +32 -0
  210. package/src/dict/sd.ts +23 -3
  211. package/src/entity/entity-manager.ts +4 -0
  212. package/src/entity/entity-template-cone.ts +298 -0
  213. package/src/entity/entity.ts +189 -13
  214. package/src/shared/app.shared.ts.txt +5 -0
  215. package/src/shared/web.shared.ts.txt +9 -5
  216. package/src/skills/project/README.md +21 -0
  217. package/src/skills/project/architecture.md +373 -0
  218. package/src/skills/project/business-logic.md +270 -0
  219. package/src/skills/project/requirements.md +160 -0
  220. package/src/skills/sonamu/SKILL.md +168 -3
  221. package/src/skills/sonamu/api.md +102 -0
  222. package/src/skills/sonamu/database.md +220 -1
  223. package/src/skills/sonamu/entity-relations.md +89 -1
  224. package/src/skills/sonamu/fixture-cli.md +501 -0
  225. package/src/skills/sonamu/frontend.md +214 -0
  226. package/src/skills/sonamu/i18n.md +95 -0
  227. package/src/skills/sonamu/model.md +153 -0
  228. package/src/skills/sonamu/project-init.md +178 -8
  229. package/src/skills/sonamu/scaffolding.md +112 -0
  230. package/src/skills/sonamu/subset.md +9 -3
  231. package/src/skills/sonamu/testing.md +287 -2
  232. package/src/skills/sonamu/workflow.md +70 -5
  233. package/src/ssr/renderer.ts +2 -2
  234. package/src/syncer/api-parser.ts +12 -0
  235. package/src/syncer/checksum.ts +0 -38
  236. package/src/syncer/syncer-actions.ts +7 -1
  237. package/src/syncer/syncer.ts +16 -5
  238. package/src/tasks/workflow-manager.ts +29 -8
  239. package/src/template/entity-converter.ts +3 -3
  240. package/src/template/helpers.ts +49 -0
  241. package/src/template/implementations/entry-server.template.ts +1 -1
  242. package/src/template/implementations/generated.template.ts +4 -0
  243. package/src/template/implementations/generated_http.template.ts +1 -0
  244. package/src/template/implementations/generated_sso.template.ts +40 -11
  245. package/src/template/implementations/queries.template.ts +8 -0
  246. package/src/template/implementations/sd.template.ts +22 -3
  247. package/src/template/implementations/services.template.ts +11 -10
  248. package/src/template/implementations/view_form.template.ts +111 -101
  249. package/src/template/implementations/view_list.template.ts +120 -119
  250. package/src/template/implementations/view_search_input.template.ts +17 -13
  251. package/src/template/zod-converter.ts +103 -6
  252. package/src/testing/_relation-graph.ts +1 -1
  253. package/src/testing/data-explorer.ts +427 -0
  254. package/src/testing/faker-mappings.ts +434 -0
  255. package/src/testing/fixture-generator.ts +1166 -0
  256. package/src/testing/fixture-manager.ts +91 -6
  257. package/src/testing/index.ts +3 -0
  258. package/src/types/types.ts +222 -26
  259. package/src/ui/ai-client.ts +9 -1
  260. package/src/ui/api.ts +429 -23
  261. package/src/utils/fs-utils.ts +14 -1
  262. package/dist/template/implementations/view_enums_select.template.d.ts +0 -17
  263. package/dist/template/implementations/view_enums_select.template.d.ts.map +0 -1
  264. package/dist/template/implementations/view_enums_select.template.js +0 -62
  265. package/dist/template/implementations/view_id_async_select.template.d.ts +0 -17
  266. package/dist/template/implementations/view_id_async_select.template.d.ts.map +0 -1
  267. package/dist/template/implementations/view_id_async_select.template.js +0 -125
  268. package/dist/ui-web/assets/index-Bd_2AkLb.css +0 -1
  269. package/dist/ui-web/assets/index-BpSbhQWo.js +0 -225
  270. package/src/template/implementations/view_enums_select.template.ts +0 -65
  271. package/src/template/implementations/view_id_async_select.template.ts +0 -139
@@ -207,6 +207,64 @@ Task (과제)
207
207
 
208
208
  **주의**: `author_id`를 props에 직접 정의하지 말 것 (자동 생성됨)
209
209
 
210
+ ### 코드에서 FK 사용하기
211
+
212
+ BelongsToOne 관계는 `{name}_id` 컬럼을 자동 생성하므로, Model이나 FixtureGenerator 등 코드에서 이를 직접 다룰 때는 올바른 필드명을 사용해야 합니다.
213
+
214
+ **올바른 패턴:**
215
+ ```typescript
216
+ // Entity 정의
217
+ {
218
+ "type": "relation",
219
+ "name": "company",
220
+ "with": "Company",
221
+ "relationType": "BelongsToOne"
222
+ }
223
+
224
+ // Model save() 또는 FixtureGenerator에서
225
+ const department = {
226
+ name: "개발팀",
227
+ company_id: 1 // ✓ CORRECT: {relation_name}_id 형태
228
+ };
229
+
230
+ await puri.ubRegister("departments", department);
231
+ ```
232
+
233
+ **잘못된 패턴 (흔한 실수):**
234
+ ```typescript
235
+ // ✗ WRONG: relation name을 직접 사용
236
+ const department = {
237
+ name: "개발팀",
238
+ company: 1 // FK가 설정되지 않음! company_id가 NULL로 저장됨
239
+ };
240
+
241
+ // ✗ WRONG: 객체로 전달
242
+ const department = {
243
+ name: "개발팀",
244
+ company: { id: 1 } // FK가 설정되지 않음!
245
+ };
246
+ ```
247
+
248
+ **FixtureGenerator 예시:**
249
+ ```typescript
250
+ // fixture-generator.ts 내부
251
+ if (isBelongsToOneRelationProp(prop) ||
252
+ (isOneToOneRelationProp(prop) && prop.hasJoinColumn)) {
253
+ const relationValue = await this.generateRelationValue(entity, prop, context);
254
+
255
+ // ✓ CORRECT: {prop.name}_id로 FK 설정
256
+ fixture[`${prop.name}_id`] = relationValue;
257
+ } else {
258
+ fixture[prop.name] = relationValue;
259
+ }
260
+ ```
261
+
262
+ **핵심:**
263
+ - Entity JSON: `"name": "company"` (relation 이름)
264
+ - DB 컬럼: `company_id` (자동 생성)
265
+ - TypeScript 코드: `company_id` 사용 (FK 설정)
266
+ - Entity subset: `"company.id"` 형태 (FieldExpr)
267
+
210
268
  ## HasMany (1:N) - 역방향 조회용
211
269
 
212
270
  **상황**: User의 Posts를 조회하고 싶을 때
@@ -294,7 +352,37 @@ HasMany 관계는 자동으로 **DataLoader 패턴**으로 최적화됩니다:
294
352
 
295
353
  **필수**: `joinTable`, `onUpdate`, `onDelete`
296
354
 
297
- **joinTable 권장 규칙**: 알파벳 순 (`posts__tags` 권장, `tags__posts`도 작동함)
355
+ ### ManyToMany 네이밍 규칙
356
+
357
+ **joinTable (조인 테이블명)**: 언더바 **2개** 사용
358
+ ```
359
+ User ↔ Role → user__roles
360
+ Post ↔ Tag → posts__tags (알파벳 순 권장)
361
+ ```
362
+
363
+ **joinColumn (조인 테이블 컬럼명)**: 언더바 **1개** 사용
364
+ ```
365
+ user__roles 테이블:
366
+ - user_id (언더바 1개)
367
+ - role_id (언더바 1개)
368
+ ```
369
+
370
+ **예시**:
371
+ ```typescript
372
+ // Entity: User
373
+ {
374
+ "name": "roles",
375
+ "relationType": "ManyToMany",
376
+ "with": "Role",
377
+ "joinTable": "user__roles" // 언더바 2개
378
+ }
379
+
380
+ // Model save()에서 사용:
381
+ puri.ubRegister("user__roles", {
382
+ user_id, // 언더바 1개
383
+ role_id // 언더바 1개
384
+ });
385
+ ```
298
386
 
299
387
  ## 자기 참조
300
388
 
@@ -0,0 +1,501 @@
1
+ ---
2
+ name: sonamu-fixture-cli
3
+ description: Sonamu Fixture CLI 사용 가이드. fixture gen/fetch/explore 명령어로 테스트 데이터 생성 및 관리. Use when creating or managing fixture data.
4
+ ---
5
+
6
+ # Fixture CLI 사용 가이드
7
+
8
+ Sonamu는 테스트용 fixture 데이터를 생성하고 관리하기 위한 CLI 명령어를 제공합니다.
9
+
10
+ **참고**: Fixture 생성 팁은 `testing.md`의 "Fixture 데이터 생성 팁" 섹션 참조
11
+
12
+ ---
13
+
14
+ ## 3-Tier DB 구조 이해 (필수)
15
+
16
+ Sonamu는 3단계 데이터베이스 구조를 사용합니다. **이 구조를 이해하지 못하면 fixture 명령어 사용 시 혼란이 발생합니다.**
17
+
18
+ ```
19
+ production/development master (실제 DB)
20
+ ↓ (fixture fetch)
21
+ project_fixture (fixture DB)
22
+ ↓ (fixture sync)
23
+ project_test (test DB)
24
+ ```
25
+
26
+ ### DB별 역할
27
+
28
+ | DB | 용도 | 데이터 출처 |
29
+ |----|------|-----------|
30
+ | `project` | 운영/개발 실제 DB | 실제 사용자 데이터 |
31
+ | `project_fixture` | 테스트용 참조 데이터 저장소 | fetch로 가져오거나 gen으로 생성 |
32
+ | `project_test` | 테스트 실행 환경 | fixture에서 sync |
33
+
34
+ ### 명령어별 DB 사용
35
+
36
+ | 명령어 | sourceDb | targetDb | 설명 |
37
+ |--------|----------|----------|------|
38
+ | `fixture gen` | fixture DB | fixture DB | fixture DB 내부에서 참조 관계 해결 및 생성 |
39
+ | `fixture fetch` | production master | fixture DB | 실제 DB → fixture DB로 import |
40
+ | `fixture sync` | fixture DB | test DB | fixture DB → test DB로 동기화 (기존) |
41
+
42
+ **CRITICAL**: sourceDb와 targetDb를 잘못 설정하면 FK 참조 오류가 발생합니다.
43
+
44
+ ---
45
+
46
+ ## CLI 명령어
47
+
48
+ ### 1. fixture gen - 새로운 fixture 생성
49
+
50
+ faker 기반으로 새로운 테스트 데이터를 생성합니다.
51
+
52
+ #### 기본 사용법
53
+
54
+ ```bash
55
+ # 대화형 모드 (권장)
56
+ pnpm sonamu fixture gen
57
+
58
+ # Entity 지정
59
+ pnpm sonamu fixture gen --include User --count 10
60
+
61
+ # 여러 Entity 지정
62
+ pnpm sonamu fixture gen --include User,Post,Comment --count 5
63
+
64
+ # 전체 Entity
65
+ pnpm sonamu fixture gen --all --count 3
66
+
67
+ # 전체에서 일부 제외
68
+ pnpm sonamu fixture gen --all --exclude Admin,Log --count 3
69
+ ```
70
+
71
+ #### 저장 옵션
72
+
73
+ ```bash
74
+ # DB 저장 (기본값)
75
+ pnpm sonamu fixture gen --include User --count 10 --save-to db
76
+
77
+ # 파일로 저장 (테이블명.json)
78
+ pnpm sonamu fixture gen --include User --count 10 --save-to file
79
+ # → test/fixtures/users.json
80
+
81
+ # 파일명 지정
82
+ pnpm sonamu fixture gen --include User --count 10 --save-to file:my-users.json
83
+ # → test/fixtures/my-users.json
84
+
85
+ # 출력만 (저장 안 함)
86
+ pnpm sonamu fixture gen --include User --count 10 --save-to none
87
+ ```
88
+
89
+ #### 옵션
90
+
91
+ - `--include <entities>`: 생성할 Entity 목록 (쉼표 구분)
92
+ - `--all`: 모든 Entity
93
+ - `--exclude <entities>`: --all과 함께 사용, 제외할 Entity
94
+ - `--count <number>`: 각 Entity별 생성 개수 (기본값: 5)
95
+ - `--save-to <target>`: 저장 방식 - `db` | `file` | `file:name.json` | `none`
96
+
97
+ ---
98
+
99
+ ### 2. fixture fetch - 실제 DB에서 import
100
+
101
+ 실제 운영/개발 DB에서 데이터를 가져와 fixture DB에 저장합니다.
102
+
103
+ #### 기본 사용법
104
+
105
+ ```bash
106
+ # 대화형 모드
107
+ pnpm sonamu fixture fetch
108
+
109
+ # 최근 데이터 가져오기
110
+ pnpm sonamu fixture fetch --include User --strategy recent --limit 10
111
+
112
+ # 여러 Entity
113
+ pnpm sonamu fixture fetch --include User,Post --strategy sample --limit 5
114
+
115
+ # 전체 Entity
116
+ pnpm sonamu fixture fetch --all --strategy recent --limit 3
117
+ ```
118
+
119
+ #### 전략 (Strategy)
120
+
121
+ | 전략 | 설명 | 예시 |
122
+ |------|------|------|
123
+ | `recent` | 최근 데이터 (created_at 기준) | `--strategy recent --limit 10` |
124
+ | `sample` | 균등 샘플링 | `--strategy sample --limit 10` |
125
+ | `random` | 랜덤 샘플링 | `--strategy random --limit 10` |
126
+
127
+ **CRITICAL**: fetch는 관련 데이터를 **재귀적으로 가져옵니다** (maxDepth: 2)
128
+ - User를 fetch하면 → User의 department, institution도 함께 import
129
+ - Post를 fetch하면 → Post의 author(User)도 함께 import
130
+
131
+ #### 옵션
132
+
133
+ - `--include <entities>`: import할 Entity 목록
134
+ - `--all`: 모든 Entity
135
+ - `--exclude <entities>`: --all과 함께 사용, 제외할 Entity
136
+ - `--strategy <strategy>`: 조회 전략 - `recent` | `sample` | `random` (기본값: recent)
137
+ - `--limit <number>`: 각 Entity별 조회 개수 (기본값: 10)
138
+
139
+ ---
140
+
141
+ ### 3. fixture explore - 데이터 조회 (저장 안 함)
142
+
143
+ 실제 DB의 데이터를 조회하여 콘솔에 출력합니다. **저장하지 않고 조회만** 합니다.
144
+
145
+ #### 기본 사용법
146
+
147
+ ```bash
148
+ # 대화형 모드
149
+ pnpm sonamu fixture explore
150
+
151
+ # 최근 User 조회
152
+ pnpm sonamu fixture explore --include User --strategy recent --limit 10
153
+
154
+ # 샘플링
155
+ pnpm sonamu fixture explore --include Department --strategy sample --limit 5
156
+ ```
157
+
158
+ #### 언제 사용하나?
159
+
160
+ - 실제 DB에 어떤 데이터가 있는지 빠르게 확인
161
+ - fixture fetch 전에 미리 확인
162
+ - 데이터 분포 파악
163
+
164
+ ---
165
+
166
+ ## 실전 사용 시나리오
167
+
168
+ ### 시나리오 1: 빈 DB에서 시작
169
+
170
+ ```bash
171
+ # 1. 기본 fixture 생성
172
+ pnpm sonamu fixture gen --all --exclude Admin,Log --count 5
173
+
174
+ # 2. fixture → test DB 동기화
175
+ pnpm sonamu fixture sync
176
+
177
+ # 3. 테스트 실행
178
+ pnpm test
179
+ ```
180
+
181
+ ### 시나리오 2: 실제 데이터 기반 테스트
182
+
183
+ ```bash
184
+ # 1. 실제 DB에서 최근 데이터 가져오기
185
+ pnpm sonamu fixture fetch --include User,Department --strategy recent --limit 20
186
+
187
+ # 2. 부족한 데이터 추가 생성
188
+ pnpm sonamu fixture gen --include Post,Comment --count 50
189
+
190
+ # 3. fixture → test DB 동기화
191
+ pnpm sonamu fixture sync
192
+
193
+ # 4. 테스트 실행
194
+ pnpm test
195
+ ```
196
+
197
+ ### 시나리오 3: 특정 시나리오 테스트 준비
198
+
199
+ ```bash
200
+ # 1. 특정 Entity만 생성
201
+ pnpm sonamu fixture gen --include User --count 3
202
+
203
+ # 2. 대화형으로 추가 생성
204
+ pnpm sonamu fixture gen
205
+ # ? Fixture를 생성할 Entity를 선택하세요: Post, Comment 선택
206
+ # ? 각 Entity별 생성 개수: 10
207
+
208
+ # 3. 파일로 저장 (버전 관리)
209
+ pnpm sonamu fixture gen --include User --count 10 --save-to file
210
+ # → test/fixtures/users.json 생성
211
+
212
+ # 4. fixture sync
213
+ pnpm sonamu fixture sync
214
+ ```
215
+
216
+ ---
217
+
218
+ ## 실전 팁
219
+
220
+ ### 1. 한국어 데이터 자동 생성
221
+
222
+ FixtureGenerator는 특정 필드명에 대해 한국어 데이터를 자동으로 생성합니다:
223
+
224
+ **자동 한국어 생성 필드**:
225
+ - `name`, `username`: 한국 사람 이름 (`fakerKO.person.fullName()`)
226
+ - Entity가 `Department`이고 prop이 `name`: 한국 부서명
227
+
228
+ **예시 결과**:
229
+ ```typescript
230
+ // User
231
+ { name: "김민준", username: "이서연" }
232
+
233
+ // Department
234
+ { name: "개발팀 1팀", name: "글로벌 마케팅팀" }
235
+ ```
236
+
237
+ **커스터마이징**:
238
+ ```typescript
239
+ // fixture-generator.ts에서 수정
240
+ if (entity?.id === "Department" && prop.name === "name") {
241
+ const departments = ["개발팀", "기획팀", "마케팅팀", "영업팀"];
242
+ // ...
243
+ }
244
+ ```
245
+
246
+ ### 2. Unique Constraint 처리
247
+
248
+ unique constraint가 있는 필드는 중복 방지 전략이 필요합니다.
249
+
250
+ **문제 상황**:
251
+ ```sql
252
+ -- departments 테이블
253
+ UNIQUE (company_id, name)
254
+ ```
255
+
256
+ **해결: 자동 변형**
257
+ ```typescript
258
+ // 같은 company_id에 "개발팀"이 여러 번 생성되지 않도록
259
+ // 70% 확률로 prefix/suffix 자동 추가
260
+
261
+ // 결과:
262
+ "개발팀" // 30%
263
+ "개발팀 1팀" // 20%
264
+ "개발팀 본부" // 20%
265
+ "글로벌 개발팀" // 30%
266
+ ```
267
+
268
+ **구현 위치**: `fixture-generator.ts`의 `generateDefaultValue()`
269
+
270
+ ### 3. BelongsToOne FK 설정
271
+
272
+ BelongsToOne 관계는 `{name}_id` 컬럼을 자동 생성하므로, 코드에서도 `_id` 접미사를 사용해야 합니다.
273
+
274
+ **Entity 정의**:
275
+ ```json
276
+ {
277
+ "type": "relation",
278
+ "name": "company",
279
+ "with": "Company",
280
+ "relationType": "BelongsToOne"
281
+ }
282
+ ```
283
+
284
+ **FixtureGenerator 내부**:
285
+ ```typescript
286
+ // ✓ CORRECT
287
+ fixture[`${prop.name}_id`] = relationValue; // company_id
288
+
289
+ // ✗ WRONG
290
+ fixture[prop.name] = relationValue; // company (FK가 NULL로 저장됨!)
291
+ ```
292
+
293
+ **실수하기 쉬운 이유**:
294
+ - Entity JSON에서는 `name: "company"`로 정의
295
+ - DB 컬럼은 `company_id`로 자동 생성
296
+ - 코드에서는 `company_id` 사용해야 함
297
+
298
+ ### 4. 순서 문제 해결
299
+
300
+ fixture gen은 **자동으로 의존성 순서를 정렬**합니다 (FixtureManager의 RelationGraph 사용).
301
+
302
+ **예시**:
303
+ ```bash
304
+ # Department는 Company를 참조하지만 순서 걱정 불필요
305
+ pnpm sonamu fixture gen --include Department,Company --count 5
306
+
307
+ # 내부적으로:
308
+ # 1. Company 먼저 생성 (FK 없음)
309
+ # 2. Department 생성 (company_id 참조)
310
+ ```
311
+
312
+ **주의**: 순환 참조는 경고 발생
313
+
314
+ ### 5. DB 시퀀스 리셋
315
+
316
+ fixture 생성 후 ID 시퀀스가 맞지 않을 수 있습니다.
317
+
318
+ **확인**:
319
+ ```bash
320
+ PGPASSWORD=1234 psql -h 0.0.0.0 -U postgres -d project_fixture -c "
321
+ SELECT sequencename, last_value
322
+ FROM pg_sequences
323
+ WHERE schemaname = 'public'
324
+ ORDER BY sequencename;
325
+ "
326
+ ```
327
+
328
+ **리셋**:
329
+ ```sql
330
+ -- 각 테이블마다
331
+ SELECT setval('departments_id_seq', (SELECT MAX(id) FROM departments), true);
332
+ SELECT setval('companies_id_seq', (SELECT MAX(id) FROM companies), true);
333
+ ```
334
+
335
+ **자동화**:
336
+ ```bash
337
+ # 모든 시퀀스 리셋 스크립트
338
+ PGPASSWORD=1234 psql -h 0.0.0.0 -U postgres -d project_fixture -c "
339
+ SELECT 'SELECT setval(''' || sequencename || ''', (SELECT COALESCE(MAX(id), 1) FROM ' ||
340
+ replace(sequencename, '_id_seq', '') || '), true);'
341
+ FROM pg_sequences
342
+ WHERE schemaname = 'public' AND sequencename LIKE '%_id_seq';
343
+ " | grep SELECT | PGPASSWORD=1234 psql -h 0.0.0.0 -U postgres -d project_fixture
344
+ ```
345
+
346
+ ### 6. 파일 저장 활용
347
+
348
+ 파일로 저장하면 **버전 관리**가 가능합니다.
349
+
350
+ ```bash
351
+ # 1. 파일로 저장
352
+ pnpm sonamu fixture gen --include User --count 10 --save-to file
353
+ # → test/fixtures/users.json
354
+
355
+ # 2. git에 커밋
356
+ git add test/fixtures/users.json
357
+ git commit -m "Add user fixtures for testing"
358
+
359
+ # 3. 다른 개발자도 동일한 데이터로 테스트 가능
360
+ ```
361
+
362
+ **언제 사용?**
363
+ - CI/CD 환경에서 일관된 테스트 데이터 필요
364
+ - 특정 시나리오 재현
365
+ - 팀원 간 테스트 데이터 공유
366
+
367
+ ---
368
+
369
+ ## 고급: cone 메타데이터 (선택사항)
370
+
371
+ Entity JSON에 `cone` 메타데이터를 추가하면 fixture 생성을 더욱 세밀하게 제어할 수 있습니다.
372
+
373
+ ### dataSource - 참조 전략 지정
374
+
375
+ ```json
376
+ {
377
+ "name": "Post",
378
+ "props": [
379
+ {
380
+ "name": "author",
381
+ "type": "relation",
382
+ "with": "User",
383
+ "relationType": "BelongsToOne",
384
+ "cone": {
385
+ "dataSource": {
386
+ "strategy": "recent",
387
+ "config": { "limit": 5 }
388
+ }
389
+ }
390
+ }
391
+ ]
392
+ }
393
+ ```
394
+
395
+ **지원 전략**:
396
+ - `sample`: 균등 샘플링
397
+ - `recent`: 최근 데이터 (created_at 기준)
398
+ - `random`: 랜덤 샘플링
399
+ - `ids`: 특정 ID 지정
400
+ - `query`: 사용자 정의 쿼리
401
+ - `file`: 파일에서 로드
402
+
403
+ ### fixtureGenerator - 커스텀 생성 로직
404
+
405
+ ```json
406
+ {
407
+ "name": "email",
408
+ "type": "string",
409
+ "cone": {
410
+ "fixtureGenerator": "faker.internet.email()"
411
+ }
412
+ }
413
+ ```
414
+
415
+ **보안 주의**: eval 사용으로 인한 보안 위험 (신뢰할 수 있는 표현식만 사용)
416
+
417
+ ### fixtureDefault - 기본값 지정
418
+
419
+ ```json
420
+ {
421
+ "name": "status",
422
+ "type": "string",
423
+ "cone": {
424
+ "fixtureDefault": "active"
425
+ }
426
+ }
427
+ ```
428
+
429
+ ### fixtureHint - 생성 힌트 (문서용)
430
+
431
+ ```json
432
+ {
433
+ "name": "phone",
434
+ "type": "string",
435
+ "cone": {
436
+ "fixtureHint": "010-XXXX-XXXX 형식의 한국 전화번호"
437
+ }
438
+ }
439
+ ```
440
+
441
+ **용도**:
442
+ - AI가 fixture 생성 시 참고
443
+ - 개발자에게 생성 패턴 설명
444
+ - 길이 제한 없음 (짧은 패턴 또는 긴 설명 모두 가능)
445
+
446
+ ---
447
+
448
+ ## 문제 해결
449
+
450
+ ### 문제 1: "Cannot generate non-nullable relation without dataSource"
451
+
452
+ **원인**: BelongsToOne relation이 nullable이 아닌데 참조할 데이터가 없음
453
+
454
+ **해결**:
455
+ ```bash
456
+ # 참조 Entity를 먼저 생성
457
+ pnpm sonamu fixture gen --include Company --count 5
458
+
459
+ # 그 다음 Department 생성
460
+ pnpm sonamu fixture gen --include Department --count 10
461
+ ```
462
+
463
+ 또는:
464
+ ```bash
465
+ # 함께 생성 (자동 순서 정렬)
466
+ pnpm sonamu fixture gen --include Company,Department --count 5
467
+ ```
468
+
469
+ ### 문제 2: "duplicate key value violates unique constraint"
470
+
471
+ **원인**: unique constraint가 있는 필드에 중복 값 생성
472
+
473
+ **해결**:
474
+ 1. `fixture-generator.ts`에서 해당 Entity의 필드별 생성 로직 수정
475
+ 2. prefix/suffix 추가 또는 UUID 사용
476
+ 3. count 줄이기
477
+
478
+ ### 문제 3: FK 참조 오류 "violates foreign key constraint"
479
+
480
+ **원인**: sourceDb와 targetDb 설정 문제
481
+
482
+ **확인**:
483
+ ```typescript
484
+ // fixture.ts 확인
485
+ const fixtureDb = createKnexInstance(Sonamu.dbConfig.fixture);
486
+ const generator = new FixtureGenerator(fixtureDb, fixtureDb, "fixture", EntityManager);
487
+ ```
488
+
489
+ **올바른 설정**:
490
+ - `fixture gen`: sourceDb = fixtureDb, targetDb = fixtureDb
491
+ - `fixture fetch`: sourceDb = production, targetDb = fixtureDb
492
+
493
+ ---
494
+
495
+ ## 참고 자료
496
+
497
+ - **3-Tier DB 구조**: `database.md` "3-Tier DB 구조" 섹션
498
+ - **Fixture 생성 팁**: `testing.md` "Fixture 데이터 생성 팁" 섹션
499
+ - **BelongsToOne FK**: `entity-relations.md` "코드에서 FK 사용하기" 섹션
500
+ - **실제 구현**: `modules/sonamu/src/bin/fixture.ts`
501
+ - **생성 로직**: `modules/sonamu/src/testing/fixture-generator.ts`