박정하 박정하 02-11
250211 박정하 스케줄용 모달 수정
@8b85376d15d48b0aa72e1c53c0c921aba706ceef
client/views/common/commonPlugin.js
--- client/views/common/commonPlugin.js
+++ client/views/common/commonPlugin.js
@@ -93,6 +93,31 @@
       });
     }
 
+    // 공통코드 호출 (부모 코드)
+    Vue.config.globalProperties.$getCommonCodeByTree = async function (codeId) {
+      const promise = new Promise((resolve, reject) => {
+        axios({
+          url: '/common/getCodeListByTree.json',
+          method: 'post',
+          headers: { 'Content-Type': 'application/json; charset=UTF-8' },
+          data: JSON.stringify({ 'codeId': codeId })
+        }).then(function (response) {
+          resolve(response.data.resultData.codeList)
+        }).catch(function (error) {
+          resolve('cancle')
+        });
+      });
+
+      return promise.then(
+        (data) => {
+          return data;
+        }
+      ).catch(function (err) {
+        console.log(err)
+        return [];
+      });
+    }
+
     // 데이터베이스 타입호출
     Vue.config.globalProperties.$getDataBaseTypeList = async function () {
       const promise = new Promise((resolve, reject) => {
client/views/component/PageNavigation.vue
--- client/views/component/PageNavigation.vue
+++ client/views/component/PageNavigation.vue
@@ -1,49 +1,44 @@
 <template>
-    <p class="navigate_bar flex20 text-rg"><router-link to="/">Home</router-link><span> > {{ pathName() }}</span></p>
+  <p class="navigate_bar flex20 text-rg">
+    <router-link to="/">Home</router-link><span> > {{ pathName() }}</span>
+  </p>
 </template>
 
 <script>
-import { useRoute } from 'vue-router';
+import { useRoute } from "vue-router";
 export default {
-    data() {
-        return {
-            //현재 라우터의 정보
-            route: useRoute(),
+  data() {
+    return {
+      //현재 라우터의 정보
+      route: useRoute(),
 
-            // 페이지 경로 목록
-            pageList: [
-                { path: "/", name: "대시보드", },
-                { path: "/fileManagement.page", name: "파일 관리", },
-                { path: "/hostManagement.page", name: "호스트 관리", },
-                { path: "/scheduleManagement.page", name: "작업 스케줄 관리", },
-                { path: "/scheduleLogManagement.page", name: "로그 관리", },
-            ],
+      // 페이지 경로 목록
+      pageList: [
+        { path: "/", name: "대시보드" },
+        { path: "/fileManagement.page", name: "파일 관리" },
+        { path: "/hostManagement.page", name: "호스트 관리" },
+        { path: "/scheduleManagement.page", name: "작업 스케줄 관리" },
+      ],
+    };
+  },
+  methods: {
+    pathName: function () {
+      for (let i = 0; i < this.pageList.length; i++) {
+        if (this.route.path == this.pageList[i]["path"]) {
+          return this.pageList[i]["name"];
         }
+      }
+      return "대시보드";
     },
-    methods: {
-        pathName: function () {
-            for (let i = 0; i < this.pageList.length; i++) {
-                if (this.route.path == this.pageList[i]['path']) {
-                    return this.pageList[i]['name'];
-                }
-            }
-            return "대시보드"
-        },
-
-    },
-    watch: {
-
-    },
-    computed: {
-
-    },
-    components: {
-    },
-}
+  },
+  watch: {},
+  computed: {},
+  components: {},
+};
 </script>
 
 <style scoped>
 .navigate_bar {
-    font-size: 1.3rem;
+  font-size: 1.3rem;
 }
 </style>
client/views/component/connection/EhojoConnection.vue
--- client/views/component/connection/EhojoConnection.vue
+++ client/views/component/connection/EhojoConnection.vue
@@ -119,7 +119,7 @@
         </div>
         <div v-if="modalView == 'preview'">
           <div class="content-titleZone flex justify-between align-center mt20">
-            <p class="box-title">데이터셋 <small>(총 ROW: {{ rowData.length > 1 ? rowData.length : 0 }}개)</small>
+            <p class="box-title">데이터셋 <small>(총 ROW: {{ rowData.length > 0 ? rowData.length : 0 }}개)</small>
             </p>
           </div>
           <div style="height: 400px; overflow: auto">
client/views/component/connection/itm/apiConnection.vue
--- client/views/component/connection/itm/apiConnection.vue
+++ client/views/component/connection/itm/apiConnection.vue
@@ -120,7 +120,7 @@
             </tbody>
           </table>
           <div class="content-titleZone flex justify-between align-center mt20">
-            <p class="box-title"> 데이터셋 <small> (총 ROW: {{ rowData.length > 1 ? rowData.length : 0 }}개) </small>
+            <p class="box-title"> 데이터셋 <small> (총 ROW: {{ rowData.length > 0 ? rowData.length : 0 }}개) </small>
             </p>
           </div>
           <table class="list-table table-flow">
@@ -162,13 +162,11 @@
 <script>
 import SvgIcon from "@jamescoyle/vue-icon";
 import { mdiClose } from "@mdi/js";
-import ApiRusultCus from "./apiRusultCus.vue";
 import axios from "axios";
 
 export default {
   components: {
     SvgIcon: SvgIcon,
-    ApiRusultCus: ApiRusultCus,
   },
   props: {
     openPopup: {
@@ -295,7 +293,7 @@
       let apiUrl = this.apiUrl;
       if (apiUrl) {
         let parameters = apiUrl.substring(apiUrl.indexOf("?") + 1);
-        if (parameters.length > 1) {
+        if (parameters.length > 0) {
           let params = parameters.split("&");
           for (let param of params) {
             let paramIndex = param.indexOf("=");
 
client/views/component/connection/itm/apiRusultCus.vue (deleted)
--- client/views/component/connection/itm/apiRusultCus.vue
@@ -1,19 +0,0 @@
-<template>
-  <div v-for="(item, index) in apiResponseResult" :key="index">
-    <p>
-      <span @click="$emit('fnJobItmDepth', item.key)">{{ item.key }}</span>
-      <span>{{ item.value }}</span>
-    </p>
-  </div>
-</template>
-
-<script>
-export default {
-  props: {
-    apiResponseResult: {
-      type: Object,
-      default: null,
-    },
-  },
-};
-</script>
 
client/views/component/connection/modalContents/ApiRead.vue (added)
+++ client/views/component/connection/modalContents/ApiRead.vue
@@ -0,0 +1,489 @@
+<template>
+  <div v-if="currentPage == 1">
+    <div class="content-titleZone">
+      <p class="box-title">API 기본정보</p>
+    </div>
+    <table class="form-table">
+      <colgroup>
+        <col style="width: 15%" />
+        <col style="width: 85%" />
+      </colgroup>
+      <tbody>
+        <tr>
+          <th>API URL</th>
+          <td>
+            <input
+              type="text"
+              class="full-input"
+              v-model="apiUrl"
+              @change="fnApiUrlChange"
+            />
+          </td>
+        </tr>
+        <tr>
+          <th>결과타입</th>
+          <td>
+            <div class="flex">
+              <div class="flex15 pl0">
+                <input
+                  type="radio"
+                  name="json"
+                  id="json"
+                  value="1"
+                  class="chekck-type"
+                  v-model="jobItm.itm.type"
+                />
+                <label for="json" class="chekcktype-label text-ct">
+                  json
+                </label>
+              </div>
+              <div class="flex15">
+                <input
+                  type="radio"
+                  name="xml"
+                  id="xml"
+                  value="2"
+                  class="chekck-type"
+                  v-model="jobItm.itm.type"
+                />
+                <label for="xml" class="chekcktype-label text-ct"> xml </label>
+              </div>
+              <div class="flex15 pr0">
+                <input
+                  type="radio"
+                  name="seol"
+                  id="seol"
+                  value="3"
+                  class="chekck-type"
+                  v-model="jobItm.itm.type"
+                />
+                <label for="seol" class="chekcktype-label text-ct">
+                  seol
+                </label>
+              </div>
+            </div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <div class="content-titleZone flex justify-between align-center mt20">
+      <p class="box-title">파라미터 목록</p>
+      <div>
+        <button class="blue-border-btn small-btn" @click="fnAddRow()">
+          파라미터 추가
+        </button>
+        <button
+          v-if="apiUrl"
+          class="blue-btn small-btn"
+          @click="fnAutoCreate()"
+        >
+          자동 생성
+        </button>
+      </div>
+    </div>
+    <div style="height: 400px; overflow: auto">
+      <table class="list-table" style="table-layout: fixed">
+        <colgroup>
+          <col width="10%" />
+          <col width="30%" />
+          <col width="50%" />
+          <col width="10%" />
+        </colgroup>
+        <thead>
+          <tr>
+            <th style="text-align: center">No</th>
+            <th style="text-align: center">Key</th>
+            <th style="text-align: center">Value</th>
+            <th style="text-align: center">삭제</th>
+          </tr>
+        </thead>
+        <tbody>
+          <template v-if="parameterList.length > 0">
+            <tr v-for="(item, index) in parameterList" :key="index">
+              <td style="text-align: center">{{ index + 1 }}</td>
+              <td>
+                <input type="text" class="full-input" v-model.trim="item.key" />
+              </td>
+              <td>
+                <input
+                  type="text"
+                  class="full-input"
+                  v-model.trim="item.value"
+                />
+              </td>
+              <td>
+                <button class="cbtnDelete" @click="fnDeleteRow(item)">
+                  <svg-icon
+                    type="mdi"
+                    :width="20"
+                    :height="20"
+                    :path="delPath"
+                  ></svg-icon>
+                </button>
+              </td>
+            </tr>
+          </template>
+          <tr v-else>
+            <td colspan="4">등록된 데이터가 없습니다.</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+  <div v-if="currentPage == 2">
+    <div class="content-titleZone flex justify-between align-center">
+      <p class="box-title">미리보기</p>
+      <button
+        type="button"
+        class="blue-border-btn small-btn"
+        @click="fnDataKeySelect"
+      >
+        조회
+      </button>
+    </div>
+    <table class="form-table">
+      <colgroup>
+        <col style="width: 15%" />
+        <col style="width: 85%" />
+      </colgroup>
+      <tbody>
+        <tr>
+          <th>사용할 데이터</th>
+          <td>
+            <select v-model="selectKey">
+              <template v-if="apiResponseKeys.length > 0">
+                <option value="">사용할 데이터를 선택하세요.</option>
+                <option
+                  v-for="(key, index) of apiResponseKeys"
+                  :key="index"
+                  :value="key"
+                >
+                  {{ key }}
+                </option>
+              </template>
+              <option v-else value="">전체 조회</option>
+            </select>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <div class="content-titleZone flex justify-between align-center mt20">
+      <p class="box-title">
+        데이터셋
+        <small>
+          (총 ROW: {{ rowData.length > 0 ? rowData.length : 0 }}개)
+        </small>
+      </p>
+    </div>
+    <table class="list-table table-flow">
+      <template v-if="!$isEmpty(dataTable)">
+        <thead>
+          <tr>
+            <th>No</th>
+            <th v-for="(rowKey, index) of rowKeys" :key="index">
+              {{ rowKey }}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="(items, indexI) of rowData" :key="indexI">
+            <td>{{ indexI + 1 }}</td>
+            <td v-for="(item, indexJ) of items" :key="indexJ">
+              {{ item }}
+            </td>
+          </tr>
+        </tbody>
+      </template>
+      <tr v-else>
+        <td>등록된 데이터가 없습니다.</td>
+      </tr>
+    </table>
+  </div>
+</template>
+<script>
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiTrashCanOutline } from "@mdi/js";
+
+import axios from "axios";
+
+export default {
+  name: "ApiRead",
+
+  components: {
+    SvgIcon,
+  },
+
+  props: {
+    jobItem: {
+      type: Object,
+    },
+    currentPage: {
+      type: Number,
+    },
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      delPath: mdiTrashCanOutline,
+
+      jobItm: this.jobItem,
+
+      apiUrl: null,
+      parameterList: [],
+
+      apiResponse: null,
+      apiResponseKeys: [],
+      selectKey: "",
+
+      dataTable: {},
+
+      rowData: [],
+      rowKeys: [],
+    };
+  },
+
+  created() {
+    this.init();
+  },
+
+  watch: {
+    jobItem(value) {
+      this.jobItm = value;
+      if (!this.$isEmpty(value.itm)) {
+        this.defaultValue(value.itm);
+      }
+    },
+
+    apiUrl(value) {
+      if (!this.$isEmpty(value)) {
+        this.$emit("onDfltSetChange", true);
+      } else {
+        this.$emit("onDfltSetChange", false);
+      }
+    },
+
+    parameterList: {
+      deep: true,
+      handler(value) {
+        if (value.length > 0) {
+          this.$emit("onDfltSetChange", true);
+        } else {
+          this.$emit("onDfltSetChange", false);
+        }
+      },
+    },
+
+    currentPage(value) {
+      if (value == 2) {
+        this.apiRequestTest();
+      }
+    },
+  },
+
+  methods: {
+    // 초기화 function
+    init() {
+      if (!this.$isEmpty(this.jobItm)) {
+        this.defaultValue(this.jobItm.itm);
+      } else {
+        this.jobItm = Object.assign({}, this.$getDefaultJobGroup().node);
+        this.jobItm.itm = Object.assign(
+          {},
+          this.$getDefaultJobGroup().connectionApi
+        );
+      }
+    },
+
+    // url 분리
+    defaultValue(item) {
+      if (item.url != null && item.url != "") {
+        if (item.url.indexOf("?") != -1) {
+          item.url.substring(0, item.url.indexOf("?"));
+        }
+        let params = "";
+        for (let param of item.params) {
+          if (params != "") {
+            params += "&";
+          }
+          params += param.key + "=" + param.value;
+        }
+        this.apiUrl = item.url + "?" + params;
+
+        for (let param of item.params) {
+          this.parameterList.push({
+            index: param.index,
+            key: param.key,
+            value: param.value,
+            addMonth: 0,
+          });
+        }
+      }
+    },
+
+    // API URL 변경
+    fnApiUrlChange() {
+      this.parameterList = []; // 파라미터 목록 초기화
+
+      // 미리보기 페이지 데이터 초기화
+      this.selectKey = "";
+      this.dataTable = {};
+      this.rowData = [];
+      this.rowKeys = [];
+    },
+
+    // 파라미터 행 추가
+    fnAddRow() {
+      this.parameterList.push({
+        index: this.parameterList.length + 1,
+        key: null,
+        value: null,
+        addMonth: 0,
+      });
+    },
+
+    // 파라미터 행 삭제
+    fnDeleteRow(item) {
+      this.parameterList = this.parameterList.filter(
+        (parameter) => parameter !== item
+      );
+    },
+
+    // 자동 생성 버튼
+    async fnAutoCreate() {
+      // 덮어쓰기 경고
+      if (this.parameterList.length > 0) {
+        let isChecked = await this.$showConfirm(
+          "경고",
+          "자동생성 실행 시, 기존에 작성한 파라미터가 삭제됩니다. 그래도 실행하시겠습니까?"
+        );
+        if (!isChecked) {
+          return;
+        } else {
+          this.parameterList = []; // 파라미터 목록 초기화
+        }
+      }
+
+      // 실행
+      let apiUrl = this.apiUrl;
+      if (apiUrl) {
+        let parameters = apiUrl.substring(apiUrl.indexOf("?") + 1);
+        if (parameters.length > 0) {
+          let params = parameters.split("&");
+          for (let param of params) {
+            let paramIndex = param.indexOf("=");
+            this.parameterList.push({
+              index: this.parameterList.length + 1,
+              key: param.substring(0, paramIndex),
+              value: param.substring(paramIndex + 1),
+              addMonth: 0,
+            });
+          }
+        }
+      }
+    },
+
+    // 셀 크기
+    fnColWidth(keys) {
+      return `width: ${100 / (keys + 1)}%`;
+    },
+
+    // 다음 버튼
+    apiRequestTest() {
+      const vm = this;
+      // 데이터 세팅
+      vm.jobItm.itm.url = vm.apiUrl.substring(0, vm.apiUrl.indexOf("?"));
+      vm.jobItm.itm.params = vm.parameterList;
+      // 실행
+      axios({
+        url: "/apiRequestTest.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: vm.jobItm.itm,
+      })
+        .then((response) => {
+          vm.apiResponseKeys = response.data.key;
+          vm.modalView = "preview"; // 모달창 변경
+        })
+        .catch((error) => {
+          vm.$showAlert(
+            "결과내용",
+            "응답이 없습니다. 파라미터를 확인해 주세요."
+          );
+        });
+    },
+
+    // 조회 버튼
+    fnDataKeySelect() {
+      let vm = this;
+      if (
+        vm.apiResponseKeys.length > 0 &&
+        (vm.selectKey == null || vm.selectKey == "")
+      ) {
+        vm.$showAlert("경고", "사용할 데이터를 선택해 주세요.");
+        return;
+      }
+      vm.jobItm.itm.depth = vm.selectKey;
+
+      // 데이터 세팅
+      vm.jobItm.itm.url = vm.apiUrl.substring(0, vm.apiUrl.indexOf("?"));
+
+      // 실행
+      axios({
+        url: "/getApiDataTable.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: vm.jobItm.itm,
+      })
+        .then((response) => {
+          let result = response.data;
+          vm.dataTable = result;
+          vm.rowKeys = []; // 초기화
+          for (let columnData of result.columnDatas) {
+            vm.rowKeys.push(columnData.columnNm);
+            // displayColumnNm 초기값 설정
+            columnData.displyColumnNm = columnData.orginlColumnNm;
+          }
+          vm.rowData = result.rowData;
+
+          vm.$emit("onDataSetChange", true);
+        })
+        .catch((error) => {
+          vm.$emit("onDataSetChange", false);
+          vm.$showAlert("결과내용", "응답이 없습니다. 데이터를 확인해 주세요.");
+        });
+    },
+
+    // 저장 버튼
+    fnSave() {
+      // 유효성 검사
+      if (this.$isEmpty(this.apiUrl)) {
+        this.$showAlert("경고", "API URL을 입력해 주세요.");
+        return false;
+      }
+      if (this.$isEmpty(this.dataTable)) {
+        this.$showAlert("경고", "사용할 데이터를 조회해 주세요.");
+        return false;
+      }
+
+      // 데이터 세팅
+      this.jobItm.dataTable = this.dataTable;
+
+      // 실행
+      this.$emit("fnSaveSetup", this.jobItm);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.table-flow {
+  width: 100%;
+  word-wrap: break-word;
+}
+</style>(No newline at end of file)
 
client/views/component/connection/modalContents/DataFilter.vue (added)
+++ client/views/component/connection/modalContents/DataFilter.vue
@@ -0,0 +1,226 @@
+<template>
+  <div class="content-titleZone">
+    <p class="box-title">필터 종류</p>
+  </div>
+  <table class="form-table">
+    <colgroup>
+      <col style="width: 15%" />
+      <col style="width: 85%" />
+    </colgroup>
+    <tbody>
+      <tr>
+        <th>필터 종류</th>
+        <td>
+          <div class="input-container flex">
+            <label class="radio-label">
+              <input
+                type="radio"
+                class="custom-radiobox"
+                name="matchType"
+                :value="true"
+                v-model="jobItm.itm.match_type"
+              />
+              <span>AND 연산</span>
+            </label>
+            <label class="radio-label">
+              <input
+                type="radio"
+                class="custom-radiobox"
+                name="matchType"
+                :value="false"
+                v-model="jobItm.itm.match_type"
+              />
+              <span>OR 연산</span>
+            </label>
+          </div>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+  <div class="content-titleZone flex justify-between align-center mt20">
+    <p class="box-title">필터 목록</p>
+    <div>
+      <button class="blue-border-btn small-btn" @click="addFiter">
+        필터 추가
+      </button>
+    </div>
+  </div>
+  <div style="height: 400px; overflow: auto">
+    <table class="list-table" style="table-layout: fixed">
+      <colgroup>
+        <col style="width: 30%" />
+        <col style="width: 30%" />
+        <col style="width: 30%" />
+        <col style="width: 10%" />
+      </colgroup>
+      <thead>
+        <tr>
+          <th>컬럼</th>
+          <th>연산</th>
+          <th>내용</th>
+          <th>관리</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="(filter, idx) in jobItm.itm.filterItems" :key="idx">
+          <td>
+            <select
+              class="full-select"
+              v-model="filter.coulmn_nm"
+              @change="onChange(filter)"
+            >
+              <option :value="null">필터를 적용할 컬럼을 선택하세요.</option>
+              <option
+                v-for="(column, index) of frontNode.dataTable.columnDatas"
+                :key="index"
+                :value="column.orginlColumnNm"
+              >
+                {{ column.displyColumnNm }}
+              </option>
+            </select>
+          </td>
+          <td>
+            <select class="full-select" v-model="filter.calc_ty">
+              <option :value="null">필터를 적용할 연산을 선택하세요.</option>
+              <option value="1">=</option>
+              <template v-if="filter.data_ty != 'STRING'">
+                <option value="2">&lt;</option>
+                <option value="3">&gt;</option>
+                <option value="4">&lt;=</option>
+                <option value="5">&gt;=</option>
+              </template>
+              <option value="6">!=</option>
+              <option value="7" v-if="filter.data_ty == 'STRING'">포함</option>
+            </select>
+          </td>
+          <td>
+            <input
+              type="text"
+              class="full-input"
+              v-if="filter.data_ty == 'STRING'"
+              v-model="filter.cmpr_value"
+              placeholder="필터를 적용할 내용을 입력하세요."
+            />
+            <input
+              type="date"
+              class="full-select"
+              v-else-if="filter.data_ty == 'DATE'"
+              v-model="filter.cmpr_value"
+            />
+            <input
+              type="datetime-local"
+              class="full-select"
+              v-else-if="filter.data_ty == 'DATETIME'"
+              v-model="filter.cmpr_value"
+            />
+            <input
+              type="number"
+              class="full-select"
+              v-else
+              v-model="filter.cmpr_value"
+            />
+          </td>
+          <td style="text-align: center">
+            <button type="button" @click="deleteFilter(filter)">
+              <svg-icon
+                type="mdi"
+                :width="20"
+                :height="20"
+                :path="delPath"
+              ></svg-icon>
+            </button>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</template>
+
+<script>
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiTrashCanOutline } from "@mdi/js";
+
+export default {
+  name: "DataFilter",
+
+  components: {
+    SvgIcon,
+  },
+
+  props: {
+    jobItem: Object,
+    currentPage: Number,
+    frontNode: Object,
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      delPath: mdiTrashCanOutline,
+
+      jobItm: this.jobItem,
+    };
+  },
+
+  created() {
+    if (this.$isEmpty(this.frontNode.dataTable.columnDatas)) {
+      this.$showAlert(
+        "경고",
+        "연결된 데이터 읽기 노드에 데이터가 비어있습니다."
+      );
+      return;
+    }
+  },
+
+  mounted() {},
+
+  computed: {},
+
+  watch: {
+    jobItem(value) {
+      this.jobItm = value;
+    },
+
+    "jobItm.itm.filterItems": {
+      deep: true,
+      handler(value) {
+        if (value.length > 0) {
+          this.$emit("onDataSetChange", true);
+        } else {
+          this.$emit("onDataSetChange", false);
+        }
+      },
+    },
+  },
+
+  methods: {
+    // 필터추가
+    addFiter() {
+      let filter = Object.assign({}, this.$getDefaultJobGroup().filterItem);
+      this.jobItm.itm.filterItems.push(filter);
+    },
+
+    // 데이터 삽입
+    onChange(item) {
+      outLoop: for (let column of this.frontNode.dataTable.columnDatas) {
+        if (column.orginlColumnNm == item.coulmn_nm) {
+          for (let filter of this.jobItm.itm.filterItems) {
+            if (filter.indx === item.indx) {
+              filter.data_ty = column.dataTy;
+              break outLoop;
+            }
+          }
+        }
+      }
+    },
+
+    // 필터 삭제
+    deleteFilter(item) {
+      this.jobItm.itm.filterItems = this.jobItm.itm.filterItems.filter(
+        (filter) => filter !== item
+      );
+    },
+  },
+};
+</script>(No newline at end of file)
 
client/views/component/connection/modalContents/DatasetUpdate.vue (added)
+++ client/views/component/connection/modalContents/DatasetUpdate.vue
@@ -0,0 +1,328 @@
+<template>
+  <div class="tab-contents mt10">
+    <div class="content-titleZone flex justify-between align-center">
+      <p class="box-title">데이터셋 설정</p>
+    </div>
+    <table class="form-table">
+      <colgroup>
+        <col style="width: 15%" />
+        <col style="width: 85%" />
+      </colgroup>
+      <tbody>
+        <tr>
+          <th>데이터셋 설정</th>
+          <td>
+            <button class="blue-border-btn small-btn" @click="fnOpenModal">
+              타겟 데이터 조회
+            </button>
+          </td>
+        </tr>
+        <tr>
+          <th>기존 데이터 삭제 여부</th>
+          <td>
+            <div class="input-container flex">
+              <label class="radio-label mr5">
+                <input
+                  type="radio"
+                  name="radio"
+                  :value="false"
+                  class="custom-radiobox"
+                  v-model="jobItm.itm_option_bool"
+                />
+                <span>기존 데이터 삭제</span>
+              </label>
+              <label class="radio-label">
+                <input
+                  type="radio"
+                  name="radio"
+                  :value="true"
+                  class="custom-radiobox"
+                  v-model="jobItm.itm_option_bool"
+                />
+                <span>기존 데이터 미삭제</span>
+              </label>
+            </div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <!-- 매핑 -->
+    <div class="flex mt20">
+      <div class="flex50 pl0">
+        <div class="content-titleZone flex justify-between align-center">
+          <p class="box-title">
+            원본 데이터 컴럼
+            <small>
+              ( 매칭 : {{ matchs.matchCnt }}, 비매칭 :
+              {{ matchs.unMatchCnt }}
+              )
+            </small>
+          </p>
+        </div>
+        <div class="columncontBox" style="height: 620px; overflow-y: auto">
+          <table>
+            <colgroup>
+              <col style="width: 50%" />
+            </colgroup>
+            <tbody>
+              <tr v-for="(column, index) of matchList" :key="index">
+                <td>
+                  <p class="col-data">
+                    {{ column.origin.displyColumnNm }}
+                  </p>
+                </td>
+                <td
+                  draggable="true"
+                  @dragstart="fnMoveStart(column)"
+                  @dragover="fnOnDragover"
+                  @drop="fnChangeMatch('drop', column)"
+                  @dblclick="fnChangeMatch('doubleClick', column)"
+                >
+                  <p class="col-data">
+                    <span v-if="!$isEmpty(column.target)">
+                      {{ column.target.displyColumnNm }}
+                    </span>
+                  </p>
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+      <div class="flex50 pr0">
+        <div class="content-titleZone flex justify-between align-center">
+          <p class="box-title">추가 데이터 컬럼</p>
+        </div>
+        <div class="columncontBox" style="height: 620px; overflow-y: auto">
+          <ul>
+            <li
+              v-for="(column, index) of frontColumns"
+              :key="index"
+              :class="{ 'col-data': true, disabled: column.isConnect }"
+              :draggable="!column.isConnect"
+              @dragstart="fnOnDragstart(column)"
+            >
+              {{ column.displyColumnNm }}
+            </li>
+          </ul>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- 데이터셋 선택 모달 -->
+  <DatasetListModal
+    v-if="isOpenModal"
+    @onSelect="fnSave"
+    @onClose="fnCloseModal"
+  />
+</template>
+
+<script>
+import axios from "axios";
+import datasetSelecter from "../itm/datasetSelecter.vue";
+import DatasetListModal from "../../modal/DatasetListModal.vue";
+
+export default {
+  name: "DatasetUpdate",
+
+  components: {
+    datasetSelecter,
+    DatasetListModal,
+  },
+
+  props: {
+    jobItem: Object,
+    currentPage: Number,
+    frontNode: Object,
+  },
+
+  data() {
+    return {
+      isOpenModal: false,
+
+      jobItm: this.jobItem,
+      frontColumns: [],
+
+      matchs: {
+        matchCnt: 0,
+        unMatchCnt: 0,
+      },
+      matchList: [],
+      currentColumn: {},
+    };
+  },
+
+  created() {
+    if (this.$isEmpty(this.frontNode.dataTable.columnDatas)) {
+      this.$showAlert(
+        "경고",
+        "연결된 데이터 읽기 노드에 데이터가 비어있습니다."
+      );
+      return;
+    }
+
+    // 추가 데이터 컬럼 생성
+    this.frontColumns = this.frontNode.dataTable.columnDatas;
+    for (let column of this.frontColumns) {
+      column.isConnect = false;
+    }
+  },
+
+  watch: {
+    jobItem(value) {
+      this.jobItm = value;
+    },
+
+    "jobItm.itmId"(value) {
+      if (this.$isEmpty(value)) {
+        this.$emit("onDataSetChange", false);
+      } else {
+        this.$emit("onDataSetChange", true);
+      }
+    },
+  },
+
+  methods: {
+    // 모달 열기
+    fnOpenModal() {
+      this.isOpenModal = true;
+    },
+
+    // 모달 닫기
+    fnCloseModal() {
+      this.isOpenModal = false;
+    },
+
+    fnSave(datasetPostId) {
+      const vm = this;
+      axios({
+        url: "/dataset/getDataTableInfo.json",
+        method: "post",
+        headers: { "Content-Type": "application/json; charset=UTF-8" },
+        data: { datapost_id: datasetPostId },
+      })
+        .then(function (response) {
+          vm.jobItm.itmId = datasetPostId;
+          vm.jobItm.dataTable = response.data.resultData.dataTable;
+          vm.fnSetTargetData();
+          // 모달 닫기
+          vm.fnCloseModal();
+        })
+        .catch(function (error) {
+          vm.$showAlert(
+            "에러 발생",
+            "에러가 발생했습니다. 관리자에게 문의해 주세요."
+          );
+        });
+    },
+
+    // 타겟 데이터 조회 후 처리
+    fnSetTargetData() {
+      this.jobItm.dataTable.columnDatas =
+        this.jobItm.dataTable.columnDatas.filter(
+          (column) => column.orginlColumnNm !== "ts_row"
+        );
+      this.matchList = [];
+      for (let column of this.jobItm.dataTable.columnDatas) {
+        this.matchList.push({
+          origin: column,
+          target: column,
+        });
+      }
+    },
+
+    // 매치 리스트
+    fnMatchList() {
+      // 초기화
+      this.matchs = {
+        matchCnt: 0,
+        unMatchCnt: 0,
+      };
+      for (let columns of this.frontColumns) {
+        columns.isConnect = false;
+      }
+
+      // 매치 결과
+      for (let item of this.matchList) {
+        if (this.$isEmpty(item.target)) {
+          this.matchs.unMatchCnt++;
+        } else {
+          this.matchs.matchCnt++;
+          for (let columns of this.frontColumns) {
+            if (item.target.orginlColumnNm == columns.orginlColumnNm) {
+              columns.isConnect = true;
+            }
+          }
+        }
+      }
+
+      this.fnUpdateMatchList(); // 데이터 업데이트
+    },
+    // 데이터 업데이트
+    fnUpdateMatchList() {
+      this.jobItm.itemList = []; // 초기화
+      for (let match of this.matchList) {
+        let originColumnNm = match.origin.orginlColumnNm;
+        let targetColumnNm = match.target.orginlColumnNm;
+
+        this.jobItm.itemList.push({
+          groupId: null,
+          jobIndx: null,
+          indx: null,
+          originColumnNm: this.$isEmpty(originColumnNm) ? null : originColumnNm,
+          targetColumnNm: this.$isEmpty(targetColumnNm) ? null : targetColumnNm,
+        });
+      }
+    },
+
+    // 드래그 이벤트
+    fnMoveStart(column) {
+      this.currentColumn = column;
+    },
+    fnOnDragstart(column) {
+      this.currentColumn = column;
+    },
+    fnOnDragover(event) {
+      event.preventDefault();
+    },
+    fnChangeMatch(type, current) {
+      let columnToUpdate = this.matchList.find((column) => column === current);
+      if (columnToUpdate) {
+        if (type == "drop") {
+          columnToUpdate.target = this.currentColumn;
+        } else if (type == "doubleClick") {
+          columnToUpdate.target = {};
+        }
+      }
+
+      this.fnMatchList();
+      this.fnUpdateMatchList(); // 업데이트
+    },
+  },
+};
+</script>
+
+<style scoped>
+.columncontBox {
+  background: #f5f5f5;
+  border-radius: 10px;
+}
+.columncontBox tr {
+  padding: 8px;
+}
+.columncontBox ul .col-data {
+  margin: 8px;
+}
+.columncontBox .col-data {
+  border: 1px solid #dbdbdb;
+  padding: 5px 10px;
+  background-color: var(--color-white);
+  border-radius: 10px;
+  cursor: move;
+  font-size: 1.3rem;
+  height: 34px;
+}
+.columncontBox .col-data.disabled {
+  background-color: #f5f5f5;
+}
+</style>(No newline at end of file)
 
client/views/component/connection/modalContents/DbRead.vue (added)
+++ client/views/component/connection/modalContents/DbRead.vue
@@ -0,0 +1,674 @@
+<template>
+  <div v-if="currentPage == 1">
+    <div class="table-zone">
+      <table class="form-table">
+        <colgroup>
+          <col style="width: 25%" />
+          <col style="width: 75%" />
+        </colgroup>
+        <tbody v-if="jobItm.itm != null">
+          <tr>
+            <th>연계정보 타입</th>
+            <td>
+              <div class="input-container flex">
+                <label class="radio-label">
+                  <input
+                    type="radio"
+                    name="radio"
+                    :value="false"
+                    class="custom-radiobox"
+                    @change="successAt = false"
+                    v-model="jobItm.itm_option_bool"
+                  />
+                  <span>직접입력</span>
+                </label>
+                <label class="radio-label">
+                  <input
+                    type="radio"
+                    name="radio"
+                    :value="true"
+                    class="custom-radiobox"
+                    @change="successAt = false"
+                    v-model="jobItm.itm_option_bool"
+                  />
+                  <span>불러오기</span>
+                </label>
+              </div>
+            </td>
+          </tr>
+          <tr v-show="jobItm.itm_option_bool">
+            <th>연계정보</th>
+            <td>
+              <input
+                type="text"
+                class="half-input"
+                disabled
+                :value="
+                  linkConnectionDB.conectNm +
+                  '(' +
+                  linkConnectionDB.conectIp +
+                  ')'
+                "
+                v-if="linkConnectionDB.conectNm != null"
+              />
+              <input type="text" class="half-input" disabled v-else />
+              <button
+                class="blue-border-btn small-btn"
+                @click="dbConSearchOpen(true)"
+              >
+                검색
+              </button>
+            </td>
+          </tr>
+          <tr>
+            <th>DMBS</th>
+            <td>
+              <select
+                id="databaseType"
+                @change="successAt = false"
+                class="square-select half-input"
+                v-model="inputConnectionDB.databaseType"
+                :disabled="jobItm.itm_option_bool"
+              >
+                <option
+                  v-for="(itm, index) in databaseTypeList"
+                  :key="index"
+                  :value="itm.key"
+                >
+                  {{ itm.value }}
+                </option>
+              </select>
+            </td>
+          </tr>
+          <tr>
+            <th>IP</th>
+            <td>
+              <input
+                id="conectIp"
+                type="text"
+                @input="successAt = false"
+                class="half-input"
+                v-model="inputConnectionDB.conectIp"
+                placeholder="127.0.0.1"
+                :disabled="jobItm.itm_option_bool"
+              />
+            </td>
+          </tr>
+          <tr>
+            <th>PORT</th>
+            <td>
+              <input
+                id="conectPort"
+                type="text"
+                @input="successAt = false"
+                class="half-input"
+                v-model="inputConnectionDB.conectPort"
+                :disabled="jobItm.itm_option_bool"
+              />
+            </td>
+          </tr>
+          <tr>
+            <th>DB 명</th>
+            <td>
+              <input
+                id="databaseNm"
+                type="text"
+                @input="successAt = false"
+                class="half-input"
+                v-model="inputConnectionDB.databaseNm"
+                placeholder="데이터베이스명 OR 스키마명"
+                :disabled="jobItm.itm_option_bool"
+              />
+            </td>
+          </tr>
+          <tr>
+            <th>접속ID</th>
+            <td>
+              <input
+                type="text"
+                class="half-input"
+                @input="successAt = false"
+                v-model="inputConnectionDB.userId"
+                placeholder="접속 ID"
+                :disabled="jobItm.itm_option_bool"
+              />
+            </td>
+          </tr>
+          <tr>
+            <th>접속PW</th>
+            <td>
+              <input
+                type="password"
+                class="half-input"
+                @input="successAt = false"
+                v-model="inputConnectionDB.userPassword"
+                placeholder="접속 PW"
+                autocomplete="new-password"
+                :disabled="jobItm.itm_option_bool"
+              />
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div class="content-titleZone flex justy justify-between align-center mt20">
+      <p class="box-title">데이터베이스 연결 결과</p>
+      <button
+        class="blue-border-btn small-btn"
+        @click="dataBaseConnectionCheck"
+      >
+        접속확인
+      </button>
+    </div>
+    <div class="table-zone">
+      <table class="list-table">
+        <colgroup>
+          <col width="10%" />
+          <col width="10%" />
+          <col width="80%" />
+        </colgroup>
+        <thead>
+          <tr>
+            <th>No</th>
+            <th>접속시간</th>
+            <th>접속결과</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="(itm, indx) in resultMessage" :key="indx">
+            <td>{{ indx + 1 }}</td>
+            <td>{{ itm.time }}</td>
+            <td>{{ itm.message }}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+  <div v-if="currentPage == 2">
+    <Splitter style="height: 100%; border: 0px solid #e5e7eb">
+      <SplitterPanel
+        class="flex align-items-center justify-content-center"
+        :size="20"
+        :minSize="10"
+      >
+        <div class="content-box">
+          <div class="file-zone">
+            <div class="content-titleZone" style="height: 60px">
+              <p class="box-title">table 정보</p>
+            </div>
+            <div class="content-zone2">
+              <ul class="content-list" v-if="tableList.length > 0">
+                <li
+                  class="cursor"
+                  v-for="(item, indx) in tableList"
+                  :key="indx"
+                >
+                  <a
+                    @click="getTableData(item)"
+                    :class="{
+                      'file-list': true,
+                      selected: selectTable === item,
+                    }"
+                  >
+                    <div class="flex align-center">
+                      <p>
+                        {{
+                          item.tableNmKr != null && item.tableNmKr != ""
+                            ? item.tableNmKr
+                            : item.tableNm
+                        }}
+                      </p>
+                    </div>
+                  </a>
+                </li>
+              </ul>
+            </div>
+          </div>
+        </div>
+      </SplitterPanel>
+      <SplitterPanel
+        class="flex align-items-center justify-content-center"
+        :size="80"
+      >
+        <div class="content-box">
+          <div class="content-titleZone" style="height: 60px">
+            <div class="flex justify-between aling-center">
+              <p class="box-title">쿼리 작업</p>
+              <div>
+                <button class="icon-btn" @click="executeQuery" title="실행">
+                  <svg-icon
+                    type="mdi"
+                    :path="playPath"
+                    :color="'#fbbe28'"
+                  ></svg-icon>
+                </button>
+              </div>
+            </div>
+          </div>
+          <div class="content-zone">
+            <Splitter style="height: 100%" layout="vertical">
+              <SplitterPanel
+                class="flex align-items-center justify-content-center"
+                :size="50"
+                :minSize="20"
+              >
+                <textarea
+                  style="
+                    resize: none;
+                    max-width: 100%;
+                    max-height: 100%;
+                    padding: 10px;
+                  "
+                  v-model="jobItm.itm.query"
+                ></textarea>
+              </SplitterPanel>
+              <SplitterPanel
+                class="align-items-center justify-content-center"
+                :size="50"
+              >
+                <ul class="tab-nav flex justify-start">
+                  <li @click="showTab('tab1')">
+                    <a
+                      href="#tab01"
+                      :class="{ activeTab: activeTab === 'tab1' }"
+                      >작업결과</a
+                    >
+                  </li>
+                  <li @click="showTab('tab2')">
+                    <a
+                      href="#tab02"
+                      :class="{ activeTab: activeTab === 'tab2' }"
+                      >작업log</a
+                    >
+                  </li>
+                </ul>
+                <div
+                  v-show="activeTab === 'tab1'"
+                  style="
+                    height: calc(100% - 60px);
+                    overflow: auto;
+                    padding: 10px;
+                  "
+                >
+                  <div
+                    class="count-zone"
+                    v-if="jobItm.dataTable.columnDatas.length > 0"
+                  >
+                    <p>
+                      총 <span>{{ jobItm.dataTable.totalRows }}</span
+                      >건 중 <span>{{ jobItm.dataTable.rowData.length }}</span
+                      >건 조회
+                    </p>
+                  </div>
+                  <div class="table-zone">
+                    <table class="list-table">
+                      <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
+                      <thead>
+                        <tr v-if="jobItm.dataTable.columnDatas.length > 0">
+                          <th
+                            v-for="(itm, indx) in jobItm.dataTable.columnDatas"
+                            :key="indx"
+                            style="min-width: 150px !important"
+                          >
+                            {{ itm.columnNm }}
+                            <label class="check-label">
+                              <input
+                                type="checkbox"
+                                class="custom-checkbox"
+                                v-model="itm.pkAt"
+                              />
+                            </label>
+                          </th>
+                        </tr>
+                      </thead>
+                      <tbody v-if="jobItm.dataTable.rowData.length > 0">
+                        <tr
+                          v-for="(row, rows) in jobItm.dataTable.rowData"
+                          :key="rows"
+                        >
+                          <td
+                            v-for="(itm, indx) in row"
+                            :key="indx"
+                            style="
+                              overflow: hidden;
+                              white-space: nowrap;
+                              text-overflow: ellipsis;
+                            "
+                          >
+                            {{ itm }}
+                          </td>
+                        </tr>
+                      </tbody>
+                    </table>
+                  </div>
+                </div>
+                <div
+                  v-show="activeTab === 'tab2'"
+                  style="
+                    height: calc(100% - 60px);
+                    overflow: auto;
+                    padding: 10px;
+                  "
+                >
+                  <div class="table-zone">
+                    <table class="list-table">
+                      <colgroup>
+                        <col width="10%" />
+                        <col width="10%" />
+                        <col width="" />
+                        <col width="10%" />
+                      </colgroup>
+                      <thead>
+                        <tr>
+                          <th>No</th>
+                          <th>접속시간</th>
+                          <th>접속결과</th>
+                          <th>접속내용</th>
+                        </tr>
+                      </thead>
+                      <tbody>
+                        <tr v-for="(itm, indx) in executeMessage" :key="indx">
+                          <td>{{ indx + 1 }}</td>
+                          <td>{{ itm.time }}</td>
+                          <td>{{ itm.message }}</td>
+                          <td>{{ itm.result }}</td>
+                        </tr>
+                      </tbody>
+                    </table>
+                  </div>
+                </div>
+              </SplitterPanel>
+            </Splitter>
+          </div>
+        </div>
+      </SplitterPanel>
+    </Splitter>
+  </div>
+
+  <DBConSearch
+    :openPopup="openSearchModal"
+    @modalclose="dbConSearchOpen"
+    @selectItm="selectDbcon"
+  />
+</template>
+
+<script>
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiPlay } from "@mdi/js";
+
+import axios from "axios";
+import DBConSearch from "../../dataComponent/DbConnectionSearchModal.vue";
+
+export default {
+  name: "DbRead",
+
+  props: {
+    jobItem: {
+      type: Object,
+    },
+    currentPage: {
+      type: Number,
+    },
+  },
+
+  data() {
+    return {
+      // 연계정보 불러오기
+      openSearchModal: false,
+      // 커넥션 DB 오브젝트
+      jobItm: this.jobItem,
+      connectionDB: {},
+      linkConnectionDB: {},
+      inputConnectionDB: {},
+      resultMessage: [],
+      executeMessage: [],
+      // 연계 성공여부
+      successAt: false,
+      databaseTypeList: [],
+      tableList: [],
+      selectTable: {},
+      activeTab: "tab1",
+      playPath: mdiPlay,
+    };
+  },
+
+  methods: {
+    // 연계가능 여부 테스트
+    dataBaseConnectionCheck: function () {
+      var vm = this;
+      if (this.jobItm.itm_option_bool == true) {
+        this.jobItm.itm = this.linkConnectionDB;
+      } else {
+        this.jobItm.itm = this.inputConnectionDB;
+      }
+      this.jobItm.itm.loadAt = this.jobItm.itm_option_bool;
+      delete this.jobItm.itm.type;
+
+      axios({
+        url: "/data/dataBaseConnectionCheck.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: vm.jobItm.itm,
+      })
+        .then(function (response) {
+          if (response.data.checkMessage.success == true) {
+            vm.successAt = true;
+          } else {
+            vm.successAt = false;
+          }
+
+          vm.$emit("onDfltSetChange", response.data.checkMessage.success);
+
+          vm.$showAlert("결과내용", response.data.checkMessage.message);
+          vm.resultMessage.push({
+            time: vm.$getFullTime(),
+            message: response.data.checkMessage.message,
+            result: response.data.checkMessage.success
+              ? "접속성공"
+              : "접속실패",
+          });
+        })
+        .catch(function (error) {});
+    },
+
+    // db커넥션 선택창 호출
+    dbConSearchOpen: function (val) {
+      this.openSearchModal = val;
+    },
+
+    // 연계정보 받기
+    selectDbcon: function (dbcon) {
+      this.linkConnectionDB = dbcon;
+    },
+
+    // 탭 변경
+    showTab: function (tabName) {
+      this.activeTab = tabName;
+    },
+
+    //DB 접속 - 테이블 목록 조회
+    getDbConnectionTableList: function () {
+      let vm = this;
+      axios({
+        url: "/data/selectTableList.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: vm.jobItm.itm,
+      })
+        .then(function (response) {
+          if (response.data.checkMessage.success == true) {
+            vm.tableList = response.data.resultData.tableList;
+          }
+        })
+        .catch(function (error) {});
+    },
+
+    // 테이블 클릭
+    clickTable: function (itm) {
+      this.selectTable = itm;
+    },
+
+    // 선택한 테이블 정보 가져오기
+    getTableData: function (itm) {
+      let vm = this;
+      itm.perPage = this.jobItm.dataTable.perPage;
+
+      // 통신시 데이터 형문제로 삭제
+      this.jobItm.itm.creatDt = null;
+      let requestData = {
+        dataset: itm,
+        connectionDB: this.jobItm.itm,
+      };
+
+      axios({
+        url: "/data/getTableData.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: requestData,
+      })
+        .then(function (response) {
+          if (response.data.checkMessage.success) {
+            vm.jobItm.dataTable = response.data.resultData.dataTable;
+            vm.jobItm.itm.query = vm.jobItm.dataTable.query;
+
+            vm.columnDatas = response.data.resultData.dataTable.columnDatas;
+          }
+
+          vm.$emit("onDataSetChange", response.data.checkMessage.success);
+
+          vm.executeMessage.push({
+            time: vm.$getFullTime(),
+            message: response.data.resultData.dataTable.checkMessage.message,
+            result: response.data.resultData.dataTable.checkMessage.success
+              ? "성공"
+              : "실패",
+          });
+        })
+        .catch(function (error) {
+          vm.$emit("onDataSetChange", false);
+        });
+    },
+
+    // 쿼리 실행
+    executeQuery: function () {
+      let vm = this;
+      // 통신시 데이터 형문제로 삭제
+      this.jobItm.itm.creatDt = null;
+      this.jobItm.dataTable.query = this.jobItm.itm.query;
+      let requestData = {
+        dataTable: this.jobItm.dataTable,
+        connectionDB: this.jobItm.itm,
+      };
+
+      axios({
+        url: "/data/getTableDataByQuery.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: requestData,
+      })
+        .then(function (response) {
+          if (response.data.checkMessage.success) {
+            vm.jobItm.dataTable = response.data.resultData.dataTable;
+            vm.jobItm.itm.query = vm.jobItm.dataTable.query;
+          }
+
+          vm.$emit("onDataSetChange", response.data.checkMessage.success);
+
+          vm.executeMessage.push({
+            time: vm.$getFullTime(),
+            message: response.data.resultData.dataTable.checkMessage.message,
+            result: response.data.resultData.dataTable.checkMessage.success
+              ? "성공"
+              : "실패",
+          });
+        })
+        .catch(function (error) {
+          vm.$emit("onDataSetChange", false);
+        });
+    },
+
+    // 초기화 function
+    init: async function () {
+      this.databaseTypeList = await this.$getDataBaseTypeList();
+
+      if (this.jobItm == null) {
+        this.jobItm = Object.assign({}, this.$getDefaultJobGroup().node);
+        this.linkConnectionDB = Object.assign(
+          {},
+          this.$getDefaultJobGroup().connectionDb
+        );
+        this.inputConnectionDB = Object.assign(
+          {},
+          this.$getDefaultJobGroup().connectionDb
+        );
+        this.jobItm.itm = Object.assign(
+          {},
+          this.$getDefaultJobGroup().connectionDb
+        );
+      } else {
+        if (this.jobItm.itm_option_bool) {
+          this.linkConnectionDB = this.jobItm.itm;
+          this.connectionDB = this.linkConnectionDB;
+        } else {
+          this.inputConnectionDB = this.jobItm.itm;
+          this.connectionDB = this.inputConnectionDB;
+        }
+      }
+
+      if (this.inputConnectionDB.databaseType == null) {
+        this.inputConnectionDB.databaseType = this.databaseTypeList.MARIADB.key;
+      }
+    },
+  },
+
+  watch: {
+    jobItem: function (v) {
+      this.jobItm = v;
+      if (v.itm_option_bool) {
+        this.linkConnectionDB = this.jobItm.itm;
+      } else {
+        this.inputConnectionDB = this.jobItm.itm;
+      }
+    },
+
+    currentPage(value) {
+      if (value == 2) {
+        this.getDbConnectionTableList();
+      }
+    },
+  },
+
+  components: {
+    DBConSearch: DBConSearch,
+    SvgIcon: SvgIcon,
+  },
+
+  mounted() {
+    this.init();
+  },
+
+  create() {
+    if (this.jobItm == null) {
+      this.jobItm = Object.assign({}, this.defaultJobItm);
+      this.jobItm.itm = this.$getDefaultJobGroup().connectionDb;
+    }
+  },
+};
+</script>
+
+<style>
+.content-zone2 {
+  height: calc(100% - 57px);
+  max-height: calc(100% - 57px);
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+</style>(No newline at end of file)
 
client/views/component/modal/DatasetListModal.vue (added)
+++ client/views/component/modal/DatasetListModal.vue
@@ -0,0 +1,87 @@
+<template>
+  <div class="modal-wrapper">
+    <div class="modal-container large-modal">
+      <div class="modal-title flex justify-between align-center">
+        <h2>데이터 셋 선택</h2>
+        <button class="close-btn" @click="$emit('onClose')">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="closePath"
+          ></svg-icon>
+        </button>
+      </div>
+      <div class="modal-content-monthly">
+        <table class="form-table">
+          <colgroup>
+            <col style="width: 20%" />
+            <col style="width: 80%" />
+          </colgroup>
+          <tbody>
+            <tr>
+              <th>선택한 데이터셋 아이디</th>
+              <td>
+                <input
+                  type="text"
+                  class="full-input"
+                  placeholder="데이터셋을 선택해 주세요."
+                  readonly
+                  v-model="itmId"
+                />
+              </td>
+            </tr>
+          </tbody>
+        </table>
+        <div style="height: clac(100% - 50px)">
+          <DataPostManagerMain type="modal" @onSelected="fnChangeTarget" />
+        </div>
+      </div>
+      <div class="modal-end flex justify-end">
+        <button class="blue-btn small-btn" @click="$emit('onSelect', itmId)">
+          선택
+        </button>
+        <button class="blue-border-btn small-btn" @click="$emit('onClose')">
+          취소
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiClose } from "@mdi/js";
+
+import DataPostManagerMain from "../../pages/data/datapost/DataPostManagerMain.vue";
+
+export default {
+  name: "DatasetUpdate",
+
+  components: {
+    SvgIcon,
+    DataPostManagerMain,
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      closePath: mdiClose,
+
+      itmId: null,
+    };
+  },
+
+  created() {},
+
+  mounted() {},
+
+  methods: {
+    // 데이터셋 선택
+    fnChangeTarget(id) {
+      this.itmId = id;
+    },
+  },
+};
+</script>(No newline at end of file)
 
client/views/component/modal/NodeSetupModal.vue (added)
+++ client/views/component/modal/NodeSetupModal.vue
@@ -0,0 +1,199 @@
+<template>
+  <div class="modal-wrapper">
+    <div class="modal-container large-modal">
+      <!-- 모달 HEAD (제목) -->
+      <div class="modal-title flex justify-between align-center">
+        <h2>{{ currentNode.label }}</h2>
+        <button class="close-btn" @click="$emit('onClose')">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="closePath"
+          ></svg-icon>
+        </button>
+      </div>
+      <!-- 모달 BODY (본문) -->
+      <div class="modal-content-monthly">
+        <!-- 리드 노드 -->
+        <DbRead
+          v-if="jobItm.type == 'DB_READ'"
+          :jobItem="jobItm"
+          :currentPage="currentPage"
+          @onDfltSetChange="fnDfltSetChange"
+          @onDataSetChange="fnDataSetChange"
+        />
+        <ApiRead
+          v-if="jobItm.type == 'API_READ'"
+          :jobItem="jobItm"
+          :currentPage="currentPage"
+          @onDfltSetChange="fnDfltSetChange"
+          @onDataSetChange="fnDataSetChange"
+        />
+        <!-- 필터 노드 -->
+        <DataFilter
+          v-if="jobItm.type == 'DATA_FILTER'"
+          :jobItem="jobItm"
+          :currentPage="currentPage"
+          :frontNode="frontNode"
+          @onDataSetChange="fnDataSetChange"
+        />
+        <!-- 프로세스 노드 -->
+        <DatasetUpdate
+          v-if="jobItm.type == 'DATASET_UPDATE'"
+          :jobItem="jobItm"
+          :currentPage="currentPage"
+          :frontNode="frontNode"
+          @onDataSetChange="fnDataSetChange"
+        />
+      </div>
+      <!-- 모달 FOOT (버튼) -->
+      <div class="modal-end flex justify-end">
+        <button
+          class="blue-border-btn small-btn"
+          v-if="currentPage == 2"
+          @click="onChange(1)"
+        >
+          이전
+        </button>
+        <button
+          class="blue-border-btn small-btn"
+          v-if="currentPage == 1"
+          @click="onChange(2)"
+        >
+          다음
+        </button>
+        <button class="blue-btn small-btn" v-if="isDataSet" @click="fnSave">
+          등록
+        </button>
+        <button class="blue-border-btn small-btn" @click="$emit('onClose')">
+          취소
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import _ from "lodash";
+
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiMagnify, mdiClose } from "@mdi/js";
+
+// 컴포넌트
+import DbRead from "../connection/modalContents/DbRead.vue";
+import ApiRead from "../connection/modalContents/ApiRead.vue";
+import DataFilter from "../connection/modalContents/DataFilter.vue";
+import DatasetUpdate from "../connection/modalContents/DatasetUpdate.vue";
+
+export default {
+  components: { SvgIcon, DbRead, ApiRead, DataFilter, DatasetUpdate },
+
+  props: {
+    frontNodes: Array,
+    currentNode: Object,
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      searchPath: mdiMagnify,
+      closePath: mdiClose,
+
+      isDfltSet: false, // 커넥션 정보 설정 여부
+      isDataSet: false, // 노드 데이터 설정 여부
+      currentPage: 1,
+      jobItm: Object.assign({}, this.$getDefaultJobGroup().jobItem),
+      frontNode: Object.assign({}, this.$getDefaultJobGroup().jobItem),
+    };
+  },
+
+  created() {
+    if (!this.$isEmpty(this.currentNode)) {
+      this.jobItm = _.cloneDeep(this.currentNode.jobItm);
+    }
+    if (!this.$isEmpty(this.frontNodes)) {
+      this.frontNode = _.cloneDeep(this.frontNodes[0].jobItm);
+    }
+
+    if (this.currentNode.type == "read") {
+      this.currentPage = 1;
+    } else {
+      this.currentPage = 0;
+    }
+  },
+
+  mounted() {},
+
+  methods: {
+    onChange(value) {
+      if (this.currentPage == 1 && !this.isDfltSet) {
+        this.$showAlert(
+          "경고",
+          "오류가 발생했습니다. 입력한 정보를 확인해 주세요."
+        );
+      } else {
+        this.currentPage = value;
+      }
+    },
+
+    // 저장
+    fnSave() {
+      if (this.jobItm.type == "DATA_FILTER") {
+        let message = this.filterValidation();
+        if (!this.$isEmpty(message)) {
+          this.$showAlert("경고", message);
+          return;
+        }
+      } else if (this.jobItm.type == "DATASET_UPDATE") {
+        let cnt = 0;
+        let list = this.jobItm.itemList;
+        for (let item of list) {
+          if (!this.$isEmpty(item.targetColumnNm)) {
+            cnt++;
+          }
+        }
+        if (cnt < 1) {
+          this.$showAlert(
+            "경고",
+            "데이터셋 매핑을 최소 1개 이상 설정해 주세요."
+          );
+          return;
+        }
+      }
+
+      this.$emit("onSave", this.jobItm);
+    },
+
+    filterValidation() {
+      if (this.$isEmpty(this.jobItm.itm.match_type)) {
+        return "필터 종류를 선택해 주세요.";
+      }
+
+      for (let filter of this.jobItm.itm.filterItems) {
+        if (
+          this.$isEmpty(filter.coulmn_nm) ||
+          this.$isEmpty(filter.calc_ty) ||
+          this.$isEmpty(filter.cmpr_value)
+        ) {
+          return "필터 목록에 빈 값이 있습니다. 빈 값을 채워 주세요.";
+        }
+      }
+
+      return null;
+    },
+
+    // 커넥션 정보 설정 여부 변경
+    fnDfltSetChange(isDfltSet) {
+      console.log("fnDfltSetChange 실행: ", isDfltSet);
+      this.isDfltSet = isDfltSet;
+    },
+
+    // 노드 데이터 설정 여부 변경
+    fnDataSetChange(isDataSet) {
+      console.log("fnDataSetChange 실행: ", isDataSet);
+      this.isDataSet = isDataSet;
+    },
+  },
+};
+</script>(No newline at end of file)
 
client/views/component/modal/SchedulePreviewModal.vue (added)
+++ client/views/component/modal/SchedulePreviewModal.vue
@@ -0,0 +1,102 @@
+<template>
+  <div class="modal-wrapper">
+    <div class="modal-container large-modal">
+      <div class="modal-title flex justify-between align-center">
+        <h2>스케줄 결과 미리보기</h2>
+        <button class="close-btn" @click="$emit('onClose')">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="closePath"
+          ></svg-icon>
+        </button>
+      </div>
+      <div class="modal-content-monthly">
+        <template v-for="(item, idx) of frontDataTables" :key="idx">
+          <ul class="tab-nav2 flex align-center">
+            <li @click="showTab(item.name)">
+              <a :class="{ activeTab: activeTab === item.name }">데이터셋</a>
+            </li>
+          </ul>
+          <ul class="tab-content2">
+            <li v-show="activeTab === item.name" class="content-box">
+              <div class="content-titleZone">
+                <p class="box-title">데이터셋 정보</p>
+              </div>
+              <div class="content-zone" style="overflow: auto">
+                <table class="list-table table-flow">
+                  <template v-if="!$isEmpty(dataTable)">
+                    <thead>
+                      <tr>
+                        <th>No</th>
+                        <th v-for="(rowKey, index) of rowKeys" :key="index">
+                          {{ rowKey }}
+                        </th>
+                      </tr>
+                    </thead>
+                    <tbody>
+                      <tr v-for="(items, indexI) of rowData" :key="indexI">
+                        <td>{{ indexI + 1 }}</td>
+                        <td v-for="(item, indexJ) of items" :key="indexJ">
+                          {{ item }}
+                        </td>
+                      </tr>
+                    </tbody>
+                  </template>
+                  <tr v-else>
+                    <td>등록된 데이터가 없습니다.</td>
+                  </tr>
+                </table>
+              </div>
+            </li>
+          </ul>
+        </template>
+      </div>
+      <div class="modal-end flex justify-end">
+        <button class="blue-btn small-btn" @click="$emit('onClose')">
+          확인
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiMagnify, mdiClose } from "@mdi/js";
+
+export default {
+  components: { SvgIcon },
+
+  props: {
+    diagram: Object,
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      searchPath: mdiMagnify,
+      closePath: mdiClose,
+
+      frontDataTables: [],
+      rowKeys: [],
+      rowData: [],
+
+      activeTab: null,
+    };
+  },
+
+  created() {
+    console.log("diagram: ", this.diagram);
+  },
+
+  mounted() {},
+
+  methods: {
+    showTab(tabName) {
+      this.activeTab = tabName;
+    },
+  },
+};
+</script>(No newline at end of file)
 
client/views/component/scheduleComponent/CreateNodeModal.vue (added)
+++ client/views/component/scheduleComponent/CreateNodeModal.vue
@@ -0,0 +1,183 @@
+<template>
+  <div class="modal-wrapper">
+    <div class="modal-container small-modal">
+      <div class="modal-title">
+        <div class="flex justify-between align-center">
+          <h2>노드 추가</h2>
+          <button class="close-btn" @click="fnCloseModal">X</button>
+        </div>
+      </div>
+      <div class="modal-content-monthly">
+        <div
+          class="mb10"
+          v-for="(type, idx) of typeList"
+          :key="idx"
+          :value="type"
+        >
+          <div class="content-titleZone flex justify-between align-center mt20">
+            <p class="box-title">{{ type.nm }}</p>
+          </div>
+          <div class="input-container flex">
+            <template v-for="(child, index) of type.childList" :key="index">
+              <label class="radio-label">
+                <input
+                  type="radio"
+                  name="radio"
+                  class="custom-radiobox"
+                  :value="child"
+                  v-model="selectedNode"
+                />
+                <span>{{ child.nm }}</span>
+              </label>
+            </template>
+          </div>
+        </div>
+      </div>
+      <div class="modal-end flex justify-end">
+        <button class="blue-btn small-btn" @click="fnAddNode">등록</button>
+        <button class="blue-border-btn small-btn" @click="fnCloseModal">
+          취소
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  name: "CreateNodeModal",
+
+  props: {
+    schdulType: {
+      type: String,
+      default: null,
+    },
+  },
+
+  data() {
+    return {
+      // 대분류
+      typeList: [],
+      selectedType: {},
+
+      // 소분류
+      nodeList: [],
+      selectedNode: {},
+    };
+  },
+
+  created() {
+    this.fnSelectTypeList();
+  },
+
+  computed: {},
+
+  watch: {
+    selectedNode: {
+      deep: true,
+      handler(value) {
+        this.selectedType = {}; // 초기화
+        for (let type of this.typeList) {
+          if (type.id === value.upId) {
+            this.selectedType = type;
+            break;
+          }
+        }
+      },
+    },
+  },
+
+  methods: {
+    // 분류 조회
+    async fnSelectTypeList() {
+      this.typeList = await this.$getCommonCodeByTree(this.schdulType);
+      this.selectedType = this.typeList[0];
+      this.selectedNode = this.typeList[0].childList[0];
+    },
+
+    // 등록 버튼 동작
+    fnAddNode() {
+      let node = Object.assign({}, this.$getDefaultJobGroup().node);
+      node.id =
+        "NODE_" + new Date().getTime() + +Math.floor(Math.random() * 1000) + 1;
+      node.label = this.selectedNode.nm;
+      node.position = {
+        x: Math.floor(Math.random() * 500) + 1,
+        y: Math.floor(Math.random() * 300) + 1,
+      };
+
+      // 타입 설정
+      switch (this.selectedType.id) {
+        case "DATA_READ_NODE": // 리드
+          node.type = "read";
+          break;
+        case "DATA_FILTER_NODE": // 필터
+          node.type = "filter";
+          break;
+        case "DATA_PROCESS_NODE": // 프로세스
+          node.type = "process";
+          break;
+      }
+
+      // jobitem 설정
+      node.jobItm = Object.assign({}, this.$getDefaultObject().jobItem);
+
+      // itm, itmType 설정j
+      node.jobItm.type = this.selectedNode.id;
+      switch (this.selectedNode.id) {
+        case "DB_READ":
+          node.jobItm.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().connectionDb
+          );
+          break;
+        case "API_READ":
+          node.jobItm.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().connectionApi
+          );
+          break;
+        case "FILE_READ":
+          node.jobItm.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().fileRead
+          );
+          break;
+        case "DATASET_READ":
+          node.jobItm.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().DatasetPost
+          );
+          break;
+        case "DATA_FILTER":
+          node.jobItm.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().dataFilter
+          );
+          break;
+        case "EHOJO_READ":
+          node.jobItm.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().connectionEhojo
+          );
+          break;
+        case "DATASET_UPDATE":
+          node.jobItm.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().jobItemGroup
+          );
+          break;
+      }
+
+      this.$emit("addNode", node);
+
+      // 모달 닫기
+      this.fnCloseModal();
+    },
+
+    // 모달 닫기
+    fnCloseModal() {
+      this.$emit("closeModal");
+    },
+  },
+};
+</script>(No newline at end of file)
 
client/views/component/scheduleComponent/DiagramList.vue (added)
+++ client/views/component/scheduleComponent/DiagramList.vue
@@ -0,0 +1,376 @@
+<template>
+  <div class="node-zone" style="height: 93%">
+    <VueFlow
+      :nodes="nodes"
+      :edges="edges"
+      :default-viewport="{ zoom: 1, position: { x: 0, y: 0 } }"
+      :min-zoom="0.2"
+      :max-zoom="4"
+      :default-edge-options="{ type: 'smoothstep' }"
+      @connect="onConnect"
+      @node-drag-stop="onMove"
+      @node-double-click="fnNodeDblClick"
+      @edge-double-click="fnDeleteEdge"
+      @edge-update="onEdgeUpdate"
+    >
+      <!-- 시작노드 -->
+      <template #node-start="props">
+        <StartNode
+          :node="props"
+          :schdulType="diagram.schdulType"
+          @updateType="fnUpdateSchdulType"
+        />
+      </template>
+      <!-- 리드 -->
+      <template #node-read="props">
+        <ReadNode
+          :node="fnFindNode(props.id)"
+          @onSetup="fnOpenSetup"
+          @onRemove="fnRemoveNode"
+        />
+      </template>
+      <!-- 필터 -->
+      <template #node-filter="props">
+        <FilterNode
+          :node="fnFindNode(props.id)"
+          @onSetup="fnOpenSetup"
+          @onRemove="fnRemoveNode"
+        />
+      </template>
+      <!-- 프로세스 -->
+      <template #node-process="props">
+        <ProcessNodeVue
+          :node="fnFindNode(props.id)"
+          @onSetup="fnOpenSetup"
+          @onRemove="fnRemoveNode"
+        />
+      </template>
+      <!-- 종료노드 -->
+      <template #node-end="props">
+        <EndNode :node="props" @onSetup="fnOpenSetup" />
+      </template>
+
+      <MiniMap />
+      <Controls />
+      <Background pattern-color="#aaa" :gap="10" />
+    </VueFlow>
+  </div>
+  <div class="flex justify-end">
+    <button
+      class="blue-border-btn small-btn"
+      v-if="!$isEmpty(diagram.schdulType)"
+      @click="fnOpenModal"
+    >
+      노드 추가
+    </button>
+    <button class="blue-border-btn small-btn">초기화</button>
+    <button class="blue-btn small-btn">로그 보기</button>
+  </div>
+
+  <!-- 모달 -->
+  <CreateNodeModal
+    v-if="isOpenModal"
+    :schdulType="diagram.schdulType"
+    @addNode="fnAddNode"
+    @closeModal="fnCloseModal"
+  />
+  <ConnectionModal
+    v-if="isOpenSetupModal"
+    :frontNodes="frontNodes"
+    :currentNode="currentNode"
+    @onSave="fnSaveSetup"
+    @onClose="fnCloseSetup"
+  />
+  <SchedulePreviewModal
+    v-if="isOpenPreviewModal"
+    :diagram="diagram"
+    @onClose="fnCloseSetup"
+  />
+</template>
+<script>
+import StartNode from "./nodes/StartNode.vue";
+import ReadNode from "./nodes/ReadNode.vue";
+import FilterNode from "./nodes/FilterNode.vue";
+import ProcessNodeVue from "./nodes/ProcessNode.vue";
+import EndNode from "./nodes/EndNode.vue";
+// vue-flow api (삭제시 동작 안함)
+import { Background } from "@vue-flow/background";
+import { Panel, VueFlow } from "@vue-flow/core";
+import { MiniMap } from "@vue-flow/minimap";
+import { Controls } from "@vue-flow/controls";
+// 모달 컴포넌트
+import CreateNodeModal from "./CreateNodeModal.vue";
+import ConnectionModal from "../modal/NodeSetupModal.vue";
+import SchedulePreviewModal from "../modal/SchedulePreviewModal.vue";
+
+export default {
+  name: "DiagramList",
+
+  components: {
+    StartNode,
+    ReadNode,
+    FilterNode,
+    ProcessNodeVue,
+    EndNode,
+    // vue-flow api (삭제시 동작 안함)
+    Background,
+    Panel,
+    VueFlow,
+    MiniMap,
+    Controls,
+    // 모달 컴포넌트
+    CreateNodeModal,
+    ConnectionModal,
+    SchedulePreviewModal,
+  },
+
+  props: {
+    currentDiagram: {
+      type: Object,
+    },
+  },
+
+  data() {
+    return {
+      diagram: this.currentDiagram,
+      nodes: [],
+      edges: [],
+
+      startNode: {}, // 시작노드
+      endNode: {}, // 종료노드
+
+      isOpenModal: false,
+      isOpenSetupModal: false,
+      isOpenPreviewModal: false,
+
+      frontNodes: [],
+      currentNode: Object.assign({}, this.$getDefaultJobGroup().node),
+    };
+  },
+
+  mounted() {
+    this.init(); // 초기화
+  },
+
+  methods: {
+    // 초기노드(시작, 종료) 생성
+    init() {
+      if (this.$isEmpty(this.currentDiagram.schdulId)) {
+        // 시작노드 생성
+        this.startNode = Object.assign({}, this.$getDefaultJobGroup().node);
+        this.startNode.id =
+          "NODE_" + new Date().getTime() + Math.floor(Math.random() * 1000) + 1;
+        this.startNode.type = "start";
+        this.startNode.label = "시작노드";
+        this.startNode.position = {
+          x: 0,
+          y: 0,
+        };
+      } else {
+        this.nodes = this.diagram.nodes;
+        this.edges = this.diagram.edges;
+
+        // 시작노드 생성
+        for (let node of this.nodes) {
+          if (node.type == "start") {
+            this.startNode = node;
+            break;
+          }
+        }
+      }
+
+      // 종료노드 생성
+      this.endNode = Object.assign({}, this.$getDefaultJobGroup().node);
+      this.endNode.id =
+        "NODE_" + new Date().getTime() + Math.floor(Math.random() * 1000) + 1;
+      this.endNode.type = "end";
+      this.endNode.label = "종료노드";
+      this.endNode.position = {
+        x: 749,
+        y: 326,
+      };
+
+      this.initDiagram(); // 다이어그램 초기화
+    },
+
+    // 다이어그램 초기화
+    initDiagram() {
+      // 노드 정보 확인
+      if (this.currentDiagram.nodes.length > 0) {
+        this.nodes = Object.assign({}, this.currentDiagram.nodes); // 노드 목록 복제
+      } else {
+        this.nodes = []; // 노드 목록 초기화
+        this.nodes.push(this.startNode);
+        this.nodes.push(this.endNode);
+      }
+
+      // 엣지 정보 확인
+      if (this.currentDiagram.edges.length > 0) {
+        this.edges = Object.assign({}, this.currentDiagram.edges); // 엣지 목록 복제
+      } else {
+        this.edges = []; // 엣지 목록 초기화
+      }
+    },
+
+    // 시작 노드 선택
+    fnUpdateSchdulType(schdulType) {
+      // 시작 노드 설정
+      if (this.$isEmpty(schdulType)) {
+        this.startNode.isSetup = false;
+      } else {
+        this.startNode.isSetup = true;
+      }
+
+      this.diagram.schdulType = schdulType; // 스케줄 타입 변경
+
+      this.initDiagram(); // 다이어그램 초기화
+    },
+
+    /* 노드추가 모달 */
+    // 모달 열기
+    fnOpenModal() {
+      this.isOpenModal = true;
+    },
+
+    // 모달 닫기
+    fnCloseModal() {
+      this.isOpenModal = false;
+    },
+
+    /* 노드설정 모달 */
+    // 노드설정 모달 열기
+    fnOpenSetup(nodeId) {
+      this.currentNode = this.fnFindNode(nodeId);
+
+      if (this.currentNode.type != "read") {
+        this.frontNodes = this.fnFindLinkedFrontNodes(nodeId);
+
+        if (this.frontNodes.length < 1) {
+          this.$showAlert("경고", "연결된 노드가 없습니다.");
+          return;
+        }
+
+        // 추후 1:다 관계 노드 생기면 타입에 따른 조건문 씌워서 사용
+        if (this.frontNodes.length > 1) {
+          this.$showAlert("경고", "연결된 노드가 많습니다.");
+          return;
+        }
+      }
+
+      if (this.currentNode.type != "end") {
+        this.isOpenSetupModal = true;
+      } else {
+        this.isOpenPreviewModal = true;
+      }
+    },
+
+    // 노드설정 저장
+    fnSaveSetup(jobItem) {
+      this.currentNode.jobItm = jobItem;
+
+      const nodeToUpdate = this.nodes.find(
+        (node) => node.id === this.currentNode.id
+      );
+
+      if (nodeToUpdate) {
+        Object.assign(nodeToUpdate, this.currentNode);
+        nodeToUpdate.isSetup = true;
+      }
+
+      this.fnCloseSetup(); // 노드설정 모달 닫기
+    },
+
+    // 노드설정 모달 닫기
+    fnCloseSetup() {
+      this.currentNode = Object.assign({}, this.$getDefaultJobGroup().node);
+
+      if (this.currentNode.type != "end") {
+        this.isOpenSetupModal = false;
+      } else {
+        this.isOpenPreviewModal = false;
+      }
+    },
+
+    /* 노드 설정 */
+    // 노드 추가
+    fnAddNode(node) {
+      this.nodes.push(node);
+    },
+
+    // 노드 목록에서 노드 아이디로 노드 찾기
+    fnFindNode(nodeId) {
+      for (let node of this.nodes) {
+        if (node.id === nodeId) {
+          return node;
+        }
+      }
+
+      return null;
+    },
+
+    // 연결된 소스 노드 찾기
+    fnFindLinkedFrontNodes(nodeId) {
+      let frontNodes = [];
+
+      for (let edge of this.edges) {
+        if (nodeId === edge.target) {
+          let frontNode = this.fnFindNode(edge.source);
+
+          if (!this.$isEmpty(frontNode)) {
+            frontNodes.push(frontNode);
+          }
+        }
+      }
+
+      return frontNodes;
+    },
+
+    // 노드 더블 클릭
+    fnNodeDblClick(v) {
+      console.log("nodes: ", this.nodes);
+      console.log("edges: ", this.edges);
+    },
+
+    // 노드 이동
+    onMove(moveNode) {
+      for (let node of this.nodes) {
+        if (node.id == moveNode.node.id) {
+          node.position.x = moveNode.node.position.x;
+          node.position.y = moveNode.node.position.y;
+        }
+      }
+    },
+
+    // 노드 삭제
+    fnRemoveNode(nodeId) {
+      this.nodes = this.nodes.filter((node) => node.id !== nodeId);
+      this.edges = this.edges.filter(
+        (edge) => edge.source !== nodeId && edge.target !== nodeId
+      );
+    },
+
+    /* 엣지 설정 */
+    // 엣지 추가
+    onConnect(event) {
+      let id = "EDGE_" + Date.now();
+
+      let edge = {
+        id: id,
+        edge_id: id,
+        source: event.source,
+        target: event.target,
+        animated: true,
+        style: { stroke: "#213f99", strokeWidth: 2 },
+      };
+
+      this.edges.push(edge);
+    },
+
+    // 엣지 삭제
+    fnDeleteEdge(event) {
+      let edgeId = event.edge.id;
+      this.edges = this.edges.filter((edge) => edge.id !== edgeId);
+    },
+  },
+};
+</script>(No newline at end of file)
 
client/views/component/scheduleComponent/DiagramLogDetailList.vue (added)
+++ client/views/component/scheduleComponent/DiagramLogDetailList.vue
@@ -0,0 +1,229 @@
+<template>
+  <div class="modal-wrapper">
+    <div class="modal-container list-modal">
+      <div class="modal-title">
+        <div class="flex justify-between align-center">
+          <h2 class="main-title flex80">로그 상세보기</h2>
+          <button class="close-btn" @click="closeModal">
+            <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon>
+          </button>
+        </div>
+      </div>
+      <div class="modal-content-monthly" v-show="modalType == 'form-modal'">
+        <div class="flex justify-start align-center">
+          <div class="cunt-selectZone">
+            <p class="box-title"> 로그분류 <select v-model="searchdetail_data.value" @change="searchDetailData">
+                <option :value="null">상태 전체</option>
+                <option value="success">성공</option>
+                <option value="fail">실패</option>
+              </select>
+            </p>
+          </div>
+        </div>
+        <table class="list-table blue">
+          <thead>
+            <tr>
+              <th>번호</th>
+              <th>작동일시</th>
+              <th>작동완료일시</th>
+              <th>노드명</th>
+              <th>호스트명</th>
+              <th>로그분류</th>
+              <th>로그메세지</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="(rowdata, dataIndex) in detaillogdata" :key="dataIndex">
+              <td>{{ dataIndex + 1 }}</td>
+              <td>{{ $getFullTime(rowdata.operation_date) }}</td>
+              <td>{{ $getFullTime(rowdata.operation_success_date) }}</td>
+              <td>{{ rowdata.node_name }}</td>
+              <td>{{ rowdata.creat_id }}</td>
+              <td>{{ rowdata.log_category }}</td>
+              <td class="text-over" title="-">{{ rowdata.log_message }}</td>
+            </tr>
+          </tbody>
+        </table>
+        <PaginationButton v-model:currentPage="searchdetail.currentPage" :perPage="searchdetail.perPage" :totalCount="searchdetail.totalRows" :maxRange="5" :click="detaillogSelectListAll" />
+      </div>
+      <div class="modal-end flex justify-end">
+        <button v-show="modalType === 'form-modal'" class="blue-btn small-btn" @click="closeModal"> 확인 </button>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import PaginationButton from "../PaginationButton.vue";
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiMagnify } from "@mdi/js";
+import { mdiClose } from "@mdi/js";
+import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
+import DefaultSearchBar from "../../template/templateElement/DefaultSearchBar.vue";
+import { useRoute } from "vue-router";
+import axios from "axios";
+export default {
+  components: {
+    PaginationButton: PaginationButton,
+    SvgIcon: SvgIcon,
+    DefaultSearchBar: DefaultSearchBar,
+  },
+  props: {
+    diagram_id: {
+      type: String, // 또는 String, 실제 타입에 맞게 설정
+      required: true,
+    },
+  },
+  data() {
+    return {
+      // 검색
+      search: this.$getDefaultSerchVO(),
+      search_date: this.$getDefaultSerchItem("operation_date", "dates"),
+      search_data1: this.$getDefaultSerchItem("state_info", "string"),
+      search_data2: {
+        key: "diagram_id",
+        value: this.diagram_id,
+      },
+
+      //상세보기 검색
+      searchdetail: this.$getDefaultSerchVO(),
+      searchdetail_data: this.$getDefaultSerchItem("log_category", "string"),
+      searchdetail_data1: {
+        key: null,
+        value: null,
+      },
+
+      //검색 데이터
+      searchPath: mdiMagnify,
+      startDate: true,
+      endDate: true,
+
+      closePath: mdiClose,
+      isModalOpen: false,
+      modalType: "form-modal",
+      checkPath: mdiCheckCircle,
+      xPath: mdiCloseCircle,
+      logdata: [],
+      detaillogdata: [],
+      logId: null,
+      diagram_id: this.diagram_id,
+
+      //현재 라우터의 정보
+      route: useRoute(),
+      // 페이지 경로 목록
+      pageList: [
+        { path: "/", name: "대시보드" },
+        { path: "/fileManagement.page", name: "파일 관리" },
+        { path: "/hostManagement.page", name: "호스트 관리" },
+        { path: "/scheduleManagement.page", name: "작업 스케줄 관리" },
+      ],
+    };
+  },
+  mounted() {
+    this.searchData();
+  },
+  watch: {
+    diagram_id(v) {
+      this.search_data2.value = v;
+      this.searchData();
+    },
+  },
+  methods: {
+    searchData() {
+      const vm = this;
+      let data = vm.search;
+      data.searchObjectList = [];
+      data.searchObjectList.push(this.search_date);
+      data.searchObjectList.push(this.search_data1);
+      data.searchObjectList.push(this.search_data2);
+      axios({
+        url: "/diagram/logSelectListAll.json",
+        method: "post",
+        headers: { "Content-Type": "application/json; charset=UTF-8" },
+        data: data,
+      })
+        .then(function (response) {
+          vm.search.currentPage = response.data.resultData.searchVO.currentPage;
+          vm.search.order = response.data.resultData.searchVO.order;
+          vm.search.orderASC = response.data.resultData.searchVO.orderASC;
+          vm.search.perPage = response.data.resultData.searchVO.perPage;
+          vm.search.searchObjectList = [];
+          vm.search.totalRows = response.data.resultData.searchVO.totalRows;
+          vm.logdata = response.data.resultData.logList;
+        })
+        .catch(function (error) {
+          vm.$showAlert(
+            "오류",
+            "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다."
+          );
+        });
+    },
+    searchDetailData() {
+      const vm = this;
+      let datadetail = vm.searchdetail;
+      datadetail.searchObjectList = [];
+      datadetail.searchObjectList.push(this.searchdetail_data);
+
+      this.detaillogSelectListAll();
+    },
+    openModal() {
+      this.isModalOpen = true;
+    },
+    closeModal() {
+      this.isModalOpen = false;
+    },
+    pathName() {
+      for (let i = 0; i < this.pageList.length; i++) {
+        if (this.route.path == this.pageList[i]["path"]) {
+          return this.pageList[i]["name"];
+        }
+      }
+      return "대시보드";
+    },
+
+    handleRowClick(logId) {
+      this.logId = logId;
+      this.detaillogSelectListAll();
+      this.openModal();
+    },
+
+    detaillogSelectListAll() {
+      const vm = this;
+      let datadetail = vm.searchdetail;
+      datadetail.searchObjectList = [];
+      datadetail.searchObjectList.push(this.searchdetail_data);
+      vm.searchdetail_data1.key = "log_id";
+      vm.searchdetail_data1.value = vm.logId;
+      datadetail.searchObjectList.push(this.searchdetail_data1);
+
+      axios({
+        url: "/diagram/detaillogSelectListAll.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: datadetail,
+      })
+        .then(function (response) {
+          vm.searchdetail.currentPage =
+            response.data.resultData.searchVO.currentPage;
+          vm.searchdetail.order = response.data.resultData.searchVO.order;
+          vm.searchdetail.orderASC = response.data.resultData.searchVO.orderASC;
+          vm.searchdetail.perPage = response.data.resultData.searchVO.perPage;
+          vm.searchdetail.searchObjectList = [];
+          vm.searchdetail.totalRows =
+            response.data.resultData.searchVO.totalRows;
+          vm.detaillogdata = response.data.resultData.logList;
+        })
+        .catch(function (error) {
+          alert("상품 주문에 오류가 발생했습니다.");
+        });
+    },
+  },
+};
+</script>
+<style scoped>
+.navigate_bar {
+  padding: 10px 0px;
+  font-size: 1.3rem;
+}
+</style>(No newline at end of file)
 
client/views/component/scheduleComponent/DiagramLogList.vue (added)
+++ client/views/component/scheduleComponent/DiagramLogList.vue
@@ -0,0 +1,68 @@
+<template>
+  <div class="content-titleZone flex justify-between align-center">
+    <p class="box-title">다이어그램 목록</p>
+    <div class="search-bar">
+      <div class="flex justify-end align-center">
+        <input type="date" name="start-date" id="start-date" class="square-date" :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value" @change="searchData()" />
+        <span class="coupler">~</span>
+        <input type="date" name="end-date" id="end-date" class="square-date ml5" :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value2" @change="searchData()" />
+        <select class="square-select ml5" v-model="search_data1.value" @change="searchData()">
+          <option :value="null">상태 전체</option>
+          <option value="success">성공</option>
+          <option value="fail">실패</option>
+        </select>
+      </div>
+    </div>
+  </div>
+  <div class="table-zone">
+    <table class="list-table">
+      <thead>
+        <tr>
+          <th>번호</th>
+          <th>총 노드수</th>
+          <th>실패 노드수</th>
+          <th>실패 노드명</th>
+          <th>실행일자</th>
+          <th>상태</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="(rowdata, dataIndex) in logdata" :key="dataIndex" @click="handleRowClick(rowdata.log_id)">
+          <td>{{ dataIndex + 1 }}</td>
+          <td>{{ rowdata.total_node_count }}</td>
+          <td>{{ rowdata.fail_node_count }}</td>
+          <td>{{ rowdata.fail_node_name }}</td>
+          <td>{{ $getFullTime(rowdata.operation_date) }}</td>
+          <td>
+            <p v-if="rowdata.state_info === 'success'" class="flex justify-center align-center state green icon-area">
+              <svg-icon type="mdi" :width="21" :height="21" :path="checkPath"></svg-icon>
+              <span>성공</span>
+            </p>
+            <p v-else-if="rowdata.state_info === 'fail'" class="flex justify-center align-center state red">
+              <svg-icon type="mdi" :width="21" :height="21" :path="xPath"></svg-icon>
+              <span>실패</span>
+            </p>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage" :totalCount="search.totalRows" :maxRange="5" :click="searchData" />
+  </div>
+</template>
+<script>
+import DiagramLogDetailList from './DiagramLogDetailList.vue';
+
+export default {
+  name: "",
+
+  components: { DiagramLogDetailList },
+
+  data() {
+    return {};
+  },
+
+  mounted() { },
+
+  methods: {},
+}
+</script>(No newline at end of file)
client/views/component/scheduleComponent/ScheduleForm.vue (Renamed from client/views/component/ScheculerCreateComp.vue)
--- client/views/component/ScheculerCreateComp.vue
+++ client/views/component/scheduleComponent/ScheduleForm.vue
@@ -1,4 +1,7 @@
 <template>
+  <div class="content-titleZone">
+    <p class="box-title">스케줄 등록</p>
+  </div>
   <div class="table-zone">
     <table class="form-table">
       <colgroup>
@@ -9,13 +12,7 @@
         <tr>
           <th>제목</th>
           <td>
-            <input
-              type="text"
-              name=""
-              id="inputid"
-              class="full-input"
-              v-model="title"
-            />
+            <input type="text" name="" id="inputid" class="full-input" v-model="title" />
           </td>
         </tr>
         <tr>
@@ -27,23 +24,11 @@
           <td>
             <div class="input-container flex">
               <label class="radio-label">
-                <input
-                  type="radio"
-                  name="radio2"
-                  :value="false"
-                  class="custom-radiobox"
-                  v-model="isUsehighClassOption"
-                />
+                <input type="radio" name="radio2" :value="false" class="custom-radiobox" v-model="isUsehighClassOption" />
                 <span>일반 방식</span>
               </label>
               <label class="radio-label">
-                <input
-                  type="radio"
-                  name="radio2"
-                  :value="true"
-                  class="custom-radiobox"
-                  v-model="isUsehighClassOption"
-                />
+                <input type="radio" name="radio2" :value="true" class="custom-radiobox" v-model="isUsehighClassOption" />
                 <span>커스텀 방식</span>
               </label>
             </div>
@@ -54,13 +39,7 @@
           <td>
             <div class="input-container flex">
               <label class="radio-label">
-                <input
-                  type="radio"
-                  name="Cyclical"
-                  :value="true"
-                  class="custom-radiobox"
-                  v-model="isCyclical"
-                />
+                <input type="radio" name="Cyclical" :value="true" class="custom-radiobox" v-model="isCyclical" />
                 <span>반복작업(반복주기 설정)</span>
               </label>
             </div>
@@ -69,243 +48,93 @@
         <tr>
           <th>반복주기 설정</th>
           <td>
-            <div
-              class="flex justify-start"
-              v-show="isUsehighClassOption == false"
-            >
-              <select
-                name="cycle"
-                v-model="cronItem"
-                :disabled="isUsehighClassOption == true"
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
-                <option
-                  v-for="(value, key, i) in cronItems"
-                  :key="i"
-                  :value="key"
-                >
-                  매 {{ value.title }}
-                </option>
+            <div class="flex justify-start" v-show="isUsehighClassOption == false">
+              <select name="cycle" v-model="cronItem" :disabled="isUsehighClassOption == true" style="margin-left: 5px; margin-bottom: 5px">
+                <option v-for="(value, key, i) in cronItems" :key="i" :value="key"> 매 {{ value.title }} </option>
                 <option value="ALL">전체</option>
               </select>
-              <select
-                name="year"
-                v-model="cronMap.YEAR"
-                v-if="isUsehighClassOption == false"
-                v-show="cronItem == 'YEAR' || cronItem == 'ALL'"
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
+              <select name="year" v-model="cronMap.YEAR" v-if="isUsehighClassOption == false" v-show="cronItem == 'YEAR' || cronItem == 'ALL'" style="margin-left: 5px; margin-bottom: 5px">
                 <option value="*">매년</option>
-                <option
-                  v-for="(item, idx) in cronItems.YEAR.values"
-                  :key="idx"
-                  :value="item"
-                >
-                  {{ item }}년
-                </option>
+                <option v-for="(item, idx) in cronItems.YEAR.values" :key="idx" :value="item"> {{ item }}년 </option>
               </select>
-              <select
-                name="months"
-                v-model="cronMap.MONTHS"
-                v-show="
-                  cronItem == 'MONTHS' ||
-                  cronItem == 'YEAR' ||
-                  cronItem == 'ALL'
-                "
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
+              <select name="months" v-model="cronMap.MONTHS" v-show="cronItem == 'MONTHS' ||
+                cronItem == 'YEAR' ||
+                cronItem == 'ALL'
+                " style="margin-left: 5px; margin-bottom: 5px">
                 <option value="*">매월</option>
-                <option
-                  v-for="(item, idx) in cronItems.MONTHS.values"
-                  :key="idx"
-                  :value="item"
-                >
-                  {{ item }}월
-                </option>
+                <option v-for="(item, idx) in cronItems.MONTHS.values" :key="idx" :value="item"> {{ item }}월 </option>
               </select>
-              <select
-                name="weeks"
-                v-model="cronMap.WEEKS"
-                v-show="cronItem == 'WEEKS' || cronItem == 'ALL'"
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
-                <option
-                  v-for="(item, idx) in cronItems.WEEKS.values"
-                  :key="idx"
-                  :value="idx + ''"
-                >
-                  {{ item }}요일
-                </option>
+              <select name="weeks" v-model="cronMap.WEEKS" v-show="cronItem == 'WEEKS' || cronItem == 'ALL'" style="margin-left: 5px; margin-bottom: 5px">
+                <option v-for="(item, idx) in cronItems.WEEKS.values" :key="idx" :value="idx + ''"> {{ item }}요일 </option>
                 <option value="1,2,3,4,5">평일</option>
                 <option value="0,6">주말(토,일)</option>
                 <option value="?" v-show="cronItem != 'WEEKS'">없음</option>
               </select>
-              <select
-                name="days"
-                v-model="cronMap.DAYS"
-                v-show="
-                  cronItem == 'DAYS' ||
-                  cronItem == 'MONTHS' ||
-                  cronItem == 'YEAR' ||
-                  cronItem == 'ALL'
-                "
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
+              <select name="days" v-model="cronMap.DAYS" v-show="cronItem == 'DAYS' ||
+                cronItem == 'MONTHS' ||
+                cronItem == 'YEAR' ||
+                cronItem == 'ALL'
+                " style="margin-left: 5px; margin-bottom: 5px">
                 <option value="*" v-show="cronItem == 'DAYS'">매일</option>
-                <option
-                  v-for="(item, idx) in cronItems.DAYS.values"
-                  :key="idx"
-                  :value="item"
-                >
-                  {{ item }}일
-                </option>
+                <option v-for="(item, idx) in cronItems.DAYS.values" :key="idx" :value="item"> {{ item }}일 </option>
                 <option value="L">말일</option>
               </select>
-              <select
-                name="hours"
-                v-model="cronMap.HOURS"
-                v-show="
-                  cronItem == 'HOURS' ||
-                  cronItem == 'DAYS' ||
-                  cronItem == 'WEEKS' ||
-                  cronItem == 'MONTHS' ||
-                  cronItem == 'YEAR' ||
-                  cronItem == 'ALL'
-                "
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
-                <option
-                  v-for="(item, idx) in cronItems.HOURS.values"
-                  :key="idx"
-                  :value="item"
-                >
-                  {{ item }}시
-                </option>
+              <select name="hours" v-model="cronMap.HOURS" v-show="cronItem == 'HOURS' ||
+                cronItem == 'DAYS' ||
+                cronItem == 'WEEKS' ||
+                cronItem == 'MONTHS' ||
+                cronItem == 'YEAR' ||
+                cronItem == 'ALL'
+                " style="margin-left: 5px; margin-bottom: 5px">
+                <option v-for="(item, idx) in cronItems.HOURS.values" :key="idx" :value="item"> {{ item }}시 </option>
               </select>
-              <select
-                name="minute"
-                v-model="cronMap.MINUTES"
-                v-show="
-                  cronItem == 'MINUTES' ||
-                  cronItem == 'HOURS' ||
-                  cronItem == 'DAYS' ||
-                  cronItem == 'WEEKS' ||
-                  cronItem == 'MONTHS' ||
-                  cronItem == 'YEAR' ||
-                  cronItem == 'ALL'
-                "
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
-                <option
-                  v-for="(item, idx) in cronItems.MINUTES.values"
-                  :key="idx"
-                  :value="item"
-                >
-                  {{ item }}분
-                </option>
+              <select name="minute" v-model="cronMap.MINUTES" v-show="cronItem == 'MINUTES' ||
+                cronItem == 'HOURS' ||
+                cronItem == 'DAYS' ||
+                cronItem == 'WEEKS' ||
+                cronItem == 'MONTHS' ||
+                cronItem == 'YEAR' ||
+                cronItem == 'ALL'
+                " style="margin-left: 5px; margin-bottom: 5px">
+                <option v-for="(item, idx) in cronItems.MINUTES.values" :key="idx" :value="item"> {{ item }}분 </option>
               </select>
-              <select
-                name="seconds"
-                v-model="cronMap.SECONDS"
-                v-show="
-                  cronItem == 'SECONDS' ||
-                  'MINUTES' ||
-                  cronItem == 'HOURS' ||
-                  cronItem == 'DAYS' ||
-                  cronItem == 'WEEKS' ||
-                  cronItem == 'MONTHS' ||
-                  cronItem == 'YEAR' ||
-                  cronItem == 'ALL'
-                "
-                style="margin-left: 5px; margin-bottom: 5px"
-              >
-                <option
-                  v-for="(item, idx) in cronItems.SECONDS.values"
-                  :key="idx"
-                  :value="item"
-                >
-                  {{ item }}초
-                </option>
+              <select name="seconds" v-model="cronMap.SECONDS" v-show="cronItem == 'SECONDS' ||
+                'MINUTES' ||
+                cronItem == 'HOURS' ||
+                cronItem == 'DAYS' ||
+                cronItem == 'WEEKS' ||
+                cronItem == 'MONTHS' ||
+                cronItem == 'YEAR' ||
+                cronItem == 'ALL'
+                " style="margin-left: 5px; margin-bottom: 5px">
+                <option v-for="(item, idx) in cronItems.SECONDS.values" :key="idx" :value="item"> {{ item }}초 </option>
               </select>
             </div>
             <div class="flex justify-start" v-show="isUsehighClassOption">
-              <select
-                name="cycle"
-                v-model="cronItem"
-                :disabled="isUsehighClassOption == true"
-                style="width: 100px; margin-left: 5px; margin-bottom: 5px"
-              >
-                <option
-                  v-for="(value, key, i) in cronItems"
-                  :key="i"
-                  :value="key"
-                >
-                  매 {{ value.title }}
-                </option>
+              <select name="cycle" v-model="cronItem" :disabled="isUsehighClassOption == true" style="width: 100px; margin-left: 5px; margin-bottom: 5px">
+                <option v-for="(value, key, i) in cronItems" :key="i" :value="key"> 매 {{ value.title }} </option>
                 <option value="ALL">전체 주기</option>
               </select>
               <label for="year">
-                <input
-                  type="text"
-                  id="year"
-                  name="year"
-                  v-model="cronMap.YEAR"
-                  style="margin-bottom: 5px; width: 100px"
-                />
+                <input type="text" id="year" name="year" v-model="cronMap.YEAR" style="margin-bottom: 5px; width: 100px" />
               </label>
               <label for="months">
-                <input
-                  type="text"
-                  id="months"
-                  name="months"
-                  v-model="cronMap.MONTHS"
-                  style="width: 100px; margin-bottom: 5px"
-                />
+                <input type="text" id="months" name="months" v-model="cronMap.MONTHS" style="width: 100px; margin-bottom: 5px" />
               </label>
               <label for="weeks">
-                <input
-                  type="text"
-                  id="weeks"
-                  name="weeks"
-                  v-model="cronMap.WEEKS"
-                  style="width: 100px; margin-bottom: 5px"
-                />
+                <input type="text" id="weeks" name="weeks" v-model="cronMap.WEEKS" style="width: 100px; margin-bottom: 5px" />
               </label>
               <label for="days">
-                <input
-                  type="text"
-                  id="days"
-                  name="days"
-                  v-model="cronMap.DAYS"
-                  style="width: 100px; margin-bottom: 5px"
-                />
+                <input type="text" id="days" name="days" v-model="cronMap.DAYS" style="width: 100px; margin-bottom: 5px" />
               </label>
               <label for="hours">
-                <input
-                  type="text"
-                  id="hours"
-                  name="hours"
-                  v-model="cronMap.HOURS"
-                  style="width: 100px; margin-bottom: 5px"
-                />
+                <input type="text" id="hours" name="hours" v-model="cronMap.HOURS" style="width: 100px; margin-bottom: 5px" />
               </label>
               <label for="minutes">
-                <input
-                  type="text"
-                  id="minutes"
-                  name="minutes"
-                  v-model="cronMap.MINUTES"
-                  style="width: 100px; margin-bottom: 5px"
-                />
+                <input type="text" id="minutes" name="minutes" v-model="cronMap.MINUTES" style="width: 100px; margin-bottom: 5px" />
               </label>
               <label for="seconds">
-                <input
-                  type="text"
-                  id="seconds"
-                  name="seconds"
-                  v-model="cronMap.SECONDS"
-                  style="width: 100px; margin-bottom: 5px"
-                />
+                <input type="text" id="seconds" name="seconds" v-model="cronMap.SECONDS" style="width: 100px; margin-bottom: 5px" />
               </label>
             </div>
           </td>
@@ -323,8 +152,14 @@
       </tbody>
     </table>
   </div>
+  <div class="flex justify-end" id="buttonZone">
+    <button class="blue-btn small-btn" id="registerButton">
+      <span>등록</span>
+      <span>수정</span>
+    </button>
+    <button class="blue-border-btn small-btn" id="resetButton">초기화</button>
+  </div>
 </template>
-
 <script>
 //크론 설정값 초기화
 var cronMapInit = {
 
client/views/component/scheduleComponent/ScheduleList.vue (added)
+++ client/views/component/scheduleComponent/ScheduleList.vue
@@ -0,0 +1,105 @@
+<template>
+  <table class="list-table">
+    <colgroup>
+      <col style="width: 5%" />
+      <col style="width: 5%" />
+      <col style="width: 25%" />
+      <col style="width: 15%" />
+      <col style="width: 20%" />
+      <col style="width: 20%" />
+      <col style="width: 15%" />
+    </colgroup>
+    <thead>
+      <tr>
+        <th>
+          <input type="checkbox" v-model="isChkAll" @change="fnChkAll()" />
+        </th>
+        <th>번호</th>
+        <th>제목</th>
+        <th>작성자</th>
+        <th>반복주기</th>
+        <th>등록일</th>
+        <th>상태</th>
+      </tr>
+    </thead>
+    <tbody>
+      <template v-if="list.length > 0">
+        <tr v-for="(item, idx) in list" :key="item" @click="$emit('fnSelectDiagram', item.diagram_id)">
+          <td>
+            <input type="checkbox" :value="item" v-model="selectList" @click.stop="" @change="fnChangeCheckList" />
+          </td>
+          <td>{{ search.totalRows - idx - (search.currentPage - 1) * search.perPage }}</td>
+          <td>{{ item.diagram_nm }}</td>
+          <td>{{ item.creat_id }}</td>
+          <td>{{ item.cron_chrctr }}</td>
+          <td>{{ $getFullTime(item.creat_dt) }}</td>
+          <td>
+            <div v-if="item.schdul_sttus === 'run'" class="flex justify-center align-center state green icon-area">
+              <svg-icon type="mdi" :width="21" :height="21" :path="checkPath" class="mr5"></svg-icon>
+              <span>진행</span>
+            </div>
+            <div v-else-if="item.schdul_sttus === 'stop'" class="flex justify-center align-center state red">
+              <svg-icon type="mdi" :width="21" :height="21" :path="xPath" class="mr5"></svg-icon>
+              <span>중단</span>
+            </div>
+          </td>
+        </tr>
+      </template>
+      <tr v-else>
+        <td colspan="7">등록된 데이터가 없습니다.</td>
+      </tr>
+    </tbody>
+  </table>
+  <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage" :totalCount="search.totalRows" :maxRange="5" :click="fnDiagramList" />
+</template>
+<script>
+// 컴포넌트 import
+import PaginationButton from "../../component/PaginationButton.vue";
+
+export default {
+  name: "ScheduleList",
+
+  components: {
+    PaginationButton,
+  },
+
+  props: {
+    list: {
+      type: Array,
+      default: [],
+    },
+    search: {
+      type: Object,
+      default: {},
+    },
+  },
+
+  data() {
+    return {
+      isChkAll: false,
+      selectList: [],
+    };
+  },
+
+  mounted() { },
+
+  methods: {
+    // 전체 선택
+    fnChkAll() {
+      this.selectList = []; // 초기화
+      if (this.isChkAll) {
+        this.selectList = this.diagramList;
+      }
+    },
+
+    // 개별 선택
+    fnChangeCheckList() {
+      if (this.selectList.length == this.diagramList.length) {
+        this.isChkAll = true;
+      } else {
+        this.isChkAll = false;
+      }
+    },
+  },
+};
+</script>(No newline at end of file)
 
client/views/component/scheduleComponent/nodes/EndNode.vue (added)
+++ client/views/component/scheduleComponent/nodes/EndNode.vue
@@ -0,0 +1,79 @@
+<template>
+  <div class="node">
+    <div class="node-header flex justify-between align-center">
+      <p class="box-title">{{ node.label }}</p>
+      <div></div>
+    </div>
+    <div class="node-body flex justify-center align-center">
+      <button
+        type="button"
+        class="blue-border-btn small-btn"
+        @click="$emit('onSetup', node.id)"
+      >
+        미리보기
+      </button>
+    </div>
+  </div>
+  <Handle type="target" :position="Position.Left" />
+</template>
+
+<script>
+import { Handle, Position } from "@vue-flow/core";
+
+export default {
+  components: {
+    Handle,
+  },
+
+  props: {
+    node: Object,
+  },
+
+  data() {},
+
+  setup() {
+    return { Position };
+  },
+
+  computed: {},
+
+  mounted() {},
+
+  methods: {},
+};
+</script>
+
+<style scoped>
+.node {
+  background: #ffffff;
+  border: 1px solid #dbe3fb;
+  border-radius: 10px;
+  width: 300px;
+  box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8);
+}
+
+.node-header {
+  padding: 10px 15px;
+  background-color: #edf0ff;
+  border-bottom: 1px solid #dbe3fb;
+}
+
+.node-header .box-title {
+  color: #213f99;
+}
+
+.node-header button {
+  width: 20px;
+  height: 20px;
+}
+
+.node-header button img {
+  width: 100%;
+  height: 100%;
+}
+
+.node-body {
+  padding: 15px;
+  border-radius: 5px;
+}
+</style>(No newline at end of file)
 
client/views/component/scheduleComponent/nodes/FilterNode.vue (added)
+++ client/views/component/scheduleComponent/nodes/FilterNode.vue
@@ -0,0 +1,110 @@
+<template>
+  <div class="node">
+    <div class="node-header flex justify-between align-center">
+      <p class="box-title">{{ node.label }}</p>
+      <div>
+        <button @click="$emit('onSetup', node.id)">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="setPath"
+          ></svg-icon>
+        </button>
+        <button @click="$emit('onRemove', node.id)">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="delPath"
+          ></svg-icon>
+        </button>
+      </div>
+    </div>
+    <div class="node-body">
+      <div v-if="node.isSetup">
+        <p style="font-size: 1.5rem">등록된 필터 개수 : {{ filterCnt }}</p>
+        <p style="font-size: 1.5rem">
+          필터 적용 후 로우 개수 : {{ node.dataTable.rowData.length }}
+        </p>
+      </div>
+      <p v-else style="font-size: 1.5rem">노드 미설정</p>
+    </div>
+    <!-- 핸들 -->
+    <Handle type="source" :position="Position.Right" />
+    <Handle type="target" :position="Position.Left" />
+  </div>
+</template>
+
+<script>
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiCog, mdiTrashCanOutline } from "@mdi/js";
+
+import { Handle, Position } from "@vue-flow/core";
+
+export default {
+  components: {
+    SvgIcon,
+    Handle,
+  },
+
+  props: {
+    node: Object,
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      setPath: mdiCog,
+      delPath: mdiTrashCanOutline,
+
+      filterCnt: 0,
+      totalRow: 0,
+    };
+  },
+
+  setup() {
+    return {
+      Position,
+    };
+  },
+
+  mounted() {},
+
+  watch: {},
+
+  methods: {},
+};
+</script>
+
+<style scoped>
+.node {
+  background: #ffffff;
+  border: 1px solid #dbe3fb;
+  border-radius: 10px;
+  width: 300px;
+  box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8);
+}
+
+.node-header {
+  padding: 10px 15px;
+  background-color: #edf0ff;
+  border-bottom: 1px solid #dbe3fb;
+}
+
+.node-header button {
+  width: 20px;
+  height: 20px;
+}
+
+.node-header button img {
+  width: 100%;
+  height: 100%;
+}
+
+.node-body {
+  padding: 15px;
+  border-radius: 5px;
+}
+</style>(No newline at end of file)
 
client/views/component/scheduleComponent/nodes/ProcessNode.vue (added)
+++ client/views/component/scheduleComponent/nodes/ProcessNode.vue
@@ -0,0 +1,118 @@
+<template>
+  <div class="node">
+    <div class="node-header flex justify-between align-center">
+      <p class="box-title">{{ node.label }}</p>
+      <div>
+        <button @click="$emit('onSetup', node.id)">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="setPath"
+          ></svg-icon>
+        </button>
+        <button @click="$emit('onRemove', node.id)">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="delPath"
+          ></svg-icon>
+        </button>
+      </div>
+    </div>
+    <div class="node-body">
+      <div v-if="node.isSetup">
+        <p style="font-size: 1.5rem">매칭 데이터 개수 : {{ matchCnt }}</p>
+      </div>
+      <p v-else style="font-size: 1.5rem">노드 미설정</p>
+    </div>
+    <!-- 핸들 -->
+    <Handle type="source" :position="Position.Right" />
+    <Handle type="target" :position="Position.Left" />
+  </div>
+</template>
+
+<script>
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiCog, mdiTrashCanOutline } from "@mdi/js";
+
+import { Handle, Position } from "@vue-flow/core";
+
+export default {
+  components: {
+    SvgIcon,
+    Handle,
+  },
+
+  props: {
+    node: Object,
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      setPath: mdiCog,
+      delPath: mdiTrashCanOutline,
+
+      matchCnt: 0,
+    };
+  },
+
+  setup() {
+    return {
+      Position,
+    };
+  },
+
+  mounted() {},
+
+  watch: {
+    "node.jobItm.itemList": {
+      deep: true,
+      handler(value) {
+        this.matchCnt = 0;
+        for (let item of value) {
+          if (!this.$isEmpty(item.targetColumnNm)) {
+            this.matchCnt++;
+          }
+        }
+      },
+    },
+  },
+
+  methods: {},
+};
+</script>
+
+<style scoped>
+.node {
+  background: #ffffff;
+  border: 1px solid #dbe3fb;
+  border-radius: 10px;
+  width: 300px;
+  box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8);
+}
+
+.node-header {
+  padding: 10px 15px;
+  background-color: #edf0ff;
+  border-bottom: 1px solid #dbe3fb;
+}
+
+.node-header button {
+  width: 20px;
+  height: 20px;
+}
+
+.node-header button img {
+  width: 100%;
+  height: 100%;
+}
+
+.node-body {
+  padding: 15px;
+  border-radius: 5px;
+}
+</style>(No newline at end of file)
 
client/views/component/scheduleComponent/nodes/ReadNode.vue (added)
+++ client/views/component/scheduleComponent/nodes/ReadNode.vue
@@ -0,0 +1,93 @@
+<template>
+  <div class="node">
+    <div class="node-header flex justify-between align-center">
+      <p class="box-title">{{ node.label }}</p>
+      <div>
+        <button @click="$emit('onSetup', node.id)">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="setPath"
+          ></svg-icon>
+        </button>
+        <button @click="$emit('onRemove', node.id)">
+          <svg-icon
+            type="mdi"
+            :width="20"
+            :height="20"
+            :path="delPath"
+          ></svg-icon>
+        </button>
+      </div>
+    </div>
+    <div class="node-body flex justify-center align-center">
+      <p style="font-size: 1.5rem">
+        {{ node.isSetup ? "노드 설정 완료" : "노드 미설정" }}
+      </p>
+    </div>
+    <!-- 핸들 -->
+    <Handle type="source" :position="Position.Right" />
+    <Handle type="target" :position="Position.Left" />
+  </div>
+</template>
+
+<script>
+// icon용 svg import
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiCog, mdiTrashCanOutline } from "@mdi/js";
+
+import { Handle, Position } from "@vue-flow/core";
+
+export default {
+  components: {
+    SvgIcon,
+    Handle,
+  },
+
+  props: {
+    node: Object,
+  },
+
+  data() {
+    return {
+      // icon용 svg path
+      setPath: mdiCog,
+      delPath: mdiTrashCanOutline,
+    };
+  },
+
+  setup() {
+    return { Position };
+  },
+
+  mounted() {},
+
+  methods: {},
+};
+</script>
+
+<style scoped>
+.node {
+  background: #ffffff;
+  border: 1px solid #dbe3fb;
+  border-radius: 10px;
+  width: 300px;
+  box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8);
+}
+
+.node-header {
+  padding: 10px 15px;
+  background-color: #edf0ff;
+  border-bottom: 1px solid #dbe3fb;
+}
+
+.node-header .box-title {
+  color: #213f99;
+}
+
+.node-body {
+  padding: 15px;
+  border-radius: 5px;
+}
+</style>(No newline at end of file)
 
client/views/component/scheduleComponent/nodes/StartNode.vue (added)
+++ client/views/component/scheduleComponent/nodes/StartNode.vue
@@ -0,0 +1,118 @@
+<template>
+  <div class="node">
+    <div class="node-header flex justify-between align-center">
+      <p class="box-title">{{ node.label }}</p>
+      <div></div>
+    </div>
+    <div class="node-body flex justify-center align-center">
+      <select class="full-select" v-model="newType" @change="onChange">
+        <option value="">스케줄 정보를 선택해주세요.</option>
+        <option
+          v-for="(item, idx) of nodeItemList"
+          :key="idx"
+          :value="item.cmmnCode"
+        >
+          {{ item.codeNm }}
+        </option>
+      </select>
+    </div>
+  </div>
+  <Handle type="source" :position="Position.Right" />
+</template>
+
+<script>
+import { Handle, Position } from "@vue-flow/core";
+
+export default {
+  name: "StartNode",
+
+  components: {
+    Handle,
+  },
+
+  props: {
+    node: Object,
+    schdulType: String,
+  },
+
+  data() {
+    return {
+      oldType: "",
+      newType: "",
+      nodeItemList: [],
+    };
+  },
+
+  setup() {
+    return { Position };
+  },
+
+  mounted() {
+    if (!this.$isEmpty(this.schdulType)) {
+      this.oldType = this.schdulType;
+      this.newType = this.schdulType;
+    }
+
+    this.fnSelectTypeList(); // 노드 아이템 조회
+  },
+
+  methods: {
+    // 노드 아이템 조회
+    async fnSelectTypeList() {
+      this.nodeItemList = await this.$getCommonCode("NODE_ITEM");
+    },
+
+    // 변경 감지
+    onChange() {
+      if (!this.$isEmpty(this.oldType) && this.oldType != this.newType) {
+        let isChecked = confirm(
+          "값 변경 시, 기존에 작성한 내용이 삭제됩니다. 그래도 실행하시겠습니까?"
+        );
+
+        if (!isChecked) {
+          this.newType = this.oldType;
+          return;
+        }
+      }
+
+      this.oldType = this.newType;
+      this.$emit("updateType", this.newType);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.node {
+  background: #ffffff;
+  border: 1px solid #dbe3fb;
+  border-radius: 10px;
+  width: 300px;
+  box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8);
+}
+
+.node-header {
+  padding: 10px 15px;
+  background-color: #edf0ff;
+  border-bottom: 1px solid #dbe3fb;
+}
+
+.node-header .box-title {
+  color: #213f99;
+}
+
+.node-header button {
+  width: 20px;
+  height: 20px;
+}
+
+.node-header button img {
+  width: 100%;
+  height: 100%;
+}
+
+.node-body {
+  padding: 15px;
+  border-radius: 5px;
+}
+</style>(No newline at end of file)
client/views/pages/AppRouter.js
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
@@ -2,31 +2,34 @@
 import store from "./AppStore";
 import { createWebHistory, createRouter } from "vue-router";
 // #페이지
-// 통합관리
+// 데이터관리
+import FileManagement from "../pages/data/FileManagement.vue"; // 파일 관리
+import DataManagement from "../pages/data/DataPostManagement.vue"; // 데이터 관리
+// 작업관리
+import ScheduleManagement from "../pages/schedule/ScheduleManagement.vue"; // 작업 스케줄 관리
+// 회원관리
 import UserManagement from "../pages/integrated/UserManagement.vue"; // 사용자 관리
 import AdminManagement from "../pages/integrated/AdminManagement.vue"; // 관리자 관리
 import DepartmentManagement from "../pages/integrated/DepartmentManagement.vue"; // 부서 관리
 import DBConnectionList from "../pages/dbConnection/DBConnectionList.vue"; // 연계정보 관리
+// 정보관리
+import myPage from "../pages/user/myPage.vue"; // 내 정보 관리
+import myPagePwd from "../pages/user/myPagePwd.vue"; // 비밀번호 변경
 
+// 미분류
 import DBConnectionDetail from "../pages/dbConnection/DBConnectionDetail.vue";
-import FileManagement from "../pages/data/FileManagement.vue";
 import HostManagement from "../pages/data/HostManagement.vue";
-import DataManagement from "../pages/data/DataPostManagement.vue";
 import InsertDataPost from "../pages/data/InsertDataPost.vue";
 import DataPostDetail from "../pages/data/DataPostDetail.vue";
 import DataEditView from "../pages/data/DataEditView.vue";
 import TermManagement from "../pages/meta/TermManagement.vue";
 import DataMetaManagement from "../pages/meta/DataMetaManagement.vue";
-import ScheduleManagement from "../pages/schedule/ScheduleManagement.vue";
-import ScheduleLogManagement from "../pages/schedule/ScheduleLogManagement.vue";
 import Push from "../pages/push/Push.vue";
 import InsertDBConnection from "../pages/dbConnection/InsertDBConnection.vue";
 import CustomSelectList from "../pages/custom/CustomSelectList.vue";
 import CustomSelectOne from "../pages/custom/CustomSelectOne.vue";
 import CustomInsert from "../pages/custom/CustomInsert.vue";
 import CustomInsertDev from "../pages/custom/CustomInsertDev.vue";
-import myPage from "../pages/user/myPage.vue";
-import myPagePwd from "../pages/user/myPagePwd.vue";
 import OpenApiList from "../pages/openapi/OpenApiList.vue";
 import OpenApiInsert from "../pages/openapi/OpenApiInsert.vue";
 import OpenApiSelectListOne from "../pages/openapi/OpenApiSelectListOne.vue";
@@ -53,7 +56,9 @@
 
 
 const routes = [
-  // 통합관리
+  // 작업관리
+  { path: "/scheduleManagement.page", name: "ScheduleManagement", component: ScheduleManagement }, // 작업 스케줄 관리
+  // 회원관리
   { path: "/user.page", name: "User", component: UserManagement }, // 사용자 관리
   { path: "/adminManagement.page", name: "AdminManagement", component: AdminManagement }, // 관리자 관리
   { path: "/departmentManagement.page", name: "Department", component: DepartmentManagement }, // 부서 관리
@@ -67,8 +72,6 @@
   { path: "/DataEditView.page", name: "DataEditView", component: DataEditView },
   { path: "/termManagement.page", name: "TermManagement", component: TermManagement },
   { path: "/dataMetaManagement.page", name: "DataMetaManagement", component: DataMetaManagement },
-  { path: "/scheduleManagement.page", name: "ScheduleManagement", component: ScheduleManagement },
-  { path: "/scheduleLogManagement.page", name: "ScheduleLogManagement", component: ScheduleLogManagement },
   { path: "/push.page", name: "Push", component: Push },
   { path: "/insertDBConnection.page", name: "InsertDBConnection", component: InsertDBConnection },
   { path: "/DBConnectionDetail.page", name: "DBConnectionDetail", component: DBConnectionDetail },
client/views/pages/data/datapost/DataPostManagerMain.vue
--- client/views/pages/data/datapost/DataPostManagerMain.vue
+++ client/views/pages/data/datapost/DataPostManagerMain.vue
@@ -18,11 +18,25 @@
           <div class="flex justify-end">
             <div class="search-bar">
               <div class="flex justify-end align-center" style="gap: 5px">
-                <input type="date" name="start-date" id="start-date" class="square-date"
-                  :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value" />
+                <input
+                  type="date"
+                  name="start-date"
+                  id="start-date"
+                  class="square-date"
+                  :class="{ 'date-placeholder': false }"
+                  data-placeholder="날짜 선택"
+                  v-model="search_date.value"
+                />
                 <span>~</span>
-                <input type="date" name="end-date" id="end-date" class="square-date"
-                  :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value2" />
+                <input
+                  type="date"
+                  name="end-date"
+                  id="end-date"
+                  class="square-date"
+                  :class="{ 'date-placeholder': false }"
+                  data-placeholder="날짜 선택"
+                  v-model="search_date.value2"
+                />
                 <select class="square-select" v-model="search_data1.value">
                   <option :value="null">공개여부</option>
                   <option :value="true">공개</option>
@@ -30,29 +44,54 @@
                 </select>
                 <select class="square-select" v-model="search_data2.value">
                   <option :value="null">카테고리</option>
-                  <option v-for="(item, indx) in categoryList" :key="indx" :value="item.cmmnCode">{{
-                    item.codeNm }}</option>
+                  <option
+                    v-for="(item, indx) in categoryList"
+                    :key="indx"
+                    :value="item.cmmnCode"
+                  >
+                    {{ item.codeNm }}
+                  </option>
                 </select>
-                <select name="" id="searchItm1" class="square-select" v-model="search_data3.key">
+                <select
+                  name=""
+                  id="searchItm1"
+                  class="square-select"
+                  v-model="search_data3.key"
+                >
                   <option :value="null">검색조건</option>
                   <option value="post_sj">제목</option>
                   <option value="post_dc">내용</option>
                 </select>
                 <div class="flex justify-end align-center no-gutter">
-                  <input type="text" class="square-input flex90" placeholder="Search" v-model="search_data3.value"
-                    v-on:keyup.enter="searchData()" />
-                  <button class="square-button blue-btn flex10" @click="searchData()">
-                    <svg-icon type="mdi" :path="this.$getIconPath()" class="square-icon"></svg-icon>
+                  <input
+                    type="text"
+                    class="square-input flex90"
+                    placeholder="Search"
+                    v-model="search_data3.value"
+                    v-on:keyup.enter="searchData()"
+                  />
+                  <button
+                    class="square-button blue-btn flex10"
+                    @click="searchData()"
+                  >
+                    <svg-icon
+                      type="mdi"
+                      :path="this.$getIconPath()"
+                      class="square-icon"
+                    ></svg-icon>
                   </button>
                 </div>
               </div>
             </div>
           </div>
-          <div class="content-zone" style="overflow: auto;">
+          <div class="content-zone" style="overflow: auto">
             <div class="table-zone">
               <div class="list-info flex justify-between align-center">
                 <div class="count-zone">
-                  <p>총<span>{{ search.totalRows }}</span>건</p>
+                  <p>
+                    총<span>{{ search.totalRows }}</span
+                    >건
+                  </p>
                 </div>
                 <div class="cunt-selectZone">
                   <select name="" id="">
@@ -83,9 +122,18 @@
                 </thead>
                 <tbody>
                   <template v-if="dataPostList.length > 0">
-                    <tr v-for="(item, indx) in dataPostList" :key="indx" @click="selectPost(item)">
-                      <td>{{ search.totalRows - indx - (search.currentPage - 1) * search.perPage
-                        }}</td>
+                    <tr
+                      v-for="(item, indx) in dataPostList"
+                      :key="indx"
+                      @click="selectPost(item)"
+                    >
+                      <td>
+                        {{
+                          search.totalRows -
+                          indx -
+                          (search.currentPage - 1) * search.perPage
+                        }}
+                      </td>
                       <td>{{ item.post_sj }}</td>
                       <td>{{ item.user_nm }}</td>
                       <td>{{ item.dept_nm }}</td>
@@ -98,12 +146,19 @@
                   </tr>
                 </tbody>
               </table>
-              <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage"
-                :totalCount="search.totalRows" :maxRange="5" :click="searchData" />
+              <PaginationButton
+                v-model:currentPage="search.currentPage"
+                :perPage="search.perPage"
+                :totalCount="search.totalRows"
+                :maxRange="5"
+                :click="searchData"
+              />
             </div>
           </div>
           <div class="flex justify-end">
-            <button class="blue-btn small-btn" @click="createDataPost">데이터 등록</button>
+            <button class="blue-btn small-btn" @click="createDataPost">
+              데이터 등록
+            </button>
           </div>
         </div>
       </div>
@@ -116,9 +171,12 @@
 import PageNavigation from "../../../component/PageNavigation.vue";
 import SvgIcon from "@jamescoyle/vue-icon";
 import PaginationButton from "../../../component/PaginationButton.vue";
-import moment from 'moment';
+import moment from "moment";
 
 export default {
+  props: {
+    type: String,
+  },
   data() {
     return {
       dataPostList: [],
@@ -152,6 +210,10 @@
     },
 
     selectPost: function (item) {
+      if (this.type == "modal") {
+        this.$emit("onSelected", item.dataset_post_id);
+        return;
+      }
       this.$router.push({
         name: "DataPostDetail",
         query: { datapost: item.dataset_post_id },
@@ -164,8 +226,8 @@
       let check = false;
       let authList = vm.$store.state.loginUser.user_auth;
       for (let auth of authList) {
-        if (auth == 'ROLE_ADMIN') {
-          check = true
+        if (auth == "ROLE_ADMIN") {
+          check = true;
           break;
         }
       }
@@ -176,7 +238,7 @@
           type: "string",
           value: check + "/" + vm.$store.state.loginUser.user_id,
           value2: vm.$store.state.loginUser.dept_code,
-        })
+        });
       }
 
       axios({
@@ -202,11 +264,13 @@
         });
     },
     setDateRange(dataList) {
-      const dates = dataList.map(item => item.creat_dt).sort();
-      
+      const dates = dataList.map((item) => item.creat_dt).sort();
+
       if (dates.length > 0) {
-        this.search_date.value = moment(dates[0]).format('YYYY-MM-DD');
-        this.search_date.value2 = moment(dates[dates.length - 1]).format('YYYY-MM-DD');
+        this.search_date.value = moment(dates[0]).format("YYYY-MM-DD");
+        this.search_date.value2 = moment(dates[dates.length - 1]).format(
+          "YYYY-MM-DD"
+        );
       }
     },
     // 초기화
 
client/views/pages/schedule/CustomNode.vue (deleted)
--- client/views/pages/schedule/CustomNode.vue
@@ -1,70 +0,0 @@
-<template>
-  <div class="node">
-    <div class="header">
-      <button @click="$emit('openSetup', node.id)">
-        <img src="../../../resources/img/option.png" alt="옵션" />
-      </button>
-      <button
-        v-if="node.label !== 'DATASET_UPDATE'"
-        @click="$emit('removeNode', node.id)"
-      >
-        <img src="../../../resources/img/delete.png" alt="삭제" />
-      </button>
-    </div>
-    <hr />
-    <div class="content">
-      <h3>{{ node.label }}</h3>
-    </div>
-    <Handle
-      v-if="node.label !== 'DATASET_UPDATE'"
-      type="source"
-      position="right"
-    />
-    <Handle
-      v-if="node.label === 'DATASET_UPDATE' || node.label !== 'DATASET_UPDATE'"
-      type="target"
-      position="left"
-    />
-  </div>
-</template>
-
-<script setup>
-import { Handle, Position } from "@vue-flow/core";
-</script>
-
-<script>
-export default {
-  components: { Handle, Position },
-  props: {
-    node: Object,
-  },
-};
-</script>
-
-<style scoped>
-.node {
-  border: 1px solid #ddd;
-  border-radius: 5px;
-  padding: 20px;
-  background-color: #f9f9f9;
-  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
-  width: 300px;
-  height: 150px;
-}
-.header {
-  display: flex;
-  justify-content: flex-end;
-}
-.header button {
-  width: 20px;
-  height: 20px;
-  margin: 0 0 5px 10px;
-}
-.header button img {
-  width: 100%;
-  height: 100%;
-}
-.content {
-  margin-top: 20px;
-}
-</style>(No newline at end of file)
 
client/views/pages/schedule/ScheduleLogManagement.vue (deleted)
--- client/views/pages/schedule/ScheduleLogManagement.vue
@@ -1,354 +0,0 @@
-<template>
-  <div class="container">
-    <div class="content">
-      <div class="content-titleZone flex justify-between align-center">
-        <p class="box-title">다이어그램 목록</p>
-        <div class="search-bar">
-          <div class="flex justify-end align-center">
-            <input
-              type="date"
-              name="start-date"
-              id="start-date"
-              class="square-date"
-              :class="{ 'date-placeholder': false }"
-              data-placeholder="날짜 선택"
-              v-model="search_date.value"
-              @change="searchData()"
-            />
-            <span class="coupler">~</span>
-            <input
-              type="date"
-              name="end-date"
-              id="end-date"
-              class="square-date ml5"
-              :class="{ 'date-placeholder': false }"
-              data-placeholder="날짜 선택"
-              v-model="search_date.value2"
-              @change="searchData()"
-            />
-            <select
-              class="square-select ml5"
-              v-model="search_data1.value"
-              @change="searchData()"
-            >
-              <option :value="null">상태 전체</option>
-              <option value="success">성공</option>
-              <option value="fail">실패</option>
-            </select>
-          </div>
-        </div>
-      </div>
-      <div class="table-zone">
-        <table class="list-table">
-          <thead>
-            <tr>
-              <th>번호</th>
-              <th>총 노드수</th>
-              <th>실패 노드수</th>
-              <th>실패 노드명</th>
-              <th>실행일자</th>
-              <th>상태</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr
-              v-for="(rowdata, dataIndex) in logdata"
-              :key="dataIndex"
-              @click="handleRowClick(rowdata.log_id)"
-            >
-              <td>{{ dataIndex + 1 }}</td>
-              <td>{{ rowdata.total_node_count }}</td>
-              <td>{{ rowdata.fail_node_count }}</td>
-              <td>{{ rowdata.fail_node_name }}</td>
-              <td>{{ $getFullTime(rowdata.operation_date) }}</td>
-              <td>
-                <p
-                  v-if="rowdata.state_info === 'success'"
-                  class="flex justify-center align-center state green icon-area"
-                >
-                  <svg-icon
-                    type="mdi"
-                    :width="21"
-                    :height="21"
-                    :path="checkPath"
-                  ></svg-icon>
-                  <span>성공</span>
-                </p>
-                <p
-                  v-else-if="rowdata.state_info === 'fail'"
-                  class="flex justify-center align-center state red"
-                >
-                  <svg-icon
-                    type="mdi"
-                    :width="21"
-                    :height="21"
-                    :path="xPath"
-                  ></svg-icon>
-                  <span>실패</span>
-                </p>
-              </td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
-      <PaginationButton
-        v-model:currentPage="search.currentPage"
-        :perPage="search.perPage"
-        :totalCount="search.totalRows"
-        :maxRange="5"
-        :click="searchData"
-      />
-    </div>
-  </div>
-  <!-- 모달 -->
-  <div v-if="isModalOpen" class="modal-wrapper">
-    <div class="modal-container list-modal">
-      <div class="modal-title">
-        <div class="flex justify-between align-center">
-          <h2 class="main-title flex80">로그 상세보기</h2>
-          <button class="close-btn" @click="closeModal">
-            <svg-icon
-              type="mdi"
-              :width="20"
-              :height="20"
-              :path="closePath"
-            ></svg-icon>
-          </button>
-        </div>
-      </div>
-      <div class="modal-content-monthly" v-show="modalType == 'form-modal'">
-        <div class="flex justify-start align-center">
-          <div class="cunt-selectZone">
-            <p class="box-title">
-              로그분류
-              <select
-                v-model="searchdetail_data.value"
-                @change="searchDetailData"
-              >
-                <option :value="null">상태 전체</option>
-                <option value="success">성공</option>
-                <option value="fail">실패</option>
-              </select>
-            </p>
-          </div>
-        </div>
-        <table class="list-table blue">
-          <thead>
-            <tr>
-              <th>번호</th>
-              <th>작동일시</th>
-              <th>작동완료일시</th>
-              <th>노드명</th>
-              <th>호스트명</th>
-              <th>로그분류</th>
-              <th>로그메세지</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr v-for="(rowdata, dataIndex) in detaillogdata" :key="dataIndex">
-              <td>{{ dataIndex + 1 }}</td>
-              <td>{{ $getFullTime(rowdata.operation_date) }}</td>
-              <td>{{ $getFullTime(rowdata.operation_success_date) }}</td>
-              <td>{{ rowdata.node_name }}</td>
-              <td>{{ rowdata.creat_id }}</td>
-              <td>{{ rowdata.log_category }}</td>
-              <td class="text-over" title="-">{{ rowdata.log_message }}</td>
-            </tr>
-          </tbody>
-        </table>
-        <PaginationButton
-          v-model:currentPage="searchdetail.currentPage"
-          :perPage="searchdetail.perPage"
-          :totalCount="searchdetail.totalRows"
-          :maxRange="5"
-          :click="detaillogSelectListAll"
-        />
-      </div>
-      <div class="modal-end flex justify-end">
-        <button
-          v-show="modalType === 'form-modal'"
-          class="blue-btn small-btn"
-          @click="closeModal"
-        >
-          확인
-        </button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import PaginationButton from "../../component/PaginationButton.vue";
-import SvgIcon from "@jamescoyle/vue-icon";
-import { mdiMagnify } from "@mdi/js";
-import { mdiClose } from "@mdi/js";
-import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
-import DefaultSearchBar from "../../template/templateElement/DefaultSearchBar.vue";
-import { useRoute } from "vue-router";
-import axios from "axios";
-export default {
-  components: {
-    PaginationButton: PaginationButton,
-    SvgIcon: SvgIcon,
-    DefaultSearchBar: DefaultSearchBar,
-  },
-  props: {
-    diagram_id: {
-      type: String, // 또는 String, 실제 타입에 맞게 설정
-      required: true,
-    },
-  },
-  data() {
-    return {
-      // 검색
-      search: this.$getDefaultSerchVO(),
-      search_date: this.$getDefaultSerchItem("operation_date", "dates"),
-      search_data1: this.$getDefaultSerchItem("state_info", "string"),
-      search_data2: {
-        key: "diagram_id",
-        value: this.diagram_id,
-      },
-
-      //상세보기 검색
-      searchdetail: this.$getDefaultSerchVO(),
-      searchdetail_data: this.$getDefaultSerchItem("log_category", "string"),
-      searchdetail_data1: {
-        key: null,
-        value: null,
-      },
-
-      //검색 데이터
-      searchPath: mdiMagnify,
-      startDate: true,
-      endDate: true,
-
-      closePath: mdiClose,
-      isModalOpen: false,
-      modalType: "form-modal",
-      checkPath: mdiCheckCircle,
-      xPath: mdiCloseCircle,
-      logdata: [],
-      detaillogdata: [],
-      logId: null,
-      diagram_id: this.diagram_id,
-
-      //현재 라우터의 정보
-      route: useRoute(),
-      // 페이지 경로 목록
-      pageList: [
-        { path: "/", name: "대시보드" },
-        { path: "/fileManagement.page", name: "파일 관리" },
-        { path: "/hostManagement.page", name: "호스트 관리" },
-        { path: "/scheduleManagement.page", name: "작업 스케줄 관리" },
-        { path: "/scheduleLogManagement.page", name: "로그 관리" },
-      ],
-    };
-  },
-  mounted() {
-    this.searchData();
-  },
-  watch: {
-    diagram_id(v) {
-      this.search_data2.value = v;
-      this.searchData();
-    },
-  },
-  methods: {
-    searchData() {
-      const vm = this;
-      let data = vm.search;
-      data.searchObjectList = [];
-      data.searchObjectList.push(this.search_date);
-      data.searchObjectList.push(this.search_data1);
-      data.searchObjectList.push(this.search_data2);
-      axios({
-        url: "/diagram/logSelectListAll.json",
-        method: "post",
-        headers: { "Content-Type": "application/json; charset=UTF-8" },
-        data: data,
-      })
-        .then(function (response) {
-          vm.search.currentPage = response.data.resultData.searchVO.currentPage;
-          vm.search.order = response.data.resultData.searchVO.order;
-          vm.search.orderASC = response.data.resultData.searchVO.orderASC;
-          vm.search.perPage = response.data.resultData.searchVO.perPage;
-          vm.search.searchObjectList = [];
-          vm.search.totalRows = response.data.resultData.searchVO.totalRows;
-          vm.logdata = response.data.resultData.logList;
-        })
-        .catch(function (error) {
-          vm.$showAlert(
-            "오류",
-            "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다."
-          );
-        });
-    },
-    searchDetailData() {
-      const vm = this;
-      let datadetail = vm.searchdetail;
-      datadetail.searchObjectList = [];
-      datadetail.searchObjectList.push(this.searchdetail_data);
-
-      this.detaillogSelectListAll();
-    },
-    openModal() {
-      this.isModalOpen = true;
-    },
-    closeModal() {
-      this.isModalOpen = false;
-    },
-    pathName() {
-      for (let i = 0; i < this.pageList.length; i++) {
-        if (this.route.path == this.pageList[i]["path"]) {
-          return this.pageList[i]["name"];
-        }
-      }
-      return "대시보드";
-    },
-
-    handleRowClick(logId) {
-      this.logId = logId;
-      this.detaillogSelectListAll();
-      this.openModal();
-    },
-
-    detaillogSelectListAll() {
-      const vm = this;
-      let datadetail = vm.searchdetail;
-      datadetail.searchObjectList = [];
-      datadetail.searchObjectList.push(this.searchdetail_data);
-      vm.searchdetail_data1.key = "log_id";
-      vm.searchdetail_data1.value = vm.logId;
-      datadetail.searchObjectList.push(this.searchdetail_data1);
-
-      axios({
-        url: "/diagram/detaillogSelectListAll.json",
-        method: "post",
-        headers: {
-          "Content-Type": "application/json; charset=UTF-8",
-        },
-        data: datadetail,
-      })
-        .then(function (response) {
-          vm.searchdetail.currentPage = response.data.resultData.searchVO.currentPage;
-          vm.searchdetail.order = response.data.resultData.searchVO.order;
-          vm.searchdetail.orderASC = response.data.resultData.searchVO.orderASC;
-          vm.searchdetail.perPage = response.data.resultData.searchVO.perPage;
-          vm.searchdetail.searchObjectList = [];
-          vm.searchdetail.totalRows = response.data.resultData.searchVO.totalRows;
-          vm.detaillogdata = response.data.resultData.logList;
-        })
-        .catch(function (error) {
-          alert("상품 주문에 오류가 발생했습니다.");
-        });
-    },
-  },
-};
-</script>
-
-<style scoped>
-.navigate_bar {
-  padding: 10px 0px;
-  font-size: 1.3rem;
-}
-</style>(No newline at end of file)
client/views/pages/schedule/ScheduleManagement.vue
--- client/views/pages/schedule/ScheduleManagement.vue
+++ client/views/pages/schedule/ScheduleManagement.vue
@@ -2,13 +2,14 @@
   <div class="container">
     <div class="page-titleZone flex justify-between align-center">
       <p class="main-title flex80">스케줄 관리</p>
+      <PageNavigation />
     </div>
     <div class="content-wrap">
       <div class="content content-box flex-column no-gutter">
         <div class="row flex50">
           <div class="flex100">
             <div class="content-titleZone flex justify-between align-center">
-              <p class="box-title">다이어그램 목록</p>
+              <p class="box-title">스케줄 목록</p>
               <div class="search-bar flex justify-end align-center">
                 <input
                   type="date"
@@ -61,89 +62,11 @@
               </div>
             </div>
             <div class="table-zone">
-              <table class="list-table">
-                <colgroup>
-                  <col style="width: 5%" />
-                  <col style="width: 5%" />
-                  <col style="width: 25%" />
-                  <col style="width: 15%" />
-                  <col style="width: 20%" />
-                  <col style="width: 20%" />
-                  <col style="width: 15%" />
-                </colgroup>
-                <thead>
-                  <tr>
-                    <th>
-                      <input
-                        type="checkbox"
-                        v-model="isChkAll"
-                        @change="fnChkAll()"
-                      />
-                    </th>
-                    <th>번호</th>
-                    <th>제목</th>
-                    <th>작성자</th>
-                    <th>반복주기</th>
-                    <th>등록일</th>
-                    <th>상태</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  <template v-if="diagramList.length > 0">
-                    <tr
-                      v-for="(diagram, dataIndex) in diagramList"
-                      :key="diagram"
-                      @click="fnDiagramDetail(diagram.diagram_id)"
-                    >
-                      <td>
-                        <input
-                          type="checkbox"
-                          :value="diagram"
-                          v-model="selectedDiagramList"
-                          @click.stop=""
-                          @change="fnChangeCheckList"
-                        />
-                      </td>
-                      <td>{{ dataIndex + 1 }}</td>
-                      <td>{{ diagram.diagram_nm }}</td>
-                      <td>{{ diagram.creat_id }}</td>
-                      <td>{{ diagram.cron_chrctr }}</td>
-                      <td>{{ $getFullTime(diagram.creat_dt) }}</td>
-                      <td>
-                        <div
-                          v-if="diagram.schdul_sttus === 'run'"
-                          class="flex justify-center align-center state green icon-area"
-                        >
-                          <svg-icon
-                            type="mdi"
-                            :width="21"
-                            :height="21"
-                            :path="checkPath"
-                            class="mr5"
-                          ></svg-icon>
-                          <span>진행</span>
-                        </div>
-                        <div
-                          v-else-if="diagram.schdul_sttus === 'stop'"
-                          class="flex justify-center align-center state red"
-                        >
-                          <svg-icon
-                            type="mdi"
-                            :width="21"
-                            :height="21"
-                            :path="xPath"
-                            class="mr5"
-                          ></svg-icon>
-                          <span>중단</span>
-                        </div>
-                      </td>
-                    </tr>
-                  </template>
-                  <tr v-else>
-                    <td colspan="7">등록된 데이터가 없습니다.</td>
-                  </tr>
-                </tbody>
-              </table>
+              <ScheduleList
+                :list="scheduleList"
+                :search="search"
+                @fnSelectDiagram="fnDiagramDetail"
+              />
             </div>
             <div class="flex justify-end">
               <button
@@ -162,166 +85,58 @@
                 선택 삭제
               </button>
             </div>
-            <PaginationButton
-              v-model:currentPage="search.currentPage"
-              :perPage="search.perPage"
-              :totalCount="search.totalRows"
-              :maxRange="5"
-              :click="fnDiagramList"
-            />
           </div>
         </div>
         <div class="row flex50">
           <div class="flex justify-between align-start content-box">
             <div class="flex30 content-box">
               <div class="left-content border flex100">
-                <div class="content-titleZone">
-                  <p class="box-title">다이어그램 등록</p>
-                </div>
-                <ScheculerCreateComp
-                  :schedule="schedule"
-                  @get-edit-scheduling="getEditScheduling"
-                />
-                <div class="flex justify-end" id="buttonZone">
-                  <button
-                    class="blue-btn small-btn"
-                    id="registerButton"
-                    @click="fnDataInsert"
-                  >
-                    <span v-if="!isSelectedDiagram">등록</span>
-                    <span v-else>수정</span>
-                  </button>
-                  <button
-                    class="blue-border-btn small-btn"
-                    id="resetButton"
-                    @click="fnFormReset"
-                  >
-                    초기화
-                  </button>
-                </div>
+                <ScheduleForm />
               </div>
             </div>
             <div class="right-content flex70 form-box">
-              <div class="node-zone flex70" style="height: 94%">
-                <ScheduleLogManagement
-                  v-if="isLogListView"
-                  :diagram_id="diagram.diagram_id"
-                />
-                <VueFlowZone
-                  v-else
-                  :nodes="diagram.nodes"
-                  :edges="diagram.edges"
-                  @changeDiagram="changeDiagram"
-                />
-              </div>
-              <div class="flex justify-end">
-                <button
-                  v-if="!isLogListView"
-                  class="blue-border-btn small-btn"
-                  @click="fnOpenAddModal"
-                >
-                  노드 추가
-                </button>
-                <button
-                  v-if="!isLogListView && diagram.nodes.length > 1"
-                  class="blue-border-btn small-btn"
-                  @click="operatorNow"
-                >
-                  즉시계산
-                </button>
-                <button
-                  v-if="!isLogListView"
-                  class="blue-border-btn small-btn"
-                  @click="resetNode"
-                >
-                  초기화
-                </button>
-                <button
-                  v-if="isLogListView"
-                  class="blue-btn small-btn"
-                  @click="fnNodeFlowView"
-                >
-                  다이어그램 보기
-                </button>
-                <button
-                  v-if="!isLogListView && diagram.nodes.length > 1"
-                  class="blue-btn small-btn"
-                  @click="fnLogListView"
-                >
-                  로그 보기
-                </button>
-              </div>
+              <DiagramList
+                v-if="editMode == 'vueFlow'"
+                :currentDiagram="currentSchedule.diagram"
+              />
+              <DiagramLogList
+                v-if="editMode == 'logList'"
+                :schdulLogs="currentSchedule.schdulLogs"
+              />
             </div>
           </div>
         </div>
       </div>
     </div>
   </div>
-  <!-- 노드 추가 모달 -->
-  <div v-show="isAddModalOpen" class="modal-wrapper">
-    <div class="modal-container small-modal">
-      <div class="modal-title">
-        <div class="flex justify-between align-center">
-          <h2>추가할 노드 선택</h2>
-          <button class="close-btn" @click="fnCloseAddModal">X</button>
-        </div>
-      </div>
-      <div class="modal-content-monthly">
-        <div class="justify-center aling-center">
-          <select class="square-select full-select" v-model="selectItem">
-            <option
-              v-for="(nodeType, indexNTL) of nodeTypeList"
-              :key="indexNTL"
-              :value="nodeType.cmmnCode"
-            >
-              {{ nodeType.codeNm }}
-            </option>
-          </select>
-        </div>
-      </div>
-      <div class="modal-end flex justify-end">
-        <button class="blue-btn small-btn" @click="fnAddNodeCheckBtn">
-          확인
-        </button>
-      </div>
-    </div>
-  </div>
 </template>
-
 <script>
 import axios from "axios";
+// icon용 svg import
 import SvgIcon from "@jamescoyle/vue-icon";
-import { mdiMagnify } from "@mdi/js";
-import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
-import DefaultSearchBar from "../../template/templateElement/DefaultSearchBar.vue";
-import PaginationButton from "../../component/PaginationButton.vue";
-import ScheculerCreateComp from "../../component/ScheculerCreateComp.vue";
-// vueFlowZone
-import VueFlowZone from "./VueFlowZone.vue";
-import ScheduleLogManagement from "./ScheduleLogManagement.vue";
-// vueFlowZone - btns
-import DatabaseConnection from "../../component/connection/itm/databaseConnection.vue";
-import apiConnection from "../../component/connection/itm/apiConnection.vue";
-import datasetUpdate from "../../component/connection/itm/datasetUpdate.vue";
-import fileSelectDiagram from "../../component/connection/itm/fileSelectDiagram.vue";
-import DataFilter from "../../component/connection/itm/dataFilter.vue";
+import { mdiMagnify, mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
+// 컴포넌트 import
+import PageNavigation from "../../component/PageNavigation.vue";
+
+import ScheduleList from "../../component/scheduleComponent/ScheduleList.vue";
+import ScheduleForm from "../../component/scheduleComponent/ScheduleForm.vue";
+
+import DiagramList from "../../component/scheduleComponent/DiagramList.vue";
+import DiagramLogList from "../../component/scheduleComponent/DiagramLogList.vue";
 
 export default {
   components: {
-    SvgIcon: SvgIcon,
-    PaginationButton,
-    DefaultSearchBar,
-    ScheculerCreateComp,
-    // vueFlowZone
-    VueFlowZone,
-    ScheduleLogManagement,
-    // vueFlowZone - btns
-    DatabaseConnection,
-    apiConnection,
-    datasetUpdate,
-    fileSelectDiagram,
-    DataFilter,
+    // 공통
+    SvgIcon,
+    PageNavigation,
+    // 일반
+    ScheduleList,
+    ScheduleForm,
+
+    DiagramList,
+    DiagramLogList,
   },
+
   data() {
     return {
       // svg
@@ -335,440 +150,19 @@
       search_data1: this.$getDefaultSerchItem("schdul_sttus", "string"),
       search_data2: this.$getDefaultSerchItem(null, "string"),
 
-      // default
-      diagramList: [],
-      diagram: {
-        nodes: [],
-        edges: [],
-      },
-      schedule: {},
-      isSelectedDiagram: false,
+      // 목록
+      scheduleList: [],
 
-      // 전체 선택 여부 (true:전체선택 / false:전체선택해제)
-      isChkAll: false,
-      selectedDiagramList: [], // 다이어그램 목록에서 선택한 다이어그램의 목록
+      // 스케줄 등록
 
-      showRepeatSettings: true, // 반복주기
-      logId: null, // 로그 페이지에서 넘어온 ID
-      totalNode: null, // 총 노드 수
-      repetselect: null, // 반복주기 타입
-
-      // 노드 타입
-      originalNodeTypeList: [],
-      nodeTypeList: [],
-
-      // 다이어그램 로그 화면 여부(true:열림/false:닫힘)
-      isLogListView: false,
-
-      // 노드 추가
-      isAddModalOpen: false, // 노드 추가 모달 열림 여부(true:열림/false:닫힘)
-      selectItem: "DB_READ", // 노드 추가값 (기본:DB_READ)
-
-      // 노드 수정
-      isSetupModalOpen: false, // 노드 설정 모달 열림 여부(true:열림/false:닫힘)
-      selectNode: {}, // 선택된 노드
+      // 다이어그램 뷰어
+      editMode: "vueFlow",
+      currentSchedule: Object.assign({}, this.$getDefaultJobGroup().schedule),
     };
   },
-  mounted() {
-    this.resetNode();
-    this.fnDiagramList();
-    this.fnSearchCommonCode();
-  },
-  methods: {
-    //초기화 함수
-    resetNode() {
-      this.diagram.nodes = []; // 노드 목록 비우기
-      this.diagram.edges = []; // 엣지 목록 비우기
 
-      // 엔드 노드(datasetUpdate) 추가
-      const node = Object.assign({}, this.$getDefaultJobGroup().node);
-      node.id = Date.now().toString();
-      node.node_id = node.id;
-      node.label = "DATASET_UPDATE";
-      node.node_name = "DATASET_UPDATE";
-      node.node_type = "DATASET_UPDATE";
-      node.itm = Object.assign({}, this.$getDefaultJobGroup().jobItemGroup);
-      node.center_x = 1050;
-      node.center_y = 25;
-      node.position = {
-        x: node.center_x,
-        y: node.center_y,
-      };
-      this.diagram.nodes.push(node);
-    },
+  mounted() {},
 
-    // 다이어그램 목록 조회
-    fnDiagramList() {
-      const vm = this;
-      // 데이터 세팅
-      let data = vm.search;
-      data.searchObjectList = []; // 초기화
-      data.searchObjectList.push(this.search_date);
-      data.searchObjectList.push(this.search_data1);
-      data.searchObjectList.push(this.search_data2);
-      // 실행
-      axios({
-        url: "/diagram/diagramListRead",
-        method: "post",
-        headers: { "Content-Type": "application/json; charset=UTF-8" },
-        data: data,
-      })
-        .then((response) => {
-          vm.diagramList = response.data.resultData.diagramList;
-          vm.search = response.data.resultData.searchVO;
-        })
-        .catch((error) => {
-          this.$showAlert(
-            "에러 발생",
-            "에러가 발생했습니다. 관리자에게 문의해 주세요."
-          );
-        });
-    },
-
-    // 노드 타입 목록 조회
-    async fnSearchCommonCode() {
-      this.originalNodeTypeList = await this.$getCommonCode("NODE_ITEM");
-      this.nodeTypeList = this.originalNodeTypeList;
-    },
-
-    // 다이어그램 전체 선택
-    fnChkAll() {
-      this.selectedDiagramList = []; // 초기화
-      if (this.isChkAll) {
-        this.selectedDiagramList = this.diagramList;
-      }
-    },
-    // 다이어그램 선택 여부 변경 시
-    fnChangeCheckList() {
-      if (this.selectedDiagramList.length == this.diagramList.length) {
-        this.isChkAll = true;
-      } else {
-        this.isChkAll = false;
-      }
-    },
-
-    // 선택 실행, 중단 버튼
-    fnChkSttus(schdul_sttus) {
-      const vm = this;
-      for (let schedule of this.selectedDiagramList) {
-        axios({
-          url:
-            "/schedule/scheduleSttusChange/" +
-            schedule.schdul_id +
-            "/" +
-            schdul_sttus,
-          method: "get",
-          headers: { "Content-Type": "application/json; charset=UTF-8" },
-        })
-          .then((response) => {
-            vm.$showAlert("수정", "수정이 완료 되었습니다.");
-            this.fnDiagramList(); // 전체 재조회
-            this.isChkAll = false; // 체크 박스 리셋
-            this.selectedDiagramList = [];
-          })
-          .catch((error) => {
-            vm.$showAlert(
-              "오류",
-              "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다."
-            );
-          });
-      }
-    },
-    // 선택 삭제 버튼
-    fnChkDel() {
-      const vm = this;
-      for (let schedule of this.selectedDiagramList) {
-        axios({
-          url: "/schedule/deleteSchedule/" + schedule.schdul_id,
-          method: "get",
-          headers: { "Content-Type": "application/json; charset=UTF-8" },
-        })
-          .then((response) => {
-            this.fnDiagramList(); // 전체 재조회
-            this.isChkAll = false; // 체크 박스 리셋
-            this.selectedDiagramList = [];
-          })
-          .catch((error) => {
-            vm.$showAlert(
-              "오류",
-              "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다."
-            );
-          });
-      }
-    },
-
-    // 다이어그램 상세 조회
-    fnDiagramDetail(diagramId) {
-      const vm = this;
-      // 실행
-      axios({
-        url: "/diagram/diagramDetailRead/" + diagramId,
-        method: "get",
-        headers: { "Content-Type": "application/json; charset=UTF8" },
-      })
-        .then((response) => {
-          vm.isSelectedDiagram = true;
-          vm.schedule = response.data.resultData.diagramDetailDTO.schedule;
-          let diagram = response.data.resultData.diagramDetailDTO.diagram;
-          for (let node of diagram.nodes) {
-            node.position = {};
-            node.position.x = node.center_x;
-            node.position.y = node.center_x;
-          }
-          for (let edge of diagram.edges) {
-            edge.id = edge.edge_id;
-          }
-          vm.diagram = diagram;
-        })
-        .catch((error) => {
-          vm.$showAlert(
-            "오류",
-            "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다."
-          );
-        });
-    },
-
-    // 노드 추가 버튼 (노드 추가 모달 열기)
-    fnOpenAddModal() {
-      this.nodeTypeList = this.originalNodeTypeList; // 초기화
-      for (let node of this.diagram.nodes) {
-        if (node["node_type"] == "DATASET_UPDATE") {
-          this.nodeTypeList = this.nodeTypeList.filter(
-            (nodeType) => nodeType.codeNm !== "DATASET_UPDATE"
-          );
-        }
-      }
-      this.isAddModalOpen = true;
-    },
-    // 노드 추가 모달 닫기
-    fnCloseAddModal() {
-      this.isAddModalOpen = false;
-      this.selectItem = "DB_READ"; // 초기화
-    },
-    // 아이디 생성
-    createId(number) {
-      let id = Math.floor(Math.random() * number) + 1;
-      return id.toString();
-    },
-    // 노드 추가
-    fnAddNodeCheckBtn() {
-      let node = Object.assign({}, this.$getDefaultJobGroup().node);
-      node.id = Date.now().toString();
-      node.node_id = node.id;
-      node.label = this.selectItem;
-      node.node_name = this.selectItem;
-      node.node_type = this.selectItem;
-      node.center_x = this.createId(500);
-      node.center_y = this.createId(500);
-      node.position = {
-        x: node.center_x,
-        y: node.center_y,
-      };
-      let tempData = JSON.parse(JSON.stringify(node));
-      tempData.data = null;
-      node.data = tempData;
-      switch (this.selectItem) {
-        case "DB_READ":
-          node.itm = Object.assign({}, this.$getDefaultJobGroup().connectionDb);
-          node.itm.type = "dbConnection";
-          break;
-        case "API_READ":
-          node.itm = Object.assign(
-            {},
-            this.$getDefaultJobGroup().connectionApi
-          );
-          break;
-        case "FILE_READ":
-          node.itm = Object.assign({}, this.$getDefaultJobGroup().fileRead);
-          break;
-        case "DATASET_READ":
-          node.itm = Object.assign({}, this.$getDefaultJobGroup().DatasetPost);
-          break;
-        case "DATA_FILTER":
-          node.itm = Object.assign({}, this.$getDefaultJobGroup().dataFilter);
-          break;
-        case "EHOJO_READ":
-          node.itm = Object.assign({}, this.$getDefaultJobGroup().connectionEhojo);
-          break;
-        case "DATASET_UPDATE":
-          node.itm = Object.assign({}, this.$getDefaultJobGroup().jobItemGroup);
-          break;
-      }
-      this.diagram.nodes.push(node);
-      // 모달 닫기
-      this.fnCloseAddModal();
-    },
-
-    // 다이어그램 유효성 검사
-    fnValidation() {
-      if (this.$isEmpty(this.schedule.sj)) {
-        this.$showAlert("경고", "제목이 비어있습니다. 제목을 입력해 주세요.");
-        return false;
-      }
-      // 엔드 노드에 연결된 노드가 없는 경우
-      let endNodeCnt = 0;
-      let endEdgeCnt = 0;
-      for (let node of this.diagram.nodes) {
-        if (node["node_type"] == "DATASET_UPDATE") {
-          endNodeCnt++;
-          for (let edge of this.diagram.edges) {
-            if (edge.target == node.id) {
-              endEdgeCnt++;
-            }
-          }
-        }
-      }
-      if (endNodeCnt < 1) {
-        this.$showAlert("오류 발생", "DATASET_UPDATE를 생성해 주세요.");
-        return false;
-      }
-      if (endEdgeCnt < 1) {
-        this.$showAlert("오류 발생", "DATASET_UPDATE에 노드를 연결해 주세요.");
-        return false;
-      }
-      return true;
-    },
-    // 다이어그램 등록
-    async fnDataInsert() {
-      const vm = this;
-      // 유효성 검사
-      if (!this.fnValidation()) {
-        return;
-      }
-      // 연결되지 않은 노드 삭제
-      let emptyEdgeCnt = 0;
-      let emptyEdgeNodeList = [];
-      for (let node of vm.diagram.nodes) {
-        let edgeCnt = 0;
-        for (let edge of vm.diagram.edges) {
-          if (node.id == edge.source || node.id == edge.target) {
-            edgeCnt++;
-          }
-        }
-        if (edgeCnt < 1) {
-          emptyEdgeCnt++;
-          emptyEdgeNodeList.push(node.id);
-        }
-      }
-      if (emptyEdgeCnt > 0) {
-        let isChecked = await vm.$showConfirm(
-          "경고",
-          "노드 전달 시, 연결되지 않은 노드는 삭제됩니다. 그래도 실행하시겠습니까?"
-        );
-        if (!isChecked) {
-          return;
-        } else {
-          for (let emptyEdgeNode of emptyEdgeNodeList) {
-            vm.diagram.nodes = vm.diagram.nodes.filter(
-              (node) => node.id !== emptyEdgeNode
-            );
-          }
-        }
-      }
-      // 데이터 세팅
-      let schedule = vm.schedule;
-      schedule.schdul_sttus = "run";
-      let diagram = vm.diagram;
-      for (let node of diagram.nodes) {
-        node.position = {};
-        node.position.x = node.center_x;
-        node.position.y = node.center_x;
-      }
-      for (let edge of diagram.edges) {
-        delete edge.id;
-      }
-      let url = null;
-      let message = null;
-      if (!vm.isSelectedDiagram) {
-        url = "/diagram/dataInsert.json";
-        message = "등록이 완료 되었습니다.";
-      } else {
-        url = "/diagram/dataEdit.json";
-        message = "수정이 완료 되었습니다.";
-      }
-      // 실행
-      axios({
-        url: url,
-        method: "post",
-        headers: { "Content-Type": "application/json; charset=UTF-8" },
-        data: {
-          schedule: schedule,
-          diagram: diagram,
-        },
-      })
-        .then((response) => {
-          vm.$showAlert("실행 결과", message);
-          this.fnDiagramList(); // 전체 재조회
-        })
-        .catch((error) => {
-          vm.$showAlert(
-            "오류",
-            "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다."
-          );
-        });
-    },
-    // 다이어그램 등록 폼 초기화
-    fnFormReset() {
-      this.schedule = {};
-    },
-
-    // 스케쥴 업데이트
-    getEditScheduling(title, dc, getCronStr, getCron, isCyclical) {
-      this.schedule.sj = title;
-      this.schedule.dc = dc;
-      this.schedule.cycle_at = isCyclical;
-      this.schedule.cron = getCron;
-      this.schedule.cron_chrctr = getCronStr;
-    },
-
-    //로그 페이지에서 ID를 가져온다
-    dataDiagram() {
-      this.logId = this.$route.query.receiveQuery;
-      return this.logId;
-    },
-
-    // 즉시계산
-    operatorNow() {
-      const vm = this;
-      // 데이터 세팅
-      let data = vm.diagram;
-      for (let edge of data.edges) {
-        delete edge.id;
-      }
-      // 실행
-      axios({
-        url: "/diagram/operatorNow.json",
-        method: "post",
-        headers: {
-          "Content-Type": "application/json; charset=UTF-8",
-        },
-        data: vm.diagram,
-      })
-        .then((response) => {
-          vm.$showAlert("결과내용", "데이터셋 업데이트를 완료했습니다.");
-        })
-        .catch((error) => {
-          vm.$showAlert(
-            "오류",
-            "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다."
-          );
-        });
-    },
-
-    // vueFlowZone 보기 변경 (log view 화면)
-    fnLogListView() {
-      this.isLogListView = true;
-    },
-    // vueFlowZone 보기 변경 (node flow 화면/디폴트)
-    fnNodeFlowView() {
-      this.isLogListView = false;
-    },
-
-    // 다이어그램 수정
-    changeDiagram(nodes, edges) {
-      this.diagram.nodes = nodes;
-      this.diagram.edges = edges;
-    },
-  },
+  methods: {},
 };
 </script>
(No newline at end of file)
 
client/views/pages/schedule/VueFlowZone.vue (deleted)
--- client/views/pages/schedule/VueFlowZone.vue
@@ -1,138 +0,0 @@
-<template>
-  <VueFlow :nodes="nodes" :edges="edges" :default-viewport="{ zoom: 0.75, position: { x: 0, y: 0 } }" :min-zoom="0.2" :max-zoom="4" :default-edge-options="{ type: 'smoothstep' }" @connect="nodeConnect" @node-drag-stop="fnMoveNode" @edge-double-click="fnDeleteEdge" @node-double-click="fnViewDiagramData">
-    <template #node-custom="node">
-      <CustomNode :node="node" @openSetup="fnOpenSetupModal" @removeNode="fnDeleteNode" />
-    </template>
-    <MiniMap />
-    <Controls />
-    <Background pattern-color="#aaa" :gap="8" />
-  </VueFlow>
-  <!-- 노드 셋업 모달 -->
-  <DatabaseConnection v-if="selectNode.node_type === 'DB_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" @closePopup="fnCloseSetupModal" @saveNodeData="fnUpdateSetup" />
-  <apiConnection v-if="selectNode.node_type === 'API_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" />
-  <fileSelectDiagram v-if="selectNode.node_type === 'FILE_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :mode="'fileRead'" @closePopup="fnCloseSetupModal" @saveNodeData="fnUpdateSetup" />
-  <datasetSelecter v-if="selectNode.node_type === 'DATASET_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" @closePopup="fnCloseSetupModal" @saveNodeData="fnUpdateSetup" />
-  <DataFilter v-if="selectNode.node_type === 'DATA_FILTER'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :nodes="nodes" :edges="edges" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" />
-  <datasetUpdate v-if="selectNode.node_type === 'DATASET_UPDATE'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :nodes="nodes" :edges="edges" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" />
-  <EhojoConnection v-if="selectNode.node_type === 'EHOJO_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :nodes="nodes" :edges="edges" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" />
-</template>
-<script>
-import CustomNode from "./CustomNode.vue";
-// vue-flow api (삭제시 동작 안함)
-import { Background } from "@vue-flow/background";
-import { Panel, VueFlow } from "@vue-flow/core";
-import { MiniMap } from "@vue-flow/minimap";
-import { Controls } from "@vue-flow/controls";
-//
-import DatabaseConnection from "../../component/connection/itm/databaseConnection.vue";
-import apiConnection from "../../component/connection/itm/apiConnection.vue";
-import datasetSelecter from "../../component/connection/itm/datasetSelecter.vue";
-import datasetUpdate from "../../component/connection/itm/datasetUpdate.vue";
-import fileSelectDiagram from "../../component/connection/itm/fileSelectDiagram.vue";
-import DataFilter from "../../component/connection/itm/dataFilter.vue";
-import EhojoConnection from "../../component/connection/EhojoConnection.vue";
-
-export default {
-  components: {
-    CustomNode,
-    // vue-flow api (삭제시 동작 안함)
-    Background,
-    Panel,
-    VueFlow,
-    MiniMap,
-    Controls,
-    //
-    DatabaseConnection,
-    apiConnection,
-    datasetSelecter,
-    datasetUpdate,
-    fileSelectDiagram,
-    DataFilter,
-    EhojoConnection
-  },
-  props: {
-    nodes: Array,
-    edges: Array,
-  },
-  data() {
-    return {
-      // 다이어그램 로그 화면 여부(true:열림/false:닫힘)
-      isLogListView: false,
-
-      // 노드 추가
-      isAddModalOpen: false, // 노드 추가 모달 열림 여부(true:열림/false:닫힘)
-      originalNodeTypeList: [],
-      nodeTypeList: [],
-      selectItem: "DB_READ", // 노드 추가값 (기본:DB_READ)
-
-      // 노드 수정
-      isSetupModalOpen: false, // 노드 설정 모달 열림 여부(true:열림/false:닫힘)
-      selectNode: {}, // 선택된 노드
-    };
-  },
-  methods: {
-    // 노드 위치 변경
-    fnMoveNode(moveNode) {
-      let nodeId = moveNode.node.id;
-      for (let node of this.nodes) {
-        if (node.id == nodeId) {
-          node.position.x = moveNode.node.position.x;
-          node.center_x = moveNode.node.position.x;
-          node.position.y = moveNode.node.position.y;
-          node.center_y = moveNode.node.position.y;
-        }
-      }
-    },
-
-    // 선을 연결하면 뜨는 이벤트
-    nodeConnect(event) {
-      let edge = {};
-      edge.id = Date.now().toString();
-      edge.edge_id = edge.id;
-      edge.source = event.source; // 시작노드
-      edge.target = event.target; // 끝 노드
-      this.edges.push(edge);
-    },
-
-    // 엣지 삭제
-    fnDeleteEdge(event) {
-      let edgeId = event.edge.id;
-      let edges = this.edges.filter((edge) => edge.id !== edgeId);
-      this.$emit("changeDiagram", this.nodes, edges);
-    },
-
-    // 노드 설정 모달 열기
-    fnOpenSetupModal(nodeId) {
-      for (let node of this.nodes) {
-        if (node.id == nodeId) {
-          this.selectNode = node;
-        }
-      }
-      this.isSetupModalOpen = true;
-    },
-    // 노드 설정 저장 후 닫기
-    fnUpdateSetup(jobItem) {
-      for (let node of this.nodes) {
-        if (node.id == jobItem.id) {
-          node = jobItem;
-        }
-      }
-      this.fnCloseSetupModal();
-    },
-    // 노드 설정 모달 닫기
-    fnCloseSetupModal() {
-      this.selectNode = {}; // 초기화
-      this.isSetupModalOpen = false;
-    },
-
-    // 노드 삭제
-    fnDeleteNode(nodeId) {
-      let nodes = this.nodes.filter((node) => node.id !== nodeId);
-      let edges = this.edges.filter(
-        (edge) => edge.source !== nodeId && edge.target !== nodeId
-      );
-      this.$emit("changeDiagram", nodes, edges);
-    },
-  },
-};
-</script>(No newline at end of file)
Add a comment
List