@makitt.io/mds-mcp-server 0.1.3 → 0.2.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 (55) hide show
  1. package/README.md +135 -34
  2. package/dist/catalog.d.ts +16 -0
  3. package/dist/catalog.d.ts.map +1 -0
  4. package/dist/catalog.js +383 -0
  5. package/dist/catalog.js.map +1 -0
  6. package/dist/data/catalog.json +41643 -4127
  7. package/dist/data/playbook/ai-fill.md +61 -48
  8. package/dist/data/playbook/anti-patterns.md +112 -110
  9. package/dist/data/playbook/array-input.md +94 -49
  10. package/dist/data/playbook/async-states.md +71 -61
  11. package/dist/data/playbook/data-grid.md +118 -101
  12. package/dist/data/playbook/feedback.md +103 -84
  13. package/dist/data/playbook/form.md +164 -134
  14. package/dist/data/playbook/overlay.md +97 -88
  15. package/dist/data/playbook/page-layout.md +95 -76
  16. package/dist/data/playbook/responsive-tokens.md +77 -58
  17. package/dist/data/recipes/admin-list-page.md +86 -0
  18. package/dist/data/recipes/async-state-section.md +60 -0
  19. package/dist/data/recipes/dashboard-overview.md +65 -0
  20. package/dist/data/recipes/detail-drawer.md +69 -0
  21. package/dist/data/recipes/modal-form.md +67 -0
  22. package/dist/data/recipes/settings-form-page.md +79 -0
  23. package/dist/index.d.ts +4 -23
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +25 -339
  26. package/dist/index.js.map +1 -1
  27. package/dist/loaders.d.ts +8 -0
  28. package/dist/loaders.d.ts.map +1 -0
  29. package/dist/loaders.js +120 -0
  30. package/dist/loaders.js.map +1 -0
  31. package/dist/recipes.d.ts +13 -0
  32. package/dist/recipes.d.ts.map +1 -0
  33. package/dist/recipes.js +82 -0
  34. package/dist/recipes.js.map +1 -0
  35. package/dist/responses.d.ts +8 -0
  36. package/dist/responses.d.ts.map +1 -0
  37. package/dist/responses.js +25 -0
  38. package/dist/responses.js.map +1 -0
  39. package/dist/text.d.ts +4 -0
  40. package/dist/text.d.ts.map +1 -0
  41. package/dist/text.js +20 -0
  42. package/dist/text.js.map +1 -0
  43. package/dist/tool-definitions.d.ts +3 -0
  44. package/dist/tool-definitions.d.ts.map +1 -0
  45. package/dist/tool-definitions.js +199 -0
  46. package/dist/tool-definitions.js.map +1 -0
  47. package/dist/tools.d.ts +4 -0
  48. package/dist/tools.d.ts.map +1 -0
  49. package/dist/tools.js +233 -0
  50. package/dist/tools.js.map +1 -0
  51. package/dist/types.d.ts +107 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +10 -0
  54. package/dist/types.js.map +1 -0
  55. package/package.json +20 -16
@@ -2,22 +2,22 @@
2
2
 
3
3
  list / table 데이터 표시에 대한 약속.
4
4
 
5
- > mds 의 list 컴포넌트 = **DataGrid** (full-feature) + **Table** (custom column).
6
- > 99% list 페이지 = DataGrid. Table 은 특수 case.
5
+ > mds 의 list 컴포넌트 = **DataGrid** (full-feature) + **Table** (custom
6
+ > column). 99% list 페이지 = DataGrid. Table 은 특수 case.
7
7
 
8
8
  ---
9
9
 
10
10
  ## 1. DataGrid vs Table 선택
11
11
 
12
- | 케이스 | 답 | 이유 |
13
- |---|---|---|
14
- | 셀러 admin 의 list 페이지 (customers / orders / products) | **DataGrid** | filter / search / sort / pagination / selection / bulk action / loading / empty / error 자동 |
15
- | URL state 동기화 필요 (deep link) | **DataGrid** | controlled props (page / sort / filter / search) 모두 노출 |
16
- | Server-side filter/sort | **DataGrid** `mode="server"` | totalItems / onPageChange / onSortChange caller 처리 |
17
- | 단순 정보 표시 (정적 list, 보통 5-10 row) | **Table** | DataGrid 의 toolbar 과잉 |
18
- | 페이지의 한 섹션 안 작은 list (예: 고객의 최근 주문 3개) | **Table** | 페이지의 다른 컨텍스트와 통합 |
19
- | 매우 custom layout (column 별 다른 component) | **Table** + caller column render |
20
- | Tree / nested rows | **Tree** (compounds) — Table 아님 |
12
+ | 케이스 | 답 | 이유 |
13
+ | --------------------------------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------- |
14
+ | 셀러 admin 의 list 페이지 (customers / orders / products) | **DataGrid** | filter / search / sort / pagination / selection / bulk action / loading / empty / error 자동 |
15
+ | URL state 동기화 필요 (deep link) | **DataGrid** | controlled props (page / sort / filter / search) 모두 노출 |
16
+ | Server-side filter/sort | **DataGrid** `mode="server"` | totalItems / onPageChange / onSortChange caller 처리 |
17
+ | 단순 정보 표시 (정적 list, 보통 5-10 row) | **Table** | DataGrid 의 toolbar 과잉 |
18
+ | 페이지의 한 섹션 안 작은 list (예: 고객의 최근 주문 3개) | **Table** | 페이지의 다른 컨텍스트와 통합 |
19
+ | 매우 custom layout (column 별 다른 component) | **Table** + caller column render |
20
+ | Tree / nested rows | **Tree** (compounds) — Table 아님 |
21
21
 
22
22
  > **Default = DataGrid**. Table 은 의식적 결정.
23
23
 
@@ -34,7 +34,12 @@ list / table 데이터 표시에 대한 약속.
34
34
  title="주문"
35
35
  filters={[
36
36
  { value: 'all', label: '전체', count: 248, predicate: () => true },
37
- { value: 'pending', label: '결제 대기', count: 12, predicate: (o) => o.status === 'pending' },
37
+ {
38
+ value: 'pending',
39
+ label: '결제 대기',
40
+ count: 12,
41
+ predicate: (o) => o.status === 'pending',
42
+ },
38
43
  ]}
39
44
  defaultFilter="all"
40
45
  searchableKeys={['id', 'customer']}
@@ -52,7 +57,7 @@ list / table 데이터 표시에 대한 약속.
52
57
  onRowClick={(o) => navigate(`/orders/${o.id}`)}
53
58
  density="default"
54
59
  stickyHeader
55
- storageKey="orders-grid" // localStorage column widths / hidden
60
+ storageKey="orders-grid" // localStorage column widths / hidden
56
61
  />
57
62
  ```
58
63
 
@@ -69,28 +74,28 @@ const columns: ColumnDef<Order>[] = [
69
74
 
70
75
  ### Column 패턴 (셀러 admin 표준)
71
76
 
72
- | Column 종류 | 컴포넌트 | 예 |
73
- |---|---|---|
74
- | ID / 번호 | `Code size="xs"` | `#HE-2719` |
75
- | 사람 (고객 / 사용자) | `PersonCell` | avatar + name + sub |
76
- | 상태 | `Pill tone={...}` | success / warning / danger / info |
77
- | 금액 | tabular-nums + right-align | `₩123,456` |
78
- | 날짜 | `relativeDate` 또는 `formatDate` | `3일 전` / `2026-05-01` |
79
- | 액션 | `IconButton` / `Menu` | 행 끝 |
80
- | 진행률 | `ProgressRow` | bar + pct |
81
- | 추세 | `Sparkline` | mini chart |
77
+ | Column 종류 | 컴포넌트 | 예 |
78
+ | -------------------- | -------------------------------- | --------------------------------- |
79
+ | ID / 번호 | `Code size="xs"` | `#HE-2719` |
80
+ | 사람 (고객 / 사용자) | `PersonCell` | avatar + name + sub |
81
+ | 상태 | `Pill tone={...}` | success / warning / danger / info |
82
+ | 금액 | tabular-nums + right-align | `₩123,456` |
83
+ | 날짜 | `relativeDate` 또는 `formatDate` | `3일 전` / `2026-05-01` |
84
+ | 액션 | `IconButton` / `Menu` | 행 끝 |
85
+ | 진행률 | `ProgressRow` | bar + pct |
86
+ | 추세 | `Sparkline` | mini chart |
82
87
 
83
88
  ### Column key 명명 표준 (필수)
84
89
 
85
- | 표준 key | 의도 | 비고 |
86
- |---|---|---|
87
- | `id` | entity primary id | snake_case 금지 (`created_at` ❌ → `createdAt`) |
88
- | `name` / `title` | entity 의 식별용 이름 / 제목 | 도메인 별 alias (`orderNumber` / `productName`) 는 의도가 명확하면 허용 |
89
- | `customer` | actor (PersonCell) | — |
90
- | `status` | Pill tone enum | — |
91
- | `amount` | 금액 (right-align mono) | `total` 같은 alias 허용 단 의도 일관성 우선 |
92
- | `createdAt` / `updatedAt` | 날짜 (camelCase 필수) | `created_at` 같은 snake_case 금지 |
93
- | `actions` | row actions (IconButton / Menu) | 모든 list 의 마지막 column 으로 고정 |
90
+ | 표준 key | 의도 | 비고 |
91
+ | ------------------------- | ------------------------------- | ----------------------------------------------------------------------- |
92
+ | `id` | entity primary id | snake_case 금지 (`created_at` ❌ → `createdAt`) |
93
+ | `name` / `title` | entity 의 식별용 이름 / 제목 | 도메인 별 alias (`orderNumber` / `productName`) 는 의도가 명확하면 허용 |
94
+ | `customer` | actor (PersonCell) | — |
95
+ | `status` | Pill tone enum | — |
96
+ | `amount` | 금액 (right-align mono) | `total` 같은 alias 허용 단 의도 일관성 우선 |
97
+ | `createdAt` / `updatedAt` | 날짜 (camelCase 필수) | `created_at` 같은 snake_case 금지 |
98
+ | `actions` | row actions (IconButton / Menu) | 모든 list 의 마지막 column 으로 고정 |
94
99
 
95
100
  → **camelCase 필수**. snake_case (`created_at`) / mixed naming 금지.
96
101
 
@@ -101,17 +106,19 @@ const columns: ColumnDef<Order>[] = [
101
106
  ```ts
102
107
  // 표준 순서 예 (order list)
103
108
  const columns = [
104
- { key: 'id', header: '주문 번호' }, // 1. entity-id
105
- { key: 'product', header: '상품' }, // 2. entity-info
106
- { key: 'customer', header: '고객' }, // 3. actor
107
- { key: 'status', header: '상태' }, // 4. status
108
- { key: 'amount', header: '금액', align: 'right' }, // 5. amount
109
- { key: 'createdAt', header: '주문일' }, // 6. metadata
110
- { key: 'actions', header: '', align: 'right' }, // 7. actions (마지막)
109
+ { key: 'id', header: '주문 번호' }, // 1. entity-id
110
+ { key: 'product', header: '상품' }, // 2. entity-info
111
+ { key: 'customer', header: '고객' }, // 3. actor
112
+ { key: 'status', header: '상태' }, // 4. status
113
+ { key: 'amount', header: '금액', align: 'right' }, // 5. amount
114
+ { key: 'createdAt', header: '주문일' }, // 6. metadata
115
+ { key: 'actions', header: '', align: 'right' }, // 7. actions (마지막)
111
116
  ];
112
117
  ```
113
118
 
114
- > 일부 도메인 (예: blog) 은 amount 가 없을 수 있음 → metadata 직전 으로 의도된 생략 OK. 단 — `id → ... → actions` 의 **앞 / 뒤 anchor** 는 모든 list 에서 동일.
119
+ > 일부 도메인 (예: blog) 은 amount 가 없을 수 있음 → metadata 직전 으로 의도된
120
+ > 생략 OK. 단 — `id → ... → actions` 의 **앞 / 뒤 anchor** 는 모든 list 에서
121
+ > 동일.
115
122
 
116
123
  ### Actions column 활용
117
124
 
@@ -144,14 +151,14 @@ const columns = [
144
151
 
145
152
  ## 3. Mobile 변형 (Responsive 표준)
146
153
 
147
- | Desktop (≥ md) | Mobile (< md) |
148
- |---|---|
149
- | DataGrid table 형 | **Card list** (각 row → card, primary column 1-2개만 표시) |
150
- | Sticky header | `position: sticky` 그대로 |
151
- | Toolbar (filter pills + search + actions) | toolbar collapse — filter drawer / search expand |
152
- | Pagination (page numbers) | infinite scroll 또는 numeric only |
153
- | selection (checkbox column) | hidden — long-press 또는 명시 toggle |
154
- | actions column (Menu) | swipe-actions 또는 row-bottom IconButton |
154
+ | Desktop (≥ md) | Mobile (< md) |
155
+ | ----------------------------------------- | ---------------------------------------------------------- |
156
+ | DataGrid table 형 | **Card list** (각 row → card, primary column 1-2개만 표시) |
157
+ | Sticky header | `position: sticky` 그대로 |
158
+ | Toolbar (filter pills + search + actions) | toolbar collapse — filter drawer / search expand |
159
+ | Pagination (page numbers) | infinite scroll 또는 numeric only |
160
+ | selection (checkbox column) | hidden — long-press 또는 명시 toggle |
161
+ | actions column (Menu) | swipe-actions 또는 row-bottom IconButton |
155
162
 
156
163
  ### Mobile 변환 mechanism
157
164
 
@@ -161,92 +168,101 @@ const columns = [
161
168
  columns={columns}
162
169
  // Mobile = card 형식 으로 자동 변환
163
170
  mobileView={{
164
- title: (row) => row.name, // card primary
165
- subtitle: (row) => row.email, // card secondary
166
- statusKey: 'status', // status badge 표시 column
167
- actionsKey: 'actions', // 우측 swipe actions
171
+ title: (row) => row.name, // card primary
172
+ subtitle: (row) => row.email, // card secondary
173
+ statusKey: 'status', // status badge 표시 column
174
+ actionsKey: 'actions', // 우측 swipe actions
168
175
  }}
169
176
  />
170
177
  ```
171
178
 
172
- - **Default**: Tailwind preset 의 token responsive (text-3xl / space-* mobile fallback 자동 — responsive-tokens.md)
173
- - **DataGrid 의 mobile = card list 자동 변환** `mobileView` prop 만 지정하면 caller 가 별도 layout 작성 없이 mobile 의 card 표시 가능
179
+ - **Default**: Tailwind preset 의 token responsive (text-3xl / space-\*
180
+ mobile fallback 자동 — responsive-tokens.md)
181
+ - **DataGrid 의 mobile = card list 자동 변환** — `mobileView` prop 만 지정하면
182
+ caller 가 별도 layout 작성 없이 mobile 의 card 표시 가능
174
183
  - **Breakpoint = md (768px)** — `useIsBreakpointUp('md')` 내부 활용
175
184
 
176
- > 현재 상태 — DataGrid 의 `mobileView` prop 은 **TBD: mds 의 DataGrid 에 추가 필요** (지금은 desktop only). 임시로는 caller 가 `useBreakpoint()` + 별도 CardList 직접 구현.
185
+ > 현재 상태 — DataGrid 의 `mobileView` prop 은 **TBD: mds 의 DataGrid 에 추가
186
+ > 필요** (지금은 desktop only). 임시로는 caller 가 `useBreakpoint()` + 별도
187
+ > CardList 직접 구현.
177
188
 
178
189
  ---
179
190
 
180
191
  ## 4. Loading / Error / Empty state — async-states.md §1 참조
181
192
 
182
- | state | 컴포넌트 |
183
- |---|---|
184
- | loading (초기 fetch) | `<SkeletonRows>` (DataGrid 자동) |
185
- | loading 더 보기 (page 2) | 부분 skeleton + 기존 row 유지 |
186
- | error | `<ErrorState error={err} onRetry={refetch}>` (DataGrid `errorState` prop) |
187
- | empty (필터 결과 0) | `<EmptyState variant="search" action={resetFilters}>` |
188
- | empty (한 번도 없음) | `<EmptyState variant="onboarding" action={createX}>` |
193
+ | state | 컴포넌트 |
194
+ | ------------------------ | ------------------------------------------------------------------------- |
195
+ | loading (초기 fetch) | `<SkeletonRows>` (DataGrid 자동) |
196
+ | loading 더 보기 (page 2) | 부분 skeleton + 기존 row 유지 |
197
+ | error | `<ErrorState error={err} onRetry={refetch}>` (DataGrid `errorState` prop) |
198
+ | empty (필터 결과 0) | `<EmptyState variant="search" action={resetFilters}>` |
199
+ | empty (한 번도 없음) | `<EmptyState variant="onboarding" action={createX}>` |
189
200
 
190
201
  ---
191
202
 
192
203
  ## 5. 결정 표 (lookup)
193
204
 
194
- | 케이스 | 답 |
195
- |---|---|
196
- | "page list 표시" | DataGrid |
197
- | "5개 정도 단순 table" | Table |
198
- | "tree / nested" | Tree (compounds) |
199
- | "row click → 상세" | DataGrid `onRowClick={navigate(...)}` |
200
- | "row click → 옆 편집 (목록 유지)" | DataGrid `onRowClick={() => openDrawer(...)}` |
201
- | "URL filter / page 동기화" | DataGrid controlled props (`page`, `onPageChange` etc.) |
202
- | "Server-side" | DataGrid `mode="server" totalItems={N}` |
203
- | "bulk action" | DataGrid `selectable bulkActions={(selected) => ...}` |
204
- | "column resize / hide / reorder" | DataGrid `storageKey` + 자동 |
205
- | "column custom component" | `column.render` prop |
206
- | "특정 column 만 sortable" | `column.sortable: true` |
207
- | "1 필드 inline 편집" | DataGrid column.render = controlled input + form.md §5 (Inline 편집 — Enter 명시 저장) |
205
+ | 케이스 | 답 |
206
+ | --------------------------------- | -------------------------------------------------------------------------------------- |
207
+ | "page list 표시" | DataGrid |
208
+ | "5개 정도 단순 table" | Table |
209
+ | "tree / nested" | Tree (compounds) |
210
+ | "row click → 상세" | DataGrid `onRowClick={navigate(...)}` |
211
+ | "row click → 옆 편집 (목록 유지)" | DataGrid `onRowClick={() => openDrawer(...)}` |
212
+ | "URL filter / page 동기화" | DataGrid controlled props (`page`, `onPageChange` etc.) |
213
+ | "Server-side" | DataGrid `mode="server" totalItems={N}` |
214
+ | "bulk action" | DataGrid `selectable bulkActions={(selected) => ...}` |
215
+ | "column resize / hide / reorder" | DataGrid `storageKey` + 자동 |
216
+ | "column custom component" | `column.render` prop |
217
+ | "특정 column 만 sortable" | `column.sortable: true` |
218
+ | "1 필드 inline 편집" | DataGrid column.render = controlled input + form.md §5 (Inline 편집 — Enter 명시 저장) |
208
219
 
209
220
  ---
210
221
 
211
222
  ## 6. 안티 패턴
212
223
 
213
- - ❌ **DataGrid 안에 form (모든 row 의 input)** — 큰 form 은 별도 페이지 (page-form). DataGrid 는 list / inline 편집 1 필드만
214
- - ❌ **Table 으로 DataGrid 흉내** toolbar / filter / pagination 다 caller 구현 = 중복. DataGrid 사용
224
+ - ❌ **DataGrid 안에 form (모든 row 의 input)** — 큰 form 은 별도 페이지
225
+ (page-form). DataGrid list / inline 편집 1 필드만
226
+ - ❌ **Table 으로 DataGrid 흉내** — toolbar / filter / pagination 다 caller 구현
227
+ = 중복. DataGrid 사용
215
228
  - ❌ **DataGrid 의 column 50+** — 사용자 스캔 불가. 일부 hidden + caller 가 선택
216
- - ❌ **Loading 시 empty state 표시** — async-states.md §2 — 로딩 중 ErrorState/EmptyState 금지
217
- - ❌ **row 클릭 + checkbox 동시 작동** — 한쪽만. selection 모드 시 row click 막힘 또는 다른 의미
229
+ - ❌ **Loading 시 empty state 표시** — async-states.md §2 — 로딩 중
230
+ ErrorState/EmptyState 금지
231
+ - ❌ **row 클릭 + checkbox 동시 작동** — 한쪽만. selection 모드 시 row click
232
+ 막힘 또는 다른 의미
218
233
  - ❌ **pagination size 가 50+** — render 부담 + a11y. 최대 20-30 권장
219
234
  - ❌ **column header 가 너무 길거나 wrap** — fixed height, ellipsis
220
- - ❌ **selection state 가 page 이동 후 사라짐** — DataGrid 의 selectedIds Set 유지 (cross-page)
235
+ - ❌ **selection state 가 page 이동 후 사라짐** — DataGrid 의 selectedIds Set
236
+ 유지 (cross-page)
221
237
 
222
238
  ---
223
239
 
224
240
  ## 7. Cross-cutting
225
241
 
226
- | Axis | 적용 |
227
- |---|---|
228
- | **Responsive** | DataGrid → card list mobile (TBD 자동 변환) |
229
- | **A11y** | `<table role="table">` 자동 / column header `<th scope="col">` / sortable button aria-sort / selection checkbox aria-label |
230
- | **i18n** | column label / filter label / empty message 모두 `t()` |
231
- | **AI fill** | DataGrid 의 filter 가 AI 자동 제안 가능 (Step 4.8) |
232
- | **Catalog** | DataGrid props (columns / filters / mode / state props) 자동 catalog (Step 5) |
233
- | **Telemetry** | row click / sort / filter / search / bulk action 자동 log (Step 7) |
242
+ | Axis | 적용 |
243
+ | -------------- | -------------------------------------------------------------------------------------------------------------------------- |
244
+ | **Responsive** | DataGrid → card list mobile (TBD 자동 변환) |
245
+ | **A11y** | `<table role="table">` 자동 / column header `<th scope="col">` / sortable button aria-sort / selection checkbox aria-label |
246
+ | **i18n** | column label / filter label / empty message 모두 `t()` |
247
+ | **AI fill** | DataGrid 의 filter 가 AI 자동 제안 가능 (Step 4.8) |
248
+ | **Catalog** | DataGrid props (columns / filters / mode / state props) 자동 catalog (Step 5) |
249
+ | **Telemetry** | row click / sort / filter / search / bulk action 자동 log (Step 7) |
234
250
 
235
251
  ---
236
252
 
237
253
  ## 8. apps/web 의 실 사용 (Step 7 Web Playbook 의 baseline)
238
254
 
239
- | Page | DataGrid 사용 |
240
- |---|---|
241
- | `/merchant/customers` | DataGrid (filter / search / row click → drawer / bulk action) |
242
- | `/merchant/orders` | DataGrid (filter / search / row click → detail page / status update bulk) |
243
- | `/merchant/payment/transactions` | DataGrid (server mode + URL state) |
244
- | `/merchant/payment/refunds` | 동일 |
245
- | `/merchant/content/blogs` (list) | DataGrid |
246
- | `/merchant/content/announcements` | DataGrid |
247
- | `/merchant/content/banners` | DataGrid |
248
- | `/admin/dead-letters` | DataGrid (admin) |
249
- | `/admin/exchange-rates` | DataGrid (admin) |
255
+ | Page | DataGrid 사용 |
256
+ | --------------------------------- | ------------------------------------------------------------------------- |
257
+ | `/merchant/customers` | DataGrid (filter / search / row click → drawer / bulk action) |
258
+ | `/merchant/orders` | DataGrid (filter / search / row click → detail page / status update bulk) |
259
+ | `/merchant/payment/transactions` | DataGrid (server mode + URL state) |
260
+ | `/merchant/payment/refunds` | 동일 |
261
+ | `/merchant/content/blogs` (list) | DataGrid |
262
+ | `/merchant/content/announcements` | DataGrid |
263
+ | `/merchant/content/banners` | DataGrid |
264
+ | `/admin/dead-letters` | DataGrid (admin) |
265
+ | `/admin/exchange-rates` | DataGrid (admin) |
250
266
 
251
267
  → 20+ 페이지가 DataGrid 사용. 균일성 보장 = Web Playbook (Step 7) 의 표준.
252
268
 
@@ -256,7 +272,8 @@ const columns = [
256
272
 
257
273
  1. **Mobile 자동 변환** — DataGrid → card list 의 자동 변환 컴포넌트 (CardList?)
258
274
  2. **Long-press multi-select** — 모바일 의 selection 패턴
259
- 3. **Inline 편집 컴포넌트** — DataGrid cell 의 EditCell wrapper 추가 (Enter / 체크 명시 저장)
275
+ 3. **Inline 편집 컴포넌트** — DataGrid cell 의 EditCell wrapper 추가 (Enter /
276
+ 체크 명시 저장)
260
277
  4. **Sticky column** — first / last column 의 sticky (가로 scroll 시)
261
278
  5. **Virtual scroll** — row 1000+ 시 virtualization (현재 mds 미지원)
262
279
  6. **Group by** — row grouping (예: status 별 group)