안녕하세요! 저번 로그인 기능 구현 게시물에 이어서 이번 포스팅에서는 회원가입 기능을 구현해 보았습니다. 이번 포스팅에서 사용되는 sendPostRequest 함수는 '로그인 기능 구현' 게시물에서 다뤘으니 이해가 안되시면 보고 오시는 게 좋을 것 같습니다! 링크는 아래 달아드리겠습니다.
1. 완성본 미리 보기
> 우선 회원가입 페이지로 가는 버튼과 회원가입 페이지는 아래와 같이 구성하였습니다.
로그인 페이지 내의 가입기능 버튼구현 | 회원가입 페이지 |
> 동작은 3개의 경우로 나누었습니다.
1. 회원가입 성공 2.ID가 중복될 경우 3. 작성하지 않은 값이 있을 경우
회원가입 성공 | ID중복 | 작성란 공백 |
> Phpmyadmin을 보시면 위에 사진에서 가입한 정보가 정상적으로 작성된 것을 확인하실 수 있습니다.
2. activity_register.xml (회원가입 페이지 UI) -코드
xml 코드는 아래와 같이 구성하였습니다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/register_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#5F5B5B"
tools:context=".RegisterActivity">
<RadioGroup
android:id="@+id/radiogp"
android:layout_width="230dp"
android:layout_height="85dp"
android:layout_marginStart="120dp"
android:layout_marginEnd="86dp"
android:layout_marginBottom="30dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/sign_up"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reg_username"
app:layout_constraintVertical_bias="0.727">
<RadioButton
android:id="@+id/Man_check"
android:layout_width="115dp"
android:layout_height="52dp"
android:text="Man" />
<RadioButton
android:id="@+id/Women_check"
android:layout_width="115dp"
android:layout_height="52dp"
android:text="Women" />
</RadioGroup>
<EditText
android:id="@+id/reg_username"
android:layout_width="220dp"
android:layout_height="48dp"
android:layout_marginStart="100dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="101dp"
android:layout_marginBottom="399dp"
android:ems="10"
android:hint="Username"
android:inputType="text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reg_email" />
<EditText
android:id="@+id/reg_email"
android:layout_width="220dp"
android:layout_height="48dp"
android:layout_marginStart="100dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="101dp"
android:layout_marginBottom="10dp"
android:ems="10"
android:hint="E-mail"
android:inputType="text"
app:layout_constraintBottom_toTopOf="@+id/reg_username"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reg_password" />
<EditText
android:id="@+id/reg_id"
android:layout_width="220dp"
android:layout_height="48dp"
android:layout_marginTop="103dp"
android:layout_marginBottom="10dp"
android:ems="10"
android:hint="ID"
android:inputType="text"
app:layout_constraintBottom_toTopOf="@+id/reg_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/reg_password"
android:layout_width="220dp"
android:layout_height="48dp"
android:layout_marginStart="100dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="101dp"
android:layout_marginBottom="10dp"
android:ems="10"
android:hint="Password"
android:inputType="text"
app:layout_constraintBottom_toTopOf="@+id/reg_email"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reg_id" />
<TextView
android:id="@+id/textView"
android:layout_width="376dp"
android:layout_height="61dp"
android:layout_marginStart="16dp"
android:layout_marginTop="15dp"
android:layout_marginEnd="19dp"
android:text="Sign Up"
android:textAlignment="center"
android:textColor="#FFC107"
android:textSize="34sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/reg_id"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/sign_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/button_style"
android:text="Sign Up"
android:textColor="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/reg_username" />
<ImageButton
android:id="@+id/btn_back"
android:layout_width="60dp"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:layout_marginTop="23dp"
android:contentDescription="back button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="?attr/actionModeCloseDrawable" />
</androidx.constraintlayout.widget.ConstraintLayout>
위 코드 중 Signup 버튼 부분에서 android:background="@drawable/button_style"가 있습니다.
여기서 drawable폴더에 button_style.xml을 제가 따로 만들어 준 것인데 아래는 그 코드입니다.
노란색의 버튼을 만들고 싶어서 따로 배경을 만들어 주었습니다...
3. RegisterActivity.kt (회원가입 기능 동작 코드)
회원가입 동작코드는 아래와 같이 구성하였습니다.
위에서 말씀드렸다시피 sendPostRequest함수의 동작은 링크 걸어드린 포스팅 보고 오시면 됩니다!
package com.example.test_app
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.test_app.databinding.ActivityRegisterBinding
import com.example.test_app.databinding.LoginMainBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.io.BufferedOutputStream
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
class RegisterActivity : AppCompatActivity() {
private var mBinding: ActivityRegisterBinding? = null //mBinding이 null을 가질수 있다.
private val binding get() = mBinding!! //!! 를 붙여줌으로써 null 이 아니라는것을 보장.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
mBinding = ActivityRegisterBinding.inflate(layoutInflater) //xml파일과 액티비티 연결
setContentView(binding.root) // xml의 부모 바인딩을 가져온다.
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.register_layout)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
/* 본격적인 코드 시작! ↓*/
binding.signUp.setOnClickListener { //sign up 버튼을 누를시 동작하는 함수
val userid = binding.regId.text.toString()
val userpass = binding.regPassword.text.toString()
val useremail = binding.regEmail.text.toString()
val username = binding.regUsername.text.toString()
val gender:Int = when(binding.radiogp.checkedRadioButtonId){
R.id.Man_check-> 1
R.id.Women_check-> 2
else->1 //gender의 기본값은 1로 설정
}
// 비동기 네트워크 요청 처리
CoroutineScope(Dispatchers.IO).launch {//UI기능과 네트워크 요청기능의 충돌방지를 위해 코루틴 사용.
val response = sendPostRequest(userid, userpass, useremail, username, gender)
withContext(Dispatchers.Main) {//Main스레드에서 Toast메세지를 출력하는 UI기능 동작
Toast.makeText(this@RegisterActivity, response.second, Toast.LENGTH_SHORT).show() //서버에서 출력된 메세지
}
}
}
binding.btnBack.setOnClickListener { //뒤로가기 버튼
val intent = Intent(this@RegisterActivity, LoginActivity::class.java)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //LoginActivity로 이동!
}
}
// 서버로 HTTP 요청을 보내는 함수
private fun sendPostRequest(userid: String, password: String,useremail:String,username:String,gender:Int): Pair<Int,String> {
val url = URL("http://yourdomain/register.php")
val postData = JSONObject()
postData.put("id", userid) //json 형식으로 보낸다는것을 주의!! php 에서 $_POST로는 사용불가!
postData.put("pass", password)
postData.put("name", useremail)
postData.put("email", username)
postData.put("gender", gender)
val result = with(url.openConnection() as HttpURLConnection) {
requestMethod = "POST"
doOutput = true
val outputStream = BufferedOutputStream(outputStream)
BufferedWriter(OutputStreamWriter(outputStream, "UTF-8")).use { writer ->
writer.write(postData.toString())
writer.flush()
}
//url로 데이터 전송후 서버로부터 응답을받는 부분
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader(InputStreamReader(inputStream)).use { reader ->
val response = StringBuilder()
var line: String?
while (reader.readLine().also { line = it } != null) {
response.append(line)
}
//Json 응답을 처리
val jsonResponse = JSONObject(response.toString())
val message=jsonResponse.getString("message") //응답에서 key 값이 message인 데이터값 가져오기
Log.d("HTTP_POST", "Response: $response") // 결과값 Logcat 으로 확인가능!
return@with Pair(1,message)
}
} else {
Log.e("HTTP_POST", "Error: $responseCode") //에러발생시 에러코드 출력!
return@with Pair(0,"Error: $responseCode")
}
}
return result
}
}
4. register.php (회원가입 절차 서버코드)
밑에 코드는 서버에서 동작하는 회원가입 절차 코드입니다.
특이사항은 아래 코드에선 서버에 저장하는 비밀번호를 HASH처리 하지않았습니다.
하지만 저는 테스트때문에 하지 않은것이지 서버 개발하실땐 무조건! Hash를 해주셔야합니다.
Hash 처리 하시려면 아래 코드에서 hash함수가 있는부분의 주석을 빼주시면 됩니다!
<?php
include 'dbcon.php';
//register.php
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); // Json 형식에서 배열로 디코딩 ,$_POST를 사용할 수 없기 때문에 이것을 사용.
$userid = $input['id']; //json 형식으로 받음
$userpass = $input['pass'];
$username = $input['name'];
$useremail = $input['email'];
$gender = $input['gender'];
$sql = "select userid from userlog where userid='" . $userid . "'"; //중복되는 아이디 있는지 체크
$result = mysqli_query($db, $sql);
$row = mysqli_fetch_array($result);
if (!(($username) && ($userid) && ($userpass) && ($useremail) && ($gender))) { //작성란 공백시
echo json_encode(['status' => 'error', 'message' => '입력하지 않은 값이 있습니다.']);
} else if ($row) { //중복되는 아이디가 존재할 시
echo json_encode(['status' => 'error', 'message' => '이미 사용중인 아이디입니다.']);
} else { // 회원가입 절차 진행!
// $userpass_hash = hash("sha256", $userpass); //비밀번호 해시처리는 일단 보류
$sql = "insert into userlog (username,userid,userpw,email,gender) values ('" . $username . "','" . $userid . "','" . $userpass . "','" . $useremail . "','" . $gender . "')";
mysqli_query($db, $sql);
echo json_encode(['status' => 'success', 'message' => '회원가입이 완료되었습니다!']);
}
?>
간단한 기능구현이지만 누군가의 공부에 도움이 되길 바랍니다!
긴 글 읽어주셔서 감사합니다!
'모바일 앱개발(Kotlin-PHP-Mysql)' 카테고리의 다른 글
[Android Studio] 게시판 기능 구현 + 리사이클 뷰 생성 (Kotlin) (0) | 2024.10.11 |
---|---|
[Android Studio] 무선 디버깅 연결이 안될때 수동으로 연결하는법. (0) | 2024.10.07 |
[Android Studio] 로그인 한 아이디 저장하는 기능 구현-Kotlin (0) | 2024.10.03 |
[Android Studio] (Kotlin ↔ PHP ↔ MYSQL) 연동을 통해 로그인 기능 구현하기 (0) | 2024.10.02 |
[Android Studio] 같은 네트워크 환경에서 열어놓은 아파치 서버로 POST요청 보낼때 URL작성시 주의할 점! (0) | 2024.10.01 |