github : https://github.com/minseung-moon/student
1. 원폼 학생관리
- 첫 직장으로 C# 개발 회사에 취업을 하였습니다. 하지만 왠걸 저는 C#이 처음이다 보니 부랴부랴 공부를 시작했고 C#을 어느정도 아는지 궁금하셔서 학생관리 폼을 만들어 보라고 하셔서 만들게 되었습니다
01. DB
-1. DB 설계
- DB : MS SQL(SQL Server)
- DB : STUDENT
- TABLE : STUDENT
- idx는 인덱스 번호로 PK와 IDENTITY를 주었다
- name은 한글입력 및 공백을 안 받기 위해서 nvarchar로 지정
-2. PROCEDURE
USE [student]
GO
/****** Object: StoredProcedure [dbo].[sp_Student_s1] Script Date: 2021-08-03 오후 12:10:59 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <문민승>
-- Create date: <2021.07. 19>
-- Description: <학생 조회>
-- =============================================
ALTER PROCEDURE [dbo].[Student_s1]
AS
Begin
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT *,
(kor + eng + math) AS total,
CONVERT(NUMERIC(12,2) ,(kor + eng + math)/3.0) AS average,
(SELECT COUNT(*) + 1 FROM student s2 WHERE (s2.kor + s2.eng + s2.math) > (s1.kor + s1.eng + s1.math)) AS Rank,
CASE
WHEN (kor < 40 OR eng < 40 OR math < 40 OR (kor + eng + math)/3 < 70) THEN '불합격'
ELSE '합격'
END AS "결과"
FROM student s1 ORDER BY idx ASC;
End
USE [student]
GO
/****** Object: StoredProcedure [dbo].[sp_Student_i1] Script Date: 2021-08-03 오후 1:02:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <문민승>
-- Create date: <2021.07. 19>
-- Description: <학생 입력>
-- =============================================
ALTER PROCEDURE [dbo].[Student_i1]
@Name NVARCHAR(10),
@Kor INT,
@Eng INT,
@Math INT
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
INSERT INTO student(name, kor, eng, math) Values(@Name, @Kor, @Eng, @Math);
USE [student]
GO
/****** Object: StoredProcedure [dbo].[Student_u1] Script Date: 2021-08-03 오후 1:03:24 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <문민승>
-- Create date: <2021.07. 19>
-- Description: <학생 수정>
-- =============================================
ALTER PROCEDURE [dbo].[sp_Student_u1]
@Name char(10),
@Kor int,
@Eng int,
@Math int,
@Idx int
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
UPDATE student SET name = @Name, kor = @kor, eng = @eng, math = @math WHERE idx = @Idx;
USE [student]
GO
/****** Object: StoredProcedure [dbo].[Student_d1] Script Date: 2021-08-03 오후 1:04:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <문민승>
-- Create date: <2021.07. 19>
-- Description: <학생 삭제>
-- =============================================
ALTER PROCEDURE [dbo].[sp_Student_d1]
@Idx int
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DELETE FROM student WHERE idx = @Idx;
02. 코드
using static student.Conn.DBConn;
using student.Model;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using student.Repository;
using System.Text.RegularExpressions;
namespace student
{
public partial class Form1 : Form
{
private IStudentRepository repository = null;
private StudentRepositoryExcel excel = null;
private List<StudentModel> studentList;
private int index = -1;
private string name = null;
private string kor = null;
private string eng = null;
private string math = null;
// 접속
public Form1()
{
InitializeComponent();
repository = new StudentRepository();
excel = new StudentRepositoryExcel();
}
// form 로드 이벤트
private void Form1_Load(object sender, EventArgs e)
{
ShowList();
}
// 추가 이벤트
private void button1_Click(object sender, EventArgs e)
{
// text box에 데이터 확인
if(TextBoxValidate())
{
MessageBox.Show("데이터를 입력해주세요");
return;
}
repository.Add(new StudentModel { _Idx = index, _Name = name, _Kor = Convert.ToInt32(kor), _Eng = Convert.ToInt32(eng), _Math = Convert.ToInt32(math) });
// 초기화
InitTextBox();
ShowList();
}
// 삭제 이벤트
private void Delete_Click(object sender, EventArgs e)
{
// 그리드 뷰 컬럼이 선택이 되었는지 확인
if (index > -1)
{
repository.DeleteByIdx(index);
InitTextBox();
ShowList();
}else MessageBox.Show("삭제할 데이터를 입력해주세요.");
}
// 업데이트 이벤트
private void Update_Click(object sender, EventArgs e)
{
// 그리드 뷰 컬럼이 선택이 되었는지 확인
if (index > -1)
{
// text box 데이터 확인
if (TextBoxValidate())
{
MessageBox.Show("데이터를 정확하게 입력해주세요");
return;
}
repository.UpdateByIdx(new StudentModel { _Idx = index, _Name = name, _Kor = Convert.ToInt32(kor), _Eng = Convert.ToInt32(eng), _Math = Convert.ToInt32(math) });
InitTextBox();
ShowList();
}
else MessageBox.Show("수정할 데이터를 정확하게 입력해주세요.");
}
// 그리드 뷰 클릭 이벤트
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) => SelectRows();
// 선택된 컬럼의 데이터 할당
private void SelectRows()
{
try
{
DataGridViewRow dr = dataGridView1.SelectedRows[0];
index = (int)dr.Cells[0].Value;
tbname.Text = dr.Cells[1].Value.ToString();
tbkor.Text = dr.Cells[2].Value.ToString();
tbeng.Text = dr.Cells[3].Value.ToString();
tbmath.Text = dr.Cells[4].Value.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
// 텍스트 박스 초기화
private void InitTextBox()
{
tbname.Text = "";
tbkor.Text = "";
tbeng.Text = "";
tbmath.Text = "";
index = -1;
}
// 텍스트박스에 값이 있는지 체크
private bool TextBoxValidate()
{
name = tbname.Text;
kor = tbkor.Text;
eng = tbeng.Text;
math = tbmath.Text;
if(!Regex.IsMatch(name, @"^[가-힣]{2,10}$"))
{
tbname.Focus();
return true;
}
if (!Regex.IsMatch(kor, @"^[0-9]{1,3}$") || (Convert.ToInt32(kor) < 0 || Convert.ToInt32(kor) > 100))
{
tbkor.Focus();
return true;
}
if(!Regex.IsMatch(eng, @"^[0-9]{1,3}$") || (Convert.ToInt32(eng) < 0 || Convert.ToInt32(eng) > 100))
{
tbeng.Focus();
return true;
}
if (!Regex.IsMatch(math, @"^[0-9]{1,3}$") || (Convert.ToInt32(math) < 0 || Convert.ToInt32(math) > 100))
{
tbmath.Focus();
return true;
}
return false;
}
// 그리드 뷰에 데이터 출력
private void ShowList()
{
// List 초기화
studentList = new List<StudentModel>();
studentList = repository.GetAll();
dataGridView1.DataSource = studentList;
dataGridView1.Columns[0].HeaderText = "번호";
dataGridView1.Columns[1].HeaderText = "성명";
dataGridView1.Columns[2].HeaderText = "국어";
dataGridView1.Columns[3].HeaderText = "영어";
dataGridView1.Columns[4].HeaderText = "수학";
dataGridView1.Columns[5].HeaderText = "총합";
dataGridView1.Columns[6].HeaderText = "평균";
dataGridView1.Columns[7].HeaderText = "등수";
dataGridView1.Columns[8].HeaderText = "결과";
}
// 엑셀 저장 이벤트
private void button1_Click_1(object sender, EventArgs e)
{
excel.saveExcelFile();
}
// 프로그램 종료 이벤트
private void End_Click(object sender, EventArgs e)
{
if(MessageBox.Show("종료하시겠습니까?", "종료", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
Application.Exit();
}
}
}
}
-2. Conn
- DB 연결
using System.Data.SqlClient;
namespace student.Conn
{
class DBConn
{
private static string uid = null; // DB 접속 아이디
private static string password = null; // DB 접속 비밀번호
private static string database = null; // 사용할 DB 이름
private static string server = null; // 서버 주소 -- sql 서버 접속시 사용한 주소
private static SqlConnection conn = null;
public static SqlConnection getConn()
{
uid = "";
password = "";
database = "";
server = "";
string connStr = $"SERVER = {server}; DATABASE = {database}; UID = {uid}; PASSWORD = {password};";
conn = new SqlConnection(connStr);
return conn;
}
}
}
-3. Repository
- Controller 역할
- IStudentRepository.cs
using student.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace student.Repository
{
interface IStudentRepository
{
void Add(StudentModel model); // 입력
List<StudentModel> GetAll(); // 전체 출력
void DeleteByIdx(int index); // 특정 학생 삭제
void UpdateByIdx(StudentModel model); // 특정 학생 업데이트
}
}
using student.Model;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static student.Conn.DBConn;
namespace student.Repository
{
class StudentRepository : IStudentRepository
{
private static List<StudentModel> studentList = null;
private SqlConnection conn = null;
private SqlCommand cmd = null;
public StudentRepository()
{
// DB 연동
conn = getConn();
cmd = new SqlCommand();
cmd.Connection = conn;
}
public void Add(StudentModel model)
{
string sql = $"EXEC sp_Student_i1 '{model._Name}', {model._Kor}, {model._Eng}, {model._Math}";
cmd.CommandText = sql;
conn.Open();
cmd.ExecuteNonQuery();
// Db 닫기
conn.Close();
}
public void DeleteByIdx(int index)
{
string sql = $"EXEC sp_Student_d1 {index}";
cmd.CommandText = sql;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
// 데이터 조회
public List<StudentModel> GetAll()
{
studentList = new List<StudentModel>();
string sql = "EXEC sp_Student_s1";
cmd.CommandText = sql;
conn.Open();
// 데이터 출력
using (SqlDataReader SR = cmd.ExecuteReader())
{
while (SR.Read())
{
StudentModel model = new StudentModel { _Idx = (int)SR[0], _Name = (string)SR[1], _Kor = (int)SR[2], _Eng = (int)SR[3], _Math = (int)SR[4], _Total = (int)SR[5], _Average = Convert.ToDouble(SR[6]), _Rank = (int)SR[7], _Result = (string)SR[8] };
// model._Result = (model._Average < 70.0 || model._Kor < 40 || model._Eng < 40 || model._Math < 40) ? "불합격" : "합격";
studentList.Add(model);
}
SR.Close();
}
conn.Close();
return studentList;
}
public void UpdateByIdx(StudentModel model)
{
string sql = $"EXEC sp_Student_u1 '{model._Name}', {model._Kor}, {model._Eng}, {model._Math}, {model._Idx}";
cmd.CommandText = sql;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
}
}
- StudentRepositoryExcel.cs
using student.Model;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Excel = Microsoft.Office.Interop.Excel;
namespace student.Repository
{
class StudentRepositoryExcel : IStudentRepository
{
private Excel.Application excelApp = null;
private Excel.Workbook workBook = null;
private Excel.Worksheet workSheet = null;
private string desktopPath = null;
private string path = null;
private IStudentRepository repository = null;
private List<StudentModel> studentList;
public StudentRepositoryExcel()
{
desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); // 바탕화면
path = Path.Combine(desktopPath, "Excel.xlsx"); // 엑셀파일
repository = new StudentRepository();
}
public void saveExcelFile()
{
try {
excelApp = new Excel.Application(); // 엑셀 어플리케이션 생성
workBook = excelApp.Workbooks.Add(); // 워크북 추가
workSheet = workBook.Worksheets.get_Item(1) as Excel.Worksheet; // 엑셀 첫번째 워크시트 가져오기
workSheet.Cells[1, 1] = "학생 번호";
workSheet.Cells[1, 2] = "학생 성명";
workSheet.Cells[1, 3] = "국어점수";
workSheet.Cells[1, 4] = "영어점수";
workSheet.Cells[1, 5] = "수학점수";
workSheet.Cells[1, 6] = "총합점수";
workSheet.Cells[1, 7] = "평균점수";
workSheet.Cells[1, 8] = "등수";
// 엑셀에 저장할 데이터
studentList = repository.GetAll();
for(int i = 0; i < studentList.Count(); i++)
{
workSheet.Cells[2 + i, 1] = studentList[i]._Idx;
workSheet.Cells[2 + i, 2] = studentList[i]._Name;
workSheet.Cells[2 + i, 3] = studentList[i]._Kor;
workSheet.Cells[2 + i, 4] = studentList[i]._Eng;
workSheet.Cells[2 + i, 5] = studentList[i]._Math;
workSheet.Cells[2 + i, 6] = studentList[i]._Total;
workSheet.Cells[2 + i, 7] = studentList[i]._Average;
workSheet.Cells[2 + i, 8] = studentList[i]._Rank;
}
workSheet.Columns.AutoFit(); // 열 너비 자동 맞춤
workBook.SaveAs(path, Excel.XlFileFormat.xlWorkbookDefault); // 엑셀 파일 저장
excelApp.Quit();
}catch(Exception e)
{
}
finally
{
ReleaseObject(workSheet);
ReleaseObject(workBook);
ReleaseObject(excelApp);
}
}
private void ReleaseObject(object obj)
{
try {
if(obj != null)
{
Marshal.ReleaseComObject(obj); // 액셀 객체 해제
obj = null;
}
} catch(Exception ex)
{
obj = null; throw ex;
} finally
{
GC.Collect(); // 가비지 수집
}
}
public void Add(StudentModel model)
{
throw new NotImplementedException();
}
public void DeleteByIdx(int index)
{
throw new NotImplementedException();
}
public List<StudentModel> GetAll()
{
throw new NotImplementedException();
}
public void UpdateByIdx(StudentModel model)
{
throw new NotImplementedException();
}
}
}
-4. Model
- data model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace student.Model
{
class StudentModel
{
private int idx; // 학생 번호
private string name; // 학생 성명
private int kor; // 국어 점수
private int eng; // 영어 점수
private int math; // 수학 점수
private int total; // 합계
private double average; // 평균
private int rank; // 순위
private string result; // 결과(합격, 불합격)
public int _Idx { get { return idx; } set { idx = value; } }
public string _Name { get { return name; } set { name = value; } }
public int _Kor { get { return kor; } set { kor = value; } }
public int _Eng { get { return eng; } set { eng = value; } }
public int _Math { get { return math; } set { math = value; } }
public int _Total { get { return total; } set { total = value; } }
public double _Average { get { return average; } set { average = value; } }
public int _Rank { get { return rank; } set { rank = value; } }
public string _Result { get { return result; } set { result = value; } }
}
}
03. 느낀 점
- 처음으로 윈폼을 만지는 부분이 있어 많이 헤맸지만 자바보다는 수월했던것 같다
- 이번에 처음으로 프로시저를 만들어서 사용하다 보니 어색한 점은 많지만 확실히 익혀놓으면 많은 도움이 될것 같다
- 지금은 간단한 query지만 나중에 복잡해지면 프로시져는 필수일것 같은 느낌이 든다