501 lines
14 KiB
Markdown
501 lines
14 KiB
Markdown
# 제어관리 데이터 소스 확장 가이드
|
|
|
|
## 개요
|
|
|
|
제어관리(플로우) 실행 시 사용할 수 있는 데이터 소스가 확장되었습니다. 이제 **폼 데이터**, **테이블 선택 항목**, **테이블 전체 데이터**, **플로우 선택 항목**, **플로우 스텝 전체 데이터** 등 다양한 소스에서 데이터를 가져와 제어를 실행할 수 있습니다.
|
|
|
|
## 지원 데이터 소스
|
|
|
|
### 1. `form` - 폼 데이터
|
|
- **설명**: 현재 화면의 폼 입력값을 사용합니다.
|
|
- **사용 시나리오**: 단일 레코드 생성/수정 시
|
|
- **데이터 형태**: 단일 객체
|
|
|
|
```typescript
|
|
{
|
|
name: "홍길동",
|
|
age: 30,
|
|
email: "test@example.com"
|
|
}
|
|
```
|
|
|
|
### 2. `table-selection` - 테이블 선택 항목
|
|
- **설명**: 테이블에서 사용자가 선택한 행의 데이터를 사용합니다.
|
|
- **사용 시나리오**: 선택된 항목에 대한 일괄 처리
|
|
- **데이터 형태**: 배열 (선택된 행들)
|
|
|
|
```typescript
|
|
[
|
|
{ id: 1, name: "항목1", status: "대기" },
|
|
{ id: 2, name: "항목2", status: "대기" }
|
|
]
|
|
```
|
|
|
|
### 3. `table-all` - 테이블 전체 데이터 🆕
|
|
- **설명**: 테이블의 **모든 데이터**를 사용합니다 (페이징 무관).
|
|
- **사용 시나리오**:
|
|
- 전체 데이터에 대한 일괄 처리
|
|
- 통계/집계 작업
|
|
- 대량 데이터 마이그레이션
|
|
- **데이터 형태**: 배열 (전체 행)
|
|
- **주의사항**: 데이터가 많을 경우 성능 이슈가 있을 수 있습니다.
|
|
|
|
```typescript
|
|
[
|
|
{ id: 1, name: "항목1", status: "대기" },
|
|
{ id: 2, name: "항목2", status: "진행중" },
|
|
{ id: 3, name: "항목3", status: "완료" },
|
|
// ... 수천 개의 행
|
|
]
|
|
```
|
|
|
|
### 4. `flow-selection` - 플로우 선택 항목
|
|
- **설명**: 플로우 위젯에서 사용자가 선택한 데이터를 사용합니다.
|
|
- **사용 시나리오**: 플로우 단계별로 선택된 항목 처리
|
|
- **데이터 형태**: 배열 (선택된 행들)
|
|
|
|
```typescript
|
|
[
|
|
{ id: 10, taskName: "작업1", stepId: 2 },
|
|
{ id: 11, taskName: "작업2", stepId: 2 }
|
|
]
|
|
```
|
|
|
|
### 5. `flow-step-all` - 플로우 스텝 전체 데이터 🆕
|
|
- **설명**: 현재 선택된 플로우 단계의 **모든 데이터**를 사용합니다.
|
|
- **사용 시나리오**:
|
|
- 특정 단계의 모든 항목 일괄 처리
|
|
- 단계별 완료율 계산
|
|
- 단계 이동 시 전체 데이터 마이그레이션
|
|
- **데이터 형태**: 배열 (해당 스텝의 전체 행)
|
|
|
|
```typescript
|
|
[
|
|
{ id: 10, taskName: "작업1", stepId: 2, assignee: "홍길동" },
|
|
{ id: 11, taskName: "작업2", stepId: 2, assignee: "김철수" },
|
|
{ id: 12, taskName: "작업3", stepId: 2, assignee: "이영희" },
|
|
// ... 해당 스텝의 모든 데이터
|
|
]
|
|
```
|
|
|
|
### 6. `both` - 폼 + 테이블 선택
|
|
- **설명**: 폼 데이터와 테이블 선택 항목을 결합하여 사용합니다.
|
|
- **사용 시나리오**: 폼의 공통 정보 + 개별 항목 처리
|
|
- **데이터 형태**: 배열 (폼 데이터 + 선택된 행들)
|
|
|
|
```typescript
|
|
[
|
|
{ name: "홍길동", age: 30 }, // 폼 데이터
|
|
{ id: 1, name: "항목1", status: "대기" },
|
|
{ id: 2, name: "항목2", status: "대기" }
|
|
]
|
|
```
|
|
|
|
### 7. `all-sources` - 모든 소스 결합 🆕
|
|
- **설명**: 폼, 테이블 전체, 플로우 등 **모든 소스의 데이터를 결합**하여 사용합니다.
|
|
- **사용 시나리오**:
|
|
- 복잡한 데이터 통합 작업
|
|
- 다중 소스 동기화
|
|
- 전체 시스템 상태 업데이트
|
|
- **데이터 형태**: 배열 (모든 소스의 데이터 병합)
|
|
- **주의사항**: 매우 많은 데이터가 전달될 수 있으므로 신중히 사용하세요.
|
|
|
|
```typescript
|
|
[
|
|
{ name: "홍길동", age: 30 }, // 폼 데이터
|
|
{ id: 1, name: "테이블1" }, // 테이블 선택
|
|
{ id: 2, name: "테이블2" }, // 테이블 선택
|
|
{ id: 3, name: "테이블3" }, // 테이블 전체
|
|
{ id: 10, taskName: "작업1" }, // 플로우 선택
|
|
// ... 모든 소스의 데이터
|
|
]
|
|
```
|
|
|
|
## 설정 방법
|
|
|
|
### 1. 버튼 상세 설정에서 데이터 소스 선택
|
|
|
|
1. 화면 디자이너에서 버튼 선택
|
|
2. 우측 패널 > **상세 설정** 탭
|
|
3. **제어관리 활성화** 체크
|
|
4. **제어 데이터 소스** 드롭다운에서 원하는 소스 선택
|
|
|
|
### 2. 데이터 소스 옵션
|
|
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ 제어 데이터 소스 │
|
|
├─────────────────────────────────────┤
|
|
│ 📄 폼 데이터 │
|
|
│ 📊 테이블 선택 항목 │
|
|
│ 📊 테이블 전체 데이터 🆕 │
|
|
│ 🔄 플로우 선택 항목 │
|
|
│ 🔄 플로우 스텝 전체 데이터 🆕 │
|
|
│ 📋 폼 + 테이블 선택 │
|
|
│ 🌐 모든 소스 결합 🆕 │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
## 실제 사용 예시
|
|
|
|
### 예시 1: 테이블 전체 데이터로 일괄 상태 업데이트
|
|
|
|
```typescript
|
|
// 제어 설정
|
|
{
|
|
controlDataSource: "table-all",
|
|
flowConfig: {
|
|
flowId: 10,
|
|
flowName: "전체 항목 승인 처리",
|
|
executionTiming: "replace"
|
|
}
|
|
}
|
|
|
|
// 실행 시 전달되는 데이터
|
|
{
|
|
buttonId: "btn_approve_all",
|
|
sourceData: [
|
|
{ id: 1, name: "항목1", status: "대기" },
|
|
{ id: 2, name: "항목2", status: "대기" },
|
|
{ id: 3, name: "항목3", status: "대기" },
|
|
// ... 테이블의 모든 행 (1000개)
|
|
]
|
|
}
|
|
```
|
|
|
|
### 예시 2: 플로우 스텝 전체를 다음 단계로 이동
|
|
|
|
```typescript
|
|
// 제어 설정
|
|
{
|
|
controlDataSource: "flow-step-all",
|
|
flowConfig: {
|
|
flowId: 15,
|
|
flowName: "단계 일괄 이동",
|
|
executionTiming: "replace"
|
|
}
|
|
}
|
|
|
|
// 실행 시 전달되는 데이터
|
|
{
|
|
buttonId: "btn_move_all",
|
|
flowStepId: 2,
|
|
sourceData: [
|
|
{ id: 10, taskName: "작업1", stepId: 2 },
|
|
{ id: 11, taskName: "작업2", stepId: 2 },
|
|
{ id: 12, taskName: "작업3", stepId: 2 },
|
|
// ... 해당 스텝의 모든 데이터
|
|
]
|
|
}
|
|
```
|
|
|
|
### 예시 3: 선택된 항목만 처리
|
|
|
|
```typescript
|
|
// 제어 설정
|
|
{
|
|
controlDataSource: "table-selection",
|
|
flowConfig: {
|
|
flowId: 5,
|
|
flowName: "선택 항목 승인",
|
|
executionTiming: "replace"
|
|
}
|
|
}
|
|
|
|
// 실행 시 전달되는 데이터 (사용자가 2개 선택한 경우)
|
|
{
|
|
buttonId: "btn_approve_selected",
|
|
sourceData: [
|
|
{ id: 1, name: "항목1", status: "대기" },
|
|
{ id: 5, name: "항목5", status: "대기" }
|
|
]
|
|
}
|
|
```
|
|
|
|
## 데이터 로딩 방식
|
|
|
|
### 자동 로딩 vs 수동 로딩
|
|
|
|
1. **테이블 선택 항목** (`table-selection`)
|
|
- ✅ 자동 로딩: 사용자가 이미 선택한 데이터 사용
|
|
- 별도 로딩 불필요
|
|
|
|
2. **테이블 전체 데이터** (`table-all`)
|
|
- ⚡ 지연 로딩: 버튼 클릭 시 필요한 경우만 로드
|
|
- 부모 컴포넌트에서 `onRequestTableAllData` 콜백 제공 필요
|
|
|
|
3. **플로우 스텝 전체 데이터** (`flow-step-all`)
|
|
- ⚡ 지연 로딩: 버튼 클릭 시 필요한 경우만 로드
|
|
- 부모 컴포넌트에서 `onRequestFlowStepAllData` 콜백 제공 필요
|
|
|
|
### 부모 컴포넌트 구현 예시
|
|
|
|
```tsx
|
|
<OptimizedButtonComponent
|
|
component={buttonComponent}
|
|
selectedRowsData={selectedRowsData}
|
|
|
|
// 테이블 전체 데이터 로드 콜백
|
|
onRequestTableAllData={async () => {
|
|
const response = await fetch(`/api/data/table/${tableId}?all=true`);
|
|
const data = await response.json();
|
|
return data.records;
|
|
}}
|
|
|
|
// 플로우 스텝 전체 데이터 로드 콜백
|
|
onRequestFlowStepAllData={async (stepId) => {
|
|
const response = await fetch(`/api/flow/step/${stepId}/all-data`);
|
|
const data = await response.json();
|
|
return data.records;
|
|
}}
|
|
/>
|
|
```
|
|
|
|
## 성능 고려사항
|
|
|
|
### 1. 대량 데이터 처리
|
|
|
|
- **테이블 전체 데이터**: 수천 개의 행이 있을 경우 메모리 및 네트워크 부담
|
|
- **해결 방법**:
|
|
- 배치 처리 사용
|
|
- 페이징 처리
|
|
- 서버 사이드 처리
|
|
|
|
### 2. 로딩 시간
|
|
|
|
```typescript
|
|
// ❌ 나쁜 예: 모든 데이터를 항상 미리 로드
|
|
useEffect(() => {
|
|
loadTableAllData(); // 버튼을 누르지 않아도 로드됨
|
|
}, []);
|
|
|
|
// ✅ 좋은 예: 필요할 때만 로드 (지연 로딩)
|
|
const onRequestTableAllData = async () => {
|
|
return await loadTableAllData(); // 버튼 클릭 시에만 로드
|
|
};
|
|
```
|
|
|
|
### 3. 캐싱
|
|
|
|
```typescript
|
|
// 전체 데이터를 캐싱하여 재사용
|
|
const [cachedTableAllData, setCachedTableAllData] = useState<any[]>([]);
|
|
|
|
const onRequestTableAllData = async () => {
|
|
if (cachedTableAllData.length > 0) {
|
|
console.log("캐시된 데이터 사용");
|
|
return cachedTableAllData;
|
|
}
|
|
|
|
const data = await loadTableAllData();
|
|
setCachedTableAllData(data);
|
|
return data;
|
|
};
|
|
```
|
|
|
|
## 노드 플로우에서 데이터 사용
|
|
|
|
### contextData 구조
|
|
|
|
노드 플로우 실행 시 전달되는 `contextData`는 다음과 같은 구조를 가집니다:
|
|
|
|
```typescript
|
|
{
|
|
buttonId: "btn_approve",
|
|
screenId: 123,
|
|
companyCode: "DEFAULT",
|
|
userId: "user001",
|
|
controlDataSource: "table-all",
|
|
|
|
// 공통 데이터
|
|
formData: { name: "홍길동" },
|
|
|
|
// 소스별 데이터
|
|
selectedRowsData: [...], // table-selection
|
|
tableAllData: [...], // table-all
|
|
flowSelectedData: [...], // flow-selection
|
|
flowStepAllData: [...], // flow-step-all
|
|
flowStepId: 2, // 현재 플로우 스텝 ID
|
|
|
|
// 통합 데이터 (모든 노드에서 사용 가능)
|
|
sourceData: [...] // controlDataSource에 따라 결정된 데이터
|
|
}
|
|
```
|
|
|
|
### 노드에서 데이터 접근
|
|
|
|
```typescript
|
|
// External Call 노드
|
|
{
|
|
nodeType: "external-call",
|
|
config: {
|
|
url: "https://api.example.com/bulk-approve",
|
|
method: "POST",
|
|
body: {
|
|
// sourceData를 사용하여 데이터 전달
|
|
items: "{{sourceData}}",
|
|
approver: "{{formData.approver}}"
|
|
}
|
|
}
|
|
}
|
|
|
|
// DDL 노드
|
|
{
|
|
nodeType: "ddl",
|
|
config: {
|
|
sql: `
|
|
UPDATE tasks
|
|
SET status = 'approved'
|
|
WHERE id IN ({{sourceData.map(d => d.id).join(',')}})
|
|
`
|
|
}
|
|
}
|
|
```
|
|
|
|
## 디버깅 및 로그
|
|
|
|
### 콘솔 로그 확인
|
|
|
|
버튼 클릭 시 다음과 같은 로그가 출력됩니다:
|
|
|
|
```
|
|
📊 데이터 소스 모드: {
|
|
controlDataSource: "table-all",
|
|
hasFormData: true,
|
|
hasTableSelection: false,
|
|
hasFlowSelection: false
|
|
}
|
|
|
|
📊 테이블 전체 데이터 로드 중...
|
|
✅ 테이블 전체 데이터 1,234건 로드 완료
|
|
|
|
🚀 노드 플로우 실행 시작: {
|
|
flowId: 10,
|
|
flowName: "전체 항목 승인",
|
|
timing: "replace",
|
|
sourceDataCount: 1234
|
|
}
|
|
```
|
|
|
|
### 에러 처리
|
|
|
|
```typescript
|
|
// 데이터 로드 실패 시
|
|
❌ 테이블 전체 데이터 로드 실패: Network error
|
|
🔔 Toast: "테이블 전체 데이터를 불러오지 못했습니다"
|
|
|
|
// 플로우 실행 실패 시
|
|
❌ 플로우 실행 실패: 조건 불만족
|
|
🔔 Toast: "테이블 전체 조건 불만족: status === 'pending' (실제값: approved)"
|
|
```
|
|
|
|
## 마이그레이션 가이드
|
|
|
|
### 기존 설정에서 업그레이드
|
|
|
|
기존에 `table-selection`을 사용하던 버튼을 `table-all`로 변경하는 경우:
|
|
|
|
1. **버튼 설정 변경**: `table-selection` → `table-all`
|
|
2. **부모 컴포넌트 업데이트**: `onRequestTableAllData` 콜백 추가
|
|
3. **노드 플로우 업데이트**: 대량 데이터 처리 로직 추가
|
|
4. **테스트**: 소량 데이터로 먼저 테스트 후 전체 적용
|
|
|
|
### 하위 호환성
|
|
|
|
- ✅ 기존 `form`, `table-selection`, `both` 설정은 그대로 동작
|
|
- ✅ 새로운 데이터 소스는 선택적으로 사용 가능
|
|
- ✅ 기존 노드 플로우는 수정 없이 동작
|
|
|
|
## 베스트 프랙티스
|
|
|
|
### 1. 적절한 데이터 소스 선택
|
|
|
|
| 시나리오 | 권장 데이터 소스 |
|
|
|---------|----------------|
|
|
| 단일 레코드 생성/수정 | `form` |
|
|
| 선택된 항목 일괄 처리 | `table-selection` |
|
|
| 전체 항목 일괄 처리 | `table-all` |
|
|
| 플로우 단계별 선택 처리 | `flow-selection` |
|
|
| 플로우 단계 전체 이동 | `flow-step-all` |
|
|
| 복잡한 통합 작업 | `all-sources` |
|
|
|
|
### 2. 성능 최적화
|
|
|
|
```typescript
|
|
// ✅ 좋은 예: 배치 처리
|
|
const batchSize = 100;
|
|
for (let i = 0; i < sourceData.length; i += batchSize) {
|
|
const batch = sourceData.slice(i, i + batchSize);
|
|
await processBatch(batch);
|
|
}
|
|
|
|
// ❌ 나쁜 예: 동기 처리
|
|
for (const item of sourceData) {
|
|
await processItem(item); // 1000개면 1000번 API 호출
|
|
}
|
|
```
|
|
|
|
### 3. 사용자 피드백
|
|
|
|
```typescript
|
|
// 대량 데이터 처리 시 진행률 표시
|
|
toast.info(`${processed}/${total} 항목 처리 중...`, {
|
|
id: "batch-progress"
|
|
});
|
|
```
|
|
|
|
## 문제 해결
|
|
|
|
### Q1: 테이블 전체 데이터가 로드되지 않습니다
|
|
|
|
**A**: 부모 컴포넌트에 `onRequestTableAllData` 콜백이 구현되어 있는지 확인하세요.
|
|
|
|
```tsx
|
|
// InteractiveScreenViewer.tsx 확인
|
|
<OptimizedButtonComponent
|
|
onRequestTableAllData={async () => {
|
|
// 이 함수가 구현되어 있어야 함
|
|
return await fetchAllData();
|
|
}}
|
|
/>
|
|
```
|
|
|
|
### Q2: 플로우 스텝 전체 데이터가 빈 배열입니다
|
|
|
|
**A**:
|
|
1. 플로우 스텝이 선택되어 있는지 확인
|
|
2. `flowSelectedStepId`가 올바르게 전달되는지 확인
|
|
3. `onRequestFlowStepAllData` 콜백이 구현되어 있는지 확인
|
|
|
|
### Q3: 데이터가 너무 많아 브라우저가 느려집니다
|
|
|
|
**A**:
|
|
1. 서버 사이드 처리 고려
|
|
2. 배치 처리 사용
|
|
3. 페이징 적용
|
|
4. `table-selection` 사용 권장 (전체 대신 선택)
|
|
|
|
## 관련 파일
|
|
|
|
### 타입 정의
|
|
- `frontend/types/control-management.ts` - `ControlDataSource` 타입
|
|
|
|
### 핵심 로직
|
|
- `frontend/lib/utils/nodeFlowButtonExecutor.ts` - 데이터 준비 및 전달
|
|
- `frontend/components/screen/OptimizedButtonComponent.tsx` - 버튼 컴포넌트
|
|
|
|
### UI 설정
|
|
- `frontend/components/screen/config-panels/ButtonDataflowConfigPanel.tsx` - 설정 패널
|
|
|
|
### 서비스
|
|
- `frontend/lib/services/optimizedButtonDataflowService.ts` - 데이터 검증 및 처리
|
|
|
|
## 업데이트 이력
|
|
|
|
- **2025-01-24**: 초기 문서 작성
|
|
- `table-all` 데이터 소스 추가
|
|
- `flow-step-all` 데이터 소스 추가
|
|
- `all-sources` 데이터 소스 추가
|
|
- 지연 로딩 메커니즘 구현
|
|
|