
• 로그인
• 회원가입
• 로그아웃
• 마이페이지
• 비밀번호 변경
• 게시판
↳ 게시글 작성 ( +첨부파일 업로드 )
↳ 게시글 수정 & 삭제
↳ 댓글
↳ 게시글 검색
↳ 페이지네이션
/project-folder
├── index.php
├── css/
│ ├── board.css
│ ├── login.css
│ ├── pw_update_process.css
│ ├── view.css
│ ├── index.css
│ ├── mypage.css
│ ├── search.css
│ ├── write.css
├── js/
│ ├── login.js
├── uploads/
├── php/
│ ├── user/
│ │ ├── mypage.php
│ │ ├── pw_update_process.php
│ ├── auth/
│ │ ├── logout.php
│ │ ├── login.php
│ │ ├── logout.php
│ │ ├── signUp2.php
│ ├── board/
│ │ ├── board.php
│ │ ├── post.php
│ │ ├── post_update.php
│ │ ├── post_delete.php
│ │ ├── search.php
│ │ ├── write.php
│ │ ├── write_process.php
│ │ ├── post_delete.php
│ │ ├── post_update_process.php
🔸비로그인 상태

🔸로그인 상태 (드롭다운 메뉴)


▶
index.php전체 코드
<?php
session_start();
$isLoggedIn = isset($_SESSION['username']);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WEBSITE</title>
<link rel="stylesheet" href="./css/index.css" />
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div class="logo">MyWebsite</div>
<nav class="main-nav">
<a href="./php/board/board.php" class="board">게시판</a>
<?php if (!$isLoggedIn): ?>
<a href="login.html" class="login-btn">Sign in / Sign up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="./php/user/mypage.php">마이페이지</a>
<a href="./php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
</header>
<main>
<h2>Welcome to the main Page!</h2>
<p>This is the homepage. Please sign in to access your account.</p>
</main>
<script>
function toggleDropdown() {
const dropdown=document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e){
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
</script>
</body>
</html>
✔️ 세션 확인
<?php
session_start();
$isLoggedIn = isset($_SESSION['username']);
?>
session_start(): 세션을 시작, 로그인 여부를 확인하기 위해$isLoggedIn: 로그인 상태인지 아닌지를 세션값이 존재하는지로 판별
✔️ <header> : 상단 네비게이션
<header>
<div class="logo">MyWebsite</div>
<nav class="main-nav">
<a href="./php/board/board.php" class="board">게시판</a>
<?php if (!$isLoggedIn): ?>
<a href="login.html" class="login-btn">Sign in / Sign up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="./php/user/mypage.php">마이페이지</a>
<a href="./php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
</header>
./php/board/board.php로 이동됨Sign in / Sign up 버튼이 표시됨마이페이지와 로그아웃 드롭다운
✔️ <main>: 메인 콘텐츠
<main>
<h2>Welcome to the main Page!</h2>
<p>This is the homepage. Please sign in to access your account.</p>
</main>
✔️ <script>: Dropdown 토글 스크립트
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
</script>
로그인 폼

회원가입 폼

•
login.html: 로그인 화면 레이아웃
•login.js: 한 화면에 로그인과 회원가입 전환 처리
•login.php: 전송 받은 파라미터를 가지고 DB에서 로그인 및 회원가입 처리
➡️ 로그인과 회원가입 페이지를 한 화면에서 토글로 처리 !
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login Page</title>
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="./css/login.css">
</head>
<body>
<div class="login">
<div class="login__forms">
<!-- 로그인 폼 -->
<form action="./php/auth/login.php" method="POST" class="login__register block" id="login-in">
<h1 class="login__title">Sign In</h1>
<div class="login__box">
<i class="fas fa-user login__icon"></i>
<input type="text" placeholder="Username" class="login__input" name="username" required>
</div>
<div class="login__box">
<i class="fas fa-lock login__icon"></i>
<input type="password" placeholder="Password" class="login__input" name="password" required>
</div>
<a href="#" class="login__forgot">Forgot Password?</a>
<button type="submit" class="login__button">Sign In</button>
<div>
<span class="login__account login__account--account">Don't Have an Account?</span>
<span class="login__signin login__signin--signup" id="sign-up">Sign Up</span>
</div>
</form>
<!-- 계정 생성(회원가입) 폼 -->
<form action="./php/auth/signUp2.php" method="POST" class="login__create none" id="login-up">
<h1 class="login__title">Create Account</h1>
<div class="login__box">
<i class="fas fa-user login__icon"></i>
<input type="text" placeholder="Username" class="login__input" name="username" required>
</div>
<div class="login__box">
<i class="fa-solid fa-at login__icon"></i>
<input type="email" placeholder="Email" class="login__input" name="email" required>
</div>
<div class="login__box">
<i class="fas fa-lock login__icon"></i>
<input type="password" placeholder="Password" class="login__input" name="password" required>
</div>
<button type="submit" class="login__button">Sign Up</button>
<div>
<span class="login__account login__account--account">Already have an Account?</span>
<span class="login__signup login__signup--signup" id="sign-in">Sign In</span>
</div>
<div class="login__social">
<a href="#" class="login__social--icon"><i class="fab fa-facebook-f"></i></a>
<a href="#" class="login__social--icon"><i class="fab fa-x-twitter"></i></a>
<a href="#" class="login__social--icon"><i class="fab fa-google"></i></a>
<a href="#" class="login__social--icon"><i class="fab fa-github"></i></a>
</div>
</form>
</div>
</div>
<script src="./js/login.js"></script>
</body>
</html>
✔️ 로그인 폼
<form action="./php/auth/login.php" method="POST" class="login__register block" id="login-in">
<h1 class="login__title">Sign In</h1>
<div class="login__box">
<i class="fas fa-user login__icon"></i>
<input type="text" placeholder="Username" class="login__input" name="username" required>
</div>
<div class="login__box">
<i class="fas fa-lock login__icon"></i>
<input type="password" placeholder="Password" class="login__input" name="password" required>
</div>
<a href="#" class="login__forgot">Forgot Password?</a>
<button type="submit" class="login__button">Sign In</button>
<div>
<span class="login__account login__account--account">Don't Have an Account?</span>
<span class="login__signin login__signin--signup" id="sign-up">Sign Up</span>
</div>
</form>
action="./php/auth/login.php" method="POST"login.php로 로그인 폼을 보냄.Don't Have an Account? Sign Up 클릭 → login.js에서 로그인 폼은 숨기고 회원가입 폼으로 토글됨.
✔️ 회원가입 폼
<form action="./php/auth/signUp2.php" method="POST" class="login__create none" id="login-up">
<h1 class="login__title">Create Account</h1>
<div class="login__box">
<i class="fas fa-user login__icon"></i>
<input type="text" placeholder="Username" class="login__input" name="username" required>
</div>
<div class="login__box">
<i class="fa-solid fa-at login__icon"></i>
<input type="email" placeholder="Email" class="login__input" name="email" required>
</div>
<div class="login__box">
<i class="fas fa-lock login__icon"></i>
<input type="password" placeholder="Password" class="login__input" name="password" required>
</div>
<button type="submit" class="login__button">Sign Up</button>
<div>
<span class="login__account login__account--account">Already have an Account?</span>
<span class="login__signup login__signup--signup" id="sign-in">Sign In</span>
</div>
<div class="login__social">
<a href="#" class="login__social--icon"><i class="fab fa-facebook-f"></i></a>
<a href="#" class="login__social--icon"><i class="fab fa-x-twitter"></i></a>
<a href="#" class="login__social--icon"><i class="fab fa-google"></i></a>
<a href="#" class="login__social--icon"><i class="fab fa-github"></i></a>
</div>
</form>
action="./php/auth/signUp2.php" method="POST"signUp2.php로 회원가입 폼을 보냄Already have an Account? Sign In 클릭 시, 로그인 폼으로 토글됨
✔️ 회원가입 폼
<script src="./js/login.js"></script>
➡️ 로그인/회원가입 폼을 한 페이지에 토글 처리 해주는 자바스크립트
✔️ DOM 요소 가져오기(로그인 폼, 회원가입 폼)
const signup = document.getElementById("sign-up");
const signin = document.getElementById("sign-in");
const loginin = document.getElementById("login-in");
const loginup = document.getElementById("login-up");
sign-up : 로그인 폼의 Sign Up 버튼sign-in : 회원가입 폼의 Sign In 버튼login-in: 로그인 폼 전체login-up: 회원가입 폼 전체
✔️ Sign Up 버튼 클릭 이벤트
signup.addEventListener("click", () => {
loginin.classList.remove("block");
loginup.classList.remove("none");
loginin.classList.add("none");
loginup.classList.add("block");
})
loginin)의 block 클래스를 제거loginup)의 none 클래스를 제거loginin)에 none 클래스 추가 → 로그인 폼 숨김loginup)에 block 클래스 추가 → 회원가입 폼 화면에 표시
✔️ Sign In 버튼 클릭 이벤트
signin.addEventListener("click", () => {
loginin.classList.remove("none");
loginup.classList.remove("block");
loginin.classList.add("block");
loginup.classList.add("none");
})
loginin)의 none 클래스를 제거loginup)의 block 클래스를 제거loginin)에 block 클래스 추가 → 로그인 폼 화면에 표시loginup)에 none 클래스 추가 → 회원가입 폼 숨김
<?php
session_start(); //세션 시작
// 1. DB 접속하기
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME,DB_PASSWORD, DB_NAME);
// 2. DB접속 확인하기
if(!$db_conn){
echo "DB Connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
// 3. 로그인 폼의 각 데이터를 받아오기
$username = $_POST['username'];
$pw = $_POST['password'];
// 4. sql 쿼리 작성
$sql = "select * from user_info where username = '$username' and pw = '$pw'";
// 5. 쿼리 실행
$result = mysqli_query($db_conn, $sql);
if($result){
// 6. 쿼리 실행 결과 가져오기
$row = mysqli_fetch_assoc($result);
// 7. 유저 존재여부 확인
if($row){
$_SESSION['username'] = $row['username']; //로그인 세션 저장
header("Location: /project-folder/index.php");
exit();
}else{
echo "<script>alert('로그인 정보가 일치하지 않습니다.'); window.location.href='/project-folder/login.html';</script>";
}
mysqli_free_result($result); // 메모리 해제
} else{
echo "쿼리 실행 실패:" . mysqli_error($db_conn);
}
// 7. DB 연결 종료
mysqli_close($db_conn);
?>
✔️ 세션 시작
session_start();
useranme)을 세션에 저장할 수 있게 준비
✔️ DB 연결 & 확인
// 1. DB 접속하기
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME,DB_PASSWORD, DB_NAME);
// 2. DB접속 확인하기
if(!$db_conn){
echo "DB Connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
✔️ 로그인 폼 데이터 받기 (POST)
// 3. 로그인 폼의 각 데이터를 받아오기
$username = $_POST['username'];
$pw = $_POST['password'];
login.html의 로그인 폼에서 입력한 username과 password 파라미터 값을 POST로 받음.
✔️ SQL 쿼리 작성 (username과 pw가 일치하는 행 조회)
// 4. sql 쿼리 작성
$sql = "select * from user_info where username = '$username' and pw = '$pw'";
✔️ 결과 실행 → 결과 유무에 따라 로그인 성공/실패 처리
// 5. 쿼리 실행
$result = mysqli_query($db_conn, $sql);
if($result){
// 6. 쿼리 실행 결과 가져오기
$row = mysqli_fetch_assoc($result);
// 7. 유저 존재여부 확인
if($row){
$_SESSION['username'] = $row['username']; //로그인 세션 저장
header("Location: /project-folder/index.php");
exit();
}else{
echo "<script>alert('로그인 정보가 일치하지 않습니다.'); window.location.href='/project-folder/login.html';</script>";
}
mysqli_free_result($result); // 메모리 해제
} else{
echo "쿼리 실행 실패:" . mysqli_error($db_conn);
}
index.php)로 이동됨login.html)로 이동됨.
✔️ DB 연결 종료
// 7. DB 연결 종료
mysqli_close($db_conn);
✔️ user_info 테이블 구조

▶ logout.php 전체 코드
<?php
session_start(); //세션 시작(현재 세션 불러오기)
session_unset(); //모든 세션 변수 제거
session_destroy(); //세션 자체를 파기
// index.php로 이동
header("Location:/project-folder/index.php");
exit();
?>
index.php)로 이동
▶
logout.php전체 코드
<?php
session_start();
// 세션에 로그인된 사용자 확인
if (!isset($_SESSION['username'])) {
header("Location: /project-folder/login.html");
exit();
}
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
// 접속 확인
if (!$db_conn) {
echo "DB Connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
$username = $_SESSION['username'];
$sql = "SELECT * FROM user_info WHERE username='$username'";
$result = mysqli_query($db_conn, $sql);
$user = mysqli_fetch_assoc($result);
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>MyPage</title>
<link rel="stylesheet" href="/project-folder/css/mypage.css">
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
</head>
<body>
<div class="mypage">
<div class="mypage__form">
<h1 class="mypage__title">마이페이지</h1>
<div class="mypage__box">
<div class="mypage__icon-circle">
<i class="fa-solid fa-user mypage__icon" style="color: #4ad395;"></i>
</div>
<h3 class="mypage__name"><?= htmlspecialchars($user['username']) ?></h3>
<a href="/project-folder/php/auth/logout.php" class="mypage__logout">로그아웃</a>
</div>
<div>
<h3 class="mypage__userInfo">회원정보</h3>
</div>
<div class="mypage__userInfo-container">
<div class="mypage__userInfo-box">
<!-- 이름 -->
<div class="label">이름</div>
<div class="value"><?= htmlspecialchars($user['username']) ?></div>
<div></div> <!-- 오른쪽 칸 빈칸 -->
<!-- 비밀번호 -->
<div class="label">비밀번호</div>
<div class="value">
<?php
$plain_pw = $user['pw'];
echo str_repeat('•', strlen($plain_pw));
?>
<input type="hidden" id="real_pw" value="<?= htmlspecialchars($plain_pw) ?>">
</div>
<a href="/project-folder/php/user/pw_update_process.php" class="mypage__logout">변경</a>
<!-- 이메일 -->
<div class="label">이메일</div>
<div class="value"><?= htmlspecialchars($user['email']) ?></div>
<div></div> <!-- 오른쪽 칸 빈칸 -->
</div>
</div>
</div>
</div>
</body>
</html>
✔️ 세션 시작 & 로그인 여부 확인
session_start();
// 세션에 로그인된 사용자 확인
if (!isset($_SESSION['username'])) {
header("Location: /project-folder/login.html");
exit();
}
$_SESSION['username']이 없으면 로그인하지 않은 사용자로 인식하고 로그인페이지(login.html)로 이동됨.
✔️ DB 연결 & 사용자 정보 조회
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
// 접속 확인
if (!$db_conn) {
echo "DB Connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
$username = $_SESSION['username'];
$sql = "SELECT * FROM user_info WHERE username='$username'";
$result = mysqli_query($db_conn, $sql);
$user = mysqli_fetch_assoc($result);
username) 가져옴.SELECT * FROM user_info WHERE username='$username' 쿼리 실행$result)의 첫 번째 row를 $user에 저장.
✔️ 마이페이지 레이아웃
<div class="mypage">
<div class="mypage__form">
<h1 class="mypage__title">마이페이지</h1>
<!-- 유저 아이콘 + 이름 + 로그아웃 -->
<div class="mypage__box">
<div class="mypage__icon-circle">
<i class="fa-solid fa-user mypage__icon" style="color: #4ad395;"></i>
</div>
<h3 class="mypage__name"><?= htmlspecialchars($user['username']) ?></h3>
<a href="/project-folder/php/auth/logout.php" class="mypage__logout">로그아웃</a>
</div>
<!-- 회원정보 -->
<h3 class="mypage__userInfo">회원정보</h3>
<div class="mypage__userInfo-container">
<div class="mypage__userInfo-box">
<!-- 이름 -->
<div class="label">이름</div>
<div class="value"><?= htmlspecialchars($user['username']) ?></div>
<div></div> <!-- 오른쪽 칸 비움 -->
<!-- 비밀번호 -->
<div class="label">비밀번호</div>
<div class="value">
<?php
$plain_pw = $user['pw'];
echo str_repeat('•', strlen($plain_pw));
?>
<input type="hidden" id="real_pw" value="<?= htmlspecialchars($plain_pw) ?>">
</div>
<a href="/project-folder/php/user/pw_update_process.php" class="mypage__logout">변경</a>
<!-- 이메일 -->
<div class="label">이메일</div>
<div class="value"><?= htmlspecialchars($user['email']) ?></div>
<div></div> <!-- 오른쪽 칸 비움 -->
</div>
</div>
</div>
</div>
•로 길이만 표시<input>태그의 hidden속성으로 숨김 처리logout.php로 이동되어 로그아웃 처리pw_update_process.php)로 이동됨
▶
pw_update_process.php전체 코드
session_start();
// 로그인 확인
if (!isset($_SESSION['username'])) {
header("Location: /project-folder/login.html");
exit();
}
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
if (!$db_conn) {
echo "DB Connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
$username = $_SESSION['username'];
// 사용자 정보 가져오기
$sql = "SELECT * FROM user_info WHERE username='$username'";
$result = mysqli_query($db_conn, $sql);
$user = mysqli_fetch_assoc($result);
// POST 처리
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$current_pw = trim($_POST['current_pw']);
$new_pw = trim($_POST['new_pw']);
if ($current_pw !== $user['pw']) {
echo "<script>alert('현재 비밀번호가 일치하지 않습니다.'); history.back();</script>";
exit();
}
if (empty($new_pw)) {
echo "<script>alert('새 비밀번호를 입력해주세요.'); history.back();</script>";
exit();
}
$stmt = $db_conn->prepare("UPDATE user_info SET pw=? WHERE username=?");
$stmt->bind_param("ss", $new_pw, $username);
$stmt->execute();
$stmt->close();
echo "<script>alert('비밀번호가 성공적으로 변경되었습니다.'); location.href='/project-folder/php/user/mypage.php';</script>";
exit();
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>비밀번호 변경</title>
<link rel="stylesheet" href="/project-folder/css/pw_update_process.css" />
</head>
<body>
<div class="mypage">
<div class="mypage__form">
<h1 class="mypage__title">비밀번호 변경</h1>
<form method="post" class="pw-update-form">
<div class="mypage__userInfo-container">
<div class="pw-row">
<label class="pw-label">현재 비밀번호</label>
<input type="password" name="current_pw" required class="pw-input" placeholder="현재 비밀번호">
</div>
<div class="pw-row">
<label class="pw-label">새 비밀번호</label>
<input type="password" name="new_pw" required class="pw-input" placeholder="새 비밀번호">
</div>
</div>
<button type="submit" class="pw-update-btn">비밀번호 변경</button>
</form>
</div>
</div>
</body>
</html>
✔️ 세션 시작 & 로그인 확인
session_start();
// 로그인 확인
if (!isset($_SESSION['username'])) {
header("Location: /project-folder/login.html");
exit();
}
$_SESSION['username']이 없으면 로그인하지 않은 사용자로 인식하고 로그인페이지(login.html)로 이동됨.
✔️ DB 연결 & 사용자 정보 조회
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
if (!$db_conn) {
echo "DB Connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
$username = $_SESSION['username'];
// 사용자 정보 가져오기
$sql = "SELECT * FROM user_info WHERE username='$username'";
$result = mysqli_query($db_conn, $sql);
$user = mysqli_fetch_assoc($result);
username) 가져옴.SELECT * FROM user_info WHERE username='$username' 쿼리 실행$result)의 첫 번째 row를 $user에 저장.
✔️ POST 요청 처리 (실제 비밀번호 변경)
// POST 처리
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$current_pw = trim($_POST['current_pw']);
$new_pw = trim($_POST['new_pw']);
if ($current_pw !== $user['pw']) {
echo "<script>alert('현재 비밀번호가 일치하지 않습니다.'); history.back();</script>";
exit();
}
if (empty($new_pw)) {
echo "<script>alert('새 비밀번호를 입력해주세요.'); history.back();</script>";
exit();
}
$stmt = $db_conn->prepare("UPDATE user_info SET pw=? WHERE username=?");
$stmt->bind_param("ss", $new_pw, $username);
$stmt->execute();
$stmt->close();
echo "<script>alert('비밀번호가 성공적으로 변경되었습니다.'); location.href='/project-folder/php/user/mypage.php';</script>";
exit();
}
if ($current_pw !== $user['pw']): 사용자가 입력한 현재 비밀번호와 DB의 비밀번호를 비교if (empty($new_pw)): 새로운 비밀번호 값이 비어있는지 체크mypage.php)로 이동됨.
✔️ 새 비밀번호 입력 폼
<div class="mypage">
<div class="mypage__form">
<h1 class="mypage__title">비밀번호 변경</h1>
<form method="post" class="pw-update-form">
<div class="mypage__userInfo-container">
<div class="pw-row">
<label class="pw-label">현재 비밀번호</label>
<input type="password" name="current_pw" required class="pw-input" placeholder="현재 비밀번호">
</div>
<div class="pw-row">
<label class="pw-label">새 비밀번호</label>
<input type="password" name="new_pw" required class="pw-input" placeholder="새 비밀번호">
</div>
</div>
<button type="submit" class="pw-update-btn">비밀번호 변경</button>
</form>
</div>
</div>
POST 요청으로 pw_update_process.php로 폼이 제출됨.🔸 비로그인 상태

🔸 로그인 상태

< 전체 흐름 요약 >
1️⃣ 세션 시작 & 로그인 여부 확인
2️⃣ DB 연결 & 게시글 수 집계
3️⃣ 페이지네이션 계산
4️⃣ 게시글 목록 출력
5️⃣ 검색 폼
6️⃣ 페이지네이션 버튼
7️⃣ 글쓰기 버튼
8️⃣ 로그인 상태별 메뉴
9️⃣ JS로 드롭다운 메뉴 처리
👉
board.php전체 코드
<?php
session_start();
$isLoggedIn = isset($_SESSION['username']);
// 1. DB 접속
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
// 2. DB 접속 확인
if (!$db_conn) {
echo "DB connect Fail<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
// 3. 페이지네이션
$posts_per_page = 5; // 한페이지당 게시글 5개
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$start_from = ($page - 1) * $posts_per_page;
// 4. 전체 게시글 수
$count_sql = "SELECT COUNT(*) AS total FROM board_post";
$count_result = mysqli_query($db_conn, $count_sql);
$total_posts = mysqli_fetch_assoc($count_result)['total'];
$total_pages = ceil($total_posts / $posts_per_page);
// 5. 게시글 목록 쿼리
$board_list_sql = "SELECT * FROM board_post ORDER BY id DESC LIMIT $start_from, $posts_per_page";
$result = mysqli_query($db_conn, $board_list_sql);
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>게시판</title>
<link rel="stylesheet" href="/project-folder/css/board.css" />
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div class="logo">MyWebsite</div>
<nav class="main-nav">
<a href="/project-folder/index.php" class="home">HOME</a>
<?php if (!$isLoggedIn): ?>
<a href="/project-folder/login.html" class="login-btn">Sign in / Sign up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="/project-folder/php/user/mypage.php">마이페이지</a>
<a href="/project-folder/php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
</header>
<main>
<div class="board-wrapper">
<h2 style="margin-bottom: 1rem;">게시판</h2>
<!-- 검색 박스 -->
<div class="search-box">
<form method="get" action="search.php" class="search-form">
<select name="search_type">
<option value="title">제목</option>
<option value="author">작성자</option>
<option value="content">내용</option>
</select>
<input type="text" name="query" placeholder="검색어 입력" />
<button type="submit">검색</button>
</form>
</div>
<!-- 게시글 테이블 -->
<table class="board-table">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회</th>
</tr>
</thead>
<tbody>
<?php
if ($result && mysqli_num_rows($result) > 0):
$virtual_number = $total_posts - $start_from; // 최신순 가상번호 시작
while ($row = mysqli_fetch_assoc($result)):
?>
<tr>
<td><?= $virtual_number ?></td>
<td>
<a href="post.php?id=<?= $row['id'] ?>&view=<?= $virtual_number ?>">
<?= htmlspecialchars($row['title']) ?>
</a>
</td>
<td><?= htmlspecialchars($row['author']) ?></td>
<td><?= $row['create_date'] ?></td>
<td><?= $row['view_count'] ?></td>
</tr>
<?php
$virtual_number--;
endwhile;
else:
?>
<tr><td colspan="5">게시글이 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
<!-- 페이지네이션 -->
<div class="pagination">
<a href="board.php?page=1">처음</a>
<a href="board.php?page=<?= max(1, $page - 1) ?>">이전</a>
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<?php if ($i == $page): ?>
<strong><?= $i ?></strong>
<?php else: ?>
<a href="board.php?page=<?= $i ?>"><?= $i ?></a>
<?php endif; ?>
<?php endfor; ?>
<a href="board.php?page=<?= min($total_pages, $page + 1) ?>">다음</a>
<a href="board.php?page=<?= $total_pages ?>">끝</a>
</div>
<a href="write.php" class="write-btn">글쓰기</a>
</div>
</main>
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
</script>
</body>
</html>
✔️ 세션 시작 & 로그인 여부 확인
session_start();
$isLoggedIn = isset($_SESSION['username']);
$_SESSION['username']를 통해 로그인 여부 확인
✔️ DB 연결
// 1. DB 접속
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
// 2. DB 접속 확인
if (!$db_conn) {
echo "DB connect Fail<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
✔️ 페이지네이션 계산
// 3. 페이지네이션
$posts_per_page = 5; //한 페이지당 글 5개 게시
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$start_from = ($page - 1) * $posts_per_page;
// 4. 전체 게시글 수
$count_sql = "SELECT COUNT(*) AS total FROM board_post";
$count_result = mysqli_query($db_conn, $count_sql);
$total_posts = mysqli_fetch_assoc($count_result)['total'];
$total_pages = ceil($total_posts / $posts_per_page);
$_GET['page'])에 값이 없으면 디폴트로 1페이지로 표현됨.$start_from : LIMIT의 OFFSET 역할ceil은 x(값)보다 크거나 같은 정수로 올림한다!🕵️♀️ 페이지네이션 상세설명
💡)LIMIT과OFFSET
•LIMIT은 SQL에서 결과 행(row)의 개수를 제한할 때 사용
예시 1)users테이블에서 상위 5명의 이름을 가져오고 싶다면?
→SELECT name FROM users LIMIT 5;
→users테이블에 있는 row 중 앞에서부터 5개만 가져옴.
예시 2)LIMIT과OFFSET함께 사용
→SELECT name FROM users LIMIT 5 OFFSET 10;
→ OFFSET 10이니까 처음 10개를 건너뜀 (=11번째 부터!)
→ LIMIT 5이니까 11번째부터 5개를 가져옴
→ 즉, 11번째부터 15번째까지의 데이터를 가져옴 !
➕)LIMIT 시치, 개수와LIMIT 개수 OFFSET 시작 위치동일!
↳LIMIT 10, 5
↳LIMIT 5 OFFESET 10
❓) 페이지네이션에서 OFFSET의 역할
•$start_from = ($page - 1) * $posts_per_page;
↳ LIMIT의 OFFSET 역할을 하기 위해 계산하는 것이다 !
❓) 왜($page - 1) * $posts_per_page야?
한 페이지당 5개의 게시글이 리스팅됨 !
그래서 페이지가 올라갈수록($page-1)을 곱해줘야 정확한 시작위치를 구할 수 있다.
💡 쿼리에서의 사용SELECT * FROM board_post ORDER BY id DESC LIMIT $start_from, $posts_per_page
$start_from: OFFEST$posts_per_page: 가져올 게시글 개수
예시 1) 페이지 당 5개 글 게시, 지금 3페이지라면,
$start_from = (3-1) * 5 = 10
→ 111번째 글부터 15번째 글까지 가져온다.
✔️ 게시글 목록 출력 쿼리
// 5. 게시글 목록 쿼리
$board_list_sql = "SELECT * FROM board_post ORDER BY id DESC LIMIT $start_from, $posts_per_page";
$result = mysqli_query($db_conn, $board_list_sql);
ORDER BY id DESC: 최신 글부터 내림차순 정렬
✔️ 게시글 목록 출력 HTML
<table class="board-table">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회</th>
</tr>
</thead>
<tbody>
<?php
if ($result && mysqli_num_rows($result) > 0):
$virtual_number = $total_posts - $start_from; // 최신순 가상번호 시작
while ($row = mysqli_fetch_assoc($result)):
?>
<tr>
<td><?= $virtual_number ?></td>
<td>
<a href="post.php?id=<?= $row['id'] ?>&view=<?= $virtual_number ?>">
<?= htmlspecialchars($row['title']) ?>
</a>
</td>
<td><?= htmlspecialchars($row['author']) ?></td>
<td><?= $row['create_date'] ?></td>
<td><?= $row['view_count'] ?></td>
</tr>
<?php
$virtual_number--;
endwhile;
else:
?>
<tr><td colspan="5">게시글이 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
if ($result && mysqli_num_rows($result) > 0):$virtual_number = $total_posts - $start_from;총 글 개수 - OFFSET으로 가상번호 시작점 계산while ($row = mysqli_fetch_assoc($result)):$virtual_number--;
🕵️♀️ 가상번호(
$virtual_number) 사용하는 이유 ?
🅰️ ) DB에서는id가 auto_increment 자동 증가이기 때문에,
글을 삭제하면id가 연속적이지 못하게 된다.
✔️ 검색 폼
<!-- 검색 박스 -->
<div class="search-box">
<form method="get" action="search.php" class="search-form">
<select name="search_type">
<option value="title">제목</option>
<option value="author">작성자</option>
<option value="content">내용</option>
</select>
<input type="text" name="query" placeholder="검색어 입력" />
<button type="submit">검색</button>
</form>
</div>
GET방식으로 search.php로 요청 보냄
✔️ 페이지네이션
<!-- 페이지네이션 -->
<div class="pagination">
<a href="board.php?page=1">처음</a>
<a href="board.php?page=<?= max(1, $page - 1) ?>">이전</a>
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<?php if ($i == $page): ?>
<strong><?= $i ?></strong>
<?php else: ?>
<a href="board.php?page=<?= $i ?>"><?= $i ?></a>
<?php endif; ?>
<?php endfor; ?>
<a href="board.php?page=<?= min($total_pages, $page + 1) ?>">다음</a>
<a href="board.php?page=<?= $total_pages ?>">끝</a>
</div>
<a href="write.php" class="write-btn">글쓰기</a>
</div>
board.php?page=페이지번호로 요청이 전송됨.
✔️ 글쓰기 버튼
<a href="write.php" class="write-btn">글쓰기</a>
write.php로 이동됨.
✔️ 로그인 상태별 메뉴 & 드롭다운
<nav class="main-nav">
<a href="/project-folder/index.php" class="home">HOME</a>
<?php if (!$isLoggedIn): ?>
<a href="/project-folder/login.html" class="login-btn">Sign in / Sign
up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="/project-folder/php/user/mypage.php">마이페이지</a>
<a href="/project-folder/php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
✔️ 드롭다운 JS
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
</script>
🔸로그인 상태 + 검색 결과 존재 X

🔸로그인 상태 + 검색 결과 존재 O

🔸비로그인 상태 + 검색 결과 존재 X

🔸비로그인 상태 + 검색 결과 존재 O

👉
search.php전체 코드
<?php
session_start();
$isLoggedIn = isset($_SESSION['username']);
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
if (!$db_conn) {
echo "DB 접속 실패: " . mysqli_connect_error();
exit();
}
// 검색 파라미터
$search_type = isset($_GET['search_type']) ? $_GET['search_type'] : '';
$query = isset($_GET['query']) ? trim($_GET['query']) : '';
// 허용된 검색 필드 확인
$allowed_fields = ['title', 'author', 'content'];
if (!in_array($search_type, $allowed_fields)) {
echo "잘못된 검색 조건입니다.";
exit();
}
// SQL 검색 실행
$escaped_query = mysqli_real_escape_string($db_conn, $query);
$sql = "SELECT * FROM board_post WHERE $search_type LIKE '%$escaped_query%' ORDER BY id DESC";
$result = mysqli_query($db_conn, $sql);
// 검색 결과 총 개수로 가상번호 시작값 설정
$total_results = $result ? mysqli_num_rows($result) : 0;
$virtual_number = $total_results;
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>검색 결과</title>
<link rel="stylesheet" href="/project-folder/css/search.css" />
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div class="logo">MyWebsite</div>
<nav class="main-nav">
<a href="/project-folder/index.php" class="home">HOME</a>
<?php if (!$isLoggedIn): ?>
<a href="/project-folder/login.html" class="login-btn">Sign in / Sign up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="/project-folder/php/user/mypage.php">마이페이지</a>
<a href="/project-folder/php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
</header>
<main>
<div class="board-wrapper">
<h2>검색 결과: "<?= htmlspecialchars($query) ?>"</h2>
<table class="board-table">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회</th>
</tr>
</thead>
<tbody>
<?php if ($result && mysqli_num_rows($result) > 0): ?>
<?php while ($row = mysqli_fetch_assoc($result)): ?>
<tr>
<td><?= $virtual_number ?></td>
<td>
<a href="post.php?id=<?= $row['id'] ?>">
<?= htmlspecialchars($row['title']) ?>
</a>
</td>
<td><?= htmlspecialchars($row['author']) ?></td>
<td><?= $row['create_date'] ?></td>
<td><?= $row['view_count'] ?></td>
</tr>
<?php $virtual_number--; ?>
<?php endwhile; ?>
<?php else: ?>
<tr><td colspan="5">검색 결과가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
<a href="board.php" class="back-button">← 게시판으로</a>
</div>
</main>
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
</script>
</body>
</html>
< 전체 흐름 요약 >
1️⃣ 세션 시작 & 로그인 여부 확인
2️⃣ DB 연결
3️⃣ 검색 조건 가져오기 (GET)
4️⃣ 검색 타입 검증 (title, author, content만 허용)
5️⃣ SQL LIKE 검색 실행
6️⃣ 검색 결과 테이블 출력
7️⃣ 로그인 상태에 따라 상단 메뉴 출력
8️⃣ 드롭다운 JS
✔️ 검색 조건 가져오기 (GET)
// 검색 파라미터
$search_type = isset($_GET['search_type']) ? $_GET['search_type'] : '';
$query = isset($_GET['query']) ? trim($_GET['query']) : '';
$search_type: 검색 옵션 (title, author, content)$query: 사용자가 입력한 검색어
✔️ 허용된 검색 옵션(타입)인지 체크
$allowed_fields = ['title', 'author', 'content'];
if (!in_array($search_type, $allowed_fields)) {
echo "잘못된 검색 조건입니다.";
exit();
title, content, author 외의 옵션은 차단.
✔️ SQL LIKE 검색 실행
// SQL 검색 실행
$escaped_query = mysqli_real_escape_string($db_conn, $query);
$sql = "SELECT * FROM board_post WHERE $search_type LIKE '%$escaped_query%' ORDER BY id DESC";
$result = mysqli_query($db_conn, $sql);
LIKE '%$escaped_query%'를 통해 일치되는 게시글 검색
✔️ HTML 부분
<div class="board-wrapper">
<h2>검색 결과: "<?= htmlspecialchars($query) ?>"</h2>
<table class="board-table">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회</th>
</tr>
</thead>
<tbody>
<?php if ($result && mysqli_num_rows($result) > 0): ?>
<?php while ($row = mysqli_fetch_assoc($result)): ?>
<tr>
<td><?= $virtual_number ?></td>
<td>
<a href="post.php?id=<?= $row['id'] ?>">
<?= htmlspecialchars($row['title']) ?>
</a>
</td>
<td><?= htmlspecialchars($row['author']) ?></td>
<td><?= $row['create_date'] ?></td>
<td><?= $row['view_count'] ?></td>
</tr>
<?php $virtual_number--; ?>
<?php endwhile; ?>
<?php else: ?>
<tr><td colspan="5">검색 결과가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
<a href="board.php" class="back-button">← 게시판으로</a>
</div>
✔️ 드롭다운 JS
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
</script>
🔸로그인 상태

🔸비로그인 상태

<전체 흐름 요약>
1️⃣ 세션 시작 & 로그인 상태 확인
2️⃣ DB 연결
3️⃣ 글쓰기 폼 (제목, 내용, 첨부파일)
4️⃣ 파일 선택 & 선택된 파일 삭제 (JavaScript)
5️⃣ 로그인 상태에 따른 네비게이션 메뉴
6️⃣ 글 작성 후 처리 로직은 `write_process.php`로 POST
👉
write.php전체 코드
<?php
session_start();
$isLoggedIn = isset($_SESSION['username']);
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
// 접속 실패 시
if (!$db_conn) {
echo "DB connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>게시판 글쓰기</title>
<link rel="stylesheet" href="/project-folder/css/write.css" />
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div class="logo">MyWebsite</div>
<nav class="main-nav">
<a href="/project-folder/index.php" class="home">HOME</a>
<?php if (!$isLoggedIn): ?>
<a href="/project-folder/login.html" class="login-btn">Sign in / Sign up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="/project-folder/php/user/mypage.php">마이페이지</a>
<a href="/project-folder/php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
</header>
<main>
<div class="write-form-container">
<h2>게시판 글쓰기</h2>
<form method="post" action="write_process.php" enctype="multipart/form-data">
<table>
<tr>
<td class="gray-cell">제목<span class="required">*</span></td>
<td><input type="text" name="title" required></td>
</tr>
<tr>
<td class="gray-cell">글 내용<span class="required">*</span></td>
<td><textarea name="content" required></textarea></td>
</tr>
<tr>
<td class="gray-cell">첨부파일</td>
<td>
<!-- 새 첨부파일 업로드 -->
<input type="file" id="fileInput" name="files[]" multiple style="display:none">
<button type="button" onclick="document.getElementById('fileInput').click()">가져오기</button>
<button type="button" onclick="removeSelectedFiles()">선택된 파일 삭제</button>
<div id="fileList"></div>
</td>
</tr>
</table>
<div class="write-buttons">
<button type="submit">CREATE</button>
<button type="button" onclick="location.href='board.php'">BACK</button>
</div>
</form>
</div>
</main>
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
// 첨부파일 미리보기 + 선택삭제
const fileInput = document.getElementById('fileInput');
const fileListDiv = document.getElementById('fileList');
fileInput.addEventListener('change', () => {
fileListDiv.innerHTML = '';
for (let i = 0; i < fileInput.files.length; i++) {
const file = fileInput.files[i];
const fileDiv = document.createElement('div');
fileDiv.innerHTML = `
<label>
<input type="checkbox" class="file-checkbox" data-index="${i}">
${file.name}
</label>`;
fileListDiv.appendChild(fileDiv);
}
});
function removeSelectedFiles() {
const checkboxes = document.querySelectorAll('.file-checkbox');
const dt = new DataTransfer();
const files = fileInput.files;
checkboxes.forEach(cb => {
const fileDiv = cb.closest('div');
if (cb.checked && fileDiv) {
fileDiv.remove();
}
});
for (let i = 0; i < files.length; i++) {
if (![...checkboxes].some(cb => cb.dataset.index == i && cb.checked)) {
dt.items.add(files[i]);
}
}
fileInput.files = dt.files;
fileInput.dispatchEvent(new Event('change'));
}
</script>
</body>
</html>
✔️ 글쓰기 폼 (제목, 내용, 첨부파일)
<form method="post" action="write_process.php" enctype="multipart/form-data">
<table>
<tr>
<td class="gray-cell">제목<span class="required">*</span></td>
<td><input type="text" name="title" required></td>
</tr>
<tr>
<td class="gray-cell">글 내용<span class="required">*</span></td>
<td><textarea name="content" required></textarea></td>
</tr>
<tr>
<td class="gray-cell">첨부파일</td>
<td>
<!-- 새 첨부파일 업로드 -->
<input type="file" id="fileInput" name="files[]" multiple style="display:none">
<button type="button" onclick="document.getElementById('fileInput').click()">가져오기</button>
<button type="button" onclick="removeSelectedFiles()">선택된 파일 삭제</button>
<div id="fileList"></div>
</td>
</tr>
</table>
<div class="write-buttons">
<button type="submit">CREATE</button>
<button type="button" onclick="location.href='board.php'">BACK</button>
</div>
</form>
write_process.php로 POST 요청을 보냄
✔️ 파일 미리보기 & 선택된 파일 삭제 (JavaScript)
// <첨부파일 미리보기 + 선택삭제>
// 1. 주요 변수
// fileInput: 숨겨진 <input type="file"> 요소
// fileListDiv: 선택된 파일 목록을 보여줄 div
const fileInput = document.getElementById('fileInput');
const fileListDiv = document.getElementById('fileList');
// 2. 파일 선택 시 목록 렌더링
fileInput.addEventListener('change', () => {
fileListDiv.innerHTML = ''; //기존 목록 초기화
for (let i = 0; i < fileInput.files.length; i++) {
const file = fileInput.files[i];
const fileDiv = document.createElement('div');
fileDiv.innerHTML = `
<label>
<input type="checkbox" class="file-checkbox" data-index="${i}">
${file.name}
</label>`;
fileListDiv.appendChild(fileDiv);
}
});
//3. 선택된 파일 삭제 함수 : removeSelectedFile()
function removeSelectedFiles() {
const checkboxes = document.querySelectorAll('.file-checkbox');
const dt = new DataTransfer(); // 새로운 FileList 역할
const files = fileInput.files;
// 3-1. 체크된 항목은 DOM에서 제거
checkboxes.forEach(cb => {
const fileDiv = cb.closest('div');
if (cb.checked && fileDiv) {
fileDiv.remove();
}
});
// 3-2. 선택 해제된 파일만 다시 추가
for (let i = 0; i < files.length; i++) {
if (![...checkboxes].some(cb => cb.dataset.index == i && cb.checked)) {
dt.items.add(files[i]);
}
}
// 3-3. input의 파일목록을 새로만든 FileList로 교체
fileInput.files = dt.files;
// 3-4. 변경된 목록에 맞게 미리보기 갱신
fileInput.dispatchEvent(new Event('change'));
}
<input type="file" multiple>로 파일 선택removeSelectedFiles()를 호출하면 체크된 파일만 목록에서 제거<input>에 반영함
✔️ 드롭다운 메뉴 JS
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
➡️ write.php의 글쓰기 폼에서 제출된 데이터를 처리하고,
DB에 저장하여 게시글 업로드를 처리해줌.
👉
write_process.php전체 코드
<?php
session_start();
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
// 로그인한 사용자 확인
$author = $_SESSION['username'] ?? null;
if (!$author) {
echo "<script>alert('로그인 후 작성 가능합니다.'); history.back();</script>";
exit();
}
// 입력값 처리
$title = trim($_POST['title']);
$content = trim($_POST['content']);
if (empty($title) || empty($content)) {
echo "<script>alert('제목과 내용을 입력해주세요.'); history.back();</script>";
exit();
}
// 글 INSERT
$stmt = $db_conn->prepare("INSERT INTO board_post (title, content, author) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $title, $content, $author);
$stmt->execute();
$post_id = $stmt->insert_id;
$stmt->close();
// 파일 업로드
$upload_dir = __DIR__ . "/../../uploads/";
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
if (!empty($_FILES['files']['name'][0])) {
foreach ($_FILES['files']['name'] as $key => $file_name) {
$tmp_name = $_FILES['files']['tmp_name'][$key];
if ($_FILES['files']['error'][$key] === UPLOAD_ERR_OK) {
$unique_name = uniqid() . "_" . basename($file_name);
$destination = $upload_dir . $unique_name;
if (move_uploaded_file($tmp_name, $destination)) {
$save_path = "/project-folder/uploads/" . $unique_name;
$stmt = $db_conn->prepare("INSERT INTO attachment (post_id, file_name, file_path)
VALUES (?, ?, ?)");
$stmt->bind_param("iss", $post_id, $file_name, $save_path);
$stmt->execute();
$stmt->close();
}
}
}
}
echo "<script>
alert('게시글이 업로드되었습니다!');
location.href = 'board.php';
</script>";
exit();
?>
< 전체 동작 흐름 요약 >
1️⃣ 세션 시작 → 로그인 사용자 확인
2️⃣ DB 연결
3️⃣ `$_POST`로 제목과 내용 받기 → 입력값 검증
4️⃣ `board_post` 테이블에 게시글 INSERT
5️⃣ 첨부파일 있으면 `/uploads/` 폴더로 이동 → `attachment` 테이블에 파일 정보 저장
6️⃣ 성공 시 `게시글이 업로드되었습니다!` alert 후 `board.php`로 이동
✔️ 제목, 내용을 POST로 가져오기
// 입력값 처리
$title = trim($_POST['title']);
$content = trim($_POST['content']);
if (empty($title) || empty($content)) {
echo "<script>alert('제목과 내용을 입력해주세요.'); history.back();</script>";
exit();
}
title, content
✔️board_post 테이블에 게시글 INSERT
// 글 INSERT
$stmt = $db_conn->prepare("INSERT INTO board_post (title, content, author) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $title, $content, $author);
$stmt->execute();
$post_id = $stmt->insert_id; // 새로 들어간 글의 id를 가져옴
$stmt->close();
board_post테이블에 전송 받은 title, content, author을 저장$stmt->insert_id 는 MySQL 서버가 방금 INSERT한 마지막 AUTO_INCREMENT 값을 가져옴! (즉, 새 글의 id를 $post_id 변수에 저장)
✔️ board_post 테이블 (게시판 테이블) 구조

id는 primary key로 설정하고 auto_increment(자동 증가)
✔️ attachcment 테이블(첨부파일 테이블) 구조

id는 primary key, post_id는 foreign keypost_id를 통해 해당 첨부파일이 어떤 게시글에 속해 있는지를 알 수 있다.post_id = 3 이면, 게시판 테이블(board_post)의 id가 3인 게시글에 연결되어 있다는 것을 알 수 있다.
✔️ 첨부파일 업로드 처리
// 파일 업로드
$upload_dir = __DIR__ . "/../../uploads/";
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
if (!empty($_FILES['files']['name'][0])) {
foreach ($_FILES['files']['name'] as $key => $file_name) {
$tmp_name = $_FILES['files']['tmp_name'][$key];
if ($_FILES['files']['error'][$key] === UPLOAD_ERR_OK) {
$unique_name = uniqid() . "_" . basename($file_name);
$destination = $upload_dir . $unique_name;
if (move_uploaded_file($tmp_name, $destination)) {
$save_path = "/project-folder/uploads/" . $unique_name;
$stmt = $db_conn->prepare("INSERT INTO attachment (post_id, file_name, file_path)
VALUES (?, ?, ?)");
$stmt->bind_param("iss", $post_id, $file_name, $save_path);
$stmt->execute();
$stmt->close();
}
}
}
}
if (!empty($_FILES['files']['name'][0]))$_FILES['files']['name']가 비어있는지 비어있지 않는지, 즉, 사용자가 첨부파일을 업로드했는지 확인함.foreach로 여러 파일 반복 처리$key는 파일의 인덱스(0부터 시작)$file_name은 원본 파일명$tmp_name은 서버에 임시 저장된 파일 경로$_FILES배열$_FILES['files']['name'] = ['a.png', 'b.png']
$_FILES['files']['tmp_name'] = ['/tmp/php1234', '/tmp/php5678']
$_FILES['files']['error'] = [0, 0]
UPLOAD_ERR_OK 확인UPLOAD_ERR_OK는 PHP의 상수로 값이 0 → 정상 업로드$unique_name = uniqid() . "_" . basename($file_name);
$destination = $upload_dir . $unique_name;
if (move_uploaded_file($tmp_name, $destination)) {
uniquid()로 유니크한 ID를 붙임/uploads/폴더로 이동시킴move_uploades_file()이 true면 파일 이동 성공
✔️ 첨부파일 정보를 attaachment에 테이블에 저장
$save_path = "/project-folder/uploads/" . $unique_name;
$stmt = $db_conn->prepare("INSERT INTO attachment (post_id, file_name, file_path)
VALUES (?, ?, ?)");
$stmt->bind_param("iss", $post_id, $file_name, $save_path);
$stmt->execute();
$stmt->close();
attatchment 테이블에 파일 정보 저장post_id: 이 파일이 어떤 게시글에 속하는지file_name: 원본 파일명fiel_path: 실제 파일이 저장된 서버 경로
✔️ 업로드 완료 알림과 리다이렉션
echo "<script>
alert('게시글이 업로드되었습니다!');
location.href = 'board.php';
</script>";
exit();
board.php)로 이동됨
👉
post.php전체 코드
<?php
session_start();
$isLoggedIn = isset($_SESSION['username']);
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
if (!$db_conn) {
echo "DB Connect Fail!<br>";
echo "에러 원인: " . mysqli_connect_error();
exit();
}
// 시간 표시 함수
function timeAgo($datetime) {
$time = strtotime($datetime);
$diff = time() - $time;
if ($diff < 60) return $diff . "초 전";
elseif ($diff < 3600) return floor($diff / 60) . "분 전";
elseif ($diff < 86400) return floor($diff / 3600) . "시간 전";
else return date('Y-m-d H:i', $time);
}
// 파라미터
$post_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
// 조회수 증가
$update_sql = "UPDATE board_post SET view_count = view_count + 1 WHERE id = $post_id";
mysqli_query($db_conn, $update_sql);
// 게시글 조회
$sql = "SELECT * FROM board_post WHERE id = $post_id";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);
if (!$post) {
echo "<script>alert('존재하지 않는 게시글입니다.'); history.back();</script>";
exit();
}
// 첨부파일
$attachment_sql = "SELECT * FROM attachment WHERE post_id = $post_id";
$attachment_result = mysqli_query($db_conn, $attachment_sql);
// [서버단] 댓글 작성 처리
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['submit_comment'])) {
if (!$isLoggedIn) {
echo "<script>alert('로그인 후 작성 가능합니다.'); history.back();</script>";
exit();
}
$author = $_SESSION['username'];
$comment_text = mysqli_real_escape_string($db_conn, $_POST['comment_text']); $insert_sql = "INSERT INTO comment (post_id, comment_text, author) VALUES ('$post_id', '$comment_text', '$author')";
mysqli_query($db_conn, $insert_sql);
header("Location: post.php?id=$post_id");
exit();
}
// 댓글 목록
$comment_list_sql = "SELECT * FROM comment WHERE post_id = $post_id ORDER BY create_date ASC";
$comment_list_result = mysqli_query($db_conn, $comment_list_sql);
$comment_count = mysqli_num_rows($comment_list_result);
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>게시글 보기</title>
<link rel="stylesheet" href="/project-folder/css/view.css" />
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div class="logo">MyWebsite</div>
<nav class="main-nav">
<a href="/project-folder/index.php" class="home">HOME</a>
<?php if (!$isLoggedIn): ?>
<a href="/project-folder/login.html" class="login-btn">Sign in / Sign up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="/project-folder/php/user/mypage.php">마이페이지</a>
<a href="/project-folder/php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
</header>
<main>
<h2 style="margin: 2rem;">게시판</h2>
<div class="post-box">
<div class="post-header"><?= htmlspecialchars($post['title']) ?></div>
<div class="post-meta">
<div class="meta-left">
작성자 | <?= htmlspecialchars($post['author']) ?>
작성일 | <?= htmlspecialchars($post['create_date']) ?>
조회 | <?= htmlspecialchars($post['view_count']) ?>
</div>
<?php if ($isLoggedIn && $_SESSION['username'] === $post['author']): ?>
<div class="meta-right">
<a href="post_update.php?id=<?= $post['id'] ?>">수정</a>
<a href="post_delete.php?id=<?= $post['id'] ?>">삭제</a>
</div>
<?php endif; ?>
</div>
<div class="post-content">
<?= $post['content'] ?>
</div>
<?php if (mysqli_num_rows($attachment_result) > 0): ?>
<div class="attachment-box">
<span class="attachment-title">
<i class="fa-solid fa-paperclip"></i> 첨부파일
</span>
<ul class="attachment-list">
<?php $i = 1; ?>
<?php while ($file = mysqli_fetch_assoc($attachment_result)): ?>
<li>
<?= $i ?>. <a href="<?= htmlspecialchars($file['file_path']) ?>" download>
<?= htmlspecialchars($file['file_name']) ?>
</a>
</li>
<?php $i++; ?>
<?php endwhile; ?>
</ul>
</div>
<?php endif; ?>
</div>
<a href="board.php" class="back-button">목록</a>
<div class="comment-box">
<h3><?= $comment_count ?>개의 댓글</h3>
<form method="post" class="comment-form">
<textarea name="comment_text" placeholder="댓글을 작성하세요" required></textarea>
<input type="hidden" name="post_id" value="<?= $post_id ?>">
<button type="submit" name="submit_comment">댓글 작성</button>
</form>
<?php while ($comment = mysqli_fetch_assoc($comment_list_result)): ?>
<div class="comment-item">
<div class="comment-author"><?= htmlspecialchars($comment['author']) ?></div>
<div class="comment-time"><?= timeAgo($comment['create_date']) ?></div>
<div class="comment-text"><?= nl2br(htmlspecialchars($comment['comment_text'])) ?></div>
</div>
<?php endwhile; ?>
</div>
</main>
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
</script>
</body>
</html>
✔️ 조회수 증가 처리
// 파라미터
$post_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
// 조회수 증가
$update_sql = "UPDATE board_post SET view_count = view_count + 1 WHERE id = $post_id";
mysqli_query($db_conn, $update_sql);
?id=숫자 파라미터로 어떤 게시글인지 식별view_count를 +1 증가시킴
✔️ 게시글 데이터 가져오기
// 게시글 조회
$sql = "SELECT * FROM board_post WHERE id = $post_id";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);
if (!$post) {
echo "<script>alert('존재하지 않는 게시글입니다.'); history.back();</script>";
exit();
}
✔️ 첨부파일 가져오기
$attachment_sql = "SELECT * FROM attachment WHERE post_id = $post_id";
$attachment_result = mysqli_query($db_conn, $attachment_sql);
attachment테이블에서 현재 게시글에 연결된 첨부파일들을 가져옴.
✔️ 댓글 작성 처리 (서버단)
// [서버단] 댓글 작성 처리
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['submit_comment']))
{
if (!$isLoggedIn) {
echo "<script>alert('로그인 후 작성 가능합니다.'); history.back();</script>";
exit();
}
$author = $_SESSION['username'];
$comment_text = mysqli_real_escape_string($db_conn, $_POST['comment_text']);
$insert_sql = "INSERT INTO comment (post_id, comment_text, author) VALUES ('$post_id', '$comment_text', '$author')";
mysqli_query($db_conn, $insert_sql);
header("Location: post.php?id=$post_id");
exit();
comment 테이블에 현재 게시글의 post_id와 함께 댓글 내용/작성자 저장.post.php?id=게시글번호로 리다이렉트해서 새로고침.
✔️ 댓글 목록 가져오기
$comment_list_sql = "SELECT * FROM comment WHERE post_id = $post_id ORDER BY create_date ASC";
$comment_list_result = mysqli_query($db_conn, $comment_list_sql);
$comment_count = mysqli_num_rows($comment_list_result);
✔️ comment 테이블 구조

id는 primary keypost_id는 foreign key👉
post_delete.php전체 코드
<?php
session_start();
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
// 로그인 확인
$author = $_SESSION['username'] ?? null;
if (!$author) {
echo "<script>alert('로그인 후 이용하세요.'); history.back();</script>";
exit();
}
// 삭제할 게시글 ID 가져오기
$post_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
// 게시글 존재 여부 확인 + 작성자 권한 확인
$sql = "SELECT * FROM board_post WHERE id = $post_id";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);
if (!$post) {
echo "<script>alert('존재하지 않는 게시글입니다.'); history.back();</script>";
exit();
}
if ($post['author'] !== $author) {
echo "<script>alert('삭제 권한이 없습니다.'); history.back();</script>";
exit();
}
// 첨부파일 실제 파일 삭제
$att_sql = "SELECT * FROM attachment WHERE post_id = $post_id";
$att_result = mysqli_query($db_conn, $att_sql);
while ($file = mysqli_fetch_assoc($att_result)) {
$file_path = $_SERVER['DOCUMENT_ROOT'] . $file['file_path'];
if (file_exists($file_path)) {
unlink($file_path);
}
}
// attachment 테이블에서 레코드 삭제 (FK ON DELETE CASCADE면 생략 가능)
mysqli_query($db_conn, "DELETE FROM attachment WHERE post_id = $post_id");
// 게시글 삭제
mysqli_query($db_conn, "DELETE FROM board_post WHERE id = $post_id");
// 게시판으로 이동
echo "<script>alert('게시글이 삭제되었습니다.'); location.href='board.php';</script>";
exit();
?>
<전체 흐름 요약>
1. 세션 시작
2. DB 연결
3. 게시글 존재/권한 체크
4. 첨부파일 삭제
5. attachment 테이블 삭제
6. 게시글 삭제
7. 완료 후 리다이렉트
✔️ 로그인 확인
session_start();
// 로그인 확인
$author = $_SESSION['username'] ?? null;
if (!$author) {
echo "<script>alert('로그인 후 이용하세요.'); history.back();</script>";
exit();
}
✔️ 삭제할 게시글 ID 가져오기
// 삭제할 게시글 ID 가져오기
$post_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
?id=숫자로 삭제할 게시글 id전달됨
✔️ 게시글 존재 여부 & 작성자 권한 체크
// 게시글 존재 여부 확인 + 작성자 권한 확인
$sql = "SELECT * FROM board_post WHERE id = $post_id";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);
if (!$post) {
echo "<script>alert('존재하지 않는 게시글입니다.'); history.back();</script>";
exit();
}
if ($post['author'] !== $author) {
echo "<script>alert('삭제 권한이 없습니다.'); history.back();</script>";
exit();
}
$author)가 작성자 ($post['author'])와 다르면 삭제 불가
✔️ 첨부파일 삭제
// 첨부파일 실제 파일 삭제
$att_sql = "SELECT * FROM attachment WHERE post_id = $post_id";
$att_result = mysqli_query($db_conn, $att_sql);
while ($file = mysqli_fetch_assoc($att_result)) {
$file_path = $_SERVER['DOCUMENT_ROOT'] . $file['file_path'];
if (file_exists($file_path)) {
unlink($file_path);
}
}
if (file_exists($file_path))실제 서버 디렉토리(/uploads/)에 저장된 파일 경로를 찾음unlink($file_path);로 파일 삭제
✔️ 첨부파일 테이블(attachment테이블)**에서 레코드 삭제
mysqli_query($db_conn, "DELETE FROM attachment WHERE post_id = $post_id");
attachment테이블 레코드도 삭제해줌
✔️ 게시글 삭제
mysqli_query($db_conn, "DELETE FROM board_post WHERE id = $post_id");
echo "<script>alert('게시글이 삭제되었습니다.'); location.href='board.php';</script>";
exit();
board.php)로 리다이렉션됨.
👉
post_update.php전체 코드
<?php
session_start();
$isLoggedIn = isset($_SESSION['username']);
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
$post_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$sql = "SELECT * FROM board_post WHERE id = $post_id";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);
if (!$post) {
echo "<script>alert('존재하지 않는 게시글입니다.'); history.back();</script>";
exit();
}
if ($_SESSION['username'] !== $post['author']) {
echo "<script>alert('권한이 없습니다.'); history.back();</script>";
exit();
}
$attachments = [];
$att_sql = "SELECT * FROM attachment WHERE post_id = $post_id";
$att_result = mysqli_query($db_conn, $att_sql);
while ($file = mysqli_fetch_assoc($att_result)) {
$attachments[] = $file;
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>게시글 수정</title>
<link rel="stylesheet" href="/project-folder/css/write.css" />
<script src="https://kit.fontawesome.com/242c7614be.js" crossorigin="anonymous"></script>
</head>
<body>
<header>
<div class="logo">MyWebsite</div>
<nav class="main-nav">
<a href="/project-folder/index.php" class="home">HOME</a>
<?php if (!$isLoggedIn): ?>
<a href="/project-folder/login.html" class="login-btn">Sign in / Sign up</a>
<?php else: ?>
<div class="user-icon-wrapper">
<div class="user-icon" onclick="toggleDropdown()">
<i class="fa-solid fa-user U-icon" style="color: #4ad395;"></i>
</div>
<div class="dropdown-menu" id="dropdown">
<a href="/project-folder/php/user/mypage.php">마이페이지</a>
<a href="/project-folder/php/auth/logout.php">로그아웃</a>
</div>
</div>
<?php endif; ?>
</nav>
</header>
<main>
<div class="write-form-container">
<h2>게시글 수정하기</h2>
<form method="post" action="post_update_process.php" enctype="multipart/form-data">
<input type="hidden" name="post_id" value="<?= $post_id ?>">
<table>
<tr>
<td class="gray-cell">제목<span class="required">*</span></td>
<td><input type="text" name="title" value="<?= htmlspecialchars($post['title']) ?>" required></td>
</tr>
<tr>
<td class="gray-cell">글 내용<span class="required">*</span></td>
<td><textarea name="content" required><?= $post['content']) ?></textarea></td>
</tr>
<tr>
<td class="gray-cell">첨부파일</td>
<td>
<?php if (!empty($attachments)): ?>
<div>
<p>기존 첨부파일</p>
<?php foreach ($attachments as $file): ?>
<div class="old-file">
<label>
<input type="checkbox" class="delete-old-checkbox" name="delete_files[]" value="<?= $file['id'] ?>">
<?= htmlspecialchars($file['file_name']) ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- 새 첨부파일 추가 -->
<input type="file" id="fileInput" name="files[]" multiple style="display:none">
<button type="button" onclick="document.getElementById('fileInput').click()">가져오기</button>
<button type="button" onclick="removeSelectedFiles()">선택된 파일 삭제</button>
<div id="fileList"></div>
</td>
</tr>
</table>
<div class="write-buttons">
<button type="submit">UPDATE</button>
<button type="button" onclick="location.href='post.php?id=<?= $post_id ?>'">BACK</button>
</div>
</form>
</div>
</main>
<script>
function toggleDropdown() {
const dropdown = document.getElementById('dropdown');
dropdown.classList.toggle('show');
}
window.addEventListener("click", function(e) {
const dropdown = document.getElementById("dropdown");
const wrapper = document.querySelector(".user-icon-wrapper");
if (!wrapper.contains(e.target)) {
dropdown.classList.remove("show");
}
});
// 새 첨부파일 미리보기 + 선택삭제
const fileInput = document.getElementById('fileInput');
const fileListDiv = document.getElementById('fileList');
fileInput.addEventListener('change', () => {
fileListDiv.innerHTML = '';
for (let i = 0; i < fileInput.files.length; i++) {
const file = fileInput.files[i];
const fileDiv = document.createElement('div');
fileDiv.classList.add('file-item');
fileDiv.innerHTML = `
<label>
<input type="checkbox" class="file-checkbox" data-index="${i}">
${file.name}
</label>`;
fileListDiv.appendChild(fileDiv);
}
});
function removeSelectedFiles() {
const checkboxes = document.querySelectorAll('.file-checkbox');
const dt = new DataTransfer();
const files = fileInput.files;
checkboxes.forEach(cb => {
const fileDiv = cb.closest('.file-item');
if (cb.checked && fileDiv) {
fileDiv.remove();
}
});
for (let i = 0; i < files.length; i++) {
if (![...checkboxes].some(cb => cb.dataset.index == i && cb.checked)) {
dt.items.add(files[i]);
}
}
fileInput.files = dt.files;
fileInput.dispatchEvent(new Event('change'));
// 기존 첨부파일 체크박스도 선택된 항목은 숨김처리
const oldCheckboxes = document.querySelectorAll('.delete-old-checkbox');
oldCheckboxes.forEach(cb => {
if (cb.checked) {
cb.closest('.old-file').style.display = 'none';
}
});
}
</script>
</body>
</html>
<전체 흐름 요약>
1. 세션시작
2. DB연결
3. 권한 체크
4. 기존 내용 출력
5. 새 첨부파일 선택 및 선택삭제
6. 기존 첨부파일 삭제 체크
7. 최종 UPDATE 처리
✔️ 세션 시작 & 로그인 확인
session_start();
$isLoggedIn = isset($_SESSION['username']);
✔️ DB 연결 & 게시글 정보 가져오기
// DB 연결
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
$post_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$sql = "SELECT * FROM board_post WHERE id = $post_id";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);
if (!$post) {
echo "<script>alert('존재하지 않는 게시글입니다.'); history.back();</script>";
exit();
}
✔️ 작성자 권한 체크
if ($_SESSION['username'] !== $post['author']) {
echo "<script>alert('권한이 없습니다.'); history.back();</script>";
exit();
}
$_SESSION['username'])와 작성자($post['author'])가 다르면 '권한이 없습니다'라는 알림창을 띄움
✔️ 기존 첨부파일 목록 가져오기
$attachments = [];
$att_sql = "SELECT * FROM attachment WHERE post_id = $post_id";
$att_result = mysqli_query($db_conn, $att_sql);
while ($file = mysqli_fetch_assoc($att_result)) {
$attachments[] = $file;
}
$attachment배열에 저장
✔️ 게시글 수정 폼
<form method="post" action="post_update_process.php" enctype="multipart/form-data">
<input type="hidden" name="post_id" value="<?= $post_id ?>">
<table>
<tr>
<td class="gray-cell">제목<span class="required">*</span></td>
<td><input type="text" name="title" value="<?= htmlspecialchars($post['title']) ?>" required></td>
</tr>
<tr>
<td class="gray-cell">글 내용<span class="required">*</span></td>
<td><textarea name="content" required><?= $post['content'] ?></textarea></td>
</tr>
...
...
...
post_update_process.php로 POST 요청
✔️ 기존 첨부파일 삭제 옵션
<?php if (!empty($attachments)): ?>
<div>
<p>기존 첨부파일</p>
<?php foreach ($attachments as $file): ?>
<div class="old-file">
<label>
<input type="checkbox" class="delete-old-checkbox" name="delete_files[]" value="<?= $file['id'] ?>">
<?= htmlspecialchars($file['file_name']) ?>
</label>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
delete_files[]로 전달됨.post_update_process.php에서 체크된 파일만 실제 파일 삭제 + DB레코드 삭제 처리 예정
✔️ 새 첨부파일 선택 및 삭제
<!-- 새 첨부파일 추가 -->
<input type="file" id="fileInput" name="files[]" multiple style="display:none">
<button type="button" onclick="document.getElementById('fileInput').click()">가져오기</button>
<button type="button" onclick="removeSelectedFiles()">선택된 파일
삭제</button>
<div id="fileList"></div>
✔️ 새 첨부파일 미리보기
// 새 첨부파일 미리보기 + 선택삭제
const fileInput = document.getElementById('fileInput');
const fileListDiv = document.getElementById('fileList');
fileInput.addEventListener('change', () => {
fileListDiv.innerHTML = '';
for (let i = 0; i < fileInput.files.length; i++) {
const file = fileInput.files[i];
const fileDiv = document.createElement('div');
fileDiv.classList.add('file-item');
fileDiv.innerHTML = `
<label>
<input type="checkbox" class="file-checkbox" data-index="${i}">
${file.name}
</label>`;
fileListDiv.appendChild(fileDiv);
}
});
✔️ 새 첨부파일 선택 삭제 + 기존 첨부파일 숨김
function removeSelectedFiles() {
const checkboxes = document.querySelectorAll('.file-checkbox');
const dt = new DataTransfer();
const files = fileInput.files;
checkboxes.forEach(cb => {
const fileDiv = cb.closest('.file-item');
if (cb.checked && fileDiv) {
fileDiv.remove();
}
});
for (let i = 0; i < files.length; i++) {
if (![...checkboxes].some(cb => cb.dataset.index == i && cb.checked)) {
dt.items.add(files[i]);
}
}
fileInput.files = dt.files;
fileInput.dispatchEvent(new Event('change'));
// 기존 첨부파일 체크박스도 선택된 항목은 숨김처리
const oldCheckboxes = document.querySelectorAll('.delete-old-checkbox');
oldCheckboxes.forEach(cb => {
if (cb.checked) {
cb.closest('.old-file').style.display = 'none';
}
});
}
👉
post_update.process.php전체 코드
<?php
session_start();
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'admin');
define('DB_PASSWORD', 'student1234');
define('DB_NAME', 'mywebsite');
$db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
mysqli_set_charset($db_conn, "utf8mb4");
$post_id = (int)$_POST['post_id'];
$title = mysqli_real_escape_string($db_conn, $_POST['title']);
$content = mysqli_real_escape_string($db_conn, $_POST['content']);
$sql = "SELECT * FROM board_post WHERE id = $post_id";
$result = mysqli_query($db_conn, $sql);
$post = mysqli_fetch_assoc($result);
if (!$post) {
echo "<script>alert('존재하지 않는 게시글입니다.'); history.back();</script>";
exit();
}
if ($_SESSION['username'] !== $post['author']) {
echo "<script>alert('권한이 없습니다.'); history.back();</script>";
exit();
}
// 게시글 UPDATE
mysqli_query($db_conn, "UPDATE board_post SET title='$title', content='$content' WHERE id=$post_id");
// 선택된 첨부파일 삭제 처리
if (isset($_POST['delete_files'])) {
foreach ($_POST['delete_files'] as $file_id) {
$file_id = (int)$file_id;
$file_sql = "SELECT * FROM attachment WHERE id = $file_id";
$file_result = mysqli_query($db_conn, $file_sql);
$file = mysqli_fetch_assoc($file_result);
if ($file) {
$real_path = $_SERVER['DOCUMENT_ROOT'] . $file['file_path'];
if (file_exists($real_path)) {
unlink($real_path);
}
mysqli_query($db_conn, "DELETE FROM attachment WHERE id = $file_id");
}
}
}
// 새 첨부파일 업로드
$upload_dir = $_SERVER['DOCUMENT_ROOT'] . "/project-folder/uploads/";
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
if (!empty($_FILES['files']['name'][0])) {
foreach ($_FILES['files']['name'] as $key => $file_name) {
$tmp_name = $_FILES['files']['tmp_name'][$key];
if ($_FILES['files']['error'][$key] === UPLOAD_ERR_OK) {
$unique_name = uniqid() . "_" . basename($file_name);
$destination = $upload_dir . $unique_name;
if (move_uploaded_file($tmp_name, $destination)) {
$save_path = "/project-folder/uploads/" . $unique_name;
$insert_sql = "INSERT INTO attachment (post_id, file_name, file_path)
VALUES ('$post_id', '$file_name', '$save_path')";
mysqli_query($db_conn, $insert_sql);
}
}
}
}
echo "<script>
alert('게시글이 수정되었습니다!');
location.href='post.php?id=$post_id';
</script>";
exit();
< 전체 흐름 요약 >
1. 수정 요청이 들어옴
2. 권한 체크
3. 수정된 제목과 내용 갱신
4. 선택된 첨부파일 삭제
5. 새 첨부파일은 업로드해서 저장
6. 수정완료 알림창 + 리다이렉트
✔️ 수정할 게시글 정보 확인
$post_id = (int)$_POST['post_id'];
$title = mysqli_real_escape_string($db_conn, $_POST['title']);
$content = mysqli_real_escape_string($db_conn, $_POST['content']);
POST로 넘어온 수정할 게시글 id, 제목, 내용 받기
✔️ 게시글 UPDATE
// 게시글 UPDATE
mysqli_query($db_conn, "UPDATE board_post SET title='$title', content='$content' WHERE id=$post_id");
✔️ 선택된 첨부파일 삭제 처리
// 선택된 첨부파일 삭제 처리
if (isset($_POST['delete_files'])) {
foreach ($_POST['delete_files'] as $file_id) {
$file_id = (int)$file_id;
$file_sql = "SELECT * FROM attachment WHERE id = $file_id";
$file_result = mysqli_query($db_conn, $file_sql);
$file = mysqli_fetch_assoc($file_result);
if ($file) {
$real_path = $_SERVER['DOCUMENT_ROOT'] . $file['file_path'];
if (file_exists($real_path)) {
unlink($real_path);
}
mysqli_query($db_conn, "DELETE FROM attachment WHERE id = $file_id");
}
}
}
$_SERVER['DOCUMENT_ROOT'])에 파일 삭제attachment테이블에서 해당 레코드도 삭제처리
✔️ 새 첨부파일 업로드 처리
// 새 첨부파일 업로드
$upload_dir = $_SERVER['DOCUMENT_ROOT'] . "/project-folder/uploads/";
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
if (!empty($_FILES['files']['name'][0])) {
foreach ($_FILES['files']['name'] as $key => $file_name) {
$tmp_name = $_FILES['files']['tmp_name'][$key];
if ($_FILES['files']['error'][$key] === UPLOAD_ERR_OK) {
$unique_name = uniqid() . "_" . basename($file_name);
$destination = $upload_dir . $unique_name;
if (move_uploaded_file($tmp_name, $destination)) {
$save_path = "/project-folder/uploads/" . $unique_name;
$insert_sql = "INSERT INTO attachment (post_id, file_name, file_path)
VALUES ('$post_id', '$file_name', '$save_path')";
mysqli_query($db_conn, $insert_sql);
}
}
}
}
uniqid()로 파일명 중복 방지(/project/folder/uploads/경로에)attachment테이블에 파일 정보 저장
✔️ 수정 완료 후 post.php로 이동
echo "<script>
alert('게시글이 수정되었습니다!');
location.href='post.php?id=$post_id';
</script>";
exit();
post.php 로 이동됨.