sonamu 0.8.24 → 0.8.26

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 (88) hide show
  1. package/dist/api/__tests__/config.test.js +189 -0
  2. package/dist/api/config.d.ts.map +1 -1
  3. package/dist/api/config.js +7 -2
  4. package/dist/api/sonamu.d.ts.map +1 -1
  5. package/dist/api/sonamu.js +14 -10
  6. package/dist/auth/index.d.ts +1 -0
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/index.js +2 -1
  9. package/dist/auth/knex-adapter.d.ts +23 -0
  10. package/dist/auth/knex-adapter.d.ts.map +1 -0
  11. package/dist/auth/knex-adapter.js +163 -0
  12. package/dist/auth/plugins/wrappers/admin.d.ts +2 -2
  13. package/dist/bin/__tests__/ts-loader-register.test.js +45 -0
  14. package/dist/bin/cli.js +47 -9
  15. package/dist/bin/ts-loader-register.js +3 -29
  16. package/dist/bin/ts-loader-registration.d.ts +2 -0
  17. package/dist/bin/ts-loader-registration.d.ts.map +1 -0
  18. package/dist/bin/ts-loader-registration.js +42 -0
  19. package/dist/cone/cone-generator.js +3 -3
  20. package/dist/database/puri-subset.test-d.js +9 -1
  21. package/dist/database/puri-subset.types.d.ts +1 -1
  22. package/dist/database/puri-subset.types.d.ts.map +1 -1
  23. package/dist/database/puri-subset.types.js +1 -1
  24. package/dist/testing/fixture-generator.js +5 -5
  25. package/dist/ui/ai-client.js +2 -2
  26. package/dist/ui/api.d.ts.map +1 -1
  27. package/dist/ui/api.js +14 -14
  28. package/dist/ui/cdd-service.d.ts +15 -18
  29. package/dist/ui/cdd-service.d.ts.map +1 -1
  30. package/dist/ui/cdd-service.js +246 -222
  31. package/dist/ui/cdd-types.d.ts +41 -68
  32. package/dist/ui/cdd-types.d.ts.map +1 -1
  33. package/dist/ui/cdd-types.js +2 -2
  34. package/dist/ui-web/assets/index-CKo0Z2Iu.css +1 -0
  35. package/dist/ui-web/assets/{index-CxiydzeC.js → index-DK-2aacv.js} +83 -83
  36. package/dist/ui-web/index.html +2 -2
  37. package/package.json +6 -2
  38. package/src/api/__tests__/config.test.ts +225 -0
  39. package/src/api/config.ts +10 -4
  40. package/src/api/sonamu.ts +16 -13
  41. package/src/auth/index.ts +1 -0
  42. package/src/auth/knex-adapter.ts +208 -0
  43. package/src/bin/__tests__/ts-loader-register.test.ts +62 -0
  44. package/src/bin/cli.ts +52 -9
  45. package/src/bin/ts-loader-register.ts +2 -32
  46. package/src/bin/ts-loader-registration.ts +55 -0
  47. package/src/cone/cone-generator.ts +2 -2
  48. package/src/database/puri-subset.test-d.ts +102 -0
  49. package/src/database/puri-subset.types.ts +1 -1
  50. package/src/skills/commands/sonamu-skills.md +20 -0
  51. package/src/skills/sonamu/SKILL.md +179 -137
  52. package/src/skills/sonamu/ai-agents.md +69 -69
  53. package/src/skills/sonamu/api.md +147 -147
  54. package/src/skills/sonamu/auth-migration.md +220 -220
  55. package/src/skills/sonamu/auth-plugins.md +83 -83
  56. package/src/skills/sonamu/auth.md +106 -106
  57. package/src/skills/sonamu/cdd.md +65 -200
  58. package/src/skills/sonamu/cone.md +138 -138
  59. package/src/skills/sonamu/config.md +191 -191
  60. package/src/skills/sonamu/create-sonamu.md +66 -66
  61. package/src/skills/sonamu/database.md +158 -158
  62. package/src/skills/sonamu/entity-basic.md +292 -293
  63. package/src/skills/sonamu/entity-relations.md +246 -246
  64. package/src/skills/sonamu/entity-validation-checklist.md +124 -124
  65. package/src/skills/sonamu/fixture-cli.md +231 -231
  66. package/src/skills/sonamu/framework-change.md +37 -37
  67. package/src/skills/sonamu/frontend.md +223 -223
  68. package/src/skills/sonamu/i18n.md +82 -82
  69. package/src/skills/sonamu/migration.md +77 -77
  70. package/src/skills/sonamu/model.md +222 -222
  71. package/src/skills/sonamu/naite.md +86 -86
  72. package/src/skills/sonamu/project-init.md +228 -228
  73. package/src/skills/sonamu/puri.md +122 -122
  74. package/src/skills/sonamu/scaffolding.md +154 -154
  75. package/src/skills/sonamu/skill-contribution.md +124 -124
  76. package/src/skills/sonamu/subset.md +46 -46
  77. package/src/skills/sonamu/tasks.md +82 -82
  78. package/src/skills/sonamu/testing-devrunner.md +147 -147
  79. package/src/skills/sonamu/testing.md +673 -673
  80. package/src/skills/sonamu/upsert.md +79 -79
  81. package/src/skills/sonamu/vector.md +67 -67
  82. package/src/testing/fixture-generator.ts +4 -4
  83. package/src/ui/ai-client.ts +1 -1
  84. package/src/ui/api.ts +18 -17
  85. package/src/ui/cdd-service.ts +264 -254
  86. package/src/ui/cdd-types.ts +40 -75
  87. package/dist/ui-web/assets/index-BrQKU3j9.css +0 -1
  88. package/src/skills/sonamu/workflow.md +0 -317
@@ -1,161 +1,161 @@
1
1
  ---
2
2
  name: sonamu-entity-basic
3
- description: Sonamu Entity 생성/수정 참조. 필드 타입, 요구사항 분석, 부모-자식 관계, OrderBy/Enum 규칙. Use when creating or modifying entities. 검증 체크리스트는 entity-validation-checklist.md 참조.
3
+ description: Reference for creating or modifying Sonamu entities. Field types, requirements analysis, parent-child relationships, OrderBy/Enum rules. Use when creating or modifying entities. See entity-validation-checklist.md for the validation checklist.
4
4
  ---
5
5
 
6
- # Entity 기본 구조
6
+ # Entity Basics
7
7
 
8
- **실제 동작 코드 참고:**
8
+ **Working code references:**
9
9
 
10
- - `sonamu/examples/miomock/api/src/application/user/user.entity.json` - 기본 Entity 예시
11
- - `sonamu/examples/miomock/api/src/application/project/project.entity.json` - 복잡한 Entity 예시
12
- - `sonamu/examples/miomock/api/src/application/employee/employee.entity.json` - BelongsToOne 관계 예시
10
+ - `sonamu/examples/miomock/api/src/application/user/user.entity.json` basic entity example
11
+ - `sonamu/examples/miomock/api/src/application/project/project.entity.json` complex entity example
12
+ - `sonamu/examples/miomock/api/src/application/employee/employee.entity.json` BelongsToOne relationship example
13
13
 
14
- ## 사용자 요청 시작 시나리오
14
+ ## When a User Requests a New System
15
15
 
16
- 사용자가 시스템 구축을 요청하면 다음 순서로 진행:
16
+ When the user requests a system to be built, proceed in the following order:
17
17
 
18
- **1. 요구사항 분석** (누락된 Entity 확인)
18
+ **1. Analyze requirements** (identify missing entities)
19
19
 
20
- - "사용자(User) Entity가 필요한가요?"
21
- - "추가로 필요한 Entity가 있나요?"
20
+ - "Do you need a User entity?"
21
+ - "Are there any other entities needed?"
22
22
 
23
- **2. Entity 관계 확인** ( 번에 하나씩 질문)
23
+ **2. Confirm relationships between entities** (one question at a time)
24
24
 
25
- - "A B 1:N인가요, N:M인가요?"
26
- - "챕터는 강좌의 자식으로 함께 관리할까요?"
25
+ - "Is A to B a 1:N or N:M relationship?"
26
+ - "Should chapters be managed as children of courses?"
27
27
 
28
- **3. parentId 사용 여부 결정**
28
+ **3. Decide whether to use parentId**
29
29
 
30
- - "부모 없이 존재 불가한가요?"
31
- - "부모와 함께 생성/삭제되나요?"
30
+ - "Can it exist without a parent?"
31
+ - "Should it be created and deleted together with the parent?"
32
32
 
33
- **4. 사용자 최종 확인**
33
+ **4. Final confirmation with the user**
34
34
 
35
- - Entity 목록 확정
36
- - 관계 다이어그램 또는 명확한 설명 제공
35
+ - Finalize entity list
36
+ - Provide a relationship diagram or clear description
37
37
 
38
- ### 엔티티 설계 완료 체크리스트
38
+ ### Entity Design Done Checklist
39
39
 
40
- - [ ] 모든 필수 Entity 식별 완료
41
- - [ ] Entity 관계 정의 완료
42
- - [ ] parentId 사용 여부 결정
43
- - [ ] 사용자 확인 완료
40
+ - [ ] All required entities identified
41
+ - [ ] Relationships between entities defined
42
+ - [ ] parentId usage decided
43
+ - [ ] User confirmation complete
44
44
 
45
- **완료 시:** 다음 단계 "Entity 생성 워크플로우" 시작
45
+ **When done:** proceed to "Entity Creation Workflow"
46
46
 
47
- **전체 워크플로우 참조:** `workflow.md` - 5단계 전체 가이드
47
+ **Full workflow reference:** `.claude/workflow/project_init.md`
48
48
 
49
49
  ---
50
50
 
51
- ## Entity 생성 워크플로우
51
+ ## Entity Creation Workflow
52
52
 
53
- **사전 준비: CRITICAL!**
53
+ **Prerequisite: CRITICAL!**
54
54
 
55
- **반드시 `/packages/api`에서 `pnpm dev`를 먼저 실행하세요!**
55
+ **Always run `pnpm dev` in `/packages/api` before starting!**
56
56
 
57
57
  ```bash
58
58
  cd packages/api
59
- pnpm dev # 상태로 유지하면서 작업
59
+ pnpm dev # keep this running during all work
60
60
  ```
61
61
 
62
- > **이유**: dev 모드에서 syncer entity.json 변경을 감지하여 types.ts를 자동 생성합니다.
62
+ > **Why**: In dev mode, the syncer detects changes to entity.json and auto-generates types.ts.
63
63
  >
64
- > auth 엔티티뻐만 아니라 **모든 엔티티 생성 dev 모드가 필수**입니다.
64
+ > Dev mode is required **for all entity creation**, not just auth entities.
65
65
 
66
- ### 1단계: stub 생성
66
+ ### Step 1: Generate stub
67
67
 
68
- **CRITICAL: EntityId 반드시 대문자로 시작해야 합니다!**
68
+ **CRITICAL: EntityId must always start with an uppercase letter!**
69
69
 
70
70
  ```bash
71
71
  pnpm sonamu stub entity {EntityId}
72
72
  ```
73
73
 
74
- **올바른 예시:**
74
+ **Correct examples:**
75
75
 
76
76
  - `pnpm sonamu stub entity Course` (correct)
77
77
  - `pnpm sonamu stub entity User` (correct)
78
78
  - `pnpm sonamu stub entity ConsultationHistory` (correct)
79
79
 
80
- **잘못된 예시:**
80
+ **Incorrect examples:**
81
81
 
82
- - `pnpm sonamu stub entity course` (wrong - 소문자로 시작)
83
- - `pnpm gen stub entity Course` (wrong - 잘못된 명령어)
82
+ - `pnpm sonamu stub entity course` (wrong starts with lowercase)
83
+ - `pnpm gen stub entity Course` (wrong incorrect command)
84
84
 
85
- 생성되는 파일: `api/src/application/{entity}/{entity}.entity.json`
85
+ Generated file: `api/src/application/{entity}/{entity}.entity.json`
86
86
 
87
- ### 2단계: stub 파일 수정
87
+ ### Step 2: Edit the stub file
88
88
 
89
- 생성된 entity.json 파일에 props, relations, subsets 추가
89
+ Add props, relations, and subsets to the generated entity.json file.
90
90
 
91
- ### 3단계: 검증 필수 파일 생성
91
+ ### Step 3: Validate and generate required files
92
92
 
93
- **CRITICAL: sync 실행 전에 반드시 검증하세요!**
93
+ **CRITICAL: Always validate before running sync!**
94
94
 
95
- **A. entity.json 검증** (`entity-validation-checklist.md` PHASE 1 참조)
95
+ **A. Validate entity.json** (see `entity-validation-checklist.md` PHASE 1)
96
96
 
97
- - [ ] 인덱스에 type 필드 있는가?
98
- - [ ] Subset에서 FK를 직접 참조하지 않고 relation.id 형식 사용?
99
- - [ ] Boolean dbDefault "true"/"false" 문자열?
100
- - [ ] OrderBy enum id-desc만 있는가?
101
- - [ ] Enum dbDefault 이스케이프된 큰따옴표? (예: `"\"pending\""`)
97
+ - [ ] Does every index have a `type` field?
98
+ - [ ] Does the subset use `relation.id` format instead of directly referencing FK columns?
99
+ - [ ] Is the Boolean `dbDefault` a string (`"true"` / `"false"`)?
100
+ - [ ] Does the OrderBy enum contain only `id-desc`?
101
+ - [ ] Is the Enum `dbDefault` using escaped double quotes? (e.g., `"\"pending\""`)
102
102
 
103
- **B. model.ts (수동 생성 필수)**
103
+ **B. model.ts (must be created manually)**
104
104
 
105
- - 반드시 수동 생성 필요
106
- - 다른 entity model.ts 참고하여 작성
107
- - 필수 메서드: findById, findOne, findMany, save, del
108
- - 템플릿은 `entity-validation-checklist.md` PHASE 2 참조
105
+ - Must be created manually
106
+ - Reference another entity's model.ts as a template
107
+ - Required methods: findById, findOne, findMany, save, del
108
+ - See `entity-validation-checklist.md` PHASE 2 for template
109
109
 
110
- **C. types.ts (자동 생성 - 대기 필요)**
110
+ **C. types.ts (auto-generated wait for it)**
111
111
 
112
- - **pnpm dev 실행 중이면** syncer 2-3초 자동 생성
113
- - 확인: `ls packages/api/src/application/{entity}/{entity}.types.ts`
114
- - 생성 되면:
115
- 1. pnpm dev 실행 중인지 확인
116
- 2. 여전히 되면 수동 생성 (템플릿은 `entity-validation-checklist.md` PHASE 2 참조)
112
+ - **If `pnpm dev` is running**, the syncer will auto-generate it within 2–3 seconds
113
+ - Check: `ls packages/api/src/application/{entity}/{entity}.types.ts`
114
+ - If not generated:
115
+ 1. Verify `pnpm dev` is running
116
+ 2. If still not generated, create manually (see `entity-validation-checklist.md` PHASE 2 for template)
117
117
 
118
- **완료 확인:**
118
+ **Done criteria:**
119
119
 
120
- - [ ] entity.json 검증 통과
121
- - [ ] model.ts 존재
122
- - [ ] types.ts 존재 (자동 생성 또는 수동 생성)
120
+ - [ ] entity.json validation passed
121
+ - [ ] model.ts exists
122
+ - [ ] types.ts exists (auto-generated or manually created)
123
123
 
124
- ### 4단계: sync
124
+ ### Step 4: Sync
125
125
 
126
126
  ```bash
127
127
  pnpm sonamu sync
128
128
  ```
129
129
 
130
- **주의:** Entity JSON 직접 작성하지 말고, 반드시 stub 명령어로 생성 수정할 것.
130
+ **Note:** Do not write entity JSON by hand. Always generate using the stub command, then edit.
131
131
 
132
- ### 5단계: Migration Scaffolding
132
+ ### Step 5: Migration and Scaffolding
133
133
 
134
- 1. Migration 생성 apply
135
- 2. Scaffolding 실행
136
- 3. Build 테스트
134
+ 1. Generate and apply migration
135
+ 2. Run scaffolding
136
+ 3. Test the build
137
137
 
138
- **전체 워크플로우:** `entity-validation-checklist.md`에서 단계별 체크리스트 확인
138
+ **Full workflow:** see step-by-step checklist in `entity-validation-checklist.md`
139
139
 
140
- ### 6단계: types.ts nullable 필드 처리 (필수)
140
+ ### Step 6: Handle nullable fields in types.ts (required)
141
141
 
142
- **CRITICAL: 테스트 작성 즉시 처리하세요!**
142
+ **CRITICAL: Do this immediately after scaffolding, before writing tests!**
143
143
 
144
- scaffolding 완료 생성된 `*.types.ts` 파일에서 nullable 필드를 처리해야 합니다.
144
+ After scaffolding completes, nullable fields in the generated `*.types.ts` must be handled.
145
145
 
146
146
  ```typescript
147
- // 자동 생성된 types.ts
147
+ // Auto-generated types.ts
148
148
  export const FAQSaveParams = FAQBaseSchema.partial({
149
149
  id: true,
150
150
  created_at: true,
151
151
  });
152
152
 
153
- // CORRECT: 즉시 수정 - nullable 필드 추가
153
+ // CORRECT: update immediately add nullable fields
154
154
  export const FAQSaveParams = FAQBaseSchema.partial({
155
155
  id: true,
156
156
  created_at: true,
157
- category: true, // nullable 추가
158
- order_num: true, // nullable 추가
157
+ category: true, // nullable added
158
+ order_num: true, // nullable added
159
159
  }).extend({
160
160
  category: z.string().nullish(),
161
161
  order_num: z.number().nullish(),
@@ -163,220 +163,219 @@ export const FAQSaveParams = FAQBaseSchema.partial({
163
163
  });
164
164
  ```
165
165
 
166
- **상세 가이드:** `testing.md`의 "엔티티 생성 즉시 해야 작업" 참조
166
+ **Detailed guide:** see "Actions to take immediately after entity creation" in `testing.md`
167
167
 
168
- ## Entity 생성 체크리스트
168
+ ## Checklist for New Entity Creation
169
169
 
170
- 1. **id**: PascalCase (예: `User`, `BlogPost`)
171
- 2. **table**: snake_case 복수형 (예: `users`, `blog_posts`) - 생략 가능
172
- 3. **title**: 한글 표시명
173
- 4. **props 권장**: `id`, `created_at` (스키마에서 강제되지 않으나 Best Practice)
174
- 5. **enums 권장**: `{EntityId}OrderBy`, `{EntityId}SearchField` (스키마에서 강제되지 않으나 Best Practice)
170
+ 1. **id**: PascalCase (e.g., `User`, `BlogPost`)
171
+ 2. **table**: snake_case plural (e.g., `users`, `blog_posts`) can be omitted
172
+ 3. **title**: display name
173
+ 4. **Recommended props**: `id`, `created_at` (not enforced by schema but best practice)
174
+ 5. **Recommended enums**: `{EntityId}OrderBy`, `{EntityId}SearchField` (not enforced but best practice)
175
175
 
176
176
  ## IMPORTANT: Analyze Requirements Before Creating Entity
177
177
 
178
- **STOP! Entity를 만들기 전에 질문을 하나씩 하세요.**
178
+ **STOP! Ask questions one at a time before creating any entity.**
179
179
 
180
- ### 누락된 Entity 확인
180
+ ### Identify missing entities
181
181
 
182
- 사용자가 명시적으로 언급한 Entity만 생성하지 것. **한 번에 하나씩 질문:**
182
+ Do not only create entities explicitly mentioned by the user. **Ask one at a time:**
183
183
 
184
- - "사용자(User) Entity가 필요한가요?" → 응답 대기
185
- - "User 역할이 여러 개인가요?" → 응답 대기
186
- - "추가로 필요한 Entity가 있나요?" → 응답 대기
184
+ - "Do you need a User entity?" → wait for response
185
+ - "Does the User have multiple roles?" → wait for response
186
+ - "Are there any other entities needed?" → wait for response
187
187
 
188
- **User Entity 주의**: `id`는 자동 증가 시퀀스(PK)이며 로그인 아이디가 아님. better-auth 사용 별도 `login_id` 불필요 (auth 테이블이 관리).
188
+ **Note on User entity**: `id` is an auto-increment sequence (PK) and is not a login ID. When using better-auth, a separate `login_id` is not needed (managed by the auth table).
189
189
 
190
- **자주 누락되는 Entity**: 컨텐츠(Comment, Like, Tag, Category), 커머스(Review, Cart, Payment), 예약(Reservation, Schedule), 교육(Enrollment, Progress)
190
+ **Commonly missed entities**: Content (Comment, Like, Tag, Category), Commerce (Review, Cart, Payment), Reservation (Reservation, Schedule), Education (Enrollment, Progress)
191
191
 
192
- ### 여러 Entity 요청 - 관계 확인
192
+ ### When multiple entities are requested confirm relationships
193
193
 
194
- 2 이상 Entity 요청 **코드 작성 전에 관계를 하나씩 질문**:
194
+ When 2+ entities are requested, **ask about relationships one at a time before writing any code**:
195
195
 
196
- - BelongsToOne, HasMany, ManyToMany, parentId 중 어떤 관계인지
197
- - 부모-자식 종속(삭제 함께 삭제)인지 독립적인지
196
+ - Which relationship type: BelongsToOne, HasMany, ManyToMany, or parentId
197
+ - Whether it is a parent-child dependency (delete together) or independent
198
198
 
199
- ### 설계 반드시 확인
199
+ ### Always confirm before designing
200
200
 
201
- **1. Polymorphic Association** (`entity_type + entity_id` 패턴):
201
+ **1. Polymorphic Association** (`entity_type + entity_id` pattern):
202
202
 
203
- - string PK 엔티티(better-auth User) 있으면 `entity_id`를 `string` 타입으로 통일
204
- - 없으면 → `integer` 사용 가능
203
+ - If there is a string PK entity (e.g., better-auth User) → use `string` type for `entity_id` uniformly
204
+ - Otherwise → `integer` is fine
205
205
 
206
- **2. 도메인 용어엔티티 영문 ID 매핑**: 코드 작성 전에 사용자와 확정 (예: "위탁연구과제" → `ResearchContract`). 중간에 바뀌면 전체 rename 필요.
206
+ **2. Domain termentity English ID mapping**: finalize with the user before writing any code (e.g., "위탁연구과제" → `ResearchContract`). Changing this later requires a full rename.
207
207
 
208
- ## 부모-자식 관계 (parentId)
208
+ ## Parent-Child Relationships (parentId)
209
209
 
210
- ### parentId란?
210
+ ### What is parentId?
211
211
 
212
- 자식 Entity가 부모 Entity에 종속되어 함께 관리될 사용하는 최상위 옵션.
212
+ A top-level option used when a child entity is managed as a dependent of a parent entity.
213
213
 
214
- - parentId 설정 시: 자식은 독립 CRUD 없이 부모를 통해 생성/수정/삭제
215
- - parentId 미설정 시: 독립 Entity로 별도 CRUD 가능
214
+ - With parentId: the child has no independent CRUD it is created, updated, and deleted through the parent
215
+ - Without parentId: an independent entity with its own CRUD
216
216
 
217
- ### 언제 parentId를 사용하나?
217
+ ### When to use parentId
218
218
 
219
- | 상황 | parentId | 예시 |
220
- | ------------------------ | -------- | -------------------------- |
221
- | 부모 없이 존재 불가 | O | 주문아이템주문 |
222
- | 부모와 함께 생성/삭제 | O | 챕터강좌, 레슨챕터 |
223
- | 독립적으로 CRUD 가능 | X | 댓글게시글 |
224
- | 여러 부모에 속할 있음 | X | 태그게시글 (ManyToMany) |
219
+ | Situation | parentId | Example |
220
+ |-----------|----------|---------|
221
+ | Cannot exist without a parent | Yes | OrderItemOrder |
222
+ | Created and deleted together with parent | Yes | ChapterCourse, LessonChapter |
223
+ | Can be independently CRUD'd | No | CommentPost |
224
+ | Can belong to multiple parents | No | TagPost (ManyToMany) |
225
225
 
226
- ### parentId 사용 예시
226
+ ### parentId usage example
227
227
 
228
228
  ```json
229
229
  {
230
230
  "id": "OrderItem",
231
231
  "table": "order_items",
232
- "title": "주문아이템",
232
+ "title": "Order Item",
233
233
  "parentId": "Order",
234
234
  "props": [...]
235
235
  }
236
236
  ```
237
237
 
238
- ### parentId 엔티티는 types.ts가 생성되지 않음
238
+ ### Child entities with parentId do not generate types.ts
239
239
 
240
- parentId 설정된 자식 엔티티(예: Chapter, Lesson) 독립적인 `types.ts` 파일이 생성되지 않습니다. 이는 정상 동작이며, 자식 엔티티는 부모 엔티티를 통해 함께 관리되기 때문입니다. 독립 CRUD types.ts 필요한 경우 parentId를 사용하지 않아야 합니다.
240
+ Child entities with parentId set (e.g., Chapter, Lesson) do not get their own `types.ts` file. This is expected behavior child entities are managed through the parent. If you need independent CRUD and types.ts, do not use parentId.
241
241
 
242
- ### parentId 자식 엔티티 폴더 위치
242
+ ### Folder location for parentId child entities
243
243
 
244
- parentId 설정된 자식 엔티티는 **루트 부모 엔티티와 같은 폴더**에 위치해야 합니다.
244
+ Child entities with parentId must be placed **in the same folder as the root parent entity**.
245
245
 
246
- | 구조 | 설명 |
247
- | ---------------------------- | ------------------------------------------- |
248
- | `course/course.entity.json` | 루트 엔티티 |
249
- | `course/chapter.entity.json` | parentId: "Course" → 같은 폴더 |
250
- | `course/lesson.entity.json` | parentId: "Chapter" → 같은 폴더 (루트 기준) |
251
- | `course/course.types.ts` | types.ts 루트만 생성됨 |
246
+ | Structure | Description |
247
+ |-----------|-------------|
248
+ | `course/course.entity.json` | Root entity |
249
+ | `course/chapter.entity.json` | parentId: "Course" → same folder |
250
+ | `course/lesson.entity.json` | parentId: "Chapter" → same folder (based on root) |
251
+ | `course/course.types.ts` | types.ts generated only for root |
252
252
 
253
- **주의**: 자식 엔티티를 별도 폴더(`chapter/chapter.entity.json`)에 생성하면 안 됩니다.
253
+ **Note:** Do not create child entities in a separate folder (e.g., `chapter/chapter.entity.json`).
254
254
 
255
255
  ### IMPORTANT: When Uncertain - Ask User (Never Guess)
256
256
 
257
- **추측하지 말고 질문하세요.** 다음과 같은 상황에서는 사용자에게 직접 물어볼 것:
257
+ **Do not guess ask.** In situations like the following, ask the user directly:
258
258
 
259
- - "챕터를 강좌의 자식으로 함께 관리할까요, 아니면 독립 Entity로 만들까요?"
260
- - "주문아이템을 주문과 함께 저장할까요, 아니면 별도로 관리할까요?"
259
+ - "Should chapters be managed as children of courses, or created as an independent entity?"
260
+ - "Should order items be saved together with the order, or managed separately?"
261
261
 
262
- **확신이 없으면 질문하세요. 틀린 설계보다 질문 번이 낫습니다.**
262
+ **When in doubt, ask. One question is better than a wrong design.**
263
263
 
264
- **판단에 도움이 되는 질문들 (사용자에게 물어볼 것):**
264
+ **Helpful questions to ask the user:**
265
265
 
266
- - " 데이터를 부모 없이 단독으로 조회/수정할 일이 있나요?"
267
- - "부모가 삭제되면 데이터도 함께 삭제되어야 하나요?"
268
- - "관리자 화면에서 별도 목록 페이지가 필요한가요?"
266
+ - "Will this data ever need to be queried or updated independently without a parent?"
267
+ - "Should this data be deleted when the parent is deleted?"
268
+ - "Does the admin UI need a separate list page for this?"
269
269
 
270
- ## 최소 템플릿
270
+ ## Minimal Template
271
271
 
272
272
  ```json
273
273
  {
274
274
  "id": "Product",
275
275
  "table": "products",
276
- "title": "상품",
276
+ "title": "Product",
277
277
  "props": [
278
278
  { "name": "id", "type": "integer", "desc": "ID" },
279
279
  {
280
280
  "name": "created_at",
281
281
  "type": "date",
282
282
  "dbDefault": "CURRENT_TIMESTAMP",
283
- "desc": "등록일시"
283
+ "desc": "Created at"
284
284
  },
285
- { "name": "name", "type": "string", "length": 255, "desc": "상품명" }
285
+ { "name": "name", "type": "string", "length": 255, "desc": "Product name" }
286
286
  ],
287
287
  "indexes": [],
288
288
  "subsets": { "A": ["id", "name", "created_at"] },
289
289
  "enums": {
290
- "ProductOrderBy": { "id-desc": "ID최신순" },
291
- "ProductSearchField": { "id": "ID", "name": "상품명" }
290
+ "ProductOrderBy": { "id-desc": "Newest" },
291
+ "ProductSearchField": { "id": "ID", "name": "Name" }
292
292
  }
293
293
  }
294
294
  ```
295
295
 
296
- ## 상황별 가이드
296
+ ## Situation-Specific Guides
297
297
 
298
- ### 문자열 필드 추가할
298
+ ### Adding a string field
299
299
 
300
300
  ```json
301
- { "name": "title", "type": "string", "length": 255, "desc": "제목" }
301
+ { "name": "title", "type": "string", "length": 255, "desc": "Title" }
302
302
  ```
303
303
 
304
- - `length` 생략 `text` 타입으로 저장 ( 텍스트용)
304
+ - Omitting `length` stored as `text` type (for long text)
305
305
 
306
- ### Enum 필드 추가할
306
+ ### Adding an enum field
307
307
 
308
308
  ```json
309
- // 1. props에 추가
310
- { "name": "status", "type": "enum", "id": "ProductStatus", "desc": "상태" }
309
+ // 1. Add to props
310
+ { "name": "status", "type": "enum", "id": "ProductStatus", "desc": "Status" }
311
311
 
312
- // 2. enums 정의 (MUST - 누락 오류 발생)
313
- "ProductStatus": { "draft": "임시저장", "published": "공개", "archived": "보관" }
312
+ // 2. Define in enums (MUST missing this causes errors)
313
+ "ProductStatus": { "draft": "Draft", "published": "Published", "archived": "Archived" }
314
314
  ```
315
315
 
316
- ### IMPORTANT: 고정값 필드는 반드시 enum으로
316
+ ### IMPORTANT: Always use enum for fixed-value fields
317
317
 
318
- 선택지가 정해진 필드를 `string`으로 정의하면 DB 정합성이 깨진다.
318
+ Defining a field with a fixed set of choices as `string` breaks DB integrity.
319
319
 
320
- **판단: " 값이 코드 외부에서 자유롭게 입력될 있는가?"** No → enum, Yes → string
320
+ **Rule: "Can this value be freely entered from outside the code?"** No → enum, Yes → string
321
321
 
322
- **enum 후보 식별**: `faker.helpers.arrayElement([...])` 형태의 string, "다음 하나/구분/유형" 나열, 셀렉트박스/라디오버튼 표시 필드
322
+ **Enum candidates**: strings that look like `faker.helpers.arrayElement([...])`, fields described as "one of the following / type / category", select boxes / radio buttons
323
323
 
324
324
  ```json
325
- // WRONG: string으로 정의
326
- { "name": "budget_item", "type": "string", "desc": "비목명" }
327
- // CORRECT: enum으로 정의
328
- { "name": "budget_item", "type": "enum", "id": "BudgetItem", "desc": "비목" }
325
+ // WRONG: defined as string
326
+ { "name": "budget_item", "type": "string", "desc": "Budget item" }
327
+ // CORRECT: defined as enum
328
+ { "name": "budget_item", "type": "enum", "id": "BudgetItem", "desc": "Budget item" }
329
329
  ```
330
330
 
331
- ### nullable 필드 추가할
331
+ ### Adding a nullable field
332
332
 
333
333
  ```json
334
- { "name": "deleted_at", "type": "date", "nullable": true, "desc": "삭제일시" }
334
+ { "name": "deleted_at", "type": "date", "nullable": true, "desc": "Deleted at" }
335
335
  ```
336
336
 
337
- **CRITICAL: nullable 속성의 중요성**
337
+ **CRITICAL: Importance of the nullable attribute**
338
338
 
339
- `nullable: true`가 **없는** 필드는 **필수 필드**로 간주됩니다.
339
+ A field without `nullable: true` is treated as **required**.
340
340
 
341
- Sonamu `ubUpsert`는 PostgreSQL `ON CONFLICT ... DO UPDATE`를 사용하므로,
342
- 업데이트 시에도 **모든 필수 필드**를 포함해야 합니다.
341
+ Sonamu's `ubUpsert` uses PostgreSQL `ON CONFLICT ... DO UPDATE`, so **all required fields** must be included even on updates.
343
342
 
344
343
  ```json
345
- // 예시
344
+ // example
346
345
  {
347
346
  "props": [
348
- { "name": "title", "type": "string" }, // 필수! (nullable 없음)
349
- { "name": "content", "type": "string" }, // 필수! (nullable 없음)
350
- { "name": "category", "type": "string", "nullable": true } // 선택 (nullable 있음)
347
+ { "name": "title", "type": "string" }, // required (no nullable)
348
+ { "name": "content", "type": "string" }, // required (no nullable)
349
+ { "name": "category", "type": "string", "nullable": true } // optional
351
350
  ]
352
351
  }
353
352
  ```
354
353
 
355
- **규칙**:
354
+ **Rules**:
356
355
 
357
- - 선택 필드가 아니면 `nullable: true` 추가 금지
358
- - 선택 필드라면 반드시 `nullable: true` 명시
359
- - 테스트/API에서 필수 필드는 항상 제공 필요
356
+ - Do not add `nullable: true` to fields that are not optional
357
+ - Always specify `nullable: true` for optional fields
358
+ - Required fields must always have a value in tests and API calls
360
359
 
361
- **상세**: `testing.md` "Quick Start" `upsert.md` "CRITICAL: 필수 필드 포함 필수" 참조
360
+ **Details:** see "Quick Start" in `testing.md` and "CRITICAL: Required fields must be included" in `upsert.md`
362
361
 
363
- ### JSON 필드 추가할
362
+ ### Adding a JSON field
364
363
 
365
364
  ```json
366
365
  {
367
366
  "name": "metadata",
368
367
  "type": "json",
369
368
  "id": "ProductMetadata",
370
- "desc": "메타데이터"
369
+ "desc": "Metadata"
371
370
  }
372
371
  ```
373
372
 
374
- - `id` 필수 (타입명으로 사용)
375
- - 별도로 TypeScript 타입 정의 필요
373
+ - `id` is required (used as the type name)
374
+ - A separate TypeScript type definition is needed
376
375
 
377
- ### searchText 필드 추가할 (pg_trgm Fuzzy Search)
376
+ ### Adding a searchText field (for pg_trgm Fuzzy Search)
378
377
 
379
- 여러 컬럼을 하나의 generated column으로 통합하는 전용 prop 타입이다. GIN 인덱스와 함께 쓴다.
378
+ A dedicated prop type that consolidates multiple columns into a single generated column. Used with a GIN index.
380
379
 
381
380
  ```json
382
381
  {
@@ -405,19 +404,19 @@ Sonamu의 `ubUpsert`는 PostgreSQL의 `ON CONFLICT ... DO UPDATE`를 사용하
405
404
  }
406
405
  ```
407
406
 
408
- source column 타입별 SQL 표현:
407
+ SQL expressions per source column type:
409
408
 
410
- | source 타입 | caseInsensitive: true | caseInsensitive: false (기본) |
411
- |------------|----------------------|------------------------------|
409
+ | source type | caseInsensitive: true | caseInsensitive: false (default) |
410
+ |------------|----------------------|----------------------------------|
412
411
  | `string` | `lower(COALESCE(col, ''))` | `COALESCE(col, '')` |
413
412
  | `string[]` | `sonamu_text_array_agg(col)` | `sonamu_text_array_agg(col, false)` |
414
413
  | `json` (z.array(z.string())) | `sonamu_jsonb_array_agg(col)` | `sonamu_jsonb_array_agg(col, false)` |
415
414
 
416
- - `string[]` 또는 `json(string[])` source 있으면 마이그레이션에 헬퍼 함수 DDL 자동 삽입됨
417
- - `searchText` 컬럼은 generated column이므로 SaveParams에서 자동 제외됨 — INSERT/UPDATE 불가
418
- - 쿼리 사용법: `puri.md` "pg_trgm Fuzzy Search" 섹션 참조
415
+ - If a `string[]` or `json(string[])` source is present, helper function DDL is automatically inserted in the migration
416
+ - `searchText` columns are generated columns and are excluded from SaveParams — INSERT/UPDATE is not allowed
417
+ - For query usage: see the "pg_trgm Fuzzy Search" section in `puri.md`
419
418
 
420
- ### 유니크 제약 추가할
419
+ ### Adding a unique constraint
421
420
 
422
421
  ```json
423
422
  {
@@ -427,7 +426,7 @@ source column 타입별 SQL 표현:
427
426
  }
428
427
  ```
429
428
 
430
- ### 복합 유니크 제약
429
+ ### Composite unique constraint
431
430
 
432
431
  ```json
433
432
  {
@@ -437,19 +436,19 @@ source column 타입별 SQL 표현:
437
436
  }
438
437
  ```
439
438
 
440
- ### IMPORTANT: indexes에서 FK 컨럼명은 실제 DB 컨럼명을 사용한다
439
+ ### IMPORTANT: Use the actual DB column name in indexes
441
440
 
442
- **indexes와 subsets에서 FK 컨럼을 참조하는 방식이 다르다. 혼동하지 않는다.**
441
+ **The way FK columns are referenced differs between indexes and subsets. Do not confuse them.**
443
442
 
444
- | 위치 | 사용 형식 | 예시 |
445
- | --------- | -------------------------- | ------------------------------------- |
446
- | `indexes` | 실제 DB 컨럼명 | `role_id`, `user_id`, `department_id` |
443
+ | Location | Format | Example |
444
+ |----------|--------|---------|
445
+ | `indexes` | Actual DB column name | `role_id`, `user_id`, `department_id` |
447
446
  | `subsets` | FieldExpr (relation.field) | `role.id`, `user.id`, `department.id` |
448
447
 
449
448
  **DO NOT:**
450
449
 
451
450
  ```json
452
- // indexes에서 FieldExpr 사용오류
451
+ // Using FieldExpr in indexes error
453
452
  "indexes": [
454
453
  { "name": "ix_role", "type": "index", "columns": [{ "name": "role.id" }] }
455
454
  ]
@@ -458,185 +457,185 @@ source column 타입별 SQL 표현:
458
457
  **DO:**
459
458
 
460
459
  ```json
461
- // indexes 실제 DB 컨럼명
460
+ // indexes use actual DB column names
462
461
  "indexes": [
463
462
  { "name": "ix_role_id", "type": "index", "columns": [{ "name": "role_id" }] }
464
463
  ]
465
464
 
466
- // subsets FieldExpr
465
+ // subsets use FieldExpr
467
466
  "subsets": {
468
467
  "A": ["id", "role.id", "role.name"]
469
468
  }
470
469
  ```
471
470
 
472
- ### IMPORTANT: unique 제약은 비즈니스 규칙 기준으로
471
+ ### IMPORTANT: Unique constraints based on business rules
473
472
 
474
- 기술적 판단이 아니라 **"같은 조합이 insert되면?"** → 오류여야 하면 unique, 허용이면 index만.
473
+ Not a technical decision — ask **"What if the same combination is inserted twice?"** → if it should error, use unique; if it should be allowed, use index only.
475
474
 
476
- **복합 unique 필요한 패턴**: 연도별 설정(`type, dept_id, year`), 사용자-역할 매핑, 연차별 예산(`project_id, year, budget_item`), 좋아요/북마크(`user_id, entity_id`)
475
+ **Patterns that need composite unique**: per-year settings (`type, dept_id, year`), user-role mappings, per-year budgets (`project_id, year, budget_item`), likes/bookmarks (`user_id, entity_id`)
477
476
 
478
- ## 흔한 실수
477
+ ## Common Mistakes
479
478
 
480
- | 실수 | 해결 |
481
- | ------------------------------------ | ------------------------------------------------------------------------ |
482
- | `id` prop 누락 | 추가 권장 (대부분의 Model 로직에서 필요) |
483
- | `created_at` prop 누락 | 추가 권장, `dbDefault: "CURRENT_TIMESTAMP"` |
484
- | `OrderBy` enum 누락 | `{EntityId}OrderBy` 추가 권장 (findMany 정렬에 필요) |
485
- | `SearchField` enum 누락 | `{EntityId}SearchField` 추가 권장 (검색 기능에 필요) |
486
- | enum prop `id`가 enums 없음 | enums 섹션에 정의 추가 |
487
- | json prop에 `id` 누락 | `id` 필드 추가 |
488
- | `"type": "text"` 직접 사용 | `text`는 유효하지 않음. `"type": "string"` + length 생략으로 사용 |
489
- | `OrderBy` enum 여러 추가 | **기본은 `id-desc`만 생성** (아래 참조) |
490
- | 고정 선택지 필드를 `string`으로 정의 | enum으로 변환 (fixtureGenerator가 arrayElement 필드 확인) |
491
- | unique 제약 없는 연도별/매핑 테이블 | 비즈니스 규칙 기준으로 복합 unique 추가 |
492
- | 정수 필드에 `number` 타입 사용 | `integer` 사용 (소수점 필요 시 `numeric`) |
493
- | indexes에서 `role.id` 형식 사용 | indexes 실제 DB 컨럼명(`role_id`), subsets FieldExpr(`role.id`) 사용 |
479
+ | Mistake | Fix |
480
+ |---------|-----|
481
+ | Missing `id` prop | Recommended to add (needed by most Model logic) |
482
+ | Missing `created_at` prop | Recommended to add with `dbDefault: "CURRENT_TIMESTAMP"` |
483
+ | Missing `OrderBy` enum | Add `{EntityId}OrderBy` (needed for findMany sorting) |
484
+ | Missing `SearchField` enum | Add `{EntityId}SearchField` (needed for search) |
485
+ | enum prop `id` not defined in enums | Add definition to the enums section |
486
+ | Missing `id` on json prop | Add the `id` field |
487
+ | Using `"type": "text"` directly | `text` is invalid. Use `"type": "string"` without a length |
488
+ | Adding multiple values to `OrderBy` enum | **Default is `id-desc` only** (see below) |
489
+ | Defining fixed-choice fields as `string` | Convert to enum (check for fields with arrayElement-style fixtureGenerator) |
490
+ | Yearly/mapping tables without unique constraints | Add composite unique based on business rules |
491
+ | Using `number` type for integer fields | Use `integer` (use `numeric` only when decimal precision is needed) |
492
+ | Using `role.id` format in indexes | indexes use actual DB column name (`role_id`); only subsets use FieldExpr (`role.id`) |
494
493
 
495
- ## Entity 스키마 검증 오류 해결
494
+ ## Resolving Entity Schema Validation Errors
496
495
 
497
- **→ `entity-validation-checklist.md` PHASE 1 참조** (인덱스 type 누락, Subset FieldExpr, 중복 컬럼, Boolean dbDefault )
496
+ **→ See `entity-validation-checklist.md` PHASE 1** (missing index type, Subset FieldExpr, duplicate columns, Boolean dbDefault, etc.)
498
497
 
499
- **빠른 체크리스트:**
498
+ **Quick checklist:**
500
499
 
501
- - [ ] 모든 인덱스에 `type` 필드 있는가? (`"index"` | `"unique"` | `"hnsw"` | `"ivfflat"`)
502
- - [ ] Subset에서 FK `relation.id` 형식으로 참조하는가? (`user_id` ✗ → `user.id` ✓)
503
- - [ ] BelongsToOne relation FK 컬럼을 중복 정의하지 않았는가?
504
- - [ ] Boolean dbDefault `"true"` / `"false"` 문자열인가? (0, 1 ✗)
505
- - [ ] Subset A에 모든 필드가 포함되어 있는가?
506
- - [ ] indexes의 columns 실제 DB 컬럼명(`role_id`) 사용했는가? (FieldExpr `role.id` ✗)
500
+ - [ ] Does every index have a `type` field? (`"index"` | `"unique"` | `"hnsw"` | `"ivfflat"`)
501
+ - [ ] Does the subset reference FK using `relation.id` format? (`user_id` ✗ → `user.id` ✓)
502
+ - [ ] No duplicate definition of BelongsToOne relation and FK column?
503
+ - [ ] Is Boolean `dbDefault` a string (`"true"` / `"false"`)? (0, 1 ✗)
504
+ - [ ] Are all fields included in Subset A?
505
+ - [ ] Do index columns use actual DB column names (`role_id`)? (FieldExpr `role.id` ✗)
507
506
 
508
507
  ## IMPORTANT: OrderBy Enum Generation Rule
509
508
 
510
- **IMPORTANT: Scaffolding 시에는 `id-desc`만 사용하는 것을 강력히 권장합니다.**
509
+ **IMPORTANT: It is strongly recommended to use only `id-desc` during initial scaffolding.**
511
510
 
512
511
  ```json
513
- // RECOMMENDED - 초기 Scaffolding용
514
- "ProductOrderBy": { "id-desc": "ID최신순" }
512
+ // RECOMMENDED for initial scaffolding
513
+ "ProductOrderBy": { "id-desc": "Newest" }
515
514
 
516
- // AVOID - Scaffolding 전에는 피하세요
517
- "ProductOrderBy": { "id-desc": "ID최신순", "name-asc": "이름순", "created_at-desc": "등록일순" }
515
+ // AVOID do not add these before scaffolding
516
+ "ProductOrderBy": { "id-desc": "Newest", "name-asc": "Name (A-Z)", "created_at-desc": "Newest first" }
518
517
  ```
519
518
 
520
- ### id-desc만 권장하나?
519
+ ### Why only id-desc?
521
520
 
522
- Scaffolding이 생성하는 model 코드는 `id-desc`만 자동 처리합니다. OrderBy enum 다른 값이 있으면:
521
+ The model code generated by scaffolding handles only `id-desc` automatically. If the OrderBy enum has other values:
523
522
 
524
- 1. Scaffolding 정상 동작하지만, model `exhaustive()` 함수에서 타입 오류 발생
525
- 2. 개발자가 수동으로 model에 케이스 추가 필요
526
- 3. 작업이 누락되면 런타임 에러 발생 가능
523
+ 1. Scaffolding succeeds, but a type error occurs in the model's `exhaustive()` function
524
+ 2. The developer must manually add cases to the model
525
+ 3. If this is missed, a runtime error may occur
527
526
 
528
- **이것은 기술적 제약이 아닌 Scaffolding의 best practice입니다.** 복잡한 OrderBy Scaffolding 완료 추가하는 것이 안전합니다.
527
+ **This is not a technical constraint — it is scaffolding best practice.** Add complex OrderBy values after scaffolding is complete.
529
528
 
530
- ### 추가 정렬 옵션이 필요할
529
+ ### When additional sort options are needed
531
530
 
532
- 나중에 정렬 옵션이 필요하면:
531
+ When sort options are required later:
533
532
 
534
- 1. entity.json의 OrderBy enum 추가
535
- 2. model의 orderBy 분기문에 해당 케이스 추가
533
+ 1. Add values to the OrderBy enum in entity.json
534
+ 2. Add the corresponding cases to the orderBy branch in the model
536
535
 
537
536
  ```typescript
538
- // model.ts - orderBy 케이스 추가 예시
537
+ // model.ts adding orderBy cases
539
538
  if (params.orderBy === "id-desc") {
540
539
  qb.orderBy("products.id", "desc");
541
540
  } else if (params.orderBy === "name-asc") {
542
- // 추가
541
+ // added
543
542
  qb.orderBy("products.name", "asc");
544
543
  } else {
545
544
  exhaustive(params.orderBy);
546
545
  }
547
546
  ```
548
547
 
549
- **규칙**: 처음에는 `id-desc`만 생성scaffolding 완료 필요시 추가
548
+ **Rule**: start with `id-desc` onlyadd others as needed after scaffolding
550
549
 
551
- ## IMPORTANT: integer vs number 타입 선택 기준
550
+ ## IMPORTANT: integer vs number Type Selection
552
551
 
553
- **CRITICAL: 숫자 필드 생성 반드시 아래 기준을 따른다. 잘못된 타입 선택은 불필요한 ALTER migration을 유발한다.**
552
+ **CRITICAL: Always follow the criteria below when creating numeric fields. Wrong type choice causes unnecessary ALTER migrations.**
554
553
 
555
- PostgreSQL 기준:
554
+ PostgreSQL mapping:
556
555
 
557
- - `integer` → DB `integer` (정수)
558
- - `number` → DB `numeric(p,s)` (소수점 포함 정밀 숫자)
556
+ - `integer` → DB `integer` (whole numbers)
557
+ - `number` → DB `numeric(p,s)` (precise decimal numbers)
559
558
 
560
- | 용도 | Entity 타입 | 예시 |
561
- | ------------------------------ | --------------------------------- | -------------------------------- |
562
- | PK, FK, 카운트, 순서, 수량 | `integer` | id, user_id, order_num, quantity |
563
- | 금액, 비율, 소수점이 필요한 | `number` (+ `precision`, `scale`) | price, rate, weight, score |
559
+ | Use case | Entity type | Example |
560
+ |----------|-------------|---------|
561
+ | PK, FK, count, order, quantity | `integer` | id, user_id, order_num, quantity |
562
+ | Amount, ratio, values requiring decimal | `number` (+ `precision`, `scale`) | price, rate, weight, score |
564
563
 
565
564
  **DO NOT:**
566
565
 
567
566
  ```json
568
- { "name": "order_num", "type": "number", "desc": "정렬순서" }
569
- { "name": "quantity", "type": "number", "desc": "수량" }
567
+ { "name": "order_num", "type": "number", "desc": "Sort order" }
568
+ { "name": "quantity", "type": "number", "desc": "Quantity" }
570
569
  ```
571
570
 
572
571
  **DO:**
573
572
 
574
573
  ```json
575
- { "name": "order_num", "type": "integer", "desc": "정렬순서" }
576
- { "name": "quantity", "type": "integer", "desc": "수량" }
577
- { "name": "price", "type": "number", "precision": 12, "scale": 2, "desc": "금액" }
578
- { "name": "rate", "type": "number", "precision": 5, "scale": 2, "desc": "비율" }
574
+ { "name": "order_num", "type": "integer", "desc": "Sort order" }
575
+ { "name": "quantity", "type": "integer", "desc": "Quantity" }
576
+ { "name": "price", "type": "number", "precision": 12, "scale": 2, "desc": "Price" }
577
+ { "name": "rate", "type": "number", "precision": 5, "scale": 2, "desc": "Rate" }
579
578
  ```
580
579
 
581
- **판단 기준: " 값에 소수점이 필요한가?"**
580
+ **Decision rule: "Does this value ever need a decimal point?"**
582
581
 
583
582
  - No → `integer`
584
- - Yes → `number` (반드시 `precision`, `scale` 명시)
585
-
586
- ## 공통 옵션 (CommonProp)
587
-
588
- 모든 prop 타입에 적용 가능한 공통 옵션:
589
-
590
- | 옵션 | 타입 | 설명 |
591
- |------|------|------|
592
- | `name` | string | 필드명 (필수) |
593
- | `desc` | string | 필드 설명 |
594
- | `nullable` | boolean | NULL 허용 여부 (기본: false) |
595
- | `toFilter` | true | sonamuFilter 필터링 대상으로 등록. model.md 참조 |
596
- | `cone` | Cone | LLM 기반 fixture 생성 메타데이터. cone.md 참조 |
597
-
598
- ## 타입별 필수 옵션
599
-
600
- | 타입 | 필수 | 선택 |
601
- | ------------ | ------------ | ----------------------------------------------------------- |
602
- | `string` | - | `length` (없으면 text), `zodFormat` (email, uuid ) |
603
- | `integer` | - | - |
604
- | `bigInteger` | - | - |
605
- | `number` | - | `precision`, `scale`, `numberType` (real/double precision/numeric) |
606
- | `numeric` | - | `precision`, `scale` |
607
- | `enum` | `id` | `nullable`, `dbDefault`, `length` |
608
- | `json` | `id` | `dbDefault: "{}"` |
609
- | `date` | - | `dbDefault`, `precision` |
610
- | `boolean` | - | `dbDefault: "false"` |
611
- | `virtual` | `id` | `virtualType` (query/code, 기본: code) |
612
- | `vector` | `dimensions` | - |
613
- | `tsvector` | - | - |
583
+ - Yes → `number` (always specify `precision` and `scale`)
584
+
585
+ ## Common Options (CommonProp)
586
+
587
+ Options applicable to all prop types:
588
+
589
+ | Option | Type | Description |
590
+ |--------|------|-------------|
591
+ | `name` | string | Field name (required) |
592
+ | `desc` | string | Field description |
593
+ | `nullable` | boolean | Whether NULL is allowed (default: false) |
594
+ | `toFilter` | true | Register as a sonamuFilter filtering target. See model.md |
595
+ | `cone` | Cone | LLM-based fixture generation metadata. See cone.md |
596
+
597
+ ## Required Options by Type
598
+
599
+ | Type | Required | Optional |
600
+ |------|----------|---------|
601
+ | `string` | | `length` (text if omitted), `zodFormat` (email, uuid, etc.) |
602
+ | `integer` | | |
603
+ | `bigInteger` | | |
604
+ | `number` | | `precision`, `scale`, `numberType` (real/double precision/numeric) |
605
+ | `numeric` | | `precision`, `scale` |
606
+ | `enum` | `id` | `nullable`, `dbDefault`, `length` |
607
+ | `json` | `id` | `dbDefault: "{}"` |
608
+ | `date` | | `dbDefault`, `precision` |
609
+ | `boolean` | | `dbDefault: "false"` |
610
+ | `virtual` | `id` | `virtualType` (query/code, default: code) |
611
+ | `vector` | `dimensions` | |
612
+ | `tsvector` | | |
614
613
 
615
614
  ## IMPORTANT: ENUM Type dbDefault Setting
616
615
 
617
- ENUM 필드에 기본값을 설정할 때는 **이스케이프된 큰따옴표**로 값을 감싸야 합니다.
616
+ When setting a default value on an ENUM field, the value must be wrapped in **escaped double quotes**.
618
617
 
619
618
  ### DO NOT - Incorrect Examples
620
619
 
621
620
  ```json
622
- // Incorrect: 따옴표 없음 - SQL에서 컬럼 참조로 해석되어 오류 발생
621
+ // Incorrect: no quotes interpreted as a column reference in SQL, causes error
623
622
  { "name": "status", "type": "enum", "id": "ApprovalStatus", "dbDefault": "pending" }
624
- // 오류: cannot use column reference in DEFAULT expression
623
+ // Error: cannot use column reference in DEFAULT expression
625
624
 
626
- // Incorrect: 작은따옴표 사용 - Biome format error 발생
625
+ // Incorrect: single quotes causes Biome format error
627
626
  { "name": "status", "type": "enum", "id": "ApprovalStatus", "dbDefault": "'pending'" }
628
627
  ```
629
628
 
630
629
  ### DO - Correct Example
631
630
 
632
631
  ```json
633
- // Correct: 이스케이프된 큰따옴표 사용
632
+ // Correct: escaped double quotes
634
633
  {
635
634
  "name": "status",
636
635
  "type": "enum",
637
636
  "id": "ApprovalStatus",
638
637
  "dbDefault": "\"pending\"",
639
- "desc": "결재상태"
638
+ "desc": "Approval status"
640
639
  }
641
640
  ```
642
641