# 즉시 저장(quickInsert) 버튼 액션 구현 계획서

## 1. 개요

### 1.1 목적
화면에서 entity 타입 선택박스로 데이터를 선택한 후, 버튼 클릭 시 특정 테이블에 즉시 INSERT하는 기능 구현

### 1.2 사용 사례
- **공정별 설비 관리**: 좌측에서 공정 선택 → 우측에서 설비 선택 → "설비 추가" 버튼 클릭 → `process_equipment` 테이블에 즉시 저장

### 1.3 화면 구성 예시
```
┌─────────────────────────────────────────────────────────────┐
│  [entity 선택박스]              [버튼: quickInsert]         │
│  ┌─────────────────────────────┐  ┌──────────────┐         │
│  │ MCT-01 - 머시닝센터 #1   ▼ │  │ + 설비 추가  │         │
│  └─────────────────────────────┘  └──────────────┘         │
└─────────────────────────────────────────────────────────────┘
```

---

## 2. 기술 설계

### 2.1 버튼 액션 타입 추가

```typescript
// types/screen-management.ts
type ButtonActionType = 
  | "save"
  | "cancel"
  | "delete"
  | "edit"
  | "add"
  | "search"
  | "reset"
  | "submit"
  | "close"
  | "popup"
  | "navigate"
  | "custom"
  | "quickInsert"  // 🆕 즉시 저장
```

### 2.2 quickInsert 설정 구조

```typescript
interface QuickInsertColumnMapping {
  targetColumn: string;           // 저장할 테이블의 컬럼명
  sourceType: "component" | "leftPanel" | "fixed" | "currentUser";
  
  // sourceType별 추가 설정
  sourceComponentId?: string;     // component: 값을 가져올 컴포넌트 ID
  sourceColumn?: string;          // leftPanel: 좌측 선택 데이터의 컬럼명
  fixedValue?: any;               // fixed: 고정값
  userField?: string;             // currentUser: 사용자 정보 필드 (userId, userName, companyCode)
}

interface QuickInsertConfig {
  targetTable: string;            // 저장할 테이블명
  columnMappings: QuickInsertColumnMapping[];
  
  // 저장 후 동작
  afterInsert?: {
    refreshRightPanel?: boolean;  // 우측 패널 새로고침
    clearComponents?: string[];   // 초기화할 컴포넌트 ID 목록
    showSuccessMessage?: boolean; // 성공 메시지 표시
    successMessage?: string;      // 커스텀 성공 메시지
  };
  
  // 중복 체크 (선택사항)
  duplicateCheck?: {
    enabled: boolean;
    columns: string[];            // 중복 체크할 컬럼들
    errorMessage?: string;        // 중복 시 에러 메시지
  };
}

interface ButtonComponentConfig {
  // 기존 설정들...
  actionType: ButtonActionType;
  
  // 🆕 quickInsert 전용 설정
  quickInsertConfig?: QuickInsertConfig;
}
```

### 2.3 데이터 흐름

```
1. 사용자가 entity 선택박스에서 설비 선택
   └─ equipment_code = "EQ-001" (내부값)
   └─ 표시: "MCT-01 - 머시닝센터 #1"

2. 사용자가 "설비 추가" 버튼 클릭

3. quickInsert 핸들러 실행
   ├─ columnMappings 순회
   │   ├─ equipment_code: component에서 값 가져오기 → "EQ-001"
   │   └─ process_code: leftPanel에서 값 가져오기 → "PRC-001"
   │
   └─ INSERT 데이터 구성
       {
         equipment_code: "EQ-001",
         process_code: "PRC-001",
         company_code: "COMPANY_7",  // 자동 추가
         writer: "wace"              // 자동 추가
       }

4. API 호출: POST /api/table-management/tables/process_equipment/add

5. 성공 시
   ├─ 성공 메시지 표시
   ├─ 우측 패널(카드/테이블) 새로고침
   └─ 선택박스 초기화
```

---

## 3. 구현 계획

### 3.1 Phase 1: 타입 정의 및 설정 UI

| 작업 | 파일 | 설명 |
|------|------|------|
| 1-1 | `frontend/types/screen-management.ts` | QuickInsertConfig 타입 추가 |
| 1-2 | `frontend/components/screen/config-panels/ButtonConfigPanel.tsx` | quickInsert 설정 UI 추가 |

### 3.2 Phase 2: 버튼 액션 핸들러 구현

| 작업 | 파일 | 설명 |
|------|------|------|
| 2-1 | `frontend/components/screen/InteractiveScreenViewerDynamic.tsx` | quickInsert 핸들러 추가 |
| 2-2 | 컴포넌트 값 수집 로직 | 같은 화면의 다른 컴포넌트에서 값 가져오기 |

### 3.3 Phase 3: 테스트 및 검증

| 작업 | 설명 |
|------|------|
| 3-1 | 공정별 설비 화면에서 테스트 |
| 3-2 | 중복 저장 방지 테스트 |
| 3-3 | 에러 처리 테스트 |

---

## 4. 상세 구현

### 4.1 ButtonConfigPanel 설정 UI

```
┌─────────────────────────────────────────────────────────────┐
│ 버튼 액션 타입                                              │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 즉시 저장 (quickInsert)                              ▼ │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                             │
│ ─────────────── 즉시 저장 설정 ───────────────             │
│                                                             │
│ 대상 테이블 *                                               │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ process_equipment                                     ▼ │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                             │
│ 컬럼 매핑                                          [+ 추가] │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 매핑 #1                                           [삭제] │ │
│ │ 대상 컬럼: equipment_code                                │ │
│ │ 값 소스: 컴포넌트 선택                                   │ │
│ │ 컴포넌트: [equipment-select ▼]                          │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 매핑 #2                                           [삭제] │ │
│ │ 대상 컬럼: process_code                                  │ │
│ │ 값 소스: 좌측 패널 데이터                                │ │
│ │ 소스 컬럼: process_code                                  │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                             │
│ ─────────────── 저장 후 동작 ───────────────               │
│                                                             │
│ ☑ 우측 패널 새로고침                                       │
│ ☑ 선택박스 초기화                                          │
│ ☑ 성공 메시지 표시                                         │
│                                                             │
│ ─────────────── 중복 체크 (선택) ───────────────           │
│                                                             │
│ ☐ 중복 체크 활성화                                         │
│   체크 컬럼: equipment_code, process_code                  │
│   에러 메시지: 이미 등록된 설비입니다.                      │
└─────────────────────────────────────────────────────────────┘
```

### 4.2 핸들러 구현 (의사 코드)

```typescript
const handleQuickInsert = async (config: QuickInsertConfig) => {
  // 1. 컬럼 매핑에서 값 수집
  const insertData: Record<string, any> = {};
  
  for (const mapping of config.columnMappings) {
    let value: any;
    
    switch (mapping.sourceType) {
      case "component":
        // 같은 화면의 컴포넌트에서 값 가져오기
        value = getComponentValue(mapping.sourceComponentId);
        break;
        
      case "leftPanel":
        // 분할 패널 좌측 선택 데이터에서 값 가져오기
        value = splitPanelContext?.selectedLeftData?.[mapping.sourceColumn];
        break;
        
      case "fixed":
        value = mapping.fixedValue;
        break;
        
      case "currentUser":
        value = user?.[mapping.userField];
        break;
    }
    
    if (value !== undefined && value !== null && value !== "") {
      insertData[mapping.targetColumn] = value;
    }
  }
  
  // 2. 필수값 검증
  if (Object.keys(insertData).length === 0) {
    toast.error("저장할 데이터가 없습니다.");
    return;
  }
  
  // 3. 중복 체크 (설정된 경우)
  if (config.duplicateCheck?.enabled) {
    const isDuplicate = await checkDuplicate(
      config.targetTable,
      config.duplicateCheck.columns,
      insertData
    );
    if (isDuplicate) {
      toast.error(config.duplicateCheck.errorMessage || "이미 존재하는 데이터입니다.");
      return;
    }
  }
  
  // 4. API 호출
  try {
    await tableTypeApi.addTableData(config.targetTable, insertData);
    
    // 5. 성공 후 동작
    if (config.afterInsert?.showSuccessMessage) {
      toast.success(config.afterInsert.successMessage || "저장되었습니다.");
    }
    
    if (config.afterInsert?.refreshRightPanel) {
      // 우측 패널 새로고침 트리거
      onRefresh?.();
    }
    
    if (config.afterInsert?.clearComponents) {
      // 지정된 컴포넌트 초기화
      for (const componentId of config.afterInsert.clearComponents) {
        clearComponentValue(componentId);
      }
    }
    
  } catch (error) {
    toast.error("저장에 실패했습니다.");
  }
};
```

---

## 5. 컴포넌트 간 통신 방안

### 5.1 문제점
- 버튼 컴포넌트에서 같은 화면의 entity 선택박스 값을 가져와야 함
- 현재는 각 컴포넌트가 독립적으로 동작

### 5.2 해결 방안: formData 활용

현재 `InteractiveScreenViewerDynamic`에서 `formData` 상태로 모든 입력값을 관리하고 있음.

```typescript
// InteractiveScreenViewerDynamic.tsx
const [localFormData, setLocalFormData] = useState<Record<string, any>>({});

// entity 선택박스에서 값 변경 시
const handleFormDataChange = (fieldName: string, value: any) => {
  setLocalFormData(prev => ({ ...prev, [fieldName]: value }));
};

// 버튼 클릭 시 formData에서 값 가져오기
const getComponentValue = (componentId: string) => {
  // componentId로 컴포넌트의 columnName 찾기
  const component = allComponents.find(c => c.id === componentId);
  if (component?.columnName) {
    return formData[component.columnName];
  }
  return undefined;
};
```

---

## 6. 테스트 시나리오

### 6.1 정상 케이스
1. 좌측 테이블에서 공정 "PRC-001" 선택
2. 우측 설비 선택박스에서 "MCT-01" 선택
3. "설비 추가" 버튼 클릭
4. `process_equipment` 테이블에 데이터 저장 확인
5. 우측 카드/테이블에 새 항목 표시 확인

### 6.2 에러 케이스
1. 좌측 미선택 상태에서 버튼 클릭 → "좌측에서 항목을 선택해주세요" 메시지
2. 설비 미선택 상태에서 버튼 클릭 → "설비를 선택해주세요" 메시지
3. 중복 데이터 저장 시도 → "이미 등록된 설비입니다" 메시지

### 6.3 엣지 케이스
1. 동일 설비 연속 추가 시도
2. 네트워크 오류 시 재시도
3. 권한 없는 사용자의 저장 시도

---

## 7. 일정

| Phase | 작업 | 예상 시간 |
|-------|------|----------|
| Phase 1 | 타입 정의 및 설정 UI | 1시간 |
| Phase 2 | 버튼 액션 핸들러 구현 | 1시간 |
| Phase 3 | 테스트 및 검증 | 30분 |
| **합계** | | **2시간 30분** |

---

## 8. 향후 확장 가능성

1. **다중 행 추가**: 여러 설비를 한 번에 선택하여 추가
2. **수정 모드**: 기존 데이터 수정 기능
3. **조건부 저장**: 특정 조건 만족 시에만 저장
4. **연쇄 저장**: 한 번의 클릭으로 여러 테이블에 저장









