에러 수정
This commit is contained in:
parent
e0777d0fc3
commit
cedb5e3ec3
|
|
@ -1523,7 +1523,7 @@ export const getUserInfo = async (req: AuthenticatedRequest, res: Response) => {
|
|||
partnerObjid: user.partner_objid,
|
||||
rank: user.rank,
|
||||
photo: user.photo
|
||||
? `data:image/jpeg;base64,${user.photo.toString("base64")}`
|
||||
? `data:image/jpeg;base64,${Buffer.from(user.photo).toString("base64")}`
|
||||
: null,
|
||||
locale: user.locale,
|
||||
companyCode: user.company_code,
|
||||
|
|
@ -2415,7 +2415,7 @@ export const updateProfile = async (
|
|||
const responseData = {
|
||||
...updatedUser,
|
||||
photo: updatedUser?.photo
|
||||
? `data:image/jpeg;base64,${updatedUser.photo.toString("base64")}`
|
||||
? `data:image/jpeg;base64,${Buffer.from(updatedUser.photo).toString("base64")}`
|
||||
: null,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ export class AuthService {
|
|||
authName: authNames || undefined,
|
||||
companyCode: userInfo.company_code || "ILSHIN",
|
||||
photo: userInfo.photo
|
||||
? `data:image/jpeg;base64,${userInfo.photo.toString("base64")}`
|
||||
? `data:image/jpeg;base64,${Buffer.from(userInfo.photo).toString("base64")}`
|
||||
: undefined,
|
||||
locale: userInfo.locale || "KR",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,469 @@
|
|||
# 🔄 제어관리 시스템 개선 계획서
|
||||
|
||||
## 📋 개요
|
||||
|
||||
데이터 매핑 시스템이 추가되면서 기존 제어관리 로직과 버튼 연동 방식의 개선이 필요합니다.
|
||||
|
||||
## 🎯 주요 개선사항
|
||||
|
||||
### 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 시스템 확장성
|
||||
|
||||
- **새로운 제어 타입 추가 용이**: 플러그인 방식으로 확장 가능
|
||||
- **데이터 매핑 시스템 완전 활용**: 외부 시스템과의 유연한 연동
|
||||
- **모니터링 및 로깅 강화**: 각 단계별 상세한 실행 로그
|
||||
|
||||
이 개선 계획을 통해 제어관리 시스템이 더욱 직관적이고 강력한 기능을 제공할 수 있을 것입니다.
|
||||
Loading…
Reference in New Issue