일전에 PHP로 기본적인 문법을 숙지한 후
간단하게 PHP로 로그인되는 간단한 로그인 페이지를 만들어 보았는데요
이번시간은 그 이후에
과제로 내어준 member 테이블의 조회 및 수정, 삭제 기능을 넣어서 기본적인 CRUD가 들어간
PHP 미니 홈페이지를 만들어 보도록 하겠습니다.
저번시간부터 시작된 템플릿 소스는
https://www.sourcecodester.com/
여기서
https://www.sourcecodester.com/tutorials/php/12348/php-pdo-login-and-registration.html
에서 시작되었고 로그인과 등록외에 부분에 대해서 기능을 추가해 보았습니다.
기존의 템플릿이 얼마나 중요한지를 알 수가 있습니다.
추가 기능에 걸린 시간은 거의 테스트에 걸린 시간일 뿐 하루도 안걸린 거 같습니다.
이 과정을 통해 PHP에 대해 감을 잡았을 것이라 판단됩니다.
우선 소스와 화면등을 공유해 드리겠습니다.
* 개발환경 : 주로 로컬 APM을 사용하였으며 마리아디비는 원격개발 디비서버 (오라클 클라우드)로 사용하였습니다.
A : apache : httpd-2.4.51-win64-VS16.zip
P : php : php-7.4.25-Win32-vc15-x64
M : MariaDB : 10.6.4-MariaDB
하기의 코드 부분은 복사가 가능하도록 하였습니다.
(1) 로그인 페이지
여기서 로그인 하거나 신규가입하는 화면으로 가는 페이지 입니다.
우선 데이터베이스 테이블 설계를 먼저 하여야 겠죠
간단하게 사용자(member) 테이블을 만들어 보았습니다.
CREATE TABLE `member` (
`mem_id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`lastname` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`username` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
`password` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL,
`reg_date` varchar(14) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`mem_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
어드민 정보는 최초에 강제 입력하였습니다.
INSERT INTO `member` (`mem_id`, `firstname`, `lastname`, `username`, `password` , `reg_date`) VALUES
(1, 'Administrator', '', 'admin', 'admin' , '20211203081616');
기본 버전에서 등록일자만 컬럼추가 하였습니다.
우선 개발 추가 사양은 이런 흐름입니다.
1) 일반사용자는 로그인, 아이디가 없으면 회원가입 그리고 로그인 후 메인페이지 보이기 (끝)
2) 어드민 사용자는 로그인 한 후 현재 사용자 등록 현황을 리스트로 조회하는 페이지와 각각의 사용자 데이터를 일부 수정 그리고 원하는 사용자 정보를 삭제하도록 기능을 추가 하였습니다.
주로 2) 번 항목위주로 만들어 보았습니다.
index.php 에서 시작합니다.
<?php session_start(); ?>
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand" href="https://sourcecodester.com">Sourcecodester</a>
</div>
</nav>
<div class="col-md-3"></div>
<div class="col-md-6 well">
<h3 class="text-primary">PHP - PDO Login and Registration</h3>
<hr style="border-top:1px dotted #ccc;"/>
<div class="col-md-2"></div>
<div class="col-md-8">
<?php if(isset($_SESSION['message'])): ?>
<div class="alert alert-<?php echo $_SESSION['message']['alert'] ?> msg"><?php echo $_SESSION['message']['text'] ?></div>
<?php
endif;
// clearing the message
unset($_SESSION['message']);
?>
<form action="login_query.php" method="POST" autocomplete=off>
<h4 class="text-success">Login here...</h4>
<hr style="border-top:1px groovy #000;">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" name="username" />
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" name="password" />
</div>
<br />
<div class="form-group">
<button class="btn btn-primary form-control" name="login">Login</button>
</div>
<a href="registration.php">Registration</a>
</form>
</div>
</div>
</body>
</html>
이것은 기존과 동일합니다.
로그인 이전에 등록페이지로 가겠습니다.
( 대다수 html은 sourcecodester에서 부트스트랩 CSS로 구성한 것을 기초로 작성)
(2) 등록 페이지
registration.php
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand" href="https://sourcecodester.com">Sourcecodester</a>
</div>
</nav>
<div class="col-md-3"></div>
<div class="col-md-6 well">
<h3 class="text-primary">PHP - PDO Login and Registration</h3>
<hr style="border-top:1px dotted #ccc;"/>
<div class="col-md-2"></div>
<div class="col-md-8">
<form action="register_query.php" method="POST" autocomplete=off>
<h4 class="text-success">Register here...</h4>
<hr style="border-top:1px groovy #000;">
<div class="form-group">
<label>Firstname</label>
<input type="text" class="form-control" name="firstname" />
</div>
<div class="form-group">
<label>Lastname</label>
<input type="text" class="form-control" name="lastname" />
</div>
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" name="username" />
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" name="password" />
</div>
<br />
<div class="form-group">
<button class="btn btn-primary form-control" name="register">Register</button>
</div>
<a href="index.php">Login</a>
</form>
</div>
</div>
</body>
</html>
등록 후 처리부분입니다.
여기서의 PHP는 웹프레임워크를 사용하지 않은 날(raw)코딩이므로 형태는 화면.php->화면처리쿼리.php 의 단순한 반복으로 구성됩니다.
registration_query.php
<?php
session_start();
require_once 'conn.php';
if(ISSET($_POST['register'])){
if($_POST['firstname'] != "" || $_POST['username'] != "" || $_POST['password'] != ""){
try{
$firstname = $_POST['firstname'];
$lastname = $_POST['lastname'];
$username = $_POST['username'];
// md5 encrypted
// $password = md5($_POST['password']);
$password = $_POST['password'];
$reg_date = date("YmdHis");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "INSERT INTO member(firstname,lastname,username,password,reg_date) VALUES ('$firstname', '$lastname', '$username', '$password','$reg_date')";
$conn->exec($sql);
}catch(PDOException $e){
echo $e->getMessage();
}
$_SESSION['message']=array("text"=>"User($username) successfully created ($reg_date).","alert"=>"info");
$conn = null;
header('location:index.php');
}else{
echo "
<script>alert('Please fill up the required field!')</script>
<script>window.location = 'registration.php'</script>
";
}
}
?>
index.php 의 로그인 화면에서 로그인처리 부분도 서버사이드 부분은 별도의 개발페이지로 진행됩니다.
login_query.php
<?php
session_start();
require_once 'conn.php';
if(ISSET($_POST['login'])){
if($_POST['username'] != "" || $_POST['password'] != ""){
$username = $_POST['username'];
// md5 encrypted
// $password = md5($_POST['password']);
$password = $_POST['password'];
$sql = "SELECT * FROM `member` WHERE `username`=? AND `password`=? ";
$query = $conn->prepare($sql);
$query->execute(array($username,$password));
$row = $query->rowCount();
$fetch = $query->fetch();
if($row > 0) {
echo "
<script>alert('Congratulations! registration complete!')</script>
<script>window.location = 'index.php'</script>
";
$_SESSION['user'] = $fetch['mem_id'];
$_SESSION['userid'] = $fetch['username'];
header("location: home.php");
} else{
echo "
<script>alert('Invalid username or password')</script>
<script>window.location = 'index.php'</script>
";
}
}else{
echo "
<script>alert('Please complete the required field!')</script>
<script>window.location = 'index.php'</script>
";
}
}
?>
소스 내용상 어려운 부분은 어렵기 때문에 흐름순으로 보시면 될 거 같습니다.
로그인 페이지에서 넘어온 사용자 아이디와 패스워드로 사용자가 있는지 member 테이블을 뒤져서 나오면 로그인 세션에 사용자 기본정보를 입력해 주고 메인페이지 home.php로 넘깁니다.
(3) 메인 페이지
(일반사용자 로그인한 경우)
(어드민 로그인한 경우)
커머셜 페이지가 아니라 테스트를 위해 로그인 후에는 페이지마다 상단의 세션정보를 계속 보여주게 하였습니다.
어드민페이지에서 많은 변화가 있었습니다.
화면에서 보여지듯이 사용자들이 그동안 회원가입한 사람들을 목록으로 조회하고 있습니다.
페이징 처리는 아직 되어 있지 않습니다. (이것도 고려해야겠습니다.)
home.php
<!DOCTYPE html>
<?php
require 'conn.php';
session_start();
if(!ISSET($_SESSION['user'])){
header('location:index.php');
}
$id = $_SESSION['user'];
$userid = $_SESSION['userid'];
$message = $_SESSION['message'];
echo " | seission_id:".$id;
echo " | session_userid:".$userid;
if(ISSET($_SESSION['message'])){
foreach($message as $value){
echo " | session_message:".$value." | ";
}
}
?>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand" href="https://sourcecodester.com">Sourcecodester</a>
</div>
</nav>
<div class="col-md-3"></div>
<div class="col-md-16 well">
<h3 class="text-primary">PHP - PDO Login and Registration</h3>
<hr style="border-top:1px dotted #ccc;"/>
<div class="col-md-2"></div>
<div class="col-md-8">
<h3>Welcome! (<?php echo $userid ?>)</h3>
<br />
<center><h4> 로그인한 시각
<?php
$tmp = date("Y-m-d H:i:s"); // 2021-12-02 23:40:03
?>
<?php echo $tmp ?><br>
</h4></center>
<div class="container">
</div>
</div>
<div class="row">
<?php
$stmt = $conn->prepare("SELECT * FROM member ORDER BY reg_date DESC");
$stmt->execute();
if ($userid == 'admin'){
?>
<table class="table table-bordered table-hover table-striped" style="table-layout: fixed">
<thead>
<tr>
<th>아이디</th>
<th>성</th>
<th>이름</th>
<th>생성일자</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
<?php
if($stmt->rowCount() > 0){
while($row=$stmt->fetch(PDO::FETCH_ASSOC)){
extract($row);
?>
<tr>
<td><?php echo $username; ?></td>
<td><?php echo $firstname; ?></td>
<td><?php echo $lastname; ?></td>
<td><?php echo substr($reg_date,0,4)."-".substr($reg_date,4,2)."-".substr($reg_date,6,2)."-".substr($reg_date,8,2).":".substr($reg_date,10,2).":".substr($reg_date,12,2); ?></td>
<td><a class="btn btn-primary" href="edit.php?username=<?php echo $username ?>"><span class="glyphicon glyphicon-pencil"></span> Edit</a></td>
<td><?php if($username=='admin'){?><?php }else{ ?><a class="btn btn-warning" href="delete_direct.php?username=<?php echo $username ?>" onclick="return confirm('<?php echo $username ?> 사용자를 삭제할까요?')">
<span class="glyphicon glyphicon-remove"></span>Del</a><?php } ?></td>
</tr>
<?php
}
}
// }
?>
<?php } ?>
</table>
</div>
<div>
<b><a href = "logout.php">LOGOUT</a></b>
</div>
</body>
</html>
home.jsp 에서 특이사항은 없어보입니다. 해당 되는 목록을 화면에 뿌릴 때, while문을 이용해 <tr><td>를 반복적으로 보여주게 한것이 다인거 같습니다.
이 화면에서 분기되는 페이지는 수정페이지와 삭제페이지입니다.
먼저 수정페이지 edit.php 입니다.
사용자아이디 역할을 하는 username은 수정못하도록 readonly 처리하였습니다. 그러나 테이블 상에 primary키는 auto increment 하고 있는 mem_id 입니다. 계속 +1 씩 row가 입력시 마다 카운트되기 때문에 unique하게 유지되게 되며 해당되는 회원아이디가 삭제되어도 나중에 동일한 아이디로 다시 가입이 가능한 구조입니다.
edit.php
<!DOCTYPE html>
<?php
require 'conn.php';
session_start();
if(!ISSET($_SESSION['user'])){
header('location:index.php');
}
$id = $_SESSION['user'];
$userid = $_SESSION['userid'];
$message = $_SESSION['message'];
echo " | seission_id:".$id;
echo " | session_userid:".$userid;
if(ISSET($_SESSION['message'])){
foreach($message as $value){
echo " | session_message:".$value." | ";
}
}
?>
<?php
if($_GET['username']!=null){
$username = $_GET['username'];
}
echo "username:".$username;
?>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand" href="https://sourcecodester.com">Sourcecodester</a>
</div>
</nav>
<div class="col-md-3"></div>
<div class="col-md-6 well">
<h3 class="text-primary">PHP - PDO Login and Registration</h3>
<hr style="border-top:1px dotted #ccc;"/>
<div class="col-md-2"></div>
<div class="col-md-8">
<h4 class="text-success">session :
Welcome! (<?php echo $userid ?>)
<br /></h4>
<form action="edit_query.php" method="POST" autocomplete=off>
<?php
$sql = $conn->prepare("SELECT * FROM `member` WHERE `username`='$username'");
$sql->execute();
$fetch = $sql->fetch();
?>
<hr style="border-top:1px groovy #000;">
<div class="form-group">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" name="username" value ="<?php echo $fetch['username']?>" readonly />
</div>
<label>Firstname</label>
<input type="text" class="form-control" name="firstname" value ="<?php echo $fetch['firstname']?>"/>
</div>
<div class="form-group">
<label>Lastname</label>
<input type="text" class="form-control" name="lastname" value ="<?php echo $fetch['lastname']?>"/>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" name="password" value ="<?php echo $fetch['password']?>"/>
</div>
<br />
<div class="form-group">
<button class="btn btn-primary form-control" name="edit">edit</button>
</div>
<!--a href="index.php">Login</a-->
<a href="home.php">Home</a>
</form>
</div>
</div>
</body>
</html>
해당 사용자 정보를 위한 select를 통해 수정가능한 컬럼을 먼저 보여주고 수정하는 식으로 간단하게 작성하였습니다.
이것 또한 MVC관점에서 View와 Controller 역할을 하며 다음 페이지 파일이 model의 역할을 하겠습니다.
(화면페이지.php -> 화면처리.php)
edit_query.php
<?php
session_start();
require_once 'conn.php';
if(ISSET($_POST['edit'])){
if($_POST['username'] != "" || $_POST['firstname'] != "" || $_POST['lastname'] != "" || $_POST['password'] != ""){
try{
$username = $_POST['username'];
$firstname = $_POST['firstname'];
$lastname = $_POST['lastname'];
// md5 encrypted
// $password = md5($_POST['password']);
$password = $_POST['password'];
$reg_date = date("YmdHis");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "UPDATE member SET firstname = '$firstname',lastname = '$lastname',password = '$password',reg_date = '$reg_date' WHERE username = '$username'" ;
$conn->exec($sql);
}catch(PDOException $e){
echo $e->getMessage();
}
$_SESSION['message']=array("text"=>"User($username) successfully updated ($reg_date).","alert"=>"info");
$conn = null;
header('location:home.php');
}else{
echo "
<script>alert('Please fill up the required field!')</script>
<script>window.location = 'edit.php'</script>
";
}
}
?>
최초에 등록시에 그렇지만 패스워드는 디비상에 평문으로 들어가 있으면 안되므로 MD5등으로 암호화를 추천드립니다.
사용자 정보를 tech5 -> tech55로 수정하고 hans5 -> hans55로 수정하겠습니다.
수정 처리가 완료 성공되면 home.php로 이동합니다.
정보가 바뀌었음을 확인합니다.
다음 삭제 처리입니다. 삭제는 두가지 버전입니다.
바로 삭제하는 경우와 수정페이지 처럼 삭제할 대상을 페이지를 한번 보여주고 삭제하는 방식입니다.
삭제할 대상을 페이지로 보여주는 형식은 home.php 에서 삭제 경로 페이지를 delete_direct.php 를 delete.php로 변경해 주겠습니다.
<td><?php if($username=='admin'){?><?php }else{ ?><a class="btn btn-warning" href="delete_direct.php?username=<?php echo $username ?>" onclick="return confirm('<?php echo $username ?> 사용자를 삭제할까요?')">
<span class="glyphicon glyphicon-remove"></span>Del</a><?php } ?></td>
삭제 페이지는 수정페이지와 동일합니다. 단지 input 태그는 모두 readonly로 처리하였습니다.
delete.php
<!DOCTYPE html>
<?php
require 'conn.php';
session_start();
if(!ISSET($_SESSION['user'])){
header('location:index.php');
}
$id = $_SESSION['user'];
$userid = $_SESSION['userid'];
$message = $_SESSION['message'];
echo " | seission_id:".$id;
echo " | session_userid:".$userid;
if(ISSET($_SESSION['message'])){
foreach($message as $value){
echo " | session_message:".$value." | ";
}
}
?>
<?php
if($_GET['username']!=null){
$username = $_GET['username'];
}
echo "username:".$username;
?>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.css"/>
<meta charset="UTF-8" name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<a class="navbar-brand" href="https://sourcecodester.com">Sourcecodester</a>
</div>
</nav>
<div class="col-md-3"></div>
<div class="col-md-6 well">
<h3 class="text-primary">PHP - PDO Login and Registration</h3>
<hr style="border-top:1px dotted #ccc;"/>
<div class="col-md-2"></div>
<div class="col-md-8">
<h4 class="text-success">session : Welcome! (<?php echo $userid ?>)
<br /></h4>
<form action="delete_query.php" method="POST" autocomplete=off>
<?php
$sql = $conn->prepare("SELECT * FROM `member` WHERE `username`='$username'");
$sql->execute();
$fetch = $sql->fetch();
?>
<hr style="border-top:1px groovy #000;">
<div class="form-group">
<div class="form-group">
<label>Username</label>
<input type="text" class="form-control" name="username" value ="<?php echo $fetch['username']?>" readonly />
</div>
<label>Firstname</label>
<input type="text" class="form-control" name="firstname" value ="<?php echo $fetch['firstname']?>" readonly />
</div>
<div class="form-group">
<label>Lastname</label>
<input type="text" class="form-control" name="lastname" value ="<?php echo $fetch['lastname']?>" readonly />
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" name="password" value ="<?php echo $fetch['password']?>" readonly />
</div>
<br />
<div class="form-group">
<button class="btn btn-primary form-control" name="delete">delete</button>
</div>
<!--a href="index.php">Login</a-->
<a href="home.php">Home</a>
</form>
</div>
</div>
</body>
</html>
바로 디비처리 부분은 delete_query.php를 보시겠습니다.
delete_query.php
<?php
session_start();
require_once 'conn.php';
$reg_date = date("YmdHis");
if(ISSET($_POST['delete'])){
if($_POST['username'] != ""){
try{
$username = $_POST['username'];
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "DELETE FROM member WHERE username = '$username'" ;
$conn->exec($sql);
}catch(PDOException $e){
echo $e->getMessage();
}
$_SESSION['message']=array("text"=>"User($username) successfully deleted ($reg_date).","alert"=>"info");
$conn = null;
header('location:home.php');
}else{
echo "
<script>alert('Please fill up the required field!')</script>
<script>window.location = 'delete.php'</script>
";
}
}
?>
해당 회원정보를 삭제한 후에 메인화면으로 이동하자 해당회원 정보가 없음을 확인합니다.
디비처리가 된 후에 해당 디비처리 메시지를 세션에 그때마다 넣고 확인하니 조금더 신뢰성을 주게 되는 거 같습니다.
삭제시 이와 같이 대상 데이터를 보여주고 하는 삭제 말고 해당 home.php에서 바로 삭제하는 경우입니다.
alert 만 띄워줄 뿐 삭제 한 후에 처리는 바로 실행됩니다.
어떤 것을 선택할지는 경우에 따라 선택하면 될 거 같습니다.
전자가 후자보다는 친절하지 않을까요? 한번 더 삭제할 고객정보를 확인하고 삭제하는 방식말이죠
delete_direct.php
<?php
session_start();
require_once 'conn.php';
$reg_date = date("YmdHis");
if(ISSET($_GET['username'])){
if($_GET['username'] != ""){
try{
$username = $_GET['username'];
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "DELETE FROM member WHERE username = '$username'" ;
$conn->exec($sql);
}catch(PDOException $e){
echo $e->getMessage();
}
$_SESSION['message']=array("text"=>"User($username) successfully deleted ($reg_date).","alert"=>"info");
$conn = null;
header('location:home.php');
}else{
echo "
<script>alert('Please fill up the required field!')</script>
<script>window.location = 'delete.php'</script>
";
}
}
?>
이상으로 간단하게 로그인/등록 페이지에 기능추가해 보았습니다.
내용이 워낙 간단하여 여러분들도 시간될 때 현재 기능에서 계속적으로 수정 및 고도화하면 재미있을거 같습니다.
쉬워보이지만 이런 것들이 쌓이면 PHP로 웹개발 화면이 점차 개선되면서 성장할 수 있는 기회가 될 거 같습니다.
다른 웹개발 언어보다 PHP는 파이썬 만큼 쉬운 편입니다.
다른 개발언어 경력자나 개발을 손 떼고 잠시 쉬었던 분들도 이정도 미니 프로젝트로 개발의 감을 다시 찾으시기 충분한 미니개발이 아니었나 생각됩니다.
'테크노트 > PHP' 카테고리의 다른 글
ubuntu와 windows10 에서 PHP Composer 설치 하기 (0) | 2021.12.11 |
---|---|
PHP에서 세션을 어떻게 사용하는가? (0) | 2021.12.03 |
PHP 날짜 함수 사용하기 (0) | 2021.12.03 |
웹개발 - PHP 활용편 : 미니홈페이지 만들기 (0) | 2021.11.24 |
웹개발 - PHP 문법 클래스편 (0) | 2021.11.15 |
댓글