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.
- package/dist/api/config.d.ts +9 -1
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/sonamu.d.ts +21 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +159 -65
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/anonymous.js +23 -0
- package/dist/auth/plugins/entity-definitions/api-key.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/api-key.js +199 -0
- package/dist/auth/plugins/entity-definitions/index.d.ts +6 -0
- package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/index.js +20 -2
- package/dist/auth/plugins/entity-definitions/jwt.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/jwt.js +67 -0
- package/dist/auth/plugins/entity-definitions/organization.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/organization.js +424 -0
- package/dist/auth/plugins/entity-definitions/passkey.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/passkey.js +129 -0
- package/dist/auth/plugins/entity-definitions/sso.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/sso.js +110 -0
- package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/types.js +1 -1
- package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/admin.js +2 -4
- package/dist/auth/plugins/wrappers/anonymous.d.ts +18 -0
- package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/anonymous.js +26 -0
- package/dist/auth/plugins/wrappers/api-key.d.ts +18 -0
- package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/api-key.js +38 -0
- package/dist/auth/plugins/wrappers/index.d.ts +6 -0
- package/dist/auth/plugins/wrappers/index.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/index.js +7 -1
- package/dist/auth/plugins/wrappers/jwt.d.ts +18 -0
- package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/jwt.js +30 -0
- package/dist/auth/plugins/wrappers/organization.d.ts +18 -0
- package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/organization.js +67 -0
- package/dist/auth/plugins/wrappers/passkey.d.ts +18 -0
- package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/passkey.js +32 -0
- package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/phone-number.js +2 -4
- package/dist/auth/plugins/wrappers/sso.d.ts +853 -0
- package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/sso.js +36 -0
- package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/two-factor.js +2 -4
- package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/username.js +2 -4
- package/dist/bin/build-config.d.ts +2 -2
- package/dist/bin/build-config.js +6 -7
- package/dist/bin/cli.js +417 -32
- package/dist/bin/fixture.d.ts +27 -0
- package/dist/bin/fixture.d.ts.map +1 -0
- package/dist/bin/fixture.js +245 -0
- package/dist/cache/decorator.d.ts +4 -3
- package/dist/cache/decorator.d.ts.map +1 -1
- package/dist/cache/decorator.js +5 -4
- package/dist/cone/cone-generator.d.ts +33 -0
- package/dist/cone/cone-generator.d.ts.map +1 -0
- package/dist/cone/cone-generator.js +286 -0
- package/dist/database/_batch_update.d.ts.map +1 -1
- package/dist/database/_batch_update.js +16 -2
- package/dist/database/puri-subset.test-d.js +1 -1
- package/dist/database/puri-subset.types.d.ts +1 -1
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-subset.types.js +1 -1
- package/dist/database/puri.d.ts +4 -0
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +20 -2
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +19 -3
- package/dist/dict/en.d.ts +15 -0
- package/dist/dict/en.d.ts.map +1 -1
- package/dist/dict/en.js +2 -1
- package/dist/dict/ko.d.ts +15 -0
- package/dist/dict/ko.d.ts.map +1 -1
- package/dist/dict/ko.js +2 -1
- package/dist/dict/rc-keys.d.ts +28 -0
- package/dist/dict/rc-keys.d.ts.map +1 -1
- package/dist/dict/rc-keys.js +31 -1
- package/dist/dict/sd.d.ts.map +1 -1
- package/dist/dict/sd.js +20 -4
- package/dist/entity/entity-manager.d.ts +298 -2
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +4 -1
- package/dist/entity/entity-template-cone.d.ts +14 -0
- package/dist/entity/entity-template-cone.d.ts.map +1 -0
- package/dist/entity/entity-template-cone.js +222 -0
- package/dist/entity/entity.d.ts +47 -2
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +161 -14
- package/dist/ssr/renderer.js +3 -3
- package/dist/syncer/api-parser.js +12 -1
- package/dist/syncer/checksum.d.ts +0 -14
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +1 -23
- package/dist/syncer/syncer-actions.d.ts.map +1 -1
- package/dist/syncer/syncer-actions.js +8 -2
- package/dist/syncer/syncer.d.ts +1 -1
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +17 -10
- package/dist/tasks/workflow-manager.d.ts +13 -1
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +18 -1
- package/dist/template/entity-converter.js +4 -4
- package/dist/template/helpers.d.ts +10 -0
- package/dist/template/helpers.d.ts.map +1 -1
- package/dist/template/helpers.js +48 -1
- package/dist/template/implementations/entry-server.template.d.ts +1 -1
- package/dist/template/implementations/entry-server.template.js +7 -2
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +5 -1
- package/dist/template/implementations/generated_http.template.d.ts +1 -0
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.js +6 -2
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +29 -8
- package/dist/template/implementations/queries.template.d.ts.map +1 -1
- package/dist/template/implementations/queries.template.js +9 -1
- package/dist/template/implementations/sd.template.d.ts +1 -1
- package/dist/template/implementations/sd.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.js +28 -4
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/services.template.js +12 -12
- package/dist/template/implementations/view_form.template.d.ts +11 -7
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +97 -87
- package/dist/template/implementations/view_list.template.d.ts +3 -3
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +115 -109
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
- package/dist/template/implementations/view_search_input.template.js +18 -14
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +95 -7
- package/dist/testing/_relation-graph.js +1 -1
- package/dist/testing/data-explorer.d.ts +61 -0
- package/dist/testing/data-explorer.d.ts.map +1 -0
- package/dist/testing/data-explorer.js +274 -0
- package/dist/testing/faker-mappings.d.ts +20 -0
- package/dist/testing/faker-mappings.d.ts.map +1 -0
- package/dist/testing/faker-mappings.js +421 -0
- package/dist/testing/fixture-generator.d.ts +161 -0
- package/dist/testing/fixture-generator.d.ts.map +1 -0
- package/dist/testing/fixture-generator.js +954 -0
- package/dist/testing/fixture-manager.d.ts +6 -1
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +72 -4
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +4 -1
- package/dist/types/types.d.ts +1520 -26
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +136 -22
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +9 -4
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +303 -24
- package/dist/ui-web/assets/index-CsUr-_pV.js +254 -0
- package/dist/ui-web/assets/index-T42zzs1K.css +1 -0
- package/dist/ui-web/index.html +2 -2
- package/dist/utils/fs-utils.d.ts +2 -1
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +14 -3
- package/package.json +19 -11
- package/src/api/config.ts +12 -1
- package/src/api/sonamu.ts +179 -65
- package/src/auth/plugins/entity-definitions/anonymous.ts +17 -0
- package/src/auth/plugins/entity-definitions/api-key.ts +93 -0
- package/src/auth/plugins/entity-definitions/index.ts +18 -0
- package/src/auth/plugins/entity-definitions/jwt.ts +35 -0
- package/src/auth/plugins/entity-definitions/organization.ts +215 -0
- package/src/auth/plugins/entity-definitions/passkey.ts +64 -0
- package/src/auth/plugins/entity-definitions/sso.ts +62 -0
- package/src/auth/plugins/entity-definitions/types.ts +11 -1
- package/src/auth/plugins/wrappers/admin.ts +1 -3
- package/src/auth/plugins/wrappers/anonymous.ts +30 -0
- package/src/auth/plugins/wrappers/api-key.ts +42 -0
- package/src/auth/plugins/wrappers/index.ts +6 -0
- package/src/auth/plugins/wrappers/jwt.ts +34 -0
- package/src/auth/plugins/wrappers/organization.ts +73 -0
- package/src/auth/plugins/wrappers/passkey.ts +36 -0
- package/src/auth/plugins/wrappers/phone-number.ts +1 -3
- package/src/auth/plugins/wrappers/sso.ts +40 -0
- package/src/auth/plugins/wrappers/two-factor.ts +1 -3
- package/src/auth/plugins/wrappers/username.ts +1 -3
- package/src/bin/build-config.ts +6 -6
- package/src/bin/cli.ts +452 -31
- package/src/bin/fixture.ts +302 -0
- package/src/cache/decorator.ts +4 -3
- package/src/cone/cone-generator.ts +363 -0
- package/src/database/_batch_update.ts +11 -0
- package/src/database/puri-subset.test-d.ts +13 -13
- package/src/database/puri-subset.types.ts +1 -1
- package/src/database/puri.ts +43 -1
- package/src/database/upsert-builder.ts +16 -2
- package/src/dict/en.ts +1 -0
- package/src/dict/ko.ts +1 -0
- package/src/dict/rc-keys.ts +32 -0
- package/src/dict/sd.ts +23 -3
- package/src/entity/entity-manager.ts +4 -0
- package/src/entity/entity-template-cone.ts +298 -0
- package/src/entity/entity.ts +189 -13
- package/src/shared/app.shared.ts.txt +5 -0
- package/src/shared/web.shared.ts.txt +9 -5
- package/src/skills/project/README.md +21 -0
- package/src/skills/project/architecture.md +373 -0
- package/src/skills/project/business-logic.md +270 -0
- package/src/skills/project/requirements.md +160 -0
- package/src/skills/sonamu/SKILL.md +168 -3
- package/src/skills/sonamu/api.md +102 -0
- package/src/skills/sonamu/database.md +220 -1
- package/src/skills/sonamu/entity-relations.md +89 -1
- package/src/skills/sonamu/fixture-cli.md +501 -0
- package/src/skills/sonamu/frontend.md +214 -0
- package/src/skills/sonamu/i18n.md +95 -0
- package/src/skills/sonamu/model.md +153 -0
- package/src/skills/sonamu/project-init.md +178 -8
- package/src/skills/sonamu/scaffolding.md +112 -0
- package/src/skills/sonamu/subset.md +9 -3
- package/src/skills/sonamu/testing.md +287 -2
- package/src/skills/sonamu/workflow.md +70 -5
- package/src/ssr/renderer.ts +2 -2
- package/src/syncer/api-parser.ts +12 -0
- package/src/syncer/checksum.ts +0 -38
- package/src/syncer/syncer-actions.ts +7 -1
- package/src/syncer/syncer.ts +16 -5
- package/src/tasks/workflow-manager.ts +29 -8
- package/src/template/entity-converter.ts +3 -3
- package/src/template/helpers.ts +49 -0
- package/src/template/implementations/entry-server.template.ts +1 -1
- package/src/template/implementations/generated.template.ts +4 -0
- package/src/template/implementations/generated_http.template.ts +1 -0
- package/src/template/implementations/generated_sso.template.ts +40 -11
- package/src/template/implementations/queries.template.ts +8 -0
- package/src/template/implementations/sd.template.ts +22 -3
- package/src/template/implementations/services.template.ts +11 -10
- package/src/template/implementations/view_form.template.ts +111 -101
- package/src/template/implementations/view_list.template.ts +120 -119
- package/src/template/implementations/view_search_input.template.ts +17 -13
- package/src/template/zod-converter.ts +103 -6
- package/src/testing/_relation-graph.ts +1 -1
- package/src/testing/data-explorer.ts +427 -0
- package/src/testing/faker-mappings.ts +434 -0
- package/src/testing/fixture-generator.ts +1166 -0
- package/src/testing/fixture-manager.ts +91 -6
- package/src/testing/index.ts +3 -0
- package/src/types/types.ts +222 -26
- package/src/ui/ai-client.ts +9 -1
- package/src/ui/api.ts +429 -23
- package/src/utils/fs-utils.ts +14 -1
- package/dist/template/implementations/view_enums_select.template.d.ts +0 -17
- package/dist/template/implementations/view_enums_select.template.d.ts.map +0 -1
- package/dist/template/implementations/view_enums_select.template.js +0 -62
- package/dist/template/implementations/view_id_async_select.template.d.ts +0 -17
- package/dist/template/implementations/view_id_async_select.template.d.ts.map +0 -1
- package/dist/template/implementations/view_id_async_select.template.js +0 -125
- package/dist/ui-web/assets/index-Bd_2AkLb.css +0 -1
- package/dist/ui-web/assets/index-BpSbhQWo.js +0 -225
- package/src/template/implementations/view_enums_select.template.ts +0 -65
- 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
|
-
|
|
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`
|