feat: 앱 메뉴 등록·수정에서 메뉴설명 칸을 뺀다
칸과 함께 도메인 필드·검증·저장 파라미터·응답 매핑까지 걷어낸다 — 그리는 곳이 이 칸뿐이라 남겨 두면 죽은 데이터가 된다. MENU_EXPLAN 컬럼은 백엔드에 그대로 있다. update가 동적 SET() 이라 보내지 않으면 기존 값이 유지되고, 신규는 NULL로 들어간다 — 메뉴권한(roleId)과 같은 방식이다. Co-Authored-By: Claude Opus 5
@124ebcfef2fb000e196ac9547df0187587c89722
--- app/(protected)/(basic)/system/menus/_actions.ts
+++ app/(protected)/(basic)/system/menus/_actions.ts
... | ... | @@ -64,7 +64,6 @@ |
| 64 | 64 |
sortOrder: parseFormNumber(formData, 'sortOrder'), |
| 65 | 65 |
isTopActive: readString(formData, 'isTopActive') === 'Y', |
| 66 | 66 |
url: readString(formData, 'url'), |
| 67 |
- description: readString(formData, 'description'), |
|
| 68 | 67 |
largeIconId: await resolveIconId(formData, 'largeIcon', 'largeIconId'), |
| 69 | 68 |
smallIconId: await resolveIconId(formData, 'smallIcon', 'smallIconId'), |
| 70 | 69 |
isActive: readString(formData, 'isActive') === 'Y', |
--- app/(protected)/(basic)/system/menus/_components/menu-form-modal.tsx
+++ app/(protected)/(basic)/system/menus/_components/menu-form-modal.tsx
... | ... | @@ -18,7 +18,6 @@ |
| 18 | 18 |
type SystemMenuNode, |
| 19 | 19 |
} from '@/lib/domain/system-menu'; |
| 20 | 20 |
import {
|
| 21 |
- DESCRIPTION_MAX_LENGTH, |
|
| 22 | 21 |
INITIAL_SYSTEM_MENU_FORM_STATE, |
| 23 | 22 |
NAME_MAX_LENGTH, |
| 24 | 23 |
URL_MAX_LENGTH, |
... | ... | @@ -192,20 +191,6 @@ |
| 192 | 191 |
<FoxHelperText |
| 193 | 192 |
type="information" |
| 194 | 193 |
message="저장되지 않는 항목입니다. 백엔드에 컬럼이 추가되면 연결됩니다." |
| 195 |
- /> |
|
| 196 |
- </div> |
|
| 197 |
- |
|
| 198 |
- <div className={styles.field}>
|
|
| 199 |
- <FoxInput |
|
| 200 |
- size="md" |
|
| 201 |
- name="description" |
|
| 202 |
- label="메뉴설명" |
|
| 203 |
- requirement="required" |
|
| 204 |
- defaultValue={menu?.description ?? ''}
|
|
| 205 |
- placeholder="메뉴설명을 입력하세요." |
|
| 206 |
- maxLength={DESCRIPTION_MAX_LENGTH}
|
|
| 207 |
- invalid={Boolean(errors.description)}
|
|
| 208 |
- message={errors.description}
|
|
| 209 | 194 |
/> |
| 210 | 195 |
</div> |
| 211 | 196 |
|
--- lib/data/repositories/system-menu-repository.ts
+++ lib/data/repositories/system-menu-repository.ts
... | ... | @@ -64,7 +64,6 @@ |
| 64 | 64 |
isActive: parseYesNo(raw.useYn), |
| 65 | 65 |
isTopActive: parseYesNo(raw.topYn), |
| 66 | 66 |
isDefault: parseYesNo(raw.defaultYn), |
| 67 |
- description: readString(raw, 'menuExplan'), |
|
| 68 | 67 |
largeIconId: readString(raw, 'atchFileId'), |
| 69 | 68 |
largeIconName: readString(raw, 'fileNm'), |
| 70 | 69 |
// 미리보기 주소는 서버에서 만든다 — 주소 조립기가 server-only라 화면이 부를 수 없다. |
... | ... | @@ -100,8 +99,8 @@ |
| 100 | 99 |
|
| 101 | 100 |
/** |
| 102 | 101 |
* 새창여부는 보내지 않는다 — 저장할 컬럼이 없다(파일 상단 ⚠️ 3). |
| 103 |
- * 메뉴권한(roleId)도 화면에서 빠져 보내지 않는다 — update는 동적 SET이라 기존 값이 유지되고, |
|
| 104 |
- * 신규는 NULL로 들어간다(권한 없는 메뉴는 원래도 있다). |
|
| 102 |
+ * 메뉴권한(roleId)과 메뉴설명(menuExplan)도 화면에서 빠져 보내지 않는다 — update는 동적 |
|
| 103 |
+ * SET(`<if test="… != null">`)이라 기존 값이 유지되고, 신규는 NULL로 들어간다. |
|
| 105 | 104 |
*/ |
| 106 | 105 |
function toWriteParams(values: SystemMenuValues) {
|
| 107 | 106 |
return {
|
... | ... | @@ -109,7 +108,6 @@ |
| 109 | 108 |
sortOrdr: values.sortOrder, |
| 110 | 109 |
topYn: values.isTopActive ? 'Y' : 'N', |
| 111 | 110 |
menuUrl: values.url, |
| 112 |
- menuExplan: values.description, |
|
| 113 | 111 |
useYn: values.isActive ? 'Y' : 'N', |
| 114 | 112 |
}; |
| 115 | 113 |
} |
--- lib/domain/system-menu-form.ts
+++ lib/domain/system-menu-form.ts
... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 |
export const NAME_MAX_LENGTH = 100; |
| 2 | 2 |
export const URL_MAX_LENGTH = 300; |
| 3 |
-export const DESCRIPTION_MAX_LENGTH = 300; |
|
| 4 | 3 |
export const SORT_ORDER_MAX = 9999; |
| 5 | 4 |
|
| 6 | 5 |
export interface SystemMenuValues {
|
... | ... | @@ -11,7 +10,6 @@ |
| 11 | 10 |
sortOrder: number; |
| 12 | 11 |
isTopActive: boolean; |
| 13 | 12 |
url: string; |
| 14 |
- description: string; |
|
| 15 | 13 |
largeIconId: string; |
| 16 | 14 |
smallIconId: string; |
| 17 | 15 |
isActive: boolean; |
... | ... | @@ -19,7 +17,7 @@ |
| 19 | 17 |
|
| 20 | 18 |
export type SystemMenuFormErrors = Partial< |
| 21 | 19 |
Record< |
| 22 |
- 'name' | 'sortOrder' | 'url' | 'description' | 'largeIconId' | 'smallIconId', |
|
| 20 |
+ 'name' | 'sortOrder' | 'url' | 'largeIconId' | 'smallIconId', |
|
| 23 | 21 |
string |
| 24 | 22 |
> |
| 25 | 23 |
>; |
... | ... | @@ -69,15 +67,6 @@ |
| 69 | 67 |
errors.url = url.error; |
| 70 | 68 |
} |
| 71 | 69 |
|
| 72 |
- const description = validateText( |
|
| 73 |
- values.description, |
|
| 74 |
- DESCRIPTION_MAX_LENGTH, |
|
| 75 |
- '메뉴설명' |
|
| 76 |
- ); |
|
| 77 |
- if (description.error) {
|
|
| 78 |
- errors.description = description.error; |
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 | 70 |
if (!Number.isInteger(values.sortOrder) || values.sortOrder < 1) {
|
| 82 | 71 |
errors.sortOrder = '메뉴순번은 1 이상의 숫자로 입력해 주세요.'; |
| 83 | 72 |
} else if (values.sortOrder > SORT_ORDER_MAX) {
|
... | ... | @@ -94,7 +83,6 @@ |
| 94 | 83 |
...values, |
| 95 | 84 |
name: name.value, |
| 96 | 85 |
url: url.value, |
| 97 |
- description: description.value, |
|
| 98 | 86 |
}, |
| 99 | 87 |
}; |
| 100 | 88 |
} |
--- lib/domain/system-menu.ts
+++ lib/domain/system-menu.ts
... | ... | @@ -9,7 +9,6 @@ |
| 9 | 9 |
isActive: boolean; |
| 10 | 10 |
isTopActive: boolean; |
| 11 | 11 |
isDefault: boolean; |
| 12 |
- description: string | null; |
|
| 13 | 12 |
largeIconId: string | null; |
| 14 | 13 |
largeIconName: string | null; |
| 15 | 14 |
largeIconUrl: string | null; |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?