373 lines
8.9 KiB
Markdown
373 lines
8.9 KiB
Markdown
# 🚨 버튼 제어관리 기능 통합 - 잠재적 문제점 및 해결방안
|
|
|
|
## 📊 성능 관련 문제점
|
|
|
|
### 1. **버튼 클릭 시 지연 시간 증가**
|
|
|
|
**문제점:**
|
|
|
|
- 기존 버튼은 단순한 액션만 수행 (50-100ms)
|
|
- 제어관리 추가 시 복합적인 처리로 인한 지연 가능성
|
|
- 데이터베이스 조건 검증: 100-300ms
|
|
- 복잡한 비즈니스 로직 실행: 500ms-2초
|
|
- 다중 테이블 업데이트: 1-5초
|
|
|
|
**현재 코드에서 확인된 문제:**
|
|
|
|
```typescript
|
|
// InteractiveScreenViewer.tsx에서 버튼 클릭 처리가 동기적
|
|
const handleButtonClick = async () => {
|
|
// 기존에는 단순한 액션만
|
|
switch (actionType) {
|
|
case "save":
|
|
await handleSaveAction();
|
|
break; // ~100ms
|
|
// 제어관리 추가 시 복합 처리로 증가 예상
|
|
}
|
|
};
|
|
```
|
|
|
|
**해결방안:**
|
|
|
|
1. **비동기 처리 + 로딩 상태**
|
|
|
|
```typescript
|
|
const [isExecuting, setIsExecuting] = useState(false);
|
|
|
|
const handleButtonClick = async () => {
|
|
setIsExecuting(true);
|
|
try {
|
|
// 제어관리 실행
|
|
} finally {
|
|
setIsExecuting(false);
|
|
}
|
|
};
|
|
```
|
|
|
|
2. **백그라운드 실행 옵션**
|
|
|
|
```typescript
|
|
// 긴급하지 않은 제어관리는 백그라운드에서 실행
|
|
if (config.executionOptions?.asyncExecution) {
|
|
// 즉시 성공 응답
|
|
// 백그라운드에서 제어관리 실행
|
|
}
|
|
```
|
|
|
|
### 2. **메모리 사용량 증가**
|
|
|
|
**문제점:**
|
|
|
|
- 각 버튼마다 제어관리 설정을 메모리에 보관
|
|
- 복잡한 조건/액션 설정으로 인한 메모리 사용량 증가
|
|
- 대량의 버튼이 있는 화면에서 메모리 부족 가능성
|
|
|
|
**해결방안:**
|
|
|
|
1. **지연 로딩**
|
|
|
|
```typescript
|
|
// 제어관리 설정을 필요할 때만 로드
|
|
const loadDataflowConfig = useCallback(async () => {
|
|
if (config.enableDataflowControl && !dataflowConfig) {
|
|
const config = await apiClient.get(`/button-dataflow/config/${buttonId}`);
|
|
setDataflowConfig(config.data);
|
|
}
|
|
}, [buttonId, config.enableDataflowControl]);
|
|
```
|
|
|
|
2. **설정 캐싱**
|
|
|
|
```typescript
|
|
// LRU 캐시로 자주 사용되는 설정만 메모리 보관
|
|
const configCache = new LRUCache({ max: 100, ttl: 300000 }); // 5분 TTL
|
|
```
|
|
|
|
### 3. **데이터베이스 성능 영향**
|
|
|
|
**문제점:**
|
|
|
|
- 버튼 클릭마다 복잡한 SQL 쿼리 실행
|
|
- EventTriggerService의 현재 구조상 전체 관계도 스캔
|
|
|
|
```typescript
|
|
// eventTriggerService.ts - 모든 관계도를 검색하는 비효율적인 쿼리
|
|
const diagrams = await prisma.$queryRaw`
|
|
SELECT * FROM dataflow_diagrams
|
|
WHERE company_code = ${companyCode}
|
|
AND (category::text = '"data-save"' OR ...)
|
|
`;
|
|
```
|
|
|
|
**해결방안:**
|
|
|
|
1. **인덱스 최적화**
|
|
|
|
```sql
|
|
-- 복합 인덱스 추가
|
|
CREATE INDEX idx_dataflow_button_lookup ON dataflow_diagrams
|
|
USING GIN ((control->'buttonId'))
|
|
WHERE category @> '["button-trigger"]';
|
|
```
|
|
|
|
2. **캐싱 계층 추가**
|
|
|
|
```typescript
|
|
// 버튼별 제어관리 매핑을 캐시
|
|
const buttonDataflowCache = new Map<string, DataflowConfig[]>();
|
|
```
|
|
|
|
## 🔧 확장성 관련 문제점
|
|
|
|
### 4. **설정 복잡도 증가**
|
|
|
|
**문제점:**
|
|
|
|
- 기존 단순한 버튼 설정에서 복잡한 제어관리 설정 추가
|
|
- 사용자 혼란 가능성
|
|
- UI가 너무 복잡해질 위험
|
|
|
|
**현재 UI 구조 문제:**
|
|
|
|
```typescript
|
|
// ButtonConfigPanel.tsx가 이미 복잡함
|
|
return (
|
|
<div className="space-y-4">
|
|
{/* 기존 15개+ 설정 항목 */}
|
|
{/* + 제어관리 설정 추가 시 더욱 복잡해짐 */}
|
|
</div>
|
|
);
|
|
```
|
|
|
|
**해결방안:**
|
|
|
|
1. **탭 구조로 분리**
|
|
|
|
```typescript
|
|
<Tabs defaultValue="basic">
|
|
<TabsList>
|
|
<TabsTrigger value="basic">기본 설정</TabsTrigger>
|
|
<TabsTrigger value="dataflow">제어관리</TabsTrigger>
|
|
<TabsTrigger value="advanced">고급 설정</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="basic">{/* 기존 설정 */}</TabsContent>
|
|
<TabsContent value="dataflow">{/* 제어관리 설정 */}</TabsContent>
|
|
</Tabs>
|
|
```
|
|
|
|
2. **단계별 설정 마법사**
|
|
|
|
```typescript
|
|
const DataflowConfigWizard = () => {
|
|
const [step, setStep] = useState(1);
|
|
// 1단계: 활성화 여부
|
|
// 2단계: 실행 타이밍
|
|
// 3단계: 제어 모드
|
|
// 4단계: 상세 설정
|
|
};
|
|
```
|
|
|
|
### 5. **타입 안전성 문제**
|
|
|
|
**문제점:**
|
|
|
|
- 기존 ButtonTypeConfig에 새로운 필드 추가로 인한 호환성 문제
|
|
- 런타임 오류 가능성
|
|
|
|
**현재 타입 구조 문제:**
|
|
|
|
```typescript
|
|
// 기존 코드들이 ButtonTypeConfig의 새 필드를 모름
|
|
const config = component.webTypeConfig; // enableDataflowControl 없을 수 있음
|
|
if (config.enableDataflowControl) { // undefined 체크 필요
|
|
```
|
|
|
|
**해결방안:**
|
|
|
|
1. **점진적 타입 확장**
|
|
|
|
```typescript
|
|
// 기존 타입은 유지하고 새로운 타입 정의
|
|
interface ExtendedButtonTypeConfig extends ButtonTypeConfig {
|
|
enableDataflowControl?: boolean;
|
|
dataflowConfig?: ButtonDataflowConfig;
|
|
dataflowTiming?: "before" | "after" | "replace";
|
|
}
|
|
|
|
// 타입 가드 함수
|
|
function hasDataflowConfig(
|
|
config: ButtonTypeConfig
|
|
): config is ExtendedButtonTypeConfig {
|
|
return "enableDataflowControl" in config;
|
|
}
|
|
```
|
|
|
|
2. **마이그레이션 함수**
|
|
|
|
```typescript
|
|
const migrateButtonConfig = (
|
|
config: ButtonTypeConfig
|
|
): ExtendedButtonTypeConfig => {
|
|
return {
|
|
...config,
|
|
enableDataflowControl: false, // 기본값
|
|
dataflowConfig: undefined,
|
|
dataflowTiming: "after",
|
|
};
|
|
};
|
|
```
|
|
|
|
### 6. **버전 호환성 문제**
|
|
|
|
**문제점:**
|
|
|
|
- 기존 저장된 버튼 설정과 새로운 구조 간 호환성
|
|
- 점진적 배포 시 일부 기능 불일치
|
|
|
|
**해결방안:**
|
|
|
|
1. **버전 필드 추가**
|
|
|
|
```typescript
|
|
interface ButtonTypeConfig {
|
|
version?: "1.0" | "2.0"; // 제어관리 추가 버전
|
|
// ...기존 필드들
|
|
}
|
|
```
|
|
|
|
2. **자동 마이그레이션**
|
|
|
|
```typescript
|
|
const migrateButtonConfig = (config: any) => {
|
|
if (!config.version || config.version === "1.0") {
|
|
return {
|
|
...config,
|
|
version: "2.0",
|
|
enableDataflowControl: false,
|
|
dataflowConfig: undefined,
|
|
};
|
|
}
|
|
return config;
|
|
};
|
|
```
|
|
|
|
## 🚫 보안 관련 문제점
|
|
|
|
### 7. **권한 검증 부재**
|
|
|
|
**문제점:**
|
|
|
|
- 제어관리 실행 시 추가적인 권한 검증 없음
|
|
- 사용자가 설정한 제어관리를 통해 의도치 않은 데이터 조작 가능
|
|
|
|
**해결방안:**
|
|
|
|
1. **제어관리 권한 체계**
|
|
|
|
```typescript
|
|
interface DataflowPermission {
|
|
canExecuteDataflow: boolean;
|
|
allowedTables: string[];
|
|
allowedActions: ("insert" | "update" | "delete")[];
|
|
}
|
|
|
|
const checkDataflowPermission = async (
|
|
userId: string,
|
|
dataflowConfig: ButtonDataflowConfig
|
|
): Promise<boolean> => {
|
|
// 사용자별 제어관리 권한 검증
|
|
};
|
|
```
|
|
|
|
2. **실행 로그 및 감사**
|
|
|
|
```typescript
|
|
const logDataflowExecution = async (
|
|
userId: string,
|
|
buttonId: string,
|
|
dataflowResult: ExecutionResult
|
|
) => {
|
|
await prisma.dataflow_audit_log.create({
|
|
data: {
|
|
user_id: userId,
|
|
button_id: buttonId,
|
|
executed_actions: dataflowResult.executedActions,
|
|
execution_time: dataflowResult.executionTime,
|
|
timestamp: new Date(),
|
|
},
|
|
});
|
|
};
|
|
```
|
|
|
|
### 8. **SQL 인젝션 위험**
|
|
|
|
**문제점:**
|
|
|
|
- 고급 모드에서 사용자가 직접 조건 설정 시 SQL 인젝션 가능성
|
|
- 동적 테이블명, 필드명 처리 시 보안 취약점
|
|
|
|
**해결방안:**
|
|
|
|
1. **화이트리스트 기반 검증**
|
|
|
|
```typescript
|
|
const ALLOWED_TABLES = ["user_info", "order_master" /* ... */];
|
|
const ALLOWED_OPERATORS = ["=", "!=", ">", "<", ">=", "<=", "LIKE"];
|
|
|
|
const validateDataflowConfig = (config: ButtonDataflowConfig) => {
|
|
if (config.directControl) {
|
|
if (!ALLOWED_TABLES.includes(config.directControl.sourceTable)) {
|
|
throw new Error("허용되지 않은 테이블입니다.");
|
|
}
|
|
// 추가 검증...
|
|
}
|
|
};
|
|
```
|
|
|
|
2. **파라미터화된 쿼리 강제**
|
|
|
|
```typescript
|
|
// 모든 동적 쿼리를 파라미터화
|
|
const executeCondition = async (condition: DataflowCondition, data: any) => {
|
|
const query = `SELECT * FROM ${tableName} WHERE ${fieldName} ${operator} $1`;
|
|
return await prisma.$queryRaw(query, condition.value);
|
|
};
|
|
```
|
|
|
|
## 💡 권장 해결 전략
|
|
|
|
### Phase 1: 안전한 시작 (MVP)
|
|
|
|
1. **간편 모드만 구현** (기존 관계도 선택)
|
|
2. **"after" 타이밍만 지원** (기존 액션 후 실행)
|
|
3. **기본적인 성능 최적화** (캐싱, 인덱스)
|
|
4. **상세한 로깅 및 모니터링** 추가
|
|
|
|
### Phase 2: 점진적 확장
|
|
|
|
1. **고급 모드 추가** (권한 검증 강화)
|
|
2. **"before", "replace" 타이밍 지원**
|
|
3. **성능 최적화 고도화** (비동기 실행, 큐잉)
|
|
4. **UI 개선** (탭, 마법사)
|
|
|
|
### Phase 3: 고도화
|
|
|
|
1. **배치 처리 지원**
|
|
2. **복잡한 비즈니스 로직 지원**
|
|
3. **AI 기반 설정 추천**
|
|
4. **성능 대시보드**
|
|
|
|
### 모니터링 지표
|
|
|
|
```typescript
|
|
interface DataflowMetrics {
|
|
averageExecutionTime: number;
|
|
errorRate: number;
|
|
memoryUsage: number;
|
|
cacheHitRate: number;
|
|
userSatisfactionScore: number;
|
|
}
|
|
```
|
|
|
|
이러한 문제점들을 사전에 고려하여 설계하면 안정적이고 확장 가능한 시스템을 구축할 수 있습니다.
|