박민혁 박민혁 01-15
24015 박민혁 commit
@bdc4fdb8df9b31c367c1710427972bee87e7a715
 
.gitignore (added)
+++ .gitignore
@@ -0,0 +1,3 @@
+client/build/
+server/logs/
+node_modules/(파일 끝에 줄바꿈 문자 없음)
 
Global.js (added)
+++ Global.js
@@ -0,0 +1,17 @@
+const PROJECT_NAME = 'NodeJS Web Server Framework(Vue)';
+const PROJECT_VERSION = '1.0';
+const BASE_DIR = __dirname;
+const LOG_BASE_DIR = `${__dirname}/server/logs`;
+const SERVICE_STATUS = process.env.NODE_ENV;//development, production
+const PORT = 80;
+const API_SERVER_HOST = "localhost:8080";
+
+module.exports = {
+    PROJECT_NAME,
+    PROJECT_VERSION,
+    BASE_DIR,
+    LOG_BASE_DIR,
+    SERVICE_STATUS,
+    PORT,
+    API_SERVER_HOST
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/css/common.css (added)
+++ client/resources/css/common.css
@@ -0,0 +1,236 @@
+@charset "utf-8";
+
+/* 정렬 */
+.flex {
+    display: flex;
+    flex-wrap: wrap;
+}
+
+.flex-column {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+}
+
+
+.justify-start {
+    justify-content: flex-start;
+}
+
+.justify-center {
+    justify-content: center;
+}
+
+.justify-between {
+    justify-content: space-between;
+}
+
+.justify-end {
+    justify-content: flex-end;
+}
+
+.align-start {
+    align-items: flex-start;
+}
+
+.align-center {
+    align-items: center;
+}
+
+.align-end {
+    align-items: flex-end;
+}
+
+
+
+/* 정렬 배율 */
+
+.flex5 {
+    flex: 0 0 4.5%
+}
+
+.flex10 {
+    flex: 0 0 9.5%
+}
+
+.flex15 {
+    flex: 0 0 14.5%
+}
+
+.flex20 {
+    flex: 0 0 19.5%
+}
+
+.flex25 {
+    flex: 0 0 24.5%
+}
+
+.flex30 {
+    flex: 0 0 29.5%
+}
+
+
+.flex40 {
+    flex: 0 0 39.5%
+}
+
+.flex50 {
+    flex: 0 0 49.5%
+}
+
+
+.flex60 {
+    flex: 0 0 59.5%
+}
+
+
+.flex70 {
+    flex: 0 0 69.5%
+}
+
+.flex80 {
+    flex: 0 0 79.5%
+}
+
+
+.flex90 {
+    flex: 0 0 89.5%
+}
+
+
+
+/* btn */
+.small-btn {
+    padding: 5px 10px;
+    border-radius: 5px;
+}
+
+.large-btn {
+    width: 100%;
+    padding: 5px 15px;
+    border-radius: 5px;
+}
+
+.icon-btn{
+    padding: 5px;
+    border-radius: 50%;
+}
+
+.logout-btn {
+    padding: 5px 10px;
+    color: #aaa;
+    position: relative;
+}
+
+.logout-btn::before {
+    content: "";
+    width: 1px;
+    height: 10px;
+    position: absolute;
+    top: 50%;
+    left: 0;
+    transform: translateY(-50%);
+    background-color: #aaa;
+}
+
+.blue-btn,
+.blue-border-btn:hover {
+    background-color: #213f99;
+    color: #fff;
+    transition: all 0.3s ease-in-out;
+}
+
+.red-btn,
+.red-border-btn:hover {
+    background-color: #ff5d5d;
+    color: #fff;
+    transition: all 0.3s ease-in-out;
+}
+
+.green-btn,
+.green-border-btn:hover {
+    background-color: #13833b;
+    color: #fff;
+    transition: all 0.3s ease-in-out;
+}
+
+.orange-btn,
+.orange-border-btn:hover {
+    background-color: #f29600;
+    color: #fff;
+    transition: all 0.3s ease-in-out;
+}
+
+.darkg-btn,
+.darkg-border-btn:hover {
+    background-color: #434343;
+    color: #fff;
+    transition: all 0.3s ease-in-out;
+}
+
+.gray-btn,
+.gray-border-btn:hover {
+    background-color: #eee;
+    color: #333;
+    transition: all 0.3s ease-in-out;
+}
+
+
+.blue-border-btn {
+    border: 1px solid #213f99;
+    color: #213f99;
+    background-color: #fff;
+}
+
+.red-border-btn {
+    border: 1px solid #ff5d5d;
+    color: #ff5d5d;
+    background-color: #fff;
+}
+
+.green-border-btn {
+    border: 1px solid #13833b;
+    color: #13833b;
+    background-color: #fff;
+}
+
+.orange-border-btn {
+    border: 1px solid #f29600;
+    color: #f29600;
+    background-color: #fff;
+}
+
+.darkg-border-btn {
+    border: 1px solid #434343;
+    color: #434343;
+    background-color: #fff;
+}
+
+.gray-border-btn {
+    border: 1px solid #aaa;
+    color: #aaa;
+    background-color: #fff;
+}
+
+.tp-btn{
+    background-color: transparent;
+    width: 15px;
+    height: 15px;
+    margin-left: 10px;
+}
+
+
+
+
+/* text 정렬 */
+.text-lf {
+    text-align: left;
+}
+
+.text-ct {
+    text-align: center;
+}
+
+.text-rg {
+    text-align: right;
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/css/font.css (added)
+++ client/resources/css/font.css
@@ -0,0 +1,27 @@
+@font-face {
+    font-family: 'Pretendard';
+    src: url('../font/PretendardVariable.woff2') format('woff');
+    font-weight: 400;
+    font-style: normal;
+}
+
+@font-face {
+    font-family: "GmarketSansM";
+    src: url("/client/resources/font/GmarketSansMedium.woff") format("woff");
+    font-weight: normal;
+    font-style: normal;
+}
+
+@font-face {
+    font-family: "GmarketSansL";
+    src: url("/client/resources/font/GmarketSansLight.woff") format("woff");
+    font-weight: normal;
+    font-style: normal;
+}
+
+@font-face {
+    font-family: "GmarketSansB";
+    src: url("/client/resources/font/GmarketSansBold.woff") format("woff");
+    font-weight: bold;
+    font-style: normal;
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/css/layout.css (added)
+++ client/resources/css/layout.css
@@ -0,0 +1,220 @@
+@charset "utf-8";
+
+.dashboard-wrap {
+    width: 100%;
+    height: 100%;
+    display: grid;
+    grid-template-columns: 270px minmax(auto, 1fr);
+    grid-template-rows: auto 1fr;
+    grid-template-areas:
+        "header header "
+        "nav  main  "
+        "nav  main  "
+}
+
+.layout-wrap{
+    width: 100%;
+    min-height: 100%;
+    display: flex;
+    flex-direction: column;  
+}
+
+header {
+    background-color: #fff;
+    padding: 15px 30px;
+    grid-area: header;
+    position: relative;
+}
+
+.logo {
+    width: 200px;
+}
+
+.logo>a {
+    display: block;
+    width: 100%;
+}
+
+.logo>a>img {
+    display: block;
+    width: 100%;
+}
+
+
+.user-name {
+    font-size: 1.4rem;
+    margin-left: 5px;
+}
+
+.sms,
+.user {
+    margin-left: 10px;
+}
+
+
+/* 메뉴 공통 */
+nav {
+    background-color: #213f99;
+    position: relative;
+}
+
+nav ul li a,
+nav ul li p {
+    padding: 5px;
+    display: block;
+    color: #fff;
+}
+
+nav.side-menu ul.sub-menu,
+nav.top-menu ul.sub-menu {
+    font-size: 1.4rem;
+    overflow: hidden;
+    transition: all 0.5s ease-in-out;
+}
+
+ul.sub-menu>li {
+    padding: 10px 20px;
+
+}
+
+p.active {
+    background-color: #fff;
+    color: #213f99;
+    border-radius: 50px
+}
+
+/* 사이드 메뉴 */
+nav.side-menu {
+    width: 100%;
+    height: 100%;
+    border-radius: 0 70px 70px 0;
+    grid-area: nav;
+    overflow-y: auto;
+}
+
+nav.side-menu::-webkit-scrollbar {
+    display: none;
+}
+
+nav.side-menu::-ms-scrollbar {
+    display: none;
+}
+
+
+nav.side-menu>ul.main-menu {
+    padding: 50px 30px;
+}
+
+nav.side-menu>ul.main-menu>li>div>a,
+nav>ul>li {
+    padding: 10px 0;
+    font-size: 1.4rem;
+    font-weight: bold;
+}
+
+
+
+/* 상단 메뉴 */
+.top-menu>ul.main-menu {
+    display: flex;
+    justify-content: center;
+}
+
+.depth1{
+    cursor: default;
+}
+
+.top-menu>ul>li {
+    min-width: 161px;
+    text-align: center;
+    position: relative;
+}
+
+.top-menu ul.sub-menu {
+    position: absolute;
+    width: 100%;
+    background-color: #fff;
+    z-index: 10000;
+    bottom: 0;
+    left: 0;
+    transform: translateY(100%);
+}
+
+.top-menu ul.sub-menu li a {
+    text-align: center;
+    color: #213f99;
+}
+
+.navbg {
+    overflow: hidden;
+    background-color: #fff;
+    border-bottom: 2px solid #213f99;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    transform: translateY(100%);
+    width: 100%;
+    z-index: 9999;
+}
+
+.top-menu ul.sub-menu.hide,
+.navbg.hide {
+    animation: slideUp 0.5s forwards;
+}
+
+.top-menu ul.sub-menu.show {
+    animation: slideDown 0.8s forwards;
+}
+
+.navbg.show {
+    animation: bgSlideDown 0.5s forwards;
+}
+
+/* 메인 */
+.main {
+    padding: 20px;
+    height: 100%;
+    grid-area: main;
+}
+
+.layout-wrap .main {
+    display: flex;
+    padding: 20px;
+    flex-grow: 1;  
+    /* overflow: auto;  */
+    grid-area: main;
+}
+
+
+
+
+
+@keyframes slideDown {
+    0% {
+        height: 0;
+    }
+
+    100% {
+        height: auto;
+    }
+}
+
+@keyframes slideUp {
+    0% {
+        height: auto;
+    }
+
+    100% {
+        height: 0;
+    }
+}
+
+@keyframes bgSlideDown {
+    0% {
+        height: 0;
+    }
+
+    100% {
+        height: 240px;
+    }
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/css/reset.css (added)
+++ client/resources/css/reset.css
@@ -0,0 +1,131 @@
+@charset "utf-8";
+
+* {
+    padding: 0;
+    margin: 0;
+    box-sizing: border-box;
+}
+
+html,
+body,
+#root {
+    width: 100%;
+    height: 100%;
+    min-height: 100vh;
+    font-size: 10px;
+    color: #333;
+    font-family: 'Pretendard';
+}
+
+body {
+    min-width: 1356px;
+    background-color: #f7f6fb;
+}
+
+
+a {
+    color: #333;
+    text-decoration: none;
+}
+
+ol,
+ul,
+li {
+    list-style: none;
+}
+
+img,
+svg {
+    width: 100%;
+    vertical-align: middle;
+}
+
+table {
+    width: 100%;
+    border-collapse: collapse;
+    table-layout: fixed;
+    margin: 15px 0;
+}
+
+table th,
+table td {
+    padding: 8px;
+    font-size: 1.4rem;
+}
+
+button {
+    border: none;
+    background-color: transparent;
+    font-size: 1.4rem;
+    margin-left: 10px;
+    cursor: pointer;
+}
+
+label {
+    font-size: 1.4rem;
+    margin-left: 5px;
+}
+
+select,
+input {
+    min-width: 100px;
+    width: 100%;
+    padding: 5px 10px;
+    border: 1px solid #eee;
+    border-radius: 5px;
+    margin-left: 5px;
+}
+
+input[type="text"] {
+    min-width: 300px;
+}
+
+input:focus {
+    outline: none;
+}
+
+input[type="checkbox"],
+input[type="radio"] {
+    vertical-align: sub;
+}
+
+input[type='date'] {
+    max-width: 240px;
+    padding: 5px;
+}
+
+input[type='date']::before {
+    content: attr(data-placeholder);
+    width: 100%;
+    color: #ddd;
+}
+
+button:first-child,
+select:first-child,
+input:first-child {
+    margin-left: 0;
+}
+
+
+/* 스크롤바 디자인 */
+::-webkit-scrollbar {
+    width: 8px;
+    height: 8px;
+}
+
+::-webkit-scrollbar-thumb {
+    background-color: #ededed;
+    border-radius: 10px;
+}
+
+::-webkit-scrollbar-track {
+    background-color: #fff;
+    border-radius: 10px;
+}
+
+
+@media screen and (min-width:1356px) and (max-width: 1536px) {
+	html{
+        font-size: 9px;
+    }
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/css/style.css (added)
+++ client/resources/css/style.css
@@ -0,0 +1,194 @@
+@charset "utf-8";
+
+/* box 공통 */
+.container {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    flex-grow: 1;
+}
+
+.content-box {
+    width: 100%;
+    height: 100%;
+}
+
+.left-content,
+.right-content {
+    height: 100%;
+}
+
+.content {
+    width: 100%;
+    height: 100%;
+    margin-bottom: 30px;
+}
+
+.content:last-child {
+    margin-bottom: 0;
+}
+
+.left-content,
+.right-content,
+.top-content,
+.bottom-content,
+.content {
+    padding: 15px;
+    border-radius: 10px;
+    background-color: #fff;
+    position: relative;
+}
+
+.form-box {
+    background-color: #edf0ff;
+    border: 1px solid #dbe3fb;
+}
+
+/* title 공통 */
+.page-titleZone {
+    margin-bottom: 20px;
+}
+
+.main-title {
+    font-family: 'GmarketSansB';
+    font-size: 2rem;
+    color: #213f99;
+}
+
+.content-titleZone {
+    padding-bottom: 10px;
+    border-bottom: 1px solid #aaa;
+    margin-bottom: 15px;
+}
+
+.box-title {
+    font-size: 1.6rem;
+    font-weight: bold;
+}
+
+/* 테이블 공통 */
+.list-table {
+    border-bottom: 1px solid #aaa;
+}
+
+.list-table th,
+.list-table td {
+    text-align: center;
+    border-top: 1px solid #aaa;
+}
+
+.list-table thead tr {
+    background-color: #dbe3fb;
+    color: #213f99;
+}
+
+.list-table tbody tr:nth-child(even) {
+    background-color: #f4f6ff;
+}
+
+.form-table th {
+    color: #213f99;
+    text-align: center;
+}
+
+
+
+.option-table th {
+    text-align: left;
+}
+
+
+/* 서치바 공통 */
+/* .table-zone */
+.searchbar-zone {
+    margin-bottom: 10px;
+}
+
+/* 기본 서치바 */
+.search-square {
+    position: relative;
+    margin-left: 5px;
+}
+
+.square-date,
+.square-select {
+    width: 150px;
+}
+
+.square-input {
+    color: #646464;
+    padding: 6px 10px;
+    border-radius: 5px;
+    width: 300px;
+    transition: all ease-in-out .5s;
+}
+
+.square-input:hover,
+.square-input:focus {
+    box-shadow: 0 0 1em #00000013;
+}
+
+.square-input:focus {
+    outline: none;
+    background-color: #f0eeee;
+}
+
+.square-input::-webkit-input-placeholder {
+    font-weight: 100;
+    color: #ccc;
+}
+
+.square-input:focus {
+    background-color: #f0eeee;
+}
+
+.square-button {
+    border: none;
+    margin-left: 0;
+    position: absolute;
+    right: 5px;
+    top: 50%;
+    transform: translateY(-50%);
+}
+
+.square-button:hover {
+    cursor: pointer;
+}
+
+.square-icon {
+    color: #b4b4b4;
+}
+
+/* 상세 서치바 */
+.search-bottom{
+    padding: 10px 0;
+}
+.option-searchbar {
+    width: 50%;
+    margin: 0 auto;
+    padding: 15px;
+    background-color: #f8f9fe;
+    border-radius: 10px;
+}
+
+/* 기타 공통 */
+.coupler {
+    font-size: 1.4rem;
+    margin-left: 5px;
+}
+
+.option-title {
+    padding: 0 5px;
+    font-size: 1.4rem;
+    color: #213f99;
+    margin-left: 5px;
+}
+
+.count-zone {
+    font-size: 1.4rem;
+}
+
+.count-zone span{
+    font-weight: 900;
+    color: #213f99;
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/font/GmarketSansBold.woff (Binary) (added)
+++ client/resources/font/GmarketSansBold.woff
Binary file is not shown
 
client/resources/font/GmarketSansLight.woff (Binary) (added)
+++ client/resources/font/GmarketSansLight.woff
Binary file is not shown
 
client/resources/font/GmarketSansMedium.woff (Binary) (added)
+++ client/resources/font/GmarketSansMedium.woff
Binary file is not shown
 
client/resources/font/PretendardVariable.woff2 (Binary) (added)
+++ client/resources/font/PretendardVariable.woff2
Binary file is not shown
 
client/resources/img/logo.png (Binary) (added)
+++ client/resources/img/logo.png
Binary file is not shown
 
client/resources/img/logo_w.png (Binary) (added)
+++ client/resources/img/logo_w.png
Binary file is not shown
 
client/views/component/FileTree.vue (added)
+++ client/views/component/FileTree.vue
@@ -0,0 +1,100 @@
+<template>
+    <v-jstree :data="fileTree" show-checkbox multiple allow-batch whole-row @item-click="itemClick"></v-jstree>
+</template>
+
+<script>
+import VJstree from 'vue-jstree'
+export default {
+    data() {
+        return {
+            fileTree: [
+                {
+                    "text": "Same but with checkboxes",
+                    "children": [
+                        {
+                            "text": "initially selected",
+                            "selected": true
+                        },
+                        {
+                            "text": "custom icon",
+                            "icon": "fa fa-warning icon-state-danger"
+                        },
+                        {
+                            "text": "initially open",
+                            "icon": "fa fa-folder icon-state-default",
+                            "opened": true,
+                            "children": [
+                                {
+                                    "text": "Another node"
+                                }
+                            ]
+                        },
+                        {
+                            "text": "custom icon",
+                            "icon": "fa fa-warning icon-state-warning"
+                        },
+                        {
+                            "text": "disabled node",
+                            "icon": "fa fa-check icon-state-success",
+                            "disabled": true
+                        }
+                    ]
+                },
+                {
+                    "text": "Same but with checkboxes",
+                    "opened": true,
+                    "children": [
+                        {
+                            "text": "initially selected",
+                            "selected": true
+                        },
+                        {
+                            "text": "custom icon",
+                            "icon": "fa fa-warning icon-state-danger"
+                        },
+                        {
+                            "text": "initially open",
+                            "icon": "fa fa-folder icon-state-default",
+                            "opened": true,
+                            "children": [
+                                {
+                                    "text": "Another node"
+                                }
+                            ]
+                        },
+                        {
+                            "text": "custom icon",
+                            "icon": "fa fa-warning icon-state-warning"
+                        },
+                        {
+                            "text": "disabled node",
+                            "icon": "fa fa-check icon-state-success",
+                            "disabled": true
+                        }
+                    ]
+                },
+                {
+                    "text": "And wholerow selection"
+                }
+            ]
+        }
+    },
+    methods: {
+        itemClick: function (node) {
+            console.log(node.model.text + ' clicked !')
+        }
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'VJstree': VJstree
+    },
+    mounted() {
+
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/component/PageNavigation.vue (added)
+++ client/views/component/PageNavigation.vue
@@ -0,0 +1,53 @@
+<template>
+    <p class="navigate_bar"><router-link to="/">Home</router-link><span> > {{ pathName() }}</span></p>
+</template>
+
+<script>
+import { useRoute } from 'vue-router';
+export default {
+    data() {
+        return {
+            //현재 라우터의 정보
+            route: useRoute(),
+
+            // 페이지 경로 목록
+            pageList: [
+                { path: "/", name: "대시보드", },
+                { path: "/fileManagement.page", name: "파일 관리", },
+                { path: "/hostManagement.page", name: "호스트 관리", },
+                { path: "/scheduleManagement.page", name: "작업 스케줄 관리", },
+                { path: "/scheduleLogManagement.page", name: "로그 관리", },
+            ],
+        }
+    },
+    methods: {
+        pathName: function () {
+            for (let i = 0; i < this.pageList.length; i++) {
+                if (this.route.path == this.pageList[i]['path']) {
+                    return this.pageList[i]['name'];
+                }
+            }
+            return "대시보드"
+        },
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
+
+<style scoped>
+.navigate_bar {
+    padding: 10px 0px;
+    font-size: 1.4rem;
+}
+</style>
 
client/views/component/PaginationButton.vue (added)
+++ client/views/component/PaginationButton.vue
@@ -0,0 +1,170 @@
+<template lang="html">
+	<div class="component-pagination" id="pagination">
+		<div class="pagination-button-type">
+			<a class="first-page" @click="excute(1)" title="첫 번 째 페이지로 이동"></a>
+			<a class="prev" @click="excute(currentPage - 1)" title="이전 페이지로 이동"></a>
+			<a @click="excute(i)" v-for="i in createRange" :class="{ 'active': currentPage == i }">{{ i }}</a>
+			<a class="next" @click="excute(currentPage + 1)" title="다음 페이지로 이동"></a>
+			<a class="end-page" @click="excute(maxEndPage)" title="마지막 페이지로 이동"></a>
+		</div>
+	</div>
+</template>
+
+<script>
+export default {
+	props: {
+		currentPage: {
+			type: Number,
+			default: 0
+		},
+		perPage: {
+			type: Number,
+			default: 10
+		},
+		totalCount: {
+			type: Number,
+			default: 0
+		},
+		maxRange: {
+			type: Number,
+			default: 5
+		},
+		click: Function
+	},
+	emits: ['update:currentPage'],
+	data: function () {
+		return {
+			//data
+		}
+	},
+	methods: {
+		excute: function (i) {
+			if (i >= 1 && i <= this.maxEndPage) {
+				if (i != this.currentPage) {
+					this.$emit('update:currentPage', i);//부모 currentPage에 선택한 page번호 할당
+					this.click(i);//부모 function 실행
+				} else {
+					return;
+				}
+			} else {
+				alert('이동할 페이지가 없습니다.');
+			}
+		}
+	},
+	computed: {
+		startPage: function () {
+			return Math.floor((this.currentPage - 1) / this.maxRange) * this.maxRange + 1;
+		},
+		endPage: function () {
+			if (this.maxEndPage < this.currentEndPage) {
+				return this.maxEndPage;
+			} else {
+				return this.currentEndPage;
+			}
+		},
+		currentEndPage: function () {
+			return this.maxRange * Math.ceil((this.currentPage / this.maxRange));
+		},
+		maxEndPage: function () {
+			return Math.ceil(this.totalCount / this.perPage);
+		},
+		createRange: function () {
+			var range = [];
+			for (var i = this.startPage; i <= this.endPage; i++) {
+				range.push(i);
+			}
+			if (range.length == 0) {
+					range.push(1);
+			}
+			return range;
+		}
+	},
+	watch: {
+
+	},
+	//beforeCreate: function () {},
+	//created: function () {},
+	//beforeUpdate: function () {},
+	//updated: function () {},
+	mounted: function () {
+	}
+}
+</script>
+
+<style scoped>
+/*PAGINATION NEW START*/
+.component-pagination {
+	padding: 15px 0;
+}
+
+.pagination-button-type {
+	display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.pagination-button-type a {
+	display: inline-block;
+	width: 30px;
+	height: 30px;
+	padding: 5px;
+	text-align: center;
+	line-height: 20px;
+	cursor: pointer;
+}
+
+/* 정우추가 */
+.pagination-button-type a.prev:after {
+	content: "\003C";
+	font-family: 'KoPub_Dotum';
+	font-weight: 900;
+}
+
+.pagination-button-type a.next:after {
+	content: "\003E";
+	font-family: 'KoPub_Dotum';
+	font-weight: 900;
+}
+
+.pagination-button-type a.first-page:after {
+	content: "\003C\003C";
+	font-family: 'KoPub_Dotum';
+	font-weight: 900;
+}
+
+.pagination-button-type a.end-page:after {
+	content: "\003E\003E";
+	font-family: 'KoPub_Dotum';
+	font-weight: 900;
+}
+
+.pagination-button-type a.active {
+	background-color: #213f99;
+	color: #fff;
+	cursor: default !important;
+	border-radius: 50px;
+}
+
+.pagination-button-type a:hover:not(.active) {
+	background-color: #213f99;
+	color: #fff;
+	border-radius: 50px;
+}
+
+.pagination-button-type a:first-child {
+	border-top-left-radius: 0px;
+	border-bottom-left-radius: 0px;
+}
+
+.pagination-button-type a:last-child {
+	border-top-right-radius: 0px;
+	border-bottom-right-radius: 0px;
+}
+
+/*PAGINATION NEW END*/
+
+@media screen and (max-width:479px) {
+	.pagination-button-type a {
+		margin: 3px;
+	}
+}</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/index.html (added)
+++ client/views/index.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+        <meta name="description" content="Node Vue Web">
+        <link rel="icon" href="" />
+        <link rel="stylesheet" href="client/resources/css/font.css">
+        <link rel="stylesheet" href="client/resources/css/reset.css">
+        <link rel="stylesheet" href="client/resources/css/layout.css">
+        <link rel="stylesheet" href="client/resources/css/common.css">
+        <link rel="stylesheet" href="client/resources/css/style.css">
+      
+        <title>TAKEN BI MANAGER</title>
+    </head>
+
+    <body>
+        <div id="root"></div>
+        <script src="/client/build/bundle.js"></script>
+    </body>
+</html>
 
client/views/index.js (added)
+++ client/views/index.js
@@ -0,0 +1,13 @@
+/**
+ * @author : 최정우
+ * @since : 2022.10.19
+ * @dscription : Vue를 활용한 Client단 구현의 시작점(Index) Component 입니다.
+ */
+import { createApp } from "vue";
+
+import AppRouter from "./pages/AppRouter.js";
+import App from "./pages/App.vue";
+
+
+
+const vue = createApp(App).use(AppRouter).mount("#root");
 
client/views/layout/Header.vue (added)
+++ client/views/layout/Header.vue
@@ -0,0 +1,65 @@
+<template>
+    <header>
+        <div :class="className === 'side' ? 'flex-between' : ''">
+            <div class="logo" :style="logoStyle">
+                <a href="/">
+                    <img src="../../resources/img/logo.png" alt="BI MANAGER LOGO">
+                </a>
+            </div>
+            <div class="user-info flex justify-end align-center">
+                <div class="sms">
+                    <svg-icon type="mdi" :path="mdiEmail" color="#213f99"></svg-icon>
+                </div>
+                <div class="user flex align-center">
+                    <div class="user-img">
+                        <svg-icon type="mdi" :path="mdiAccountCircle" color="#213f99"></svg-icon>
+                    </div>
+                    <span class="user-name">000님</span>
+                </div>
+                <button class="logout-btn">로그아웃</button>
+            </div>
+        </div>
+    </header>
+</template>
+
+<script>
+import SvgIcon from '@jamescoyle/vue-icon'
+import { mdiEmail, mdiAccountCircle } from '@mdi/js'
+export default {
+    props: {
+        className: String
+    },
+    data() {
+        return {
+            mdiEmail: mdiEmail,
+            mdiAccountCircle: mdiAccountCircle
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+        logoStyle() {
+            if (this.className === 'top') {
+                return {
+                    position: 'absolute',
+                    top:'50%',
+                    left:'50%',
+                    transform: 'translate(-50%,-50%)'
+                }
+            } else {
+                return {}
+            }
+        }
+    },
+    components: {
+        'SvgIcon': SvgIcon
+    },
+    mounted() {
+        console.log('Header mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/layout/SideMenu.vue (added)
+++ client/views/layout/SideMenu.vue
@@ -0,0 +1,120 @@
+<template>
+    <nav class="side-menu">
+        <ul class="main-menu">
+            <li v-for="(mainMenu, idx) in menuList" :key="idx" @click="toggleMenu(mainMenu)">
+                <div class="flex-between">
+                    <p>{{ mainMenu.pathName }}</p>
+                    <svg-icon v-if="mainMenu.subMenu" type="mdi" :path="mainMenu.path" color="#fff"></svg-icon>
+                </div>
+                <ul v-if="mainMenu.subMenu" class="sub-menu" :style="{ 'max-height': mainMenu.isOpen ? '360px' : '0' }">
+                    <li v-for="(subMenu, idx) in mainMenu.subMenu" :key="idx">
+                        <router-link :to="subMenu.subPath">{{ subMenu.pathName }}</router-link>
+                    </li>
+                </ul>
+            </li>
+        </ul>
+    </nav>
+</template>
+ 
+<script>
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMenuUp, mdiMenuDown } from '@mdi/js';
+export default {
+    data() {
+        return {
+            menuList: [
+                // { path: "/", pathName: "Dashboard", },
+                {
+                    mainPath: "/", pathName: "데이터관리",
+                    subMenu: [
+                        { subPath: "/fileManagement.page", pathName: "파일관리" },
+                        { subPath: "/hostManagement.page", pathName: "호스트관리" },
+                        { subPath: "/", pathName: "데이터셋 목록" },
+                        { subPath: "/", pathName: "데이터 공유 관리" },
+                    ],
+                    isOpen: false,
+                    path: mdiMenuUp,
+                },
+                {
+                    mainPath: "/", pathName: "메타관리",
+                    subMenu: [
+                        { subPath: "/", pathName: "데이터 메타 정보" },
+                        { subPath: "/", pathName: "표준 용어 관리" },
+                    ],
+                    isOpen: false,
+                    path: mdiMenuUp,
+                },
+                {
+                    mainPath: "/", pathName: "작업관리",
+                    subMenu: [
+                        { subPath: "/scheduleManagement.page", pathName: "작업 스케줄 관리" },
+                        { subPath: "/scheduleLogManagement.page", pathName: "로그 관리" },
+                    ],
+                    isOpen: false,
+                    path: mdiMenuUp,
+                },
+                {
+                    mainPath: "/", pathName: "데이터활용",
+                    subMenu: [
+                        { subPath: "/", pathName: "데이터 활용 관리" },
+                        { subPath: "/", pathName: "데이터 활용 공유 관리" },
+                        { subPath: "/", pathName: "GIS데이터 관리" },
+                        { subPath: "/", pathName: "데이터현황 관리" },
+                        { subPath: "/", pathName: "OpenAPI 목록" },
+                    ],
+                    isOpen: false,
+                    path: mdiMenuUp,
+                },
+                {
+                    mainPath: "/", pathName: "통합관리",
+                    subMenu: [
+                        { subPath: "/", pathName: "사용자관리" },
+                        { subPath: "/", pathName: "부서관리" },
+                        { subPath: "/", pathName: "연계정보관리" },
+                    ],
+                    isOpen: false,
+                    path: mdiMenuUp,
+                },
+                {
+                    mainPath: "/", pathName: "정보관리",
+                    subMenu: [
+                        { subPath: "/", pathName: "내정보관리" },
+                        { subPath: "/", pathName: "비밀번호 변경" },
+                        { subPath: "/", pathName: "부서메일" },
+                    ],
+                    isOpen: false,
+                    path: mdiMenuUp,
+                },
+                { mainPath: "/", pathName: "ai solution", path: mdiMenuUp, },
+            ],
+
+        }
+    },
+    methods: {
+        toggleMenu: function (menu) {
+            this.menuList.forEach(item => {
+                if (item === menu) {
+                    item.isOpen = !menu.isOpen;
+                    item.path = item.isOpen ? mdiMenuDown : mdiMenuUp
+
+                } else{
+                    item.isOpen = false;
+                    item.path = mdiMenuUp;
+                }
+            });
+        },
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'SvgIcon': SvgIcon
+    },
+    mounted() {
+        console.log('Menu mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/layout/TopMenu.vue (added)
+++ client/views/layout/TopMenu.vue
@@ -0,0 +1,138 @@
+<template>
+    <nav class="top-menu">
+        <ul class="main-menu">
+            <li v-for="(mainMenu, idx) in menuList" :key="idx" @mouseover="showAllSubMenus" @mouseout="hideAllSubMenus">
+                <p class="depth1" :class="{ active: isMainMenuActive(mainMenu) }">{{ mainMenu.pathName }}</p>
+                <ul v-if="mainMenu.subMenu" class="sub-menu" v-show="showSubmenu"
+                    :class="{ show: showSubmenu, hide: !showSubmenu }">
+                    <li v-for="(subMenu, idx) in mainMenu.subMenu" :key="idx" :class="{ active: isSubMenuActive(subMenu) }">
+                        <router-link :to="subMenu.path">{{ subMenu.pathName }}</router-link>
+                    </li>
+                </ul>
+            </li>
+        </ul>
+        <div class="navbg" v-show="showSubmenu" :class="{ show: showSubmenu, hide: !showSubmenu }"></div>
+    </nav>
+</template>
+ 
+<script>
+
+export default {
+    data() {
+        return {
+            menuList: [
+                // { path: "/", pathName: "Dashboard", },
+                {
+                    pathName: "데이터관리",
+                    subMenu: [
+                        { path: "/fileManagement.page", pathName: "파일관리" },
+                        { path: "/hostManagement.page", pathName: "호스트관리" },
+                        { path: "/", pathName: "데이터셋 목록" },
+                        { path: "/", pathName: "데이터 공유 관리" },
+                    ],
+                },
+                {
+                    pathName: "메타관리",
+                    subMenu: [
+                        { path: "/", pathName: "데이터 메타 정보" },
+                        { path: "/", pathName: "표준 용어 관리" },
+                    ],
+                },
+                {
+                    pathName: "작업관리",
+                    subMenu: [
+                        { path: "/scheduleManagement.page", pathName: "작업 스케줄 관리" },
+                        { path: "/scheduleLogManagement.page", pathName: "로그 관리" },
+                    ],
+                },
+                {
+                    pathName: "데이터활용",
+                    subMenu: [
+                        { path: "/", pathName: "데이터 활용 관리" },
+                        { path: "/", pathName: "데이터 활용 공유 관리" },
+                        { path: "/", pathName: "GIS데이터 관리" },
+                        { path: "/", pathName: "데이터현황 관리" },
+                        { path: "/", pathName: "OpenAPI 목록" },
+                    ],
+                },
+                {
+                    pathName: "통합관리",
+                    subMenu: [
+                        { path: "/", pathName: "사용자관리" },
+                        { path: "/", pathName: "부서관리" },
+                        { path: "/", pathName: "연계정보관리" },
+                    ],
+                },
+                {
+                    pathName: "정보관리",
+                    subMenu: [
+                        { path: "/", pathName: "내정보관리" },
+                        { path: "/", pathName: "비밀번호 변경" },
+                        { path: "/", pathName: "부서메일" },
+                    ],
+                },
+                {
+                    pathName: "ai solution",
+                    subMenu: [
+                        { path: "/", pathName: "ai solution" },
+
+                    ],
+                },
+                // {
+                //     pathName: "템플릿",
+                //     subMenu: [
+                //         { path: "/searchbar.page", pathName: "서치바" },
+                //         { path: "/table.page", pathName: "테이블" },
+                //         { path: "/btnPosition.page", pathName: "버튼별 위치" },
+                //     ],
+                // },
+                // {
+                //     pathName: "레이아웃 템플릿",
+                //     subMenu: [
+                //         { path: "/vertical.page", pathName: "수직 레이아웃" },
+                //         { path: "/horizontal.page", pathName: "수평 레이아웃" },
+                //     ],
+                // },
+            ],
+            currentRoute: null,
+            showSubmenu: false
+        }
+    },
+    methods: {
+        showAllSubMenus() {
+            this.showSubmenu = true;
+        },
+        hideAllSubMenus() {
+            this.showSubmenu = false;
+        },
+
+        isMainMenuActive(mainMenu) {
+            return this.currentRoute && (this.currentRoute.path === mainMenu.path || this.isSubMenuActive(mainMenu.subMenu));
+        },
+
+        isSubMenuActive(subMenu) {
+            if (!subMenu) return false;
+            for (let i = 0; i < subMenu.length; i++) {
+                if (this.currentRoute.path === subMenu[i].path) {
+                    return true;
+                }
+            }
+            return false;
+        },
+    },
+    watch: {
+        $route(to, from) {
+            this.currentRoute = to;
+        },
+    },
+    computed: {
+
+    },
+    components: {
+
+    },
+    mounted() {
+        console.log('Menu mounted');
+    }
+}
+</script>
 
client/views/pages/App.vue (added)
+++ client/views/pages/App.vue
@@ -0,0 +1,57 @@
+<template>
+   <div v-cloak :class="layoutType === 'side' ? 'dashboard-wrap' : 'layout-wrap'">
+      <Header :className="layoutType" />
+      <SideMenu v-if="layoutType === 'side'" />
+      <TopMenu v-else />
+      <div class="main">
+         <PageNavigation v-if="layoutType === 'side'" />
+         <router-view />
+      </div>
+   </div>
+</template>
+
+<script>
+import Header from '../layout/Header.vue';
+import SideMenu from '../layout/SideMenu.vue';
+import TopMenu from '../layout/TopMenu.vue';
+import PageNavigation from '../component/PageNavigation.vue';
+const App = {
+   data: () => {
+      return {
+         // side혹은 top 둘중 한개 타입
+         layoutType: "top",
+      }
+   },
+   methods: {
+
+   },
+   watch: {
+
+   },
+   computed: {
+
+   },
+   components: {
+      'Header': Header,
+      'SideMenu': SideMenu,
+      'TopMenu': TopMenu,
+      'PageNavigation': PageNavigation,
+   },
+   mounted: () => {
+      console.log('Vue mounted');
+   }
+}
+
+export default App;
+</script>
+
+<style scoped>
+[v-cloak] {
+   display: none;
+}
+
+.main-warp {
+   border: 1px solid #000;
+   margin: 20px 0px;
+}
+</style>
 
client/views/pages/AppRouter.js (added)
+++ client/views/pages/AppRouter.js
@@ -0,0 +1,42 @@
+import { createWebHistory, createRouter } from "vue-router";
+
+import Main from "../pages/main/Main.vue";
+import FileManagement from "../pages/file/FileManagement.vue";
+import HostManagement from "../pages/file/HostManagement.vue";
+import ScheduleManagement from "../pages/schedule/ScheduleManagement.vue";
+import ScheduleLogManagement from "../pages/schedule/ScheduleLogManagement.vue";
+import Login from "../pages/login/Login.vue";
+
+// 템플릿 
+import Searchbar from "../template/templateElement/Searchbar.vue";
+import Table from "../template/templateElement/Table.vue";
+import BtnPosition from "../template/templateElement/BtnPosition.vue";
+import Horizontal from "../template/layoutTemplate/Horizontal.vue";
+import Vertical from "../template/layoutTemplate/Vertical.vue";
+import Guide from "../template/guide/TemplateGuide.vue";
+
+const routes = [
+  /* 메인화면 */
+  // { path: "/", name: "Main", component: Main },
+  {path: "/fileManagement.page",name: "FileManagement",component: FileManagement},
+  {path: "/hostManagement.page",name: "HostManagement",component: HostManagement},
+  {path: "/scheduleManagement.page",name: "ScheduleManagement",component: ScheduleManagement,},
+  {path: "/scheduleLogManagement.page",name: "ScheduleLogManagement",component: ScheduleLogManagement,},
+  {path: "/login.page",name: "Login",component: Login,},
+
+
+  // 템플릿 화면
+  {path: "/searchbar.page",name: "Searchbar",component: Searchbar,},
+  {path: "/table.page",name: "Table",component: Table,},
+  {path: "/btnPosition.page",name: "BtnPosition",component: BtnPosition,},
+  {path: "/horizontal.page",name: "Horizontal",component: Horizontal,},
+  {path: "/vertical.page",name: "Vertical",component: Vertical,},
+  {path: "/guide.page",name: "Guide",component: Guide,},
+];
+
+const AppRouter = createRouter({
+  history: createWebHistory(),
+  routes,
+});
+
+export default AppRouter;
 
client/views/pages/file/FileManagement.vue (added)
+++ client/views/pages/file/FileManagement.vue
@@ -0,0 +1,30 @@
+<template>
+    <div class="container vertical">
+        <div class="left-content"></div>
+        <div class="right-content"></div>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/file/HostManagement.vue (added)
+++ client/views/pages/file/HostManagement.vue
@@ -0,0 +1,30 @@
+<template>
+    <div class="container horizontal">
+        <div class="top-content"></div>
+        <div class="bottom-content"></div>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/login/Login.vue (added)
+++ client/views/pages/login/Login.vue
@@ -0,0 +1,101 @@
+<template>
+    <div class="login-page">
+        <div class="login-wrap flex-center">
+            <div class="login-content">
+                <div class="logo">
+                    <img src="../../../resources/img/logo.png" alt="">
+                </div>
+                <div class="login-zone">
+                    <div class="input-wrap">
+                        <input type="text" placeholder="아이디를 입력하세요." class="login-input"/>
+                        <input type="password" placeholder="비밀번호를 입력하세요." class="login-input"/>
+                    </div>
+                    <div class="found-zone flex-center">
+                        <router-link to="">아이디찾기</router-link>
+                        <router-link to="">비밀번호 찾기</router-link>
+                        <router-link to="">회원가입</router-link>
+                    </div>
+                </div>
+                <button class="blue-btn large-btn">로그인</button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
+
+<style scoped>
+.login-page{
+    width: 100%;
+    height: 100%;
+}
+
+.login-wrap{
+    width: 100%;
+    height: 100%;
+}
+
+.login-content{
+    min-width: 300px;
+    max-width: 400px;
+    padding:30px;
+    border: 1px solid #eee;
+    border-radius: 10px;
+    background-color: #fff;
+}
+.logo{
+    width: 100%;
+    margin-bottom: 30px;
+}
+.logo > img{
+    display: block;
+    margin: 0 auto;
+}
+
+.login-input{
+    margin-left: 0;
+    margin-bottom: 10px;
+    padding: 10px;
+    border: 1px solid #f1f5ff;
+}
+
+.login-input:last-child{
+    margin-bottom: 0;
+}
+
+.found-zone{
+  padding: 20px 0;
+}
+
+.found-zone > a{
+    font-size: 1.4rem;
+    padding: 0 10px;
+}
+
+.login-page .blue-btn{
+    margin-left: 0;
+    padding: 10px 0;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/main/Main.vue (added)
+++ client/views/pages/main/Main.vue
@@ -0,0 +1,27 @@
+<template>
+    <div>Main.vue</div>
+</template>
+
+<script>
+
+export default {
+    data () {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/schedule/ScheduleLogManagement.vue (added)
+++ client/views/pages/schedule/ScheduleLogManagement.vue
@@ -0,0 +1,29 @@
+<template>
+    <div class="container horizontal">
+        <div class="content"></div>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/schedule/ScheduleManagement.vue (added)
+++ client/views/pages/schedule/ScheduleManagement.vue
@@ -0,0 +1,30 @@
+<template>
+    <div class="container horizontal">
+        <div class="top-content"></div>
+        <div class="bottom-content"></div>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/template/guide/TemplateGuide.vue (added)
+++ client/views/template/guide/TemplateGuide.vue
@@ -0,0 +1,49 @@
+<template>
+    <div>
+        <div class="content">
+            <div class="content-titleZone">
+                <p class="box-title">레이아웃</p>
+                <p>레이아웃은 2가지 형태로 나뉘며 좌우 아님 상하로 나뉘어짐</p>
+            </div>
+            <table class="option-table">
+                <colgroup>
+                    <col>
+                </colgroup>
+                <tbody>
+                    <tr>
+                        <th>상하(수평레이아웃)</th>
+                        <td></td>
+                    </tr>
+                    <tr>
+                        <th>좌우(수직레이아웃)</th>
+                        <td></td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/template/layoutTemplate/Horizontal.vue (added)
+++ client/views/template/layoutTemplate/Horizontal.vue
@@ -0,0 +1,228 @@
+<template>
+    <div class="container">
+        <div class="page-titleZone flex justify-between">
+            <p class="main-title">페이지 제목을 적어주세요</p>
+            <PageNavigation />
+        </div>
+        <div class="content">
+            <div class="top-content">
+                <div class="content-titleZone">
+                    <p class="box-title">목록</p>
+                </div>
+                <div class="searchbar-zone">
+                    <div class="flex justify-end align-center">
+                        <span class="option-title">검증유형</span>
+                        <select name="" id="" class="square-select">
+                            <option value="all">전체</option>
+                        </select>
+                        <span class="option-title">품질지표명</span>
+                        <select name="" id="" class="square-select">
+                            <option value="all">전체</option>
+                        </select>
+                        <div class="search-square">
+                            <input type="text" class="square-input" placeholder="Search">
+                            <button class="square-button">
+                                <svg-icon type="mdi" :path="searchPath" class="square-icon"></svg-icon>
+                            </button>
+                        </div>
+                    </div>
+                </div>
+                <div class="table-zone">
+                    <div class="flex justify-between align-center">
+                        <div class="count-zone">
+                            <p>총 <span>40</span>건 중 <span>01</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">
+                        <colgroup>
+                            <col style="width: ;">
+                            <col style="width: ;">
+                            <col style="width: ;">
+                            <col style="width: ;">
+                            <col style="width: ;">
+                        </colgroup>
+                        <thead>
+                            <tr>
+                                <th><input type="checkbox" name="" id=""></th>
+                                <th>No</th>
+                                <th>user ID</th>
+                                <th>이름</th>
+                                <th>생년월일</th>
+                                <th>성별</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                            <tr>
+                                <td><input type="checkbox" name="" id=""></td>
+                                <td>1</td>
+                                <td>test123</td>
+                                <td>홍길동</td>
+                                <td>1999.01.01</td>
+                                <td>여</td>
+                            </tr>
+                        </tbody>
+                    </table>
+                    <div class="flex justify-end">
+                        <button class="red-border-btn small-btn">선택항목 삭제</button>
+                    </div>
+                </div>
+                <PaginationButton />
+            </div>
+            <div class="bottom-content form-box">
+                <div class="content-titleZone">
+                    <p class="box-title">등록</p>
+                </div>
+                <div class="table-zone">
+                    <table class="form-table">
+                        <colgroup>
+                            <col style="width: 10%;">
+                            <col style="width: 40%;">
+                            <col style="width: 10%;">
+                            <col style="width: 40%;">
+                        </colgroup>
+                        <tbody>
+                            <tr>
+                                <th>user ID</th>
+                                <td><input type="text" name="" id=""></td>
+                                <th>이름</th>
+                                <td><input type="text" name="" id=""></td>
+                            </tr>
+                            <tr>
+                                <th>생년월일</th>
+                                <td>
+                                    <div class="flex justify-between ">
+                                        <select name="" id="" class="flex30"></select>
+                                        <select name="" id="" class="flex30"></select>
+                                        <select name="" id="" class="flex30"></select>
+                                    </div>
+                                </td>
+                                <th>성별</th>
+                                <td>
+                                    <select name="" id=""></select>
+                                </td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+                <div class="flex justify-end">
+                    <button class="blue-border-btn small-btn">등록</button>
+                    <!-- 값이 있을때 수정 버튼으로 변경 -->
+                    <!-- <button class="orange-border-btn small-btn">수정</button> -->
+                    <button class="darkg-border-btn small-btn">초기화</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import PageNavigation from '../../component/PageNavigation.vue';
+import PaginationButton from '../../component/PaginationButton.vue';
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify } from '@mdi/js';
+export default {
+    data() {
+        return {
+            searchPath: mdiMagnify,
+            inputValue: null
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'PageNavigation': PageNavigation,
+        'PaginationButton': PaginationButton,
+        'SvgIcon': SvgIcon
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
+
+<style scoped></style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/template/layoutTemplate/Vertical.vue (added)
+++ client/views/template/layoutTemplate/Vertical.vue
@@ -0,0 +1,165 @@
+<template>
+    <div class="container">
+        <div class="page-titleZone flex justify-between">
+            <p class="main-title">페이지 제목을 적어주세요</p>
+            <PageNavigation />
+        </div>
+        <div class="content-box flex justify-between">
+            <div class="left-content flex20">
+                <div class="content-titleZone flex justify-between">
+                    <p class="box-title">폴더 리스트</p>
+                    <button class="darkg-border-btn small-btn">폴더추가</button>
+                </div>
+                <div class="content-zone">
+
+                </div>
+            </div>
+            <div class="right-content flex80">
+                <div class="searchbar-zone">
+                    <div class="flex justify-end align-center">
+                        <input type="date" name="" id="" class="square-date">
+                        <span class="coupler">~</span>
+                        <input type="date" name="" id="" class="square-date">
+                        <select name="" id="" class="square-select">
+                            <option value="all">전체</option>
+                        </select>
+                        <div class="search-square">
+                            <input type="text" class="square-input" placeholder="Search">
+                            <button class="square-button">
+                                <svg-icon type="mdi" :path="searchPath" class="square-icon"></svg-icon>
+                            </button>
+                        </div>
+                    </div>
+                </div>
+                <div class="flex-column">
+                    <div class="table-zone">
+                        <table class="list-table">
+                            <colgroup>
+                                <col style="width: ;">
+                                <col style="width: ;">
+                                <col style="width: ;">
+                                <col style="width: ;">
+                            </colgroup>
+                            <thead>
+                                <tr>
+                                    <th>No</th>
+                                    <th>user ID</th>
+                                    <th>이름</th>
+                                    <th>생년월일</th>
+                                    <th>성별</th>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>
+                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>                                <tr>
+                                    <td>1</td>
+                                    <td>test123</td>
+                                    <td>홍길동</td>
+                                    <td>1999.01.01</td>
+                                    <td>여</td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                    <PaginationButton />
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import PageNavigation from '../../component/PageNavigation.vue';
+import PaginationButton from '../../component/PaginationButton.vue';
+import FileTree from '../../component/FileTree.vue';
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify } from '@mdi/js';
+export default {
+    data() {
+        return {
+            searchPath: mdiMagnify
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'PageNavigation': PageNavigation,
+        'FileTree': FileTree,
+        'SvgIcon': SvgIcon,
+        'PaginationButton': PaginationButton
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
+
+<style lang="css" scoped>
+.content-zone {
+    border: 1px solid #eee;
+    padding: 10px;
+    height: calc(100% - 55px);
+    overflow: auto;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/template/templateElement/BtnPosition.vue (added)
+++ client/views/template/templateElement/BtnPosition.vue
@@ -0,0 +1,215 @@
+<template>
+    <div class="content">
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">타이틀 옆 버튼 위치</p>
+            <!-- 여기서부터 복사 -->
+            <div class="flex-between">
+                <p class="box-title">폴더 리스트</p>
+                <button class="darkg-border-btn small-btn">폴더추가</button>
+            </div>
+        </div>
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">다운로드버튼 및 crud 한페이지에서 이루어지는 page의 조회 및 삭제</p>
+            <!-- 여기서부터 복사 -->
+            <div class="flex-end">
+                <button class="green-border-btn small-btn">파일 다운로드</button>
+            </div>
+            <table class="list-table">
+                <colgroup>
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                </colgroup>
+                <thead>
+                    <tr>
+                        <th>No</th>
+                        <th>user ID</th>
+                        <th>이름</th>
+                        <th>생년월일</th>
+                        <th>성별</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr>
+                        <td>1</td>
+                        <td>test123</td>
+                        <td>홍길동</td>
+                        <td>1999.01.01</td>
+                        <td>여</td>
+                    </tr>
+                    <tr>
+                        <td>1</td>
+                        <td>test123</td>
+                        <td>홍길동</td>
+                        <td>1999.01.01</td>
+                        <td>여</td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">조건 검색과 같이 들어가는 버튼</p>
+            <!-- 여기서부터 복사 -->
+            <div class="flex-between">
+                <div>
+                    <button class="darkg-border-btn small-btn">선택이동</button>
+                    <button class="darkg-border-btn small-btn">선택복사</button>
+                    <button class="darkg-border-btn small-btn">선택삭제</button>
+                </div>
+                <div class="flex-end">
+                    <div class="search-bar flex">
+                        <select name="" id="">
+                            <option value="all">전체</option>
+                        </select>
+                        <input type="text" name="" id="">
+                    </div>
+                    <button class="blue-btn small-btn">검색</button>
+                </div>
+            </div>
+            <table class="list-table">
+                <colgroup>
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                </colgroup>
+                <thead>
+                    <tr>
+                        <th>No</th>
+                        <th>user ID</th>
+                        <th>이름</th>
+                        <th>생년월일</th>
+                        <th>성별</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr>
+                        <td>1</td>
+                        <td>test123</td>
+                        <td>홍길동</td>
+                        <td>1999.01.01</td>
+                        <td>여</td>
+                    </tr>
+                    <tr>
+                        <td>1</td>
+                        <td>test123</td>
+                        <td>홍길동</td>
+                        <td>1999.01.01</td>
+                        <td>여</td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">등록</p>
+            <table class="list-table">
+                <colgroup>
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                </colgroup>
+                <thead>
+                    <tr>
+                        <th>No</th>
+                        <th>user ID</th>
+                        <th>이름</th>
+                        <th>생년월일</th>
+                        <th>성별</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr>
+                        <td>1</td>
+                        <td>test123</td>
+                        <td>홍길동</td>
+                        <td>1999.01.01</td>
+                        <td>여</td>
+                    </tr>
+                    <tr>
+                        <td>1</td>
+                        <td>test123</td>
+                        <td>홍길동</td>
+                        <td>1999.01.01</td>
+                        <td>여</td>
+                    </tr>
+                </tbody>
+            </table>
+            <!-- 여기서부터 복사 -->
+            <div class="flex-end">
+                <button class="darkg-btn small-btn">등록</button>
+            </div>
+        </div>
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">수정 삭제 목록</p>
+            <table class="form-table">
+                <colgroup>
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                    <col style="width: ;">
+                </colgroup>
+                <tbody>
+                    <tr>
+                        <th>user ID</th>
+                        <td><input type="text" name="" id=""></td>
+                        <th>이름</th>
+                        <td><input type="text" name="" id=""></td>
+                    </tr>
+                    <tr>
+                        <th>생년월일</th>
+                        <td>
+                            <div class="flex">
+                                <select name="" id=""></select>
+                                <select name="" id=""></select>
+                                <select name="" id=""></select>
+                            </div>
+                        </td>
+                        <th>성별</th>
+                        <td>
+                            <select name="" id=""></select>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+            <!-- 여기서부터 복사 -->
+            <div class="flex-end">
+                <button class="red-border-btn small-btn">삭제</button>
+                <button class="darkg-border-btn small-btn">목록</button>
+                <button class="green-border-btn small-btn">수정</button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+    }
+}
+</script>
+
+<style scoped>
+.box {
+    padding: 50px;
+    border: 1px solid #eee;
+    margin-bottom: 30px;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/template/templateElement/BtnTable.vue (added)
+++ client/views/template/templateElement/BtnTable.vue
@@ -0,0 +1,76 @@
+<template>
+    <div class="table-zone">
+        <div class="flex justify-between align-center">
+            <div class="count-zone">
+                <p>총 <span>40</span>건 중 <span>01</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">
+            <colgroup>
+                <col style="width: ;">
+                <col style="width: ;">
+                <col style="width: ;">
+                <col style="width: ;">
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>No <button class="tp-btn"><svg-icon type="mdi" :path="settingPath" :color="'#213f99'"></svg-icon></button></th>
+                    <th>user ID <button class="tp-btn"><svg-icon type="mdi" :path="settingPath" :color="'#213f99'"></svg-icon></button></th>
+                    <th>이름 <button class="tp-btn"><svg-icon type="mdi" :path="settingPath" :color="'#213f99'"></svg-icon></button></th>
+                    <th>생년월일 <button class="tp-btn"><svg-icon type="mdi" :path="settingPath" :color="'#213f99'"></svg-icon></button></th>
+                    <th>성별 <button class="tp-btn"><svg-icon type="mdi" :path="settingPath" :color="'#213f99'"></svg-icon></button></th>
+                </tr>
+            </thead>
+            <tbody>
+                <tr>
+                    <td>1</td>
+                    <td>test123</td>
+                    <td>홍길동</td>
+                    <td>1999.01.01</td>
+                    <td>여</td>
+                </tr>
+                <tr>
+                    <td>1</td>
+                    <td>test123</td>
+                    <td>홍길동</td>
+                    <td>1999.01.01</td>
+                    <td>여</td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</template>
+
+<script>
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiCog } from '@mdi/js';
+
+export default {
+    data() {
+        return {
+            settingPath: mdiCog
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'SvgIcon': SvgIcon
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
 
client/views/template/templateElement/DefaultSearchBar.vue (added)
+++ client/views/template/templateElement/DefaultSearchBar.vue
@@ -0,0 +1,45 @@
+<template>
+    <div class="search-bar">
+        <div class="flex justify-end align-center">
+            <input type="date" name="" id="" class="square-date">
+            <span class="coupler">~</span>
+            <input type="date" name="" id="" class="square-date">
+            <select name="" id="" class="square-select">
+                <option value="all">전체</option>
+            </select>
+            <div class="search-square">
+                <input type="text" class="square-input" placeholder="Search">
+                <button class="square-button">
+                    <svg-icon type="mdi" :path="searchPath" class="square-icon"></svg-icon>
+                </button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify } from '@mdi/js';
+export default {
+    data() {
+        return {
+            searchPath: mdiMagnify
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'SvgIcon': SvgIcon
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
 
client/views/template/templateElement/DetailSearchBar.vue (added)
+++ client/views/template/templateElement/DetailSearchBar.vue
@@ -0,0 +1,181 @@
+<template>
+    <div class="search-bar">
+        <div class="option-searchbar">
+            <div class="search-top flex justify-center">
+                <div class="flex align-center">
+                    <input type="date" name="" id="" class="square-date">
+                    <span class="coupler">~</span>
+                    <input type="date" name="" id="" class="square-date">
+                    <select name="" id="" class="square-select">
+                        <option value="all">전체</option>
+                    </select>
+                    <div class="search-square">
+                        <input type="text" class="square-input" placeholder="Search">
+                        <button class="square-button">
+                            <svg-icon type="mdi" :path="searchPath" class="square-icon"></svg-icon>
+                        </button>
+                    </div>
+                </div>
+            </div>
+            <div class="search-bottom">
+                <div class="flex justify-between">
+                    <div class="flex flex30 align-center justify-center">
+                    <span class="option-title">단일조건</span>
+                        <div class="input-container flex">
+                            <label class="radio-label">
+                                <input type="radio" name="radio" checked="" class="custom-radiobox ">
+                                <span>HTML</span>
+                            </label>
+                            <label class="radio-label">
+                                <input type="radio" name="radio" class="custom-radiobox ">
+                                <span>CSS</span>
+                            </label>
+                            <label class="radio-label">
+                                <input type="radio" name="radio" class="custom-radiobox ">
+                                <span>Javascript</span>
+                            </label>
+                        </div>
+                    </div>
+                    <div class="flex flex30 align-center justify-center">
+                    <span class="option-title">다중조건</span>
+                        <div class="input-container flex">
+                            <label class="check-label">
+                                <input type="checkbox" name="check" checked="" class="custom-checkbox ">
+                                <span>HTML</span>
+                            </label>
+                            <label class="check-label">
+                                <input type="checkbox" name="check" class="custom-checkbox ">
+                                <span>CSS</span>
+                            </label>
+                            <label class="check-label">
+                                <input type="checkbox" name="check" class="custom-checkbox ">
+                                <span>Javascript</span>
+                            </label>
+                        </div>
+                    </div>
+                    <div class="flex flex30 align-center justify-center">
+                    <span class="option-title">다중조건</span>
+                        <div class="input-container flex">
+                            <label class="check-label">
+                                <input type="checkbox" name="check" checked="" class="custom-checkbox ">
+                                <span>HTML</span>
+                            </label>
+                            <label class="check-label">
+                                <input type="checkbox" name="check" class="custom-checkbox ">
+                                <span>CSS</span>
+                            </label>
+                            <label class="check-label">
+                                <input type="checkbox" name="check" class="custom-checkbox ">
+                                <span>Javascript</span>
+                            </label>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify } from '@mdi/js';
+export default {
+    data() {
+        return {
+            searchPath: mdiMagnify
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'SvgIcon': SvgIcon
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
+
+<style>
+.search-top {
+    padding: 15px 0;
+    border-bottom: 1px solid #aaa;
+}
+
+/* radio css */
+.input-container label {
+    display: flex;
+    cursor: pointer;
+    font-weight: 500;
+    position: relative;
+    overflow: hidden;
+    margin-bottom: 3px;
+}
+
+.input-container label input.custom-radiobox,
+.input-container label input.custom-checkbox {
+    position: absolute;
+    left: -9999px;
+}
+
+.input-container label input.custom-radiobox:checked+span {
+    background-color: #5b72b8;
+    color: #fff;
+}
+
+.input-container label input.custom-checkbox:checked+span {
+    background-color: #f8bb59;
+    color: #fff;
+}
+
+.input-container label input.custom-radiobox:checked+span:before {
+    box-shadow: inset 0 0 0 4px #213f99;
+}
+
+.input-container label input.custom-checkbox:checked+span:before {
+    box-shadow: inset 0 0 0 4px #ff9d00;
+}
+
+.input-container label span {
+    display: flex;
+    align-items: center;
+    padding: 3px 7px;
+    border-radius: 10px;
+    transition: 0.25s ease;
+    color: #333;
+}
+
+
+.input-container label.radio-label span:hover {
+    background-color: #d6d6e5;
+}
+
+.input-container label.check-label span:hover {
+    background-color: #f4e3c2;
+}
+
+.input-container label.radio-label span:before,
+.input-container label.check-label span:before {
+    display: flex;
+    flex-shrink: 0;
+    content: "";
+    background-color: #fff;
+    width: 15px;
+    height: 15px;
+    border-radius: 50%;
+    margin-right: 3px;
+    transition: 0.25s ease;
+    box-shadow: inset 0 0 0 1px #333;
+}
+
+.input-container label.check-label span:before {
+    border-radius: 0%;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/template/templateElement/FormTable.vue (added)
+++ client/views/template/templateElement/FormTable.vue
@@ -0,0 +1,59 @@
+<template>
+    <div class="table-zone">
+        <table class="form-table">
+            <colgroup>
+                <col style="width: ;">
+                <col style="width: ;">
+                <col style="width: ;">
+                <col style="width: ;">
+            </colgroup>
+            <tbody>
+                <tr>
+                    <th>user ID</th>
+                    <td><input type="text" name="" id=""></td>
+                    <th>이름</th>
+                    <td><input type="text" name="" id=""></td>
+                </tr>
+                <tr>
+                    <th>생년월일</th>
+                    <td>
+                        <div class="flex justify-between">
+                            <select name="" id="" class="flex30"></select>
+                            <select name="" id="" class="flex30"></select>
+                            <select name="" id="" class="flex30"></select>
+                        </div>
+                    </td>
+                    <th>성별</th>
+                    <td>
+                        <select name="" id=""></select>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
+
 
client/views/template/templateElement/ListTable.vue (added)
+++ client/views/template/templateElement/ListTable.vue
@@ -0,0 +1,72 @@
+<template>
+    <div class="table-zone">
+        <div class="flex justify-between align-center">
+            <div class="count-zone">
+                <p>총 <span>40</span>건 중 <span>01</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">
+            <colgroup>
+                <col style="width: ;">
+                <col style="width: ;">
+                <col style="width: ;">
+                <col style="width: ;">
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>No</th>
+                    <th>user ID</th>
+                    <th>이름</th>
+                    <th>생년월일</th>
+                    <th>성별</th>
+                </tr>
+            </thead>
+            <tbody>
+                <tr>
+                    <td>1</td>
+                    <td>test123</td>
+                    <td>홍길동</td>
+                    <td>1999.01.01</td>
+                    <td>여</td>
+                </tr>
+                <tr>
+                    <td>1</td>
+                    <td>test123</td>
+                    <td>홍길동</td>
+                    <td>1999.01.01</td>
+                    <td>여</td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+    },
+    mounted() {
+        console.log('main mounted');
+    }
+}
+</script>
 
client/views/template/templateElement/Searchbar.vue (added)
+++ client/views/template/templateElement/Searchbar.vue
@@ -0,0 +1,53 @@
+<template>
+    <div class="content">
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">기본 Search Bar</p>
+            <div style="padding:30px 0">
+                <DefaultSearchBar />
+            </div>
+        </div>
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">상세 Search Bar</p>
+            <div>
+                <DetailSearchBar />
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import DefaultSearchBar from './DefaultSearchBar.vue';
+import DetailSearchBar from './DetailSearchBar.vue';
+export default {
+    data() {
+        return {
+
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'DefaultSearchBar': DefaultSearchBar,
+        'DetailSearchBar': DetailSearchBar
+    },
+    mounted() {
+    }
+}
+</script>
+
+<style scoped>
+/* */
+.box {
+    padding: 50px;
+    border: 1px solid #eee;
+    margin-bottom: 30px;
+    background-color: #fff;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/template/templateElement/Table.vue (added)
+++ client/views/template/templateElement/Table.vue
@@ -0,0 +1,53 @@
+<template>
+    <div class="content">
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">기본 List Table</p>
+            <ListTable />
+        </div>
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">기본 form Table</p>
+            <FormTable />
+        </div>
+        <div class="box">
+            <p style="font-size:2rem; font-weight:800; margin-bottom:10px">기본 form Table</p>
+            <BtnTable />
+        </div>
+    </div>
+</template>
+
+<script>
+import ListTable from './ListTable.vue';
+import FormTable from './FormTable.vue';
+import BtnTable from './BtnTable.vue';
+export default {
+    data() {
+        return {
+        }
+    },
+    methods: {
+
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        'ListTable': ListTable,
+        'FormTable': FormTable,
+        'BtnTable':BtnTable
+    },
+    mounted() {
+
+    }
+}
+</script>
+
+<style scoped>
+.box {
+    padding: 50px;
+    border: 1px solid #eee;
+    margin-bottom: 30px;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
package-lock.json (added)
+++ package-lock.json
This file is too big to display.
 
package.json (added)
+++ package.json
@@ -0,0 +1,40 @@
+{
+  "dependencies": {
+    "@amcharts/amcharts5": "^5.5.7",
+    "@babel/cli": "7.19.3",
+    "@babel/core": "7.19.3",
+    "@jamescoyle/vue-icon": "^0.1.2",
+    "@mdi/js": "^7.4.47",
+    "axios": "^1.6.5",
+    "babel-loader": "8.2.5",
+    "css-loader": "6.7.1",
+    "express": "4.18.1",
+    "express-http-proxy": "^2.0.0",
+    "file-loader": "6.2.0",
+    "file-saver": "^2.0.5",
+    "fs": "0.0.1-security",
+    "lodash": "^4.17.21",
+    "new-line": "^1.1.1",
+    "pg": "8.8.0",
+    "url-loader": "4.1.1",
+    "vue": "3.2.40",
+    "vue-jstree": "^2.1.6",
+    "vue-loader": "^17.0.0",
+    "vue-router": "4.1.5",
+    "vue-style-loader": "4.1.3",
+    "vue3-sfc-loader": "^0.8.4",
+    "vuex": "^4.1.0",
+    "webpack": "5.74.0",
+    "webpack-cli": "4.10.0"
+  },
+  "scripts": {
+    "prod": "set NODE_ENV=production&&node ./server/modules/web/server.js",
+    "dev": "set NODE_ENV=development&&node ./server/modules/web/server.js",
+    "windows-prod": "set NODE_ENV=production&&node ./server/modules/web/server.js",
+    "windows-dev": "set NODE_ENV=development&&node ./server/modules/web/server.js",
+    "linux-prod": "export NODE_ENV=production&&node ./server/modules/web/server.js",
+    "linux-dev": "export NODE_ENV=development&&node ./server/modules/web/server.js",
+    "webpack-build": "webpack",
+    "webpack-build-watch": "webpack --watch"
+  }
+}
 
server/modules/db/mysql/MysqlConnection.js (added)
+++ server/modules/db/mysql/MysqlConnection.js
@@ -0,0 +1,62 @@
+/**
+ * @author : 최정우
+ * @since : 2022.10.21
+ * @dscription : Mysql DB Connection Pool 생성 관리 모듈 입니다. (private와 public object 환경 구성)
+ */
+ const MysqlConnection = (function () {
+
+    //Mysql DB Connection 라이브러리 모듈
+    const mysql = require('mysql');
+
+    //Connection Pool 객체 - private object(변수)
+    const connectionPool = mysql.createPool({
+        host: 'localhost',
+        user: 'root',
+        password: '1234',
+        database: 'test',
+        ssl: false,
+        port: 3306,
+        max: 10,
+    });
+
+    return {
+        getConnectionPool: function () {
+            return connectionPool;
+        },
+        queryExcute: function (sql, params) {
+            const result = new Promise((resolve, reject) => {
+                connectionPool.getConnection(function (connectionError, connection) {
+                    if (!connectionError) {
+                        try {
+                            connection.query(sql, params, function (queryError, rows, columns) {
+                                if (!queryError) {
+                                    resolve({'rows': rows, 'columns': columns});
+                                } else {
+                                    reject(queryError);
+                                }
+                            })
+                        } catch (proccessError) {
+                            reject(proccessError);
+                        } finally {
+                            connection.release(); 
+                        }
+                    } else {
+                        reject(connectionError);
+                    }
+                })
+
+                /* connectionPool.getConnection().then(async (connection) => {
+                    let queryResult = await connection.query(sql);
+                    resolve(queryResult);
+                    connection.release();
+                }).catch((error) => {
+                    reject(error);
+                }); */
+            })
+            return result;
+        },
+    }
+
+})();
+
+module.exports = MysqlConnection;(파일 끝에 줄바꿈 문자 없음)
 
server/modules/db/oracle/OracleConnection.js (added)
+++ server/modules/db/oracle/OracleConnection.js
@@ -0,0 +1,62 @@
+/**
+ * @author : 방선주
+ * @since : 2022.09.22
+ * @dscription : Oracle DB Connection Pool 생성 관리 모듈 입니다.
+ * 
+ * @modifier : 최정우
+ * @modified : 2022.10.30
+ */
+ const { BASE_DIR } = require('../../../../Global');
+
+ const OracleConnection = function () {
+   //오라클 라이브러리 import
+   const oracledb = require('oracledb');
+   //라이브러리 초기화(oracle client setting)
+   oracledb.initOracleClient({ libDir: `${BASE_DIR}/server/modules/db/oracle/client/client_19.16` });
+ 
+   //DB Connection Pool
+   let connectionPool = null;
+ 
+   //DB Connection Pool 생성
+   oracledb.createPool({
+     user: 'ajin_data',
+     password: 'ajinvldosvl1121',  // myhrpw contains the hr schema password
+     connectString: '(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 211.216.206.147)(PORT = 1521)))(CONNECT_DATA = (SID = XE)))',
+     poolMin: 0,
+     poolMax: 20,
+     //poolAlias: 'ajin_odbcp'//poolAlias가 정의되지 않으면, default pool로 정의됨
+   }).then((result) => {
+     connectionPool = result;
+     console.log("Successfully connected to Oracle Database");
+   }).catch((error) => {
+     console.error(error);
+   });
+ 
+   return {
+     getConnectionPool: function () {
+       return connectionPool;
+     },
+     getConnection: function () {
+       return oracledb.getConnection();
+     },
+     queryExcute: function (sql, params) {
+       const result = new Promise((resolve, reject) => {
+         oracledb.getConnection().then(async (connection) => {
+           let queryResult = null;
+           if (!params) {
+             queryResult = await connection.execute(sql);
+           } else {
+             queryResult = await connection.execute(sql, params);
+           }
+           resolve(queryResult);
+           connection.close();
+         }).catch((error) => {
+           reject(error);
+         });
+       })
+       return result;
+     },
+   }
+ }();
+ 
+ module.exports = OracleConnection;(파일 끝에 줄바꿈 문자 없음)
 
server/modules/db/oracle/client/client_19.16/BASIC_LICENSE (added)
+++ server/modules/db/oracle/client/client_19.16/BASIC_LICENSE
@@ -0,0 +1,123 @@
+Oracle Free Use Terms and Conditions
+
+Definitions
+
+"Oracle" refers to Oracle America, Inc. "You" and "Your" refers to (a)
+a company or organization (each an "Entity") accessing the Programs,
+if use of the Programs will be on behalf of such Entity; or (b) an
+individual accessing the Programs, if use of the Programs will not be
+on behalf of an Entity. "Program(s)" refers to Oracle software
+provided by Oracle pursuant to the following terms and any updates,
+error corrections, and/or Program Documentation provided by
+Oracle. "Program Documentation" refers to Program user manuals and
+Program installation manuals, if any. If available, Program
+Documentation may be delivered with the Programs and/or may be
+accessed from www.oracle.com/documentation. "Separate Terms" refers to
+separate license terms that are specified in the Program
+Documentation, readmes or notice files and that apply to Separately
+Licensed Technology. "Separately Licensed Technology" refers to Oracle
+or third party technology that is licensed under Separate Terms and
+not under the terms of this license.
+
+Separately Licensed Technology
+
+Oracle may provide certain notices to You in Program Documentation,
+readmes or notice files in connection with Oracle or third party
+technology provided as or with the Programs. If specified in the
+Program Documentation, readmes or notice files, such technology will
+be licensed to You under Separate Terms. Your rights to use Separately
+Licensed Technology under Separate Terms are not restricted in any way
+by the terms herein. For clarity, notwithstanding the existence of a
+notice, third party technology that is not Separately Licensed
+Technology shall be deemed part of the Programs licensed to You under
+the terms of this license.
+
+Source Code for Open Source Software
+
+For software that You receive from Oracle in binary form that is
+licensed under an open source license that gives You the right to
+receive the source code for that binary, You can obtain a copy of the
+applicable source code from https://oss.oracle.com/sources/ or
+http://www.oracle.com/goto/opensourcecode. If the source code for such
+software was not provided to You with the binary, You can also receive
+a copy of the source code on physical media by submitting a written
+request pursuant to the instructions in the "Written Offer for Source
+Code" section of the latter website.
+
+-------------------------------------------------------------------------------
+
+The following license terms apply to those Programs that are not
+provided to You under Separate Terms.
+
+License Rights and Restrictions
+
+Oracle grants to You, as a recipient of this Program, a nonexclusive,
+nontransferable, limited license to, subject to the conditions stated
+herein, (a) internally use the unmodified Programs for the purposes of
+developing, testing, prototyping and demonstrating your applications,
+and running the Programs for your own internal business operations;
+and (b) redistribute unmodified Programs and Programs Documentation,
+under the terms of this License, provided that You do not charge Your
+end users any additional fees for the use of the Programs. You may
+make copies of the Programs to the extent reasonably necessary for
+exercising the license rights granted herein and for backup
+purposes. You are granted the right to use the Programs to provide
+third party training in the use of the Programs and associated
+Separately Licensed Technology only if there is express authorization
+of such use by Oracle on the Program's download page or in the Program
+Documentation.
+
+Your license is contingent on Your compliance with the following conditions:
+
+    - You include a copy of this license with any distribution by You
+      of the Programs;
+
+    - You do not remove markings or notices of either Oracle's or a
+      licensor's proprietary rights from the Programs or Program
+      Documentation;
+
+    - You comply with all U.S. and applicable export control and
+      economic sanctions laws and regulations that govern Your use of
+      the Programs (including technical data);
+
+    - You do not cause or permit reverse engineering, disassembly or
+      decompilation of the Programs (except as allowed by law) by You
+      nor allow an associated party to do so.
+
+For clarity, any source code that may be included in the distribution
+with the Programs is provided solely for reference purposes and may
+not be modified, unless such source code is under Separate Terms
+permitting modification.
+
+Ownership
+
+Oracle or its licensors retain all ownership and intellectual property
+rights to the Programs.
+
+Information Collection
+
+The Programs' installation and/or auto-update processes, if any, may
+transmit a limited amount of data to Oracle or its service provider
+about those processes to help Oracle understand and optimize
+them. Oracle does not associate the data with personally identifiable
+information. Refer to Oracle's Privacy Policy at
+www.oracle.com/privacy.
+
+Disclaimer of Warranties; Limitation of Liability
+
+THE PROGRAMS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ORACLE
+FURTHER DISCLAIMS ALL WARRANTIES, EXPRESS AND IMPLIED, INCLUDING
+WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL ORACLE BE LIABLE TO
+YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+Last updated:  8 October 2018
+
 
server/modules/db/oracle/client/client_19.16/BASIC_README (added)
+++ server/modules/db/oracle/client/client_19.16/BASIC_README
@@ -0,0 +1,30 @@
+Basic Package Information 
+========================= 
+Sun Jul 17 14:46:23 MDT 2022
+Client Shared Library 64-bit - 19.16.0.0.0
+
+Windows NT Version V6.2  
+CPU                 : 4 - type 86644 physical cores
+Process Affinity    : 0x0000000000000000
+Memory (Avail/Total): Ph:10181M/16381M, Ph+PgF:12546M/18813M 
+
+
+TIMEZONE INFORMATION
+--------------------
+Operating in ORACLE_HOME environment.
+
+Small timezone file = timezone_32.dat
+Large timezone file = timezlrg_32.dat
+
+LICENSE AGREEMENT
+-----------------------------
+Your use of this copy of Oracle Instant Client software product is subject to, and may not exceed the conditions of use for which you are authorized under one of the following:
+
+(i) the license or cloud services terms that you accepted when you obtained the right to use Oracle Instant Client software; or
+(ii) the license terms that you agreed to when you placed your order with Oracle for an Oracle product containing Oracle Instant Client software; or
+(iii) the Oracle Instant Client software license terms, if any, included with the hardware that you acquired from Oracle; or
+(iv) the Oracle Technology Network License Agreement (which you acknowledge you have read and agree to) available at http://www.oracle.com/technetwork/licenses/distribution-license-152002.html; or, if (i), (ii), (iii), and or (iv) are not applicable, then,
+(v) the Oracle Free Use Terms and Conditions available at BASIC_LICENSE.
+
+Oracle's obligations with respect to your use of the Oracle Instant Client, including, without limitation, obligations to indemnify you, if any, shall only be as set forth in the specific license under which you are authorized and choose to use Oracle Instant Client.
+
 
server/modules/db/oracle/client/client_19.16/adrci.exe (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/adrci.exe
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/adrci.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/adrci.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/genezi.exe (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/genezi.exe
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/genezi.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/genezi.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oci.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oci.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oci.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oci.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/ocijdbc19.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/ocijdbc19.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/ocijdbc19.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/ocijdbc19.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/ociw32.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/ociw32.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/ociw32.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/ociw32.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/ojdbc8.jar (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/ojdbc8.jar
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oramysql19.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oramysql19.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oramysql19.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oramysql19.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/orannzsbb19.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/orannzsbb19.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/orannzsbb19.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/orannzsbb19.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oraocci19.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oraocci19.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oraocci19.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oraocci19.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oraocci19d.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oraocci19d.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oraocci19d.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oraocci19d.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oraociei19.dll (added)
+++ server/modules/db/oracle/client/client_19.16/oraociei19.dll
This file is too big to display.
 
server/modules/db/oracle/client/client_19.16/oraociei19.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oraociei19.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/oraons.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/oraons.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/orasql19.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/orasql19.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/orasql19.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/orasql19.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/ucp.jar (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/ucp.jar
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/uidrvci.exe (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/uidrvci.exe
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/uidrvci.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/uidrvci.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/vc14/oraocci19.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/vc14/oraocci19.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/vc14/oraocci19d.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19d.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/vc14/oraocci19d.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19d.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_19.16/xstreams.jar (Binary) (added)
+++ server/modules/db/oracle/client/client_19.16/xstreams.jar
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/BASIC_LICENSE (added)
+++ server/modules/db/oracle/client/client_21.6/BASIC_LICENSE
@@ -0,0 +1,123 @@
+Oracle Free Use Terms and Conditions
+
+Definitions
+
+"Oracle" refers to Oracle America, Inc. "You" and "Your" refers to (a)
+a company or organization (each an "Entity") accessing the Programs,
+if use of the Programs will be on behalf of such Entity; or (b) an
+individual accessing the Programs, if use of the Programs will not be
+on behalf of an Entity. "Program(s)" refers to Oracle software
+provided by Oracle pursuant to the following terms and any updates,
+error corrections, and/or Program Documentation provided by
+Oracle. "Program Documentation" refers to Program user manuals and
+Program installation manuals, if any. If available, Program
+Documentation may be delivered with the Programs and/or may be
+accessed from www.oracle.com/documentation. "Separate Terms" refers to
+separate license terms that are specified in the Program
+Documentation, readmes or notice files and that apply to Separately
+Licensed Technology. "Separately Licensed Technology" refers to Oracle
+or third party technology that is licensed under Separate Terms and
+not under the terms of this license.
+
+Separately Licensed Technology
+
+Oracle may provide certain notices to You in Program Documentation,
+readmes or notice files in connection with Oracle or third party
+technology provided as or with the Programs. If specified in the
+Program Documentation, readmes or notice files, such technology will
+be licensed to You under Separate Terms. Your rights to use Separately
+Licensed Technology under Separate Terms are not restricted in any way
+by the terms herein. For clarity, notwithstanding the existence of a
+notice, third party technology that is not Separately Licensed
+Technology shall be deemed part of the Programs licensed to You under
+the terms of this license.
+
+Source Code for Open Source Software
+
+For software that You receive from Oracle in binary form that is
+licensed under an open source license that gives You the right to
+receive the source code for that binary, You can obtain a copy of the
+applicable source code from https://oss.oracle.com/sources/ or
+http://www.oracle.com/goto/opensourcecode. If the source code for such
+software was not provided to You with the binary, You can also receive
+a copy of the source code on physical media by submitting a written
+request pursuant to the instructions in the "Written Offer for Source
+Code" section of the latter website.
+
+-------------------------------------------------------------------------------
+
+The following license terms apply to those Programs that are not
+provided to You under Separate Terms.
+
+License Rights and Restrictions
+
+Oracle grants to You, as a recipient of this Program, a nonexclusive,
+nontransferable, limited license to, subject to the conditions stated
+herein, (a) internally use the unmodified Programs for the purposes of
+developing, testing, prototyping and demonstrating your applications,
+and running the Programs for your own internal business operations;
+and (b) redistribute unmodified Programs and Programs Documentation,
+under the terms of this License, provided that You do not charge Your
+end users any additional fees for the use of the Programs. You may
+make copies of the Programs to the extent reasonably necessary for
+exercising the license rights granted herein and for backup
+purposes. You are granted the right to use the Programs to provide
+third party training in the use of the Programs and associated
+Separately Licensed Technology only if there is express authorization
+of such use by Oracle on the Program's download page or in the Program
+Documentation.
+
+Your license is contingent on Your compliance with the following conditions:
+
+    - You include a copy of this license with any distribution by You
+      of the Programs;
+
+    - You do not remove markings or notices of either Oracle's or a
+      licensor's proprietary rights from the Programs or Program
+      Documentation;
+
+    - You comply with all U.S. and applicable export control and
+      economic sanctions laws and regulations that govern Your use of
+      the Programs (including technical data);
+
+    - You do not cause or permit reverse engineering, disassembly or
+      decompilation of the Programs (except as allowed by law) by You
+      nor allow an associated party to do so.
+
+For clarity, any source code that may be included in the distribution
+with the Programs is provided solely for reference purposes and may
+not be modified, unless such source code is under Separate Terms
+permitting modification.
+
+Ownership
+
+Oracle or its licensors retain all ownership and intellectual property
+rights to the Programs.
+
+Information Collection
+
+The Programs' installation and/or auto-update processes, if any, may
+transmit a limited amount of data to Oracle or its service provider
+about those processes to help Oracle understand and optimize
+them. Oracle does not associate the data with personally identifiable
+information. Refer to Oracle's Privacy Policy at
+www.oracle.com/privacy.
+
+Disclaimer of Warranties; Limitation of Liability
+
+THE PROGRAMS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. ORACLE
+FURTHER DISCLAIMS ALL WARRANTIES, EXPRESS AND IMPLIED, INCLUDING
+WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, OR NONINFRINGEMENT.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL ORACLE BE LIABLE TO
+YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+Last updated:  8 October 2018
+
 
server/modules/db/oracle/client/client_21.6/BASIC_README (added)
+++ server/modules/db/oracle/client/client_21.6/BASIC_README
@@ -0,0 +1,30 @@
+Basic Package Information 
+========================= 
+Fri May 27 02:14:20 MDT 2022
+Client Shared Library 64-bit - 21.6.0.0.0
+
+Windows NT Version V6.3  OS Build 9600
+CPU                 : 4 - type 86644 physical cores
+Process Affinity    : 0x0000000000000000
+Memory (Avail/Total): Ph:10006M/16381M, Ph+PgF:12115M/18813M 
+
+
+TIMEZONE INFORMATION
+--------------------
+Operating in ORACLE_HOME environment.
+
+Small timezone file = timezone_35.dat
+Large timezone file = timezlrg_35.dat
+
+LICENSE AGREEMENT
+-----------------------------
+Your use of this copy of Oracle Instant Client software product is subject to, and may not exceed the conditions of use for which you are authorized under one of the following:
+
+(i) the license or cloud services terms that you accepted when you obtained the right to use Oracle Instant Client software; or
+(ii) the license terms that you agreed to when you placed your order with Oracle for an Oracle product containing Oracle Instant Client software; or
+(iii) the Oracle Instant Client software license terms, if any, included with the hardware that you acquired from Oracle; or
+(iv) the Oracle Technology Network License Agreement (which you acknowledge you have read and agree to) available at http://www.oracle.com/technetwork/licenses/distribution-license-152002.html; or, if (i), (ii), (iii), and or (iv) are not applicable, then,
+(v) the Oracle Free Use Terms and Conditions available at BASIC_LICENSE.
+
+Oracle's obligations with respect to your use of the Oracle Instant Client, including, without limitation, obligations to indemnify you, if any, shall only be as set forth in the specific license under which you are authorized and choose to use Oracle Instant Client.
+
 
server/modules/db/oracle/client/client_21.6/adrci.exe (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/adrci.exe
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/adrci.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/adrci.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/genezi.exe (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/genezi.exe
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/genezi.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/genezi.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/network/admin/README (added)
+++ server/modules/db/oracle/client/client_21.6/network/admin/README
@@ -0,0 +1,9 @@
+============================================================================= 
+This is the default directory for Oracle Network and Oracle Client 
+configuration files. You can place files such as tnsnames.ora, sqlnet.ora 
+and oraaccess.xml in this directory. 
+NOTE: 
+If you set an environment variable TNS_ADMIN to another directory containing 
+configuration files, they will be used instead of the files in this default 
+directory. 
+============================================================================= 
 
server/modules/db/oracle/client/client_21.6/oci.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oci.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oci.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oci.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/ocijdbc21.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/ocijdbc21.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/ocijdbc21.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/ocijdbc21.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/ociw32.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/ociw32.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/ociw32.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/ociw32.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/ojdbc8.jar (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/ojdbc8.jar
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oramysql.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oramysql.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oramysql.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oramysql.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/orannzsbb.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/orannzsbb.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/orannzsbb.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/orannzsbb.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oraocci21.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oraocci21.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oraocci21.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oraocci21.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oraocci21d.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oraocci21d.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oraocci21d.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oraocci21d.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/oraociei.dll (added)
+++ server/modules/db/oracle/client/client_21.6/oraociei.dll
This file is too big to display.
 
server/modules/db/oracle/client/client_21.6/oraociei.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/oraociei.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/orasql.dll (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/orasql.dll
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/orasql.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/orasql.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/ucp.jar (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/ucp.jar
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/uidrvci.exe (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/uidrvci.exe
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/uidrvci.sym (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/uidrvci.sym
Binary file is not shown
 
server/modules/db/oracle/client/client_21.6/xstreams.jar (Binary) (added)
+++ server/modules/db/oracle/client/client_21.6/xstreams.jar
Binary file is not shown
 
server/modules/db/postgresql/PostgresqlConnection.js (added)
+++ server/modules/db/postgresql/PostgresqlConnection.js
@@ -0,0 +1,41 @@
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : PostgreSQL DB Connection Pool 생성 관리 모듈 입니다. (private와 public object 환경 구성)
+ */
+const PostgresqlConnection = (function () {
+
+    //PostgreSQL DB Connection 라이브러리 모듈
+    const pg = require('pg');
+
+    //Connection Pool 객체 - private object(변수)
+    const connectionPool = new pg.Pool({
+        //host: 'localhost',
+        host: '192.168.0.250',
+        user: 'test_user',
+        password: '1234',
+        database: 'test_db',
+        ssl: false,
+        port: 5432,
+        max: 10,
+    });
+
+    //public object
+    return {
+        getConnectionPool: function () {
+            return connectionPool;
+        },
+        getConnectionPoolClient: function () {
+            return connectionPool.connect();
+        },
+        queryExcute: function (sql, params) {
+            return connectionPool.query(sql, params);
+        },
+    }
+
+})();
+
+//Module Export
+module.exports = PostgresqlConnection;
+
+
 
server/modules/log/Logger.js (added)
+++ server/modules/log/Logger.js
@@ -0,0 +1,131 @@
+const { LOG_BASE_DIR, SERVICE_STATUS } = require('../../../Global');
+const fs = require('fs');
+const Queue = require('../util/Queue');
+
+/**
+ * @author : 최정우
+ * @since : 2022.09.21
+ * @dscription : Log 생성기 모듈 입니다.
+ */
+const Logger = (function () {
+
+    /* let testInterval = setInterval(() => {
+        const date = new Date();
+        var now = `${date.getFullYear()}.${(date.getMonth()+1)}.${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}`;
+        console.log('now :', now);
+    }, 1000) */
+
+    //로그 쓰기 전, 대기 저장소
+    const eventQueue = new Queue();
+    //로그 쓰는 중인지 아닌지 상태값
+    let isLogging = false;
+
+    /**
+     * @author : 최정우
+     * @since : 2022.09.21
+     * @dscription : Log 처리
+     */
+    const logging = (message) => {
+        const date = new Date();
+        let year = date.getFullYear();
+        let month = prefixZero((date.getMonth() + 1), 2);
+        let day = prefixZero(date.getDate(), 2);
+        let hour = prefixZero(date.getHours(), 2);
+        let minute = prefixZero(date.getMinutes(), 2);
+        let second = prefixZero(date.getSeconds(), 2);
+        let millisecond = prefixZero(date.getMilliseconds(), 3);
+
+        //로그에 쓰일 정보
+        const logMessage = {
+            message: message,
+            datetime: `${year}.${month}.${day} ${hour}:${minute}:${second}.${millisecond}`,
+            logFolderDir: `${LOG_BASE_DIR}/${year}${month}`,//log 폴더 경로
+            logFileName: `log-${year}${month}${day}.log`//log 파일명
+        }
+
+        //로그 쓰는 중이면, 대기 저장소에 등록
+        if (isLogging == true) {
+            eventQueue.push(logMessage);
+        } else {//로그 쓰는 중이 아니면, 로그 쓰는 중인 상태로 변경 후, 로그 쓰기
+            isLogging = true;
+
+            try {
+                //log 폴더 생성
+                if (!fs.existsSync(logMessage.logFolderDir)) {
+                    fs.mkdirSync(logMessage.logFolderDir, { recursive: true/*재귀적 폴더 생성*/ });
+                }
+
+                //log 파일 Full Path
+                let logFileFullPath = `${logMessage.logFolderDir}/${logMessage.logFileName}`;
+                //log 내용
+                let logContent = `[${logMessage.datetime}] ${logMessage.message}`;
+
+                //log 내용 쓰기
+                writeLogFile(logFileFullPath, logContent);
+            } catch (error) {
+                console.log('logging error : ', error);
+            } finally {
+                isLogging = false;
+            }
+        }
+    }
+
+    /**
+     * @author : 최정우
+     * @since : 2022.09.21
+     * @dscription : Log 내용 쓰기
+     */
+    const writeLogFile = (path, content) => {
+        if (SERVICE_STATUS == 'development') {
+            console.log(content);
+        }
+        
+        //파일 쓰기
+        fs.appendFileSync(path, `${content}\n`, 'utf8');
+
+        //로그 쓰기 저장소에서 로그 메세지 꺼내기
+        let logMessage = eventQueue.pop();
+        //메세지가 존재하면 => Log 내용 쓰기 (재귀 호출)
+        if (logMessage != undefined) {
+            //log 파일 Full Path
+            let logFileFullPath = `${logMessage.logFolderDir}/${logMessage.logFileName}`;
+            //log 내용
+            let logContent = `[${logMessage.datetime}] ${logMessage.message}`;
+            //Log 내용 쓰기 (재귀 호출)
+            writeLogFile(logFileFullPath, logContent);
+        } else {
+            return;
+        }
+    }
+
+    /**
+     * @author : 최정우
+     * @since : 2022.09.21
+     * @dscription : 특정 길이만큼 앞에 '0' 붙이기
+     */
+    const prefixZero = (target, length) => {
+        let zero = '';
+        let suffix = target;
+        let result = '';
+
+        if ((typeof target) === "number") {
+            suffix = target.toString();
+        }
+        if (suffix.length < length) {
+            for (i = 0; i < length - suffix.length; i++) {
+                zero += '0';
+            }
+        }
+        result = zero + suffix;
+        return result;
+    }
+
+
+    return {
+        logging: logging
+    }
+
+})();
+
+//Module Export
+module.exports = Logger;(파일 끝에 줄바꿈 문자 없음)
 
server/modules/util/Queue.js (added)
+++ server/modules/util/Queue.js
@@ -0,0 +1,32 @@
+/**
+ * @author : 최정우
+ * @since : 2022.09.21
+ * @dscription : Queue(선입선출) 자료형 객체 입니다.
+ */
+class Queue {
+    constructor() {
+        this._arr = [];
+    }
+
+    //입력
+    push (item) {
+        this._arr.push(item);
+    }
+
+    //출력 후, 제거
+    pop () {
+        return this._arr.shift();
+    }
+
+    //출력 대기 중인 item return
+    peek () {
+        return this._arr[0];
+    }
+
+    //확인
+    showQueue () {
+        console.log('Queue : ', this._arr);
+    }
+}
+
+module.exports = Queue;(파일 끝에 줄바꿈 문자 없음)
 
server/modules/web/Server.js (added)
+++ server/modules/web/Server.js
@@ -0,0 +1,133 @@
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : Express 라이브러리 활용 HTTP Web Server 모듈입니다.
+ */
+const { BASE_DIR, PORT, API_SERVER_HOST } = require("../../../Global");
+const Logger = require("../log/Logger"); //Logger(필수)
+
+const express = require("express");
+const webServer = express();
+const expressProxy = require("express-http-proxy");
+
+//파일 시스템 관련 라이브러리
+const FS = require("fs");
+//stream: 특정 자원을 Streaming 하기 위한 라이브러리 => Transform: Streaming 중인 자원의 Data에 Data 수정 및 추가를 지원해주는 객체
+const Transform = require("stream").Transform;
+//Streaming 중인 자원에 새로운 데이터를 stream 공간에 추가하기 위한 라이브러리
+const newLineStream = require("new-line");
+
+webServer.use((req, res, next) => {
+  res.header("X-Frame-Options", "SAMEORIGIN"); // or 'SAMEORIGIN' or 'ALLOW-FROM uri'
+  next();
+});
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : HTTP Server start
+ */
+webServer.listen(PORT, function () {
+  Logger.logging(`★★★ Node.js를 활용한 Web Server 구동(Port:${PORT}) ★★★`);
+});
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : Intercepter 역할을 하는 미들웨어 기능
+ */
+webServer.use(function (request, response, next) {
+  let ip = request.headers["x-forwarded-for"] || request.socket.remoteAddress;
+  Logger.logging(
+    `[HTTP] ${request.url} (Method: ${request.method}, IP: ${ip})`
+  );
+  next();
+});
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : ROOT URL -> index.html
+ */
+webServer.get("/", function (request, response) {
+  //response.sendFile을 통한 HTTP html reponse (html내용 Streaming)
+  response.sendFile(`${BASE_DIR}/client/views/index.html`);
+});
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : 화면요청 URL 처리
+ */
+webServer.get("*.page", function (request, response, next) {
+  //index.html 내용을 직접 Streaming하여 Response, Streaming 중간에 내용 수정
+  //수정 내용 : URL 요청이 아닌, 브라우저에 표시된 URL만 변경하여, 해당하는 URL PATH의 Vue Component를 routing하기 위함
+  const StreamTransform = new Transform();
+  StreamTransform._transform = function (data, encoding, done) {
+    let fileContent = data.toString();
+    let replaceBeforeContent = `<script id="app-start-vue-page">const APP_USER_HTTP_REQUEST_URL = '/';</script>`;
+    let replaceAfterContent = `<script id="app-start-vue-page">const APP_USER_HTTP_REQUEST_URL = '${request.params["0"]}.page';</script>`;
+    fileContent.replace(replaceBeforeContent, replaceAfterContent);
+    this.push(fileContent);
+    done();
+  };
+  //Streaming 진행
+  FS.createReadStream(`${BASE_DIR}/client/views/index.html`)
+    .pipe(newLineStream())
+    .pipe(StreamTransform)
+    .pipe(response);
+});
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : REST API 서버에 데이터 요청 보내기(Proxy)
+ */
+webServer.use(
+  "*.json",
+  expressProxy(API_SERVER_HOST, {
+    proxyReqPathResolver: function (request) {
+      return `${request.params["0"]}.json`;
+    },
+  })
+);
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : REST API 서버에 데이터 요청 보내기(Proxy)
+ */
+webServer.use(
+  "*.file",
+  expressProxy(API_SERVER_HOST, {
+    parseReqBody: false,
+    proxyReqPathResolver: function (request) {
+      console.log("request : ", request.url, request.params[0]);
+      return `${request.params["0"]}.file`;
+    },
+  })
+);
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : ROOT URL, Router's, 화면요청 URL 등.. 이 외 나머지 정적 자원에 대한 처리 기능
+ */
+webServer.get("*.*", function (request, response, next) {
+  response.sendFile(`${BASE_DIR}${request.params["0"]}.${request.params["1"]}`);
+});
+
+/**
+ * @author : 하석형
+ * @since : 2023.08.24
+ * @dscription : Global Error Handler (*맨 마지막에 적용해야됨)
+ */
+webServer.use(function (error, request, response, next) {
+  const errorCode = !error.statusCode ? 500 : error.statusCode;
+  response
+    .status(errorCode)
+    .send("에러가 발생하였습니다. 관리자에게 문의바랍니다.");
+  let message = `[Error:${errorCode}] ${request.url}/n ${error.stack}/n`;
+  Logger.logging(message);
+  //next();
+});
 
server/service/test/model/TestDAO.js (added)
+++ server/service/test/model/TestDAO.js
@@ -0,0 +1,190 @@
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트용 DB 접근을 담당하는 모듈 입니다.
+ */
+ const TestDAO = (function () {
+
+    //PostgreSQL DB Connection 객체 - private object(변수)
+    const PostgresqlConnection = require('../../../modules/db/postgresql/PostgresqlConnection');
+    //console.log('PostgresqlConnection : ', PostgresqlConnection);
+    const connectionPool = PostgresqlConnection.getConnectionPool();
+
+    //public object
+    return {
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 목록 조회
+         */
+        testSelectList: function (param) {
+            return connectionPool.query('SELECT * FROM test_schema.test_table');
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 상세 조회
+         */
+        testSelectOne: function (param) {
+            return connectionPool.query('SELECT * FROM test_schema.test_table WHERE test_seq = $1', param);
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 등록
+         */
+        testInsertOne: function (param) {
+            return connectionPool.query(
+                //RETURNING <*||COLUMN NAME1,COLUMN NAME2,...> : 등록한 Data return 받는 기능
+                //ex1) RETURNING * : 모든 컬럼 다 return 함
+                //ex2) RETURNING name : 컬럼명이 'name'인 데이터만 return 함
+                `INSERT INTO test_schema.test_table (
+                    test_seq
+                    , test_title
+                    , test_content
+                    , test_create_datetime
+                    , test_create_user
+                ) VALUES (
+                    (SELECT COALESCE(MAX(test_seq), 0) + 1 FROM test_schema.test_table)
+                    , $1
+                    , $2
+                    , current_timestamp
+                    , 1
+                ) RETURNING test_seq`,
+                param
+            );
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 수정
+         */
+        testUpdateOne: function (param) {
+            return connectionPool.query(
+                `UPDATE
+                    test_schema.test_table
+                SET 
+                    test_title = $2
+                    , test_content = $3
+                    , test_update_datetime = current_timestamp
+                    , test_update_user = 1
+                WHERE
+                    test_seq = $1`,
+                param
+            );
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 삭제
+         */
+        testDeleteOne: function (param) {
+            return connectionPool.query(
+                `DELETE
+                FROM
+                    test_schema.test_table
+                WHERE
+                    test_seq = $1`,
+                param
+            );
+        },
+
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 트랜젝션 처리
+         * 
+         * 트랜잭션 작업 진행 시나리오
+         *  1. connection pool 한 개 받아오기
+         *  2. BEGIN(트랜잭션 시작 처리)
+         *  3. [※작업내용1] 데이터 등록
+         *  4. [※작업내용2] 등록한 데이터 수정
+         *  5. *작업 완료시, 실행(dispatch)한 모든 쿼리를 COMMIT(커밋)
+         *     *에러 발생시, 실행(dispatch)한 모든 쿼리를 ROLLBACK(롤백)
+         *  6. 받아온 connection pool 반납(해제) - 필수(반납하지 않으면 해당 pool을 사용하는 query는 트랜잭션에 계속 매달려있음)
+         */
+         testTransaction: function (param) {
+            return new Promise((resolve, reject) => {
+                //1. connection pool 한 개 받아오기
+                const poolClient = PostgresqlConnection.getConnectionPoolClient();
+                poolClient.then(async(client) => {//async 처리
+                    try {
+                        //2. BEGIN(트랜잭션 시작 처리)
+                        await client.query('BEGIN');
+
+                        //처리 3초 지연
+                        //await client.query('SELECT pg_sleep(3);');
+
+                        //3. [※작업내용1] 데이터 등록
+                        let insertResultData = await client.query(
+                            `INSERT INTO test_schema.test_table (
+                                test_seq
+                                , test_title
+                                , test_content
+                                , test_create_datetime
+                                , test_create_user
+                            ) VALUES (
+                                (SELECT COALESCE(MAX(test_seq), 0) + 1 FROM test_schema.test_table)
+                                , $1
+                                , $2
+                                , current_timestamp
+                                , 1
+                            ) RETURNING test_seq`,
+                            param
+                        );
+                        //console.log('등록 결과 정보 insertResultData : ', insertResultData);
+
+                        //throw '트랜잭션 처리중 Error 강제로 발생시킴';
+
+                        //4. [※작업내용2] 등록한 데이터 수정
+                        let updateResultData = await client.query(
+                            `UPDATE
+                                test_schema.test_table
+                            SET 
+                                test_title = 'title transaction update'
+                                , test_content = 'content transaction update'
+                                , test_update_datetime = current_timestamp 1231ㄱㄹㅇㄴ
+                                , test_update_user = 1
+                            WHERE
+                                test_seq = ${insertResultData.rows[0].test_seq}`
+                        );
+                        //console.log('수정 결과 정보 updateResultData : ', updateResultData);
+
+                        throw '트랜잭션 처리중 Error 강제로 발생시킴';
+
+                        //5. *작업 완료시, 실행(dispatch)한 모든 쿼리를 COMMIT(커밋)
+                        let commitResultData = await client.query('COMMIT');
+                        //console.log('커밋 결과 정보 commitResultData : ', commitResultData);
+
+                        //Promise resolve 처리
+                        resolve(commitResultData);
+                    } catch (error) {
+                        //5. *에러 완료시, 실행(dispatch)한 모든 쿼리를 COMMIT(커밋)
+                        let rollbackResultData = await client.query('ROLLBACK');
+                        console.log('롤백 결과 정보 rollbackResultData : ', rollbackResultData);
+
+                        //Promise reject 처리
+                        reject(error);
+                    } finally {
+                        //6. 받아온 connection pool 반납(해제) - 필수(반납하지 않으면 해당 pool을 사용하는 query는 트랜잭션에 계속 매달려있음)
+                        client.release();
+                    }
+                }).catch((error) => {
+                    //Promise reject 처리
+                    reject(error);
+                })
+            });
+        },//testTransaction() end
+
+    }//return end
+ 
+ })();
+ 
+ //Module Export
+ module.exports = TestDAO;(파일 끝에 줄바꿈 문자 없음)
 
server/service/test/model/TestService.js (added)
+++ server/service/test/model/TestService.js
@@ -0,0 +1,138 @@
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트용 서비스 로직을 담당하는 모듈 입니다.
+ */
+ const TestService = (function () {
+
+    //DAO
+    const TestDAO = require('./TestDAO');
+    
+    //public object
+    return {
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 목록 조회
+         * @callback : parameter -> {data: Object, error: Object}
+         */
+        testSelectList: function (callback, param) {
+            //Data Access 접근
+            let dataAccessResult = TestDAO.testSelectList(param);
+            //Data Access 결과
+            dataAccessResult.then((data) => {
+                console.log('TestService testSelectList connect dataAccessResult : ', data);
+                callback({data: data, error: null});//Router에 결과 전달
+            }).catch((error) => {
+                callback({data: null, error: error});//Router에 Error 전달
+            })
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 상세 조회
+         */
+        testSelectOne: function (callback, param) {
+            param = [1];
+            //Data Access 접근
+            let dataAccessResult = TestDAO.testSelectOne(param);
+            //Data Access 결과
+            dataAccessResult.then((data) => {
+                console.log('TestService testSelectOne connect dataAccessResult : ', data);
+                callback({data: data, error: null});//Router에 결과 전달
+            }).catch((error) => {
+                callback({data: null, error: error});//Router에 Error 전달
+            })
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 등록
+         */
+         testInsertOne: function (callback, param) {
+            param = ['test title', 'test content'];
+            //Data Access 접근
+            let dataAccessResult = TestDAO.testInsertOne(param);
+            //Data Access 결과
+            dataAccessResult.then((data) => {
+                console.log('TestService testInsertOne connect dataAccessResult : ', data.rows[0]['test_seq']);
+                callback({data: data, error: null});//Router에 결과 전달
+            }).catch((error) => {
+                callback({data: null, error: error});//Router에 Error 전달
+            })
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 수정
+         */
+        testUpdateOne: function (callback, param) {
+            param = [1, 'test title update', 'test content update'];
+            //Data Access 접근
+            let dataAccessResult = TestDAO.testUpdateOne(param);
+            //Data Access 결과
+            dataAccessResult.then((data) => {
+                console.log('TestService testUpdateOne connect dataAccessResult : ', data);
+                callback({data: data, error: null});//Router에 결과 전달
+            }).catch((error) => {
+                callback({data: null, error: error});//Router에 Error 전달
+            })
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 데이터 삭제
+         */
+        testDeleteOne: function (callback, param) {
+            param = [10];
+            //Data Access 접근
+            let dataAccessResult = TestDAO.testDeleteOne(param);
+            //Data Access 결과
+            dataAccessResult.then((data) => {
+                console.log('TestService testDeleteOne connect dataAccessResult : ', data);
+                callback({data: data, error: null});//Router에 결과 전달
+            }).catch((error) => {
+                callback({data: null, error: error});//Router에 Error 전달
+            })
+        },
+
+        /**
+         * @author : 최정우
+         * @since : 2022.09.20
+         * @dscription : 테스트 트랜젝션 처리
+         * 
+         * 트랜잭션 작업 진행 시나리오
+         *  1. connection pool 한 개 받아오기
+         *  2. BEGIN(트랜잭션 시작 처리)
+         *  3. [※작업내용1] 데이터 등록
+         *  4. [※작업내용2] 등록한 데이터 수정
+         *  5. *작업 완료시, 실행(dispatch)한 모든 쿼리를 COMMIT(커밋)
+         *     *에러 발생시, 실행(dispatch)한 모든 쿼리를 ROLLBACK(롤백)
+         *  6. 받아온 connection pool 반납(해제) - 필수(반납하지 않으면 해당 pool을 사용하는 query는 트랜잭션에 계속 매달려있음)
+         */
+         testTransaction: function (callback, param) {
+            param = ['test title', 'test content'];
+            //Data Access 접근
+            let dataAccessResult = TestDAO.testTransaction(param);
+            //Data Access 결과
+            dataAccessResult.then((data) => {
+                console.log('TestService testTransaction connect dataAccessResult : ', data);
+                callback({data: data, error: null});//Router에 결과 전달
+            }).catch((error) => {
+                callback({data: null, error: error});//Router에 Error 전달
+            })
+        },
+
+
+    }
+ 
+ })();
+ 
+ //Module Export
+ module.exports = TestService;
+ (파일 끝에 줄바꿈 문자 없음)
 
server/service/test/router/TestRouter.js (added)
+++ server/service/test/router/TestRouter.js
@@ -0,0 +1,119 @@
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트용 Router(Controller) 모듈 입니다.
+ */
+const express = require('express');
+const router = express.Router();
+
+//Service
+const TestService = require('../model/TestService');
+
+
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트 데이터 목록 조회(POST)
+ */
+router.get('/testSelectList.json', function (request, response, next) {
+    //Callback 함수 Parameter result = {data: Object, error: Object}
+    TestService.testSelectList((result) => {
+        if (!result.error) {
+            response.send(result.data);
+        } else {
+            next(result.error);//Global Error Handler에게 예외상황 맡기기
+        }
+    });
+})
+
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트 데이터 상세 조회(POST)
+ */
+router.get('/testSelectOne.json', function (request, response, next) {
+    //Callback 함수 Parameter -> result = {data: Object, error: Object}
+    TestService.testSelectOne((result) => {
+        if (!result.error) {
+            response.send(result.data);
+        } else {
+            next(result.error);//Global Error Handler에게 예외상황 맡기기
+        }
+    });
+})
+
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트 데이터 등록 (POST)
+ */
+router.get('/testInsertOne.json', function (request, response, next) {
+    //Callback 함수 Parameter -> result = {data: Object, error: Object}
+    TestService.testInsertOne((result) => {
+        if (!result.error) {
+            response.send(result.data);
+        } else {
+            next(result.error);//Global Error Handler에게 예외상황 맡기기
+        }
+    })
+})
+
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트 데이터 수정 (POST)
+ */
+router.get('/testUpdateOne.json', function (request, response, next) {
+    //Callback 함수 Parameter -> result = {data: Object, error: Object}
+    TestService.testUpdateOne((result) => {
+        if (!result.error) {
+            response.send(result.data);
+        } else {
+            next(result.error);//Global Error Handler에게 예외상황 맡기기
+        }
+    })
+})
+
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트 데이터 삭제 (POST)
+ */
+router.get('/testDeleteOne.json', function (request, response, next) {
+    //Callback 함수 Parameter -> result = {data: Object, error: Object}
+    TestService.testDeleteOne((result) => {
+        if (!result.error) {
+            response.send(result.data);
+        } else {
+            next(result.error);//Global Error Handler에게 예외상황 맡기기
+        }
+    })
+})
+
+/**
+ * @author : 최정우
+ * @since : 2022.09.20
+ * @dscription : 테스트 트랜젝션 처리
+ * 
+ * 트랜잭션 작업 진행 시나리오
+ *  1. connection pool 한 개 받아오기
+ *  2. BEGIN(트랜잭션 시작 처리)
+ *  3. [※작업내용1] 데이터 등록
+ *  4. [※작업내용2] 등록한 데이터 수정
+ *  5. *작업 완료시, 실행(dispatch)한 모든 쿼리를 COMMIT(커밋)
+ *     *에러 발생시, 실행(dispatch)한 모든 쿼리를 ROLLBACK(롤백)
+ *  6. 받아온 connection pool 반납(해제) - 필수(반납하지 않으면 해당 pool을 사용하는 query는 트랜잭션에 계속 매달려있음)
+ */
+router.get('/testTransaction.json', function (request, response, next) {
+    //Callback 함수 Parameter -> result = {data: Object, error: Object}
+    TestService.testTransaction((result) => {
+        if (!result.error) {
+            response.send(result.data);
+        } else {
+            next(result.error);//Global Error Handler에게 예외상황 맡기기
+        }
+    })
+})
+
+//Module Export
+module.exports = router;(파일 끝에 줄바꿈 문자 없음)
 
webpack.config.js (added)
+++ webpack.config.js
@@ -0,0 +1,42 @@
+const { VueLoaderPlugin } = require("vue-loader");
+
+const {PROJECT_NAME, BASE_DIR, SERVICE_STATUS} = require('./Global');
+
+module.exports = {
+  name: PROJECT_NAME,
+  mode: SERVICE_STATUS,
+  devtool: 'eval',
+
+  entry: {
+    app: [`${BASE_DIR}/client/views/index.js`]
+  },
+
+  module: {
+    rules: [{
+      test: /\.vue?$/,
+      loader: 'vue-loader',
+    }, {
+      test: /\.(js|jsx)?$/,
+      loader: 'babel-loader',
+    }, {
+      test: /\.css$/,
+      use: ['vue-style-loader', 'css-loader']
+    }, {
+      test: /\.(jpe?g|png|gif|svg|ttf|eot|woff|woff2)$/i,
+      use: [{
+        loader:'url-loader',
+        options:{
+          limit:8192,
+          fallback:require.resolve('file-loader')
+        }
+      }]
+    }],
+  },
+
+  plugins: [new VueLoaderPlugin()],
+
+  output: {
+    path: `${BASE_DIR}/client/build`,	// __dirname: webpack.config.js 파일이 위치한 경로
+    filename: 'bundle.js'
+  },
+}(파일 끝에 줄바꿈 문자 없음)
Add a comment
List