오늘은 php와 MySQL을 사용해서 게시판을 만들어보도록 하겠습니다.
우선 테이블을 두개 만들겠습니다. 하나는 게시판에 들어갈 포스트에 대한 테이블, 다른 하나는 로그인에 사용될 사용자 테이블 입니다. 저는 각각 board 와 user 로 지었습니다.
// 게시글 테이블
CREATE TABLE `board` (
`board_id` INT NOT NULL AUTO_INCREMENT ,
`board_title` VARCHAR(512) NOT NULL ,
`user_id` BIGINT(20) NOT NULL ,
`board_date` DATETIME NOT NULL DEFAULT NOW() ,
`board_views` INT NOT NULL DEFAULT 0 ,
PRIMARY KEY (`board_id`)
CONSTRAINT `FK_board_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`)
) ENGINE = InnoDB;
// 사용자 테이블
CREATE TABLE `user` (
`user_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_email` VARCHAR(40) NOT NULL,
`user_name` VARCHAR(20) NOT NULL,
`user_pw` VARCHAR(256) NULL,
`user_date` DATETIME NOT NULL DEFAULT NOW(),
PRIMARY KEY (`user_id`),
UNIQUE INDEX `user_email` (`user_email`)
);
board
- board_id : 게시글의 고유 아이디 값 (PK)
- board_title : 게시글의 제목
- user_id : 작성자의 고유 아이디 값 (FK)
- board_date : 게시글 작성 날짜와 시간
- board_views : 게시글 조회 수
user
- user_id : 사용자의 고유 아이디 값 (PK)
- user_email : 로그인에 사용되는 사용자의 이메일
- user_name : 사용자의 이름
- user_pw : 사용자의 비밀번호
- user_date : 사용자의 계정 생성 날일시
그리고 제가 게시판을 만들 때 작성한 php 문서는 총 8개입니다.
- index.php : 게시글 목록을 볼 수 있는 페이지
- write.php : 게시글을 작성할 수 있는 페이지 (로그인 후 사용 가능)
- view.php : 게시글의 내용을 볼 수 있는 페이지
- modify.php : 게시글의 내용을 수정할 수 있는 페이지 (본인 글만 수정 가능)
- delete.php : 게시글을 삭제할 수 있는 페이지 (본인 글만 삭제 가능)
- login.php : 로그인을 할 수 있는 페이지
- logout.php : 로그아웃을 처리하는 페이지
- register.php : 회원가입을 하는 페이지
게시판 목록 만들기
그 다음 게시글 목록을 보여줄 index.php 를 작성해보겠습니다.
<div class="loginButton">
<?php
if (isset($_SESSION) === false) {session_start();}
if (isset($_SESSION['id']) === false) {
?>
<a href="login.php">로그인</a>
<a href="register.php">회원가입</a>
<?php
} else {
?>
<a href="logout.php">로그아웃</a>
<?php
}
?>
</div>
이렇게 로그인 상태가 아니라면 로그인과 회원가입 버튼을 띄우고, 로그인 상태라면 로그아웃 버튼을 띄울 수 있도록 해주겠습니다.
<div class="index">
<h1>자유 게시판</h1>
<h4>자유롭게 글을 쓸 수 있는 게시판입니다.</h4>
<button onclick="writePost()">글쓰기</button>
<table>
<tr>
<th width="70">번호</th>
<th width="500">제목</th>
<th width="120">작성자</th>
<th width="100">작성일</th>
<th width="100">조회수</th>
</tr>
그 다음엔 idex라는 클래스로 묶어서 게시글 테이블을 만들어주세요. 글쓰기 버튼에 함수가 있는 이유는 로그인 된 사용자만 글을 쓸 수 있도록 하기 위해서 입니다.
<?php
$list_num = 10;
$page_num = 10;
$num = query('SELECT * FROM board')->num_rows;
$page = isset($_GET['page']) ? $_GET['page'] : 1;
$total_page = ceil($num / $list_num);
$total_block = ceil($total_page / $page_num);
$now_block = ceil($page / $page_num);
$s_page = ($now_block * $page_num) - ($page_num - 1);
if ($s_page <= 0) {
$s_page = 1;
};
$e_page = $now_block * $page_num;
if ($total_page < $e_page) {
$e_page = $total_page;
};
$start = ($page - 1) * $list_num;
$sql = query("SELECT * FROM board ORDER BY board_id DESC LIMIT $start, $list_num");
그 다음 페이지네이션을 해줘야 하는데요, 이런 의미로 사용된 변수들 입니다.
- list_num : 한 번에 보이고 싶은 게시글 수
- page_num : 한번에 보이고 싶은 페이지 버튼 수
- num : 총 게시글 수
- page : 현재 페이지 위치
- total_page : 총 필요한 페이지 수
- total_block : 총 필요한 페이지 블럭 수
- now_block : 현재 페이지가 포함되어있는 블럭 위치
- s_page : 시작 페이지
- e_page : 끝나는 페이지
- start : 시작 페이지 부분
참고한 블로그
테이블을 출력하기 전에 이렇게 미리 정의해 주고,
while ($row = $sql->fetch_array()) {
echo '<tr>';
echo '<td>' . $row['board_id'] . '</td>';
echo '<td><a href="view.php?id=' . $row['board_id'] . '">' . $row['board_title'] . '</a></td>';
$user_sql = query("SELECT user_name FROM user WHERE user_id = " . $row['user_id']);
$user_data = $user_sql->fetch_array();
$user_name = $user_data['user_name'];
echo '<td>' . $user_name . '</td>';
echo '<td>' . $row['board_date'] . '</td>';
echo '<td>' . $row['board_views'] . '</td>';
echo '</tr>';
}
?>
</table>
이렇게 게시글 정보들을 sql문에서 추출해서 표에 넣어줍니다.
board 라는 테이블에 user_id 가 있는데 작성자의 이름을 갖고 오기 위해서 중간에 다시 한번 질의문을 작성해 주었습니다.
그 다음은 페이지 버튼 부분인데요, 페이지 위치가 1이 아닐 경우에만 이전 버튼을 클릭할 수 있도록 해주고,
마찬가지로 현재 페이지에 해당하는 부분은 strong 태그를 사용하고 그렇지 않은 부분은 a 태그를 사용해주었습니다.
<div class="page">
<?php
if ($page <= 1) {
echo '<span class="fo_re"> 이전 </span>';
} else {
echo '<a href="index.php?page=1"> 이전 </a>';
}
for($print_page = $s_page; $print_page <= $e_page; $print_page++) {
if ($print_page == $page) {
echo '<strong> ' . $print_page . ' </strong>';
} else {
echo '<a href="index.php?page=' . $print_page . '"> ' . $print_page . ' </a>';
}
}
if ($page >= $total_page) {
echo '<span class="fo_re"> 다음 </span>';
} else {
echo '<a href="index.php?page=' . $total_page . '"> 다음 </a>';
}
?>
</div>
마지막으로 작성한 부분은 아까 글쓰기 버튼에 사용된 스크립트인데요,
<?php echo '<h4 class="pagenum"> 총 ' . $num . '개의 글이 있습니다</h4>' ?>
</div>
<script>
function writePost() {
<?php if (isset($_SESSION['id']) === false) { ?>
alert('로그인이 필요합니다.');
location.href='login.php';
<?php } else { ?>
location.href='write.php';
<?php } ?>
}
</script>
</body>
</html>
이렇게 세션에 사용자의 아이디가 존재하지 않는다면 로그인 페이지로 이동하게 하고, 만약 로그인이 되어있는 상태라면 게시글 작성 페이지로 이동하게 합니다. 그럼 다음 차례로 글을 작성하는 페이지를 설명해드리겠습니다.
게시글 본문 페이지 만들기
게시글 본문 페이지에서 보이는 것은 아래와 같이 제목, 작성자, 작성일시, 조회수, 본문 그리고 삭제 수정 버튼입니다.
<div class="view">
<h2><?php echo $board['board_title']; ?></h2>
<div class="user_info">
<p><b>작성자 </b>
<?php
$user_sql = query("SELECT user_name FROM user WHERE user_id = " . $board['user_id']);
$user_data = $user_sql->fetch_array();
$user_name = $user_data['user_name'];
echo $user_name; ?> | <?php echo $board['board_date']; ?> | <b>조회수</b> <?php echo $board['board_views']; ?></p>
</div>
<hr>
<div class="content">
<?php echo nl2br($board['board_content']); ?>
</div>
제목부터 본문 까지의 부분은 이렇게 작성해주었습니다. 여기서도 사용자의 이름을 불러왔는데, 자주 사용하는 것 같으니 추후 수정할 때에는 db.php 에 따로 함수를 만들어야할 것 같네요 ..
<div class="viewButton">
<ul>
<li><button onclick="location.href='index.php'">목록</button></li>
<?php
if ($board['user_id'] == $_SESSION['id']) { ?>
<li><button onclick="location.href='modify.php?id=<?php echo $board['board_id']; ?>'">수정</button></li>
<li><button onclick="location.href='delete.php?id=<?php echo $board['board_id']; ?>'">삭제</button></li>
<?php } ?>
</ul>
</div>
아래 보이는 목록, 수정, 삭제 버튼 부분은 쉬운 배치를 위해서 ul 태그를 사용해주고요, 수정과 삭제 버튼은 작성자 본인에게만 보일 수 있도록 설정 해줍시다.
게시글 작성 페이지 만들기
이제는 게시글을 작성하는 페이지를 만들어 보겠습니다. 틀은 대충 아래 처럼 생겼고 이에 맞게 태그를 구성해봅시다.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<title>글쓰기</title>
</head>
<body>
<div class="write">
<h1>글을 작성하세요</h1>
<hr/>
<form method="POST" action="write.php">
<table class="writeTable">
<tr>
<th width="50">제목</th>
<td><input type="text" name="title" placeholder="제목을 입력하세요" required></td>
</tr>
<tr>
<th>내용</th>
<td><textarea name="content" rows="5" cols="40" placeholder="내용을 입력하세요" required ></textarea></td>
</tr>
</table>
<ul>
<li><button type="button" onclick="location.href='index.php'">취소</button></li>
<li><input class="button" type="submit" value="작성 완료"></li>
</ul>
</form>
</div>
</body>
</html>
그 다음 게시글을 작성했으면 해당 게시글에 대한 정보를 POST 형식으로 DB에 전달해주어야겠죠?
그래서 저는 write_ok.php 를 따로 만들지 않고 위의 코드 위에 스크립트를 작성했습니다.
<?php
include 'db.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$title = $_POST['title'];
$content = $_POST['content'];
if (empty($title) || empty($content)) {
echo "빈칸을 채워주세요";
} else {
$sql = "INSERT INTO board (board_title, board_content, user_id) VALUES ('$title', '$content', '$_SESSION[id]')";
query($sql);
header("Location: index.php");
exit();
}
}
?>
만약 서버의 요청 메소드가 포스트라면, 두 항목이 모두 채워져 있을 때 두 변수를 쿼리문으로 board 테이블에 저장해줍니다.
그 후에는 header 를 사용하여 게시글 리스트가 있는 index.php로 이동하도록 해주도록 하였습니다.
게시글 수정 페이지 만들기
그 다음 게시글 작성과 비슷한 게시글 수정 기능을 만들어보겠습니다.
우선 틀은 게시글 작성과 크게 다르지 않습니다. 게시글 작성 형식에 제목과 본문의 내용을 불러오기만 하면 됩니다.
<!DOCTYPE html>
<html>
<head>
<title>게시판</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="write">
<h1>글을 수정하세요</h1>
<hr/>
<form method="POST" action="modify.php?id=<?php echo $board['board_id']; ?>">
<table class="writeTable">
<tr>
<th width="50">제목</th>
<td><input type="text" name="title" value="<?php echo $board['board_title']; ?>" required></td>
</tr>
<tr>
<th>내용</th>
<td><textarea name="content" rows="5" cols="40" required><?php echo $board['board_content']; ?></textarea></td>
</tr>
</table>
<input type="hidden" name="id" value="<?php echo $board['board_id']; ?>">
<ul>
<li><button type="button" onclick="location.href='view.php?id=<?php echo $board['board_id']; ?>'">취소</button></li>
<li><input class="button" type="submit" value="수정 완료"></li>
</ul>
</form>
</div>
</body>
</html>
버튼은 수정 취소와 수정 완료 두가지만 있으면 되겠죠?
그런 다음 수정 완료를 눌렀을 때 실행할 동작도 마찬가지로 해당 문서 위에 작성해주겠습니다.
<?php
include 'db.php';
$id = $_GET['id'];
$sql = query("SELECT * FROM board WHERE board_id = $id");
$board = $sql->fetch_array();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$title = $_POST['title'];
$content = $_POST['content'];
if (empty($title) || empty($content)) {
echo "빈칸을 채워주세요";
} else {
$modify = "UPDATE board SET board_title = '$title', board_content = '$content' WHERE board_id = $id";
query($modify);
header("Location: view.php?id=$id");
exit();
}
}
?>
우선 게시글의 아이디를 갖고와서 해당 게시글의 정보를 불러와야 아까 html 안에서 해당 정보를 input 과 textarea 칸 안에 적어줄 수 있겠죠?
그 다음 게시글 작성과 마찬가지로 포스트 요청이 들어왔을 때 일정 조건을 충족하면 수정 질의를 시행해주면 됩니다.
위와 같이 코드를 작성하면 아래와 같은 화면처럼 나타납니다 !
게시글 삭제 기능 만들기
게시글 삭제 기능은 따로 화면을 구성할 필요가 없고 처리만 해주면 되기 때문에 아주 간단합니다.
<?php
include 'db.php';
$id = $_GET['id'];
$sql = query("DELETE FROM board WHERE board_id='$id';");
?>
<script type="text/javascript">alert("삭제되었습니다.");</script>
<meta http-equiv="refresh" content="0 url=index.php" />
이렇게 게시글의 아이디를 테이블에서 삭제하고, 삭제되었다는 알림을 띄우면 됩니다.
회원가입 페이지 만들기
로그인 페이지에 앞서, 회원가입 페이지를 먼저 만들어 볼건데요,
이렇게 이메일, 비밀번호 그리고 이름만 입력 받으면 됩니다.
그래서 html은 이런식으로 구성해주면 됩니다.
<!DOCTYPE html>
<html>
<head>
<title>회원가입</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="login">
<h1>회원가입</h1>
<form method="post" action="register.php">
<table>
<tr>
<td style="text-align:right; font-weight:bolder;">이메일</td>
<td style="text-align:left;"><input type="email" name="email" required placeholder="이메일" size="40"></td>
</tr>
<tr>
<td style="text-align:right; font-weight:bolder;">비밀번호</td>
<td style="text-align:left;"><input type="password" name="pw" required placeholder="비밀번호" size="40"></td>
</tr>
<tr>
<td style="text-align:right; font-weight:bolder;">이름</td>
<td style="text-align:left;"><input type="text" name="name" required placeholder="이름" size="40"></td>
</tr>
<tr class="loginSubmit">
<td colspan="2"><input type="submit" value="회원가입"></td>
</tr>
<tr>
<td colspan="2" style="font-size: 15px;">이미 회원이신가요? <a href="login.php">로그인</a></td>
</tr>
</form>
</div>
</body>
</html>
div 클래스는 로그인 화면과 스타일이 비슷하기 때문에 따로 변경하지 않았습니다.
이렇게 submit 버튼으로 회원가입 버튼을 누르게 되면 해당 처리는 php가 담당하게 됩니다.
$stmt = $db->prepare("SELECT COUNT(user_id) as cnt FROM user WHERE user_email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$stmt->bind_result($user_count);
$stmt->fetch();
$stmt->close();
if ($user_count == 1) {
echo "<script>alert('이미 존재하는 이메일입니다.'); location.href='register.php';</script>";
exit();
}
여기서 신경써야할 부분은 사용자가 입력한 이메일이 이미 존재하는지 확인하는 것인데요,
만약 사용자가 입력한 이메일과 같은 이메일이 이미 user 테이블에 존재한다면 이미 존재하는 이메일임을 알려줍니다.
그 다음으로는 사용자가 입력한 비밀번호로 해시값을 생성하여 DB로 넘겨줍니다. 회원가입이 완료되었다면 로그인 페이지로 이동하도록 설정해줍니다.
$bcrypt_pw = password_hash($pw, PASSWORD_BCRYPT);
$sql = query("INSERT INTO user (user_email, user_pw, user_name) VALUES ('$email', '$bcrypt_pw', '$name')");
if ($sql) {
echo "<script>alert('회원가입 되었습니다.'); location.href='login.php';</script>";
} else {
echo "<script>alert('회원가입에 실패했습니다.');</script>";
}
}
?>
로그인 페이지 만들기
회원가입이 완료가 되었다면 로그인을 해야겠죠? 로그인 페이지는 회원가입 페이지에서 이름을 작성하는 부분만 제외하면 거의 동일합니다.
html은 이런 형식으로 작성 되었습니다. 회원 가입과 거의 다르지 않죠?
<!DOCTYPE html>
<html>
<head>
<title>로그인</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="login">
<h1>로그인</h1>
<form method="post" action="login.php">
<table>
<tr>
<td style="text-align:right; font-weight:bolder;">이메일</td>
<td style="text-align:left;"><input type="email" name="email" required placeholder="이메일" size="40"></td>
</tr>
<tr>
<td style="text-align:right; font-weight:bolder;">비밀번호</td>
<td style="text-align:left;"><input type="password" name="pw" required placeholder="비밀번호" size="40"></td>
</tr>
<tr class="loginSubmit">
<td colspan="2"><input type="submit" value="로그인"></td>
</tr>
<tr>
<td colspan="2" style="font-size: 15px;">아직 회원이 아니신가요? <a href="register.php">회원가입</a></td>
</tr>
</form>
</div>
</body>
</html>
이제 로그인을 처리하는 방법을 알아봅시다.
$stmt = $db->prepare("SELECT user_id, user_pw FROM user WHERE user_email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$stmt->bind_result($user_id, $user_pw);
$stmt->fetch();
$stmt->close();
$is_match_pw = password_verify($pw, $user_pw);
if ($is_match_pw) {
session_start();
$_SESSION['id'] = $user_id;
echo "<script>alert('로그인 되었습니다.'); location.href='index.php';</script>";
} else {
echo "<script>alert('이메일 또는 비밀번호가 일치하지 않습니다.'); location.href='login.php';</script>";
}
로그인은 회원가입과 다르게 이메일의 중복여부를 따질 것이 아니라 해당 이메일에 해당하는 해시 비밀번호 값을 $user_pw 로 가지고 와서, 사용자가 로그인 페이지에서 입력한 비밀번호인 $pw 와 일치하는지 비교를 해줍니다.
만약 둘이 일치한다면, 세션을 시작하고, 해당 세션의 아이디를 유저의 고유 아이디 값인 $user_id로 설정해줍니다.
사용자가 로그인이 완료되었다면 index.php로 이동하도록 하고, 만약 입력된 정보가 서로 일치하지 않는다면 다시 login.php로 이동하게 됩니다.
로그아웃은 아주 간단합니다. 세션을 session_destroy()를 통하여 종료시켜주면 됩니다. session_destroy() 는 특정 세션을 삭제하는 것이 아니라 모든 세션을 삭제하는 함수이기 때문에 따로 사용자의 아이디를 적어줄 필요가 없게 됩니다. 만약 두 페이지에서 로그인을 하였다면 한 계정으로 로그아웃 했을 때 둘 다 로그아웃이 되게 됩니다.
<?php
session_start();
session_destroy();
echo "<script>alert('로그아웃 되었습니다.'); location.href='index.php';</script>";
?>
나머지 css는 제 깃허브에 올라와있으니 참고하실 분은 참고하시길 바랍니다.
https://github.com/5AgA/NoticeBoard
느낀점
아직 기본적인 기능만 구현해서, 보안적인 부분이라던지, 디테일적인 부분에서 아주 많이 부족한 것 같다.
특히 링크에서 페이지의 위치를 볼 수 있기 때문에 로그인을 하지 않더라도 게시글을 작성할 수 있고, 본인의 게시글이 아니더라도 삭제가 가능하다. 이러한 점은 추후에 좀 더 공부하고 개선해보도록 하겠다.