Normaltic 모의해킹 취업반 스터디 8기 - 웹 개발 과제

containerxox·2025년 7월 7일
post-thumbnail

☑️ 구현

로그인
회원가입
로그아웃
마이페이지
비밀번호 변경

게시판
↳ 게시글 작성 ( +첨부파일 업로드 )
↳ 게시글 수정 & 삭제
↳ 댓글
↳ 게시글 검색
↳ 페이지네이션




☑️ 프로젝트 디렉터리 구조

/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)

🔸비로그인 상태

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

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>
  • 로고 : MyWebsite 로그
  • 게시판 메뉴: ./php/board/board.php로 이동됨
  • 로그인 여부에 따라
    ↳ 로그인 X : Sign in / Sign up 버튼이 표시됨
    ↳ 로그인 O : 유저아이콘 → 클릭하면 마이페이지로그아웃 드롭다운

✔️ <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에서 로그인 및 회원가입 처리

▶ login.html 전체 코드

➡️ 로그인과 회원가입 페이지를 한 화면에서 토글로 처리 !

<!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"
    : 로그인 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"
    : 회원가입 POST 요청을 처리할 signUp2.php로 회원가입 폼을 보냄
  • Already have an Account? Sign In 클릭 시, 로그인 폼으로 토글됨

✔️ 회원가입 폼

<script src="./js/login.js"></script>
  • 폼 토글 기능

▶ login.js 전체 코드

➡️ 로그인/회원가입 폼을 한 페이지에 토글 처리 해주는 자바스크립트

✔️ 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");
})
  • Sign Up 버튼 클릭 시,
    ① 로그인 폼(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");
})
  • Sign In 버튼 클릭 시,
    ① 로그인 폼(loginin)의 none 클래스를 제거
    ② 회원가입 폼(loginup)의 block 클래스를 제거
    ③ 로그인 폼(loginin)에 block 클래스 추가 → 로그인 폼 화면에 표시
    ④ 회원가입 폼(loginup)에 none 클래스 추가 → 회원가입 폼 숨김
    ➡️ 회원가입 폼 → 로그인 폼으로 전환 !

▶ login.php 전체 코드

<?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 쿼리 작성 (usernamepw가 일치하는 행 조회)

// 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);
}
  • 쿼리 결과 존재 = 일치하는 사용자 有
    → 세션에 username 저장 → 메인페이지(index.php)로 이동됨
  • 쿼리 결과 존재 X = 일치하는 사용자 X
    → '로그인 정보가 일치하기 않습니다'라는 알림창 띄움
    → 로그인 페이지(login.html)로 이동됨.
  • 쿼리 결과 존재 X = 일치하는 사용자 X



✔️ DB 연결 종료

// 7. DB 연결 종료
mysqli_close($db_conn);

✔️ user_info 테이블 구조




✅ 로그아웃 (logout.php)

▶ logout.php 전체 코드

<?php
session_start(); //세션 시작(현재 세션 불러오기)
session_unset(); //모든 세션 변수 제거
session_destroy(); //세션 자체를 파기

// index.php로 이동
header("Location:/project-folder/index.php");
exit();
?>
  • 세션을 파괴하고 메인페이지(index.php)로 이동




✅ 마이페이지 (logout.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>
  • 비밀번호는 로 길이만 표시
  • 실제 plain pw는 <input>태그의 hidden속성으로 숨김 처리
  • 로그아웃 버튼 클릭 → logout.php로 이동되어 로그아웃 처리
  • 변경 버튼 클릭 → 비밀번호 변경 페이지(pw_update_process.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);
  • DB 연결
  • 세션에서 로그인한 사용자 이름(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)): 새로운 비밀번호 값이 비어있는지 체크
    ↳ 비어 있다면, '새 비밀번호를 입력해주세요'라는 알림창 띄움
  • 모든 조건 만족 시 update 쿼리 실행 (비밀번호 변경 처리)
  • 비밀번호 변경 처리 완료되면, '비밀번호가 성공적으로 변경되었습니다.'라는 알림창을 띄우고, 마이페이지(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로 폼이 제출됨.




✅ 게시판

▶ 게시판 메인 화면 (board.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();
}
  • DB 접속
  • DB 연결 실패시 에러 원인 출력함.

✔️ 페이지네이션 계산

// 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 파라미터($_GET['page'])에 값이 없으면 디폴트로 1페이지로 표현됨.
  • $start_from : LIMIT의 OFFSET 역할
  • 전체 글 개수 가져와서 총 페이지 수 계산.
  • ceil은 x(값)보다 크거나 같은 정수로 올림한다!

🕵️‍♀️ 페이지네이션 상세설명
💡) LIMITOFFSET
LIMIT은 SQL에서 결과 행(row)의 개수를 제한할 때 사용

예시 1) users테이블에서 상위 5명의 이름을 가져오고 싶다면?
SELECT name FROM users LIMIT 5;
users테이블에 있는 row 중 앞에서부터 5개만 가져옴.

예시 2) LIMITOFFSET 함께 사용
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: 최신 글부터 내림차순 정렬
  • 현재 페이지 범위(한 페이지당 5개)에 맞게 LIMIT으로 게시글 일부만 가져옴

✔️ 게시글 목록 출력 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):
    ↳ 쿼리 결과가 있고, 가져온 게시글이 1개 이상이라면 실행
  • $virtual_number = $total_posts - $start_from;
    ↳ 최신순 번호 매기기 위해 총 글 개수 - OFFSET으로 가상번호 시작점 계산
  • while ($row = mysqli_fetch_assoc($result)):
    ↳ 결과 행들을 한 줄 씩 가져와서 테이블에 출력
  • $virtual_number--;
    ↳ while문 돌면서 가상번호가 1씩 줄어듦.



🕵️‍♀️ 가상번호($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>
  • 비로그인 상태 → sign in / sign up 버튼 표시
  • 로그인 상태 → 유저 아이콘 + 드롭다운 메뉴(마이페이지, 로그아웃)

✔️ 드롭다운 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>



▶ 게시판 검색 기능 (search.php)

🔸로그인 상태 + 검색 결과 존재 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>



▶ 게시글 작성 페이지( +첨부파일 업로드 ) (write.php)

🔸로그인 상태

🔸비로그인 상태

<전체 흐름 요약>
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_process.php)

➡️ 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();
}
  • POST 요청 받은 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 key
  • post_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)

👉 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']) ?>
        &nbsp;&nbsp; 작성일 | <?= htmlspecialchars($post['create_date']) ?>
        &nbsp;&nbsp; 조회 | <?= 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>
          &nbsp;
          <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);
  • URL의 ?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();
}
  • DB에서 조건에 맞는 게시글 하나를 가져옴
  • 게시글 없으면 '존재하지 않는 게시글입니다.'라는 알림창이 뜨고 이전 페이지로 돌아감.


✔️ 첨부파일 가져오기

$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();
  • POST 요청이고 submit_comment 값이 전송되었다면, 로그인 여부 확인
  • 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 key
  • post_id는 foreign key

▶ 게시글 삭제 (post_delete.php)

👉 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;
  • URL에 ?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();
}
  • 해당 id의 게시물 존재 X → '존재하지 않는 게시글입니다.'알림창
  • 현재 로그인한 사용자($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)

👉 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>
  • JS로 선택된 파일 목록 미리보기 + 삭제

✔️ 새 첨부파일 미리보기

// 새 첨부파일 미리보기 + 선택삭제
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';
    }
  });
}
  • 새 첨부파일에서 체크된 항목은 리스트에서 제거
  • 실제 input의 파일 목록에서도 제외
  • 기존 첨부파일 체크박스도 선택된 항목은 숨김처리

▶ 게시글 수정 처리 기능 (post_update_process.php)

👉 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");
  • 제목과 내용을 수정된 값으로 DB에 반영함.

✔️ 선택된 첨부파일 삭제 처리

// 선택된 첨부파일 삭제 처리
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");
    }
  }
}
  • 사용자가 기존 첨부파일 삭제 체크박스를 체크한 경우
    ↳ DB에서 파일 정보를 가져옴
    ↳ 서버 실제 경로($_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 로 이동됨.

0개의 댓글