Normaltic 모의해킹 취업반 스터디 8기 - 2주차 과제(로그인 페이지에 DB 연동하기)

containerxox·2025년 4월 23일
post-thumbnail

🚩2주차 과제 - 로그인 페이지에 DB 연동

  • login.html페이지에 DB를 연동하여
    DB에 저장된 정보이면 로그인 성공,
    DB에 저장되지 않은 정보이면 로그인 실패

① login.html

  • 로그인 폼을 login.php에 POST방식으로 전송!
<!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">
            <!-- login form -->
            <form action="./php/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>

            <!-- create account form -->
            <form action="./php/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>



② login.php

( 1 ) DB 접속
( 2 ) login.html의 로그인 폼으로터 데이터 받아오기
( 3 ) Prepared Statment를 사용하여 쿼리 틀을 DB에 전달하여 준비

// 사용자가 입력한 id를 기준으로 user_info 테이블에서 해당 유저 정보 찾음
    $sql = "SELECT * FROM user_info WHERE id = ?";
    $stmt = mysqli_prepare($db_conn, $sql);

( 4 ) 값을 바인딩 (사용자가 입력한 username을 ? 자리에 바인딩)

 mysqli_stmt_bind_param($stmt,"s", $username);

( 5 ) 바인딩한 쿼리를 실행 (? 자리에 유저가 입력한 username을 넣어서 쿼리를 실행)

mysqli_stmt_execute($stmt);

( 6 ) DB 서버로부터 위의 쿼리 조건에 부합되는 결과 테이블 받아오기
(위의 쿼리 조건에 부합되는 데이터만 DB서버에서 가져옴)

$result = mysqli_stmt_get_result($stmt);

( 7 ) 사용자가 login.html에서 입력한 username과 비밀번호를 검사해서,
→ 맞으면 loginSuccess.php로 이동
→ 틀리면 로그인 실패 메시지를 띄우기
➡️ while을 사용한 경우

 		 	// 사용자가 입력한 username을 가진 row를 찾기 위해 while문을 통해 반복
            // (위의 쿼리 조건에 부합한) 결과테이블의 모든 row를 읽어서 
            // 사용자가 입력한 username과 일치한 row 찾는다!
            ///쿼리 조건에 해당되는 데이터들(레코드들) 중 위부터 순차적으로 꺼냄
            while($row=mysqli_fetch_assoc($result)){ 
                    if(password_verify($pw,$row['pw'])){ //각 레코드(row)마다 사용자가 입력한 pw와 레코드의 pw가 일치한지 확인
                            //로그인 성공시 -> username 포함해서 success.php로 이동
                            header("Location: loginSuccess.php?username=" . urlencode($row['username']));
                            exit(); //스크립트 종료
                    }
            }
            // 로그인 실패 알림창 띄우고 login.html로 리다이렉션
            echo "<script>alert('로그인 정보가 일치하지 않습니다.'); window.location.href='login.html';</script>";
    }

➡️ if를 사용한 경우 (✔️)

// 나는 db에서 username을 unique로 설정해서 결과가 0개 또는 1개임.
// 즉, username이 중복 되지 않는다.
// 그래서 while문 보다는 if문을 쓰는게 더 효율적이라고 생각했다.
// 👉 username이 DB에 존재하면 → row 1개만 반환됨
// 👉 그 1개 row에서 password 확인
// 👉 맞으면 → 로그인 성공
// 👉 틀리면 → 실패 메시지 출력
$row = mysqli_fetch_assoc($result);
        // 10. 사용자 존재 + 비밀번호 일치하는지 확인
        if($row && password_verify($pw, $row['pw'])){
                header("Location: loginSuccess.php?username=" . urlencode($row['username']));
                exit();
        }else{
                // 로그인 실패
                echo "<script>alert('로그인 정보가 일치하지 않습니다.'); window.location.href='/project-folder/login.html';</script>";
        }
        // 11. sql 쿼리 종료
        mysqli_stmt_close($stmt);

  
( 8 ) DB 연결 종료

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

💡mysqli_stmt_get_result()

  • 이 함수는 결과 테이블을 가져옴
  • result = mysqli_stmt_get_result(stmt);에서
    stmt는 execute()한 후의 Prepared Statement 객체.
  • 이 함수는 $stmt로부터 결과를 가져와서
    → mysqli_fetch_assoc() 같은 함수로 다룰 수 있는 일반적인 결과 객체로 바꿔줌


    💡mysqli_fetch_assoc()
  • 결과에서 한 행(row)을 연관 배열(associative array)로 가져옴
    $user['id'], $user['pw'], $user['username']처럼 사용할 수 있음
    한 번 실행하면 다음 행으로 넘어감 (루프에서 여러 행 읽을 수 있음)

⬇️ login.php 전체 코드

<?php
// 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'];

// < Prepared Statment 사용>
// 4. sql 쿼리 준비하기
$sql = "select * from user_info where username = ?";
$stmt = mysqli_prepare($db_conn, $sql);

if($stmt){
        // 6. 값 바인딩하기
        mysqli_stmt_bind_param($stmt, "s", $username);
        //7. 바인딩 마친 sql쿼리를 실제로 실행하기
        mysqli_stmt_execute($stmt);
        // 8. DB로부터 sql쿼리조건에 부합한 결과 테이블 받아오기
        $result = mysqli_stmt_get_result($stmt);

        //9. 받아온 결과 테이블에서 하나의 행을 가져옴.
        //나는 db에서 username을 unique로 설정해서 결과가 0개 또는 1개임.
        //즉, username이 중복 X
        $row = mysqli_fetch_assoc($result);

        // 10. 사용자 존재 + 비밀번호 일치하는지 확인
        if($row && password_verify($pw, $row['pw'])){
                header("Location: loginSuccess.php?username=" . urlencode($row['username']));
                exit();
        }else{
                // 로그인 실패
                echo "<script>alert('로그인 정보가 일치하지 않습니다.'); window.location.href='/project-folder/login.html';</script>";
        }

        // 11. sql 쿼리 종료
        mysqli_stmt_close($stmt);
}
else{
        //쿼리 준비 실패 시, 출력됨.
        echo "쿼리 준비 실패: " . mysqli_error($db_conn);
}

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

?>



💡 SELECT * FROM user_info WHERE username = ?에서
      where 조건에 id만 적은 이유

idUNIQUE, NOT NULL로 설정했기 때문에
WHERE id = ? 하나만 써도 로그인 조건으로 충분하다
( id는 중복되지 않아서 고유하고, NULL값이 아니므로 해당되는 레코드를 찾는데 적합하다 )



③ loginSuccess.php

GET방식으로 받은 username을 화면을 출력!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Success!</title>
    <style>
        .container{
            display:grid;
            place-items:center;
            height: 100vh;
            padding: 1.5rem;
        }

        .contents{
            width:100%;
            max-width:348px;
            background-color:#f2f2f2;
            padding:2rem 1rem;
            border-radius:1rem;
            text-align:center;
            box-shadow: 0 8px 20px rgba(35, 0, 77, 0.2);
        }

        .welcome,.username{
            font-weight: 600;
            font-size:1.5rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="contents">
            <div class="welcome">Welcome !</div>
            <div class="username"><?php echo $_GET['username'];?></div> 
        </div>
    </div>
</body>
</html>



☑️ 작동 확인

▶ 테이블 상태

  • 현재 user_info테이블



▶ 로그인 (성공)

ID는 user01, PW는 user01user01로 로그인 시도

로그인 성공 → user01이라는 username으로 출력됨



▶ 로그인 (실패)

user_info테이블에 존재하지 않는 ID와 PW로 로그인 시도

로그인 실패
→ 로그인 정보가 일치하지 않습니다 라는 알림창 뜸
login.html로 리다이렉트

0개의 댓글