ERP-node/제어관리_시스템_개선_계획서.md

470 lines
13 KiB
Markdown
Raw Normal View History

2025-09-29 10:23:21 +09:00
# 🔄 제어관리 시스템 개선 계획서
## 📋 개요
데이터 매핑 시스템이 추가되면서 기존 제어관리 로직과 버튼 연동 방식의 개선이 필요합니다.
## 🎯 주요 개선사항
### 1. 명칭 변경: "관계도" → "관계"
#### 1.1 변경 이유
- **기존**: "관계도"는 다이어그램 전체를 의미하는 용어
- **현재**: 실제로는 개별 "관계"를 설정하고 관리
- **개선**: 사용자 이해도 향상 및 용어 일관성 확보
#### 1.2 변경 대상 파일들
```typescript
// UI 컴포넌트들
frontend / components / screen / config -
panels / ButtonDataflowConfigPanel.tsx;
frontend / components / dataflow / DataFlowDesigner.tsx;
frontend / components / dataflow / SaveDiagramModal.tsx;
frontend / components / dataflow / RelationshipListModal.tsx;
frontend /
components /
dataflow /
connection /
redesigned /
RightPanel /
ConnectionStep.tsx;
// API 및 서비스
frontend / lib / api / dataflow.ts;
frontend / hooks / useDataFlowDesigner.ts;
// 타입 정의
frontend / types / control - management.ts;
```
### 2. 버튼 제어관리 로직 개선
#### 2.1 현재 문제점
```typescript
// 🔴 기존: 복잡한 관계도 선택 방식
interface ButtonDataflowConfig {
controlMode: "simple" | "advanced";
selectedDiagramId?: number; // 관계도 전체 선택
selectedRelationshipId?: string; // 개별 관계 선택
// ...
}
```
#### 2.2 개선 방향
```typescript
// 🟢 개선: 단순화된 관계 직접 선택
interface ButtonDataflowConfig {
controlMode: "relationship" | "external_call" | "custom";
// 관계 기반 제어
relationshipConfig?: {
relationshipId: string; // 관계 직접 선택
relationshipName: string; // 관계명 표시
executionTiming: "before" | "after" | "replace";
contextData?: Record<string, any>; // 실행 시 전달할 컨텍스트
};
// 외부호출 제어
externalCallConfig?: {
configId: string; // external_call_configs ID
configName: string; // 설정명 표시
executionTiming: "before" | "after" | "replace";
dataMappingEnabled: boolean; // 데이터 매핑 사용 여부
};
// 커스텀 제어
customConfig?: {
actionType: string;
parameters: Record<string, any>;
};
}
```
### 3. 외부호출 연동 개선
#### 3.1 현재 외부호출 설정 방식
```typescript
// 🔴 현재: 복잡한 설정 구조
interface ExternalCallConfig {
callType: "rest-api";
restApiSettings: {
apiUrl: string;
httpMethod: string;
// ... 많은 설정들
};
}
```
#### 3.2 개선된 연동 방식
```typescript
// 🟢 개선: 단순화된 참조 구조
interface ButtonExternalCallConfig {
// 1단계: 저장된 외부호출 설정 선택
externalCallConfigId: string; // external_call_configs 테이블 ID
configName: string; // 설정명 (UI 표시용)
// 2단계: 실행 시점 설정
executionTiming: "before" | "after" | "replace";
// 3단계: 데이터 전달 방식
dataMapping: {
enabled: boolean; // 데이터 매핑 사용 여부
sourceMode: "form" | "table" | "custom"; // 데이터 소스
sourceConfig?: {
tableName?: string; // table 모드용
customData?: Record<string, any>; // custom 모드용
};
};
// 4단계: 실행 옵션
executionOptions: {
rollbackOnError: boolean; // 실패 시 롤백
showLoadingIndicator: boolean; // 로딩 표시
successMessage?: string; // 성공 메시지
errorMessage?: string; // 실패 메시지
};
}
```
### 4. 버튼 액션 실행 로직 개선
#### 4.1 현재 실행 플로우
```mermaid
graph TD
A[버튼 클릭] --> B[기존 액션 실행]
B --> C[제어관리 확인]
C --> D[관계도 조회]
D --> E[관계 찾기]
E --> F[조건 검증]
F --> G[액션 실행]
```
#### 4.2 개선된 실행 플로우
```mermaid
graph TD
A[버튼 클릭] --> B[제어 설정 확인]
B --> C{제어 타입}
C -->|관계| D[관계 직접 실행]
C -->|외부호출| E[외부호출 실행]
C -->|없음| F[기존 액션만 실행]
D --> G[조건 검증]
G --> H[관계 액션 실행]
E --> I[데이터 매핑]
I --> J[외부 API 호출]
J --> K[응답 처리]
H --> L[완료]
K --> L
F --> L
```
#### 4.3 개선된 ButtonActionExecutor
```typescript
export class ButtonActionExecutor {
/**
* 🔥 개선된 버튼 액션 실행
*/
static async executeButtonAction(
buttonConfig: ExtendedButtonTypeConfig,
formData: Record<string, any>,
context: ButtonExecutionContext
): Promise<ButtonExecutionResult> {
const executionPlan = this.createExecutionPlan(buttonConfig);
const results: ExecutionResult[] = [];
try {
// 1. Before 타이밍 제어 실행
if (executionPlan.beforeControls.length > 0) {
const beforeResults = await this.executeControls(
executionPlan.beforeControls,
formData,
context
);
results.push(...beforeResults);
}
// 2. 메인 액션 실행 (replace가 아닌 경우에만)
if (!executionPlan.hasReplaceControl) {
const mainResult = await this.executeMainAction(
buttonConfig,
formData,
context
);
results.push(mainResult);
}
// 3. After 타이밍 제어 실행
if (executionPlan.afterControls.length > 0) {
const afterResults = await this.executeControls(
executionPlan.afterControls,
formData,
context
);
results.push(...afterResults);
}
return {
success: true,
results,
executionTime: Date.now() - context.startTime,
};
} catch (error) {
// 롤백 처리
await this.handleExecutionError(error, results, buttonConfig);
throw error;
}
}
/**
* 🔥 제어 실행 (관계 또는 외부호출)
*/
private static async executeControls(
controls: ControlConfig[],
formData: Record<string, any>,
context: ButtonExecutionContext
): Promise<ExecutionResult[]> {
const results: ExecutionResult[] = [];
for (const control of controls) {
switch (control.type) {
case "relationship":
const relationshipResult = await this.executeRelationship(
control.relationshipConfig!,
formData,
context
);
results.push(relationshipResult);
break;
case "external_call":
const externalCallResult = await this.executeExternalCall(
control.externalCallConfig!,
formData,
context
);
results.push(externalCallResult);
break;
}
}
return results;
}
/**
* 🔥 관계 실행
*/
private static async executeRelationship(
config: RelationshipConfig,
formData: Record<string, any>,
context: ButtonExecutionContext
): Promise<ExecutionResult> {
// 1. 관계 정보 조회
const relationship = await RelationshipAPI.getRelationshipById(
config.relationshipId
);
// 2. 컨텍스트 데이터 준비
const contextData = {
...formData,
...config.contextData,
buttonId: context.buttonId,
screenId: context.screenId,
userId: context.userId,
companyCode: context.companyCode,
};
// 3. 관계 실행
return await EventTriggerService.executeSpecificRelationship(
relationship,
contextData,
context.companyCode
);
}
/**
* 🔥 외부호출 실행
*/
private static async executeExternalCall(
config: ExternalCallConfig,
formData: Record<string, any>,
context: ButtonExecutionContext
): Promise<ExecutionResult> {
// 1. 외부호출 설정 조회
const externalCallConfig = await ExternalCallConfigAPI.getConfigById(
config.configId
);
// 2. 데이터 매핑 처리
let mappedData = formData;
if (config.dataMappingEnabled && externalCallConfig.dataMappingConfig) {
mappedData = await DataMappingService.processOutboundData(
externalCallConfig.dataMappingConfig.outboundMapping,
formData
);
}
// 3. 외부 API 호출
const callResult = await ExternalCallService.executeWithDataMapping(
externalCallConfig.configData,
externalCallConfig.dataMappingConfig,
mappedData
);
// 4. 응답 데이터 처리 (Inbound 매핑)
if (
callResult.success &&
config.dataMappingEnabled &&
externalCallConfig.dataMappingConfig?.direction === "inbound"
) {
await DataMappingService.processInboundData(
callResult.response,
externalCallConfig.dataMappingConfig.inboundMapping!
);
}
return {
success: callResult.success,
message: callResult.success ? "외부호출 성공" : callResult.error,
executionTime: callResult.executionTime,
data: callResult,
};
}
}
```
### 5. UI/UX 개선 사항
#### 5.1 버튼 설정 패널 개선
```typescript
// 🟢 단순화된 제어 설정 UI
const ButtonControlConfigPanel = () => {
return (
<Card>
<CardHeader>
<CardTitle>버튼 제어 설정</CardTitle>
</CardHeader>
<CardContent>
<Tabs value={controlType} onValueChange={setControlType}>
<TabsList>
<TabsTrigger value="none">제어 없음</TabsTrigger>
<TabsTrigger value="relationship">관계 실행</TabsTrigger>
<TabsTrigger value="external_call">외부 호출</TabsTrigger>
</TabsList>
<TabsContent value="relationship">
<RelationshipSelector
selectedRelationshipId={config.relationshipId}
onSelect={handleRelationshipSelect}
/>
</TabsContent>
<TabsContent value="external_call">
<ExternalCallSelector
selectedConfigId={config.externalCallConfigId}
onSelect={handleExternalCallSelect}
/>
</TabsContent>
</Tabs>
</CardContent>
</Card>
);
};
```
#### 5.2 관계 선택 컴포넌트
```typescript
const RelationshipSelector = ({ onSelect }) => {
const [relationships, setRelationships] = useState([]);
useEffect(() => {
// 전체 관계 목록 로드 (관계도별 구분 없이)
loadAllRelationships();
}, []);
return (
<div className="space-y-4">
<Label>실행할 관계 선택</Label>
<Select onValueChange={onSelect}>
<SelectTrigger>
<SelectValue placeholder="관계를 선택하세요" />
</SelectTrigger>
<SelectContent>
{relationships.map((rel) => (
<SelectItem key={rel.id} value={rel.id}>
<div className="flex flex-col">
<span className="font-medium">{rel.name}</span>
<span className="text-xs text-muted-foreground">
{rel.sourceTable} → {rel.targetTable}
</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
};
```
### 6. 구현 우선순위
#### Phase 1: 명칭 변경 (1일)
1. **UI 텍스트 변경**: "관계도" → "관계"
2. **변수명 정리**: `diagram``relationship` 관련
3. **API 엔드포인트 정리**: 일관성 있는 명명
#### Phase 2: 버튼 제어 로직 개선 (2-3일)
1. **ButtonDataflowConfig 타입 개선**
2. **RelationshipSelector 컴포넌트 개발**
3. **ExternalCallSelector 컴포넌트 개발**
4. **ButtonActionExecutor 로직 개선**
#### Phase 3: 외부호출 통합 (1-2일)
1. **외부호출 설정 참조 방식 개선**
2. **데이터 매핑 통합**
3. **실행 플로우 최적화**
#### Phase 4: 테스트 및 최적화 (1일)
1. **전체 플로우 테스트**
2. **성능 최적화**
3. **사용자 가이드 업데이트**
### 7. 기대 효과
#### 7.1 사용자 경험 개선
- **직관적인 용어**: "관계도" → "관계"로 이해도 향상
- **단순화된 설정**: 복잡한 관계도 탐색 → 직접 관계 선택
- **통합된 제어**: 관계 실행과 외부호출을 동일한 방식으로 관리
#### 7.2 개발 효율성 향상
- **명확한 책임 분리**: 관계 관리와 외부호출 관리 분리
- **재사용성 증대**: 외부호출 설정의 재사용성 향상
- **유지보수성 개선**: 단순화된 로직으로 디버깅 용이
#### 7.3 시스템 확장성
- **새로운 제어 타입 추가 용이**: 플러그인 방식으로 확장 가능
- **데이터 매핑 시스템 완전 활용**: 외부 시스템과의 유연한 연동
- **모니터링 및 로깅 강화**: 각 단계별 상세한 실행 로그
이 개선 계획을 통해 제어관리 시스템이 더욱 직관적이고 강력한 기능을 제공할 수 있을 것입니다.