이번에 경험한 것은 안드로이드 앱에서 PHP를 이용하여 MySQL(DB)에 접근해 데이터를 저장하는 방식의 회원가입을 만들어보도록 하겠습니다.
[이 글의 체크 포인트]
==> 안드로이드는 바로 MySQL에 접근을 하면 되는데 굳이 왜? PHP를 거쳐서 MySQL에 접근을 할까?
◼️ 1. Android PHP MySQL 통신 방법 ◼️
보통 안드로이드 앱에서 PHP를 이용하여 MySQL(DB)에 접근해 사용하는 방식들을 사용하고 있습니다.
==> 안드로이드는 바로 MySQL에 접근을 하면 되는데 굳이 왜? PHP를 거쳐서 MySQL에 접근을 할까?
위의 의문점이 드실 수 있습니다. 그 이유는 바로 보안상의 이유(MySQL 서버 접속용 아이디와 패스워드 유출) 때문에 '외부'데이터베이스(MySQL)에 바로 접근하지 못하기 때문입니다.
그렇기에 위의 방식으로 애플리케이션 <-> PHP + 아파치서버 <-> 외부 DB(MySQL, MariaDB)
◼️ 1-1. Android PHP MySQL 구체적인 통신 방법
구체적인 예시를 들어보자면,
PHP와 MySQL의 입장 : PHP에서 MySQL의 데이터에 접근을 하여 저장을 하거나 echo로 웹페이지에 MySQL의 내용을 띄어주면, 그 내용을 안드로이드에서 읽어오는것이다.
Android와 PHP,Apach의 입장 : 안드로이드에서 http 요청 코드를 작성을 하여 아파치 웹서버의 php파일의 출력하고 있는 내용을 읽거나 거기에 내용을 보내 MySQL에 접근을 하는것이다.
◼️ 2. Android PHP MySQL 통신을 위한 환경설정(Apache2, MysQl, PHP설치) ◼️
[ 필자의 환경은 Mac M1 Pro, AWS EC2-Ubunt 서버를 빌려서 하는 상태입니다. 참고해주세요. ]
◼️ 2-1. MySQL 설치
◼️ 2-2. Apache, PHP 설치
◼️ 3. MySQL 데이터베이스 및 테이블 생성 ◼️
[ 필자는 편리하게 이용하기 위해서 MySQL Workbench tool을 사용하여 DB 및 Table을 만들었고 , 확인은 VSCODE에서 확장 기능으로 MySQL을 설치하여 확인하였습니다. ]
◼️ 3-1. MySQL Workbench 설치 및 연동
◼️ 3-2. MySQl Workbench 에서 DB 및 테이블 생성
[ Workbench의 기본적인 사용법(스키마, 테이블 생성)을 모르는 사람은 위의 링크를 가서 익혀주세요. ]
필자는
DB : androidApp
테이블 : users
필드 : id(INT 타입, PK(기본키), NN(NotNull), UK(유니크키), AI(Auto Increment 데이터를 추가 할 때 마다 1씩 자동 증가))
useremail(VARCHAR 타입, NN(NotNull), UK(유니크키)) /// userid(VARCHAR 타입, NN(NotNull), UK(유니크키))
password((VARCHAR 타입, NN(NotNull)) /// userphone(VARCHAR 타입, NN(NotNull), UK(유니크키))
username((VARCHAR 타입, NN(NotNull)) /// userdate((VARCHAR 타입, NN(NotNull))
userquestion((VARCHAR 타입, NN(NotNull)) /// useranswer((VARCHAR 타입, NN(NotNull))
usercreated((VARCHAR 타입, NN(NotNull))
으로 설정을 하였다.
위에서 필드를 저렇게 설정한 이유는 회원가입할때 필요한 정보들이기 때문에 저렇게 설정을 하였다.
◼️ 4. PHP MySQL 연동 ◼️
여기서는 Android 앱으로 먼저 연동하기 전에 미리 PHP MySQL의 연동을 확인하기 위한 작업이다.
==> PHP에서 작성한 내용들이 MySQL과의 연동에 성공하면 MySQL의 androidApp DB의 users 테이블에 필드들에 데이터가 들어가야 한다.
◼️ 4-1. html , PHP 간략한 사용법
◼️ 4-2. MySQL 서버 접속을 위한 PHP 파일
우선 MySQL 서버 접속을 위해 PHP 파일 2개를 이용할 것이다.
하나의 파일은 android_log_php.php 파일이며 MySQL 서버 접속을 위해 사용되는 코드들을 담고 있다.
하나의 파일은 android_log_insert_php.php 파일이며 지정한 데이터베이스의 테이블에 데이터를 저장하기 위한 코드들을 담고 있다.
[ 필자는 AWS EC2-ubuntu를 사용하고 있어 /var/www/html/ 경로에서 파일을 생성하였고 작업은 쉘 환경이 아닌 VSCODE에서 작업을 하였습니다. VSCODE에서 PHP 쉽게 사용하시려면 아래 링크를 클릭해주세요. ]
< android_log_php.php >
<?php
$host = 'localhost';
$username = 'shopproject'; # MySQL 계정 아이디
$password = 'MySQL 비밀번호를 입력하세요'; # MySQL 계정 패스워드
$dbname = 'androidApp'; # DATABASE 이름
$options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8');
try {
$con = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8",$username, $password);
} catch(PDOException $e) {
die("Failed to connect to the database: " . $e->getMessage());
}
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
function undo_magic_quotes_gpc(&$array) {
foreach($array as &$value) {
if(is_array($value)) {
undo_magic_quotes_gpc($value);
}
else {
$value = stripslashes($value);
}
}
}
undo_magic_quotes_gpc($_POST);
undo_magic_quotes_gpc($_GET);
undo_magic_quotes_gpc($_COOKIE);
}
header('Content-Type: text/html; charset=utf-8');
#session_start();
?>
< android_log_inset_php.php >
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
include('android_log_php.php');
if (($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['submit']))
{
$useremail=$_POST['useremail'];
$userid=$_POST['userid'];
$userpw=$_POST['userpw'];
$userpw_ch=$_POST['userpw_ch'];
$userphone=$_POST['userphone'];
$username=$_POST['username'];
$userdate=$_POST['userdate'];
$useranswer=$_POST['useranswer'];
$userquestion=$_POST['userquestion'];
$usercreated = date("Y-m-d H:i:s");
if(empty($useremail)){
$errMSG = "이메일을 입력하세요.";
}
else if(empty($userid)){
$errMSG = "아이디를 입력하세요.";
}
else if(empty($userpw)){
$errMSG = "비밀번호를 입력하세요.";
}
else if(empty($userphone)){
$errMSG = "핸드폰 번호를 입력하세요.";
}
else if(empty($username)){
$errMSG = "이름을 입력하세요.";
}
else if(empty($userdate)){
$errMSG = "생년월일을 입력하세요.";
}
else if(!empty($_POST['userquestion'])){ //콤보박스의 값을 가져왔는데 비어있지 않으면
$selectd =$_POST['userquestion']; //$selectd라는 변수에 선택한 콤보박스의 값 userquetion을 담는다.
$question_default = "- 질문을 선택해주세요 -"; //콤보박스를 아무것도 건들이지 않았을때 값을 question_default로 생각하여 - 질문을 선택해주세요 - 값을 넣었다.
if($selectd == $question_default){ //선택한 콤보박스의 값이 - 질문을 선택해주세요 - 즉, 아무것도 선택안했을 경우에
$errMSG = "질문을 선택해주세요.";
}
}
else if(empty($useranswer)){
$errMSG = "질문의 답을 입력하세요.";
}
else if($userpw != $userpw_ch ){
$errMSG = "비밀번호가 같지 않습니다.";
}
if(!isset($errMSG)) //$errMSG가 존재하지 않았을 경우
{
try{ //try , catch
$stmt = $con->prepare('INSERT INTO users(useremail, userid, password,userphone, username, userdate, userquestion, useranswer, usercreated)
VALUES(:useremail,:userid, :userpw, :userphone , :username, :userdate, :userquestion, :useranswer, :usercreated )');
$stmt->bindParam(':useremail', $useremail);
$stmt->bindParam(':userid', $userid);
$stmt->bindParam(':userpw', $userpw);
$stmt->bindParam(':userphone', $userphone);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':userdate', $userdate);
$stmt->bindParam(':userquestion', $userquestion);
$stmt->bindParam(':useranswer', $useranswer);
$stmt->bindParam(':usercreated', $usercreated);
if($stmt->execute())
{
$successMSG = "새로운 사용자를 추가했습니다.";
}
else
{
$errMSG = "사용자 추가 에러";
}
} catch(PDOException $e) { //PHP와 MySQL 연동은 성공하였으나 DB 오류가 났을때
die("Database error: " . $e->getMessage());
}
}
}
?>
<html>
<body>
<?php
if (isset($errMSG)) { //만약 $errMSG의 변수의 값이 존재할 경우
// 자바 스크립트의 alert를 이용하여 팝업창에 $errMSG를 띄운다.
echo "<script>
alert('{$errMSG}');
</script>";
}
if (isset($successMSG)){ //만약 $$successMSG 변수의 값이 존재할 경우
// 자바 스크립트의 alert를 이용하여 팝업창에 $successMSG 띄운다.
echo "<script>
alert('{$successMSG}');
</script>";
}
?>
<form action="<?php $_PHP_SELF ?>" method="POST">
<h1>회원가입 창</h1>
<!-- 일반적인 입력 형태 -->
<p><input type="email" name="useremail" id="useremail" placeholder="E-mail"></p>
<p><input type="text" name="userid" id="userid" placeholder="ID"></p>
<p><input type="password" name="userpw" id="userpw" placeholder="Password"></p>
<p><input type="password" name="userpw_ch" id="userpw_ch" placeholder="Password Check"></p>
<p><input type="text" name="userphone" id="userphone" placeholder="Phone Number 000-0000-0000"></p>
<p><input type="text" name="username" id="username" placeholder="Name"></p>
<p><input type="date" name="userdate" id="userdate" placeholder="Date"></p>
<!-- 콤보 박스를 이용하였습니다. name 속성을 이용하여 콤보박스 안에 있는 것들을 하나로 묶어주었습니다. -->
<select name = "userquestion" onchange ="userquestion_selectchangebox(this.value);">
<option value = "- 질문을 선택해주세요 -" selected>- 질문을 선택해주세요 -</option>
<option value = "당신의 별명은 무엇입니까?">당신의 별명은 무엇입니까?</option>
<option value = "당신의 아버지의 성함은?">당신의 아버지의 성함은?</option>
<option value = "당신의 보물 1호는?">당신의 보물 1호는?</option>
<option value = "당신의 고향은?">당신의 고향은?</option>
<option value = "당신이 감명깊게 읽은 책은?">당신이 감명깊게 읽은 책은?</option>
</select>
<p><input type="text" name="useranswer" id="useranswer" placeholder="Answer"></p>
<input type = "submit" name = "submit" />
</form>
</body>
</html>
<?php
?>
[ 위의 코드를 보게되면 POST 방식이 나오는데 GET 방식과 비교되서 많이 나온다. 둘 다 브라우저가 서버에 요청하는것이고
POST 방식은 리소스를 생성/변경하기 위해 설계되었기 때문에 전송해야될 데이터를 HTTP 메세지의 Body에 담아서 전송한다.
GET 방식은 요청을 전송할 때 필요한 데이터를 Body에 담지 않고, 쿼리스트링(URL 끝에 ?와 함께 이름과 값으로 쌍을 이루는 요청 파라미터)을 통해 전송한다. ]
필자는 PDO(PHP DATA OBJECT) 방식으로 데이터베이스(MySQL)에 접근을 하였다.
나는 PHP에 깊게 공부하지 않았고, 대략적으로 문맥만을 파악하기 위해 PDO를 공부하였다. 아래의 링크를 참고하면 좋을 것 이다.
◼️ 4-3. PHP MySQL 연동 성공
위의 두 파일을 추가하고 ' 자신의 IP + /android_log_inset_php.php' 를 입력하여 웹 페이지에 접속해주세요.
[ ex : 54.180.59.123/android_log_insert_php.php ]
아래의 사진은 성공적으로 PHP와 MySQL와 연동하여 새로운 사용자를 내가 원하는 DB의 테이블의 필드에 값을 성공적으로 넣었을 경우
◼️ 4-4. PHP MySQL DB 오류
위의 사진은 MySQL과 연동은 성공적으로 하였으나, MySQL DB 오류 사진이다. DB 오류를 알아보자면 users 테이블의 useremail 필드는 UNIQUE Key 즉, 중복이 없어야 한다. 하지만 나는 users 테이블의 useremail 필드에 저장되어 있는 값을 또 다시 넣어준 것이다.
==> users 테이블의 useremail의 UNIQUE Key 설정을 한 필드 값에 중복값을 넣어주어 오류 떴는데 이것을 PHP와 MySQL을 연동을 하였고 오류 메세지를 띄우는것도 PHP에서 코드로 구현했기때문에 웹서버에 오류 메세지가 뜨게 된다.
◼️ 5. Android PHP MySQL 연동 ◼️
이번에는 안드로이드 스튜디오에서 PHP 통신 코드(http)를 통해 PHP를 연동하고 PHP 파일을 통해 데이터베이스(MySQL)과 연동을 하여 데이터를 저장하는 회원가입 기능 앱을 작성하겠습니다.
==> 안드로이드 스튜디오 <-> PHP <-> MySQL 관계
◼️ 5-1. android_log_inset_php.php 수정(안드로이드 앱과 연동하는 코드 추가)
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
include('android_log_php.php');
$android = strpos($_SERVER['HTTP_USER_AGENT'],"Android");
if( (($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['submit'])) || $android)
{
$useremail=$_POST['useremail'];
$userid=$_POST['userid'];
$userpw=$_POST['userpw'];
$userpw_ch=$_POST['userpw_ch'];
$userphone=$_POST['userphone'];
$username=$_POST['username'];
$userdate=$_POST['userdate'];
$useranswer=$_POST['useranswer'];
$userquestion=$_POST['userquestion'];
$usercreated = date("Y-m-d H:i:s");
if(empty($useremail)){
$errMSG = "이메일을 입력하세요.";
}
else if(empty($userid)){
$errMSG = "아이디를 입력하세요.";
}
else if(empty($userpw)){
$errMSG = "비밀번호를 입력하세요.";
}
else if(empty($userphone)){
$errMSG = "핸드폰 번호를 입력하세요.";
}
else if(empty($username)){
$errMSG = "이름을 입력하세요.";
}
else if(empty($userdate)){
$errMSG = "생년월일을 입력하세요.";
}
else if(!empty($_POST['userquestion'])){ //콤보박스의 값을 가져왔는데 비어있지 않으면
$selectd =$_POST['userquestion']; //$selectd라는 변수에 선택한 콤보박스의 값 userquetion을 담는다.
$question_default = "- 질문을 선택해주세요 -"; //콤보박스를 아무것도 건들이지 않았을때 값을 question_default로 생각하여 - 질문을 선택해주세요 - 값을 넣었다.
if($selectd == $question_default){ //선택한 콤보박스의 값이 - 질문을 선택해주세요 - 즉, 아무것도 선택안했을 경우에
$errMSG = "질문을 선택해주세요.";
}
}
else if(empty($useranswer)){
$errMSG = "질문의 답을 입력하세요.";
}
else if($userpw != $userpw_ch ){
$errMSG = "비밀번호가 같지 않습니다.";
}
if(!isset($errMSG)) //$errMSG가 존재하지 않았을 경우
{
try{ //try , catch
$stmt = $con->prepare('INSERT INTO users(useremail, userid, password,userphone, username, userdate, userquestion, useranswer, usercreated)
VALUES(:useremail,:userid, :userpw, :userphone , :username, :userdate, :userquestion, :useranswer, :usercreated )');
$stmt->bindParam(':useremail', $useremail);
$stmt->bindParam(':userid', $userid);
$stmt->bindParam(':userpw', $userpw);
$stmt->bindParam(':userphone', $userphone);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':userdate', $userdate);
$stmt->bindParam(':userquestion', $userquestion);
$stmt->bindParam(':useranswer', $useranswer);
$stmt->bindParam(':usercreated', $usercreated);
if($stmt->execute())
{
$successMSG = "새로운 사용자를 추가했습니다.";
}
else
{
$errMSG = "사용자 추가 에러";
}
} catch(PDOException $e) { //PHP와 MySQL 연동은 성공하였으나 DB 오류가 났을때
die("Database error: " . $e->getMessage());
}
}
}
?>
<html>
<body>
<?php
if (isset($errMSG)) { //만약 $errMSG의 변수의 값이 존재할 경우
// 자바 스크립트의 alert를 이용하여 팝업창에 $errMSG를 띄운다.
echo "<script>
alert('{$errMSG}');
</script>";
}
if (isset($successMSG)){ //만약 $$successMSG 변수의 값이 존재할 경우
// 자바 스크립트의 alert를 이용하여 팝업창에 $successMSG 띄운다.
echo "<script>
alert('{$successMSG}');
</script>";
}
$android = strpos($_SERVER['HTTP_USER_AGENT'],"Android");
if(!$android)
{
?>
<form action="<?php $_PHP_SELF ?>" method="POST">
<h1>회원가입 창</h1>
<!-- 일반적인 입력 형태 -->
<p><input type="email" name="useremail" id="useremail" placeholder="E-mail"></p>
<p><input type="text" name="userid" id="userid" placeholder="ID"></p>
<p><input type="password" name="userpw" id="userpw" placeholder="Password"></p>
<p><input type="password" name="userpw_ch" id="userpw_ch" placeholder="Password Check"></p>
<p><input type="text" name="userphone" id="userphone" placeholder="Phone Number 000-0000-0000"></p>
<p><input type="text" name="username" id="username" placeholder="Name"></p>
<p><input type="date" name="userdate" id="userdate" placeholder="Date"></p>
<!-- 콤보 박스를 이용하였습니다. name 속성을 이용하여 콤보박스 안에 있는 것들을 하나로 묶어주었습니다. -->
<select name = "userquestion" onchange ="userquestion_selectchangebox(this.value);">
<option value = "- 질문을 선택해주세요 -" selected>- 질문을 선택해주세요 -</option>
<option value = "당신의 별명은 무엇입니까?">당신의 별명은 무엇입니까?</option>
<option value = "당신의 아버지의 성함은?">당신의 아버지의 성함은?</option>
<option value = "당신의 보물 1호는?">당신의 보물 1호는?</option>
<option value = "당신의 고향은?">당신의 고향은?</option>
<option value = "당신이 감명깊게 읽은 책은?">당신이 감명깊게 읽은 책은?</option>
</select>
<p><input type="text" name="useranswer" id="useranswer" placeholder="Answer"></p>
<input type = "submit" name = "submit" />
</form>
</body>
</html>
<?php
}
?>
◼️ 5-2. 안드로이드 스튜디오 내의 AndroidManifest.xml(매니페스트 파일)에서 manifest 태그 하위 항목으로 인터넷 접근 허용 퍼미션을 추가한다.
[ android 9.0 이상에서 동작하게 하기 위해선 usesCleartextTraffic 옵션도 추가해야 한다. 해주지 않으면 "cleartext http traffic to not permitted" 에러가 발생한다. ]
◼️ 5-3. 안드로이드 스튜디오 내에서 회원가입을 위한 sign_up.xml을 생성한다.
[ string2.xml 은 spinner에 들어갈 항목을 xml로 만들어논 파일이다. ]
<sign_up.xml >
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Signup"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="82dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_marginLeft="9dp"
android:id="@+id/back"
android:layout_width="53dp"
android:layout_height="48dp"
app:srcCompat="@drawable/left_arrow"
tools:layout_editor_absoluteX="9dp"
tools:layout_editor_absoluteY="6dp"
android:layout_marginBottom="20dp"
/>
<ImageView
android:id="@+id/imageView2"
android:layout_width="233dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="42dp"
app:srcCompat="@drawable/corn"
tools:layout_editor_absoluteX="158dp"
tools:layout_editor_absoluteY="16dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
/>
</LinearLayout>
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="아이디"
android:textStyle="bold"
tools:layout_editor_absoluteX="9dp"
tools:layout_editor_absoluteY="108dp"
/>
<EditText
android:id="@+id/signup_id"
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:layout_width="380dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:background="@drawable/bolder2"
android:ems="10"
android:inputType="textEmailAddress"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="128dp"
android:hint=" 이메일 형식으로 입력해주세요."/>
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="비밀번호"
android:textStyle="bold"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="191dp" />
<EditText
android:layout_gravity="center"
android:id="@+id/signup_pwd"
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:layout_width="380dp"
android:layout_height="40dp"
android:background="@drawable/bolder2"
android:ems="10"
android:inputType="textPassword"
tools:layout_editor_absoluteX="9dp"
tools:layout_editor_absoluteY="291dp"
android:hint=" 6글자 이상 입력해주세요."/>
<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="비밀번호 재확인"
android:textStyle="bold"
tools:layout_editor_absoluteX="9dp"
tools:layout_editor_absoluteY="272dp" />
<EditText
android:layout_gravity="center"
android:id="@+id/signup_pwd2"
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:layout_width="380dp"
android:layout_height="40dp"
android:background="@drawable/bolder2"
android:ems="10"
android:inputType="textPassword"
tools:layout_editor_absoluteX="9dp"
tools:layout_editor_absoluteY="291dp"
android:hint=" 6글자 이상 입력해주세요."/>
<TextView
android:id="@+id/phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="휴대전화"
android:textStyle="bold"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="356dp" />
<EditText
android:layout_gravity="center"
android:id="@+id/signup_phone"
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:layout_width="380dp"
android:layout_height="40dp"
android:background="@drawable/bolder2"
android:ems="10"
android:inputType="phone"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="377dp"
android:hint=" - 빼고 입력해주세요. ex)010-0000-0000"/>
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="이름"
android:textStyle="bold"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="356dp" />
<EditText
android:layout_gravity="center"
android:id="@+id/signup_name"
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:layout_width="380dp"
android:layout_height="40dp"
android:background="@drawable/bolder2"
android:ems="10"
android:inputType="textPersonName"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="377dp"
android:hint=" 이름을 입력해주세요."/>
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="생년월일"
android:textStyle="bold"
tools:layout_editor_absoluteX="13dp"
tools:layout_editor_absoluteY="441dp" />
<EditText
android:layout_gravity="center"
android:id="@+id/signup_date"
android:layout_width="380dp"
android:layout_height="40dp"
android:background="@drawable/bolder2"
android:ems="10"
android:inputType="date"
tools:layout_editor_absoluteX="12dp"
tools:layout_editor_absoluteY="469dp"
android:hint="숫자만 8글자 입력해주세요. ex)19990101"/>
<Spinner
android:id="@+id/spinner1"
android:layout_width="405dp"
android:layout_height="58dp"
android:spinnerMode="dropdown"
tools:layout_editor_absoluteX="3dp"
tools:layout_editor_absoluteY="512dp">
</Spinner>
<EditText
android:layout_gravity="center"
android:id="@+id/questtion"
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:layout_width="380dp"
android:layout_height="40dp"
android:background="@drawable/bolder2"
android:ems="10"
android:inputType="text"
tools:layout_editor_absoluteX="14dp"
tools:layout_editor_absoluteY="555dp"
android:hint=" 질문의 답을 입력해주세요."/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/signup_button"
android:layout_width="331dp"
android:layout_height="58dp"
android:layout_gravity="center"
android:background="@drawable/yellow_round_square"
android:text="회원가입"
android:textColor="@color/white"
android:textSize="18dp"
android:textStyle="bold"
tools:layout_editor_absoluteX="40dp"
tools:layout_editor_absoluteY="638dp"
android:layout_marginTop="25dp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/textView_main_result"
>
</TextView>
</LinearLayout>
</LinearLayout>
< string2.xml >
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="shopproject"> </string>
<string-array name="question">
<item>
- 질문을 선택해주세요 -
</item>
<item>
당신의 별명은 무엇입니까?
</item>
<item>
당신의 아버지의 성함은?
</item>
<item>
당신의 보물 1호는?
</item>
<item>
당신의 고향은?
</item>
<item>
당신이 감명깊게 읽은 책은?
</item>
</string-array>
</resources>
◼️ 5-4. 안드로이드 스튜디오 내에서 회원가입을 위한 Signup_Php_Mysql.java 파일을 생성한다.
[ 코드 내용들을 주석으로 달아놨으니 참고해주세요. ]
[ 밑의 코드를 보게되면 POST 방식이 나오는데 GET 방식과 비교되서 많이 나온다. 둘 다 브라우저가 서버에 요청하는것이고
POST 방식은 리소스를 생성/변경하기 위해 설계되었기 때문에 전송해야될 데이터를 HTTP 메세지의 Body에 담아서 전송한다.
GET 방식은 요청을 전송할 때 필요한 데이터를 Body에 담지 않고, 쿼리스트링(URL 끝에 ?와 함께 이름과 값으로 쌍을 이루는 요청 파라미터)을 통해 전송한다. ]
package com.example.shopproject;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class Signup_Php_Mysql extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private static String IP_ADDRESS = "54.180.59.123"; //본인 IP주소를 넣으세요.
private static String TAG = "phptest"; //phptest log 찍으려는 용도
private TextView signup_id;
private TextView signup_pwd;
private TextView signup_pwd2;
private TextView signup_name;
private TextView signup_date;
private Button signup_button;
private TextView questtion;
private ImageView back;
private Spinner spinner;
private EditText signup_phone;
private ImageView imageView2;
private TextView mTextViewResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.signup);
spinner = findViewById(R.id.spinner1);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.question, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(this);
questtion = (EditText) findViewById(R.id.questtion);
signup_id = (EditText) findViewById(R.id.signup_id);
signup_pwd = (EditText) findViewById(R.id.signup_pwd);
signup_pwd2 = (EditText) findViewById(R.id.signup_pwd2);
signup_name = (EditText) findViewById(R.id.signup_name);
signup_date = (EditText) findViewById(R.id.signup_date);
signup_phone = (EditText) findViewById(R.id.signup_phone);
signup_button = (Button) findViewById(R.id.signup_button);
back = (ImageView) findViewById(R.id.back);
imageView2 = (ImageView) findViewById(R.id.imageView2);
mTextViewResult = (TextView)findViewById(R.id.textView_main_result);
mTextViewResult.setMovementMethod(new ScrollingMovementMethod());
back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
signup_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String email = signup_id.getText().toString().trim();
String pwd = signup_pwd.getText().toString().trim();
String pwdcheck = signup_pwd2.getText().toString().trim();
String name = signup_name.getText().toString().trim();
String date = signup_date.getText().toString().trim();
String question = questtion.getText().toString().trim() ;
String spinner1 = spinner.getSelectedItem().toString().trim();
String phone = signup_phone.getText().toString().trim();
//회원가입을 할 때 예외 처리를 해준 것이다.
if (email.equals("") || pwd.equals("") || pwdcheck.equals("") || name.equals("")|| date.equals("") || phone.equals("") || spinner1.equals("- 질문을 선택해주세요 -") || question.equals("") )
{
Toast.makeText(Signup_Php_Mysql.this, "정보를 입력해주세요.", Toast.LENGTH_SHORT).show();
}
else {
if(pwd.equals(pwdcheck)) {
if(pwd.length()<=5){
Toast.makeText(Signup_Php_Mysql.this, "비밀번호를 6자리 이상 입력해주세요.", Toast.LENGTH_SHORT).show();
}
else if(!email.contains("@") || !email.contains(".com")){
Toast.makeText(Signup_Php_Mysql.this, "아이디에 @ 및 .com을 포함시키세요.", Toast.LENGTH_SHORT).show();
}
else if(phone.contains("-") || !(Integer.parseInt(String.valueOf(phone.charAt(1)))==1)){
Toast.makeText(Signup_Php_Mysql.this,"올바른 전화번호 형식으로 입력해주세요..", Toast.LENGTH_SHORT).show();
}
else if(date.length()<=7 || Integer.parseInt(String.valueOf(date.charAt(0))) >= 3 || Integer.parseInt(String.valueOf(date.charAt(4)))>1
|| Integer.parseInt(String.valueOf(date.charAt(5)))==0 || Integer.parseInt(String.valueOf(date.charAt(6)))>3
||(Integer.parseInt(String.valueOf(date.charAt(6)))==3 && Integer.parseInt(String.valueOf(date.charAt(7)))>1
|| (Integer.parseInt(String.valueOf(date.charAt(4)))==1 && Integer.parseInt(String.valueOf(date.charAt(5)))>2)
||Integer.parseInt(String.valueOf(date.charAt(7)))==0)
) {
Toast.makeText(Signup_Php_Mysql.this, "생년월일 8자 이상 및 올바르게 입력하세요.", Toast.LENGTH_SHORT).show();
}
else if (date.length()<=7 || ( Integer.parseInt(String.valueOf(date.charAt(0)))==2 && Integer.parseInt(String.valueOf(date.charAt(1)))>0
&& Integer.parseInt(String.valueOf(date.charAt(2)))>2 && Integer.parseInt(String.valueOf(date.charAt(3)))>2)|| Integer.parseInt(String.valueOf(date.charAt(6)))>3
|| (Integer.parseInt(String.valueOf(date.charAt(6)))==3 && Integer.parseInt(String.valueOf(date.charAt(7)))>1)
||(Integer.parseInt(String.valueOf(date.charAt(4)))==1 && Integer.parseInt(String.valueOf(date.charAt(5)))>2
||Integer.parseInt(String.valueOf(date.charAt(7)))==0)
) {
Toast.makeText(Signup_Php_Mysql.this, "생년월일 8자 이상 및 올바르게 입력하세요.", Toast.LENGTH_SHORT).show();
}
else {
InsertData task = new InsertData(); //PHP 통신을 위한 InsertData 클래스의 task 객체 생성
//본인이 접속할 PHP 주소와 보낼 데이터를 입력해준다.
task.execute("http://"+IP_ADDRESS+"/android_log_insert_php.php",email,email,pwd,phone,name,date,spinner1,question);
Toast.makeText(Signup_Php_Mysql.this, "회원가입에 성공하셨습니다.", Toast.LENGTH_SHORT).show();
finish();
}
}
else {
Toast.makeText(Signup_Php_Mysql.this, "비밀번호가 일치 하지 않습니다.", Toast.LENGTH_SHORT).show();
}
}
}
});
}
class InsertData extends AsyncTask<String,Void,String> { // 통신을 위한 InsertData 생성
ProgressDialog progressDialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
//진행 다이얼로그 생성
progressDialog = ProgressDialog.show(Signup_Php_Mysql.this,
"Please Wait", null, true, true);
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progressDialog.dismiss(); //onPostExcute 에 오게되면 진행 다이얼로그 취소
mTextViewResult.setText(result); //xml의 작은 공간인 mTextViewResult에 result값 넣기
Log.d(TAG, "POST response - " + result); // result 값 확인하기
}
@Override
protected String doInBackground(String... params) {
/*
PHP 파일을 실행시킬 수 있는 주소와 전송할 데이터를 준비한다.
POST 방식으로 데이터 전달시에는 데이터가 주소에 직접 입력되지 않는다.
이 값들은 InsertData 객체.excute 에서 매개변수로 준 값들이 배열 개념으로 차례대로 들어가
값을 받아오는 개념이다.
*/
String serverURL = (String) params[0];
String useremail = (String)params[1];
String userid = (String)params[2];
String userpw = (String)params[3];
String userphone = (String)params[4];
String username = (String)params[5];
String userdate = (String)params[6];
String userquestion = (String)params[7];
String useranswer = (String)params[8];
/*
HTTP 메세지 본문에 포함되어 전송되기 때문에 따로 데이터를 준비해야한다.
전송할 데이터는 "이름=값" 형식이며 여러 개를 보내야 할 경우에 항목 사이에 &를 추가해준다.
여기에 적어준 이름들은 나중에 PHP에서 사용하여 값을 얻게 된다.
*/
String postParameters ="useremail="+useremail+"&userid="+ userid
+"&userpw="+userpw+"&userphone="+userphone+"&username="+username
+"&userdate="+userdate+"&userquestion="+userquestion+"&useranswer="+useranswer;
try{ // HttpURLConnection 클래스를 사용하여 POST 방식으로 데이터를 전송한다.
URL url = new URL(serverURL); //주소가 저장된 변수를 이곳에 입력한다.
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setReadTimeout(5000); //5초안에 응답이 오지 않으면 예외가 발생한다.
httpURLConnection.setConnectTimeout(5000); //5초안에 연결이 안되면 예외가 발생한다.
httpURLConnection.setRequestMethod("POST"); //요청 방식을 POST로 한다.
httpURLConnection.connect();
OutputStream outputStream = httpURLConnection.getOutputStream();
//전송할 데이터가 저장된 변수를 이곳에 입력한다. 인코딩을 고려해줘야 하기 때문에 UTF-8 형식으로 넣어준다.
outputStream.write(postParameters.getBytes("UTF-8"));
Log.d("php postParameters_데이터 : ",postParameters); //postParameters의 값이 정상적으로 넘어왔나 Log를 찍어줬다.
outputStream.flush();//현재 버퍼에 저장되어 있는 내용을 클라이언트로 전송하고 버퍼를 비운다.
outputStream.close(); //객체를 닫음으로써 자원을 반납한다.
int responseStatusCode = httpURLConnection.getResponseCode(); //응답을 읽는다.
Log.d(TAG, "POST response code-" + responseStatusCode);
InputStream inputStream;
if(responseStatusCode == httpURLConnection.HTTP_OK){ //만약 정상적인 응답 데이터 라면
inputStream=httpURLConnection.getInputStream();
Log.d("php정상: ","정상적으로 출력"); //로그 메세지로 정상적으로 출력을 찍는다.
}
else {
inputStream = httpURLConnection.getErrorStream(); //만약 에러가 발생한다면
Log.d("php비정상: ","비정상적으로 출력"); // 로그 메세지로 비정상적으로 출력을 찍는다.
}
// StringBuilder를 사용하여 수신되는 데이터를 저장한다.
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) !=null ) {
sb.append(line);
}
bufferedReader.close();
Log.d("php 값 :", sb.toString());
//저장된 데이터를 스트링으로 변환하여 리턴값으로 받는다.
return sb.toString();
}
catch (Exception e) {
Log.d(TAG, "InsertData: Error",e);
return new String("Error " + e.getMessage());
}
}
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
}
◼️ 5-5. 안드로이드 스튜디오에서 앱을 avd(에뮬레이터)를 이용하여 PHP와 연동 및 MySQL에 데이터가 잘 저장이 되는지 확인한다.
◼️ 5-6. 전체적인 코드 설명
1. 안드로이드 앱에서 POST 방식으로 PHP 코드에 데이터를 전달한다. ( Android -> PHP )
2. 미리 만들어 놓았던 PHP 코드에서 MySQL 서버에 접속하여 전달받은 데이터를 저장한다. ( PHP -> MySQL )
[ *POST 방식은 리소스를 생성/변경하기 위해 설계되었기 때문에 전송해야될 데이터를 HTTP 메세지의 Body에 담아서 전송한다.
GET 방식은 요청을 전송할 때 필요한 데이터를 Body에 담지 않고, 쿼리스트링(URL 끝에 ?와 함께 이름과 값으로 쌍을 이루는 요청 파라미터)을 통해 전송한다. ]
◼️ 6. Android PHP MySQL 연동 느낀점
내가 맨 처음 한 생각은 안드로이드 앱에서 외부 DB(MySQL)을 이용하기 위해서 손쉽게 MySQL과 연동하는 코드를 이용하여 MySQL과 연동 후 DB를 사용하면 되는 줄 알았다. 큰 오산이였다.
MySQL을 사용하기 위해선 필수적으로 필요한 것은
Apache 웹 서버와 그것을 이용하여 웹 프로그래밍 하는 언어인 PHP 문법 MySQL이 있었다.
필수적으로 필요하지 않은데 쉽게 사용하기 위해서는 많은 사람들의 데이터를 안전하게 관리를 해주며 다른 유저의 메모리나 CPU 사용 관섭이 없고 24시간 내내 켜져야하는 서버 컴퓨터, Shall(터미널) 환경은 인텔리센스 기능이 없어 코드 작성하기가 어려워 VSCODE를 깔고 Apache와 MySQL과의 연동을 해야했다.
나는 그래서 서버 빌리는것(AWS EC2-ubuntu)에서부터 차근차근 Apache 웹서버, PHP, MySQL을 빌린 서버(AWS EC2-ubuntu)에 설치 하였다. Shall(터미널)환경에서 작업하는 것은 너무 번거롭고 귀찮아서 Tool을 찾아보았는데 Visual Studio Code 라는 Tool이 있었다. 줄여서 VSCODE인데, 이것은 PHP와 MySQL만을 위한 Tool은 아니지만, 확장 기능을 통해 PHP와 MySQL을 설치하고 그것들을 코딩하는데 도움이 되는 확장팩을 다운받아 사용하였다. VSCODE와 PHP, MySQL을 연동을 하기위해선 빌린 서버(AWS EC2-ubuntu)와 ssh연동을 해야하기 때문에 확장 기능으로 Remote ssh를 설치하고 연동을 해주고 그다음으로 PHP, MySQL을 연동하여 사용할 수 있었다. [ 이때 많은 시행착오를 겪었고 많은것을 배웠던 것 같다. ]
[ 참고로 MySQL 작업 같은 경우에 테이블 생성 같은 코딩들은 MySQL 전문 Tool인 Workbench를 이용했고, 결과 확인은 VSCODE를 통해 확인하였다.]
PHP와 MySQL과의 연동을 위해 먼저 html를 이용해 폼을 만들고 PHP 웹 프로그래밍 언어를 이용하여 MySQL과 연동을 하였다.
[ 이때 html과 PHP문법을 전혀 모르는 상태로 시작을 하여 많은 시행착오를 겪었고 많은것을 배웠던 것 같다. ]
PHP와 MySQL을 연동된 것을 확인후에 본격적으로 안드로이드 스튜디오에서 PHP와 연동할 수 있는 코드를 입력해주었고, 많은 시행착오 끝에 연결이 되어 정상적으로 Android -> PHP -> MySQL 연동이 성공되었다.
누구도 가르쳐주지 않고 혼자서 어떤 목표를 해보자고 했을때 속도가 더디며, 처음에는 큰 난관에 봉착하지만, 기초적인 것을 하나하나 뜯어보고 해결하기 때문에 결국에 기억에 오래남아 좋은점이 있는 것 같다.
누군가가 가르쳐줘서 따라하기 보다는 내가 무엇이 필요하고, 무슨 지식이 필요한지 뚜렷하게 파악 가능한 능력은 개발자로서는 정말로 큰 능력이란 것을 깨달았다.
나도 무엇인가를 진행을 할 때 온전히 가르쳐주는것을 받아드리는것이 아니라 내가 찾아보고 노력하고 시행착오를 겪는것이 더욱 큰 성장이 되는 것이란 것을 깨닫았다. 앞으로 내가 무엇이 필요하고, 무슨 지식이 필요한지 뚜렷하게 파악하는 능력을 차츰 차츰 늘려 나갈 것이다.
'Android > Android 통신(PHP)' 카테고리의 다른 글
AWS ec2-ubuntu 에서 Apache , PHP 설치 및 index.html, index.php 생성 (0) | 2022.08.25 |
---|