회원가입 폼 DB에 전송

호박이와 칼림바·2023년 12월 16일
0
post-thumbnail

이번에는 지난 시간에 구현한 회원가입 폼을 제출하면 데이터가 DB에 저장되도록 해볼 것이다.
먼저 DBeaver에서 회원가입을 한 사용자의 정보를 담을 테이블을 만들어 줘야 한다.

users 테이블을 만들었으니 이제 프론트 코드 작성을 하러 가자.

register.php 파일을 생성해놓고, 회원가입 폼 코드 일부를 불러와 수정해보겠다.

Membership 컴포넌트의 return문

return (
    <>
        <MembershipBox>
            <H1>회원가입</H1>
            <Form 
                method="POST"
                action="http://localhost/Album/src/Data/register.php"
                onSubmit={(e) => registerBtn === false && e.preventDefault()}
            >
  				...
				...
                // 생략
                <Register checked={registerBtn}>회원가입</Register>
                <Back type="button" onClick={() => navigate('/')}>back</Back>
                <Check checked={confirmPassword}>비밀번호가 일치하지 않습니다.</Check> 
            </Form>
        </MembershipBox>
    </>
);

Form 속성에 method와 action을 추가했다.
form을 서버에 보낼 것이기 때문에 POST 메서드를 사용하였다.

그럼 다시 register.php로 돌아와서 기본 설정을 해보자.

1. PHP 기본 설정

<?php
header("Access-Control-Allow-Origin: http://localhost:3000");

$mysqli = new mysqli('localhost', 'root', 'system', 'album');
$mysqli->set_charset("utf8mb4"); // 4바이트의 인코딩

if ($mysqli->connect_error) {
    die("데이터베이스 연결 실패: " . $mysqli->connect_error);
}

mysqli_close($mysqli); 
?>

CORS header을 사용하여 현재 로컬 주소를 허용해주도록 하고, 나의 DB 정보를 넣어 DB와 연결하였다.

다음은 클라이언트 측에서 서버로 전송한 회원가입 폼을 활용하도록 하겠다.

2. 전달받은 데이터 변수에 저장하기

<?php
header("Access-Control-Allow-Origin: http://localhost:3000");

$mysqli = new mysqli('localhost', 'root', 'system', 'album');
$mysqli->set_charset("utf8mb4"); // 4바이트의 인코딩

if ($mysqli->connect_error) {
    die("데이터베이스 연결 실패: " . $mysqli->connect_error);
}

date_default_timezone_set('Asia/Seoul'); // 한국 서버

$id = $_POST['id'];
$password = $_POST['password'];
$username = $_POST['username'];    
$phone = $_POST['phone'];
$created = date("Y-m-d H:i:s"); // 현재 시간

mysqli_close($mysqli); 
?>

클라이언트가 POST한 데이터 id ,password, username, phone을 각각 변수에 담았다.
그리고 한국 서버의 기준으로 회원가입한 시각을 created변수에 담았다.

3. SQL injection 방어를 위한 쿼리문

회원가입을 할 때 이미 생성된 아이디가 있는지 검색하는 쿼리문을 작성해보자.

// 이미 존재하는 아이디인지 확인
$checkIdDuplicateQuery = "SELECT id FROM users WHERE id = ?";
$stmtId = $mysqli->prepare($checkIdDuplicateQuery);
$stmtId->bind_param("s", $id); // '?'에 사용할 값을 바인딩
$stmtId->execute();
$stmtId->store_result();

// 이미 존재하는 휴대폰 번호인지 확인
$checkPhoneDuplicateQuery = "SELECT phone FROM users WHERE phone = ?";
$stmtPhone = $mysqli->prepare($checkPhoneDuplicateQuery);
$stmtPhone->bind_param("s", $phone); // '?'에 사용할 값을 바인딩
$stmtPhone->execute();
$stmtPhone->store_result();

🔸물음표 매개변수

  • 사용자가 입력한 데이터를 쿼리에 직접 삽입하는 SQL 인젝션 공격을 방지해준다.
  • 사용자로부터 입력된 데이터가 쿼리에 직접 포함되지 않아서 안전하게 전달할 수 있다.

🔸prepare문

  • 사용자 입력을 쿼리에 안전하게 삽입할 때 유용하다.
  • 주로 동일한 쿼리를 여러 번 실행할 때 사용한다.(위의 코드에선 한번만 실행)

🔸bind_param 함수

  • 매개변수를 SQL 쿼리에 바인딩하고 매개변수가 무엇인지 데이터베이스에 알려준다.
  • 인수 's'는 문자열을 나타낸다.

🔸store_result 함수

  • 쿼리의 결과를 저장한다.
  • 나중에 결과 집합에 대한 정보에 접근하기 위해 필요
    • $stmt->num_rows에 사용되어 결과 집합의 행 수를 확인할 수 있다.

4. 사용자 정보를 단방향 암호화하여 DB에 저장

위에서 회원가입을 하기 위해 사용자가 입력한 아이디 또는 휴대폰 번호가 중복인지 아닌지를 검색하는 쿼리문을 작성하였다. 이 쿼리문을 활용해보자.

// 생성하려는 아이디와 휴대폰 번호가 중복이 없을 경우
if($stmtId->num_rows == 0 && $stmtPhone->num_rows == 0) { 
    // 비밀번호를 해시화하여 저장
    $hashedPassword = password_hash($password, PASSWORD_DEFAULT);

    $insertQuery = "INSERT INTO users(id, password, username, phone, created) 
    VALUES (?, ?, ?, ?, ?)";

    $stmt = $mysqli->prepare($insertQuery);
    $stmt->bind_param("sssss", $id, $hashedPassword, $username, $phone, $created);

    if ($stmt->execute()) {
        echo("
            <script>
                alert('회원가입 성공! 나만의 앨범집을 만들어 보세요.');
                location.replace('http://localhost:3000');
            </script>
        ");
    } else {
        echo("
            <script>
                alert('서버가 연결되어 있지 않습니다.');
            </script>
        ");
    }
} else {
    if($stmtId->num_rows != 0) { 
        // 아이디 중복
        echo("
            <script>
                alert('이미 사용 중인 아이디입니다.');
                location.replace('http://localhost:3000/membership');
            </script>
        ");
    } else if($stmtPhone->num_rows != 0) { 
        // 휴대폰 번호 중복 (= 기존 아이디 존재)
        echo("
            <script>
                alert('이미 가입된 계정이 있습니다.');
                location.replace('http://localhost:3000/membership');
            </script>
        ");       
    }
}

$stmt->store_result(); 이 문장을 사용하면 $stmt->num_rows을 사용할 수 있다.
if($stmtId->num_rows == 0 && $stmtPhone->num_rows == 0)은 결과 집합의 행 수가 0일 때를 의미하는데, 사용자가 입력한 아이디와 휴대폰 번호가 중복이 아닐 경우를 뜻한다.

그 다음 사용자가 입력한 비밀번호를 그대로 저장하는 것이 아니라 해시화하여 변수에 저장해놓았다.
users 테이블에 해시화하여 저장한 변수 hashedPassword와 사용자가 입력한 id, username, phone 그리고 회원가입을 요청한 시각 created를 INSERT하는 쿼리문을 작성하였다.

그 쿼리문을 실행하여 성공적으로 DB에 저장되면 회원가입 성공 알림을 뜨게 했고, 정상적으로 실행되지 않았을 경우 서버의 연결 문제가 있다는 알림을 뜨게 했다.

사용자의 아이디와 휴대폰 번호가 이미 존재하는 경우도 따로 알림 처리해주었다.


다음 아래는 실행 결과이다.

회원가입 성공

사용 중인 아이디

이미 존재하는 휴대폰 번호


전체 코드

<?php
header("Access-Control-Allow-Origin: http://localhost:3000");

$mysqli = new mysqli('localhost', 'root', 'system', 'album');
$mysqli->set_charset("utf8mb4"); // 4바이트의 인코딩

if ($mysqli->connect_error) {
    die("데이터베이스 연결 실패: " . $mysqli->connect_error);
}

date_default_timezone_set('Asia/Seoul'); // 한국 서버

$id = $_POST['id'];
$password = $_POST['password'];
$username = $_POST['username'];    
$phone = $_POST['phone'];
$created = date("Y-m-d H:i:s"); // 현재 시간

// 이미 존재하는 아이디인지 확인
$checkIdDuplicateQuery = "SELECT id FROM users WHERE id = ?";
$stmtId = $mysqli->prepare($checkIdDuplicateQuery);
$stmtId->bind_param("s", $id); // '?'에 사용할 값을 바인딩
$stmtId->execute();
$stmtId->store_result();

// 이미 존재하는 휴대폰 번호인지 확인
$checkPhoneDuplicateQuery = "SELECT phone FROM users WHERE phone = ?";
$stmtPhone = $mysqli->prepare($checkPhoneDuplicateQuery);
$stmtPhone->bind_param("s", $phone); // '?'에 사용할 값을 바인딩
$stmtPhone->execute();
$stmtPhone->store_result();

// 생성하려는 아이디와 휴대폰 번호가 중복이 없을 경우
if($stmtId->num_rows == 0 && $stmtPhone->num_rows == 0) { 
    // 비밀번호를 해시화하여 저장
    $hashedPassword = password_hash($password, PASSWORD_DEFAULT);

    $insertQuery = "INSERT INTO users(id, password, username, phone, created) 
    VALUES (?, ?, ?, ?, ?)";

    $stmt = $mysqli->prepare($insertQuery);
    $stmt->bind_param("sssss", $id, $hashedPassword, $username, $phone, $created);

    if ($stmt->execute()) {
        echo("
            <script>
                alert('회원가입 성공! 나만의 앨범집을 만들어 보세요.');
                location.replace('http://localhost:3000');
            </script>
        ");
    } else {
        echo("
            <script>
                alert('서버가 연결되어 있지 않습니다.');
            </script>
        ");
    }
} else {
    if($stmtId->num_rows != 0) { 
        // 아이디 중복
        echo("
            <script>
                alert('이미 사용 중인 아이디입니다.');
                location.replace('http://localhost:3000/membership');
            </script>
        ");
    } else if($stmtPhone->num_rows != 0) { 
        // 휴대폰 번호 중복 (= 기존 아이디 존재)
        echo("
            <script>
                alert('이미 가입된 계정이 있습니다.');
                location.replace('http://localhost:3000/membership');
            </script>
        ");       
    }
}

$stmt->close();
mysqli_close($mysqli); 
?>
profile
프론트엔드 개발자입니다.

0개의 댓글