@axboot-mcp/mcp-server 1.0.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 (78) hide show
  1. package/CLAUDE.md +119 -0
  2. package/MCP_TOOL_PLAN.md +710 -0
  3. package/MCP_USAGE.md +914 -0
  4. package/README.md +168 -0
  5. package/REPOSITORY_CONVENTIONS.md +250 -0
  6. package/SEARCH_PARAMS_MCP_TOOL_COMPLETE_PLAN.md +646 -0
  7. package/SEARCH_PARAMS_PLAN.md +2570 -0
  8. package/STORE_PATTERNS.md +1178 -0
  9. package/debug-dto.js +72 -0
  10. package/generate-banner-store.js +62 -0
  11. package/generation-plan.json +2176 -0
  12. package/generation-results.json +1817 -0
  13. package/package.json +45 -0
  14. package/scripts/batch-generate-all.js +159 -0
  15. package/scripts/batch-generate-mcp.js +329 -0
  16. package/scripts/batch-generate-stores-v2.js +272 -0
  17. package/scripts/batch-generate-stores.js +179 -0
  18. package/scripts/batch-plan.json +3810 -0
  19. package/scripts/batch-process.py +90 -0
  20. package/scripts/batch-regenerate.js +356 -0
  21. package/scripts/direct-generate.js +227 -0
  22. package/scripts/execute-batches.js +1911 -0
  23. package/scripts/generate-all-stores.js +144 -0
  24. package/scripts/generate-stores-mcp.js +161 -0
  25. package/scripts/generate-stores-v2.js +450 -0
  26. package/scripts/generate-stores-v3.js +412 -0
  27. package/scripts/generate-stores-v4.js +521 -0
  28. package/scripts/generate-stores.js +382 -0
  29. package/scripts/repos-to-process.json +1899 -0
  30. package/src/config/nh-layout-patterns.ts +166 -0
  31. package/src/docs/HOOK_GENERATION_PLAN.md +2226 -0
  32. package/src/docs/NH_STORE_PATTERNS.md +297 -0
  33. package/src/docs/README.md +216 -0
  34. package/src/docs/index.ts +28 -0
  35. package/src/docs/loader.ts +568 -0
  36. package/src/docs/patterns.json +419 -0
  37. package/src/docs/practical-examples.md +732 -0
  38. package/src/docs/quick-start.md +257 -0
  39. package/src/docs/requirements-analysis-guide.md +364 -0
  40. package/src/docs/rules.json +321 -0
  41. package/src/docs/store-pattern-analysis.md +664 -0
  42. package/src/docs/store-patterns-rules.md +1168 -0
  43. package/src/docs/store-patterns-usage-guide.md +1835 -0
  44. package/src/docs/troubleshooting.md +544 -0
  45. package/src/docs/type-selection-guide.md +572 -0
  46. package/src/docs//354/202/254/354/232/251/353/262/225/AntD-/354/273/264/355/217/254/353/204/214/355/212/270-/354/202/254/354/232/251/353/262/225.md +1515 -0
  47. package/src/docs//354/202/254/354/232/251/353/262/225/DataGrid-/354/202/254/354/232/251/353/262/225.md +866 -0
  48. package/src/docs//354/202/254/354/232/251/353/262/225/FormItem-/354/202/254/354/232/251/353/262/225.md +903 -0
  49. package/src/docs//354/202/254/354/232/251/353/262/225/FormModal-/354/202/254/354/232/251/353/262/225.md +1155 -0
  50. package/src/docs//354/202/254/354/232/251/353/262/225/MCP-/353/260/224/354/235/264/353/270/214/354/275/224/353/224/251-/352/260/200/354/235/264/353/223/234.md +1133 -0
  51. package/src/docs//354/202/254/354/232/251/353/262/225/MSW-Mock-/353/215/260/354/235/264/355/204/260-/354/202/254/354/232/251/353/262/225.md +579 -0
  52. package/src/docs//354/202/254/354/232/251/353/262/225/Search-/354/273/264/355/217/254/353/204/214/355/212/270-/354/202/254/354/232/251/353/262/225.md +738 -0
  53. package/src/docs//354/202/254/354/232/251/353/262/225/Store-/355/214/250/355/204/264-/354/202/254/354/232/251/353/262/225.md +1135 -0
  54. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/224/353/251/264/352/265/254/354/204/261-/355/203/200/354/236/205/353/263/204-/352/260/234/353/260/234/354/210/234/354/204/234.md +1805 -0
  55. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/224/353/251/264/355/203/200/354/236/205/353/263/204-/352/260/234/353/260/234-/355/224/204/353/241/254/355/224/204/355/212/270-/352/260/200/354/235/264/353/223/234.md +946 -0
  56. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/225/354/236/245/355/231/224/353/251/264/355/203/200/354/236/205/353/263/204-/354/203/201/354/204/270-/355/224/204/353/241/254/355/224/204/355/212/270/352/260/200/354/235/264/353/223/234.md +2422 -0
  57. package/src/features/store-features.ts +232 -0
  58. package/src/handlers/analyze-requirements.ts +403 -0
  59. package/src/handlers/analyze.ts +1373 -0
  60. package/src/handlers/generate-from-requirements.ts +250 -0
  61. package/src/handlers/generate-hook.ts +950 -0
  62. package/src/handlers/generate-interactive.ts +840 -0
  63. package/src/handlers/generate-listdatagrid.ts +521 -0
  64. package/src/handlers/generate-multi-stores.ts +577 -0
  65. package/src/handlers/generate-requirements-from-layout.ts +160 -0
  66. package/src/handlers/generate-search-params.ts +717 -0
  67. package/src/handlers/generate.ts +911 -0
  68. package/src/handlers/list-templates.ts +104 -0
  69. package/src/handlers/scan-metadata.ts +485 -0
  70. package/src/handlers/suggest-layout.ts +326 -0
  71. package/src/index.ts +959 -0
  72. package/src/prompts/search-params.md +793 -0
  73. package/src/templates/index.ts +107 -0
  74. package/src/templates/unified.ts +462 -0
  75. package/store-generation-error-patterns.md +225 -0
  76. package/test/useAgentStore.ts +136 -0
  77. package/test-server.js +78 -0
  78. package/tsconfig.json +20 -0
@@ -0,0 +1,664 @@
1
+ # AXBoot Zustand Store 패턴 분석
2
+
3
+ ## 개요
4
+
5
+ NH-FE-B 프로젝트의 `/src/pages/resources/NH` 하위 Store 파일들에서 발견되는 일관된 패턴을 분석한 문서입니다. 이 패턴은 MCP-AXBoot의 Store 생성 템플릿에 반영하기 위한 목적으로 작성되었습니다.
6
+
7
+ ---
8
+
9
+ ## 1. 전체 파일 구조
10
+
11
+ ```typescript
12
+ // 1. Imports
13
+ import { AXDGDataItem, AXDGPage, AXDGSortParam } from "@axboot/datagrid";
14
+ import { pageStoreActions } from "@core/stores/pageStoreActions";
15
+ import { PageStoreActions, StoreActions } from "@core/stores/types";
16
+ import { ProgramFn } from "@types";
17
+ import { XXXService, PostXXXListRequest, XXXRes } from "services";
18
+ import { getTabStoreListener } from "stores";
19
+ import { create } from "zustand";
20
+ import { subscribeWithSelector } from "zustand/middleware";
21
+ import { shallow } from "zustand/shallow";
22
+
23
+ // 2. Interface 정의 (ListRequest, DtoItem, SaveRequest 등)
24
+ interface ListRequest extends PostXXXListRequest { ... }
25
+ interface DtoItem extends XXXRes {}
26
+ interface SaveRequest { ... }
27
+
28
+ // 3. MetaData 인터페이스 (페이지 모델에 저장될 필드)
29
+ interface MetaData {
30
+ programFn?: ProgramFn;
31
+ listRequestValue: ListRequest;
32
+ listColWidths: number[];
33
+ listSortParams: AXDGSortParam[];
34
+ // ... 추가 필드들
35
+ }
36
+
37
+ // 4. States 인터페이스
38
+ interface States extends MetaData {
39
+ routePath?: string;
40
+ listSpinning: boolean;
41
+ listData: AXDGDataItem<DtoItem>[];
42
+ listPage: AXDGPage;
43
+ // ... 추가 상태들
44
+ }
45
+
46
+ // 5. createState 초기값
47
+ const createState: States = { ... };
48
+
49
+ // 6. Actions 인터페이스
50
+ interface Actions extends PageStoreActions<States> {
51
+ // ... 액션 메서드들
52
+ }
53
+
54
+ // 7. createActions 구현
55
+ const createActions: StoreActions<States & Actions, Actions> = (set, get) => ({ ... });
56
+
57
+ // 8. Store 타입 내보내기
58
+ export interface xxxStore extends States, Actions, PageStoreActions<States> {}
59
+
60
+ // 9. Store 생성
61
+ export const useXXXStore = create(subscribeWithSelector<xxxStore>((set, get) => ({
62
+ ...createState,
63
+ ...createActions(set, get),
64
+ })));
65
+
66
+ // 10. Store.subscribe (페이지 모델 동기화)
67
+ useXXXStore.subscribe(
68
+ (s): Record<keyof MetaData, any> => ({ ... }),
69
+ getTabStoreListener<MetaData>(createState.routePath),
70
+ { equalityFn: shallow },
71
+ );
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 2. createActions 패턴 상세 분석
77
+
78
+ ### 2.1 필수 액션 (모든 Store에 공통)
79
+
80
+ ```typescript
81
+ const createActions: StoreActions<States & Actions, Actions> = (set, get) => ({
82
+ // 1) 메타데이터 동기화 - 상태를 초기값으로 재설정
83
+ syncMetadata: (s = createState) => set(s),
84
+
85
+ // 2) 앱 마운트 시 실행
86
+ onMountApp: async () => {
87
+ // onDidMount and store initialized
88
+ },
89
+
90
+ // 3) 리스트 요청값 설정
91
+ setListRequestValue: (requestValues, changedValues) => {
92
+ set({ listRequestValue: requestValues });
93
+ // 또는
94
+ set({
95
+ listRequestValue: {
96
+ ...get().listRequestValue,
97
+ ...requestValues,
98
+ },
99
+ });
100
+ },
101
+
102
+ // 4) 컬럼 너비 설정
103
+ setListColWidths: (colWidths) => set({ listColWidths: colWidths }),
104
+
105
+ // 5) 로딩 스피닝 설정
106
+ setListSpinning: (spinning) => set({ listSpinning: spinning }),
107
+
108
+ // 6) 정렬 파라미터 설정
109
+ setListSortParams: (sortParams) => set({ listSortParams: sortParams }),
110
+
111
+ // 7) pageStoreActions 확장 (항상 마지막에 위치)
112
+ ...pageStoreActions(set, get, { createState }),
113
+ });
114
+ ```
115
+
116
+ ### 2.2 리스트 API 호출 패턴 (callListApi)
117
+
118
+ ```typescript
119
+ callListApi: async (request) => {
120
+ if (get().listSpinning) return; // 중복 호출 방지
121
+ set({ listSpinning: true });
122
+
123
+ try {
124
+ const apiParam: ListRequest = { ...get().listRequestValue, ...request };
125
+ const response = await XXXService.postXXXList(
126
+ deleteEmptyValue({ ...apiParam })
127
+ );
128
+
129
+ set({
130
+ listRequestValue: apiParam,
131
+ listData: response.ds.map((values) => ({ values })),
132
+ listPage: {
133
+ currentPage: response.page.pageNumber ?? 1,
134
+ pageSize: response.page.pageSize ?? 0,
135
+ totalPages: response.page.pageCount ?? 0,
136
+ totalElements: response.page?.totalCount,
137
+ },
138
+ });
139
+
140
+ return response; // 선택사항
141
+ } finally {
142
+ set({ listSpinning: false });
143
+ }
144
+ },
145
+ ```
146
+
147
+ ### 2.3 페이지 변경 패턴 (changeListPage)
148
+
149
+ ```typescript
150
+ changeListPage: async (pageNumber, pageSize) => {
151
+ await get().callListApi({
152
+ pageNumber,
153
+ pageSize,
154
+ // 추가 파라미터가 필요한 경우
155
+ });
156
+ },
157
+ ```
158
+
159
+ ### 2.4 선택 관련 액션 패턴
160
+
161
+ #### 단일 선택 (setSelectedItem)
162
+ ```typescript
163
+ setSelectedItem: async (selectedItem) => {
164
+ set({ selectedItem, detailLoading: true });
165
+
166
+ if (!selectedItem || selectedItem["__status__"] === "C") {
167
+ set({ saveRequestValue: {} });
168
+ }
169
+
170
+ await delay(300); // 딜레이 (선택사항)
171
+
172
+ set({ detailLoading: false });
173
+ },
174
+ ```
175
+
176
+ #### 행 키 선택 (setListSelectedRowKey)
177
+ ```typescript
178
+ setListSelectedRowKey: async (key, detail) => {
179
+ if (detail) {
180
+ set({ listSelectedRowKey: key, detail, saveRequestValue: { ...detail } });
181
+ } else {
182
+ set({ listSelectedRowKey: undefined, detail: undefined, saveRequestValue: undefined });
183
+ }
184
+ },
185
+
186
+ // 단순 버전
187
+ setListSelectedRowKey: (key) => {
188
+ set({ listSelectedRowKey: key });
189
+ },
190
+ ```
191
+
192
+ #### 체크박스 관련
193
+ ```typescript
194
+ setCheckedRowKeys: (checkedRowKeys) => set({ checkedRowKeys }),
195
+ setListCheckedIndexes: (indexes) => set({ listCheckedIndexes: indexes }),
196
+ setListCheckedKeys: (checkedKeys) => set({ listCheckedKeys: checkedKeys }),
197
+ ```
198
+
199
+ ### 2.5 저장 API 패턴 (callSaveApi)
200
+
201
+ ```typescript
202
+ callSaveApi: async (request) => {
203
+ if (get().saveSpinning) return;
204
+ set({ saveSpinning: true });
205
+
206
+ try {
207
+ const apiParam: SaveRequest = {
208
+ ...get().saveRequestValue,
209
+ ...request,
210
+ };
211
+
212
+ // 상태 플래그 설정 (신규/수정)
213
+ apiParam.__status__ = get().listSelectedRowKey ? "U" : "C";
214
+
215
+ const { rs } = await XXXService.postXXXSave(apiParam);
216
+
217
+ // 응답으로 상태 업데이트
218
+ set({ saveRequestValue: rs, detail: rs });
219
+
220
+ // 저장 후 목록 갱신
221
+ await get().callListApi();
222
+ } finally {
223
+ set({ saveSpinning: false });
224
+ }
225
+ },
226
+ ```
227
+
228
+ ### 2.6 삭제 API 패턴 (callDeleteApi)
229
+
230
+ ```typescript
231
+ callDeleteApi: async (request) => {
232
+ if (get().deleteSpinning) return;
233
+ set({ deleteSpinning: true });
234
+
235
+ try {
236
+ const apiParam: RemoveRequest = {
237
+ ...get().saveRequestValue,
238
+ ...request,
239
+ __status__: "D",
240
+ };
241
+
242
+ await XXXService.postXXXDelete(apiParam);
243
+
244
+ // 삭제 후 목록 갱신
245
+ await get().callListApi();
246
+ } finally {
247
+ set({ deleteSpinning: false });
248
+ }
249
+ },
250
+ ```
251
+
252
+ ### 2.7 엑셀 다운로드 패턴
253
+
254
+ ```typescript
255
+ // 별도 스피닝 사용
256
+ callExcelDownloadApi: async (request) => {
257
+ if (get().excelSpinning) return;
258
+ set({ excelSpinning: true });
259
+
260
+ try {
261
+ const { pageNumber, pageSize, ...restParams }: ListRequest = {
262
+ ...get().listRequestValue,
263
+ ...request,
264
+ };
265
+
266
+ await XXXService.postXXXListExcel(
267
+ deleteEmptyValue({ ...restParams })
268
+ );
269
+ } finally {
270
+ set({ excelSpinning: false });
271
+ }
272
+ },
273
+
274
+ // 메서드명이 downloadExcel인 경우
275
+ downloadExcel: async () => {
276
+ try {
277
+ const apiParam = deleteEmptyValue({
278
+ ...get().listRequestValue,
279
+ // 추가 변환 로직
280
+ });
281
+
282
+ await XXXService.postXXXExcelDownload(apiParam);
283
+ } catch (error) {
284
+ console.error("엑셀 다운로드 오류:", error);
285
+ throw error;
286
+ }
287
+ },
288
+ ```
289
+
290
+ ### 2.8 폼 활성화/비활성화 패턴
291
+
292
+ ```typescript
293
+ setFormActive: () => {
294
+ set({ formActive: true, detail: undefined, saveRequestValue: undefined });
295
+ },
296
+
297
+ cancelFormActive: () => {
298
+ set({ formActive: false, listSelectedRowKey: undefined });
299
+ },
300
+ ```
301
+
302
+ ### 2.9 대시보드/다중 리스트 패턴
303
+
304
+ ```typescript
305
+ // 여러 리스트의 요청값/정렬/스피닝을 개별 관리
306
+ setRequest01Value: (requestValues) => set({ listRequest01Value: requestValues }),
307
+ setRequest02Value: (requestValues) => set({ listRequest02Value: requestValues }),
308
+ setList01SortParams: (sortParams) => set({ list01SortParams: sortParams }),
309
+ setList02SortParams: (sortParams) => set({ list02SortParams: sortParams }),
310
+ setActiveTabKey: (key) => set({ activeTabKey: key }),
311
+
312
+ // 동적 스피닝 설정
313
+ setListSpinning: (spinning, target = "01") =>
314
+ set({ [`list${target}Spinning`]: spinning }),
315
+ ```
316
+
317
+ ---
318
+
319
+ ## 3. Store.subscribe 패턴 상세 분석
320
+
321
+ ### 3.1 기본 구조
322
+
323
+ ```typescript
324
+ useXXXStore.subscribe(
325
+ (s): Record<keyof MetaData, any> => ({
326
+ // Selector: Store 상태에서 필요한 필드만 추출
327
+ }),
328
+ getTabStoreListener<MetaData>(createState.routePath), // Listener
329
+ { equalityFn: shallow }, // Options
330
+ );
331
+ ```
332
+
333
+ ### 3.2 필드 조합별 패턴
334
+
335
+ #### Pattern 1: 기본 리스트형 (가장 일반적)
336
+ ```typescript
337
+ interface MetaData {
338
+ programFn?: ProgramFn;
339
+ listRequestValue: ListRequest;
340
+ listColWidths: number[];
341
+ listSortParams: AXDGSortParam[];
342
+ }
343
+
344
+ useXXXStore.subscribe(
345
+ (s): Record<keyof MetaData, any> => ({
346
+ programFn: s.programFn,
347
+ listSortParams: s.listSortParams,
348
+ listRequestValue: s.listRequestValue,
349
+ listColWidths: s.listColWidths,
350
+ }),
351
+ getTabStoreListener<MetaData>(createState.routePath),
352
+ { equalityFn: shallow },
353
+ );
354
+ ```
355
+
356
+ #### Pattern 2: 리스트 + 상세폼
357
+ ```typescript
358
+ interface MetaData {
359
+ programFn?: ProgramFn;
360
+ listRequestValue: ListRequest;
361
+ listColWidths: number[];
362
+ listSortParams: AXDGSortParam[];
363
+ saveRequestValue: SaveRequest;
364
+ }
365
+
366
+ useXXXStore.subscribe(
367
+ (s): Record<keyof MetaData, any> => ({
368
+ programFn: s.programFn,
369
+ listSortParams: s.listSortParams,
370
+ listRequestValue: s.listRequestValue,
371
+ listColWidths: s.listColWidths,
372
+ saveRequestValue: s.saveRequestValue,
373
+ }),
374
+ getTabStoreListener<MetaData>(createState.routePath),
375
+ { equalityFn: shallow },
376
+ );
377
+ ```
378
+
379
+ #### Pattern 3: 마스터-디테일
380
+ ```typescript
381
+ interface MetaData {
382
+ programFn?: ProgramFn;
383
+ listRequestValue: ListRequest;
384
+ listColWidths: number[];
385
+ listSortParams: AXDGSortParam[];
386
+ listSelectedRowKey?: React.Key;
387
+ }
388
+
389
+ useXXXStore.subscribe(
390
+ (s): Record<keyof MetaData, any> => ({
391
+ programFn: s.programFn,
392
+ listSortParams: s.listSortParams,
393
+ listRequestValue: s.listRequestValue,
394
+ listColWidths: s.listColWidths,
395
+ listSelectedRowKey: s.listSelectedRowKey,
396
+ }),
397
+ getTabStoreListener<MetaData>(createState.routePath),
398
+ { equalityFn: shallow },
399
+ );
400
+ ```
401
+
402
+ #### Pattern 4: 리스트 + 체크박스
403
+ ```typescript
404
+ interface MetaData {
405
+ programFn?: ProgramFn;
406
+ listRequestValue: ListRequest;
407
+ listColWidths: number[];
408
+ listSortParams: AXDGSortParam[];
409
+ listSelectedRowKey?: React.Key;
410
+ listCheckedIndexes?: number[];
411
+ }
412
+
413
+ useXXXStore.subscribe(
414
+ (s): Record<keyof MetaData, any> => ({
415
+ programFn: s.programFn,
416
+ listSortParams: s.listSortParams,
417
+ listRequestValue: s.listRequestValue,
418
+ listColWidths: s.listColWidths,
419
+ listSelectedRowKey: s.listSelectedRowKey,
420
+ listCheckedIndexes: s.listCheckedIndexes,
421
+ }),
422
+ getTabStoreListener<MetaData>(createState.routePath),
423
+ { equalityFn: shallow },
424
+ );
425
+ ```
426
+
427
+ #### Pattern 5: CRUD (리스트 + 상세 + 체크박스)
428
+ ```typescript
429
+ interface MetaData {
430
+ programFn?: ProgramFn;
431
+ listRequestValue: ListRequest;
432
+ listColWidths: number[];
433
+ listSortParams: AXDGSortParam[];
434
+ listSelectedRowKey?: React.Key;
435
+ flexGrow: number;
436
+ saveRequestValue: SaveRequest;
437
+ detail?: SaveRequest;
438
+ formActive: boolean;
439
+ listCheckedKeys: React.Key[];
440
+ }
441
+
442
+ useXXXStore.subscribe(
443
+ (s): Record<keyof MetaData, any> => ({
444
+ programFn: s.programFn,
445
+ listSortParams: s.listSortParams,
446
+ listRequestValue: s.listRequestValue,
447
+ listColWidths: s.listColWidths,
448
+ listSelectedRowKey: s.listSelectedRowKey,
449
+ flexGrow: s.flexGrow,
450
+ saveRequestValue: s.saveRequestValue,
451
+ detail: s.detail,
452
+ formActive: s.formActive,
453
+ listCheckedKeys: s.listCheckedKeys,
454
+ }),
455
+ getTabStoreListener<MetaData>(createState.routePath),
456
+ { equalityFn: shallow },
457
+ );
458
+ ```
459
+
460
+ #### Pattern 6: 대시보드/다중 리스트
461
+ ```typescript
462
+ interface MetaData {
463
+ programFn?: ProgramFn;
464
+ summaryRequestValue: SummaryRequest;
465
+ listRequest01Value: ListRequest01;
466
+ listRequest02Value: ListRequest02;
467
+ activeTabKey: PanelType;
468
+ list01SortParams: AXDGSortParam[];
469
+ list02SortParams: AXDGSortParam[];
470
+ }
471
+
472
+ useXXXStore.subscribe(
473
+ (s): Record<keyof MetaData, any> => ({
474
+ programFn: s.programFn,
475
+ summaryRequestValue: s.summaryRequestValue,
476
+ listRequest01Value: s.listRequest01Value,
477
+ listRequest02Value: s.listRequest02Value,
478
+ activeTabKey: s.activeTabKey,
479
+ list01SortParams: s.list01SortParams,
480
+ list02SortParams: s.list02SortParams,
481
+ }),
482
+ getTabStoreListener<MetaData>(createState.routePath),
483
+ { equalityFn: shallow },
484
+ );
485
+ ```
486
+
487
+ ### 3.3 MetaData 필드별 설명
488
+
489
+ | 필드명 | 타입 | 설명 | 필수여부 |
490
+ |--------|------|------|----------|
491
+ | `programFn` | `ProgramFn \| undefined` | 프로그램 함수 참조 | 기본 |
492
+ | `listSortParams` | `AXDGSortParam[]` | 그리드 정렬 파라미터 | 기본 |
493
+ | `listRequestValue` | `ListRequest` | 리스트 요청값 (검색조건 등) | 기본 |
494
+ | `listColWidths` | `number[]` | 그리드 컬럼 너비 배열 | 기본 |
495
+ | `saveRequestValue` | `SaveRequest` | 저장/상세 요청값 | 선택 |
496
+ | `selectedItem` | `DtoItem \| undefined` | 선택된 아이템 | 선택 |
497
+ | `listSelectedRowKey` | `React.Key \| undefined` | 선택된 행 키 | 선택 |
498
+ | `listCheckedIndexes` | `number[] \| undefined` | 체크된 인덱스 목록 | 선택 |
499
+ | `checkedRowKeys` | `React.Key[]` | 체크된 행 키 목록 | 선택 |
500
+ | `listCheckedKeys` | `React.Key[]` | 체크된 키 목록 | 선택 |
501
+ | `flexGrow` | `number` | 플렉스 성장 비율 (레이아웃) | 선택 |
502
+ | `detail` | `SaveRequest \| undefined` | 상세 데이터 | 선택 |
503
+ | `formActive` | `boolean` | 폼 활성화 상태 | 선택 |
504
+ | `activeTabKey` | `string` | 활성 탭 키 | 선택 |
505
+ | `summaryRequestValue` | `SummaryRequest` | 요약 요청값 | 선택 |
506
+
507
+ ### 3.4 필드 네이밍 규칙 (다중 리스트)
508
+
509
+ 다중 리스트 패턴에서는 다음과 같은 네이밍 규칙이 사용됩니다:
510
+
511
+ ```
512
+ - listRequestValue → listRequest01Value, listRequest02Value, ...
513
+ - listSortParams → list01SortParams, list02SortParams, ...
514
+ - listData → list01Data, list02Data, ...
515
+ - listPage → list01Page, list02Page, ...
516
+ - listSpinning → list01Spinning, list02Spinning, ...
517
+ ```
518
+
519
+ ---
520
+
521
+ ## 4. States 필드별 초기값 패턴
522
+
523
+ ### 4.1 기본 States 필드
524
+
525
+ ```typescript
526
+ interface States {
527
+ routePath?: string; // 초기값: undefined
528
+ listSpinning: boolean; // 초기값: false
529
+ listData: AXDGDataItem<DtoItem>[]; // 초기값: []
530
+ listPage: AXDGPage; // 초기값: { currentPage: 0, totalPages: 0 }
531
+ }
532
+ ```
533
+
534
+ ### 4.2 createState 초기화 패턴
535
+
536
+ ```typescript
537
+ const createState: States = {
538
+ // 검색조건 초기값
539
+ listRequestValue: {
540
+ pageNumber: 1,
541
+ pageSize: 100,
542
+ // 추가 검색조건...
543
+ },
544
+
545
+ // UI 상태 초기값
546
+ listColWidths: [],
547
+ listSpinning: false,
548
+ listData: [],
549
+ listPage: {
550
+ currentPage: 0,
551
+ totalPages: 0,
552
+ },
553
+ listSortParams: [],
554
+
555
+ // 선택 관련 초기값
556
+ listSelectedRowKey: "",
557
+ checkedRowKeys: [],
558
+
559
+ // 스피닝 초기값 (필요시)
560
+ excelSpinning: false,
561
+ saveSpinning: false,
562
+ detailSpinning: false,
563
+ deleteSpinning: false,
564
+ };
565
+ ```
566
+
567
+ ---
568
+
569
+ ## 5. 타입 정의 패턴
570
+
571
+ ### 5.1 ListRequest 정의
572
+
573
+ ```typescript
574
+ // 기본: Repository Request 타입 확장
575
+ interface ListRequest extends PostXXXListRequest {
576
+ // 추가 필드 (dateRange 등)
577
+ dateRange?: [string, string];
578
+ }
579
+
580
+ // Partial 사용 (일부 필드만 필수로)
581
+ interface ListRequest extends Partial<PostXXXListRequest> {
582
+ dateRange?: [string, string];
583
+ }
584
+ ```
585
+
586
+ ### 5.2 SaveRequest 정의
587
+
588
+ ```typescript
589
+ // Repository Save Request 확장
590
+ interface SaveRequest extends PostXXXSaveRequest {}
591
+
592
+ // 또는 별도 정의
593
+ interface SaveRequest {
594
+ // 필드들...
595
+ }
596
+ ```
597
+
598
+ ### 5.3 DtoItem 정의
599
+
600
+ ```typescript
601
+ // Repository Response 타입 확장
602
+ interface DtoItem extends XXXRes {}
603
+ ```
604
+
605
+ ---
606
+
607
+ ## 6. Store 타입 내보내기 패턴
608
+
609
+ ```typescript
610
+ // Store 이름: camelCase + "Store"
611
+ export interface xxxStore extends States, Actions, PageStoreActions<States> {}
612
+
613
+ // Hook 이름: "use" + PascalCase + "Store"
614
+ export const useXXXStore = create(
615
+ subscribeWithSelector<xxxStore>((set, get) => ({
616
+ ...createState,
617
+ ...createActions(set, get),
618
+ })),
619
+ );
620
+ ```
621
+
622
+ ---
623
+
624
+ ## 7. 요약: 템플릿 생성을 위한 결정사항
625
+
626
+ ### 7.1 Store 타입별 분류
627
+
628
+ | 타입 | 설명 | 주요 패턴 |
629
+ |------|------|-----------|
630
+ | **기본 리스트형** | 단순 조회만 | Pattern 1 |
631
+ | **리스트+상세** | 조회 + 상세보기 | Pattern 2 |
632
+ | **마스터-디테일** | 마스터 선택 시 디테일 표시 | Pattern 3 |
633
+ | **체크박스 포함** | 다중 선택 기능 | Pattern 4 |
634
+ | **CRUD** | 생성/조회/수정/删除 | Pattern 5 |
635
+ | **대시보드** | 여러 데이터 표시 | Pattern 6 |
636
+ | **다중 리스트** | 여러 그리드 | 다중 리스트 패턴 |
637
+
638
+ ### 7.2 createActions 구현 순서 (권장)
639
+
640
+ 1. `syncMetadata`
641
+ 2. `onMountApp`
642
+ 3. Setter들 (`setListRequestValue`, `setListColWidths`, `setListSpinning`, `setListSortParams`)
643
+ 4. API 호출 (`callListApi`, `callSaveApi`, `callDeleteApi`, `callExcelDownloadApi`)
644
+ 5. 페이지 변경 (`changeListPage`)
645
+ 6. 선택 관련 (`setSelectedItem`, `setListSelectedRowKey`, `setCheckedRowKeys`)
646
+ 7. 폼 관련 (`setFormActive`, `cancelFormActive`)
647
+ 8. 기타 커스텀 액션
648
+ 9. `...pageStoreActions(set, get, { createState })`
649
+
650
+ ### 7.3 MetaData 필드 포함 기준
651
+
652
+ | 필드 | 포함 기준 |
653
+ |------|-----------|
654
+ | `programFn` | 항상 포함 |
655
+ | `listSortParams` | 정렬 기능이 있을 때 |
656
+ | `listRequestValue` | 리스트가 있을 때 |
657
+ | `listColWidths` | 그리드 컬럼 너비 조절이 필요할 때 |
658
+ | `saveRequestValue` | 저장/상세 기능이 있을 때 |
659
+ | `listSelectedRowKey` | 단일 선택이 필요할 때 |
660
+ | `listCheckedIndexes` | 인덱스 기반 다중 선택 |
661
+ | `listCheckedKeys` | 키 기반 다중 선택 |
662
+ | `flexGrow` | 플렉스 레이아웃 조절이 필요할 때 |
663
+ | `formActive` | 폼 활성화 상태를 추적할 때 |
664
+ | `activeTabKey` | 탭 전환이 있을 때 |