@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,866 @@
1
+ # DataGrid 컴포넌트 사용법
2
+
3
+ `DataGrid`는 AXBoot 프레임워크의 `AXDataGrid`를 래핑한 React 컴포넌트입니다. 테이블 형태의 데이터를 효율적으로 표시하고 관리할 수 있는 기능을 제공합니다.
4
+
5
+ ## 기본 사용법
6
+
7
+ ```tsx
8
+ import { DataGrid } from "@core/components/DataGrid";
9
+ import { useContainerSize } from "@core/hooks/useContainerSize";
10
+
11
+ function MyListComponent() {
12
+ const { containerRef, width: containerWidth, height: containerHeight } = useContainerSize();
13
+
14
+ return (
15
+ <div ref={containerRef} style={{ flex: 1 }}>
16
+ <DataGrid<MyDataType>
17
+ width={containerWidth}
18
+ height={containerHeight}
19
+ columns={columns}
20
+ data={data}
21
+ rowKey="id"
22
+ />
23
+ </div>
24
+ );
25
+ }
26
+ ```
27
+
28
+ ## Props 정의
29
+
30
+ ### 기본 Props
31
+
32
+ | 속성 | 타입 | 설명 | 기본값 |
33
+ |------|------|------|--------|
34
+ | `width` | `number` | 그리드 너비 (필수) | - |
35
+ | `height` | `number` | 그리드 높이 (필수) | - |
36
+ | `showLineNumber` | `boolean` | 행 번호 표시 여부 | `true` |
37
+
38
+ ### 데이터 관련
39
+
40
+ | 속성 | 타입 | 설명 |
41
+ |------|------|------|
42
+ | `data` | `AXDGDataItem<T>[]` | 표시할 데이터 배열 |
43
+ | `rowKey` | `React.Key \| React.Key[]` | 행 식별키 |
44
+ | `selectedRowKey` | `React.Key \| React.Key[]` | 선택된 행 키 |
45
+
46
+ ### 컬럼 관련
47
+
48
+ | 속성 | 타입 | 설명 |
49
+ |------|------|------|
50
+ | `columns` | `AXDGColumnWithOptionalWidth<T>[]` | 컬럼 정의 배열 (필수) |
51
+ | `columnsGroup` | `AXDGColumnGroup[]` | 컬럼 그룹 정의 |
52
+ | `frozenColumnIndex` | `number` | 고정 컬럼 인덱스 |
53
+ | `onChangeColumns` | `(columnIndex: number \| null, info: AXDGChangeColumnsInfo<T>) => void` | 컬럼 변경 이벤트 |
54
+
55
+ ### 레이아웃 관련
56
+
57
+ | 속성 | 타입 | 설명 | 기본값 |
58
+ |------|------|------|--------|
59
+ | `headerHeight` | `number` | 헤더 높이 | `28` |
60
+ | `footerHeight` | `number` | 푸터 높이 | `28` |
61
+ | `itemHeight` | `number` | 행 높이 | `20` |
62
+ | `summaryHeight` | `number` | 요약 행 높이 | - |
63
+ | `itemPadding` | `number` | 행 패딩 | - |
64
+
65
+ ### 페이징 관련
66
+
67
+ 페이징 설정은 `page` prop으로 전달됩니다:
68
+
69
+ | 속성 | 타입 | 설명 |
70
+ |------|------|------|
71
+ | `currentPage` | `number` | 현재 페이지 |
72
+ | `pageSize` | `number` | 페이지당 항목 수 |
73
+ | `totalPages` | `number` | 전체 페이지 수 |
74
+ | `totalElements` | `number` | 전체 항목 수 |
75
+ | `loading` | `boolean` | 로딩 상태 |
76
+ | `onChange` | `(currentPage: number, pageSize?: number) => void` | 페이지 변경 이벤트 |
77
+
78
+ ### 정렬 관련
79
+
80
+ 정렬 설정은 `sort` prop으로 전달됩니다:
81
+
82
+ | 속성 | 타입 | 설명 |
83
+ |------|------|------|
84
+ | `multiSort` | `boolean` | 다중 정렬 허용 |
85
+ | `sortParams` | `AXDGSortParam[]` | 정렬 파라미터 |
86
+ | `onChange` | `(sortParams: AXDGSortParam[]) => void` | 정렬 변경 이벤트 |
87
+
88
+ ### 행 선택 관련
89
+
90
+ 행 체크박스 설정은 `rowChecked` prop으로 전달됩니다:
91
+
92
+ | 속성 | 타입 | 설명 |
93
+ |------|------|------|
94
+ | `isRadio` | `boolean` | 라디오 모드 |
95
+ | `checkedIndexes` | `number[]` | 선택된 인덱스 배열 |
96
+ | `checkedRowKeys` | `React.Key[]` | 선택된 키 배열 |
97
+ | `disabled` | `(index: number, item: AXDGDataItem<T>) => boolean` | 비활성화 조건 |
98
+ | `onChange` | `(checkedIndexes: number[], checkedRowKeys: React.Key[], checkedAll?: CheckedAll) => void` | 선택 변경 이벤트 |
99
+
100
+ ### 이벤트 관련
101
+
102
+ | 속성 | 타입 | 설명 |
103
+ |------|------|------|
104
+ | `onClick` | `(params: AXDGClickParams<T>) => void` | 행 클릭 이벤트 |
105
+ | `onChangeData` | `(index: number, columnIndex: number \| null, item: T, column: AXDGColumn<T> \| null) => void` | 데이터 변경 이벤트 |
106
+
107
+ ### 기타
108
+
109
+ | 속성 | 타입 | 설명 |
110
+ |------|------|------|
111
+ | `spinning` | `boolean` | 로딩 스피너 표시 |
112
+ | `loading` | `boolean` | 로딩 상태 |
113
+ | `editable` | `boolean` | 편집 가능 여부 |
114
+ | `editTrigger` | `'dblclick' \| 'click'` | 편집 트리거 |
115
+ | `className` | `string` | CSS 클래스명 |
116
+ | `style` | `React.CSSProperties` | 인라인 스타일 |
117
+
118
+ ## 컬럼 정의 (AXDGColumn)
119
+
120
+ ```tsx
121
+ interface AXDGColumn<T> {
122
+ key: string | string[]; // 데이터 키
123
+ label: ReactNode; // 컬럼 제목
124
+ width: number; // 컬럼 너비
125
+ align?: 'left' | 'center' | 'right'; // 정렬
126
+ headerAlign?: 'left' | 'center' | 'right'; // 헤더 정렬
127
+ sortDisable?: boolean; // 정렬 비활성화
128
+ className?: string; // CSS 클래스
129
+ getClassName?: (item: AXDGDataItem<T>) => string; // 동적 클래스
130
+ headerClassName?: string; // 헤더 CSS 클래스
131
+ itemRender?: React.FC<AXDGItemRenderProps<T>>; // 셀 렌더러
132
+ editable?: boolean; // 편집 가능 여부
133
+ }
134
+ ```
135
+
136
+ ## 사용 예시
137
+
138
+ ### 1. 기본 리스트 (회원 목록)
139
+
140
+ ```tsx
141
+ import { DataGrid } from "@core/components/DataGrid";
142
+ import { useDataGridColumns } from "@core/hooks/useDataGridColumns";
143
+ import { useDataGridSortedList } from "@core/hooks/useDataGridSortedList";
144
+
145
+ interface MemberData {
146
+ usrNo: number;
147
+ usrNm: string;
148
+ usrHpNo: string;
149
+ pntAmt: number;
150
+ creatDtm: string;
151
+ }
152
+
153
+ export function MemberListDataGrid() {
154
+ const { containerRef, width: containerWidth, height: containerHeight } = useContainerSize();
155
+
156
+ // 컬럼 정의
157
+ const { columns } = useDataGridColumns<MemberData>([
158
+ { key: "usrNo", label: "번호", type: "rowNo" },
159
+ { key: "usrNm", label: "이름", align: "left", width: 120 },
160
+ { key: "usrHpNo", label: "핸드폰번호", align: "left", width: 150 },
161
+ {
162
+ key: "pntAmt",
163
+ label: "포인트",
164
+ type: "money",
165
+ width: 90,
166
+ itemRender: ({ value }) => formatterNumber(value),
167
+ },
168
+ {
169
+ key: "creatDtm",
170
+ label: "가입일",
171
+ type: "dateTime",
172
+ itemRender: ({ value }) => formatterDate(value),
173
+ },
174
+ ], { colWidths: listColWidths });
175
+
176
+ const sortedListData = useDataGridSortedList<MemberData>(listData, listSortParams);
177
+
178
+ return (
179
+ <div ref={containerRef} style={{ flex: 1 }}>
180
+ <DataGrid<MemberData>
181
+ frozenColumnIndex={0}
182
+ width={containerWidth}
183
+ height={containerHeight}
184
+ columns={columns}
185
+ data={sortedListData}
186
+ spinning={listSpinning}
187
+ onClick={onClickItem}
188
+ page={{
189
+ ...listPage,
190
+ loading: false,
191
+ onChange: async (currentPage, pageSize) => {
192
+ await changeListPage(currentPage, pageSize);
193
+ },
194
+ }}
195
+ sort={{
196
+ sortParams: listSortParams,
197
+ onChange: setListSortParams,
198
+ }}
199
+ rowKey="usrNo"
200
+ selectedRowKey={selectedItem?.usrNo ?? ""}
201
+ />
202
+ </div>
203
+ );
204
+ }
205
+ ```
206
+
207
+ ### 2. 체크박스가 있는 리스트
208
+
209
+ ```tsx
210
+ const [checkedRowKeys, setCheckedRowKeys] = useState<React.Key[]>([]);
211
+
212
+ return (
213
+ <DataGrid<ItemType>
214
+ width={containerWidth}
215
+ height={containerHeight}
216
+ columns={columns}
217
+ data={data}
218
+ rowChecked={{
219
+ checkedRowKeys: checkedRowKeys,
220
+ onChange: (checkedIndexes, checkedRowKeys, checkedAll) => {
221
+ setCheckedRowKeys(checkedRowKeys);
222
+ },
223
+ }}
224
+ rowKey="id"
225
+ />
226
+ );
227
+ ```
228
+
229
+ ### 3. 액션 버튼이 있는 리스트
230
+
231
+ ```tsx
232
+ const { columns } = useDataGridColumns<ProductData>([
233
+ { key: "prodgrpMastrNm", label: "제품군 마스터 명", type: "title", width: 300 },
234
+ { key: "prodgrpMastrNo", label: "제품군 코드", align: "center", width: 100 },
235
+ {
236
+ key: "",
237
+ label: "관리",
238
+ align: "left",
239
+ width: 120,
240
+ itemRender: ({ item }) => {
241
+ return (
242
+ <Flex gap={6} justify="center">
243
+ <Button
244
+ size="small"
245
+ onClick={(e) => {
246
+ e.stopPropagation(); // 행 클릭 이벤트 방지
247
+ handleCopy(item.values.prodgrpMastrNo);
248
+ }}
249
+ >
250
+ 복사
251
+ </Button>
252
+ <Button
253
+ size="small"
254
+ variant="outlined"
255
+ color="danger"
256
+ onClick={(e) => {
257
+ e.stopPropagation();
258
+ handleDelete(item.values.prodgrpMastrNo);
259
+ }}
260
+ >
261
+ 삭제
262
+ </Button>
263
+ </Flex>
264
+ );
265
+ },
266
+ },
267
+ ]);
268
+
269
+ // 특정 컬럼 클릭 시 행 클릭 이벤트 방지
270
+ const onClickItem = useCallback((params: AXDGClickParams<ProductData>) => {
271
+ if (params.columnIndex === 2) { // 관리 컬럼
272
+ return; // 클릭 이벤트 무시
273
+ }
274
+ // 일반 행 클릭 처리
275
+ handleItemClick(params);
276
+ }, [handleItemClick]);
277
+ ```
278
+
279
+ ### 4. 컬럼 그룹 헤더
280
+
281
+ ```tsx
282
+ // 마일리지 일일 요약
283
+ <DataGrid<DtoItem>
284
+ width={containerWidth}
285
+ height={containerHeight}
286
+ headerHeight={54} // 그룹 헤더를 위해 높이 증가
287
+ columns={columns}
288
+ columnsGroup={[
289
+ {
290
+ label: "마일리지",
291
+ groupStartIndex: 1,
292
+ groupEndIndex: 3,
293
+ headerAlign: "center"
294
+ }
295
+ ]}
296
+ data={sortedListData}
297
+ />
298
+
299
+ // 복합 그룹 헤더 (배당 현황)
300
+ <DataGrid<DtoItem>
301
+ headerHeight={60}
302
+ columnsGroup={[
303
+ { groupStartIndex: 10, groupEndIndex: 11, label: "현금배당 수익률(%)" },
304
+ { groupStartIndex: 12, groupEndIndex: 13, label: "주식배당 수익률(%)" },
305
+ { groupStartIndex: 14, groupEndIndex: 15, label: "주당 현금배당금(원)" },
306
+ { groupStartIndex: 16, groupEndIndex: 17, label: "주당 주식배당(주)" },
307
+ ]}
308
+ // ... other props
309
+ />
310
+ ```
311
+
312
+ ### 5. 셀 병합 기능
313
+
314
+ ```tsx
315
+ // 주문 ID별로 셀 병합
316
+ <DataGrid<OrderData>
317
+ width={containerWidth}
318
+ height={containerHeight}
319
+ columns={columns}
320
+ data={data}
321
+ cellMergeOptions={{
322
+ columnsMap: {
323
+ 0: { mergeBy: "ordId" }, // 첫 번째 컬럼을 ordId로 병합
324
+ 1: { mergeBy: "ordId" }, // 두 번째 컬럼을 ordId로 병합
325
+ 2: { mergeBy: "ordId" }, // 세 번째 컬럼을 ordId로 병합
326
+ 3: { mergeBy: "ordId" }, // 네 번째 컬럼을 ordId로 병합
327
+ },
328
+ }}
329
+ variant="vertical-bordered" // 세로 경계선 표시
330
+ />
331
+
332
+ // 패키지 정산 - 상담 그룹별 병합
333
+ <DataGrid<SettlementData>
334
+ cellMergeOptions={{
335
+ columnsMap: {
336
+ 1: { mergeBy: "nhCnsltGroupNo" },
337
+ 2: { mergeBy: "nhCnsltGroupNo" },
338
+ 3: { mergeBy: "nhCnsltGroupNo" },
339
+ 4: { mergeBy: "nhCnsltGroupNo" },
340
+ },
341
+ }}
342
+ // ... other props
343
+ />
344
+ ```
345
+
346
+ ### 6. 요약 행 표시
347
+
348
+ ```tsx
349
+ // 상단 요약
350
+ const summaryTop: AXDGProps<any>["summary"] = {
351
+ position: "top",
352
+ columns: [
353
+ {
354
+ columnIndex: 0,
355
+ align: "center",
356
+ colSpan: 3,
357
+ itemRender: () => <strong>요약 정보</strong>,
358
+ },
359
+ {
360
+ columnIndex: 3,
361
+ align: "right",
362
+ itemRender: ({ data }) => {
363
+ const total = data.reduce((sum, item) => sum + item.values.amount, 0);
364
+ return `총합: ${formatterNumber(total)}원`;
365
+ },
366
+ },
367
+ ],
368
+ };
369
+
370
+ // 하단 요약
371
+ const summaryBottom: AXDGProps<any>["summary"] = {
372
+ position: "bottom",
373
+ columns: [
374
+ {
375
+ columnIndex: 0,
376
+ align: "center",
377
+ colSpan: 2,
378
+ itemRender: () => "합계",
379
+ },
380
+ {
381
+ columnIndex: 2,
382
+ align: "right",
383
+ itemRender: ({ data }) => {
384
+ return formatterNumber(data.length) + "건";
385
+ },
386
+ },
387
+ ],
388
+ };
389
+
390
+ <DataGrid<DataType>
391
+ // ... other props
392
+ summary={summaryTop} // 또는 summaryBottom
393
+ />
394
+ ```
395
+
396
+ ### 7. 중첩된 객체 데이터 참조
397
+
398
+ ```tsx
399
+ // JSON 객체의 중첩 키 참조
400
+ const { columns } = useDataGridColumns<FinancialData>([
401
+ { key: "bbscttMgmtYy", label: "년도", align: "center", width: 80 },
402
+ { key: "bbscttBgnDt", label: "분기", align: "center", width: 80 },
403
+
404
+ // paramtrJson.consolidated.revenue 형태로 중첩 참조
405
+ { key: ["paramtrJson", "consolidated", "revenue"], label: "매출액", type: "money" },
406
+ { key: ["paramtrJson", "consolidated", "gross_profit"], label: "매출이익", type: "money" },
407
+ { key: ["paramtrJson", "consolidated", "operating_income"], label: "영업이익", type: "money" },
408
+ { key: ["paramtrJson", "consolidated", "net_income"], label: "순이익", type: "money" },
409
+
410
+ // 별도 재무 데이터
411
+ { key: ["paramtrJson", "separate", "revenue"], label: "별도매출액", type: "money" },
412
+ { key: ["paramtrJson", "separate", "total_assets"], label: "별도자산", type: "money" },
413
+ ]);
414
+
415
+ // 데이터 구조 예시
416
+ const sampleData = {
417
+ bbscttMgmtYy: "2024",
418
+ bbscttBgnDt: "2024-01-01",
419
+ paramtrJson: {
420
+ consolidated: {
421
+ revenue: 1000000,
422
+ gross_profit: 200000,
423
+ operating_income: 150000,
424
+ net_income: 100000,
425
+ },
426
+ separate: {
427
+ revenue: 800000,
428
+ total_assets: 5000000,
429
+ }
430
+ }
431
+ };
432
+ ```
433
+
434
+ ### 8. 이미지 및 미디어 렌더링
435
+
436
+ ```tsx
437
+ const { columns } = useDataGridColumns<BannerData>([
438
+ { key: "bannerNm", label: "배너명", width: 200 },
439
+ {
440
+ key: "image",
441
+ label: "이미지",
442
+ width: 180,
443
+ itemRender: ({ values }) => {
444
+ return (
445
+ <Flex gap={6} justify="center" align="center">
446
+ {values.pcImageFlInfo && (
447
+ <ThumbPreview url={values.pcImageFlInfo.prevewUrlCtnts} />
448
+ )}
449
+ {values.moImageFlInfo && (
450
+ <ThumbPreview url={values.moImageFlInfo.prevewUrlCtnts} />
451
+ )}
452
+ </Flex>
453
+ );
454
+ },
455
+ },
456
+ {
457
+ key: "expsrBgnDtm",
458
+ label: "노출기간",
459
+ width: 150,
460
+ itemRender: ({ values }) => {
461
+ return values.expsrBgnDtm ? (
462
+ <Flex wrap gap={5} style={{ lineHeight: 1 }} justify="center">
463
+ <span>{dayjs(values.expsrBgnDtm).format("YYYY-MM-DD HH:mm")}</span>
464
+ <span>~</span>
465
+ <span>{dayjs(values.expsrEndDtm).format("YYYY-MM-DD HH:mm")}</span>
466
+ </Flex>
467
+ ) : (
468
+ "제한없음"
469
+ );
470
+ },
471
+ },
472
+ ]);
473
+
474
+ <DataGrid<BannerData>
475
+ itemHeight={60} // 이미지 표시를 위해 행 높이 증가
476
+ // ... other props
477
+ />
478
+ ```
479
+
480
+ ### 9. 인라인 편집
481
+
482
+ ```tsx
483
+ <DataGrid<EditableData>
484
+ width={containerWidth}
485
+ height={containerHeight}
486
+ columns={editableColumns}
487
+ data={data}
488
+ editable={true}
489
+ editTrigger="dblclick" // 더블클릭으로 편집 시작
490
+ onChangeData={(index, columnIndex, item, column) => {
491
+ // 데이터 변경 시 호출
492
+ console.log("Data changed:", { index, columnIndex, item, column });
493
+ // 서버에 저장 로직
494
+ handleSaveData(item);
495
+ }}
496
+ rowKey="id"
497
+ />
498
+
499
+ // 편집 가능한 컬럼 정의
500
+ const editableColumns = [
501
+ { key: "id", label: "ID", width: 80, editable: false }, // 편집 불가
502
+ { key: "name", label: "이름", width: 150, editable: true },
503
+ { key: "email", label: "이메일", width: 200, editable: true },
504
+ { key: "status", label: "상태", width: 100, editable: true },
505
+ ];
506
+ ```
507
+
508
+ ### 10. 엑셀 업로드 및 일괄 처리
509
+
510
+ ```tsx
511
+ const handleExcelUpload = useCallback(async () => {
512
+ try {
513
+ await openExcelUploaderModal<ExcelDto>({
514
+ title: "재무 하이라이트 일괄등록",
515
+ showResultList: false,
516
+ showResultCount: true,
517
+ targetColumns: excelUploadColumns,
518
+ headerBtnRender: ExcelSampleDownload,
519
+ callExcelUpload: async (list) => {
520
+ // 엑셀 데이터 처리
521
+ for await (const item of list) {
522
+ const apiParams = {
523
+ bbscttMgmtYy: dayjs(item.bbscttBgnDt, "YYYY-Q").format("YYYY"),
524
+ bbscttBgnDt: dayjs(item.bbscttBgnDt, "YYYY-Q").format("YYYY-MM-DD"),
525
+ paramtrJson: {
526
+ consolidated: {
527
+ revenue: item.consolidated_revenue ? Number(item.consolidated_revenue) : undefined,
528
+ gross_profit: item.consolidated_gross_profit ? Number(item.consolidated_gross_profit) : undefined,
529
+ }
530
+ },
531
+ useYn: item.useYn === "Y" ? "Y" : "N",
532
+ };
533
+
534
+ await BbsPostService.postBbsPostSave(apiParams);
535
+ }
536
+
537
+ return {
538
+ successList: list,
539
+ failList: [],
540
+ };
541
+ },
542
+ });
543
+
544
+ await callListApi(); // 목록 새로고침
545
+ } catch (err) {
546
+ await errorHandling(err);
547
+ }
548
+ }, [callListApi]);
549
+
550
+ // 엑셀 컬럼 정의
551
+ const excelUploadColumns: ExcelTargetColumn[] = [
552
+ { key: "bbscttBgnDt", label: "분기", type: "string", required: true },
553
+ { key: "useYn", label: "사용여부", type: "string", required: true },
554
+ { key: "consolidated_revenue", label: "연결_매출액", type: "number", required: true },
555
+ { key: "consolidated_gross_profit", label: "연결_매출이익", type: "number", required: true },
556
+ ];
557
+ ```
558
+
559
+ ## 훅 활용
560
+
561
+ ### useDataGridColumns
562
+ 컬럼 정의와 너비 관리를 위한 훅
563
+
564
+ ```tsx
565
+ const { columns } = useDataGridColumns<DataType>(columnDefs, {
566
+ colWidths: listColWidths,
567
+ deps: [CODES] // 의존성 배열
568
+ });
569
+ ```
570
+
571
+ ### useDataGridSortedList
572
+ 클라이언트 사이드 정렬을 위한 훅
573
+
574
+ ```tsx
575
+ const sortedListData = useDataGridSortedList<DataType>(listData, listSortParams);
576
+ ```
577
+
578
+ ### useContainerSize
579
+ 컨테이너 크기 자동 계산을 위한 훅
580
+
581
+ ```tsx
582
+ const { containerRef, width: containerWidth, height: containerHeight } = useContainerSize();
583
+ ```
584
+
585
+ ## 고급 기능
586
+
587
+ ### 컬럼 너비 조정
588
+
589
+ ```tsx
590
+ const handleColumnsChange = React.useCallback(
591
+ (columnIndex: number | null, { columns }: AXDGChangeColumnsInfo<DtoItem>) => {
592
+ // 사용자가 컬럼 너비를 조정할 때 상태에 저장
593
+ setListColWidths(columns.map((column) => column.width));
594
+ },
595
+ [setListColWidths],
596
+ );
597
+
598
+ <DataGrid
599
+ columns={columns}
600
+ onChangeColumns={handleColumnsChange}
601
+ // ... other props
602
+ />
603
+ ```
604
+
605
+ ### 조건부 스타일링
606
+
607
+ ```tsx
608
+ const { columns } = useDataGridColumns<DataType>([
609
+ {
610
+ key: "status",
611
+ label: "상태",
612
+ width: 100,
613
+ // 셀별 동적 클래스
614
+ getClassName: (item) => {
615
+ switch (item.values.status) {
616
+ case "active": return "status-active";
617
+ case "inactive": return "status-inactive";
618
+ case "pending": return "status-pending";
619
+ default: return "";
620
+ }
621
+ },
622
+ },
623
+ ]);
624
+
625
+ <DataGrid
626
+ // 행별 동적 클래스
627
+ getRowClassName={(ri, item) => {
628
+ return item.values.important ? "important-row" : undefined;
629
+ }}
630
+ // ... other props
631
+ />
632
+ ```
633
+
634
+ ### 무한 스크롤
635
+
636
+ ```tsx
637
+ <DataGrid<DataType>
638
+ width={containerWidth}
639
+ height={containerHeight}
640
+ columns={columns}
641
+ data={data}
642
+ enableLoadMore={true}
643
+ onLoadMore={({ scrollLeft, scrollTop }) => {
644
+ // 스크롤 위치에 따른 추가 데이터 로드
645
+ if (scrollTop > threshold) {
646
+ loadMoreData();
647
+ }
648
+ }}
649
+ endLoadMoreRender={() => (
650
+ <div style={{ textAlign: "center", padding: "20px" }}>
651
+ 더 이상 데이터가 없습니다.
652
+ </div>
653
+ )}
654
+ />
655
+ ```
656
+
657
+ ### 드래그 앤 드롭 정렬
658
+
659
+ ```tsx
660
+ <DataGrid<DataType>
661
+ width={containerWidth}
662
+ height={containerHeight}
663
+ columns={columns}
664
+ data={data}
665
+ reorder={{
666
+ enabled: true,
667
+ handleIcon: <Icon name="grip-vertical" />,
668
+ onReorder: (newData) => {
669
+ // 새로운 순서로 데이터 업데이트
670
+ setData(newData);
671
+ // 서버에 순서 저장
672
+ saveOrderToServer(newData);
673
+ },
674
+ }}
675
+ />
676
+ ```
677
+
678
+ ## 성능 최적화
679
+
680
+ ### 가상화 스크롤
681
+
682
+ ```tsx
683
+ // DataGrid는 기본적으로 가상화를 지원합니다
684
+ <DataGrid<DataType>
685
+ width={containerWidth}
686
+ height={containerHeight}
687
+ itemHeight={40} // 일정한 행 높이 설정으로 성능 향상
688
+ columns={columns}
689
+ data={largeDataset} // 수천, 수만 건의 데이터도 부드럽게 처리
690
+ />
691
+ ```
692
+
693
+ ### 메모이제이션
694
+
695
+ ```tsx
696
+ // 컬럼 정의 메모이제이션
697
+ const columns = useMemo(() => [
698
+ { key: "id", label: "ID", width: 80 },
699
+ { key: "name", label: "이름", width: 150 },
700
+ // 복잡한 itemRender 함수도 메모이제이션
701
+ {
702
+ key: "status",
703
+ label: "상태",
704
+ width: 100,
705
+ itemRender: useCallback(({ value }) => {
706
+ return <StatusBadge status={value} />;
707
+ }, []),
708
+ },
709
+ ], []);
710
+
711
+ // 데이터 정렬도 메모이제이션
712
+ const sortedData = useMemo(() => {
713
+ return useDataGridSortedList(rawData, sortParams);
714
+ }, [rawData, sortParams]);
715
+ ```
716
+
717
+ ## 실제 사용 사례
718
+
719
+ ### 전자상거래 주문 관리
720
+
721
+ ```tsx
722
+ // 주문 상태별 색상 구분, 셀 병합, 액션 버튼 등 복합 기능
723
+ <DataGrid<OrderData>
724
+ cellMergeOptions={{
725
+ columnsMap: {
726
+ 0: { mergeBy: "ordId" },
727
+ 1: { mergeBy: "ordId" },
728
+ 2: { mergeBy: "ordId" },
729
+ },
730
+ }}
731
+ getRowClassName={(ri, item) => {
732
+ switch (item.values.orderStatus) {
733
+ case "pending": return "order-pending";
734
+ case "processing": return "order-processing";
735
+ case "completed": return "order-completed";
736
+ case "cancelled": return "order-cancelled";
737
+ default: return "";
738
+ }
739
+ }}
740
+ onClick={handleOrderDetail}
741
+ rowChecked={{
742
+ checkedRowKeys: selectedOrders,
743
+ onChange: handleOrderSelection,
744
+ }}
745
+ // ... other props
746
+ />
747
+ ```
748
+
749
+ ### 재무 데이터 대시보드
750
+
751
+ ```tsx
752
+ <DataGrid<FinancialData>
753
+ headerHeight={60}
754
+ columnsGroup={[
755
+ { groupStartIndex: 3, groupEndIndex: 6, label: "연결 재무" },
756
+ { groupStartIndex: 7, groupEndIndex: 10, label: "별도 재무" },
757
+ ]}
758
+ summary={{
759
+ position: "bottom",
760
+ columns: [
761
+ {
762
+ columnIndex: 0,
763
+ colSpan: 3,
764
+ itemRender: () => "합계",
765
+ },
766
+ {
767
+ columnIndex: 3,
768
+ itemRender: ({ data }) => {
769
+ const total = data.reduce((sum, item) =>
770
+ sum + (item.values.paramtrJson?.consolidated?.revenue || 0), 0
771
+ );
772
+ return formatterNumber(total);
773
+ },
774
+ },
775
+ ],
776
+ }}
777
+ // JSON 중첩 키 참조
778
+ columns={[
779
+ { key: ["paramtrJson", "consolidated", "revenue"], label: "연결매출", type: "money" },
780
+ { key: ["paramtrJson", "separate", "revenue"], label: "별도매출", type: "money" },
781
+ ]}
782
+ />
783
+ ```
784
+
785
+ ### 콘텐츠 관리 시스템
786
+
787
+ ```tsx
788
+ <DataGrid<BannerData>
789
+ itemHeight={80} // 이미지 표시를 위한 높은 행
790
+ columns={[
791
+ {
792
+ key: "images",
793
+ label: "이미지",
794
+ width: 200,
795
+ itemRender: ({ values }) => (
796
+ <Flex gap={6} justify="center">
797
+ {values.pcImage && <ThumbPreview url={values.pcImage} />}
798
+ {values.mobileImage && <ThumbPreview url={values.mobileImage} />}
799
+ </Flex>
800
+ ),
801
+ },
802
+ {
803
+ key: "period",
804
+ label: "노출기간",
805
+ width: 180,
806
+ itemRender: ({ values }) => (
807
+ <Flex direction="column" align="center">
808
+ <span>{dayjs(values.startDate).format("YYYY-MM-DD")}</span>
809
+ <span>~</span>
810
+ <span>{dayjs(values.endDate).format("YYYY-MM-DD")}</span>
811
+ </Flex>
812
+ ),
813
+ },
814
+ ]}
815
+ onClick={(params) => {
816
+ if (params.columnIndex === 5) return; // 액션 컬럼 제외
817
+ handleEditBanner(params.item);
818
+ }}
819
+ />
820
+ ```
821
+
822
+ ## 스타일링
823
+
824
+ DataGrid는 Emotion을 사용한 테마 시스템을 지원합니다:
825
+
826
+ ```tsx
827
+ const Container = styled.div`
828
+ flex: 1;
829
+
830
+ .status-active {
831
+ background-color: #f6ffed;
832
+ color: #52c41a;
833
+ }
834
+
835
+ .status-inactive {
836
+ background-color: #fff2f0;
837
+ color: #ff4d4f;
838
+ }
839
+
840
+ .important-row {
841
+ background-color: #fffbe6;
842
+ border-left: 3px solid #faad14;
843
+ }
844
+
845
+ .selectable:hover {
846
+ background-color: #e6f7ff;
847
+ cursor: pointer;
848
+ }
849
+ `;
850
+ ```
851
+
852
+ ## 주의사항
853
+
854
+ > **⚠️ 중요사항들을 반드시 확인하세요:**
855
+
856
+ 1. `width`와 `height`가 0인 경우 컴포넌트가 렌더링되지 않습니다.
857
+ 2. `showLineNumber`는 기본적으로 `true`로 설정됩니다.
858
+ 3. 테마 시스템을 통해 다양한 스타일 변수를 커스터마이징할 수 있습니다.
859
+ 4. 반드시 `rowKey`를 설정해야 행 선택과 업데이트가 정상 작동합니다.
860
+ 5. 큰 데이터셋의 경우 페이징을 활용하는 것이 성능상 유리합니다.
861
+ 6. 셀 병합 사용 시 `variant="vertical-bordered"` 옵션으로 시각적 구분을 개선할 수 있습니다.
862
+ 7. 액션 버튼 클릭 시 행 클릭 이벤트가 발생하지 않도록 `e.stopPropagation()`을 사용하세요.
863
+ 8. 다중 키 참조 시 배열 형태로 키를 전달합니다: `key: ["parent", "child", "grandchild"]`
864
+
865
+ > **💡 참고사항:**
866
+ > 더 많은 예시와 고급 사용법은 프로젝트 내 `/src/pages/resources/NH/` 폴더의 다양한 `ListDataGrid.tsx` 파일들을 참조하세요.