- @dnd-kit/core, @dnd-kit/sortable로 드래그앤드롭 순서 변경 지원 - SortableMenuItem 컴포넌트 분리, 드래그 핸들(grip) + DragOverlay 프리뷰 - 기존 UP/DOWN 버튼 유지 (드래그와 병행 사용) - docs/MENU-TAB-GUIDE.md: 새 메뉴 탭 추가 시 수정 파일 및 절차 가이드 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
187 lines
6.0 KiB
Markdown
187 lines
6.0 KiB
Markdown
# WING 메뉴 탭 추가 가이드
|
|
|
|
새로운 메뉴 탭을 추가할 때 필요한 절차를 설명합니다.
|
|
|
|
## 메뉴 시스템 구조
|
|
|
|
```
|
|
DB: AUTH_SETTING (menu.config JSON)
|
|
↕ GET/PUT /api/menus
|
|
Backend: settingsService.ts (DEFAULT_MENU_CONFIG, VALID_MENU_IDS)
|
|
↕ API
|
|
Frontend: menuStore.ts → TopBar.tsx (탭 렌더링)
|
|
→ App.tsx (renderView 라우팅)
|
|
```
|
|
|
|
- **DB**가 메뉴 정의의 단일 소스 (id, label, icon, enabled, order)
|
|
- **TopBar**는 `enabled && hasPermission` 조건으로 탭을 필터링하고 `order` 순 정렬
|
|
- **App.tsx**의 `renderView`가 탭 ID에 따라 뷰 컴포넌트를 매핑
|
|
- **admin** 탭은 메뉴 관리 대상에서 제외 (TopBar에서 별도 아이콘 버튼으로 접근)
|
|
|
|
## 수정 파일 요약
|
|
|
|
| 순서 | 파일 | 작업 | 필수 |
|
|
|------|------|------|------|
|
|
| 1 | `frontend/src/components/views/XxxView.tsx` | 뷰 컴포넌트 생성 | O |
|
|
| 2 | `frontend/src/App.tsx` | MainTab 타입 + import + renderView | O |
|
|
| 3 | `backend/src/settings/settingsService.ts` | DEFAULT_MENU_CONFIG에 항목 추가 | O |
|
|
| 4 | `database/auth_init.sql` | menu.config 초기 JSON에 추가 | O |
|
|
| 5 | 관리자 UI | 메뉴 관리에서 활성화 | O |
|
|
|
|
## Step 1: 뷰 컴포넌트 생성
|
|
|
|
`frontend/src/components/views/` 에 새 뷰 컴포넌트를 생성합니다.
|
|
|
|
```tsx
|
|
// frontend/src/components/views/MonitoringView.tsx
|
|
|
|
export function MonitoringView() {
|
|
return (
|
|
<div className="flex flex-1 overflow-hidden bg-bg-0">
|
|
<div className="flex-1 flex flex-col overflow-hidden p-6">
|
|
<h1 className="text-lg font-bold text-text-1 font-korean">실시간 모니터링</h1>
|
|
{/* 뷰 콘텐츠 */}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
```
|
|
|
|
기존 뷰 컴포넌트(`OilSpillView`, `WeatherView` 등)의 레이아웃 패턴을 참고하세요.
|
|
|
|
## Step 2: App.tsx 탭 등록
|
|
|
|
3가지를 수정합니다.
|
|
|
|
### 2-1. MainTab 타입에 ID 추가
|
|
|
|
```tsx
|
|
// frontend/src/App.tsx (line 20)
|
|
|
|
// Before
|
|
export type MainTab = 'prediction' | 'hns' | ... | 'admin'
|
|
|
|
// After
|
|
export type MainTab = 'prediction' | 'hns' | ... | 'monitoring' | 'admin'
|
|
```
|
|
|
|
### 2-2. 뷰 컴포넌트 import
|
|
|
|
```tsx
|
|
import { MonitoringView } from './components/views/MonitoringView'
|
|
```
|
|
|
|
### 2-3. renderView switch에 case 추가
|
|
|
|
```tsx
|
|
const renderView = () => {
|
|
switch (activeMainTab) {
|
|
// ... 기존 case들 ...
|
|
case 'monitoring':
|
|
return <MonitoringView />
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
## Step 3: 백엔드 메뉴 설정 등록
|
|
|
|
`backend/src/settings/settingsService.ts`의 `DEFAULT_MENU_CONFIG` 배열에 항목을 추가합니다.
|
|
|
|
```typescript
|
|
const DEFAULT_MENU_CONFIG: MenuConfigItem[] = [
|
|
// ... 기존 10개 메뉴 ...
|
|
{ id: 'monitoring', label: '실시간 모니터링', icon: '📡', enabled: true, order: 11 },
|
|
]
|
|
```
|
|
|
|
`VALID_MENU_IDS`는 `DEFAULT_MENU_CONFIG`에서 자동 파생되므로 별도 수정 불필요합니다.
|
|
|
|
```typescript
|
|
const VALID_MENU_IDS = DEFAULT_MENU_CONFIG.map(m => m.id) // 자동 포함됨
|
|
```
|
|
|
|
> **주의**: `updateMenuConfig()`은 `VALID_MENU_IDS.length` 개수 전체가 포함되어야 저장을 허용합니다.
|
|
> 기존 운영 DB에 새 메뉴가 없는 상태에서도 `getMenuConfig()`의 fallback이 DEFAULT_MENU_CONFIG을 반환하므로 정상 동작합니다.
|
|
|
|
## Step 4: DB 초기 데이터 업데이트
|
|
|
|
`database/auth_init.sql`의 `menu.config` 초기 JSON에 새 항목을 추가합니다.
|
|
|
|
```sql
|
|
INSERT INTO AUTH_SETTING (SETTING_KEY, SETTING_VAL, SETTING_DC, MDFCN_DTM) VALUES
|
|
('menu.config', '[
|
|
{"id":"prediction","label":"유출유 확산예측","icon":"🛢️","enabled":true,"order":1},
|
|
...기존 메뉴들...
|
|
{"id":"monitoring","label":"실시간 모니터링","icon":"📡","enabled":true,"order":11}
|
|
]', '메뉴 구성 설정', NOW())
|
|
ON CONFLICT (SETTING_KEY) DO NOTHING;
|
|
```
|
|
|
|
> **참고**: 이 SQL은 신규 설치 시에만 적용됩니다. 기존 운영 DB는 관리자 UI에서 메뉴를 관리합니다.
|
|
|
|
## Step 5: 관리자 메뉴 관리에서 활성화
|
|
|
|
코드 배포 후:
|
|
1. 관리자 계정으로 로그인
|
|
2. 관리자 패널(⚙️) → 메뉴 관리 탭
|
|
3. 새 메뉴가 목록에 표시됨
|
|
4. 활성/비활성 토글, 순서, 라벨, 아이콘을 설정
|
|
5. "변경사항 저장" 클릭
|
|
|
|
> 기존 DB에 새 메뉴 ID가 없으면 `getMenuConfig()`가 DEFAULT_MENU_CONFIG fallback을 사용하여 새 메뉴가 자동으로 목록에 나타납니다.
|
|
|
|
## 실전 예시: "모니터링" 탭 추가
|
|
|
|
### 1. 뷰 컴포넌트 생성
|
|
|
|
```bash
|
|
# frontend/src/components/views/MonitoringView.tsx 파일 생성
|
|
```
|
|
|
|
### 2. App.tsx 수정 (3곳)
|
|
|
|
```diff
|
|
+ import { MonitoringView } from './components/views/MonitoringView'
|
|
|
|
- export type MainTab = 'prediction' | 'hns' | 'rescue' | 'reports' | 'aerial' | 'assets' | 'scat' | 'incidents' | 'board' | 'weather' | 'admin'
|
|
+ export type MainTab = 'prediction' | 'hns' | 'rescue' | 'reports' | 'aerial' | 'assets' | 'scat' | 'incidents' | 'board' | 'weather' | 'monitoring' | 'admin'
|
|
|
|
const renderView = () => {
|
|
switch (activeMainTab) {
|
|
// ...
|
|
+ case 'monitoring':
|
|
+ return <MonitoringView />
|
|
case 'admin':
|
|
return <AdminView />
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. settingsService.ts 수정
|
|
|
|
```diff
|
|
const DEFAULT_MENU_CONFIG: MenuConfigItem[] = [
|
|
// ... 기존 메뉴들 ...
|
|
{ id: 'incidents', label: '통합조회', icon: '🔍', enabled: true, order: 10 },
|
|
+ { id: 'monitoring', label: '실시간 모니터링', icon: '📡', enabled: true, order: 11 },
|
|
]
|
|
```
|
|
|
|
### 4. auth_init.sql 수정
|
|
|
|
menu.config JSON에 새 항목 추가 (신규 설치용)
|
|
|
|
### 5. 배포 후 관리자 UI에서 활성화
|
|
|
|
## 체크리스트
|
|
|
|
- [ ] 뷰 컴포넌트 생성 (`frontend/src/components/views/`)
|
|
- [ ] `MainTab` 타입 업데이트 (`App.tsx`)
|
|
- [ ] import 및 renderView switch case 추가 (`App.tsx`)
|
|
- [ ] `DEFAULT_MENU_CONFIG`에 추가 (`settingsService.ts`)
|
|
- [ ] `menu.config` 초기 JSON 업데이트 (`auth_init.sql`)
|
|
- [ ] TypeScript 컴파일 통과 (`cd frontend && npx tsc --noEmit`)
|
|
- [ ] ESLint 통과 (`cd frontend && npx eslint .`)
|
|
- [ ] 관리자 메뉴 관리에서 새 메뉴 표시 확인
|