빅데이터 Java 개발자 교육 - 44일차 [웹 클로닝 사이트 만들기-4] (실습)

Jun_Gyu·2023년 3월 31일
0
post-thumbnail

금일 수업에서는 관리자용 SB Admin Web 페이지를 클로닝해보았다.
클로닝하려는 페이지는 아래와 같다.

메뉴를 클릭하면 다른 페이지로도 이동하지만, 오늘은 메인 페이지만 최대한 구성해보는 것을 목표로 진행하였다.

먼저 HTML이다

HTML

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset='utf-8'>
    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
    <meta name="author" content="PJK">
    <meta name="description" content="웹솔루션 관리자 화면으로 가장 많이 사용되는 웹템플릿">
    <title>SB Admin Web</title>
    <meta name='viewport' content='width=device-width, initial-scale=1'>

    <!-- 1. Bootstrap용 css CDN 추가 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/css/bootstrap.min.css"
        integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous">
    <!-- 2. Bootstrap용 Icon CDN / font-awesome 아이콘용 CDN -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <!-- 3. font awesome용 css CDN  -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
        integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />

    <!-- CSS -->
    <link rel='stylesheet' type='text/css' media='screen' href='css/main.css'>

    <!-- 아이콘 이미지 추가 -->
    <link rel="icon" href="assets/images/ui.ico">
    <!-- 4. jQuery 추가 -->
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"
        integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script>
    <!-- 5. font awesome용 스크립트 CDN -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"
        integrity="sha512-fD9DI5bZwQxOi7MhYWnnNPlvXdp/2Pj3XSTRrFs5FQa4mizyGLnJcN6tuvUS6LbmgN1ut+XGSABKvjN0H6Aoow=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>

    <!-- JS -->
    <script src=''></script>

</head>

<body class="sb-nav-fixed">
    <!-- 네비게이션 영역 -->
    <nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
        <!-- Navbar Brand -->
        <a class="navbar-brand ps-3" href="#">
            <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-tools"
                viewBox="0 0 16 16">
                <path
                    d="M1 0 0 1l2.2 3.081a1 1 0 0 0 .815.419h.07a1 1 0 0 1 .708.293l2.675 2.675-2.617 2.654A3.003 3.003 0 0 0 0 13a3 3 0 1 0 5.878-.851l2.654-2.617.968.968-.305.914a1 1 0 0 0 .242 1.023l3.27 3.27a.997.997 0 0 0 1.414 0l1.586-1.586a.997.997 0 0 0 0-1.414l-3.27-3.27a1 1 0 0 0-1.023-.242L10.5 9.5l-.96-.96 2.68-2.643A3.005 3.005 0 0 0 16 3c0-.269-.035-.53-.102-.777l-2.14 2.141L12 4l-.364-1.757L13.777.102a3 3 0 0 0-3.675 3.68L7.462 6.46 4.793 3.793a1 1 0 0 1-.293-.707v-.071a1 1 0 0 0-.419-.814L1 0Zm9.646 10.646a.5.5 0 0 1 .708 0l2.914 2.915a.5.5 0 0 1-.707.707l-2.915-2.914a.5.5 0 0 1 0-.708ZM3 11l.471.242.529.026.287.445.445.287.026.529L5 13l-.242.471-.026.529-.445.287-.287.445-.529.026L3 15l-.471-.242L2 14.732l-.287-.445L1.268 14l-.026-.529L1 13l.242-.471.026-.529.445-.287.287-.445.529-.026L3 11Z" />
            </svg>
        </a> <!-- # == ./index.html -->

        <!-- SideBar Toggle -->
        <button class="btn btn-lick btn-sm order-1 order-lg-0 me-4 me-lg-0" id="sidebarToggle" type="button">
            <i class="bi bi-menu-up" style="color: #0d6dfa;"></i>

        </button>
        <!-- serach bar -->
        <form class="d-none d-md-inline-block form-inline ms-auto me-0 me-md-3 my-2 my-md-0">
            <div class="input-group">
                <input class="form-control" type="text" placeholder="검색어 입력 ..." aria-label="검색어 입력 ..."
                    aria-describedby="btnSearch">
                <button class="btn btn-primary" id="btnSearch" type="submit" type="submit"><i
                        class="bi bi-search"></i></button>
            </div>
        </form>
        <!-- Profile 영역 -->
        <ul class="navbar-nav ms-auto ms-md-0 me-3 me-lg-4">
            <li class="nav-item dropdown"> <a class="nav-link dropdown-tohhle" id="profile" href="#" role="button"
                    data-bs-toggle="dropdown" aria-expanded="false">
                    <i class="bi bi-person-circle"></i></a>
                <ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="profile">
                    <li><a class="dropdown-item" href="#!">Setting</a></li>
                    <li><a class="dropdown-item" href="#!">Activity Log</a></li>
                    <li>
                        <hr class="dropdown-divider">
                    </li>
                    <li><a class="dropdown-item" href="#!">Log out</a></li>
                </ul>
            </li>

        </ul>
    </nav>
    <!-- 전체화면 영역 -->
    <div id="layoutTotal">
        <!-- 전체화면 > 좌측 메뉴 영역(사이드바) -->
        <div id="layoutLeftSide">
            <nav class="sb-sidenav accordion sb-sidenav-dark" id="sidenavAccordion">
                <div class="sb-sidenav-menu">
                    <div class="nav">
                        <div class="sb-sidenav-menu-heading">Main</div>
                        <a class="nav-link" href="./index.html">
                            <div class="sb-nav-link-icon"><i class="fas fa-tachometer-alt"></i></div>
                            Dashboard
                        </a>
                        <div class="sb-sidenav-menu-heading">INTERFACE</div>
                        <a class="nav-link collapsed" href="#" data-bs-toggle="collapse"
                            data-bs-target="#collapseLayouts" aria-expanded="false" aria-controls="collapseLayout">
                            <div class="sb-nav-link-icon"><i class="fas fa-columns"></i></div>
                            Layouts
                            <div class="sb-sidenav-collapse-arrow"><i class="fas fa-angle-down"></i></div>
                        </a>
                        <div class="collapse" id="collapseLayouts" aria-labelledby="headingOne"
                            data-bs-parents="#sidenavAccordion">
                            <nav class="sb-sidenav-menu-nested nav">
                                <a class="nav-link" href="">Static Navigation</a>
                                <a class="nav-link" href="">Light Theme</a>
                            </nav>
                        </div>
                        <a class="nav-link collapsed" href="#" data-bs-toggle="collapse" data-bs-target="#collapsePages"
                            aria-expanded="false" aria-controls="collapsePages">
                            <div class="sb-nav-link-icon"><i class="fas fa-book-open"></i></div>
                            Pages
                            <div class="sb-sidenav-collapse-arrow"><i class="fas fa-angle-down"></i></div>
                        </a>
                        <div class="collapse" id="collapsePages" aria-labelledby="headingTwo"
                            data-bs-parents="#sidenavAccordion">
                            <a class="nav-link collapsed" href="#" >Authentication</a>
                                                    
                            <nav class="sb-sidenav-menu-nested nav accordion" id="sidenavAccordionPages"> 
                                    <a class="nav-link" href="#!">Log in</a>
                                    <a class="nav-link" href="#!">Register</a>
                                    <a class="nav-link" href="#!">Forgot password</a>
                                </nav>
                            <a>Error</a>
                            <div>
                                <nav>
                                    <a class="nav-link" href="#!">401 Page</a>
                                    <a class="nav-link" href="#!">404 Page</a>
                                    <a class="nav-link" href="#!">500 Page</a>
                                </nav>
                            </div> 
                        </div>
                        <div class="sb-sidenav-menu-heading">ADDONS</div>
                        <a>
                            <div class="sb-nav-link-icon"><i class="fas fa-chart-area me-1"></i></div>
                            Charts
                        </a>
                        <a>
                            <div><i class="fas fa-chart-pie me-1"></i></div>
                            Tables
                        </a>
                        </div>
                    </div>
                    <div class="sb-sidenav-footer">
                        <div class="samll">Logged in as :</div>
                        pjk1025
                    </div>
            </nav>
        </div>
        <!-- 전체화면 > 우측 메인화면 영역 -->
        <div id="layoutContent">
            <main>
                <div class="container-fluid px-4"> <!-- 옆쪽에 딱 붙음. -->
                    <h1 class="mt-4">Dashboard</h1>
                    <ul class="breadcrum mb-4">
                        <li class="breadcrumb-item active">Dashboard</li>
                    </ul>
                    <!-- 카드영역 -->
                    <div class="row">
                        <div class="col-lg-3 col-md-6">
                            <div class="card bg-primary text-white mb-4"> <!-- primary : 파란색  -->
                                <div class="card-body">1번 카드</div>
                                <div class="card-footer d-flex justify-content-between align-items-center">
                                    <a class="small text-white stretched-link">상세</a>
                                    <div class="samll text-white"><i class="fas fa-angle-right"></i></div>
                                </div>
                            </div>
                        </div>
                        <div class="col-lg-3 col-md-6">
                            <div class="card bg-warning text-white mb-4">
                                <div class="card-body">2번 카드</div>
                                <div class="card-footer d-flex justify-content-between align-items-center">
                                    <a class="small text-white stretched-link">상세</a>
                                    <div class="samll text-white"><i class="fas fa-angle-right"></i></div>
                                </div>
                            </div>
                        </div>
                        <div class="col-lg-3 col-md-6">
                            <div class="card bg-success text-white mb-4">
                                <div class="card-body">3번 카드</div>
                                <div class="card-footer d-flex justify-content-between align-items-center">
                                    <a class="small text-white stretched-link">상세</a>
                                    <div class="samll text-white"><i class="fas fa-angle-right"></i></div>
                                </div>
                            </div>
                        </div>
                        <div class="col-lg-3 col-md-6">
                            <div class="card bg-danger text-white mb-4">
                                <div class="card-body">4번 카드</div>
                                <div class="card-footer d-flex justify-content-between align-items-center">
                                    <a class="small text-white stretched-link">상세</a>
                                    <div class="samll text-white"><i class="fas fa-angle-right"></i></div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <!-- 차트영역 -->
                    <div class="row">
                        <div class="col-xl-6">
                            <div class="card mb-4">
                                <div class="card-header">
                                    <i class="fas fa-chart-area me-1"></i>
                                    Area Chart Examples
                                </div>
                                <div class="card-body">
                                    <canvas id="myAreaChart" width="100%" height="50"></canvas>
                                </div>
                            </div>
                        </div>
                        <div class="col-lg-6">
                            <div class="card mb-4">
                                <div class="card-header">
                                    <i class="fas fa-chart-bar me-1"></i>
                                    Bar Chart Examples
                                </div>
                                <div class="card-body">
                                    <canvas id="myBarChart" width="100%" height="50"></canvas>
                                </div>
                            </div>
                        </div>
                    </div>
                    <!-- 파이 테이블 영역 -->
                    <div class="row">
                        <div class="col">
                            <div class="card mb-4">
                                <div class="card-header">
                                    <i class="fas fa-chart-pie me-1"></i>
                                    Pie Chart Example
                                </div>
                                <div class="card-body">
                                    <canvas id="myPieChart" width="100%" height="50"></canvas>
                                </div>
                            </div>
                        </div>
                    </div>
                     <!-- 보드 테이블 영역 -->
                    <div class="row">
                        <div class="col">
                            <div class="card mb-4">
                                <div class="card-header">
                                    <i class="fas fa-chart-pie me-1"></i>
                                    Board Chart Example
                                </div>
                                <div class="card-body">
                                    <table>

                                    </table>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </main>
            <footer class="py-4 bg-light mt-auto">
                <div class="container-fluid py-5">
                    <div class="d-flex justify-content-between align-items-center small">
                        <div class="text-muted">Copyright &copy; PKNU 2023.</div>
                        <div>
                            <a href="#!">Privacy Policy</a> &middot; <a href="#!">Terms</a> &amp; <a
                                href="#!">Conditions</a>
                        </div>
                    </div>
                </div>
            </footer>
        </div>
    </div>

    <!-- 6. Bootstrap용 js는 body에 위치해야 함! -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N"
        crossorigin="anonymous"></script>
        <!-- Chart.js용 CDN -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js" crossorigin="anonymous"></script>
    <script src="js/chart-area-demo.js"></script>
    <script src="js/chart-bar-demo.js"></script>
    <script src="js/chart-pie-demo.js"></script>
</body>

</html>

CSS를 구성하기 전 Bootstrap을 최대한 활용하고자 CDN을 통해 icon 이미지나 여러가지 컬럼들을 활성화 해주었으며,

또한, 도표 샘플데이터를 표시하기 위하여 Chart.js를 사용하였다.

JS나 CSS를 활성화 하기 전 HTML로 구성을 마친 모습은 아래 사진과 같다.

이번 클로닝에서는 기존 페이지들과 달리 네비게이션 메뉴가 Toggle 형식으로 좌측에 표시되는 형태이기 때문에, 대략적인 기능들만 미리 구현을 해둔 뒤 나머지는 CSS에서 디테일하게 위치를 잡아주도록 하겠다.

CSS

@charset "UTF-8";
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');


* {
    font-family: 'Roboto', sans-serif;
}

html,
body {
    height: 100vh;
}


hr {
    margin: 1rem 0;
    color: inherit;
    border: 0;
    border-top: 1px solid;
    opacity: 0.25;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    margin-top: 0;
    margin-bottom: 0.5rem;
    font-weight: 500;
    line-height: 1.2;
}

p {
    margin-top: 0;
    margin-bottom: 1.0rem;
}

ul {
    /* ul 점 사라지게 하는 코드. */
    list-style: none;
    padding-left: 0px;
}


.sb-nav-fixed #layoutLeftSide .sb-topnav {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    z-index: 999;
}

.sb-nav-fixed #layoutTotal #layoutLeftSide {
    position:relative;
}

/* 전체 레이아웃 */
#layoutTotal {
    display: flex;
}

#layoutTotal #layoutLeftSide {
    flex-basis: 225px;
    flex-shrink: 0;
    transition: transform 0.15s ease-in-out;
    z-index: 1000;
    /* transform: translateX(-225px); /* 화면 왼쪽으로 사라지게 하기 위함이다.  */
}

#layoutTotal #layoutContent {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    min-width: 0;
    flex-grow: 1;
    min-height: 100%;
    /* margin-left: -225px */
}

/* 전체레이아웃 끝 */


/* 탑메뉴 */
.sb-topnav {
    padding-left: 0;
    height: 56px;
    z-index: 1001;

}

.sb-topnav .navbar-brand {
    width: 225px;
    margin: 0;
}

.sb-topnav #sidebarToggle {
    color: rgba(255, 255, 255, 0.5);
}

/* 왼쪽메뉴 */
.sb-sidenav {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    height: 100vh;
}

.sb-sidenav .sb-sidenav-menu {
    flex-grow: 1;
}

.sb-sidenav .sb-sidenav-menu .nav
{
    flex-direction: column;
    flex-wrap: nowrap;
}

.sb-sidenav .sb-sidenav-menu .nav .sb-sidenav-menu-heading {
    padding: 1.75rem 1rem 0.75rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    opacity: 0.7;
}

.sb-sidenav .sb-sidenav-menu .nav .nav-link {
    display: flex;
    align-items: center;
    padding-top: 0.75rem;
    padding-bottom: 0.75rem;
    position: relative;
}

.sb-sidenav .sb-sidenav-menu .nav .nav-link .sb-nav-link-icon {
    font-size: 0.9rem;
    padding-right: 0.5rem;
}

.sb-sidenav .sb-sidenav-menu .nav .nav-link .sb-sidenav-collapse-arrow {
    display: inline-block;
    margin-left: auto;
    transition: transform 0.15s;
}

.sb-sidenav .sb-sidenav-menu .nav .nav-link.collapsed .sb-sidenav-collapse-arrow {
    transform: rotate(-90deg);
}
.sb-sidenav .sb-sidenav-footer{
    padding: 0.75rem;
    flex-shrink: 0;
}

.sb-sidenav-dark {
    background-color: #212529;
    color: rgba(255, 255, 255, 0.5);
}
.sb-sidenav-dark .sb-sidenav-footer{
    background-color: #343a40;
}

CSS에서는 좌측 #layoutLeftSide 를 클로닝하려는 웹 샘플과 최대한 유사하도록 코드를 구성하였다.

JS

그래프

// Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#292b2c';

// Area Chart Example
var ctx = document.getElementById("myAreaChart");
var myLineChart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: ["3월 1일", "3월 2일", "3월 3일", "3월 4일", "3월 5일", "3월 6일", "3월 7일", "3월 8일", "3월 9일", "3월 10일", "3월 11일", "3월 12일", "3월 13일"],
    datasets: [{
      label: "Sessions",
      lineTension: 0.3,
      backgroundColor: "rgba(2,117,216,0.2)",
      borderColor: "rgba(2,117,216,1)",
      pointRadius: 5,
      pointBackgroundColor: "rgba(2,117,216,1)",
      pointBorderColor: "rgba(255,255,255,0.8)",
      pointHoverRadius: 5,
      pointHoverBackgroundColor: "rgba(2,117,216,1)",
      pointHitRadius: 50,
      pointBorderWidth: 2,
      data: [10000, 30162, 26263, 18394, 18287, 28682, 31274, 33259, 25849, 24159, 32651, 31984, 38451],
    }],
  },
  options: {
    scales: {
      xAxes: [{
        time: {
          unit: 'date'
        },
        gridLines: {
          display: false
        },
        ticks: {
          maxTicksLimit: 7
        }
      }],
      yAxes: [{
        ticks: {
          min: 0,
          max: 40000,
          maxTicksLimit: 5
        },
        gridLines: {
          color: "rgba(0, 0, 0, .125)",
        }
      }],
    },
    legend: {
      display: false
    }
  }
});

막대

// Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#292b2c';

// Bar Chart Example
var ctx = document.getElementById("myBarChart");
var myLineChart = new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월" ,"10월", "11월", "12월"],
    datasets: [{
      label: "Revenue",
      backgroundColor: "rgba(2,117,216,1)",
      borderColor: "rgba(2,117,216,1)",
      data: [4215, 5312, 6251, 7841, 9821, 14984, 8847,1837,2497,24422,29847,39921],
    }],
  },
  options: {
    scales: {
      xAxes: [{
        time: {
          unit: 'month'
        },
        gridLines: {
          display: false
        },
        ticks: {
          maxTicksLimit: 6
        }
      }],
      yAxes: [{
        ticks: {
          min: 0,
          max: 100000,
          maxTicksLimit: 5
        },
        gridLines: {
          display: true
        }
      }],
    },
    legend: {
      display: false
    }
  }
});

원형그래프

// Set new default font family and font color to mimic Bootstrap's default styling
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';

Chart.defaults.global.defaultFontColor = '#292b2c';

// Pie Chart Example
var ctx = document.getElementById("myPieChart");
var myPieChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ["Top", "Mid", "Jungle", "Bottom"],
    datasets: [{
      data: [12.21, 15.58, 11.25, 8.32],
      backgroundColor: ['#007bff', '#dc3545', '#ffc107', '#28a745'],
    }],
  },
});

js의 경우에는 위에서도 언급했듯이 Char.js를 통하여 도표 샘플데이터를 가져와 적용한 모습이다.

Feedback

현재 메인페이지만 구현을 마친 상태이기 때문에 아직 치명적인 문제점을 발견하진 못했지만, 기능적으로 다소 부족한 부분이 존재하였다.

그중에서도 네비게이션 메뉴 기능을 구현하는데 있어서 해결하지 못한 부분들이 다소 존재하였다.

먼저 메뉴 버튼을 클릭하여 Toggle 방식으로 네비게이션 메뉴를 활성화,비활성화 시키는것이 주 목적이었지만, 현재 작동을 하지 않고 있는 상태이다.

다음으로는 "Pages" 부모 collumn 속에 Authentication, Error 자식 collumn이 존재하며, 각각의 항목들을 따로따로 볼 수 있도록 구성을 해주어야 하는데, 현재 구분없이 한꺼번에 나타나고 있는 모습이다.

또한 한 메뉴를 활성화할때, 다른메뉴는 저절로 비활성화되도록 구성해주어야 한다.
(쉽게말해 Layouts을 누르면 Pages는 닫히는 것.)

마지막으로는 모든 메뉴바가 스크롤을 따라서 고정되어 움직여야 한다는 것이다.


profile
시작은 미약하지만, 그 끝은 창대하리라

0개의 댓글