쟝이의 세상
**Project** Part 1. 회원가입 페이지 및 송금페이지 본문
"프로젝트 초기 환경"
운영체제: MAC OS
데이터베이스: MySQL
사용 언어: HTML, CSS, Javascript
"조원 웹 페이지 역할 분담"
동욱 - 대시보드, 로그인
유진 - 메인페이지, 거래내역
정미 - 계정 관리, 대출 신청
지향 - 송금페이지, 회원가입
=> 우선 각자 로컬호스트 환경에서 구축해보기로 했음!
웹페이지 구성 - 9월 27일 금요일까지
데이터베이스 생성
create database bank;
use bank;
CREATE TABLE users ( /* 회원 테이블 */
user_num INT AUTO_INCREMENT PRIMARY KEY,
userid VARCHAR(20) NOT NULL UNIQUE,
username VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
phone_number VARCHAR(20) NOT NULL UNIQUE,
date_of_birth CHAR(6) NOT NULL,
account_created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL
);
CREATE TABLE transaction_types ( /* 거래 유형 참조 테이블 */
id INT AUTO_INCREMENT PRIMARY KEY,
type VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE accounts ( /* 계좌 테이블 */
account_id INT AUTO_INCREMENT PRIMARY KEY,
user_num INT NOT NULL,
account_number VARCHAR(20) NOT NULL UNIQUE,
balance BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_num) REFERENCES users(user_num)
);
CREATE TABLE transactions ( /* 거래내역 테이블 */
transaction_id INT AUTO_INCREMENT PRIMARY KEY,
account_id INT NOT NULL,
transaction_type_id INT NOT NULL,
amount BIGINT NOT NULL,
amount_after BIGINT NOT NULL,
receiver_account VARCHAR(20),
transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(account_id),
FOREIGN KEY (transaction_type_id) REFERENCES transaction_types(id)
);
CREATE TABLE loan_types ( /* 대출 유형 참조 테이블 */
id INT AUTO_INCREMENT PRIMARY KEY,
type VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE loan_statuses ( /* 대출 상태 참조 테이블 */
id INT AUTO_INCREMENT PRIMARY KEY,
status VARCHAR(50) NOT NULL UNIQUE
);
CREATE TABLE loans ( /* 대출 테이블 */
loan_id INT AUTO_INCREMENT PRIMARY KEY,
user_num INT NOT NULL,
loan_type_id INT NOT NULL,
loan_amount BIGINT NOT NULL,
interest_rate DECIMAL(5, 2) NOT NULL,
loan_start_date DATE NOT NULL,
loan_end_date DATE NOT NULL,
loan_status_id INT NOT NULL,
FOREIGN KEY (user_num) REFERENCES users(user_num),
FOREIGN KEY (loan_type_id) REFERENCES loan_types(id),
FOREIGN KEY (loan_status_id) REFERENCES loan_statuses(id)
);
CREATE TABLE transfer_statuses ( /* 송금 상태 참조 테이블 */
id INT AUTO_INCREMENT PRIMARY KEY,
status VARCHAR(50) NOT NULL UNIQUE
-- 상태 이름: 'Pending', 'Completed', 'Failed'
-- name에서 변경
);
CREATE TABLE transfers ( /* 송금 테이블 */
transfer_id INT AUTO_INCREMENT PRIMARY KEY,
sender_account_id INT NOT NULL,
receiver_account VARCHAR(20) NOT NULL,
amount BIGINT NOT NULL,
transfer_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status_id INT NOT NULL,
FOREIGN KEY (sender_account_id) REFERENCES accounts(account_id),
FOREIGN KEY (status_id) REFERENCES transfer_statuses(id)
);
CREATE TABLE login_history ( /* 로그인 기록 테이블 */
login_id INT AUTO_INCREMENT PRIMARY KEY,
user_num INT NOT NULL,
login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_address VARCHAR(45),
device_info VARCHAR(255),
FOREIGN KEY (user_num) REFERENCES users(user_num)
);
생성된 데이터베이스 다이어그램
회원가입, 송금 페이지 파일 연결도
DB연결
<?php
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
$current_file = basename($_SERVER['PHP_SELF']);
//a페이지나 b페이지가 아닐 경우 로그인 페이지로 다시 이동
/*if (($current_file != '#' && $current_file != '#') && !isset($_SESSION['userid'])) {
echo "<script>alert('로그인이 필요합니다');</script>";
echo "<script>location.href='#';</script>"; //로그인 후 이동할 페이지
exit;
}*/
?>
<?php
$serverName = "localhost";
$database = "bank";
$uid = "root";
$pwd = "wlgid0420@";
try {
$conn = new PDO("mysql:host=$serverName;dbname=$database", $uid, $pwd);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Error connecting to mysql");
}
?>
회원가입
<?php
include 'dbconn.php';
//회원가입 처리
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$userid = $_POST['userid'];
$username = $_POST['username'];
$password = $_POST['password'];
$email = $_POST['email'];
$phone_number = $_POST['phone1'] . '-' . $_POST['phone2'] . '-' . $_POST['phone3'];
$birth = $_POST['birth'];
try {
$sql = "INSERT INTO users(userid, username, password, email, phone_number, date_of_birth) VALUES('$userid', '$username', '$password', '$email', '$phone_number', '$birth')";
//쿼리 실행
$conn->exec($sql);
echo "회원가입이 완료되었습니다."; //성공 메시지 출력
header('Location: login.php'); //로그인 페이지 이동
} catch (PDOException $e) {
//쿼리 실행 실패 시 오류 메시지 출력
die("회원가입 중 오류가 발생했습니다: " . $e->getMessage());
}
}
?>
<html>
<head>
<script src="javascript/register.js"></script>
<link rel="stylesheet" href="css/register.css">
</head>
<body>
<!--회원가입 폼-->
<form id="signForm" method="POST">
<div> <!--아이디 입력-->
<label>사용자 ID:</label>
<input type="text" name="userid" maxlength="20" id="userid" placeholder="아이디를 입력해주세요." required>
<button type="button" onclick="checkUserID()">중복체크</button>
<div id="idError" class="error"></div> <!--입력 값 확인 메시지 출력-->
<span id="useridFeedback"></span> <!--중복체크 메시지 출력-->
</div>
<div> <!--이름 입력-->
<label>사용자 이름:</label>
<input type="text" name="username" maxlength="10" id="username" placeholder="이름을 입력해주세요." required>
<div id="nameError" class="error"></div> <!--입력 값 확인 메시지 출력-->
</div>
<div> <!--비밀번호 입력-->
<label>비밀번호:</label>
<input type="password" name="password" maxlength="20" id="password" placeholder="영문, 숫자, 특수문자 포함 8자리 이상 입력"
required>
<div id="passError" class="error"></div> <!--입력 값 확인 메시지 출력-->
</div>
<div> <!--이메일 입력-->
<label>이메일:</label>
<input type="text" name="email" maxlength="20" id="email" placeholder="이메일을 입력해주세요." required>
<div id="emailError" class="error"></div> <!--입력 값 확인 메시지 출력-->
</div>
<div> <!--핸드폰 번호 입력-->
<label>핸드폰 번호:</label>
<div id="phoneNum">
<input type="text" name="phone1" size="3" id="phone1" maxlength="3" oninput="nextPhone1()" required> -
<input type="text" name="phone2" size="6" id="phone2" maxlength="4" oninput="nextPhone2()" required> -
<input type="text" name="phone3" size="6" id="phone3" maxlength="4" required>
</div>
<div id="phoneError" class="error"></div> <!--입력 값 확인 메시지 출력-->
</div>
<div> <!--생년월일 입력-->
<label>생년월일:</label>
<input type="text" name="birth" id="birth" maxlength="6" placeholder="주민번호 앞 6자리 입력" required>
<div id="birthError" class="error"></div> <!--입력 값 확인 메시지 출력-->
</div>
<button type="button" id="signUp" onclick="signupCheck()">회원가입</button>
</form>
</body>
</html>
Javascript
⬇️ 아래 코드 보기
더보기
function nextPhone1() {
const phone1 = document.getElementById("phone1").value;
if (phone1.length === 3) {
document.getElementById("phone2").focus();
}
}
function nextPhone2() {
const phone2 = document.getElementById("phone2").value;
if (phone2.length === 4) {
document.getElementById("phone3").focus();
}
}
let isIdChecked = false;
//ID중복체크
function checkUserID() {
const userid = document.getElementById("userid").value;
const feedback = document.getElementById("useridFeedback");
if (userid.trim() === "") {
feedback.textContent = "아이디를 입력해주세요.";
return;
}
fetch("../api/check_userid.php", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ userid: userid }),
})
.then((response) => response.json())
.then((data) => {
if (data.exists) {
feedback.textContent = "이미 사용 중인 아이디입니다.";
feedback.style.color = "red";
isIdChecked = false;
} else {
feedback.textContent = "사용 가능한 아이디입니다.";
feedback.style.color = "green";
isIdChecked = true;
}
})
.catch((error) => {
console.error("Error:", error);
});
}
//회원가입 버튼 클릭 시 입력값 검증
function signupCheck() {
let id = document.getElementById("userid").value;
let name = document.getElementById("username").value;
let password = document.getElementById("password").value;
let email = document.getElementById("email").value;
let birth = document.getElementById("birth").value;
// let phone1 = document.getElementById("phone1").value;
let check = true;
//아이디확인
if (id.trim() === "") {
document.getElementById("idError").innerHTML = "아이디를 입력해주세요.";
check = false;
} else if (id.includes(" ")) {
document.getElementById("idError").innerHTML =
"아이디를 다시 입력해주세요.";
check = false;
} else {
document.getElementById("idError").innerHTML = "";
}
if (!isIdChecked) {
document.getElementById("idError").innerHTML =
"아이디 중복체크를 해주세요.";
check = false;
} else {
document.getElementById("idError").innerHTML = "";
}
//이름확인
if (name.includes(" ")) {
document.getElementById("nameError").innerHTML =
"공백이 포함되어 있습니다. 공백을 제거해주세요.";
check = false;
} else if (name === "") {
document.getElementById("nameError").innerHTML =
"이름이 올바르지 않습니다. 다시 입력해주세요.";
check = false;
} else {
document.getElementById("nameError").innerHTML = "";
}
//비밀번호확인
let hasLetter = /[a-zA-Z]/.test(password); //영문 확인(T/F 반환)
let hasNumber = /[0-9]/.test(password); //숫자 확인(T/F 반환)
let hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(password); //특수문자 확인(T/F 반환)
if (!hasLetter) {
document.getElementById("passError").innerHTML =
"비밀번호에 영문을 포함해주세요.";
check = false;
} else if (!hasNumber) {
document.getElementById("passError").innerHTML =
"비밀번호에 숫자를 포함해주세요.";
check = false;
} else if (!hasSpecial) {
document.getElementById("passError").innerHTML =
"비밀번호에 특수문자를 포함해주세요.";
check = false;
} else if (password.length < 8) {
document.getElementById("passError").innerHTML =
"비밀번호를 8자리 이상 입력해주세요.";
check = false;
} else {
document.getElementById("passError").innerHTML = "";
}
//이메일확인
if (email.includes("@")) {
let emailID = email.split("@")[0];
let emailServer = email.split("@")[1];
if (emailID === "" || emailServer === "") {
document.getElementById("emailError").innerHTML =
"이메일을 알맞은 형식으로 입력해주세요.";
check = false;
} else if (!emailServer.includes(".")) {
document.getElementById("emailError").innerHTML =
"이메일을 알맞은 형식으로 입력해주세요.";
check = false;
} else {
document.getElementById("emailError").innerHTML = "";
}
} else {
document.getElementById("emailError").innerHTML =
"이메일을 다시 입력해주세요.";
check = false;
}
//생년월일확인
let birthNumber = /[0-9]/.test(birth);
if (!birthNumber) {
document.getElementById("birthError").innerHTML =
"주민번호 앞 6자리를 숫자로 입력해주세요.";
check = false;
} else {
document.getElementById("birthError").innerHTML = "";
}
//모든 값 true일 경우
if (check) {
document.getElementById("idError").innerHTML = "";
document.getElementById("nameError").innerHTML = "";
document.getElementById("passError").innerHTML = "";
document.getElementById("emailError").innerHTML = "";
document.getElementById("birthError").innerHTML = "";
document.getElementById("signForm").submit();
setTimeout(function () {
alert("가입이 완료되었습니다.");
}, 0);
}
}
php(api 역할)
⬇️ 아래 코드 보기
더보기
<?php
include "../dbconn.php";
// Get the username from the request
$data = json_decode(file_get_contents('php://input'), true);
$userid = $data['userid'];
// Check if the username exists
$sql = "SELECT * FROM users WHERE userid='$userid'";
$stmt = $conn->prepare($sql);
$stmt->execute();
$response = array();
$response['exists'] = ($stmt->rowCount() > 0);
echo json_encode($response);
$conn = null;
exit();
?>
송금 페이지
<?php
session_start();
if (!isset($_SESSION["userid"]) || !isset($_SESSION["username"])) {
echo "로그인이 필요합니다.";
}
?>
<!DOCTYPE html>
<html>
<head>
<script src="javascript/transfer.js"></script>
</head>
<body>
<form method="POST">
<div>
<?php echo $_SESSION["username"]; ?>님
<a href="#"> <!--로그아웃 페이지-->
<button>로그아웃</button>
</a>
</div>
<div> <!--출금계좌선택-->
<label>출금계좌번호</label>
<select id="out_account" name="out_account">
<option value="">선택하세요</option>
<?php
include "dbconn.php";
$select_user_num = $_SESSION['user_num'];
if ($select_user_num) {
$query = "SELECT * FROM accounts WHERE user_num = $select_user_num";
$stmt = $conn->prepare($query);
$stmt->execute();
if ($stmt->rowCount() > 0) {
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
?>
<option value="<?= $row['account_number'] ?>"><?= $row['account_number'] ?></option>
<?php
}
} else {
echo "<option value=''>계좌가 없습니다.</option>";
}
} else {
echo "<option value=''>user_num이 전달되지 않았습니다.</option>";
} ?>
</select>
<div>
<button type="button" onclick="myAccount()">출금가능금액 조회</button>
<div id="balance">잔액: 0원</div>
</div>
</div>
<!-- <div>
<label>입금기관</label>
<input type="text" id="select_bank" name="select_bank" readonly>
<button type="button" onclick="popupBank()">기관선택</button>
</div> -->
<div> <!--입금계좌번호 입력-->
<label>입금계좌번호</label>
<input type="text" id="in_account" name="in_account" required>
</div>
<div> <!--이체금액 입력-->
<label>이체금액</label>
<input type="number" id="transfer_amount" name="transfer_amount" required>
</div>
<button type="submit" onclick="transferSubmit()">이체하기</button>
</form>
</body>
</html>
Javascript
⬇️ 아래 코드 보기
더보기
function setValueFromPopup(value) {
document.getElementById("select_bank").value = value;
}
// function popupBank() {
// window.open("popup.html", "popupWindow", "width=300, height=200");
// }
let currentBalance;
//잔액 조회 함수
function myAccount() {
const user_num = document.getElementById("out_account").value;
fetch(`../api/check_account.php?user_num=${user_num}`, {
method: "GET",
})
.then((response) => response.json())
// .then((response) => {
// console.log("res", response);
// })
.then((data) => {
if (data.balance !== undefined) {
currentBalance = parseFloat(data.balance);
document.getElementById(
"balance"
).innerHTML = `잔액: ${currentBalance}원`;
console.log("잔액: " + currentBalance);
console.log(data.balance);
} else {
document.getElementById("balance").innerHTML =
"잔액 정보를 가져올 수 없습니다.";
console.log("잔액 정보 오류");
}
})
.catch((error) => {
console.error("Error: ", error);
document.getElementById("balance").innerHTML =
"잔액 정보를 가져오는 중 오류가 발생하였습니다.";
});
}
//이체 실행 함수
function transferSubmit() {
const account_number_out = document.getElementById("out_account").value; //출금 계좌
const account_number_in = document.getElementById("in_account").value; //입금 계좌
const transferAmount = parseFloat(
document.getElementById("transfer_amount").value
);
console.log("출금 계좌: ", account_number_out);
console.log("입금 계좌: ", account_number_in);
console.log("이체 금액: ", transferAmount);
//서버로 이체 요청
fetch(`../api/transfer_account.php`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
account_number_out: account_number_out,
account_number_in: account_number_in,
transfer_amount: transferAmount,
}),
})
.then((response) => response.json())
// .then((response) => {
// console.log("res", response);
// })
.then((data) => {
console.log("서버응답:", data);
if (data.success) {
alert("이체가 완료되었습니다.");
myAccount(account_number_out);
} else {
alert("이체에 실패했습니다." + data.message);
}
})
.catch((error) => {
console.log("오류: ", error);
});
return false;
}
php(api 역할1) - 입출금
⬇️ 아래 코드 보기
더보기
<?php
include "../dbconn.php";
$data = json_decode(file_get_contents("php://input"), true);
$account_number_out = $data['account_number_out'] ?? null; //출금 계좌
$account_number_in = $data['account_number_in'] ?? null; //입금 계좌
$transfer_amount = $data['transfer_amount'] ?? null; //이체 금액
if ($_SERVER["REQUEST_METHOD"] === "POST") {
if ($account_number_out === null || $account_number_in === null) {
echo json_encode(['success' => false, 'message' => '필수 데이터가 누락되었습니다.']);
exit;
}
try {
//트랜잭션 시작
$conn->beginTransaction();
//1) 출금
$sql = "SELECT * FROM accounts WHERE account_number = :account_number_out";
$stmt = $conn->prepare($sql);
$stmt->bindParam(":account_number_out", $account_number_out);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
$current_balance = $result['balance'];
$sender_account_id = $result['account_id'];
if ($current_balance >= $transfer_amount) {
//출금 계좌 잔액에서 이체 금액 차감
$new_balance = $current_balance - $transfer_amount;
//출금 계좌 업데이트
$update_sql = "UPDATE accounts SET balance = :new_balance WHERE account_number = :account_number_out";
$stmt = $conn->prepare($update_sql);
$stmt->bindParam(":new_balance", $new_balance);
$stmt->bindParam(":account_number_out", $account_number_out);
$stmt->execute();
//2) 입금
$sql_in = "SELECT * FROM accounts WHERE account_number = :account_number_in";
$stmt_in = $conn->prepare($sql_in);
$stmt_in->bindParam(":account_number_in", $account_number_in);
$stmt_in->execute();
$result_in = $stmt_in->fetch(PDO::FETCH_ASSOC);
if ($result_in) {
//입금 계좌 잔액에서 이체 금액 더함
$new_balance_in = $result_in['balance'] + $transfer_amount;
$receiver_account_id = $result_in['account_id'];
//입금 계좌 업데이트
$update_sql_in = "UPDATE accounts SET balance = :new_balance_in WHERE account_number = :account_number_in";
$stmt = $conn->prepare($update_sql_in);
$stmt->bindParam(":new_balance_in", $new_balance_in);
$stmt->bindParam(":account_number_in", $account_number_in);
$stmt->execute();
//(출금)송금 테이블에 데이터 삽입
$transfer_sql = "INSERT INTO transfers(sender_account_id, receiver_account, amount, transfer_date, status_id)
VALUES(:sender_account_id, :receiver_account, :transfer_amount, NOW(), 2)";
$stmt_transfer = $conn->prepare($transfer_sql);
$stmt_transfer->bindParam(":sender_account_id", $sender_account_id);
$stmt_transfer->bindParam(":receiver_account", $account_number_in);
$stmt_transfer->bindParam(":transfer_amount", $transfer_amount);
$stmt_transfer->execute();
//(출금)거래내역 테이블에 데이터 삽입
$history_out = "INSERT INTO transactions(account_id, transaction_type_id, amount, amount_after, receiver_account, transaction_date)
VALUES(:sender_account_id, 2, :transfer_amount, :new_balance, :account_number_in, NOW())";
$stmt_history_out = $conn->prepare($history_out);
$stmt_history_out->bindParam(":sender_account_id", $sender_account_id);
$stmt_history_out->bindParam(":transfer_amount", $transfer_amount);
$stmt_history_out->bindParam(":new_balance", $new_balance);
$stmt_history_out->bindParam(":account_number_in", $account_number_in);
$stmt_history_out->execute();
//(입금)거래내역 테이블에 데이터 삽입
$history_in = "INSERT INTO transactions(account_id, transaction_type_id, amount, amount_after, receiver_account, transaction_date)
VALUES(:receiver_account_id, 1, :transfer_amount, :new_balance, :account_number_in, NOW())";
$stmt_history_in = $conn->prepare($history_in);
$stmt_history_in->bindParam(":receiver_account_id", $receiver_account_id);
$stmt_history_in->bindParam(":transfer_amount", $transfer_amount);
$stmt_history_in->bindParam(":new_balance", $new_balance_in);
$stmt_history_in->bindParam(":account_number_in", $account_number_in);
$stmt_history_in->execute();
} else {
throw new Exception("입금 계좌를 찾을 수 없습니다.");
}
} else {
throw new Exception("잔액이 부족합니다.");
}
} else {
error_log("출금 계좌를 찾을 수 없습니다. : " . $account_number_out);
throw new Exception("출금 계좌를 찾을 수 없습니다.");
}
$conn->commit();
echo json_encode(['success' => true]);
} catch (Exception $e) {
$conn->rollBack();
error_log('오류 발생: ' . $e->getMessage());
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
}
$conn = null;
exit();
?>
php(api 역할2) - 잔액확인
⬇️ 아래 코드 보기
더보기
<?php
session_start();
include "../dbconn.php";
$user_num = $_SESSION['user_num'];
try {
$sql = "SELECT balance FROM accounts WHERE user_num = '$user_num'";
$stmt = $conn->prepare($sql);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if ($result) {
echo json_encode(['balance' => $result['balance']]);
} else {
echo json_encode(['balance' => 0]);
}
} catch (PDOException $e) {
echo json_encode(['error' => $e->getMessage()]);
}
$conn = null;
exit();
?>
'프로젝트' 카테고리의 다른 글
**Project** Part4. 모의해킹 (0) | 2024.10.25 |
---|---|
**Project** Part3. 취약점 진단 (0) | 2024.10.14 |
**Project** Part2. DB수정 및 웹사이트 코딩 수정 (0) | 2024.10.07 |