하관우 하관우 02-11
2025-02-11 하관우 moment 추가 파일관리 컴퍼넌트
@9905dfc1047e39bf9ea9d35af65868e8fef561e3
client/views/pages/data/FileManagement.vue
--- client/views/pages/data/FileManagement.vue
+++ client/views/pages/data/FileManagement.vue
@@ -13,1650 +13,24 @@
       <PageNavigation />
     </div>
     <div class="content-wrap">
-      <div class="content-box flex justify-between">
-        <div class="flex20 content-box">
-          <div class="left-content flex100 content-box">
-            <div class="content-box">
-              <div class="flex align-end  justify-between no-gutter">
-                <div class="mb10 flex70">
-                  <div class="content">
-                    <p class="box-title">호스트 선택</p>
-                  </div>
-                  <div>
-                    <select class="only full-select" v-model="selectedHostCodeData">
-                      <option :value="null" disabled>선택</option>
-                      <option v-for="(host, idx) in hostList" :key="idx" :value="idx">
-                        {{ host.host_nm + " - (" + host.host_ip + ")" }}
-                      </option>
-                    </select>
-                  </div>
-                </div>
-                <div class="mb10 flex70" v-if="userauth !== 'ROLE_ADMIN'">
-                  <div class="content">
-                    <p class="box-title">디렉토리 선택</p>
-                  </div>
-                  <div>
-                    <select class="only full-select" v-model="selectedDirectory">
-                      <option :value="null" disabled>선택</option>
-                      <option v-for="(host, idx) in hostList" :key="idx" :value="idx">
-                        {{ host.dept_drctry_nm }}
-                      </option>
-                    </select>
-                  </div>
-                </div>
-                <div class="mb10 flex25">
-                  <button class="blue-btn large-btn" @click="uniquePaths()">
-                    연결
-                  </button>
-                </div>
-              </div>
-              <div class="file-tree-zone" :style="{ height: userauth !== 'ROLE_ADMIN' ? 'calc(100% - 156px)' : 'calc(100% - 79px)' }">
-                <div class="content-titleZone flex justify-between align-center">
-                  <p class="box-title">폴더 리스트</p>
-                  <button class="blue-border-btn small-btn" @click="openMkdirModal()">
-                    폴더추가
-                  </button>
-                </div>
-                <div class="file-zone overflow-y">
-                  <ul class="tree-wrap">
-                    <TreeItem ref="treeItem" :connection="connection" :selectedNode="selectedNode"
-                      v-for="(item, idx) in nodes" :item="item" :idx="item.id" :selectItem="selectItem" :key="idx"
-                      @selectFolder="selectFolder" @isLoading="handleIsLoading" @selectItem="handleSelectItem" />
-
-                  </ul>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-        <div class="flex80 content-box">
-          <div class="right-content flex100">
-            <div class="flex-column justify-between">
-              <div class="flex justify-between align-center no-gutter">
-                <div class="flex40">
-                  <button class="blue-border-btn small-btn" @click="openTreeModal('copy')">
-                    선택복사
-                  </button>
-                  <button class="blue-border-btn small-btn" @click="openTreeModal('move')">
-                    선택이동
-                  </button>
-                  <button class="red-border-btn small-btn" @click="remove()">
-                    선택삭제
-                  </button>
-                </div>
-                <div class="flex justify-end flex60">
-                  <div class="search-bar">
-                    <div class="flex justify-end align-center">
-                      <select name="" id="" class="square-select" v-model="searchType">
-                        <option value="all">전체</option>
-                        <option value="folder">현재폴더</option>
-                      </select>
-                      <div class="search-square">
-                        <div class="flex justify-end align-center no-gutter">
-                          <input type="text" class="square-input flex90" placeholder="Search File" v-model="searchText"
-                            @keyup.enter="searchFiles()" />
-                          <button class="square-button blue-btn flex10">
-                            <svg-icon type="mdi" :path="searchPath" class="square-icon"
-                              @click="searchFiles()"></svg-icon>
-                          </button>
-                        </div>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-              <div class="content-zone" style="overflow-y: auto">
-                <div class="table-zone file-table">
-                  <div class="list-info flex justify-between align-center">
-                    <div class="count-zone">
-                      <p>
-                        총
-                        <span v-if="search.totalRows != 0">{{
-                          search.totalRows
-                        }}</span>
-                        <span v-else>0</span>건 중
-                        <span>{{ selectedFiles.length }}</span>건 선택
-                      </p>
-                      <p>
-                        현재경로 : <span>{{ connection.path }}</span>
-                      </p>
-                    </div>
-                  </div>
-                  <table class="list-table">
-                    <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
-                    <colgroup>
-                      <col style="width: 5%" />
-                      <col style="width: 45%" />
-                      <col style="width: 10%" />
-                      <col style="width: 15%" />
-                      <col style="width: 10%" />
-                      <col style="width: 15%" />
-                    </colgroup>
-                    <thead>
-                      <tr>
-                        <th>
-                          <input type="checkbox" @click="filesCheckAll" v-model="checkAll" />
-                        </th>
-                        <th>이름</th>
-                        <th>타입</th>
-                        <th>마지막 수정</th>
-                        <th>크기</th>
-                        <th>관리</th>
-                      </tr>
-                    </thead>
-                    <tbody>
-                      <tr v-for="(file, index) in fileList" :key="index" @click="selectFileList(file)">
-                        <td v-if="file.text != '상위폴더로 이동'" @click.stop="">
-                          <input type="checkbox" @click.stop="" v-model="selectedFiles" :value="{
-                            folder: file.folder,
-                            path: file.path,
-                            fileName: file.text,
-                            extension: file.extension,
-                          }" name="files" />
-                        </td>
-                        <td v-else @click.stop=""></td>
-                        <td>
-                          <div class="text-lf">
-                            <span v-if="file.extension === 'txt'"><img src="../../../resources/img/icon/txt.png" ref=""
-                                alt="" /></span>
-                            <span v-else-if="file.extension === 'pdf'"><img src="../../../resources/img/icon/pdf.png"
-                                alt="" /></span>
-                            <span v-else-if="
-                              file.extension === 'pptx' ||
-                              file.extension === 'ppt'
-                            "><img src="../../../resources/img/icon/ppt.png" alt="" /></span>
-                            <span v-else-if="file.extension === 'hwp'"><img src="../../../resources/img/icon/hwp.png"
-                                alt="" /></span>
-                            <span v-else-if="
-                              file.extension === 'xls' ||
-                              file.extension === 'xlsx' ||
-                              file.extension === 'csv'
-                            "><img src="../../../resources/img/icon/xls.png" alt="" /></span>
-                            <span v-else-if="file.extension === 'jpg'"><img src="../../../resources/img/icon/img.png"
-                                alt="" /></span>
-                            <span v-else-if="file.extension === 'png'"><img src="../../../resources/img/icon/img.png"
-                                ref="" alt="" /></span>
-                            <span>{{ " " + file.text }}</span>
-                          </div>
-                        </td>
-                        <td>{{ file.extension }}</td>
-                        <td>{{ file.lastUpdate }}</td>
-                        <td v-if="file.size != 0">
-                          {{ $filters.bytesToSize(file.size) }}
-                        </td>
-                        <td v-else></td>
-                        <td>
-                          <div v-if="!file.folder">
-                            <button class="icon-btn orange-btn" title="미리보기" @click.stop="preview(file)">
-                              <svg-icon type="mdi" :width="18" :height="18" :path="fileFindPath"></svg-icon>
-                            </button>
-                            <button class="icon-btn green-btn ml5" title="파일명 변경" @click.stop="openReNameModal(file)">
-                              <svg-icon type="mdi" :width="18" :height="18" :path="fileNamePath"></svg-icon>
-                            </button>
-                            <button class="icon-btn blue-btn ml5" title="다운로드" @click.stop="download(file)">
-                              <svg-icon type="mdi" :width="18" :height="18" :path="filedownPath"></svg-icon>
-                            </button>
-                          </div>
-                          <div v-else-if="
-                            file.folder && file.text != '상위폴더로 이동'
-                          ">
-                            <button class="icon-btn green-btn ml5" title="폴더명 변경" @click.stop="openReNameModal(file)">
-                              <svg-icon type="mdi" :width="18" :height="18" :path="fileNamePath"></svg-icon>
-                            </button>
-                          </div>
-                        </td>
-                      </tr>
-                    </tbody>
-                  </table>
-                </div>
-              </div>
-              <div class="flex justify-end">
-                <button class="blue-btn small-btn" @click="setModal('typeSelected', 'small')">
-                  업로드
-                </button>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-  <!-- 폴더 목록 모달 -->
-  <TreeModal :modalOpen="treeModalOpen" :modalConnection="modalConnection" :modalNodes="modalNodes"
-    :selectType="selectModalType" @modalSelectFolder="modalSelectFolder" @closeTreeModal="closeTreeModal"
-    @modalSubmit="fileConfirm" />
-  <!-- 데이터셋 미리보기 모달 -->
-  <div v-show="previewModalOpen" class="modal-wrapper" :style="isLoading ? { cursor: 'wait' } : {}">
-    <div v-if="isLoading" class="loading-overlay">
-      <div class="loading-div">
-        <span>LOADING&nbsp;</span>
-        <span class="anima">.</span>
-        <span class="anima">.</span>
-        <span class="anima">.</span>
-      </div>
-    </div>
-    <div class="modal-container list-modal">
-      <div class="modal-title">
-        <div class="flex justify-between align-center">
-          <h2>데이터 미리보기</h2>
-          <button class="close-btn" @click="previewModalClose()">X</button>
-        </div>
-      </div>
-      <div class="modal-content-monthly">
-        <FileDataRead v-if="previewModalOpen" v-model:isLoading="isLoading" :dataTable="dataTable" :preview="true">
-        </FileDataRead>
-      </div>
-      <div class="modal-end flex justify-end">
-        <button class="blue-btn small-btn" @click="previewModalClose()">
-          확인
-        </button>
-      </div>
-    </div>
-  </div>
-  <!-- 폴더, 파일 이름변경 모달 -->
-  <div v-show="reNameModalOpen" class="modal-wrapper">
-    <div class="modal-container small-modal">
-      <div class="modal-title text-ct">
-        <h2>폴더(파일)명 변경</h2>
-      </div>
-      <div class="modal-content-monthly">
-        <div class="table-zone">
-          <table class="form-table2">
-            <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
-            <colgroup>
-              <col style="width: 25%" />
-              <col style="width: 75%" />
-            </colgroup>
-            <tbody>
-              <tr>
-                <th>현재 이름</th>
-                <td>{{ reNameObject.currentName }}</td>
-              </tr>
-              <tr>
-                <th>변경 이름</th>
-                <td>
-                  <input type="text" class="full-input" v-model="reNameObject.changeName" @keyup.enter="rename()" />
-                </td>
-              </tr>
-            </tbody>
-          </table>
-        </div>
-      </div>
-      <div class="modal-end flex justify-between" style="flex-wrap: nowrap">
-        <button class="gray-btn large-btn" @click="closeReNameModal()">
-          취소
-        </button>
-        <button class="blue-btn large-btn" @click="rename()">확인</button>
-      </div>
-    </div>
-  </div>
-  <!-- 폴더추가 모달 -->
-  <div v-show="mkdirModalOpen" class="modal-wrapper">
-    <div class="modal-container small-modal">
-      <div class="modal-title text-ct">
-        <h2>폴더 추가</h2>
-      </div>
-      <div class="modal-content-monthly">
-        <div class="table-zone">
-          <table class="form-table2">
-            <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
-            <colgroup>
-              <col style="width: 25%" />
-              <col style="width: 75%" />
-            </colgroup>
-            <tbody>
-              <tr>
-                <th>현재경로</th>
-                <td>{{ connection.path }}</td>
-              </tr>
-              <tr>
-                <th>폴더명</th>
-                <td>
-                  <input type="text" class="full-input" v-model="folderName" @keyup.enter="mkdir()" />
-                </td>
-              </tr>
-            </tbody>
-          </table>
-        </div>
-      </div>
-      <div class="modal-end flex justify-between" style="flex-wrap: nowrap">
-        <button class="blue-btn large-btn" @click="mkdir()">확인</button>
-        <button class="gray-btn large-btn" @click="closeMkdirModal()">
-          취소
-        </button>
-      </div>
-    </div>
-  </div>
-  <!-- 엑셀 타입 지정 -->
-  <div v-if="isOpenExcel" class="modal-wrapper">
-    <div class="modal-container small-modal">
-      <div class="modal-title text-ct">
-        <div class="flex justify-between align-center">
-          <h2>ExcelEncoding</h2>
-          <button class="close-btn" @click="closeExcel">
-            <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon>
-          </button>
-        </div>
-      </div>
-      <!-- ExcelEncoding 선택-->
-      <div class="modal-content-monthly">
-        <div class="flex justify-center align-center mb10">
-          <div class="flex50 pl0">
-            <button class="blue-border-btn large-btn" @click="selectEncoding('utf-8')">
-              UTF-8
-            </button>
-          </div>
-          <div class="flex50 pr0">
-            <button class="blue-border-btn large-btn" @click="selectEncoding('euc-kr')">
-              EUC-KR
-            </button>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-  <!-- 모달 -->
-  <div v-show="isModalOpen" class="modal-wrapper">
-    <div :class="{
-      'modal-container': true,
-      'small-modal': modalSize === 'small',
-      'large-modal': modalSize === 'large',
-    }">
-      <div class="modal-title text-ct" v-show="modalType === 'typeSelected'">
-        <div class="flex justify-between align-center">
-          <h2>업로드</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 === 'typeSelected'">
-        <div class="flex justify-center align-center mb10">
-          <div class="flex50 pl0">
-            <button class="blue-border-btn large-btn" @click="setModal('file-modal', 'small')">
-              파일
-            </button>
-          </div>
-          <div class="flex50 pr0">
-            <button class="blue-border-btn large-btn" @click="setModal('db-modal', 'middle')">
-              DB
-            </button>
-          </div>
-        </div>
-        <div class="flex justify-center align-center">
-          <div class="flex50 pl0">
-            <button class="blue-border-btn large-btn" @click="setModal('api-modal', 'middle')">
-              API
-            </button>
-          </div>
-          <div class="flex50 pr0">
-            <button class="blue-border-btn large-btn" @click="setModal('ehojo-modal', 'middle')">
-              차세대 API
-            </button>
-          </div>
-        </div>
-      </div>
-      <!-- 파일 (file-modal) -->
-      <template v-if="modalType == 'file-modal'">
-        <div class="modal-title">
-          <div class="flex justify-between align-center">
-            <h2>파일 등록</h2>
-            <button class="close-btn" @click="closeModal">X</button>
-          </div>
-        </div>
-        <div class="modal-content-monthly">
-          <div class="table-zone">
-            <table class="form-table">
-              <colgroup>
-                <col style="width: 15%" />
-                <col style="width: 85%" />
-              </colgroup>
-              <tbody>
-                <tr>
-                  <th>현재경로</th>
-                  <td>{{ connection.path }}</td>
-                </tr>
-                <tr>
-                  <th>파일명</th>
-                  <td>
-                    <input type="file" name="file" id="file" ref="fileInput" @change="fileInput()" />
-                  </td>
-                </tr>
-              </tbody>
-            </table>
-            <div class="modal-end flex justify-between" style="flex-wrap: nowrap">
-              <button class="blue-btn large-btn" @click="upload()">확인</button>
-              <button class="gray-btn large-btn" @click="closeModal()">
-                취소
-              </button>
-            </div>
-          </div>
-        </div>
-      </template>
-      <!-- DB (db-modal) -->
-      <DatabaseConnection v-if="modalType == 'db-modal'" openPopup="true" :jobItem="selectNode" @closePopup="closeModal"
-        @saveNodeData="fnUpdateSetup" />
-      <!-- API (api-modal) -->
-      <apiConnection v-if="modalType == 'api-modal'" openPopup="true" :jobItem="selectNode" @fnCloseModal="closeModal"
-        @fnSaveSetup="fnUpdateSetup" />
-      <!-- 차세대 API (ehojo-modal) -->
-      <EhojoConnection v-if="modalType == 'ehojo-modal'" openPopup="true" :jobItem="selectNode"
-        @fnCloseModal="closeModal" @fnSaveSetup="fnUpdateSetup" />
-      <div class="modal-end flex justify-end" v-show="modalType == 'test-modal'" style="flex-wrap: nowrap">
-        <button class="gray-border-btn small-btn" @click="closeModal">
-          이전
-        </button>
-        <button class="blue-border-btn small-btn" @click="closeModal">
-          확인
-        </button>
-        <button class="darkg-border-btn small-btn" @click="closeModal">
-          닫기
-        </button>
-      </div>
+      <FileManagementMain />
     </div>
   </div>
 </template>
 <script>
-import axios from "axios";
 import PageNavigation from "../../component/PageNavigation.vue";
-import PaginationButton from "../../component/PaginationButton.vue";
-import TreeItem from "../../component/FileTree.vue";
-import TreeModal from "../../component/FileTreeModal.vue";
-import SvgIcon from "@jamescoyle/vue-icon";
-import DatabaseConnection from "../../component/connection/itm/databaseConnection.vue";
-import apiConnection from "../../component/connection/itm/apiConnection.vue";
-import EhojoConnection from "../../component/connection/EhojoConnection.vue";
-import {
-  mdiMagnify,
-  mdiClose,
-  mdiFileCog,
-  mdiDownload,
-  mdiFileFind,
-  mdiClockPlusOutline,
-} from "@mdi/js";
-import store from "../AppStore";
-import FileDataRead from "../../component/connection/itm/fileDataRead.vue";
-import * as XLSX from "xlsx";
+import FileManagementMain from "../../pages/data/filemanger/FileManagementMain.vue";
 
 export default {
   data() {
     return {
-      //엑셀 등록시 타입지정
-      isOpenExcel: false,
-      encodingType: null,
-      // 검색 객체
-      search: this.$getDefaultSerchVO(),
-      search_data: this.$getDefaultSerchItem(null, "String"),
-
-      userauth: null,
-      searchPath: mdiMagnify,
-      closePath: mdiClose,
-      fileNamePath: mdiFileCog,
-      filedownPath: mdiDownload,
-      fileFindPath: mdiFileFind,
-      isModalOpen: false,
-      activeTab: "tab1",
-      modalType: "",
-      modalSize: "small",
-      mkdirModalOpen: false,
-      reNameModalOpen: false,
-      treeModalOpen: false,
-
-      selectedHost: {},
-      selectedHostCode: null,
-      // 호스트 목록
-      hostList: [],
-      // 부서-호스트 목록
-      //01.17 deptHostList: [],
-      connection: {
-        host_code: null,
-        path: null,
-        depth: null,
-        type: null,
-      },
-
-      selectedTab: '', // 현재 선택된 탭
-
-      uploadObject: {
-        host_code: null,
-        path: null,
-        uploadFile: null,
-        confirm: false,
-      },
-      nodes: [],
-      fileList: [],
-      selectedNode: null,
-      folderName: null,
-      uploadFile: null,
-
-      reNameObject: {
-        currentPath: null,
-        parentPath: null,
-        currentName: null,
-        extension: null,
-        changeName: null,
-      },
-      checkAll: false,
-      selectedFiles: [],
-      modalNodes: [],
-      modalConnection: {
-        host_code: null,
-        path: null,
-        depth: null,
-        type: null,
-      },
-      modalSeletedNode: null,
-      selectModalType: 0,
-
-      searchType: "all",
-      searchText: null,
-
-      selectItem: {
-        id: null,
-      },
-
-      // 데이터 미리보기
-      isLoading: false,
-      previewModalOpen: false,
-      dataTable: {},
-
-      selectNode: {}, // 선택된 노드
-
-      selectedHostCodeData: null,
-      selectedDirectory: null,
-      selectedHostData: null,
-      isUpdating: false  // 무한 루프 방지를 위한 플래그
     };
   },
   methods: {
-    // 검색 객체 초기화
-    searchInit: function () {
-      this.search.searchObjectList.push(this.search_data);
-    },
-
-    searchFiles() {
-      const vm = this;
-
-      if (vm.searchText === null || vm.searchText === "") {
-        vm.$showAlert("파일 검색", "검색어를 입력해 주세요.");
-        return;
-      }
-
-      let path = vm.connection.path;
-
-      if (vm.searchType === "all") {
-        path = "#";
-      } else {
-        if (!path) {
-          vm.$showAlert("파일 검색", "폴더를 선택해 주세요.");
-          return;
-        }
-      }
-
-      vm.isLoading = true; // 로딩 시작
-
-      axios({
-        url: "/files/search/" + vm.connection.host_code,
-        method: "post",
-        headers: {},
-        data: {
-          searchType: vm.searchType,
-          searchText: vm.searchText,
-          path: path,
-        },
-      })
-        .then(function (response) {
-          vm.isLoading = false; // 로딩 해제
-          vm.fileList = response.data.resultData.fileList;
-          if (vm.fileList.length == 0) {
-            vm.$showAlert("파일 검색", "검색 결과가 없습니다.");
-          }
-        })
-        .catch(function (error) {
-          vm.isLoading = false; // 로딩 해제
-          vm.$showAlert(
-            "파일 검색",
-            "파일 검색 오류, 관리자에게 문의바랍니다."
-          );
-        });
-    },
-
-    // 로딩 상태 변경
-    handleIsLoading(isLoadingValue) {
-      this.isLoading = isLoadingValue; // 부모 컴포넌트의 데이터 업데이트
-    },
-
-    // 탭 변경
-    showTab: function (tabName) {
-      this.activeTab = tabName;
-    },
-    openModal: function () {
-      this.isModalOpen = true;
-    },
-    closeModal: function () {
-      // this.selectNode = {}; // 초기화
-      this.modalType = null;
-      this.isModalOpen = false;
-    },
-    setModal: function (type, size) {
-      if (type == "typeSelected") {
-        if (
-          this.connection.path == null ||
-          this.connection.path == "" ||
-          this.connection.path === undefined
-        ) {
-          this.$showAlert("폴더 추가", "폴더를 선택해 주세요.");
-          return;
-        }
-      } else if (type == "db-modal") {
-        this.fnCreateNode("DB_READ");
-      } else if (type == "api-modal") {
-        this.fnCreateNode("API_READ");
-      } else if (type == "ehojo-modal") {
-        this.fnCreateNode("EHOJO_READ");
-      }
-      this.modalType = type;
-      this.modalSize = size;
-    },
-
-    openMkdirModal() {
-      if (
-        this.connection.path == null ||
-        this.connection.path == "" ||
-        this.connection.path === undefined
-      ) {
-        this.$showAlert("폴더 추가", "폴더를 선택해 주세요.");
-        return;
-      }
-      this.mkdirModalOpen = true;
-    },
-
-    closeMkdirModal() {
-      this.mkdirModalOpen = false;
-    },
-
-    openTreeModal(type) {
-      if (this.selectedFiles.length === 0) {
-        if (type === "move") {
-          this.$showAlert("파일 이동", "선택한 파일이 없습니다.");
-        } else if (type === "copy") {
-          this.$showAlert("파일 복사", "선택한 파일이 없습니다.");
-        }
-        return;
-      }
-
-      this.treeModalOpen = true;
-      this.selectModalType = type;
-    },
-
-    closeTreeModal() {
-      // this.modalNodes = [];
-      this.treeModalOpen = false;
-      this.modalSeletedNode = null;
-    },
-
-    openReNameModal(file) {
-      this.reNameObject.currentPath = file.path;
-      this.reNameObject.parentPath = file.parent;
-      this.reNameObject.currentName = file.text;
-      this.reNameObject.extension = file.extension;
-      this.reNameModalOpen = true;
-    },
-
-    closeReNameModal() {
-      this.reNameObject.changeName = null;
-      this.reNameModalOpen = false;
-    },
-
-    filesCheckAll() {
-      this.checkAll = !this.checkAll;
-
-      if (this.checkAll) {
-        this.fileList.forEach((file) => {
-          if (file.text != "상위폴더로 이동") {
-            this.selectedFiles.push({
-              folder: file.folder,
-              path: file.path,
-              fileName: file.text,
-              extension: file.extension,
-            });
-          }
-        });
-      } else {
-        this.selectedFiles = [];
-      }
-    },
-
-    // 선택한 호스트 연결
-    async connectionConfirm() {
-      const vm = this;
-      if (vm.selectedHostCode === null || vm.selectedHostCode === undefined) {
-        vm.$showAlert("파일시스템 연결", "호스트를 선택해 주세요.");
-        return;
-      }
-
-      // 이전에 선택한 호스트 코드가 있으면 저장
-      let tempHostCode = null;
-      let tempModalHostCode = null;
-      if (vm.connection.host_code) {
-        tempHostCode = _.cloneDeep(vm.connection.host_code);
-        tempModalHostCode = _.cloneDeep(vm.modalConnection.host_code);
-      }
-
-      vm.connection.host_code = vm.selectedHost.host_code;
-      vm.modalConnection.host_code = vm.selectedHost.host_code;
-
-      vm.isLoading = true; // 로딩 시작
-
-      try {
-        const response = await axios.get("/files/connection", {
-          params: { host_code: vm.connection.host_code },
-        });
-
-        vm.isLoading = false; // 로딩 해제
-        if (response.data.status === 200) {
-          await this.fileTreeList();
-        } else {
-          // 이전에 선택한 호스트 코드로 변경
-          if (tempHostCode) {
-            vm.connection.host_code = tempHostCode;
-            vm.selectedHost.host_code = tempHostCode;
-            vm.selectedHostCode = tempHostCode;
-          }
-        }
-        this.selectedFiles = [];
-        this.checkAll = false;
-        vm.$showAlert("파일시스템 연결", response.data.message);
-      } catch (error) {
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert(
-          "파일시스템 연결",
-          "파일시스템 연결 오류, 관리자에게 문의하세요."
-        );
-        this.selectedFiles = [];
-        this.checkAll = false;
-        // 이전에 선택한 호스트 코드로 변경
-        if (tempHostCode) {
-          vm.connection.host_code = tempHostCode;
-          vm.selectedHost.host_code = tempHostCode;
-          vm.selectedHostCode = tempHostCode;
-        }
-      }
-    },
-    uniquePaths() {
-      // this.selectedHost = null;
-      this.selectedHost = this.selectedHostData;
-      this.selectedHostCode = this.selectedHostData.host_code;
-      this.connectionConfirm();
-    },
-    // 호스트 목록 조회
-    selectHosts() {
-      const vm = this;
-      vm.isLoading = true; // 로딩 시작
-      axios({
-        url: "/files/hosts",
-        method: "post",
-        headers: {
-          "Content-Type": "application/json; charset=UTF-8",
-        },
-        data: {
-          userId: store.state.loginUser.user_id,
-          //01.17 authList: store.state.loginUser.user_auth,
-          author: store.state.loginUser.user_auth[0],
-          dept_code: store.state.loginUser.dept_code,
-        },
-      })
-        .then((response) => {
-          vm.hostList = response.data.resultData.hostList;
-          console.log("@@", vm.hostList);
-          //01.17 vm.deptHostList = response.data.resultData.deptHostList;
-          // this.connection.host_code = this.hostList[this.hostList.length - 1].host_code
-          if (this.hostList.length > 0) {
-            vm.selectedHost = vm.hostList[0];
-            vm.selectedHostCode = vm.hostList[0].host_code;
-            vm.selectedHostCodeData = 0;
-            vm.selectedDirectory = 0;
-            vm.selectedHostData = vm.hostList[0];
-            // 01.17for (let i = 0; i < vm.hostList.length; i++) {
-            //   for (let j = 0; j < vm.deptHostList.length; j++) {
-            //     if (vm.hostList[i].host_code === vm.deptHostList[j].host_code) {
-            //       vm.hostList[i].main_folder_path =
-            //         vm.deptHostList[j].main_folder_path;
-            //     }
-            //   }
-            // }
-            vm.connectionConfirm();
-          } else {
-            vm.isLoading = false; // 로딩 해제
-            vm.selectedHost = {};
-          }
-        })
-        .catch((error) => {
-          vm.isLoading = false; // 로딩 해제
-          vm.$showAlert(
-            "호스트 조회",
-            "호스트 조회 오류, 관리자에게 문의하세요."
-          );
-        });
-    },
-
-    // 폴더 리스트 조회
-    async fileTreeList() {
-      const vm = this;
-      vm.nodes = [];
-      vm.connection.path = vm.selectedHost.dept_drctry_path;
-      vm.connection.depth = 0;
-      vm.connection.type = "folder";
-      vm.modalNodes = [];
-      vm.modalConnection.path = vm.selectedHost.dept_drctry_path;
-      vm.modalConnection.depth = 0;
-      vm.modalConnection.type = "folder";
-      vm.modalConnection.host_code = vm.connection.host_code;
-
-      vm.isLoading = true; // 로딩 시작
-
-      try {
-        const response = await axios.get("/files/tree", {
-          params: vm.connection,
-        });
-
-        vm.nodes = response.data.resultData.fileTree;
-        vm.modalNodes = response.data.resultData.fileTree;
-        vm.connection.path = null;
-        vm.fileList = [];
-        this.checkAll = false;
-        vm.isLoading = false; // 로딩 해제
-      } catch (error) {
-        vm.connection.path = null;
-        vm.fileList = [];
-        this.checkAll = false;
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert(
-          "파일리스트 조회",
-          "파일리스트 조회 오류, 관리자에게 문의하세요."
-        );
-      }
-    },
-
-    modalFileTreeList() {
-      const vm = this;
-
-      vm.modalNodes = [];
-
-      vm.modalConnection.host_code = vm.connection.host_code;
-      vm.modalConnection.path = "#";
-      vm.modalConnection.depth = 0;
-      vm.modalConnection.type = "folder";
-
-      axios
-        .get("/files/tree", { params: vm.modalConnection })
-        .then((response) => {
-          vm.modalNodes = response.data.resultData.fileTree;
-        })
-        .catch((error) => {
-          vm.$showAlert(
-            "파일리스트 조회",
-            "파일리스트 조회 오류, 관리자에게 문의하세요."
-          );
-        });
-    },
-
-    selectFolder(path) {
-      if (path === null || path === undefined) {
-        this.isLoading = false;
-      }
-      this.connection.path = path;
-      this.fileSelectList();
-    },
-
-    handleSelectItem(item) {
-      this.selectItem = item;
-    },
-
-    modalSelectFolder(path) {
-      this.modalSeletedNode = path;
-    },
-
-    selectFileList(item) {
-      // 폴더가 선택된 경우, 선택된 노드와 경로를 업데이트하고 파일 리스트 조회
-      if (item.extension === "폴더" || item.extension === "") {
-        this.selectedNode = item.id;
-        this.connection.path = item.id;
-        this.fileSelectList();
-      } else {
-      }
-    },
-
-    // 파일 리스트 조회
-    async fileSelectList() {
-      const vm = this;
-
-      if (vm.connection.path === null || vm.connection.path === "") {
-        vm.fileList = null;
-        return;
-      }
-
-      vm.connection.type = "all";
-      vm.connection.depth = 1;
-
-      vm.isLoading = true; // 로딩 시작
-      try {
-        const response = await axios.get("/files/list", {
-          params: vm.connection,
-        });
-        vm.fileList = response.data.resultData.fileList;
-        vm.search.totalRows = response.data.resultData.totalRow;
-        vm.selectedFiles = [];
-        vm.checkAll = false;
-        vm.isLoading = false; // 로딩 해제
-      } catch (error) {
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert(
-          "파일리스트 조회",
-          "파일리스트 조회 오류, 관리자에게 문의하세요."
-        );
-        vm.selectedFiles = [];
-        vm.checkAll = false;
-      }
-    },
-
-    // 폴더 추가
-    async mkdir() {
-      const vm = this;
-      if (vm.connection.path === null || vm.connection.path === "") {
-        vm.$showAlert(
-          "폴더 추가",
-          "선택된 경로가 없습니다.\n 경로 선택 후 다시 시도해주세요."
-        );
-        vm.closeMkdirModal();
-        vm.folderName = null;
-        return;
-      }
-
-      const mkdirVO = {};
-      mkdirVO.host_code = vm.connection.host_code;
-      mkdirVO.path = vm.connection.path;
-      mkdirVO.folderName = vm.folderName;
-
-      vm.isLoading = true; // 로딩 시작
-
-      try {
-        const response = await axios.post("/files/mkdir", mkdirVO);
-
-        vm.isLoading = false; // 로딩 해제
-        vm.getChildren(vm.selectItem.id, vm.selectItem.children); // 자식 폴더 리스트 업데이트
-        await vm.fileSelectList();
-        vm.$showAlert("폴더 추가", response.data.message);
-        vm.closeMkdirModal();
-        vm.folderName = null;
-      } catch (error) {
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert("폴더 추가", "폴더 추가 오류, 관리자에게 문의하세요.");
-        vm.folderName = null;
-      }
-    },
-
-    // 자식 파일 정보 조회
-    getChildren(path, children) {
-      const vm = this;
-      vm.connection.path = path;
-      vm.connection.type = "folder";
-      vm.connection.depth = 1;
-
-      axios
-        .get("/files/list", { params: vm.connection })
-        .then((response) => {
-          let childrenList = response.data.resultData.fileList;
-
-          // 기존 배열 초기화
-          children.length = 0;
-
-          // 새로운 배열의 요소들을 기존 배열에 추가
-          Array.prototype.push.apply(children, childrenList);
-        })
-        .catch((error) => { });
-    },
-
-    //파일업로드
-    fileInput() {
-      this.uploadFile = this.$refs.fileInput.files[0];
-    },
-
-    async upload() {
-      const vm = this;
-      vm.uploadObject.path = vm.connection.path;
-      vm.uploadObject.host_code = vm.connection.host_code;
-      vm.uploadObject.uploadFile = vm.uploadFile;
-      vm.isLoading = true;
-
-      // 파일 타입 및 인코딩 확인
-      if (vm.uploadFile) {
-        const fileName = vm.uploadFile.name.toLowerCase();
-        const isExcel = fileName.endsWith(".xlsx") || fileName.endsWith(".xls");
-        const isCsv = fileName.endsWith(".csv");
-
-        if (isExcel || isCsv) {
-          try {
-            const fileReader = new FileReader();
-
-            fileReader.onload = function (e) {
-              const content = new Uint8Array(e.target.result);
-              let encoding = "Unknown";
-
-              // BOM 확인으로 UTF-8 검사
-              if (
-                content.length >= 3 &&
-                content[0] === 0xef &&
-                content[1] === 0xbb &&
-                content[2] === 0xbf
-              ) {
-                encoding = "UTF-8 with BOM";
-              } else if (
-                content.length >= 2 &&
-                content[0] === 0xff &&
-                content[1] === 0xfe
-              ) {
-                encoding = "UTF-16 LE";
-              } else {
-                // 텍스트 분석으로 인코딩 추정
-                try {
-                  const text = new TextDecoder("UTF-8").decode(content);
-                  const hasInvalidChars = text.includes("�");
-                  encoding = hasInvalidChars ? "Possibly EUC-KR" : "UTF-8";
-                } catch (err) {
-                  encoding = "Possibly EUC-KR";
-                }
-              }
-
-              console.log(`파일 타입: ${isExcel ? "Excel" : "CSV"}`);
-              console.log(`인코딩 타입: ${encoding}`);
-            };
-
-            // 파일의 처음 1KB만 읽어서 분석
-            fileReader.readAsArrayBuffer(vm.uploadFile.slice(0, 1024));
-          } catch (error) {
-            console.error("인코딩 확인 중 오류:", error);
-          }
-        }
-      }
-
-      try {
-        const response = await axios.post("/files/upload", vm.uploadObject, {
-          headers: {
-            "Content-Type": "multipart/form-data",
-          },
-        });
-
-        vm.isLoading = false;
-
-        if (response.data.status === 200) {
-          document.getElementById("file").value = "";
-          await vm.fileSelectList();
-          vm.$showAlert("파일 업로드", response.data.message);
-        } else if (response.data.status === 100) {
-          if (!(await vm.$showConfirm("파일 업로드", response.data.message))) {
-            document.getElementById("file").value = "";
-            return;
-          } else {
-            vm.uploadObject.confirm = true;
-            await vm.reUpload();
-          }
-        }
-
-        vm.closeModal();
-      } catch (error) {
-        vm.isLoading = false;
-        vm.$showAlert(
-          "파일 업로드",
-          "파일 업로드 오류, 관리자에게 문의하세요."
-        );
-        vm.uploadObject.confirm = false;
-        document.getElementById("file").value = "";
-      }
-    },
-
-    async reUpload() {
-      const vm = this;
-      vm.isLoading = true; // 로딩 시작
-
-      try {
-        const response = await axios.post("/files/upload", vm.uploadObject, {
-          headers: {
-            "Content-Type": "multipart/form-data",
-          },
-        });
-
-        vm.isLoading = false; // 로딩 종료
-        await vm.fileSelectList();
-        vm.$showAlert("파일 업로드", response.data.message);
-        vm.uploadObject.confirm = false;
-        document.getElementById("file").value = "";
-      } catch (error) {
-        vm.isLoading = false; // 로딩 종료
-        vm.$showAlert(
-          "파일 업로드",
-          "파일 업로드 오류, 관리자에게 문의하세요."
-        );
-        vm.uploadObject.confirm = false;
-        document.getElementById("file").value = "";
-      }
-    },
-
-    download(file) {
-      const vm = this;
-
-      vm.isLoading = true; // 로딩 시작
-
-      axios
-        .post("/files/download", {
-          host_code: vm.connection.host_code,
-          path: file.parent,
-          fileName: file.text,
-        })
-        .then((response) => {
-          vm.isLoading = false; // 로딩 해제
-          vm.$showAlert("파일 다운로드", response.data.message);
-        })
-        .catch((error) => {
-          vm.isLoading = false; // 로딩 해제
-          vm.$showAlert(
-            "파일 다운로드",
-            "파일 다운로드 오류, 관리자에게 문의하세요."
-          );
-        });
-    },
-
-    async rename() {
-      const vm = this;
-
-      if (vm.reNameObject.changeName === null) {
-        vm.$showAlert("이름 바꾸기", "변경할 이름을 입력해주세요.");
-        return;
-      }
-
-      let fullName =
-        vm.reNameObject.changeName + "." + vm.reNameObject.extension;
-
-      if (vm.reNameObject.extension === "폴더") {
-        fullName = vm.reNameObject.changeName;
-      }
-
-      vm.isLoading = true; // 로딩 시작
-
-      try {
-        const response = await axios.put("/files/rename", {
-          host_code: vm.connection.host_code,
-          currentPath: vm.reNameObject.currentPath,
-          changePath: vm.reNameObject.parentPath + "/" + fullName,
-        });
-
-        vm.isLoading = false; // 로딩 해제
-        vm.reNameObject.changeName = null;
-        await vm.fileSelectList();
-        vm.$showAlert("이름 바꾸기", response.data.message);
-        vm.closeReNameModal();
-      } catch (error) {
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert(
-          "이름 바꾸기",
-          "이름 바꾸기 오류, 관리자에게 문의하세요."
-        );
-        vm.closeReNameModal();
-        vm.reNameObject.changeName = null;
-      }
-    },
-
-    async remove() {
-      const vm = this;
-
-      if (vm.selectedFiles.length === 0) {
-        vm.$showAlert("파일 삭제", "선택한 파일이 없습니다.");
-        return;
-      }
-
-      if (
-        !(await vm.$showConfirm("파일 삭제", "선택한 파일을 삭제하시겠습니까?"))
-      ) {
-        return;
-      }
-
-      vm.selectedFiles.forEach((file) => {
-        delete file.fileName;
-        delete file.extension;
-      });
-      vm.isLoading = true; // 로딩 시작
-      try {
-        const response = await axios.delete(
-          `/files/${vm.connection.host_code}`,
-          { data: vm.selectedFiles }
-        );
-
-        vm.isLoading = false;
-        vm.getChildren(vm.selectItem.id, vm.selectItem.children); // 자식 폴더 리스트 업데이트
-        await vm.fileSelectList();
-        vm.$showAlert("파일 삭제", response.data.message);
-        vm.checkAll = false;
-        vm.selectedFiles = [];
-      } catch (error) {
-        vm.isLoading = false;
-        vm.$showAlert("파일 삭제", "파일 삭제 오류, 관리자에게 문의하세요.");
-        vm.checkAll = false;
-        vm.selectedFiles = [];
-      }
-    },
-
-    async fileConfirm(type) {
-      const vm = this;
-
-      if (
-        !(await vm.$showConfirm(
-          "파일 " + type,
-          "선택한 파일을 " + type + " 하시겠습니까?"
-        ))
-      ) {
-        return;
-      }
-
-      let file = vm.selectedFiles[0];
-      let index = file.path.lastIndexOf("/");
-
-      if (file.path.substring(0, index) === file.movePath) {
-        vm.$showAlert(
-          "파일 " + type,
-          "현재 파일 경로와 동일합니다. 다른 경로를 선택해주세요."
-        );
-        return;
-      }
-
-      if (type === "복사") {
-        vm.copy(vm.selectedFiles, "confirm", false);
-      } else {
-        vm.move(vm.selectedFiles, [], "confirm", false);
-      }
-    },
-    async move(fileList, removeFolderList, type, check) {
-      const vm = this;
-
-      fileList.forEach((file) => {
-        if (!file.movePath) {
-          file.movePath = vm.modalSeletedNode;
-        }
-      });
-
-      vm.isLoading = true; // 로딩 시작
-      try {
-        const response = await axios.put(
-          "/files/move/" + vm.connection.host_code,
-          {
-            fileList: fileList,
-            removeFolder: removeFolderList,
-            type: type,
-            check: check,
-          }
-        );
-
-        vm.isLoading = false; // 로딩 해제
-        if (response.data.checkMessage.status === 100) {
-          let returnFileList = response.data.resultData.fileList;
-          let folderList = response.data.resultData.removeFolderList;
-          const moveOrCopy = await vm.$showRadioConfirm(
-            "파일 이동",
-            response.data.checkMessage.message
-          );
-          vm.move(
-            returnFileList,
-            folderList,
-            moveOrCopy.type,
-            moveOrCopy.checkBox
-          );
-        } else {
-          let folderList = response.data.resultData.removeFolderList;
-          vm.checkAll = false;
-          vm.selectedFiles = [];
-
-          if (folderList.length != 0) {
-            vm.removeFolder(folderList);
-          } else {
-            await vm.fileSelectList();
-          }
-          vm.$showAlert("파일 이동", response.data.checkMessage.message);
-          vm.closeTreeModal();
-        }
-      } catch (error) {
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert("파일 이동", "파일 이동 오류, 관리자에게 문의하세요.");
-        vm.checkAll = false;
-        vm.selectedFiles = [];
-        vm.closeTreeModal();
-      }
-    },
-
-    async copy(fileList, type, check) {
-      const vm = this;
-
-      fileList.forEach((file) => {
-        if (!file.movePath) {
-          file.movePath = vm.modalSeletedNode;
-        }
-      });
-
-      vm.isLoading = true; // 로딩 시작
-      try {
-        const response = await axios.put(
-          "/files/copy/" + vm.connection.host_code,
-          { fileList: fileList, type: type, check: check }
-        );
-
-        vm.isLoading = false; // 로딩 해제
-        if (response.data.checkMessage.status === 100) {
-          let returnFileList = response.data.resultData.fileList;
-          const moveOrCopy = await vm.$showRadioConfirm(
-            "파일 복사",
-            response.data.checkMessage.message
-          );
-          vm.copy(returnFileList, moveOrCopy.type, moveOrCopy.checkBox);
-        } else {
-          vm.$showAlert("파일 복사", response.data.checkMessage.message);
-          vm.checkAll = false;
-          vm.selectedFiles = [];
-          vm.closeTreeModal();
-        }
-      } catch (error) {
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert("파일 복사", "파일 이동 오류, 관리자에게 문의하세요.");
-        vm.checkAll = false;
-        vm.selectedFiles = [];
-        vm.closeTreeModal();
-      }
-    },
-
-    async removeFolder(folderList) {
-      const vm = this;
-
-      vm.isLoading = false; // 로딩 해제
-
-      try {
-        const response = await axios.delete(
-          "/files/folder/" + vm.connection.host_code,
-          { data: folderList }
-        );
-
-        vm.isLoading = false; // 로딩 해제
-        vm.getChildren(vm.selectItem.id, vm.selectItem.children); // 자식 폴더 리스트 업데이트
-        await vm.fileSelectList();
-        if (response.data.status === 500) {
-          vm.$showAlert("파일 이동", response.data.message);
-        }
-      } catch (error) {
-        vm.isLoading = false; // 로딩 해제
-        vm.$showAlert(
-          "파일 이동",
-          "원본 폴더 삭제 오류, 관리자에게 문의하세요."
-        );
-      }
-    },
-
-    // 미리보기 모달 닫기
-    previewModalClose() {
-      this.previewModalOpen = false;
-      this.dataTable = {};
-    },
-
-    // 파일 미리보기
-    preview(file) {
-      // 허용된 파일 확장자 검사
-      const allowedExtensions = ["xlsx", "xls", "csv", "json"];
-
-      if (!allowedExtensions.includes(file.extension.toLowerCase())) {
-        this.$showAlert(
-          "파일 미리보기",
-          "엑셀, CSV, JSON 파일만 읽을 수 있습니다."
-        );
-        return;
-      }
-
-      this.previewModalOpen = true;
-      this.fileRead(file);
-    },
-
-    // 파일 미리보기 - 읽기
-    async fileRead(file) {
-      const vm = this;
-      vm.isLoading = true;
-      axios
-        .post("/files/read/" + vm.connection.host_code, {
-          path: file.path,
-          fileName: file.text,
-          extension: file.extension,
-          type: "file",
-          datasetId: "",
-          viewMode: true,
-        })
-        .then((response) => {
-          vm.dataTable = response.data.resultData.dataTableMap;
-        })
-        .catch(() => {
-          vm.isLoading = false;
-        });
-    },
-    selectEncoding(encoding) {
-      // 선택한 인코딩에 대한 처리
-      this.encodingType = encoding;
-      console.log(`${encoding} 인코딩이 선택되었습니다.`);
-      this.closeExcel(); // 선택 후 모달 닫기
-    },
-    closeExcel() {
-      this.isOpenExcel = false; // 모달 상태를 false로 설정+
-      this.exportToCSV();
-    },
-    // 노드 설정 저장 후 닫기
-    fnUpdateSetup(jobItem) {
-      console.log("값을 보자 ", jobItem);
-      if (
-        !jobItem.dataTable.rowData ||
-        jobItem.dataTable.rowData.length === 0
-      ) {
-        // rowData가 null이거나 빈 리스트일 때 실행할 코드
-        this.$showAlert("엑셀 생성 실패", "저장된 데이터가 없습니다.");
-      } else {
-        this.closeModal(); // 괄호 추가
-        this.isOpenExcel = true;
-        this.selectNode = jobItem;
-      }
-    },
-    async exportToCSV() {
-      const vm = this;
-
-      try {
-        // 컬럼 헤더 생성
-        console.log()
-        const header = this.selectNode.dataTable.columnDatas.map(
-          (col) => col.displyColumnNm
-        );
-
-        // 데이터 준비 (rowData를 객체로 변환)
-        const data = this.selectNode.dataTable.rowData.map((row) => {
-          const rowObject = {};
-          this.selectNode.dataTable.columnDatas.forEach((col, index) => {
-            rowObject[col.displyColumnNm] = row[index];
-          });
-          return rowObject;
-        });
-
-        // CSV 데이터 생성
-        const csvContent = [
-          header.join(","),
-          ...data.map((row) =>
-            header
-              .map((col) => {
-                // null, undefined 처리 및 쉼표가 포함된 데이터 처리
-                const cellData = row[col] ?? "";
-                return typeof cellData === "string" && cellData.includes(",")
-                  ? `"${cellData}"`
-                  : cellData;
-              })
-              .join(",")
-          ),
-        ].join("\n");
-
-        let blob;
-        // encodingType이 설정되지 않은 경우 기본값으로 utf-8 사용
-        const encoding = this.encodingType || "utf-8";
-        console.log("@@@", encoding);
-        if (encoding === "utf-8") {
-          // UTF-8 BOM 추가
-          blob = new Blob([new Uint8Array([0xef, 0xbb, 0xbf]), csvContent], {
-            type: "text/csv;charset=utf-8",
-          });
-        } else if (encoding === "euc-kr") {
-          // EUC-KR 인코딩
-          const encoder = new TextEncoder();
-          const encodedData = encoder.encode(csvContent);
-          blob = new Blob([encodedData], {
-            type: "text/csv;charset=euc-kr",
-          });
-        } else {
-          console.log("지원되지 않는 인코딩 타입입니다: " + encoding);
-        }
-
-        if (!blob) {
-          console.log("Blob 생성 실패");
-        }
-
-        // 파일 이름 설정
-        const fileName = `${vm.selectNode.node_type}_${vm.$getFullFileTime(
-          new Date()
-        )}.csv`;
-
-        // 서버에 업로드
-        const formData = new FormData();
-        formData.append("uploadFile", blob, fileName);
-        formData.append("path", this.connection.path);
-        formData.append("host_code", this.connection.host_code);
-
-        const response = await axios.post("/files/formDataUpload", formData, {
-          headers: {
-            "Content-Type": "multipart/form-data",
-          },
-        });
-
-        vm.$showAlert("업로드", response.data.message);
-        vm.fileSelectList();
-      } catch (error) {
-        console.error("CSV 내보내기 중 오류 발생:", error);
-        vm.$showAlert("업로드", "업로드 오류, 관리자에게 문의하세요.");
-      }
-    },
-    // 노드 생성
-    fnCreateNode(type) {
-      let node = Object.assign({}, this.$getDefaultJobGroup().node);
-      node.id = Math.floor(Math.random() * 1000) + 1;
-      node.node_id = node.id;
-      node.label = type;
-      node.node_name = type;
-      node.node_type = type;
-      node.position = {
-        x: Math.floor(Math.random() * 500) + 1,
-        y: Math.floor(Math.random() * 500) + 1,
-      };
-      let tempData = JSON.parse(JSON.stringify(node));
-      tempData.data = null;
-      node.data = tempData;
-      switch (type) {
-        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 "EHOJO_READ":
-          node.itm = Object.assign(
-            {},
-            this.$getDefaultJobGroup().connectionEhojo
-          );
-          break;
-      }
-      this.selectNode = node;
-    },
-  },
-
-  watch: {
-    // 모달타입에 맞는 모달창 화면 보여주기
-    modalType: function (newValue, oldValue) {
-      if (this.modalType != null) {
-        this.openModal(newValue);
-      }
-    },
-    selectedHostCodeData(newVal) {
-      if (newVal !== null && !this.isUpdating) {
-        this.isUpdating = true;  // 업데이트 시작
-
-        // newVal이 이제 인덱스입니다
-        this.selectedDirectory = newVal;
-        this.selectedHostData = this.hostList[newVal];
-
-        this.$nextTick(() => {
-          this.isUpdating = false;  // 업데이트 완료
-        });
-      }
-    },
-
-    selectedDirectory(newVal) {
-      if (newVal !== null && !this.isUpdating) {
-        this.isUpdating = true;  // 업데이트 시작
-
-        // newVal이 이제 인덱스입니다
-        this.selectedHostCodeData = newVal;
-        this.selectedHostData = this.hostList[newVal];
-
-        this.$nextTick(() => {
-          this.isUpdating = false;  // 업데이트 완료
-        });
-      }
-    }
   },
   components: {
-    PageNavigation: PageNavigation,
-    SvgIcon: SvgIcon,
-    PaginationButton: PaginationButton,
-    TreeItem: TreeItem,
-    TreeModal: TreeModal,
-    FileDataRead: FileDataRead,
-    DatabaseConnection: DatabaseConnection,
-    apiConnection: apiConnection,
-    XLSX: XLSX,
-    EhojoConnection: EhojoConnection,
-  },
-  mounted() {
-    this.userauth = this.$authorChk();
-    this.selectHosts();
-    this.searchInit();
+    FileManagementMain: FileManagementMain,
+    PageNavigation: PageNavigation
   },
 };
-</script>
-<style>
-.tabs-container {
-  margin-top: 1rem;
-}
-
-.tab-btn {
-  padding: 0.5rem 1rem;
-  margin-right: 0.5rem;
-  border: 1px solid #ddd;
-  border-bottom: none;
-  border-radius: 4px 4px 0 0;
-  cursor: pointer;
-  background: #f5f5f5;
-}
-
-.tab-btn.active {
-  background: #fff;
-  border-bottom: 2px solid #007bff;
-  color: #007bff;
-}
-</style>
(파일 끝에 줄바꿈 문자 없음)
+</script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/data/datapost/DataPostManagerMain.vue
--- client/views/pages/data/datapost/DataPostManagerMain.vue
+++ client/views/pages/data/datapost/DataPostManagerMain.vue
@@ -1,116 +1,114 @@
 <template>
-    <div class="content-box flex justify-between">
-        <div class="flex20 content-box">
-            <div class="left-content flex100 content-box">
-                <div class="content-box">
-                    <div class="content-titleZone">
-                        <p class="box-title">카테고리 리스트</p>
-                    </div>
-                    <div class="content-zone">
-                        <CodeList :groupCode="'DATA_CTGRY'" @selectCode="selectCode" />
-                    </div>
-                </div>
-            </div>
+  <div class="content-box flex justify-between">
+    <div class="flex20 content-box">
+      <div class="left-content flex100 content-box">
+        <div class="content-box">
+          <div class="content-titleZone">
+            <p class="box-title">카테고리 리스트</p>
+          </div>
+          <div class="content-zone">
+            <CodeList :groupCode="'DATA_CTGRY'" @selectCode="selectCode" />
+          </div>
         </div>
-        <div class="flex80 content-box">
-            <div class="right-content flex100">
-                <div class="flex-column justify-between">
-                    <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" />
-                                <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" />
-                                <select class="square-select" v-model="search_data1.value">
-                                    <option :value="null">공개여부</option>
-                                    <option :value="true">공개</option>
-                                    <option :value="false">비공개</option>
-                                </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>
-                                </select>
-                                <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>
-                                    </button>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                    <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>
-                                </div>
-                                <div class="cunt-selectZone">
-                                    <select name="" id="">
-                                        <option value="">10개 보기</option>
-                                        <option value="">20개 보기</option>
-                                    </select>
-                                </div>
-                            </div>
-                            <table class="list-table">
-                                <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
-                                <colgroup>
-                                    <col style="width: 10%" />
-                                    <col style="width: 40%" />
-                                    <col style="width: 10%" />
-                                    <col style="width: 10%" />
-                                    <col style="width: 10%" />
-                                    <col style="width: 30%" />
-                                </colgroup>
-                                <thead>
-                                    <tr>
-                                        <th>No</th>
-                                        <th>제목</th>
-                                        <th>생성자</th>
-                                        <th>부서</th>
-                                        <th>카테고리</th>
-                                        <th>등록일시</th>
-                                    </tr>
-                                </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>
-                                            <td>{{ item.post_sj }}</td>
-                                            <td>{{ item.user_nm }}</td>
-                                            <td>{{ item.dept_nm }}</td>
-                                            <td>{{ item.ctgry_id }}</td>
-                                            <td>{{ $getFullTime(item.creat_dt) }}</td>
-                                        </tr>
-                                    </template>
-                                    <tr v-else>
-                                        <td colspan="6">등록된 데이터가 없습니다.</td>
-                                    </tr>
-                                </tbody>
-                            </table>
-                            <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>
-                    </div>
-                </div>
-            </div>
-        </div>
+      </div>
     </div>
+    <div class="flex80 content-box">
+      <div class="right-content flex100">
+        <div class="flex-column justify-between">
+          <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" />
+                <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" />
+                <select class="square-select" v-model="search_data1.value">
+                  <option :value="null">공개여부</option>
+                  <option :value="true">공개</option>
+                  <option :value="false">비공개</option>
+                </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>
+                </select>
+                <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>
+                  </button>
+                </div>
+              </div>
+            </div>
+          </div>
+          <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>
+                </div>
+                <div class="cunt-selectZone">
+                  <select name="" id="">
+                    <option value="">10개 보기</option>
+                    <option value="">20개 보기</option>
+                  </select>
+                </div>
+              </div>
+              <table class="list-table">
+                <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
+                <colgroup>
+                  <col style="width: 10%" />
+                  <col style="width: 40%" />
+                  <col style="width: 10%" />
+                  <col style="width: 10%" />
+                  <col style="width: 10%" />
+                  <col style="width: 30%" />
+                </colgroup>
+                <thead>
+                  <tr>
+                    <th>No</th>
+                    <th>제목</th>
+                    <th>생성자</th>
+                    <th>부서</th>
+                    <th>카테고리</th>
+                    <th>등록일시</th>
+                  </tr>
+                </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>
+                      <td>{{ item.post_sj }}</td>
+                      <td>{{ item.user_nm }}</td>
+                      <td>{{ item.dept_nm }}</td>
+                      <td>{{ item.ctgry_id }}</td>
+                      <td>{{ $getFullTime(item.creat_dt) }}</td>
+                    </tr>
+                  </template>
+                  <tr v-else>
+                    <td colspan="6">등록된 데이터가 없습니다.</td>
+                  </tr>
+                </tbody>
+              </table>
+              <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>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
 </template>
 <script>
 import CodeList from "../../../component/common/Component_CodeList.vue";
@@ -118,6 +116,7 @@
 import PageNavigation from "../../../component/PageNavigation.vue";
 import SvgIcon from "@jamescoyle/vue-icon";
 import PaginationButton from "../../../component/PaginationButton.vue";
+import moment from 'moment';
 
 export default {
   data() {
@@ -131,7 +130,14 @@
       // 선택
       search_data3: this.$getDefaultSerchItem("post_sj", "string"),
       // 날짜
-      search_date: this.$getDefaultSerchItem("creat_dt", "dates"),
+      search_date: {
+        key: "creat_dt",
+        value: null,
+        key2: "creat_dt",
+        value2: null,
+        type: "dates",
+      },
+      // search_date: this.$getDefaultSerchItem("creat_dt", "dates"),
       categoryList: [],
     };
   },
@@ -152,7 +158,7 @@
       });
     },
     searchData: function () {
-      console.log("@@검색값",this.search);
+      console.log("@@검색값", this.search);
       const vm = this;
 
       let check = false;
@@ -183,17 +189,26 @@
       })
         .then(function (response) {
           if (response.data.checkMessage.success) {
+            vm.setDateRange(response.data.resultData.dataPostList);
             vm.dataPostList = response.data.resultData.dataPostList;
             vm.search.totalRows = response.data.resultData.totalRow;
           }
         })
         .catch(function (error) {
-          this.$showAlert(
+          vm.$showAlert(
             "에러 발생",
             "에러가 발생했습니다. 관리자에게 문의해 주세요."
           );
         });
     },
+    setDateRange(dataList) {
+      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');
+      }
+    },
     // 초기화
     init: async function () {
       this.search.searchObjectList.push(this.search_date);
 
client/views/pages/data/filemanger/FileManagementMain.vue (added)
+++ client/views/pages/data/filemanger/FileManagementMain.vue
@@ -0,0 +1,1646 @@
+<template>
+    <div class="content-box flex justify-between">
+        <div class="flex20 content-box">
+            <div class="left-content flex100 content-box">
+                <div class="content-box">
+                    <div class="flex align-end  justify-between no-gutter">
+                        <div class="mb10 flex70">
+                            <div class="content">
+                                <p class="box-title">호스트 선택</p>
+                            </div>
+                            <div>
+                                <select class="only full-select" v-model="selectedHostCodeData">
+                                    <option :value="null" disabled>선택</option>
+                                    <option v-for="(host, idx) in hostList" :key="idx" :value="idx">
+                                        {{ host.host_nm + " - (" + host.host_ip + ")" }}
+                                    </option>
+                                </select>
+                            </div>
+                        </div>
+                        <div class="mb10 flex70" v-if="userauth !== 'ROLE_ADMIN'">
+                            <div class="content">
+                                <p class="box-title">디렉토리 선택</p>
+                            </div>
+                            <div>
+                                <select class="only full-select" v-model="selectedDirectory">
+                                    <option :value="null" disabled>선택</option>
+                                    <option v-for="(host, idx) in hostList" :key="idx" :value="idx">
+                                        {{ host.dept_drctry_nm }}
+                                    </option>
+                                </select>
+                            </div>
+                        </div>
+                        <div class="mb10 flex25">
+                            <button class="blue-btn large-btn" @click="uniquePaths()">
+                                연결
+                            </button>
+                        </div>
+                    </div>
+                    <div class="file-tree-zone"
+                        :style="{ height: userauth !== 'ROLE_ADMIN' ? 'calc(100% - 156px)' : 'calc(100% - 79px)' }">
+                        <div class="content-titleZone flex justify-between align-center">
+                            <p class="box-title">폴더 리스트</p>
+                            <button class="blue-border-btn small-btn" @click="openMkdirModal()">
+                                폴더추가
+                            </button>
+                        </div>
+                        <div class="file-zone overflow-y">
+                            <ul class="tree-wrap">
+                                <TreeItem ref="treeItem" :connection="connection" :selectedNode="selectedNode"
+                                    v-for="(item, idx) in nodes" :item="item" :idx="item.id" :selectItem="selectItem"
+                                    :key="idx" @selectFolder="selectFolder" @isLoading="handleIsLoading"
+                                    @selectItem="handleSelectItem" />
+
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="flex80 content-box">
+            <div class="right-content flex100">
+                <div class="flex-column justify-between">
+                    <div class="flex justify-between align-center no-gutter">
+                        <div class="flex40">
+                            <button class="blue-border-btn small-btn" @click="openTreeModal('copy')">
+                                선택복사
+                            </button>
+                            <button class="blue-border-btn small-btn" @click="openTreeModal('move')">
+                                선택이동
+                            </button>
+                            <button class="red-border-btn small-btn" @click="remove()">
+                                선택삭제
+                            </button>
+                        </div>
+                        <div class="flex justify-end flex60">
+                            <div class="search-bar">
+                                <div class="flex justify-end align-center">
+                                    <select name="" id="" class="square-select" v-model="searchType">
+                                        <option value="all">전체</option>
+                                        <option value="folder">현재폴더</option>
+                                    </select>
+                                    <div class="search-square">
+                                        <div class="flex justify-end align-center no-gutter">
+                                            <input type="text" class="square-input flex90" placeholder="Search File"
+                                                v-model="searchText" @keyup.enter="searchFiles()" />
+                                            <button class="square-button blue-btn flex10">
+                                                <svg-icon type="mdi" :path="searchPath" class="square-icon"
+                                                    @click="searchFiles()"></svg-icon>
+                                            </button>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="content-zone" style="overflow-y: auto">
+                        <div class="table-zone file-table">
+                            <div class="list-info flex justify-between align-center">
+                                <div class="count-zone">
+                                    <p>
+                                        총
+                                        <span v-if="search.totalRows != 0">{{
+                                            search.totalRows
+                                        }}</span>
+                                        <span v-else>0</span>건 중
+                                        <span>{{ selectedFiles.length }}</span>건 선택
+                                    </p>
+                                    <p>
+                                        현재경로 : <span>{{ connection.path }}</span>
+                                    </p>
+                                </div>
+                            </div>
+                            <table class="list-table">
+                                <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
+                                <colgroup>
+                                    <col style="width: 5%" />
+                                    <col style="width: 45%" />
+                                    <col style="width: 10%" />
+                                    <col style="width: 15%" />
+                                    <col style="width: 10%" />
+                                    <col style="width: 15%" />
+                                </colgroup>
+                                <thead>
+                                    <tr>
+                                        <th>
+                                            <input type="checkbox" @click="filesCheckAll" v-model="checkAll" />
+                                        </th>
+                                        <th>이름</th>
+                                        <th>타입</th>
+                                        <th>마지막 수정</th>
+                                        <th>크기</th>
+                                        <th>관리</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    <tr v-for="(file, index) in fileList" :key="index" @click="selectFileList(file)">
+                                        <td v-if="file.text != '상위폴더로 이동'" @click.stop="">
+                                            <input type="checkbox" @click.stop="" v-model="selectedFiles" :value="{
+                                                folder: file.folder,
+                                                path: file.path,
+                                                fileName: file.text,
+                                                extension: file.extension,
+                                            }" name="files" />
+                                        </td>
+                                        <td v-else @click.stop=""></td>
+                                        <td>
+                                            <div class="text-lf">
+                                                <span v-if="file.extension === 'txt'"><img
+                                                        src="../../../../resources/img/icon/txt.png" ref=""
+                                                        alt="" /></span>
+                                                <span v-else-if="file.extension === 'pdf'"><img
+                                                        src="../../../../resources/img/icon/pdf.png" alt="" /></span>
+                                                <span v-else-if="
+                                                    file.extension === 'pptx' ||
+                                                    file.extension === 'ppt'
+                                                "><img src="../../../../resources/img/icon/ppt.png" alt="" /></span>
+                                                <span v-else-if="file.extension === 'hwp'"><img
+                                                        src="../../../../resources/img/icon/hwp.png" alt="" /></span>
+                                                <span v-else-if="
+                                                    file.extension === 'xls' ||
+                                                    file.extension === 'xlsx' ||
+                                                    file.extension === 'csv'
+                                                "><img src="../../../../resources/img/icon/xls.png" alt="" /></span>
+                                                <span v-else-if="file.extension === 'jpg'"><img
+                                                        src="../../../../resources/img/icon/img.png" alt="" /></span>
+                                                <span v-else-if="file.extension === 'png'"><img
+                                                        src="../../../../resources/img/icon/img.png" ref=""
+                                                        alt="" /></span>
+                                                <span>{{ " " + file.text }}</span>
+                                            </div>
+                                        </td>
+                                        <td>{{ file.extension }}</td>
+                                        <td>{{ file.lastUpdate }}</td>
+                                        <td v-if="file.size != 0">
+                                            {{ $filters.bytesToSize(file.size) }}
+                                        </td>
+                                        <td v-else></td>
+                                        <td>
+                                            <div v-if="!file.folder">
+                                                <button class="icon-btn orange-btn" title="미리보기"
+                                                    @click.stop="preview(file)">
+                                                    <svg-icon type="mdi" :width="18" :height="18"
+                                                        :path="fileFindPath"></svg-icon>
+                                                </button>
+                                                <button class="icon-btn green-btn ml5" title="파일명 변경"
+                                                    @click.stop="openReNameModal(file)">
+                                                    <svg-icon type="mdi" :width="18" :height="18"
+                                                        :path="fileNamePath"></svg-icon>
+                                                </button>
+                                                <button class="icon-btn blue-btn ml5" title="다운로드"
+                                                    @click.stop="download(file)">
+                                                    <svg-icon type="mdi" :width="18" :height="18"
+                                                        :path="filedownPath"></svg-icon>
+                                                </button>
+                                            </div>
+                                            <div v-else-if="
+                                                file.folder && file.text != '상위폴더로 이동'
+                                            ">
+                                                <button class="icon-btn green-btn ml5" title="폴더명 변경"
+                                                    @click.stop="openReNameModal(file)">
+                                                    <svg-icon type="mdi" :width="18" :height="18"
+                                                        :path="fileNamePath"></svg-icon>
+                                                </button>
+                                            </div>
+                                        </td>
+                                    </tr>
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                    <div class="flex justify-end">
+                        <button class="blue-btn small-btn" @click="setModal('typeSelected', 'small')">
+                            업로드
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <!-- 폴더 목록 모달 -->
+    <TreeModal :modalOpen="treeModalOpen" :modalConnection="modalConnection" :modalNodes="modalNodes"
+        :selectType="selectModalType" @modalSelectFolder="modalSelectFolder" @closeTreeModal="closeTreeModal"
+        @modalSubmit="fileConfirm" />
+    <!-- 데이터셋 미리보기 모달 -->
+    <div v-show="previewModalOpen" class="modal-wrapper" :style="isLoading ? { cursor: 'wait' } : {}">
+        <div v-if="isLoading" class="loading-overlay">
+            <div class="loading-div">
+                <span>LOADING&nbsp;</span>
+                <span class="anima">.</span>
+                <span class="anima">.</span>
+                <span class="anima">.</span>
+            </div>
+        </div>
+        <div class="modal-container list-modal">
+            <div class="modal-title">
+                <div class="flex justify-between align-center">
+                    <h2>데이터 미리보기</h2>
+                    <button class="close-btn" @click="previewModalClose()">X</button>
+                </div>
+            </div>
+            <div class="modal-content-monthly">
+                <FileDataRead v-if="previewModalOpen" v-model:isLoading="isLoading" :dataTable="dataTable"
+                    :preview="true">
+                </FileDataRead>
+            </div>
+            <div class="modal-end flex justify-end">
+                <button class="blue-btn small-btn" @click="previewModalClose()">
+                    확인
+                </button>
+            </div>
+        </div>
+    </div>
+    <!-- 폴더, 파일 이름변경 모달 -->
+    <div v-show="reNameModalOpen" class="modal-wrapper">
+        <div class="modal-container small-modal">
+            <div class="modal-title text-ct">
+                <h2>폴더(파일)명 변경</h2>
+            </div>
+            <div class="modal-content-monthly">
+                <div class="table-zone">
+                    <table class="form-table2">
+                        <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
+                        <colgroup>
+                            <col style="width: 25%" />
+                            <col style="width: 75%" />
+                        </colgroup>
+                        <tbody>
+                            <tr>
+                                <th>현재 이름</th>
+                                <td>{{ reNameObject.currentName }}</td>
+                            </tr>
+                            <tr>
+                                <th>변경 이름</th>
+                                <td>
+                                    <input type="text" class="full-input" v-model="reNameObject.changeName"
+                                        @keyup.enter="rename()" />
+                                </td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-end flex justify-between" style="flex-wrap: nowrap">
+                <button class="gray-btn large-btn" @click="closeReNameModal()">
+                    취소
+                </button>
+                <button class="blue-btn large-btn" @click="rename()">확인</button>
+            </div>
+        </div>
+    </div>
+    <!-- 폴더추가 모달 -->
+    <div v-show="mkdirModalOpen" class="modal-wrapper">
+        <div class="modal-container small-modal">
+            <div class="modal-title text-ct">
+                <h2>폴더 추가</h2>
+            </div>
+            <div class="modal-content-monthly">
+                <div class="table-zone">
+                    <table class="form-table2">
+                        <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 -->
+                        <colgroup>
+                            <col style="width: 25%" />
+                            <col style="width: 75%" />
+                        </colgroup>
+                        <tbody>
+                            <tr>
+                                <th>현재경로</th>
+                                <td>{{ connection.path }}</td>
+                            </tr>
+                            <tr>
+                                <th>폴더명</th>
+                                <td>
+                                    <input type="text" class="full-input" v-model="folderName" @keyup.enter="mkdir()" />
+                                </td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-end flex justify-between" style="flex-wrap: nowrap">
+                <button class="blue-btn large-btn" @click="mkdir()">확인</button>
+                <button class="gray-btn large-btn" @click="closeMkdirModal()">
+                    취소
+                </button>
+            </div>
+        </div>
+    </div>
+    <!-- 엑셀 타입 지정 -->
+    <div v-if="isOpenExcel" class="modal-wrapper">
+        <div class="modal-container small-modal">
+            <div class="modal-title text-ct">
+                <div class="flex justify-between align-center">
+                    <h2>ExcelEncoding</h2>
+                    <button class="close-btn" @click="closeExcel">
+                        <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon>
+                    </button>
+                </div>
+            </div>
+            <!-- ExcelEncoding 선택-->
+            <div class="modal-content-monthly">
+                <div class="flex justify-center align-center mb10">
+                    <div class="flex50 pl0">
+                        <button class="blue-border-btn large-btn" @click="selectEncoding('utf-8')">
+                            UTF-8
+                        </button>
+                    </div>
+                    <div class="flex50 pr0">
+                        <button class="blue-border-btn large-btn" @click="selectEncoding('euc-kr')">
+                            EUC-KR
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <!-- 모달 -->
+    <div v-show="isModalOpen" class="modal-wrapper">
+        <div :class="{
+            'modal-container': true,
+            'small-modal': modalSize === 'small',
+            'large-modal': modalSize === 'large',
+        }">
+            <div class="modal-title text-ct" v-show="modalType === 'typeSelected'">
+                <div class="flex justify-between align-center">
+                    <h2>업로드</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 === 'typeSelected'">
+                <div class="flex justify-center align-center mb10">
+                    <div class="flex50 pl0">
+                        <button class="blue-border-btn large-btn" @click="setModal('file-modal', 'small')">
+                            파일
+                        </button>
+                    </div>
+                    <div class="flex50 pr0">
+                        <button class="blue-border-btn large-btn" @click="setModal('db-modal', 'middle')">
+                            DB
+                        </button>
+                    </div>
+                </div>
+                <div class="flex justify-center align-center">
+                    <div class="flex50 pl0">
+                        <button class="blue-border-btn large-btn" @click="setModal('api-modal', 'middle')">
+                            API
+                        </button>
+                    </div>
+                    <div class="flex50 pr0">
+                        <button class="blue-border-btn large-btn" @click="setModal('ehojo-modal', 'middle')">
+                            차세대 API
+                        </button>
+                    </div>
+                </div>
+            </div>
+            <!-- 파일 (file-modal) -->
+            <template v-if="modalType == 'file-modal'">
+                <div class="modal-title">
+                    <div class="flex justify-between align-center">
+                        <h2>파일 등록</h2>
+                        <button class="close-btn" @click="closeModal">X</button>
+                    </div>
+                </div>
+                <div class="modal-content-monthly">
+                    <div class="table-zone">
+                        <table class="form-table">
+                            <colgroup>
+                                <col style="width: 15%" />
+                                <col style="width: 85%" />
+                            </colgroup>
+                            <tbody>
+                                <tr>
+                                    <th>현재경로</th>
+                                    <td>{{ connection.path }}</td>
+                                </tr>
+                                <tr>
+                                    <th>파일명</th>
+                                    <td>
+                                        <input type="file" name="file" id="file" ref="fileInput"
+                                            @change="fileInput()" />
+                                    </td>
+                                </tr>
+                            </tbody>
+                        </table>
+                        <div class="modal-end flex justify-between" style="flex-wrap: nowrap">
+                            <button class="blue-btn large-btn" @click="upload()">확인</button>
+                            <button class="gray-btn large-btn" @click="closeModal()">
+                                취소
+                            </button>
+                        </div>
+                    </div>
+                </div>
+            </template>
+            <!-- DB (db-modal) -->
+            <DatabaseConnection v-if="modalType == 'db-modal'" openPopup="true" :jobItem="selectNode"
+                @closePopup="closeModal" @saveNodeData="fnUpdateSetup" />
+            <!-- API (api-modal) -->
+            <apiConnection v-if="modalType == 'api-modal'" openPopup="true" :jobItem="selectNode"
+                @fnCloseModal="closeModal" @fnSaveSetup="fnUpdateSetup" />
+            <!-- 차세대 API (ehojo-modal) -->
+            <EhojoConnection v-if="modalType == 'ehojo-modal'" openPopup="true" :jobItem="selectNode"
+                @fnCloseModal="closeModal" @fnSaveSetup="fnUpdateSetup" />
+            <div class="modal-end flex justify-end" v-show="modalType == 'test-modal'" style="flex-wrap: nowrap">
+                <button class="gray-border-btn small-btn" @click="closeModal">
+                    이전
+                </button>
+                <button class="blue-border-btn small-btn" @click="closeModal">
+                    확인
+                </button>
+                <button class="darkg-border-btn small-btn" @click="closeModal">
+                    닫기
+                </button>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import axios from "axios";
+import TreeItem from "../../../component/FileTree.vue";
+import TreeModal from "../../../component/FileTreeModal.vue";
+import SvgIcon from "@jamescoyle/vue-icon";
+import DatabaseConnection from "../../../component/connection/itm/databaseConnection.vue";
+import apiConnection from "../../../component/connection/itm/apiConnection.vue";
+import EhojoConnection from "../../../component/connection/EhojoConnection.vue";
+import {
+  mdiMagnify,
+  mdiClose,
+  mdiFileCog,
+  mdiDownload,
+  mdiFileFind,
+  mdiClockPlusOutline,
+} from "@mdi/js";
+import store from "../../AppStore";
+import FileDataRead from "../../../component/connection/itm/fileDataRead.vue";
+import * as XLSX from "xlsx";
+
+export default {
+  data() {
+    return {
+      //엑셀 등록시 타입지정
+      isOpenExcel: false,
+      encodingType: null,
+      // 검색 객체
+      search: this.$getDefaultSerchVO(),
+      search_data: this.$getDefaultSerchItem(null, "String"),
+
+      userauth: null,
+      searchPath: mdiMagnify,
+      closePath: mdiClose,
+      fileNamePath: mdiFileCog,
+      filedownPath: mdiDownload,
+      fileFindPath: mdiFileFind,
+      isModalOpen: false,
+      activeTab: "tab1",
+      modalType: "",
+      modalSize: "small",
+      mkdirModalOpen: false,
+      reNameModalOpen: false,
+      treeModalOpen: false,
+
+      selectedHost: {},
+      selectedHostCode: null,
+      // 호스트 목록
+      hostList: [],
+      // 부서-호스트 목록
+      //01.17 deptHostList: [],
+      connection: {
+        host_code: null,
+        path: null,
+        depth: null,
+        type: null,
+      },
+
+      selectedTab: '', // 현재 선택된 탭
+
+      uploadObject: {
+        host_code: null,
+        path: null,
+        uploadFile: null,
+        confirm: false,
+      },
+      nodes: [],
+      fileList: [],
+      selectedNode: null,
+      folderName: null,
+      uploadFile: null,
+
+      reNameObject: {
+        currentPath: null,
+        parentPath: null,
+        currentName: null,
+        extension: null,
+        changeName: null,
+      },
+      checkAll: false,
+      selectedFiles: [],
+      modalNodes: [],
+      modalConnection: {
+        host_code: null,
+        path: null,
+        depth: null,
+        type: null,
+      },
+      modalSeletedNode: null,
+      selectModalType: 0,
+
+      searchType: "all",
+      searchText: null,
+
+      selectItem: {
+        id: null,
+      },
+
+      // 데이터 미리보기
+      isLoading: false,
+      previewModalOpen: false,
+      dataTable: {},
+
+      selectNode: {}, // 선택된 노드
+
+      selectedHostCodeData: null,
+      selectedDirectory: null,
+      selectedHostData: null,
+      isUpdating: false  // 무한 루프 방지를 위한 플래그
+    };
+  },
+  methods: {
+    // 검색 객체 초기화
+    searchInit: function () {
+      this.search.searchObjectList.push(this.search_data);
+    },
+
+    searchFiles() {
+      const vm = this;
+
+      if (vm.searchText === null || vm.searchText === "") {
+        vm.$showAlert("파일 검색", "검색어를 입력해 주세요.");
+        return;
+      }
+
+      let path = vm.connection.path;
+
+      if (vm.searchType === "all") {
+        path = "#";
+      } else {
+        if (!path) {
+          vm.$showAlert("파일 검색", "폴더를 선택해 주세요.");
+          return;
+        }
+      }
+
+      vm.isLoading = true; // 로딩 시작
+
+      axios({
+        url: "/files/search/" + vm.connection.host_code,
+        method: "post",
+        headers: {},
+        data: {
+          searchType: vm.searchType,
+          searchText: vm.searchText,
+          path: path,
+        },
+      })
+        .then(function (response) {
+          vm.isLoading = false; // 로딩 해제
+          vm.fileList = response.data.resultData.fileList;
+          if (vm.fileList.length == 0) {
+            vm.$showAlert("파일 검색", "검색 결과가 없습니다.");
+          }
+        })
+        .catch(function (error) {
+          vm.isLoading = false; // 로딩 해제
+          vm.$showAlert(
+            "파일 검색",
+            "파일 검색 오류, 관리자에게 문의바랍니다."
+          );
+        });
+    },
+
+    // 로딩 상태 변경
+    handleIsLoading(isLoadingValue) {
+      this.isLoading = isLoadingValue; // 부모 컴포넌트의 데이터 업데이트
+    },
+
+    // 탭 변경
+    showTab: function (tabName) {
+      this.activeTab = tabName;
+    },
+    openModal: function () {
+      this.isModalOpen = true;
+    },
+    closeModal: function () {
+      // this.selectNode = {}; // 초기화
+      this.modalType = null;
+      this.isModalOpen = false;
+    },
+    setModal: function (type, size) {
+      if (type == "typeSelected") {
+        if (
+          this.connection.path == null ||
+          this.connection.path == "" ||
+          this.connection.path === undefined
+        ) {
+          this.$showAlert("폴더 추가", "폴더를 선택해 주세요.");
+          return;
+        }
+      } else if (type == "db-modal") {
+        this.fnCreateNode("DB_READ");
+      } else if (type == "api-modal") {
+        this.fnCreateNode("API_READ");
+      } else if (type == "ehojo-modal") {
+        this.fnCreateNode("EHOJO_READ");
+      }
+      this.modalType = type;
+      this.modalSize = size;
+    },
+
+    openMkdirModal() {
+      if (
+        this.connection.path == null ||
+        this.connection.path == "" ||
+        this.connection.path === undefined
+      ) {
+        this.$showAlert("폴더 추가", "폴더를 선택해 주세요.");
+        return;
+      }
+      this.mkdirModalOpen = true;
+    },
+
+    closeMkdirModal() {
+      this.mkdirModalOpen = false;
+    },
+
+    openTreeModal(type) {
+      if (this.selectedFiles.length === 0) {
+        if (type === "move") {
+          this.$showAlert("파일 이동", "선택한 파일이 없습니다.");
+        } else if (type === "copy") {
+          this.$showAlert("파일 복사", "선택한 파일이 없습니다.");
+        }
+        return;
+      }
+
+      this.treeModalOpen = true;
+      this.selectModalType = type;
+    },
+
+    closeTreeModal() {
+      // this.modalNodes = [];
+      this.treeModalOpen = false;
+      this.modalSeletedNode = null;
+    },
+
+    openReNameModal(file) {
+      this.reNameObject.currentPath = file.path;
+      this.reNameObject.parentPath = file.parent;
+      this.reNameObject.currentName = file.text;
+      this.reNameObject.extension = file.extension;
+      this.reNameModalOpen = true;
+    },
+
+    closeReNameModal() {
+      this.reNameObject.changeName = null;
+      this.reNameModalOpen = false;
+    },
+
+    filesCheckAll() {
+      this.checkAll = !this.checkAll;
+
+      if (this.checkAll) {
+        this.fileList.forEach((file) => {
+          if (file.text != "상위폴더로 이동") {
+            this.selectedFiles.push({
+              folder: file.folder,
+              path: file.path,
+              fileName: file.text,
+              extension: file.extension,
+            });
+          }
+        });
+      } else {
+        this.selectedFiles = [];
+      }
+    },
+
+    // 선택한 호스트 연결
+    async connectionConfirm() {
+      const vm = this;
+      if (vm.selectedHostCode === null || vm.selectedHostCode === undefined) {
+        vm.$showAlert("파일시스템 연결", "호스트를 선택해 주세요.");
+        return;
+      }
+
+      // 이전에 선택한 호스트 코드가 있으면 저장
+      let tempHostCode = null;
+      let tempModalHostCode = null;
+      if (vm.connection.host_code) {
+        tempHostCode = _.cloneDeep(vm.connection.host_code);
+        tempModalHostCode = _.cloneDeep(vm.modalConnection.host_code);
+      }
+
+      vm.connection.host_code = vm.selectedHost.host_code;
+      vm.modalConnection.host_code = vm.selectedHost.host_code;
+
+      vm.isLoading = true; // 로딩 시작
+
+      try {
+        const response = await axios.get("/files/connection", {
+          params: { host_code: vm.connection.host_code },
+        });
+
+        vm.isLoading = false; // 로딩 해제
+        if (response.data.status === 200) {
+          await this.fileTreeList();
+        } else {
+          // 이전에 선택한 호스트 코드로 변경
+          if (tempHostCode) {
+            vm.connection.host_code = tempHostCode;
+            vm.selectedHost.host_code = tempHostCode;
+            vm.selectedHostCode = tempHostCode;
+          }
+        }
+        this.selectedFiles = [];
+        this.checkAll = false;
+        vm.$showAlert("파일시스템 연결", response.data.message);
+      } catch (error) {
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert(
+          "파일시스템 연결",
+          "파일시스템 연결 오류, 관리자에게 문의하세요."
+        );
+        this.selectedFiles = [];
+        this.checkAll = false;
+        // 이전에 선택한 호스트 코드로 변경
+        if (tempHostCode) {
+          vm.connection.host_code = tempHostCode;
+          vm.selectedHost.host_code = tempHostCode;
+          vm.selectedHostCode = tempHostCode;
+        }
+      }
+    },
+    uniquePaths() {
+      // this.selectedHost = null;
+      this.selectedHost = this.selectedHostData;
+      this.selectedHostCode = this.selectedHostData.host_code;
+      this.connectionConfirm();
+    },
+    // 호스트 목록 조회
+    selectHosts() {
+      const vm = this;
+      vm.isLoading = true; // 로딩 시작
+      axios({
+        url: "/files/hosts",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          userId: store.state.loginUser.user_id,
+          author: store.state.loginUser.user_auth[0],
+          dept_code: store.state.loginUser.dept_code,
+        },
+      })
+        .then((response) => {
+          vm.hostList = response.data.resultData.hostList;
+          console.log("@@", vm.hostList);
+          if (this.hostList.length > 0) {
+            vm.selectedHost = vm.hostList[0];
+            vm.selectedHostCode = vm.hostList[0].host_code;
+            vm.selectedHostCodeData = 0;
+            vm.selectedDirectory = 0;
+            vm.selectedHostData = vm.hostList[0];
+            vm.connectionConfirm();
+          } else {
+            vm.isLoading = false; // 로딩 해제
+            vm.selectedHost = {};
+          }
+        })
+        .catch((error) => {
+          vm.isLoading = false; // 로딩 해제
+          vm.$showAlert(
+            "호스트 조회",
+            "호스트 조회 오류, 관리자에게 문의하세요."
+          );
+        });
+    },
+
+    // 폴더 리스트 조회
+    async fileTreeList() {
+      const vm = this;
+      vm.nodes = [];
+      vm.connection.path = vm.selectedHost.dept_drctry_path;
+      vm.connection.depth = 0;
+      vm.connection.type = "folder";
+      vm.modalNodes = [];
+      vm.modalConnection.path = vm.selectedHost.dept_drctry_path;
+      vm.modalConnection.depth = 0;
+      vm.modalConnection.type = "folder";
+      vm.modalConnection.host_code = vm.connection.host_code;
+
+      vm.isLoading = true; // 로딩 시작
+
+      try {
+        const response = await axios.get("/files/tree", {
+          params: vm.connection,
+        });
+
+        vm.nodes = response.data.resultData.fileTree;
+        vm.modalNodes = response.data.resultData.fileTree;
+        vm.connection.path = null;
+        vm.fileList = [];
+        this.checkAll = false;
+        vm.isLoading = false; // 로딩 해제
+      } catch (error) {
+        vm.connection.path = null;
+        vm.fileList = [];
+        this.checkAll = false;
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert(
+          "파일리스트 조회",
+          "파일리스트 조회 오류, 관리자에게 문의하세요."
+        );
+      }
+    },
+
+    modalFileTreeList() {
+      const vm = this;
+
+      vm.modalNodes = [];
+
+      vm.modalConnection.host_code = vm.connection.host_code;
+      vm.modalConnection.path = "#";
+      vm.modalConnection.depth = 0;
+      vm.modalConnection.type = "folder";
+
+      axios
+        .get("/files/tree", { params: vm.modalConnection })
+        .then((response) => {
+          vm.modalNodes = response.data.resultData.fileTree;
+        })
+        .catch((error) => {
+          vm.$showAlert(
+            "파일리스트 조회",
+            "파일리스트 조회 오류, 관리자에게 문의하세요."
+          );
+        });
+    },
+
+    selectFolder(path) {
+      if (path === null || path === undefined) {
+        this.isLoading = false;
+      }
+      this.connection.path = path;
+      this.fileSelectList();
+    },
+
+    handleSelectItem(item) {
+      this.selectItem = item;
+    },
+
+    modalSelectFolder(path) {
+      this.modalSeletedNode = path;
+    },
+
+    selectFileList(item) {
+      // 폴더가 선택된 경우, 선택된 노드와 경로를 업데이트하고 파일 리스트 조회
+      if (item.extension === "폴더" || item.extension === "") {
+        this.selectedNode = item.id;
+        this.connection.path = item.id;
+        this.fileSelectList();
+      } else {
+      }
+    },
+
+    // 파일 리스트 조회
+    async fileSelectList() {
+      const vm = this;
+
+      if (vm.connection.path === null || vm.connection.path === "") {
+        vm.fileList = null;
+        return;
+      }
+
+      vm.connection.type = "all";
+      vm.connection.depth = 1;
+
+      vm.isLoading = true; // 로딩 시작
+      try {
+        const response = await axios.get("/files/list", {
+          params: vm.connection,
+        });
+        vm.fileList = response.data.resultData.fileList;
+        vm.search.totalRows = response.data.resultData.totalRow;
+        vm.selectedFiles = [];
+        vm.checkAll = false;
+        vm.isLoading = false; // 로딩 해제
+      } catch (error) {
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert(
+          "파일리스트 조회",
+          "파일리스트 조회 오류, 관리자에게 문의하세요."
+        );
+        vm.selectedFiles = [];
+        vm.checkAll = false;
+      }
+    },
+
+    // 폴더 추가
+    async mkdir() {
+      const vm = this;
+      if (vm.connection.path === null || vm.connection.path === "") {
+        vm.$showAlert(
+          "폴더 추가",
+          "선택된 경로가 없습니다.\n 경로 선택 후 다시 시도해주세요."
+        );
+        vm.closeMkdirModal();
+        vm.folderName = null;
+        return;
+      }
+
+      const mkdirVO = {};
+      mkdirVO.host_code = vm.connection.host_code;
+      mkdirVO.path = vm.connection.path;
+      mkdirVO.folderName = vm.folderName;
+
+      vm.isLoading = true; // 로딩 시작
+
+      try {
+        const response = await axios.post("/files/mkdir", mkdirVO);
+
+        vm.isLoading = false; // 로딩 해제
+        vm.getChildren(vm.selectItem.id, vm.selectItem.children); // 자식 폴더 리스트 업데이트
+        await vm.fileSelectList();
+        vm.$showAlert("폴더 추가", response.data.message);
+        vm.closeMkdirModal();
+        vm.folderName = null;
+      } catch (error) {
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert("폴더 추가", "폴더 추가 오류, 관리자에게 문의하세요.");
+        vm.folderName = null;
+      }
+    },
+
+    // 자식 파일 정보 조회
+    getChildren(path, children) {
+      const vm = this;
+      vm.connection.path = path;
+      vm.connection.type = "folder";
+      vm.connection.depth = 1;
+
+      axios
+        .get("/files/list", { params: vm.connection })
+        .then((response) => {
+          let childrenList = response.data.resultData.fileList;
+
+          // 기존 배열 초기화
+          children.length = 0;
+
+          // 새로운 배열의 요소들을 기존 배열에 추가
+          Array.prototype.push.apply(children, childrenList);
+        })
+        .catch((error) => { });
+    },
+
+    //파일업로드
+    fileInput() {
+      this.uploadFile = this.$refs.fileInput.files[0];
+    },
+
+    async upload() {
+      const vm = this;
+      vm.uploadObject.path = vm.connection.path;
+      vm.uploadObject.host_code = vm.connection.host_code;
+      vm.uploadObject.uploadFile = vm.uploadFile;
+      vm.isLoading = true;
+
+      // 파일 타입 및 인코딩 확인
+      if (vm.uploadFile) {
+        const fileName = vm.uploadFile.name.toLowerCase();
+        const isExcel = fileName.endsWith(".xlsx") || fileName.endsWith(".xls");
+        const isCsv = fileName.endsWith(".csv");
+
+        if (isExcel || isCsv) {
+          try {
+            const fileReader = new FileReader();
+
+            fileReader.onload = function (e) {
+              const content = new Uint8Array(e.target.result);
+              let encoding = "Unknown";
+
+              // BOM 확인으로 UTF-8 검사
+              if (
+                content.length >= 3 &&
+                content[0] === 0xef &&
+                content[1] === 0xbb &&
+                content[2] === 0xbf
+              ) {
+                encoding = "UTF-8 with BOM";
+              } else if (
+                content.length >= 2 &&
+                content[0] === 0xff &&
+                content[1] === 0xfe
+              ) {
+                encoding = "UTF-16 LE";
+              } else {
+                // 텍스트 분석으로 인코딩 추정
+                try {
+                  const text = new TextDecoder("UTF-8").decode(content);
+                  const hasInvalidChars = text.includes("�");
+                  encoding = hasInvalidChars ? "Possibly EUC-KR" : "UTF-8";
+                } catch (err) {
+                  encoding = "Possibly EUC-KR";
+                }
+              }
+
+              console.log(`파일 타입: ${isExcel ? "Excel" : "CSV"}`);
+              console.log(`인코딩 타입: ${encoding}`);
+            };
+
+            // 파일의 처음 1KB만 읽어서 분석
+            fileReader.readAsArrayBuffer(vm.uploadFile.slice(0, 1024));
+          } catch (error) {
+            console.error("인코딩 확인 중 오류:", error);
+          }
+        }
+      }
+
+      try {
+        const response = await axios.post("/files/upload", vm.uploadObject, {
+          headers: {
+            "Content-Type": "multipart/form-data",
+          },
+        });
+
+        vm.isLoading = false;
+
+        if (response.data.status === 200) {
+          document.getElementById("file").value = "";
+          await vm.fileSelectList();
+          vm.$showAlert("파일 업로드", response.data.message);
+        } else if (response.data.status === 100) {
+          if (!(await vm.$showConfirm("파일 업로드", response.data.message))) {
+            document.getElementById("file").value = "";
+            return;
+          } else {
+            vm.uploadObject.confirm = true;
+            await vm.reUpload();
+          }
+        }
+
+        vm.closeModal();
+      } catch (error) {
+        vm.isLoading = false;
+        vm.$showAlert(
+          "파일 업로드",
+          "파일 업로드 오류, 관리자에게 문의하세요."
+        );
+        vm.uploadObject.confirm = false;
+        document.getElementById("file").value = "";
+      }
+    },
+
+    async reUpload() {
+      const vm = this;
+      vm.isLoading = true; // 로딩 시작
+
+      try {
+        const response = await axios.post("/files/upload", vm.uploadObject, {
+          headers: {
+            "Content-Type": "multipart/form-data",
+          },
+        });
+
+        vm.isLoading = false; // 로딩 종료
+        await vm.fileSelectList();
+        vm.$showAlert("파일 업로드", response.data.message);
+        vm.uploadObject.confirm = false;
+        document.getElementById("file").value = "";
+      } catch (error) {
+        vm.isLoading = false; // 로딩 종료
+        vm.$showAlert(
+          "파일 업로드",
+          "파일 업로드 오류, 관리자에게 문의하세요."
+        );
+        vm.uploadObject.confirm = false;
+        document.getElementById("file").value = "";
+      }
+    },
+
+    download(file) {
+      const vm = this;
+
+      vm.isLoading = true; // 로딩 시작
+
+      axios
+        .post("/files/download", {
+          host_code: vm.connection.host_code,
+          path: file.parent,
+          fileName: file.text,
+        })
+        .then((response) => {
+          vm.isLoading = false; // 로딩 해제
+          vm.$showAlert("파일 다운로드", response.data.message);
+        })
+        .catch((error) => {
+          vm.isLoading = false; // 로딩 해제
+          vm.$showAlert(
+            "파일 다운로드",
+            "파일 다운로드 오류, 관리자에게 문의하세요."
+          );
+        });
+    },
+
+    async rename() {
+      const vm = this;
+
+      if (vm.reNameObject.changeName === null) {
+        vm.$showAlert("이름 바꾸기", "변경할 이름을 입력해주세요.");
+        return;
+      }
+
+      let fullName =
+        vm.reNameObject.changeName + "." + vm.reNameObject.extension;
+
+      if (vm.reNameObject.extension === "폴더") {
+        fullName = vm.reNameObject.changeName;
+      }
+
+      vm.isLoading = true; // 로딩 시작
+
+      try {
+        const response = await axios.put("/files/rename", {
+          host_code: vm.connection.host_code,
+          currentPath: vm.reNameObject.currentPath,
+          changePath: vm.reNameObject.parentPath + "/" + fullName,
+        });
+
+        vm.isLoading = false; // 로딩 해제
+        vm.reNameObject.changeName = null;
+        await vm.fileSelectList();
+        vm.$showAlert("이름 바꾸기", response.data.message);
+        vm.closeReNameModal();
+      } catch (error) {
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert(
+          "이름 바꾸기",
+          "이름 바꾸기 오류, 관리자에게 문의하세요."
+        );
+        vm.closeReNameModal();
+        vm.reNameObject.changeName = null;
+      }
+    },
+
+    async remove() {
+      const vm = this;
+
+      if (vm.selectedFiles.length === 0) {
+        vm.$showAlert("파일 삭제", "선택한 파일이 없습니다.");
+        return;
+      }
+
+      if (
+        !(await vm.$showConfirm("파일 삭제", "선택한 파일을 삭제하시겠습니까?"))
+      ) {
+        return;
+      }
+
+      vm.selectedFiles.forEach((file) => {
+        delete file.fileName;
+        delete file.extension;
+      });
+      vm.isLoading = true; // 로딩 시작
+      try {
+        const response = await axios.delete(
+          `/files/${vm.connection.host_code}`,
+          { data: vm.selectedFiles }
+        );
+
+        vm.isLoading = false;
+        vm.getChildren(vm.selectItem.id, vm.selectItem.children); // 자식 폴더 리스트 업데이트
+        await vm.fileSelectList();
+        vm.$showAlert("파일 삭제", response.data.message);
+        vm.checkAll = false;
+        vm.selectedFiles = [];
+      } catch (error) {
+        vm.isLoading = false;
+        vm.$showAlert("파일 삭제", "파일 삭제 오류, 관리자에게 문의하세요.");
+        vm.checkAll = false;
+        vm.selectedFiles = [];
+      }
+    },
+
+    async fileConfirm(type) {
+      const vm = this;
+
+      if (
+        !(await vm.$showConfirm(
+          "파일 " + type,
+          "선택한 파일을 " + type + " 하시겠습니까?"
+        ))
+      ) {
+        return;
+      }
+
+      let file = vm.selectedFiles[0];
+      let index = file.path.lastIndexOf("/");
+
+      if (file.path.substring(0, index) === file.movePath) {
+        vm.$showAlert(
+          "파일 " + type,
+          "현재 파일 경로와 동일합니다. 다른 경로를 선택해주세요."
+        );
+        return;
+      }
+
+      if (type === "복사") {
+        vm.copy(vm.selectedFiles, "confirm", false);
+      } else {
+        vm.move(vm.selectedFiles, [], "confirm", false);
+      }
+    },
+    async move(fileList, removeFolderList, type, check) {
+      const vm = this;
+
+      fileList.forEach((file) => {
+        if (!file.movePath) {
+          file.movePath = vm.modalSeletedNode;
+        }
+      });
+
+      vm.isLoading = true; // 로딩 시작
+      try {
+        const response = await axios.put(
+          "/files/move/" + vm.connection.host_code,
+          {
+            fileList: fileList,
+            removeFolder: removeFolderList,
+            type: type,
+            check: check,
+          }
+        );
+
+        vm.isLoading = false; // 로딩 해제
+        if (response.data.checkMessage.status === 100) {
+          let returnFileList = response.data.resultData.fileList;
+          let folderList = response.data.resultData.removeFolderList;
+          const moveOrCopy = await vm.$showRadioConfirm(
+            "파일 이동",
+            response.data.checkMessage.message
+          );
+          vm.move(
+            returnFileList,
+            folderList,
+            moveOrCopy.type,
+            moveOrCopy.checkBox
+          );
+        } else {
+          let folderList = response.data.resultData.removeFolderList;
+          vm.checkAll = false;
+          vm.selectedFiles = [];
+
+          if (folderList.length != 0) {
+            vm.removeFolder(folderList);
+          } else {
+            await vm.fileSelectList();
+          }
+          vm.$showAlert("파일 이동", response.data.checkMessage.message);
+          vm.closeTreeModal();
+        }
+      } catch (error) {
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert("파일 이동", "파일 이동 오류, 관리자에게 문의하세요.");
+        vm.checkAll = false;
+        vm.selectedFiles = [];
+        vm.closeTreeModal();
+      }
+    },
+
+    async copy(fileList, type, check) {
+      const vm = this;
+
+      fileList.forEach((file) => {
+        if (!file.movePath) {
+          file.movePath = vm.modalSeletedNode;
+        }
+      });
+
+      vm.isLoading = true; // 로딩 시작
+      try {
+        const response = await axios.put(
+          "/files/copy/" + vm.connection.host_code,
+          { fileList: fileList, type: type, check: check }
+        );
+
+        vm.isLoading = false; // 로딩 해제
+        if (response.data.checkMessage.status === 100) {
+          let returnFileList = response.data.resultData.fileList;
+          const moveOrCopy = await vm.$showRadioConfirm(
+            "파일 복사",
+            response.data.checkMessage.message
+          );
+          vm.copy(returnFileList, moveOrCopy.type, moveOrCopy.checkBox);
+        } else {
+          vm.$showAlert("파일 복사", response.data.checkMessage.message);
+          vm.checkAll = false;
+          vm.selectedFiles = [];
+          vm.closeTreeModal();
+        }
+      } catch (error) {
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert("파일 복사", "파일 이동 오류, 관리자에게 문의하세요.");
+        vm.checkAll = false;
+        vm.selectedFiles = [];
+        vm.closeTreeModal();
+      }
+    },
+
+    async removeFolder(folderList) {
+      const vm = this;
+
+      vm.isLoading = false; // 로딩 해제
+
+      try {
+        const response = await axios.delete(
+          "/files/folder/" + vm.connection.host_code,
+          { data: folderList }
+        );
+
+        vm.isLoading = false; // 로딩 해제
+        vm.getChildren(vm.selectItem.id, vm.selectItem.children); // 자식 폴더 리스트 업데이트
+        await vm.fileSelectList();
+        if (response.data.status === 500) {
+          vm.$showAlert("파일 이동", response.data.message);
+        }
+      } catch (error) {
+        vm.isLoading = false; // 로딩 해제
+        vm.$showAlert(
+          "파일 이동",
+          "원본 폴더 삭제 오류, 관리자에게 문의하세요."
+        );
+      }
+    },
+
+    // 미리보기 모달 닫기
+    previewModalClose() {
+      this.previewModalOpen = false;
+      this.dataTable = {};
+    },
+
+    // 파일 미리보기
+    preview(file) {
+      // 허용된 파일 확장자 검사
+      const allowedExtensions = ["xlsx", "xls", "csv", "json"];
+
+      if (!allowedExtensions.includes(file.extension.toLowerCase())) {
+        this.$showAlert(
+          "파일 미리보기",
+          "엑셀, CSV, JSON 파일만 읽을 수 있습니다."
+        );
+        return;
+      }
+
+      this.previewModalOpen = true;
+      this.fileRead(file);
+    },
+
+    // 파일 미리보기 - 읽기
+    async fileRead(file) {
+      const vm = this;
+      vm.isLoading = true;
+      axios
+        .post("/files/read/" + vm.connection.host_code, {
+          path: file.path,
+          fileName: file.text,
+          extension: file.extension,
+          type: "file",
+          datasetId: "",
+          viewMode: true,
+        })
+        .then((response) => {
+          vm.dataTable = response.data.resultData.dataTableMap;
+        })
+        .catch(() => {
+          vm.isLoading = false;
+        });
+    },
+    selectEncoding(encoding) {
+      // 선택한 인코딩에 대한 처리
+      this.encodingType = encoding;
+      console.log(`${encoding} 인코딩이 선택되었습니다.`);
+      this.closeExcel(); // 선택 후 모달 닫기
+    },
+    closeExcel() {
+      this.isOpenExcel = false; // 모달 상태를 false로 설정+
+      this.exportToCSV();
+    },
+    // 노드 설정 저장 후 닫기
+    fnUpdateSetup(jobItem) {
+      console.log("값을 보자 ", jobItem);
+      if (
+        !jobItem.dataTable.rowData ||
+        jobItem.dataTable.rowData.length === 0
+      ) {
+        // rowData가 null이거나 빈 리스트일 때 실행할 코드
+        this.$showAlert("엑셀 생성 실패", "저장된 데이터가 없습니다.");
+      } else {
+        this.closeModal(); // 괄호 추가
+        this.isOpenExcel = true;
+        this.selectNode = jobItem;
+      }
+    },
+    async exportToCSV() {
+      const vm = this;
+
+      try {
+        // 컬럼 헤더 생성
+        console.log()
+        const header = this.selectNode.dataTable.columnDatas.map(
+          (col) => col.displyColumnNm
+        );
+
+        // 데이터 준비 (rowData를 객체로 변환)
+        const data = this.selectNode.dataTable.rowData.map((row) => {
+          const rowObject = {};
+          this.selectNode.dataTable.columnDatas.forEach((col, index) => {
+            rowObject[col.displyColumnNm] = row[index];
+          });
+          return rowObject;
+        });
+
+        // CSV 데이터 생성
+        const csvContent = [
+          header.join(","),
+          ...data.map((row) =>
+            header
+              .map((col) => {
+                // null, undefined 처리 및 쉼표가 포함된 데이터 처리
+                const cellData = row[col] ?? "";
+                return typeof cellData === "string" && cellData.includes(",")
+                  ? `"${cellData}"`
+                  : cellData;
+              })
+              .join(",")
+          ),
+        ].join("\n");
+
+        let blob;
+        // encodingType이 설정되지 않은 경우 기본값으로 utf-8 사용
+        const encoding = this.encodingType || "utf-8";
+        console.log("@@@", encoding);
+        if (encoding === "utf-8") {
+          // UTF-8 BOM 추가
+          blob = new Blob([new Uint8Array([0xef, 0xbb, 0xbf]), csvContent], {
+            type: "text/csv;charset=utf-8",
+          });
+        } else if (encoding === "euc-kr") {
+          // EUC-KR 인코딩
+          const encoder = new TextEncoder();
+          const encodedData = encoder.encode(csvContent);
+          blob = new Blob([encodedData], {
+            type: "text/csv;charset=euc-kr",
+          });
+        } else {
+          console.log("지원되지 않는 인코딩 타입입니다: " + encoding);
+        }
+
+        if (!blob) {
+          console.log("Blob 생성 실패");
+        }
+
+        // 파일 이름 설정
+        const fileName = `${vm.selectNode.node_type}_${vm.$getFullFileTime(
+          new Date()
+        )}.csv`;
+
+        // 서버에 업로드
+        const formData = new FormData();
+        formData.append("uploadFile", blob, fileName);
+        formData.append("path", this.connection.path);
+        formData.append("host_code", this.connection.host_code);
+
+        const response = await axios.post("/files/formDataUpload", formData, {
+          headers: {
+            "Content-Type": "multipart/form-data",
+          },
+        });
+
+        vm.$showAlert("업로드", response.data.message);
+        vm.fileSelectList();
+      } catch (error) {
+        console.error("CSV 내보내기 중 오류 발생:", error);
+        vm.$showAlert("업로드", "업로드 오류, 관리자에게 문의하세요.");
+      }
+    },
+    // 노드 생성
+    fnCreateNode(type) {
+      let node = Object.assign({}, this.$getDefaultJobGroup().node);
+      node.id = Math.floor(Math.random() * 1000) + 1;
+      node.node_id = node.id;
+      node.label = type;
+      node.node_name = type;
+      node.node_type = type;
+      node.position = {
+        x: Math.floor(Math.random() * 500) + 1,
+        y: Math.floor(Math.random() * 500) + 1,
+      };
+      let tempData = JSON.parse(JSON.stringify(node));
+      tempData.data = null;
+      node.data = tempData;
+      switch (type) {
+        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 "EHOJO_READ":
+          node.itm = Object.assign(
+            {},
+            this.$getDefaultJobGroup().connectionEhojo
+          );
+          break;
+      }
+      this.selectNode = node;
+    },
+  },
+
+  watch: {
+    // 모달타입에 맞는 모달창 화면 보여주기
+    modalType: function (newValue, oldValue) {
+      if (this.modalType != null) {
+        this.openModal(newValue);
+      }
+    },
+    selectedHostCodeData(newVal) {
+      if (newVal !== null && !this.isUpdating) {
+        this.isUpdating = true;  // 업데이트 시작
+
+        // newVal이 이제 인덱스입니다
+        this.selectedDirectory = newVal;
+        this.selectedHostData = this.hostList[newVal];
+
+        this.$nextTick(() => {
+          this.isUpdating = false;  // 업데이트 완료
+        });
+      }
+    },
+
+    selectedDirectory(newVal) {
+      if (newVal !== null && !this.isUpdating) {
+        this.isUpdating = true;  // 업데이트 시작
+
+        // newVal이 이제 인덱스입니다
+        this.selectedHostCodeData = newVal;
+        this.selectedHostData = this.hostList[newVal];
+
+        this.$nextTick(() => {
+          this.isUpdating = false;  // 업데이트 완료
+        });
+      }
+    }
+  },
+  components: {
+    SvgIcon: SvgIcon,
+    TreeItem: TreeItem,
+    TreeModal: TreeModal,
+    FileDataRead: FileDataRead,
+    DatabaseConnection: DatabaseConnection,
+    apiConnection: apiConnection,
+    XLSX: XLSX,
+    EhojoConnection: EhojoConnection,
+  },
+  mounted() {
+    this.userauth = this.$authorChk();
+    this.selectHosts();
+    this.searchInit();
+  },
+};
+</script>
+<style>
+.tabs-container {
+  margin-top: 1rem;
+}
+
+.tab-btn {
+  padding: 0.5rem 1rem;
+  margin-right: 0.5rem;
+  border: 1px solid #ddd;
+  border-bottom: none;
+  border-radius: 4px 4px 0 0;
+  cursor: pointer;
+  background: #f5f5f5;
+}
+
+.tab-btn.active {
+  background: #fff;
+  border-bottom: 2px solid #007bff;
+  color: #007bff;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
package-lock.json
--- package-lock.json
+++ package-lock.json
@@ -26,6 +26,7 @@
         "html2canvas": "^1.4.1",
         "https": "^1.0.0",
         "lodash": "^4.17.21",
+        "moment": "^2.30.1",
         "new-line": "^1.1.1",
         "pg": "8.8.0",
         "primeicons": "^6.0.1",
@@ -7388,6 +7389,7 @@
       "version": "2.30.1",
       "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
       "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+      "license": "MIT",
       "engines": {
         "node": "*"
       }
package.json
--- package.json
+++ package.json
@@ -21,6 +21,7 @@
     "html2canvas": "^1.4.1",
     "https": "^1.0.0",
     "lodash": "^4.17.21",
+    "moment": "^2.30.1",
     "new-line": "^1.1.1",
     "pg": "8.8.0",
     "primeicons": "^6.0.1",
Add a comment
List