# 배치 스케줄러 + 노드 플로우 연동 계획서

## 1. 배경 및 목적

### 현재 상태

현재 시스템에는 두 개의 독립적인 실행 엔진이 있다:

| 시스템 | 역할 | 트리거 방식 |
|--------|------|-------------|
| **배치 스케줄러** | Cron 기반 자동 실행 (데이터 복사만 가능) | 시간 기반 (node-cron) |
| **노드 플로우 엔진** | 조건/변환/INSERT/UPDATE/DELETE 등 복합 로직 | 버튼 클릭 (수동) |

### 문제

- 배치는 **INSERT/UPSERT만** 가능하고, 조건 기반 UPDATE/DELETE를 못 함
- 노드 플로우는 강력하지만 **수동 실행만** 가능 (버튼 클릭 필수)
- "퇴사일이 지나면 자동으로 퇴사 처리" 같은 **시간 기반 비즈니스 로직**을 구현할 수 없음

### 목표

배치 스케줄러가 노드 플로우를 자동 실행할 수 있도록 연동하여, 
시간 기반 비즈니스 로직 자동화를 지원한다.

```
[배치 스케줄러] ──Cron 트리거──> [노드 플로우 실행 엔진]
     │                              │
     │                              ├── 테이블 소스 조회
     │                              ├── 조건 분기
     │                              ├── UPDATE / DELETE / INSERT
     │                              ├── 이메일 발송
     │                              └── 로깅
     │
     └── 실행 로그 기록 (batch_execution_logs)
```

---

## 2. 사용 시나리오

### 시나리오 A: 자동 퇴사 처리

```
매일 자정 실행:
  1. user_info에서 퇴사일 <= NOW() AND 상태 != '퇴사' 인 사람 조회
  2. 해당 사용자의 상태를 '퇴사'로 UPDATE
  3. 관리자에게 이메일 알림 발송
```

### 시나리오 B: 월말 재고 마감

```
매월 1일 00:00 실행:
  1. 전월 재고 데이터를 재고마감 테이블로 INSERT
  2. 이월 수량 계산 후 UPDATE
```

### 시나리오 C: 미납 알림

```
매일 09:00 실행:
  1. 납기일이 지난 미납 주문 조회
  2. 담당자에게 이메일 발송
  3. 알림 로그 INSERT
```

### 시나리오 D: 외부 API 연동 자동화

```
매시간 실행:
  1. 외부 REST API에서 데이터 조회
  2. 조건 필터링 (변경된 데이터만)
  3. 내부 테이블에 UPSERT
```

---

## 3. 구현 범위

### 3.1 DB 변경 (batch_configs 테이블 확장)

```sql
-- batch_configs 테이블에 컬럼 추가
ALTER TABLE batch_configs 
  ADD COLUMN execution_type VARCHAR(20) DEFAULT 'mapping',
  ADD COLUMN node_flow_id INTEGER DEFAULT NULL,
  ADD COLUMN node_flow_context JSONB DEFAULT NULL;

-- execution_type: 'mapping' (기존 데이터 복사) | 'node_flow' (노드 플로우 실행)
-- node_flow_id: node_flows 테이블의 flow_id (FK)
-- node_flow_context: 플로우 실행 시 전달할 컨텍스트 데이터 (선택)

COMMENT ON COLUMN batch_configs.execution_type IS '실행 타입: mapping(기존 데이터 복사), node_flow(노드 플로우 실행)';
COMMENT ON COLUMN batch_configs.node_flow_id IS '연결된 노드 플로우 ID (execution_type이 node_flow일 때 사용)';
COMMENT ON COLUMN batch_configs.node_flow_context IS '플로우 실행 시 전달할 컨텍스트 데이터 (JSON)';
```

기존 데이터에 영향 없음 (`DEFAULT 'mapping'`으로 하위 호환성 보장)

### 3.2 백엔드 변경

#### BatchSchedulerService 수정 (핵심)

`executeBatchConfig()` 메서드에서 `execution_type` 분기:

```
executeBatchConfig(config)
  ├── config.execution_type === 'mapping'
  │     └── 기존 executeBatchMappings() (변경 없음)
  │
  └── config.execution_type === 'node_flow'
        └── NodeFlowExecutionService.executeFlow()
              ├── 노드 플로우 조회
              ├── 위상 정렬
              ├── 레벨별 실행
              └── 결과 반환
```

수정 파일:
- `backend-node/src/services/batchSchedulerService.ts`
  - `executeBatchConfig()` 에 node_flow 분기 추가
  - 노드 플로우 실행 결과를 배치 로그 형식으로 변환

#### 배치 설정 API 수정

수정 파일:
- `backend-node/src/types/batchTypes.ts`
  - `BatchConfig` 인터페이스에 `execution_type`, `node_flow_id`, `node_flow_context` 추가
  - `CreateBatchConfigRequest`, `UpdateBatchConfigRequest` 에도 추가
- `backend-node/src/services/batchService.ts`
  - `createBatchConfig()` - 새 필드 INSERT
  - `updateBatchConfig()` - 새 필드 UPDATE
- `backend-node/src/controllers/batchManagementController.ts`
  - 생성/수정 시 새 필드 처리

#### 노드 플로우 목록 API (배치용)

추가 파일/수정:
- `backend-node/src/routes/batchManagementRoutes.ts`
  - `GET /api/batch-management/node-flows` 추가 (배치 설정 UI에서 플로우 선택용)

### 3.3 프론트엔드 변경

#### 배치 생성/편집 UI 수정

수정 파일:
- `frontend/app/(main)/admin/automaticMng/batchmngList/create/page.tsx`
- `frontend/app/(main)/admin/automaticMng/batchmngList/edit/[id]/page.tsx`

변경 내용:
- "실행 타입" 선택 추가 (기존 매핑 / 노드 플로우)
- 노드 플로우 선택 시: 플로우 드롭다운 표시 (기존 매핑 설정 숨김)
- 노드 플로우 선택 시: 컨텍스트 데이터 입력 (선택사항, JSON)

```
┌─────────────────────────────────────────┐
│ 배치 설정                                │
├─────────────────────────────────────────┤
│ 배치명: [자동 퇴사 처리           ]      │
│ 설명:   [퇴사일 경과 사용자 자동 처리]    │
│ Cron:   [0 0 * * *              ]      │
│                                         │
│ 실행 타입: ○ 데이터 매핑  ● 노드 플로우   │
│                                         │
│ ┌─ 노드 플로우 선택 ─────────────────┐  │
│ │ [▾ 자동 퇴사 처리 플로우      ]    │  │
│ │                                    │  │
│ │ 플로우 설명: user_info에서 퇴사일..│  │
│ │ 노드 수: 4개                       │  │
│ └────────────────────────────────────┘  │
│                                         │
│            [취소]  [저장]                │
└─────────────────────────────────────────┘
```

#### 배치 목록 UI - Ops 대시보드 리디자인

현재 배치 목록은 단순 테이블인데, Vercel/Railway 스타일의 **운영 대시보드**로 전면 리디자인한다.
노드 플로우 연동과 함께 적용하면 새로운 실행 타입도 자연스럽게 표현 가능.

디자인 컨셉: **"편집기"가 아닌 "운영 대시보드"**
- 데이터 타입 관리 = 컬럼 편집기 → 3패널(리스트/그리드/설정)이 적합
- 배치 관리 = 운영 모니터링 → 테이블 + 인라인 상태 표시가 적합
- 역할이 다르면 레이아웃도 달라야 함

---

##### 전체 레이아웃

```
┌──────────────────────────────────────────────────────────────┐
│ [헤더]  배치 관리                          [새로고침] [새 배치] │
│  └ 데이터 동기화 배치 작업을 모니터링하고 관리합니다            │
├──────────────────────────────────────────────────────────────┤
│ [통계 카드 4열 그리드]                                        │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐         │
│ │ 전체 배치 │ │ 활성 배치 │ │ 오늘 실행 │ │ 오늘 실패 │         │
│ │    8     │ │    6     │ │   142    │ │    3     │         │
│ │ +2 이번달│ │ 2 비활성 │ │+12% 전일 │ │+1 전일   │         │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘         │
├──────────────────────────────────────────────────────────────┤
│ [툴바]                                                       │
│ 🔍 검색...  [전체|활성|비활성]  [전체|DB-DB|API-DB|플로우]  총 8건 │
├──────────────────────────────────────────────────────────────┤
│ [테이블 헤더]                                                 │
│   ●  배치          타입      스케줄     최근24h   마지막실행  │
├──────────────────────────────────────────────────────────────┤
│ ● 품목 마스터 동기화  DB→DB  */30****  ▌▌▌▐▌▌▌  14:30  ▶✎🗑 │
│ ┌────────────────────────────────────────────────────────┐   │
│ │ [확장 상세 패널 - 클릭 시 토글]                          │   │
│ │  내러티브 + 파이프라인 + 매핑 + 설정 + 타임라인          │   │
│ └────────────────────────────────────────────────────────┘   │
│ ● 거래처 ERP 연동    API→DB  0*/2***  ▌▌▌▌▌▌▌  14:00  ▶✎🗑 │
│ ◉ 재고 현황 수집     API→DB  06,18**  ▌▌▐▌▌▌░  실행중  ▶✎🗑 │
│ ○ BOM 백업          DB→DB   0 3**0   ░░░░░░░  비활성  ▶✎🗑 │
│ ...                                                          │
└──────────────────────────────────────────────────────────────┘
```

---

##### 1. 페이지 헤더

```
구조: flex, align-items: flex-end, justify-content: space-between
하단 보더: 1px solid border
하단 마진: 24px

좌측:
  - 제목: "배치 관리" (text-xl font-extrabold tracking-tight)
  - 부제: "데이터 동기화 배치 작업을 모니터링하고 관리합니다" (text-xs text-muted-foreground)

우측 버튼 그룹 (gap-2):
  - [새로고침] 버튼: variant="outline", RefreshCw 아이콘
  - [새 배치] 버튼: variant="default" (primary), Plus 아이콘
```

---

##### 2. 통계 카드 영역

```
레이아웃: grid grid-cols-4 gap-3
각 카드: rounded-xl border bg-card p-4

카드 구조:
  ┌──────────────────────────┐
  │ [라벨]            [아이콘] │  ← stat-top: flex justify-between
  │                          │
  │ 숫자값 (28px 모노 볼드)    │  ← stat-val: font-mono text-3xl font-extrabold
  │                          │
  │ [변화량 배지] 기간 텍스트   │  ← stat-footer: flex items-center gap-1.5
  └──────────────────────────┘

4개 카드 상세:
┌─────────────┬────────────┬───────────────────────────────┐
│ 카드         │ 아이콘 색상 │ 값 색상                        │
├─────────────┼────────────┼───────────────────────────────┤
│ 전체 배치    │ indigo bg  │ foreground (기본)              │
│ 활성 배치    │ green bg   │ green (--success)             │
│ 오늘 실행    │ cyan bg    │ cyan (--info 계열)             │
│ 오늘 실패    │ red bg     │ red (--destructive)           │
└─────────────┴────────────┴───────────────────────────────┘

변화량 배지:
  - 증가: green 배경 + green 텍스트, "+N" 또는 "+N%"
  - 감소/악화: red 배경 + red 텍스트
  - 크기: text-[10px] font-bold px-1.5 py-0.5 rounded

아이콘 박스: 28x28px rounded-lg, 배경색 투명도 10%
아이콘: lucide-react (LayoutGrid, CheckCircle, Activity, XCircle)
```

**데이터 소스:**
```
GET /api/batch-management/stats
→ {
    totalBatches: number,      // batch_configs COUNT(*)
    activeBatches: number,     // batch_configs WHERE is_active='Y'
    todayExecutions: number,   // batch_execution_logs WHERE DATE(start_time)=TODAY
    todayFailures: number,     // batch_execution_logs WHERE DATE(start_time)=TODAY AND status='FAILED'
    // 선택사항: 전일 대비 변화량
    prevDayExecutions?: number,
    prevDayFailures?: number
  }
```

---

##### 3. 툴바

```
레이아웃: flex items-center gap-2.5

요소 1 - 검색:
  - 위치: 좌측, flex-1 max-w-[320px]
  - 구조: relative div + input + Search 아이콘(absolute left)
  - input: h-9, rounded-lg, border, bg-card, text-xs
  - placeholder: "배치 이름으로 검색..."
  - focus: ring-2 ring-primary

요소 2 - 상태 필터 (pill-group):
  - 컨테이너: flex gap-0.5, bg-card, border, rounded-lg, p-0.5
  - 각 pill: text-[11px] font-semibold px-3 py-1.5 rounded-md
  - 활성 pill: bg-primary/10 text-primary
  - 비활성 pill: text-muted-foreground, hover시 밝아짐
  - 항목: [전체] [활성] [비활성]

요소 3 - 타입 필터 (pill-group):
  - 동일 스타일
  - 항목: [전체] [DB-DB] [API-DB] [노드 플로우]  ← 노드 플로우는 신규

요소 4 - 건수 표시:
  - 위치: ml-auto (우측 정렬)
  - 텍스트: "총 N건" (text-[11px] text-muted-foreground, N은 font-bold)
```

---

##### 4. 배치 테이블

```
컨테이너: border rounded-xl overflow-hidden bg-card

테이블 헤더:
  - 배경: bg-muted/50
  - 높이: 40px
  - 글자: text-[10px] font-bold text-muted-foreground uppercase tracking-wider
  - 그리드 컬럼: 44px 1fr 100px 130px 160px 100px 120px
  - 컬럼: [LED] [배치] [타입] [스케줄] [최근 24h] [마지막 실행] [액션]
```

---

##### 5. 배치 테이블 행 (핵심)

```
그리드: 44px 1fr 100px 130px 160px 100px 120px
높이: min-height 60px
하단 보더: 1px solid border
hover: bg-card/80 (약간 밝아짐)
선택됨: bg-primary/10 + 좌측 3px primary 박스 섀도우 (inset)
클릭 시: 상세 패널 토글

[셀 1] LED 상태 표시:
  ┌──────────────────────────────────────┐
  │ 원형 8x8px, 센터 정렬                │
  │                                      │
  │ 활성(on):   green + box-shadow glow  │
  │ 실행중(run): amber + 1.5s blink 애니  │
  │ 비활성(off): muted-foreground (회색)  │
  │ 에러(err):  red + box-shadow glow    │
  └──────────────────────────────────────┘

[셀 2] 배치 정보:
  ┌──────────────────────────────────────┐
  │ 배치명: text-[13px] font-bold        │
  │ 설명:   text-[10px] text-muted-fg    │
  │         overflow ellipsis (1줄)       │
  │                                      │
  │ 비활성 배치: 배치명도 muted 색상       │
  └──────────────────────────────────────┘

[셀 3] 타입 배지:
  ┌──────────────────────────────────────┐
  │ inline-flex, text-[10px] font-bold   │
  │ px-2 py-0.5 rounded-[5px]           │
  │                                      │
  │ DB → DB:    cyan 배경/텍스트          │
  │ API → DB:   violet 배경/텍스트        │
  │ 노드 플로우: indigo 배경/텍스트 (신규) │
  └──────────────────────────────────────┘

[셀 4] Cron 스케줄:
  ┌──────────────────────────────────────┐
  │ Cron 표현식: font-mono text-[11px]   │
  │              font-medium             │
  │ 한글 설명:   text-[9px] text-muted   │
  │              "매 30분", "매일 01:00"  │
  │                                      │
  │ 비활성: muted 색상                    │
  └──────────────────────────────────────┘
  
  Cron → 한글 변환 예시:
  - */30 * * * *  → "매 30분"
  - 0 */2 * * *   → "매 2시간"
  - 0 6,18 * * *  → "06:00, 18:00"
  - 0 1 * * *     → "매일 01:00"
  - 0 3 * * 0     → "매주 일 03:00"
  - 0 0 1 * *     → "매월 1일 00:00"

[셀 5] 스파크라인 (최근 24h):
  ┌──────────────────────────────────────┐
  │ flex, items-end, gap-[1px], h-6      │
  │                                      │
  │ 24개 바 (시간당 1개):                 │
  │  - 성공(ok):   green, opacity 60%    │
  │  - 실패(fail): red, opacity 80%      │
  │  - 미실행(none): muted, opacity 15%  │
  │                                      │
  │ 각 바: flex-1, min-w-[3px]           │
  │        rounded-t-[1px]               │
  │        높이: 실행시간 비례 또는 고정   │
  │ hover: opacity 100%                  │
  └──────────────────────────────────────┘
  
  데이터: 최근 24시간을 1시간 단위로 슬라이싱
  각 슬롯별 가장 최근 실행의 status 사용
  높이: 성공=80~95%, 실패=20~40%, 미실행=5%

[셀 6] 마지막 실행:
  ┌──────────────────────────────────────┐
  │ 시간: font-mono text-[10px]          │
  │       "14:30:00"                     │
  │ 경과: text-[9px] muted              │
  │       "12분 전"                      │
  │                                      │
  │ 실행 중: amber 색상 "실행 중..."      │
  │ 비활성:  muted "-" + "비활성"         │
  └──────────────────────────────────────┘

[셀 7] 액션 버튼:
  ┌──────────────────────────────────────┐
  │ flex gap-1, justify-end              │
  │                                      │
  │ 3개 아이콘 버튼 (28x28 rounded-md):  │
  │                                      │
  │ [▶] 수동 실행                        │
  │     hover: green 테두리+배경+아이콘   │
  │     아이콘: Play (lucide)            │
  │                                      │
  │ [✎] 편집                             │
  │     hover: 기본 밝아짐               │
  │     아이콘: Pencil (lucide)          │
  │                                      │
  │ [🗑] 삭제                            │
  │     hover: red 테두리+배경+아이콘     │
  │     아이콘: Trash2 (lucide)          │
  └──────────────────────────────────────┘
```

---

##### 6. 행 확장 상세 패널 (클릭 시 토글)

행을 클릭하면 아래로 펼쳐지는 상세 패널. 매핑 타입과 노드 플로우 타입에 따라 내용이 달라진다.

```
컨테이너:
  - border (상단 border 없음, 행과 이어짐)
  - rounded-b-xl
  - bg-muted/30 (행보다 약간 어두운 배경)
  - padding: 20px 24px

내부 구조:
  ┌────────────────────────────────────────────────────────────┐
  │ [내러티브 박스]                                             │
  │ "ERP_SOURCE DB의 item_master 테이블에서 현재 DB의           │
  │  item_info 테이블로 12개 컬럼을 매 30분마다 동기화하고       │
  │  있어요. 오늘 48회 실행, 마지막 실행은 12분 전이에요."        │
  ├────────────────────────────────────────────────────────────┤
  │ [파이프라인 플로우 다이어그램]                                │
  │                                                            │
  │  ┌─────────────┐     12 컬럼 UPSERT     ┌─────────────┐   │
  │  │ 🗄 DB아이콘  │  ─────────────────→   │ 🗄 DB아이콘  │   │
  │  │ ERP_SOURCE  │    WHERE USE_YN='Y'   │  현재 DB     │   │
  │  │ item_master │                        │ item_info    │   │
  │  └─────────────┘                        └─────────────┘   │
  ├──────────────────────┬─────────────────────────────────────┤
  │ [좌측: 매핑 + 설정]   │ [우측: 실행 이력 타임라인]           │
  │                      │                                     │
  │ --- 컬럼 매핑 (12) --- │ --- 실행 이력 (최근 5건) ---        │
  │ ITEM_CD → item_code PK│ ● 14:30:00 [성공] 1,842건 3.2s    │
  │ ITEM_NM → item_name   │ │                                  │
  │ ITEM_SPEC → spec...   │ ● 14:00:00 [성공] 1,840건 3.1s    │
  │ UNIT_CD → unit_code   │ │                                  │
  │ STD_PRICE → std_price │ ✕ 13:30:00 [실패] Timeout         │
  │ + 7개 더 보기          │ │                                  │
  │                      │ ● 13:00:00 [성공] 1,838건 2.9s     │
  │ --- 설정 ---          │ │                                  │
  │ 배치 크기: 500        │ ● 12:30:00 [성공] 1,835건 3.5s     │
  │ 타임아웃:  30s        │                                     │
  │ 실패 시:   3회 재시도  │                                     │
  │ 매칭 키:  item_code   │                                     │
  │ 모드:    [UPSERT]     │                                     │
  └──────────────────────┴─────────────────────────────────────┘
```

**6-1. 내러티브 박스 (Toss 스타일 자연어 설명)**

```
스타일:
  - rounded-lg
  - 배경: linear-gradient(135deg, primary/6%, info/4%)
  - 보더: 1px solid primary/8%
  - padding: 12px 14px
  - margin-bottom: 16px

텍스트: text-[11px] text-muted-foreground leading-relaxed
강조 텍스트:
  - 굵은 텍스트(b): foreground font-semibold
  - 하이라이트(hl): primary font-bold

매핑 타입 예시:
  "ERP_SOURCE 데이터베이스의 item_master 테이블에서 현재 DB의
   item_info 테이블로 12개 컬럼을 매 30분마다 동기화하고 있어요.
   오늘 48회 실행, 마지막 실행은 12분 전이에요."

노드 플로우 타입 예시:
  "자동 퇴사 처리 노드 플로우를 매일 00:00에 실행하고 있어요.
   user_info 테이블에서 퇴사일이 지난 사용자를 조회하여
   상태를 '퇴사'로 변경합니다. 4개 노드로 구성되어 있어요."
```

**6-2. 파이프라인 플로우 다이어그램**

```
컨테이너:
  - flex items-center
  - rounded-lg border bg-card p-4
  - margin-bottom: 16px

구조: [소스 노드] ──[커넥터]──> [타겟 노드]

소스 노드 (pipe-node src):
  - 배경: cyan/6%, 보더: cyan/12%
  - 아이콘: 32x32 rounded-lg, cyan/12% 배경
  - DB 타입: Database 아이콘 (lucide)
  - API 타입: Cloud 아이콘 (lucide) + violet 색상
  - 이름: text-xs font-bold cyan 색상
  - 부제: font-mono text-[10px] muted (테이블명/URL)

커넥터 (pipe-connector):
  - flex-1, flex-col items-center
  - 상단 라벨: text-[9px] font-bold muted ("12 컬럼 UPSERT")
  - 라인: width 100%, h-[2px], gradient(cyan → green)
  - 라인 끝: 삼각형 화살표 (CSS ::after)
  - 하단 라벨: text-[9px] font-bold muted ("WHERE USE_YN='Y'")

타겟 노드 (pipe-node tgt):
  - 배경: green/6%, 보더: green/12%
  - 아이콘: green/12% 배경
  - 이름: text-xs font-bold green 색상
  - 부제: 테이블명

노드 플로우 타입일 때:
  - 소스/타겟 대신 노드 플로우 요약 카드로 대체
  - 아이콘: Workflow 아이콘 (lucide) + indigo 색상
  - 이름: 플로우명
  - 부제: "N개 노드 | 조건 분기 포함"
  - 노드 목록: 간략 리스트 (Source → Condition → Update → Email)
```

**6-3. 하단 2열 그리드**

```
레이아웃: grid grid-cols-2 gap-5

[좌측 컬럼] 매핑 + 설정:

  섹션 1 - 컬럼 매핑:
    헤더: flex items-center gap-1.5
      - Link 아이콘 (lucide, 13px, muted)
      - "컬럼 매핑" (text-[11px] font-bold muted)
      - 건수 배지 (ml-auto, text-[9px] font-bold, primary/10% bg, primary 색)

    매핑 행 (map-row):
      - flex items-center gap-1.5
      - rounded-md border bg-card px-2.5 py-1.5
      - margin-bottom: 2px

      구조: [소스 컬럼] → [타겟 컬럼] [태그]
        소스: font-mono font-semibold text-[11px] cyan
        화살표: "→" muted
        타겟: font-mono font-semibold text-[11px] green
        태그: text-[8px] font-bold px-1.5 py-0.5 rounded-sm
              PK = green 배경 + dark 텍스트

    5개까지 표시 후 "+ N개 더 보기" 접기/펼치기

  노드 플로우 타입일 때:
    매핑 대신 "노드 구성" 섹션으로 대체
    각 행: [노드 아이콘] [노드 타입] [노드 설명]
    예: 🔍 테이블 소스 | user_info 조회
        🔀 조건 분기   | 퇴사일 <= NOW()
        ✏️ UPDATE      | status → '퇴사'
        📧 이메일      | 관리자 알림

  섹션 2 - 설정 (cprop 리스트):
    헤더: Settings 아이콘 + "설정"
    
    각 행 (cprop):
      - flex justify-between py-1.5
      - 하단 보더: 1px solid white/3%
      - 키: text-[11px] muted
      - 값: text-[11px] font-semibold, mono체는 font-mono text-[10px]
      - 특수 값: UPSERT 배지 (green/10% bg, green 색, text-[10px] font-bold)

    매핑 타입 설정:
      - 배치 크기: 500
      - 타임아웃: 30s
      - 실패 시 재시도: 3회 (green)
      - 매칭 키: item_code (mono)
      - 모드: [UPSERT] (배지)

    노드 플로우 타입 설정:
      - 플로우 ID: 42
      - 노드 수: 4개
      - 실행 타임아웃: 60s
      - 컨텍스트: { ... } (mono, 접기 가능)


[우측 컬럼] 실행 이력 타임라인:

  헤더: Clock 아이콘 + "실행 이력" + "최근 5건" 배지 (green)

  타임라인 (timeline):
    flex-col, gap-0

    각 항목 (tl-item):
      - flex items-start gap-3
      - padding: 10px 0
      - 하단 보더: 1px solid white/3%

      좌측 - 점+선 (tl-dot-wrap):
        - flex-col items-center, width 16px
        - 점 (tl-dot): 8x8 rounded-full
          성공(ok): green
          실패(fail): red
          실행중(run): amber + blink 애니메이션
        - 선 (tl-line): width 1px, bg border, min-h 12px
          마지막 항목에는 선 없음

      우측 - 내용 (tl-body):
        - 시간: font-mono text-[10px] font-semibold
        - 상태 배지: text-[9px] font-bold px-1.5 py-0.5 rounded
          성공: green/10% bg + green 색
          실패: red/10% bg + red 색
        - 메시지: text-[10px] muted, margin-top 2px
          성공: "1,842건 처리 / 3.2s 소요"
          실패: "Connection timeout: ERP_SOURCE 응답 없음"
```

---

##### 7. 반응형 대응

```
1024px 이하 (태블릿):
  - 통계 카드: grid-cols-2
  - 테이블 그리드: 36px 1fr 80px 110px 120px 80px (액션 숨김)
  - 상세 패널 2열 그리드 → 1열

640px 이하 (모바일):
  - 컨테이너 padding: 16px
  - 통계 카드: grid-cols-2 gap-2
  - 테이블 헤더: 숨김
  - 테이블 행: grid-cols-1, 카드형태 (padding 16px, gap 8px)
```

---

##### 8. 필요한 백엔드 API

```
1. GET /api/batch-management/stats
   → {
       totalBatches: number,
       activeBatches: number,
       todayExecutions: number,
       todayFailures: number,
       prevDayExecutions?: number,
       prevDayFailures?: number
     }
   쿼리: batch_configs COUNT + batch_execution_logs 오늘/어제 집계

2. GET /api/batch-management/batch-configs/:id/sparkline
   → [{ hour: 0~23, status: 'success'|'failed'|'none', count: number }]
   쿼리: batch_execution_logs WHERE batch_config_id=$1
         AND start_time >= NOW() - INTERVAL '24 hours'
         GROUP BY EXTRACT(HOUR FROM start_time)

3. GET /api/batch-management/batch-configs/:id/recent-logs?limit=5
   → [{ start_time, end_time, execution_status, total_records,
         success_records, failed_records, error_message, duration_ms }]
   쿼리: batch_execution_logs WHERE batch_config_id=$1
         ORDER BY start_time DESC LIMIT $2

4. GET /api/batch-management/batch-configs (기존 수정)
   → 각 배치에 sparkline 요약 + last_execution 포함하여 반환
     목록 페이지에서 개별 sparkline API를 N번 호출하지 않도록
     한번에 가져오기 (LEFT JOIN + 서브쿼리)
```

---

## 4. 변경 파일 목록

### DB

| 파일 | 변경 | 설명 |
|------|------|------|
| `db/migrations/XXXX_batch_node_flow_integration.sql` | 신규 | ALTER TABLE batch_configs |

### 백엔드

| 파일 | 변경 | 설명 |
|------|------|------|
| `backend-node/src/services/batchSchedulerService.ts` | 수정 | executeBatchConfig에 node_flow 분기 |
| `backend-node/src/types/batchTypes.ts` | 수정 | BatchConfig 타입에 새 필드 추가 |
| `backend-node/src/services/batchService.ts` | 수정 | create/update에 새 필드 처리 |
| `backend-node/src/controllers/batchManagementController.ts` | 수정 | 새 필드 API + stats/sparkline/recent-logs API |
| `backend-node/src/routes/batchManagementRoutes.ts` | 수정 | node-flows/stats/sparkline 엔드포인트 추가 |

### 프론트엔드

| 파일 | 변경 | 설명 |
|------|------|------|
| `frontend/app/(main)/admin/automaticMng/batchmngList/page.tsx` | **리디자인** | Ops 대시보드 스타일로 전면 재작성 |
| `frontend/app/(main)/admin/automaticMng/batchmngList/create/page.tsx` | 수정 | 실행 타입 선택 + 플로우 선택 |
| `frontend/app/(main)/admin/automaticMng/batchmngList/edit/[id]/page.tsx` | 수정 | 실행 타입 선택 + 플로우 선택 |

---

## 5. 핵심 구현 상세

### 5.1 BatchSchedulerService 변경 (가장 중요)

```typescript
// batchSchedulerService.ts - executeBatchConfig 메서드 수정

static async executeBatchConfig(config: any) {
  const startTime = new Date();
  let executionLog: any = null;

  try {
    // ... 실행 로그 생성 (기존 코드 유지) ...

    let result;

    // 실행 타입에 따라 분기
    if (config.execution_type === 'node_flow' && config.node_flow_id) {
      // 노드 플로우 실행
      result = await this.executeNodeFlow(config);
    } else {
      // 기존 매핑 실행 (하위 호환)
      result = await this.executeBatchMappings(config);
    }

    // ... 실행 로그 업데이트 (기존 코드 유지) ...
    return result;
  } catch (error) {
    // ... 에러 처리 (기존 코드 유지) ...
  }
}

/**
 * 노드 플로우 실행 (신규)
 */
private static async executeNodeFlow(config: any) {
  const { NodeFlowExecutionService } = await import('./nodeFlowExecutionService');

  const context = {
    sourceData: [],
    dataSourceType: 'none',
    nodeResults: new Map(),
    executionOrder: [],
    buttonContext: {
      buttonId: `batch_${config.id}`,
      companyCode: config.company_code,
      userId: config.created_by || 'batch_system',
      formData: config.node_flow_context || {},
    },
  };

  const flowResult = await NodeFlowExecutionService.executeFlow(
    config.node_flow_id,
    context
  );

  // 노드 플로우 결과를 배치 로그 형식으로 변환
  return {
    totalRecords: flowResult.totalNodes || 0,
    successRecords: flowResult.successNodes || 0,
    failedRecords: flowResult.failedNodes || 0,
  };
}
```

### 5.2 실행 결과 매핑

노드 플로우 결과 → 배치 로그 변환:

| 노드 플로우 결과 | 배치 로그 필드 | 설명 |
|------------------|---------------|------|
| 전체 노드 수 | total_records | 실행 대상 노드 수 |
| 성공 노드 수 | success_records | 성공적으로 실행된 노드 |
| 실패 노드 수 | failed_records | 실패한 노드 |
| 에러 메시지 | error_message | 첫 번째 실패 노드의 에러 |

### 5.3 보안 고려사항

- 배치에서 실행되는 노드 플로우도 **company_code** 필터링 적용
- 배치 설정의 company_code와 노드 플로우의 company_code가 일치해야 함
- 최고 관리자(`*`)는 모든 플로우 실행 가능
- 실행 로그에 `batch_system`으로 사용자 기록

---

## 6. 구현 순서

### Phase 1: DB + 백엔드 코어 (1일)

1. 마이그레이션 SQL 작성 및 실행
2. `batchTypes.ts` 타입 수정
3. `batchService.ts` create/update 수정
4. `batchSchedulerService.ts` 핵심 분기 로직 추가
5. `batchManagementRoutes.ts` 노드 플로우 목록 API 추가
6. 수동 실행 테스트 (`POST /batch-configs/:id/execute`)

### Phase 2: 백엔드 대시보드 API (0.5일)

1. `GET /api/batch-management/stats` - 전체/활성/오늘실행/오늘실패 집계 API
2. `GET /api/batch-management/batch-configs/:id/sparkline` - 최근 24h 실행 결과 (시간대별 성공/실패/미실행)
3. `GET /api/batch-management/batch-configs/:id/recent-logs?limit=5` - 최근 N건 실행 이력
4. 기존 목록 API에 sparkline 요약 데이터 포함 옵션 추가

### Phase 3: 프론트엔드 - 배치 목록 Ops 대시보드 (1.5일)

상세 UI 명세는 위 "3.3 배치 목록 UI - Ops 대시보드 리디자인" 섹션 참조.

1. **페이지 헤더**: 제목 + 부제 + 새로고침/새배치 버튼 (명세 항목 1)
2. **통계 카드 영역**: 4개 카드 + stats API 연동 (명세 항목 2)
3. **툴바**: 검색 + 상태/타입 필터 pill-group + 건수 표시 (명세 항목 3)
4. **배치 테이블**: 7열 그리드 헤더 + 행 (명세 항목 4~5)
5. **행 확장 상세 패널**: 내러티브 + 파이프라인 + 매핑/플로우 + 설정 + 타임라인 (명세 항목 6)
6. **반응형**: 1024px/640px 브레이크포인트 (명세 항목 7)
7. 배치 생성/편집 모달에 실행 타입 선택 + 노드 플로우 드롭다운

### Phase 4: 테스트 및 검증 (0.5일)

1. 테스트용 노드 플로우 생성 (간단한 UPDATE)
2. 배치 설정에 연결
3. 수동 실행 테스트
4. Cron 스케줄 자동 실행 테스트
5. 실행 로그 확인
6. 대시보드 통계/스파크라인 정확성 확인

---

## 7. 리스크 및 대응

### 7.1 노드 플로우 실행 시간 초과

- **리스크**: 복잡한 플로우가 오래 걸려서 다음 스케줄과 겹칠 수 있음
- **대응**: 실행 중인 배치는 중복 실행 방지 (락 메커니즘) - Phase 2 이후 고려

### 7.2 노드 플로우 삭제 시 배치 깨짐

- **리스크**: 연결된 노드 플로우가 삭제되면 배치 실행 실패
- **대응**: 
  - 플로우 존재 여부 체크 후 실행
  - 실패 시 로그에 "플로우를 찾을 수 없습니다" 기록
  - (향후) 플로우 삭제 시 연결된 배치가 있으면 경고

### 7.3 멀티 인스턴스 환경

- **리스크**: 서버가 여러 대일 때 같은 배치가 중복 실행
- **대응**: 현재 단일 인스턴스 운영이므로 당장은 문제 없음. 향후 Redis 기반 분산 락 고려

---

## 8. 기대 효과

1. **시간 기반 비즈니스 자동화**: 수동 작업 없이 조건 충족 시 자동 처리
2. **기존 인프라 재활용**: 검증된 배치 스케줄러(1,200+건 성공) + 강력한 노드 플로우 엔진
3. **최소 코드 변경**: DB 컬럼 3개 + 백엔드 분기 1개 + 프론트 UI 확장
4. **운영 가시성 극대화**: Ops 대시보드로 배치 상태/건강도를 한눈에 파악 (스파크라인, LED, 타임라인)
5. **확장성**: 향후 이벤트 트리거(데이터 변경 감지) 등으로 확장 가능

---

## 9. 설계 의도 - 왜 기존 화면과 다른 레이아웃인가

| 비교 항목 | 데이터 타입 관리 (편집기) | 배치 관리 (대시보드) |
|-----------|------------------------|-------------------|
| 역할 | 컬럼 메타데이터 편집 | 운영 상태 모니터링 |
| 레이아웃 | 3패널 (리스트/그리드/설정) | 테이블 + 인라인 모니터링 |
| 주요 행위 | 필드 추가/삭제/수정 | 상태 확인, 수동 실행, 이력 조회 |
| 시각적 요소 | 폼, 드래그앤드롭 | LED, 스파크라인, 타임라인 |
| 참고 UI | IDE, Figma 속성 패널 | Vercel Functions, Railway |

### 디자인 키포인트 6가지

1. **스파크라인 = 건강 상태 한눈에**: Vercel의 Function 목록처럼 각 배치 행에 최근 24h 실행 결과를 미니 바 차트로 표현. 숫자 읽을 필요 없이 패턴으로 건강 상태 파악.

2. **Expandable Row 패턴**: 3패널 대신 클릭하면 행이 확장되어 상세 정보 표시. 파이프라인 플로우 + 매핑 + 타임라인이 한 번에. Railway/GitHub Actions의 Job 상세 패턴.

3. **LED 상태 표시**: 카드의 Badge(활성/비활성) 대신 LED 점으로 상태 표현. 초록=활성, 주황깜빡임=실행중, 회색=비활성. 운영실 모니터 느낌.

4. **파이프라인 플로우 다이어그램**: 소스 → 화살표 → 타겟을 수평 파이프라인으로 시각화. DB-DB는 DB 아이콘 둘, API-DB는 클라우드+DB. 데이터 흐름이 직관적.

5. **내러티브 박스**: 설정값을 나열하는 대신 자연어로 요약. "A에서 B로 N개 컬럼을 매 30분마다 동기화하고 있어요" 식. Toss 스타일 UX Writing.

6. **타임라인 실행 이력**: 테이블 로그 대신 세로 타임라인(점+선). 성공/실패가 시각적으로 즉시 구분. 문제 발생 시점 빠르게 특정 가능.

### 디자인 원본

HTML 프리뷰 파일: `_local/batch-management-v3-preview.html` (브라우저에서 열어 시각적 확인 가능)
