부트스트랩과 c# web form, ms-sql를 이용해서 학교의 학급, 교사, 학생을 관리하는 프로젝트를 진행합니다. 데이터베이스는 해당 사항을 구현하기전에 그때그때 처리하고, 부트스트랩 기반으로 페이지를 설계하여 기능을 추가 해 나가도록 하겠습니다.
vs를 실행하여 새로운 프로젝트를 생성해줍니다. web form으로 진행하지만 굳이 web from 템플릿을 지정해주지는 않고, 사이드에 있는 폴더 및 핵심 참조 추가에서 웹폼을 체크해주고 진행합니다.
그리고 생성된 프로젝트의 오른쪽 마우스를 클릭하여 Nuget을 이용해 bootstrap을 설치하겠습니다.
정상적으로 설치됐다면 해당 Scripts 폴더에 js파일들이 저장됩니다. jquery, popper, bootstrap 파일들을 일단 사용할겁니다.
새로운 폴더를 만들고, Master page 파일을 생성합니다. 그 후에 해당 코드를 붙여넣습니다. 부트스트랩을 이용한 템플릿으로 설계된 웹페이지 사이트입니다.
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="AdminMst.master.cs" Inherits="Project_PracticeSH.Admin.AdminMst" %>
<!DOCTYPE html>
<html>
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title> 학교 매니지먼트 </title>
<script src="https://kit.fontawesome.com/b99e675b6e.js"></script>
<link href="../Content/styles.css" rel="stylesheet" />
<link href="../Content/bootstrap.min.css" rel="stylesheet" />
<script src="../Scripts/jquery-3.0.0.js"></script>
<script src="../Scripts/popper.js"></script>
<script src="../Scripts/bootstrap.min.js"></script>
<script>
$(document).ready(function () {
$(".siderbar_menu li").click(function () {
$(".siderbar_menu li").removeClass("active");
$(this).addClass("active");
});
$(".hamburger").click(function () {
$(".wrapper").addClass("active");
});
$(".close, .bg_shadow").click(function () {
$(".wrapper").removeClass("active");
});
});
</script>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<div class="wrapper">
<div class="sidebar">
<div class="bg_shadow"></div>
<div class="sidebar_inner">
<div class="close">
<i class="fas fa-times"></i>
</div>
<div class="profile_info">
<div class="profile_img">
<img src="../Image/logo.png" alt="profile_img">
</div>
<div class="profile_data">
<p class="name">Admin Module</p>
<span><i class="fas fa-map-marker-alt"></i>경기도, 안양</span>
</div>
</div>
<ul class="siderbar_menu">
<li>
<a href="../Admin/AdminHome.aspx">
<div class="icon"><i class="fas fa-home"></i></div>
<div class="title">홈</div>
</a>
</li>
<li>
<a href="#">
<div class="icon"><i class="fas fa-hotel"></i></div>
<div class="title"> 학급</div>
<div class="arrow"><i class="fas fa-chevron-down"></i></div>
</a>
<ul class="accordion">
<li><a href="../Admin/AddClass.aspx" class="active"><i class="fas fa-user-plus pr-1"></i>학급 추가</a></li>
<li><a href="../Admin/ClassFees.aspx" class="active"><i class="fas fa-money-bill-alt pr-1"></i>학급 관리비</a></li>
</ul>
</li>
<li>
<a href="../Admin/Subject.aspx">
<div class="icon"><i class="fas fa-book"></i></div>
<div class="title">과제</div>
</a>
</li>
<li>
<a href="#">
<div class="icon"><i class="fas fa-user-tie"></i></div>
<div class="title">교사</div>
<div class="arrow"><i class="fas fa-chevron-down"></i></div>
</a>
<ul class="accordion">
<li><a href="../Admin/Teacher.aspx" class="active"><i class="fas fa-user-plus pr-1"></i>교사 추가</a></li>
<li><a href="../Admin/TeacherSubject.aspx" class="active"><i class="fas fa-book-reader pr-1"></i>교사 과목</a></li>
<li><a href="../Admin/Expense.aspx" class="active"><i class="fas fa-hand-holding-usd pr-1"></i>추가</a></li>
<li><a href="../Admin/ExpenseDetails.aspx" class="active"><i class="fas fa-money-check-alt pr-1"></i>자세히</a></li>
</ul>
</li>
<li>
<a href="#">
<div class="icon"><i class="fas fa-user-graduate"></i></div>
<div class="title">학생</div>
<div class="arrow"><i class="fas fa-chevron-down"></i></div>
</a>
<ul class="accordion">
<li><a href="../Admin/Student.aspx" class="active"><i class="fas fa-users pr-1"></i>학생 등록</a></li>
<li><a href="../Admin/StudAttendanceDetails.aspx" class="active"><i class="far fa-list-alt pr-1"></i>학생 세부정보</a></li>
</ul>
</li>
<li>
<a href="#">
<div class="icon"><i class="fas fa-clipboard-list"></i></div>
<div class="title">시험</div>
<div class="arrow"><i class="fas fa-chevron-down"></i></div>
</a>
<ul class="accordion">
<li><a href="../Admin/Marks.aspx" class="active"><i class="fas fa-notes-medical pr-1"></i>시험지 추가</a></li>
<li><a href="../Admin/MarkDetails.aspx" class="active"><i class="fas fa-clipboard-check pr-1"></i>시험 결과</a></li>
</ul>
</li>
<li>
<a href="#">
<div class="icon"><i class="fas fa-calendar-alt"></i></div>
<div class="title">출석현황</div>
<div class="arrow"><i class="fas fa-chevron-down"></i></div>
</a>
<ul class="accordion">
<li><a href="../Admin/EmployeeAttendance.aspx" class="active"><i class="fas fa-calendar-plus pr-1"></i>출석현황</a></li>
<li><a href="../Admin/EmpAttendanceDetails.aspx" class="active"><i class="fas fa-calendar-check pr-1"></i>출석 세부사항</a></li>
</ul>
</li>
</ul>
<div class="logout_btn">
<asp:LinkButton ID="btnLogOut" runat="server" CausesValidation="false">로그아웃</asp:LinkButton>
</div>
</div>
</div>
<div class="main_container">
<div class="navbar">
<div class="hamburger">
<i class="fas fa-bars"></i>
</div>
<div class="logo">
<a href="#">학교이름</a>
</div>
</div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</div>
</form>
</body>
</html>
content 폴더에 style.css파일을 생성하고 하단 내용을 붙여넣습니다. 큰 틀의 html, css, js 구성은 어느정도 부트스트랩이 도 맡아 해주기 때문에 부트스트랩의 활용법을 알고가는것이 좋습니다.
body {
}
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,600,700|Trade+Winds&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
list-style: none;
text-decoration: none;
font-family: 'Montserrat';
}
body {
background: #e7f1ff;
font-size: 14px;
letter-spacing: 1px;
}
.wrapper {
display: flex;
width: 100%;
}
.sidebar {
position: relative;
}
.sidebar .bg_shadow {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
background: #000;
z-index: 998;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.sidebar_inner {
width: 250px;
position: fixed;
top: 0;
left: 0;
height: 100vh;
background: #5558c9;
z-index: 999;
transition: all 0.3s ease;
}
.main_container {
margin-left: 250px;
width: calc(100% - 250px);
transition: all 0.3s ease;
}
.sidebar_inner .profile_info {
padding: 20px;
text-align: center;
}
.sidebar_inner .profile_info .profile_img {
width: 100px;
margin: 0 auto 5px;
}
.sidebar_inner .profile_info .profile_img img {
width: 100%;
display: block;
}
.sidebar_inner .profile_info .profile_data .name {
font-size: 18px;
color: #fff;
margin-bottom: 5px;
font-family: 'Trade Winds';
}
.sidebar_inner .profile_info .profile_data span {
color: #c4dcff;
}
.sidebar_inner .siderbar_menu {
height: 500px;
overflow: auto;
}
.sidebar_inner .siderbar_menu > li > a {
padding: 12px 20px;
display: flex;
align-items: center;
position: relative;
margin-bottom: 1px;
color: #c4dcff;
}
.sidebar_inner .siderbar_menu > li > a .icon {
font-size: 25px;
margin-right: 15px;
}
.sidebar_inner .siderbar_menu > li.active > a,
.sidebar_inner .siderbar_menu > li > a:hover {
background: #3d3d79;
color: #fff;
text-decoration: none !important;
}
.sidebar_inner .siderbar_menu > li > a .arrow {
position: absolute;
top: 20px;
right: 20px;
transition: all 0.3s ease;
}
.sidebar .logout_btn a {
position: absolute;
bottom: 20px;
left: 20px;
width: 210px;
border: 1px solid #fff;
color: #fff;
border-radius: 5px;
font-weight: 600;
padding: 10px;
text-align: center;
transition: all 0.3s ease;
}
.sidebar .logout_btn a:hover {
background: #fff;
color: #3d3d79;
text-decoration: none;
}
.sidebar_inner .close {
position: absolute;
top: 5px;
right: 15px;
font-size: 25px;
color: #fff;
cursor: pointer;
display: none;
}
.sidebar_inner .close:hover,
.navbar .hamburger:hover {
opacity: 0.7;
}
.navbar {
background: #fff;
height: 50px;
width: 100%;
box-shadow: 0 3px 5px rgba(0,0,0,0.125);
display: flex;
align-items: center;
padding: 0 20px;
}
.navbar .hamburger {
font-size: 25px;
cursor: pointer;
margin-right: 20px;
color: #5558c9;
display: none;
}
.navbar .logo a {
font-family: 'Trade Winds';
color: #5558c9;
font-size: 20px;
}
.content {
padding: 20px;
display: flex;
flex-wrap: wrap;
}
.content .item {
background: #fff;
box-shadow: 2px 2px 4px rgba(0,0,0,0.125), -2px -2px 4px rgba(0,0,0,0.125);
margin: 10px 5px;
width: 31.8%;
padding: 20px;
}
.accordion {
background: #6f6fc7;
padding-left: 20px;
height: 0px;
transition: all 0.3s ease;
overflow: hidden;
}
.accordion li a {
display: block;
color: #c4dcff;
padding: 12px 0;
padding-left: 10px;
}
.accordion li:last-child a {
border-bottom: 0px;
}
.accordion li a.active {
color: #fff;
text-decoration: none;
}
.accordion li a:hover {
background: #3d3d79;
color: #fff;
}
.siderbar_menu > li.active .accordion {
height: auto;
}
.siderbar_menu > li.active .arrow {
transform: rotate(180deg);
transition: all 0.3s ease;
}
@media (max-width: 1024px) {
.sidebar_inner {
left: -115%;
transition: all 0.5s ease;
}
.main_container {
width: 100%;
margin-left: 0;
}
.navbar .hamburger,
.sidebar_inner .close {
display: block;
}
.content .item {
width: 47%;
}
.wrapper.active .sidebar_inner {
left: 0;
transition: all 0.5s ease;
}
.wrapper.active .sidebar .bg_shadow {
visibility: visible;
opacity: 0.7;
}
}
@media (max-width: 528px) {
.content .item {
width: 100%;
}
}
실행해서 잘 적용이 됐다면 다시 추가 키를 눌러서 새로운 페이지를 추가해줍니다. 이번에는 마스터 페이지 내부에 추가할 수 있는 Web Form with Master Page 입니다. 언어 설정이 다르면 검색해도 자동으로 나오지 않습니다.
AdminHome.aspx를 생성합니다. 사이트를 들어갔을때 맨 처음, 아무것도 누르지 않은 default 값의 보여줄 화면입니다. 버튼의 기능구현을 먼저한 후에 하나씩 편집해나가고 현재로써는 홈페이지인것을 표시만 해주겠습니다.
<div style="background-image:url('')">
<div class="container p-md-4 p-sm-4">
<div>
<asp:Label ID="IblMsg" runat="server"></asp:Label>
</div>
<h2 class="text-center"> 운영 홈페이지</h2>
</div>
</div>
전체적으로 ui들이 잘 작동하는지, 오류는 없는지 확인하고 위에서 부터 천천히 기능구현을 시작합니다. 제일 기본적인 Class 학급 추가버튼부터 진행합니다. 새로운 학급 이름을 입력하고, 추가버튼을 클릭하면 데이터 베이스에 추가됩니다. 각 고유번호가 부여되고, 정렬을 위한 Sr.No까지 보여줍니다.
동일한 데이터가 있다면 상단의 이미 동일한 데이터가 존재한다는 알림창을, js를 이용해서 출력해줄 예정입니다. 데이터베이스를 연결하기 위해 Web.config에서 설정을 계속합니다. 문구 삽입위치는 configuration단 사이입니다.
<connectionStrings>
<add name="SchoolCS" connectionString="Data Source=SQL이름; Initial Catalog=데이터베이스 이름; Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
<connectionStrings> 사이에 데이터베이스 연결에 필요한 정보를 입력하고, 저번에 MS-SQL과 연동했던 방법으로 aspx.cs 파일에서 편집해 줍니다. </connectionStrings> 저는 ms-sql에 접속하여서 programmingDB 데이터베이스를 생성하여 그 곳에서 테이블을 생성하고 쿼리작업을 진행할 예정입니다.
CREATE TABLE Class (
ClassId int primary key identity(1,1) not null,
ClassName varchar(50) null
)
Class 테이블입니다. 학급 추가할때 사용할 class 테이블이고 ClassId는 다른곳에서도 fk로 사용할 예정입니다. 지금 당장 진행할 작업은 더없으니, 다시 vs편집을 진행합니다. webconfig에서 connectionString를 제외하고 <appSetting>을 추가해서 유효성검사 기능을 추가합니다.
query를 연결하기 위해서 Models 폴더에 CommonFn 클래스 파일을 생성해줍니다. 저번 ms연동과 비슷한 부분이 많으니 코드 전문을 먼저 확인하겠습니다.
protected void Button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=DESKTOP-F3TPC61;Initial Catalog=programmingDB;User ID=계정;Password=비밀번호");
con.Open();
SqlCommand comm = new SqlCommand("Insert into Student_Info values('" + int.Parse(StudentID.Text) + "','" + StudentName.Text + "','" + Address.Text + "','" + double.Parse(Age.Text) + "','" + Contact.Text + "')", con);
comm.ExecuteNonQuery();
con.Close();
ScriptManager.RegisterStartupScript(this, this.GetType(), "script", "alert('Successfully Inserted');", true);
}
위는 저번에 연동을 했던 클래스파일입니다. 이 경우는 sqlConnection 구현을 button1_click 내부에 올려놓았는데, 이번엔 공용 메서드를 따로 만들어서 필요할때마다 키워드로 꺼내고 각 버튼구현에서는 쿼리문 만 설정하겠습니다.
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
namespace Project_PracticeSH.Models
{
public class CommonFn
{
public class CommonFnx
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["SchoolCS"].ConnectionString);
public void Query(string query)
{
if ( con.State == ConnectionState.Closed )
{
con.Open();
}
SqlCommand cmd = new SqlCommand(query, con);
cmd.ExecuteNonQuery();
con.Close();
}
public DataTable Fetch(string query)
{
if (con.State == ConnectionState.Closed)
{
con.Open();
}
SqlCommand cmd = new SqlCommand(query, con);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
sda.Fill(dt);
return dt;
}
}
}
}
Query, fetch, commonFnx 키워드로 필요한 정보들을 그때그때 불러들이겠습니다.
<li>
<a href="#">
<div class="icon"><i class="fas fa-hotel"></i></div>
<div class="title"> 학급</div>
<div class="arrow"><i class="fas fa-chevron-down"></i></div>
</a>
<ul class="accordion">
<li><a href="../Admin/AddClass.aspx" class="active"><i class="fas fa-user-plus pr-1"></i>학급 추가</a></li>
<li><a href="../Admin/ClassFees.aspx" class="active"><i class="fas fa-money-bill-alt pr-1"></i>학급 관리비</a></li>
</ul>
</li>
해당 코드는 Mst 페이지에 있는 코드입니다. 학급을 눌렀을때 펼쳐지는 아코디언 기능에서 a href링크를 보시면, AddClass.aspx로 전환되는 것을 알 수 있습니다. 위와 같이 다시한번 페이지를 추가하여 AddClass.aspx를 생성합니다.
<div style="background-image:url('')">
<div class="container p-md-4 p-sm-4">
<div>
<asp:Label ID="IblMsg" runat="server"></asp:Label>
</div>
<h3 class="text-center"> 새로운 학급 추가</h3>
<div class="row mb-3 mr-lg-5 ml-lg-5 mt-md-5">
<div class="col-md-6">
<label for="textClass"> 학급 이름 </label>
<asp:TextBox ID="txtClass" runat="server" CssClass="form-control" placeholder="학급 이름을 입력해주세요" required></asp:TextBox>
</div>
</div>
<div class="row mb-3 mr-lg-5 ml-lg-5">
<div class="col-md-3 col-md-offset-2 mb-3">
<asp:Button ID="btnAdd" runat="server" CssClass="btn btn-primary btn-block" BackColor="#5558C9" Text="추가" OnClick="btnAdd_Click"/>
</div>
</div>
<div class="row mb-3 mr-lg-5 ml-lg-5 mt-md-5">
<div class="col-md-6">
<asp:GridView ID="GridView1" runat="server" CssClass="table table-hover table-bordered"></asp:GridView>
</div>
</div>
</div>
</div>
메인 운영페이지 텍스트가 적혀있던 div단 하단에 학급 이름과 텍스트칸, 버튼과 그 색깔을 조정했고, 이미 저장돼 있는 정보들을 표시해주는 grid를 플로팅해주려고 GridView를 추가했습니다. cssClass로 해당 부트스트랩에 정보들을 수정했습니다.
aspx파일이 모두 정상적으로 나온다면, 내부에 cs파일에서 기능을 구현하겠습니다.
CommonFnx fn = new CommonFnx();
만들어놓았던 model을 사용하기 위해 제일 먼저 CommonFnx를 초기화 한 후에 진행합니다. DB에 있는 정보를 빼내는 문구부터 진행하겠습니다. 학급을 불러오는 GetClass() 클래스를 생성합니다.
private void GetClass()
{
DataTable dt = fn.Fetch("쿼리문");
GridView1.DataSource = dt;
GridView1.DataBind();
}
GridView에 데이터를 삽입하는 것은 저번에도 진행하였습니다. DataTable dt = 를 이용하여 쿼리문을 작성할건데, 미리 만들어 놓았던 CommonFnx를 이용하여 내부에 있는 데이터베이스 접속 부분을 사용하여 연결합니다. 모든 정보를 불러들일건데, 이번에는 순서대로 1번부터 정렬을 해야합니다. Row_number를 이용하여 내림차순으로 정렬하려 합니다.
https://learn.microsoft.com/ko-kr/sql/t-sql/functions/row-number-transact-sql?view=sql-server-ver16
ROW_NUMBER(Transact-SQL) - SQL Server
ROW_NUMBER 함수의 Transact-SQL 참조입니다. 이 함수는 결과 집합의 출력 번호를 매깁니다.
learn.microsoft.com
해당 microsoft 사이트에 정보가 나와있으니 참고하시면 좋을 듯 합니다.
select Row_NUMBER() over(Order by (Select 1)) as [Sr.No], ClassId, ClassName from Class
Class 테이블에 [sr.no], classId, ClassName 을 내림차순으로 정렬하는 쿼리문입니다. sql 데이터베이스에서 실행 후 오류가 없다면 해당 쿼리문 입력하는 부분에 대체하여 진행합니다. 그 후, getClass가 사이트에서 바로 로딩될 수 있게 Page_Load 메서드에서 다음 코드를 진행합니다.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack )
{
GetClass();
}
}
데이터가 잘 들어오는지, 데이터베이스에서 임의로 정보를 입력하고 해당 페이지를 출력해봅니다.
출력이 정상인 것을 확인했다면 마지막으로 버튼 클릭 이벤트 기능을 구현하겠습니다.
protected void btnAdd_Click(object sender, EventArgs e)
{
try
{
DataTable dt = fn.Fetch("select * from Class where ClassName = '"+txtClass.Text.Trim()+"' ");
if ( dt.Rows.Count == 0 )
{
string query = "Insert into Class values('" + txtClass.Text.Trim() + "')";
fn.Query(query);
IblMsg.Text = "정상적으로 추가됐습니다!";
IblMsg.CssClass = "alert alert-success";
txtClass.Text = string.Empty;
GetClass();
}
else
{
IblMsg.Text = "이미 동일한 데이터가 존재합니다.";
IblMsg.CssClass = "alert alert-success";
}
}
catch (Exception ex)
{
Response.Write("<script>alert('" + ex.Message + "')<script>");
}
}
위와 같이 DataTable을 이용하여 쿼리문을 작성했습니다. 그 후에 클래스 이름의 Rows.Count를 비교하여 0이어야 중복이 아니므로, 정상적으로 추가됐다는 문구가 출력되고 데이터가 db로 전송됩니다. 만약 그렇지 않다면 중복이라고 알려주고 db전송을 실행하지 않습니다.
정상적으로 진행이 됐다면, 추가 뿐만 아니라 관리를 해야하기 때문에, 동일한 페이지에서 grid에서 자체로 수정 혹은 삭제 기능을 추가해보도록 하겠습니다.
'C#' 카테고리의 다른 글
C# Asp .net Web Form 학급 관리 프로젝트 - 02. (0) | 2022.12.07 |
---|---|
C# IComarable 인터페이스 (0) | 2022.12.02 |
22.11.30 C# 일지_5 (0) | 2022.11.30 |
Asp .Net Web Forms - 04. CRUD 구현 (0) | 2022.11.29 |
22.11.29 C# 일지_4 (0) | 2022.11.29 |