1. 메인 폼 UI 구현
01. 구성
- 메뉴
- 홈(관리자, 직원, 고객)
- 회원관리(관리자)
- 도서관리(관리자, 직원)
- rental & return (관리자, 직원, 고객)
- 렌탈조회, 연체자조회, 렌탈등록, 반납확인, 기록조회
02. UI Control
-1. RibbonControl
- 리본 컨트롤을 사용할 예정입니다
- 일단 메뉴만 먼저 만들고 메인은 어떤 내용이냐에 따라 추가할 예정입니다!
- RibbonControl 추가 후에 Main Form Control 속성에 isMidContainer 속성을 true로 바꿔주세요!
- 그리고 실행을 해보면 탭메뉴 옆에 파일 메뉴가 기본적으로 있고 클릭해도 안되요!
- 하지만 나는 필요가 없다 삭제하고 싶다면 RibbonControl 속성에 showApplicationButton을 flase로 바꿔주세요!
- 그리고 저는 모든 메뉴 모든 버튼에 LargeWidth 속성에 60을 줄거에요!
-2. 홈 메뉴
- 조회, 비밀번호변경, 종료 버튼을 추가할 예정이구요!
- 버튼은 그룹에 있는 추가 버튼으로 추가해서 캡션이랑 이미지 설정해서 만들예정입니다
- Page
- Group
- 메인, 기타
- home_main_group, home_other_group
- Button
- 조회, 비밀번호 변경, 종료
- home_reset_btn, home_changePassword_btn, home_close_btn
-3. 회원 관리 메뉴
- Page
- Group
- Button
- 회원조회, 회원등록, 회원수정, 회원삭제, 회원검색
- user_reset_btn, user_add_btn, user_update_btn, user_delete_btn, user_search_btn
-4. 도서 관리 메뉴
- Page
- Group
- Button
- 도서조회, 도서등록, 도서수정, 도서삭제, 도서검색
- book_reset_btn, book_add_btn, book_update_btn, book_delete_btn, book_search_btn
-5. 렌탈 & 반납 메뉴
- Page
- Group
- Button
- 렌탈조회, 연제차조회, 렌탈등록, 반납확인, 기록조회
- rental_reset_btn, overdue_reset_btn, rental_add_btn, rental_return_btn, rental_log_btn
2. 간단 기능 해결
01. Enter 로그인
- 계속 로그인을 할려고 하는데 버튼 누르거나 버튼까지 tab을 눌러서 이동하기가 귀찮더라구요!
- 그래서 비밀번호 텍스트창에서 키업을 했을때 enter키면 로그인이 되도록 할거에요!
- 그럴 경우 로그인 버튼에 있던 코드를 메서드 추출해줘야 겠죠!
using BookManagementProgram.Repository;
using System;
using System.Linq;
using System.Windows.Forms;
namespace BookManagementProgram.LOGIN
{
public partial class Login : Form
{
BookUserRepository userRepository = new BookUserRepository();
public Login()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
if(Properties.Settings.Default.isLoginIdSave)
{
id_txt.Text = Properties.Settings.Default.LoginId;
idSaveCheck_chk.Checked = Properties.Settings.Default.isLoginIdSave;
}
}
// TextBox Focussing
private void label1_Click(object sender, EventArgs e)
{
id_txt.Focus();
}
private void label2_Click(object sender, EventArgs e)
{
pw_txt.Focus();
}
// 비밀번호 보이기 체크박스
private void showPassword_chk_CheckedChanged(object sender, EventArgs e)
{
if (showPassword_chk.Checked)
pw_txt.PasswordChar = default(char);
else
pw_txt.PasswordChar = '*';
}
// 아이디 저장 체크박스
private static void NotSaveID()
{
Properties.Settings.Default.isLoginIdSave = false;
Properties.Settings.Default.Save();
}
private void SaveID()
{
Properties.Settings.Default.LoginId = id_txt.Text;
Properties.Settings.Default.isLoginIdSave = true;
Properties.Settings.Default.Save();
}
// 아이디 찾기 라벨
private void findId_lbl_Click(object sender, EventArgs e)
{
(new FindById()).ShowDialog();
}
// 비밀번호 찾기 라벨
private void findPassword_lbl_Click(object sender, EventArgs e)
{
(new FindByPassword()).ShowDialog();
}
// 로그인 버튼
private void login_btn_Click(object sender, EventArgs e)
{
doLogin();
}
// 비밀번호 텍스트 창
private void pw_txt_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
doLogin();
}
}
private void doLogin()
{
if (string.IsNullOrEmpty(id_txt.Text))
{
MessageBox.Show("아이디를 입력해주세요.");
id_txt.Focus();
return;
}
if (string.IsNullOrEmpty(pw_txt.Text))
{
MessageBox.Show("비밀번호를 입력해주세요.");
pw_txt.Focus();
return;
}
string result = userRepository.Login(id_txt.Text, pw_txt.Text);
if (!result.All(char.IsDigit))
{
MessageBox.Show(result);
return;
}
if (!userRepository.InsertLoginLog())
{
MessageBox.Show("인터넷을 연결해주세요.");
return;
}
if (idSaveCheck_chk.Checked)
{
SaveID();
}
else
{
NotSaveID();
}
Main main = new Main();
main.Show();
main.BringToFront();
this.Hide();
}
}
}
02. 메인 폼 클로즈 버튼
- 간단하니까 빠르게 끊을 내도록 하죠!
- 나가는건 로그를 안찍을 꺼라 그냥 application 꺼주는걸로 끝낼께요!
using BookManagementProgram.LOGIN;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BookManagementProgram
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
}
private void Main_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
private void home_close_btn_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Application.Exit();
}
}
}
03. 메인 비밀번호 변경 버튼
- 이것도 이미 구현한 FindByPassword를 사용할거에요!
using BookManagementProgram.LOGIN;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BookManagementProgram
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
}
private void Main_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
private void home_close_btn_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Application.Exit();
}
private void home_changePassword_btn_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
(new FindByPassword()).ShowDialog();
}
}
}
3. 수정 및 메뉴 Visible 설정
01. authority_name과 grade_name을 no으로 변경
- 메뉴 visible 작업을 하면서 생각이 든게 이름으로 받으면 작성할게 많구나 싶어서 그냥 번호로받아서 필요할 때 이름으로 바꿀게요!
-1. DB / PROCUDURE / BOOK_LOGIN_S1
USE [BookManagement]
GO
/****** Object: StoredProcedure [dbo].[BOOK_LOGIN_S1] Script Date: 2021-08-19 오후 6:12:20 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <문민승>
-- Create date: <2021.08.19,>
-- Description: <도서관리 프로그램 로그인>
-- =============================================
ALTER PROCEDURE [dbo].[BOOK_LOGIN_S1]
@USER_ID NVARCHAR(20)
,@USER_PW CHAR(64)
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
IF NOT EXISTS
(
SELECT user_no
FROM Book_User_LogIn
WHERE user_id = @USER_ID
AND user_pw = @USER_PW
)
BEGIN
RAISERROR('존재하지 않는 사용자 입니다.',16,1)
RETURN
END
SELECT l.user_no, i.user_name, i.user_address, i.user_birth, l.user_id, l.user_pw, l.register_date, a.authority_no, g.grade_no
FROM Book_User_LogIn as l
INNER JOIN Book_User_Info as i
ON l.user_no = i.user_no
INNER JOIN Book_User_Auth as a
ON l.authority_no = a.authority_no
INNER JOIN Book_User_Grade as g
ON l.grade_no = g.grade_no
WHERE user_id = @USER_ID
AND user_pw = @USER_PW
END
-2. Model / User.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookManagementProgram.Model
{
class User
{
private string user_no;
private string user_name;
private string user_address;
private string user_birth;
private string user_id;
private string user_pw;
private string register_date;
private int authority_no;
private int grade_no;
public string userNo { get { return user_no; } set { user_no = value; } }
public string userName { get { return user_name; } set { user_name = value; } }
public string userAddress { get { return user_address; } set { user_address = value; } }
public string userBirth { get { return user_birth; } set { user_birth = value; } }
public string userId { get { return user_id; } set { user_id = value; } }
public string userPw { get { return user_pw; } set { user_pw = value; } }
public string registerDate { get { return register_date; } set { register_date = value; } }
public int authorityNo { get { return authority_no; } set { authority_no = value; } }
public int gradeNo { get { return grade_no; } set { grade_no = value; } }
public User(string user_no, string user_name, string user_address, string user_birth, string user_id, string user_pw, string register_date, int authority_no, int grade_no)
{
this.user_no = user_no;
this.user_name = user_name;
this.user_address = user_address;
this.user_birth = user_birth;
this.user_id = user_id;
this.user_pw = user_pw;
this.register_date = register_date;
this.authority_no = authority_no;
this.grade_no = grade_no;
}
public User()
{
}
}
}
-3. Repository / BookUserRepository.cs
using BookManagementProgram.Model;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
namespace BookManagementProgram.Repository
{
class BookUserRepository : IBookUserRepository
{
private SqlConnection conn = null;
private SqlCommand cmd = null;
public static List<User> userList = null;
public static User user = null;
public BookUserRepository()
{
conn = DBConn.DBConn.getConn();
cmd = new SqlCommand();
cmd.Connection = conn;
}
// 로그인 로직
public string Login(string userId, string userPw)
{
try
{
string sql = $"EXEC BOOK_LOGIN_S1 @USER_ID = '{userId}', @USER_PW = '{Encryption(userPw)}';";
cmd.CommandText = sql;
conn.Open();
// 데이터 출력
using (SqlDataReader SR = cmd.ExecuteReader())
{
while (SR.Read())
{
user = new User(
SR[0].ToString()
, SR[1].ToString()
, SR[2].ToString()
, SR[3].ToString()
, SR[4].ToString()
, SR[5].ToString()
, SR[6].ToString()
, (int)SR[7]
, (int)SR[8]
);
}
SR.Close();
}
return user.userNo;
}
catch(SqlException e)
{
return e.Message;
}
finally
{
conn.Close();
}
}
// SHA256 암호화 메서드
public static string Encryption(string data)
{
SHA256 sha = new SHA256Managed();
byte[] hash = sha.ComputeHash(Encoding.ASCII.GetBytes(data));
StringBuilder stringBuilder = new StringBuilder();
foreach (byte b in hash)
{
stringBuilder.AppendFormat("{0:x2}", b);
}
return stringBuilder.ToString();
}
// 아이디 찾기 로직
public string FindById(string userNo, string userName)
{
try
{
string sql = $"EXEC BOOK_LOGIN_S2 @USER_NO = {userNo}, @USER_NAME = '{userName}';";
string user_id = string.Empty;
cmd.CommandText = sql;
conn.Open();
// 데이터 출력
using (SqlDataReader SR = cmd.ExecuteReader())
{
while (SR.Read())
{
user_id = SR[0].ToString();
}
SR.Close();
}
return $"아이디 : {user_id} 입니다.";
}
catch (SqlException e)
{
return e.Message;
}
finally
{
conn.Close();
}
}
// 비밀번호 찾기 로직
public bool FindByPassword(string userNo, string userId, string newPassword)
{
try
{
string sql = $"EXEC BOOK_LOGIN_U1 @USER_NO = {userNo}, @USER_ID = '{userId}', @NEW_PW = '{Encryption(newPassword)}';";
cmd.CommandText = sql;
conn.Open();
return cmd.ExecuteNonQuery() > 0 ? true : false;
}
catch (SqlException e)
{
return false;
}
finally
{
conn.Close();
}
}
public bool InsertLoginLog()
{
try
{
string sql = $"EXEC BOOK_LOGIN_I1 @USER_NO = {user.userNo}, @USER_ID = '{user.userId}', @ENTER_IP = '{GetExternalIPAddress()}';";
cmd.CommandText = sql;
conn.Open();
return cmd.ExecuteNonQuery() > 0 ? true : false;
}
catch (SqlException e)
{
return false;
}
finally
{
conn.Close();
}
}
// 외부 IP
public static string GetExternalIPAddress()
{
string externalip = new WebClient().DownloadString("http://ipinfo.io/ip").Trim();
return externalip ?? GetInternalIPAddress();//null경우 내부 IP 반환;
}
// 내부
// DNS, 단순 도메인 이름 확인 기능이 제공
// GetGostEntity, 호스트 이름 또는 IP 주소를 IPHostEntry 인스턴스로 확인
// GetHostName, 로컬 컴퓨터의 호스트 이름을 가져온다
public static string GetInternalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("인터넷 연결 없음");
}
}
}
02. 메뉴 Visible
- 상단에 구성한 대로 권한에 따라 메뉴를 보이게 안보이게 할게요!
- 이런 경우에는 if...else 문보다 switch문이 간결하다는 사실!
using BookManagementProgram.LOGIN;
using BookManagementProgram.Model;
using BookManagementProgram.Repository;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BookManagementProgram
{
public partial class Main : Form
{
User user = BookUserRepository.user;
public Main()
{
InitializeComponent();
this.StartPosition = FormStartPosition.CenterScreen;
switch(user.authorityNo)
{
case 0:
user_page.Visible = true;
book_page.Visible = true;
rental_page.Visible = true;
break;
case 1:
book_page.Visible = true;
rental_page.Visible = true;
break;
case 2:
rental_page.Visible = true;
break;
}
}
private void Main_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
private void home_close_btn_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Application.Exit();
}
private void home_changePassword_btn_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
(new FindByPassword()).ShowDialog();
}
}
}