본문 바로가기

웹 모의해킹 스터디/웹 개발 (PHP | MySQL)

[php / mysql] 로그인 / 회원가입 페이지 만들기

2주차 과제

  • 회원가입 페이지 만들기 (기능 구현)
  • 로그인 페이지 ( DB 연동하기)

 

메인 폴더 안에 member 폴더를 생성해서 코드를 작성했다. (CSS 파일은 메인 폴더 안에 CSS 폴더 생성해서 저장)

 

 

회원 정보를 저장할 memeber 테이블

 

로그인 페이지

login.php

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="../css/style.css" />
  <title>로그인</title>
  
  <script type="text/javascript">
    function login_check() {
      var userid = document.getElementById("id");
      var userpw = document.getElementById("pw");
      if (userid.value == "") {
        var id_txt = document.querySelector(".err_id");
        id_txt.textContent = "아이디를 입력하세요.";
        userid.focus();
        return false;
      };
      if (userpw.value == "") {
        var pw_txt = document.querySelector(".err_pw");
        pw_txt.textContent = "비밀번호를 입력하세요.";
        userpw.focus();
        return false;
      };
    };
  </script>
</head>

<body>
  <?php
  session_start();
  if (isset($_SESSION['name'])) {
    echo "<script>
        alert(\"이미 로그인 하셨습니다.\");
        location.href = \"../main/index.php\";
        </script>";
  } else { ?>
    <div id="login_wrap" class="wrap">
      <div>
        <h1>Login</h1>
        <form action="login_proc.php" method="post" name="loginform" id="login_form" class="form">
          <p><input type="text" name="id" id="id" placeholder="ID"></p>
          <span class="err_id"></span> // 경고문 출력
          <p><input type="password" name="pw" id="pw" placeholder="Password"></p>
          <span class="err_pw"></span> // 경고문 출력
          <p><input type="submit" value="로그인" class="form_btn"></p>
          <p class="pre_btn"><a href="regist.php">회원가입</a></p>
        </form>
      </div>
    </div>
</body>
</html>
<?php
  }
?>

 

자바스크립트로 아이디나 비밀번호 입력폼이 공백일 때 입력하라는 경고문을 출력하고, 커서가 가도록 하였다.

 

 

style.css

회원가입 화면이랑 같이 쓰는 파일이라 정리가 좀 안돼있어 보기 어렵다 (...나도 헷갈림)

/* style.css */
* {
  padding: 0;
  margin: 0;
}
a {
  text-decoration: none;
  color: #000;
}
/* body를 가운데 정렬하기 위해 html에 flex를 주었습니다. */
html {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
}
body {
  width: 100%;
  margin: 0 auto;
  text-align: center; /* 회원가입 글자 */
  background: linear-gradient(black, gray);
  font-family: sans-serif;
}
.wrap {
  /* 하얀 상자 */
  width: 500px; /* body 밑 div 상자 */
  height: 650px;
  margin: 0 auto;
  background-color: #fff;
  border-radius: 20px; /* 상자 둥근 정도 */
  display: flex;
  justify-content: center;
  align-items: center;
}
.wrap > div {
  width: 100%;
  margin: 0 100px;
}
h1 {
  margin-bottom: 20px;
}
/* submit을 제외한 나머지 input 태그에 적용 */
.wrap .form input:not(input[type="submit"], input[type="checkbox"]) {
  border: 1px solid #d9d9d9;
  width: 400px;
  height: 40px;
  margin: 5px;
  padding-left: 10px;
  border-radius: 10px;
  box-sizing: border-box;
  font-size: 16px;
}

/* 체크 버튼 스타일 지정 */
#regist_wrap #regist_form input#checkIdBtn {
  position: relative;
  top: -2px;
  width: 45px;
  height: 35px;
  margin-left: 0;
  padding-left: 0;
  border: 0;
  box-sizing: content-box;
  background: gray;
  font-size: 12px;
  font-weight: bold;
  color: #fff;
  cursor: pointer;
}
#regist_wrap #regist_form input#check_button {
  position: relative;
  top: -2px;
  width: 100px;
  height: 28px;
  font-size: 12px;
  margin-left: 0;
  padding-left: 0;
  /* border: 0; */
  box-sizing: content-box;
  background: white;
  border-color: black;
  /* font-weight: bold; */
  /* color: white; */
  cursor: pointer;
}
/* 체크 시 표시될 영역 비표시(자바스크립트로 제어) */
#regist_wrap #regist_form #result {
  display: none;
}

#login_wrap .forgetpw {
  text-align: left;
  font-size: 14px;
  margin: 0 0 10px 10px;
  cursor: pointer;
}

.wrap .form_btn {
  /* wrap 안에 있는 form_btn 클래스 */
  width: 400px;
  height: 50px;
  margin: 10px;
  border-radius: 5px;
  border: 0;
  background: black;
  color: #fff;
  font-weight: bold;
  font-size: 18px;
  cursor: pointer;
}

#login_wrap .forgetpw a,
#regist_wrap .pre_btn a {
  /* a 태그에 적용 */
  color: red;
}

#login_wrap .pre_btn a {
  font-size: 13px;
  color: gray;
}

 

로그인 페이지 화면

 

ID나 PW를 공백으로 두고 로그인을 하면 입력폼 아래 경고문이 출력됨

 

 

login_proc.php

<?php
session_start();
include "../db_conn.php";

$id = $_POST['id'];
$pw = $_POST['pw'];
$hashed_pw = hash('sha256', $pw);

//아이디 존재 여부 검사
$sql = "select * from member where userid='$id'";
$result = mysqli_query($db_conn, $sql);
$row = mysqli_fetch_array($result);

if (!$row) { // 아이디가 존재하지 않으면 로그인 페이지로
    echo "<script> 
        alert(\"일치하는 아이디가 없습니다.\");
        history.back();
        </script>";
    exit;
} else { // 아이디가 존재하면 비밀번호 확인
    if ($row['userpw'] != $hashed_pw) { // 비밀번호 불일치 시 로그인 페이지로
        echo "<script>
                    alert(\"비밀번호가 일치하지 않습니다.\");
                    history.back();
                </script>";
        exit;
    } else { // 비밀번호 일치 시 세션 변수 생성
        $_SESSION['name'] = $row['username'];
        $_SESSION['id'] = $row['userid'];
        mysqli_close($db_conn);
        header("Location: ../main/index.php");
    }
}

 

비밀번호는 보안을 위해 해시값으로 DB에 저장할 것이므로 hash함수를 이용해서 비밀번호를 변환하여 확인한다. (아래 회원가입 코드에서 확인) 

 

먼저 DB에 아이디가 존재하는지 확인한 다음 비밀번호가 일치하는 지 확인한다. 

(식별 / 인증 분리)

 

비밀번호까지 확인되어 로그인이 되었다면 세션 변수를 생성해서 사용자의 이름과 id를 저장한다.

 

 

 

회원가입 페이지

[regist.php]

<?php
include "db_conn.php";
?>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="../css/style.css" />
  <script type="text/javascript" src="./regist.js"></script>
  <title>회원가입</title>
</head>

<body>
  <?php
  session_start();
  if (isset($_SESSION['name'])) {
    echo "<script>
       alert(\"이미 로그인 하셨습니다.\");
       location.href = \"../main/index.php\";
       </script>";
  } else { ?>
    <div id="regist_wrap" class="wrap">
      <div>
        <h1>Join</h1>
        <form action="regist_proc.php" method="post" name="regiform" id="regist_form" class="form" onsubmit="return sendit()">
          <p><input type="text" name="name" id="username" placeholder="Name (한글, 영어)"></p>
          <p><input type="text" name="id" id="userid" placeholder="ID">
            <input type="hidden" name="decide_id" id="decide_id">
          </p>
          <p><span id="decide" style='color:red; font-size:13px;'>* ID 중복 여부를 확인해주세요.&nbsp;</span>
            <input type="button" id="check_button" value="ID 중복체크" onclick="checkId();">
          </p>
          <p><input type="password" name="pw" id="userpw" placeholder="Password"></p>
          <p><input type="password" name="pw_ch" id="userpw_ch" placeholder="Password Check"></p>
          <p><input style="width:210px;" type="text" name="email" id="useremail" placeholder="Email">@
            <select style="width:165px; height:40px; border: 1px solid #d9d9d9; border-radius: 10px;font-size: 14px;" name="emadress">
              <option value="naver.com">naver.com</option>
              <option value="gmail.com">gmail.com</option>
              <option value="daum.net">daum.net</option>
            </select>
          </p>
          <p><input type="text" name="phone" id="userphone" placeholder="Phone Number"></p>
          <p><span style='color:red; font-size:13px; float:left;'>&nbsp;&nbsp;&nbsp;"-" 없이 11자리 숫자만 입력</span>
            <input type="submit" value="회원가입" id="join_button" class="form_btn" disabled=true>
          </p>
          <p><a href="login.php" style="color: gray; font-size:13px;">로그인</a></p>
        </form>
      </div>
    </div>
</body>

</html>
<?php
  }
?>

 

[regist.js]

function decide() {
  document.getElementById("decide").innerHTML =
    "<span style='color:blue;'>사용 가능한 아이디입니다.&nbsp;</span>";
  document.getElementById("decide_id").value =
    document.getElementById("userid").value;
  document.getElementById("userid").disabled = true;
  document.getElementById("join_button").disabled = false;
  document.getElementById("check_button").value = "다른 ID로 변경";
  document.getElementById("check_button").setAttribute("onclick", "change()");
}
function change() {
  document.getElementById("decide").innerHTML =
    "<span style='color:red;'>ID 중복 여부를 확인해주세요.&nbsp;</span>";
  document.getElementById("userid").disabled = false;
  document.getElementById("userid").value = "";
  document.getElementById("join_button").disabled = true;
  document.getElementById("check_button").value = "ID 중복 검사";
  document.getElementById("check_button").setAttribute("onclick", "checkId()");
}
function checkId() {
  var userid = document.getElementById("userid").value;
  if (userid) {
    url = "check.php?userid=" + userid;
    window.open(url, "chkid", "width=400,height=200");
  } else {
    alert("아이디를 입력하세요.");
  }
}

const sendit = () => {
  const userid = document.regiform.userid;
  const userpw = document.regiform.userpw;
  const userpw_ch = document.regiform.userpw_ch;
  const username = document.regiform.username;
  const userphone = document.regiform.userphone;
  const useremail = document.regiform.useremail;

  // username값이 비어있으면 실행.
  if (username.value == "") {
    alert("이름을 입력해주세요.");
    username.focus();
    return false;
  }
  // 한글 이름 형식 정규식
  const expNameText = /[가-힣a-zA-Z0-9]+$/;
  // username값이 정규식에 부합한지 체크
  if (!expNameText.test(username.value)) {
    alert("이름 형식이 맞지않습니다. 형식에 맞게 입력해주세요.");
    username.focus();
    return false;
  }
  if (username.value.length > 10) {
    alert("이름은 10글자 이하로 입력해주세요.");
    username.focus();
    return false;
  }
  if (userid.value == "") {
    alert("아이디를 입력해주세요.");
    userid.focus();
    return false;
  }
  // userid값이 4자 이상 12자 이하를 벗어나면 실행.
  if (userid.value.length < 3 || userid.value.length > 10) {
    alert("아이디는 3자 이상 10자 이하로 입력해주세요.");
    userid.focus();
    return false;
  }
  // userpw값이 비어있으면 실행.
  if (userpw.value == "") {
    alert("비밀번호를 입력해주세요.");
    userpw.focus();
    return false;
  }
  // userpw_ch값이 비어있으면 실행.
  if (userpw_ch.value == "") {
    alert("비밀번호 확인을 입력해주세요.");
    userpw_ch.focus();
    return false;
  }
  // userpw값이 6자 이상 20자 이하를 벗어나면 실행.
  if (userpw.value.length < 3 || userpw.value.length > 20) {
    alert("비밀번호는 3자 이상 20자 이하로 입력해주세요.");
    userpw.focus();
    return false;
  }
  // userpw값과 userpw_ch값이 다르면 실행.
  if (userpw.value != userpw_ch.value) {
    alert("비밀번호가 일치하지 않습니다. 다시 입력해주세요.");
    userpw_ch.focus();
    return false;
  }
  // useremail값이 비어있으면 알림창을 띄우고 input에 포커스를 맞춘 뒤 False를 리턴한다.
  if (useremail.value == "") {
    alert("이메일을 입력해주세요.");
    useremail.focus();
    return false;
  }
  // 이메일 형식 정규식
  const expEmailText = /[a-zA-Z0-9]+$/;
  // useremail값이 정규식에 부합한지 체크
  if (!expEmailText.test(useremail.value)) {
    alert("이메일 형식이 맞지 않습니다.");
    useremail.focus();
    return false;
  }
  // userphone값이 비어있으면 실행.
  if (userphone.value == "") {
    alert("핸드폰 번호를 입력해주세요.");
    userphone.focus();
    return false;
  }
  // 핸드폰 번호 형식 정규식
  const expHpText = /^\d{3}\d{3,4}\d{4}$/;
  // userphone값이 정규식에 부합한지 체크
  if (!expHpText.test(userphone.value)) {
    alert("핸드폰 번호 형식이 맞지않습니다.");
    userphone.focus();
    return false;
  }

  return true;
};

 

checkID 함수는 id 중복체크 시에 중복여부를 새로운 창을 띄어 보여준다.

decide 함수는 id 중복체크 후 사용을 확정했을 때 (이 때만 회원가입 버튼이 활성화됨),

change 함수는 id가 중복되었거나 변경을 원할 경우에 실행된다.

 

sendit 함수는 각 입력 값의 유효성을 검사한다.

 

 

[sytel.css]

로그인 페이지와 동일

 

 

회원가입 페이지 화면

 

 

중복확인

id에 123qwe를 입력하고 'ID 중복체크' 버튼을 클릭하면

새로운 창이 나오며 id의 사용 가능 여부를 알려준다.

'이 ID 사용' 버튼을 클릭하면

 

'ID 중복체크' => '다른 ID로 변경'  (decide 함수 실행)

'다른 ID로 변경'을 클릭하면

 

 

다시 처음 상태로 초기화된다. (change 함수 실행)

 

 

[regist_proc.php]

<?php
include "../db_conn.php";

$pw = $_POST['pw'];
$hashed_pw = hash('sha256', $pw);

$email = $_POST['email'] . '@' . $_POST['emadress'];
$sql = "insert into member values(null, '{$_POST['name']}', '{$_POST['decide_id']}','$hashed_pw', '$email','{$_POST['phone']}', now())";
$result = mysqli_query($db_conn, $sql);
/* $result = $db_conn -> query($sql);*/
// 입력이 됐으면 결과가 1

if ($result === false) { /* === 이면 자료형까지 일치하는지 확인 */
    echo "저장에 문제가 생겼습니다. 관리자에게 문의 바랍니다.";
    echo mysqli_error($db_conn);
} else { ?>
    <script>
        alert("회원가입이 완료되었습니다.")
        location.href = "../main/index.php"
    </script>;

<?php
}
?>

 

db_conn.php는 데이터베이스 연결을 위해 따로 저장해둔 파일이다.

 

회원가입에 성공하면 index 페이지로 이동하게 하였고, 실패하면 문제가 생겼다는 문자를 출력한다. 그리고 이후 코드가 노출되지 않도록 exit로 해당 코드를 완전히 빠져나온다.