안녕하세요! 이번시간에는 Kotlin을 통해서 PHP와 통신하여 MYSQL(DB)에 있는 정보를 가져오는 코드를 짜보겠습니다. 우선 컴퓨터에 설치할 준비물은 다음과 같습니다. 준비가 끝나셨다면 밑에 내용으로 차근차근 따라오시면 됩니다!
[준비물]
- Android Studio
- APM서버(Apache+PHP+Mysql)
- Kotlin언어로 기본적인 기능구현 경험
"Kotlin 언어로 기본적인 기능구현 경험이라 함은?"
> 기본적인 Kotlin 기능구현 강좌 하나정도는 완강하시고 오는 게 좋다는 뜻입니다.
개인적으로 저는 유튜버 '홍드로이드' 님의 Kotlin 개발강의 영상을 완강하여 기본적인 UI설정이나 기능구현을 미리 구현해 본 상태에서 시작하니 훨씬 쉬웠습니다. Kotlin 문법강의는 안 듣더라도 밑에 링크 강의는 꼭 한 번쯤 완강하시는 것을 적극 추천합니다! 총 12개의 강좌이고 솔직히 하루 날 잡고 하면 완강할 수 있을 만큼 설명을 너무 명쾌하게 잘하셨습니다.
혹시 본인이 안드로이드 앱개발을 안 해봤는데 맨땅에 헤딩식으로 이 게시물을 따라오려 한다면 제발 그냥 하지 마시고 제가 걸어드린 밑에 링크 강의 12개를 꼭 따라 해 보고 오셨으면 좋겠습니다.... 그냥 하면 너무 힘듭니다... 경험담입니다...
<목차>
0. Kotlin에서 굳이 PHP를 거쳐가야 하는 이유
1. Kotlin에서 PHP로 로그인 요청 보내기
2. PHP에서 로그인 요청 처리하기
3. PHP가 출력한 내용을 Kotlin으로 가져오기
4. 가져온 정보를 통해 로그인 동작
5. 전체 코드
※만약 전체 완성된 코드만 참고하고 싶다 하시면 목차에서 '5. 전체 코드' 부분을 클릭해 주세요.
<완성되었을 때 화면>
로그인이 성공유무에 따라 출력되는 메시지와 동작이 다른 것을 보실 수 있습니다.
로그인 실패시 화면 | 로그인 성공시 화면 |
[0. Kotlin에서 굳이 PHP를 거쳐가야 하는 이유]
MySQL 과는 'JDBC 드라이버' 라는것을 추가함으로써 PHP를 거치지 않고 직접연동을 할 수도 있지만, 안드로이드 앱은 디컴파일링이 가능합니다. 즉 앱의 Kotlin 코드를 볼 수 있는데 DB정보(root 권한 로그인정보)를 Kotlin 코드에 저장을 할 시 보안상으로 큰 위험이 될 수 있습니다. 그래서 PHP에 DB접속 정보를 적어주게 되면 앱을 디컴파일하더라도 DB정보는 서버에 저장되어 있으므로 DB정보가 탈취당할 일이 없게 됩니다! 이런 보안상의 이유로 PHP를 거쳐서 통신하는 것이 안전합니다!
사실 이게 웹개발과 크게 차이는 없습니다. 웹개발로 대응해보면 아래와 같습니다.
Kotlin => HTML
XML => CSS
PHP => PHP
MYSQL => MYSQL
따라서 웹개발을 해보셨던 분들은 모바일 앱개발이라고 부담감 느낄 필요 없이 저런 대응관계를 지닌다고 생각하고 접근하시면 조금 더 접근하기 수월하실 것 같습니다!
[시작 전 환경설정]
Android Studio의 폴더에서 build.gradle.kts(Moudle:app) 를 열어주세요
이후 dependencies에 다음과 같은 구문을 추가해주세요.
implementation("com.android.volley:volley:1.2.1") // 인터넷 권한 선언
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0") //코루틴 라이브러리 추가
[1. Kotlin에서 PHP로 로그인 요청 보내기]
<PHP로 POST요청을 보내는 함수> - kotlin
(sendPostRequest함수의 일부분/전반부)
fun sendPostRequest(userid: String, password: String): Pair<Int,String> {
val url = URL("http://192.168.0.9/login.php")
val postData = JSONObject()
postData.put("id", userid) //json 형식으로 보낸다는것을 주의!! php 에서 $_POST로는 사용불가!
postData.put("pass", password)
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()
}
<코드설명>
1. val url = URL("http://192.168.0.9/login.php")
> url 변숫값에 요청을 보낼 주소를 입력합니다. 지금 같은 경우는 동일한 컴퓨터에서 서버를 연 상태이므로 안드로이드 기기는 (127.0.0.1) 이 되고 PHP서버는(192.168.0.9)가 됩니다. 즉 login.php 파일이 있다면 http://192.168.0.9/login.php로 요청을 보내야 한다는 뜻이죠! 아파치 서버를 열었다고 무심코 127.0.0.1/login.php로 보내시면 안 됩니다!
본인의 컴퓨터 주소를 알아내는 법은 아래 게시글을 참고하시면 됩니다.
[Android Studio] 로컬 컴퓨터 ip주소 알아내는 법
2. val postData = JSONObject()
> Post 요청을 보낼 데이터를 JSON형식으로 보낼 것이므로 Json객체를 postData에게 부여합니다.
Json형식 = [userid:admin , password: admin123] (이런 식으로 Key:Value 쌍으로 구성되어 있음)
3.postData.put("id", userid)
> 본격적으로 postData안에 key="id" , 변숫값은 userid 값이라는 정보를 담는 과정입니다.
참고로 userid는 사용자가 시도하려는 로그인 ID이고 만약 값이 mooner라면 , [id : mooner] 이런 식으로 전달됩니다.
4. val result = with(url.openConnection() as HttpURLConnection){..... }
> 위에서 설정해 준 url 변수 객체에서 openConnection()을 호출하여 서버에 연결하고, 이 연결 객체를 HttpURLConnection을 사용하여 HTTP프로토콜 통신을 할 거다!라고 알려주는 함수입니다.
함수명이 with인데 이는 스코프 함수로 with(객체){...}로 작성하게 되면 이 안에 작성하는 코드는 객체의 메서드 값을 반복적으로 참조할 필요 없이 사용할 수 있게 됩니다.
5.doOutput = true
> OutputStream을 통해 데이터를 전송할 수 있게 됩니다.
※OutputStream은 Java와 Kotlin에서 출력을 처리하는 바이트 스트림입니다.
6.val outputStream = BufferedOutputStream(outputStream)
> BufferedOutputStream을 사용하면 outputStream안에 내용을 버퍼를 통해 하나하나 보내는 것이 아닌 모아놨다가 한꺼번에 보내는 등 효율적으로 전달할 수 있습니다.
7.BufferedWriter(OutputStreamWriter(outputStream, "UTF-8")). use { writer ->
writer.write(postData.toString())
writer.flush()
}
> OutputStreamWriter는 outputStream에 있는 문자를 UTF-8로 인코딩하여 바이트 스트림으로 바꿔주는 작업을 거치게 합니다. 네트워크로 값이 전달될 때는 바이트 스트림을 통해 전달되기 때문에 인코딩 작업이 꼭 필요합니다.
이후. use {writer ->를 통해 "UTF-8로 인코딩 된 OutputStream"이란 거대한 객체를 writer 이란 이름으로 간단히 표현할 거다라는 표현입니다. 그래서 밑에 코드를 보시면 writer.write를 통해 저희가 보낼 값인 postData를 OutputStream에 저장할 수 있는 겁니다.
마지막으로 writer.flush()를 통해서 버퍼에 있는 로그인 요청을 192.168.0.9/login.php로 보내주게 됩니다.
[2. PHP에서 로그인 요청 처리하기]
[Kotlin에서 보내온 요청을 처리하는 코드] - PHP
<?php
include 'dbcon.php';
//login.php
session_start();
session_cache_expire(60);// 세션 유지시간 1시간
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); // Json 형식에서 배열로 디코딩 ,$_POST를 사용할 수 없기 때문에 이것을 사용.
$userid = $input['id'];
$userpass = $input['pass'];
$result = mysqli_query($db, "select * from userlog where userid='" . $userid . "' and userpw='" . $userpass . "'");
$userdata = mysqli_fetch_assoc($result);
if ($userdata) {
$_SESSION['username'] = $userdata['username']; // 세션에 유저정보 저장
$_SESSION['userid'] = $userdata['userid'];
$_SESSION['userpw'] = $userdata['userpw'];
echo json_encode(['status' => 'success', 'data' => $userdata, 'message' => 'wellcome!! ' . $userdata['username']]);
} else {
echo json_encode(['status' => 'error', 'message' => 'Wrong ID or Password']);
}
?>
[코드설명]
1.$inputJSON = file_get_contents('php://input');
> php서버로 들어온 'input' 요청을 JSON타입의 변수인 inputJSON으로 받기.
2.$input = json_decode($inputJSON, TRUE);
> PHP 코드로 접근할 수 있게 JSON 형식에서 배열로 디코딩.
3.echo json_encode(['status' => 'success', 'data' => $userdata, 'message' => 'wellcome!! '. $userdata ['username']]);
> 이후 식별&인증절차를 통해 로그인 인증 후 그에 따라 메시지와 유저정보를 json 타입으로 인코딩하여 출력.
[3.PHP가 출력한 내용을 Kotlin으로 가져오기]
<PHP에 출력된 내용을 가져오는 코드> - kotlin
(sendPostRequest함수의 일부분/후반부)
//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
}
[코드설명]
1. if (responseCode == HttpURLConnection.HTTP_OK) {
> 보낸 HTTP요청이 성공했으면 이면 if문을 실행!
2.var line: String?
> "line이라는 변수를 선언하는데 이 변수는 null 값을 가질 수 있다"라는 뜻
3. while (reader.readLine(). also { line = it }!= null) {
response.append(line)
}
> "reader객체에서 한 줄 읽어온다 , 또한 line이라는 변수에 읽어온 한 줄을 넣어준다 , 읽어온 값이 null 이 아닐 때까지.
이후 response라는 변수에 읽어온 한줄한줄을 붙여준다."라는 뜻
4.val message=jsonResponse.getString("message")
> 로그인 결과를 출력해 주기 위해 PHP서버에 출력된 응답 중 Key값 = message 인 내용을 변수에 넣어준다.
5.return@with Pair(1, message)
> 로그인 성공 시 1이라는 숫자와 message라는 변수를 포함한 쌍을 반환한다.
"밑에 사진처럼 실패&성공 시 출력된 응답을 가져오게 됩니다."
[4. 가져온 정보를 통해 로그인 동작]
[로그인 버튼을 눌렀을 때 동작하는 코드] - Kotlin
binding.btnLogin.setOnClickListener { //로그인 버튼 클릭시 동작
val userid: String = binding.loginid.text.toString() //에딧테스트에 입력되있는값
val userpass: String = binding.loginpass.text.toString() //에딧테스트에 입력되있는값
CoroutineScope(Dispatchers.IO).launch {
var requestresult = sendPostRequest(userid, userpass) //서버로 id ,password 를 보내준 후 응답을 받음
Log.d("requestresult: ",requestresult.toString())
withContext(Dispatchers.Main) {
if (requestresult.first == 1) { //요청을 한후 응답을 받았을때
// 로그인 결과에 대한 Toast 메세지 출력 ↓
if(requestresult.second=="Wrong ID or Password") { //로그인이 틀렸을때
Toast.makeText(
this@LoginActivity,
requestresult.second,
Toast.LENGTH_SHORT
).show()
}else { //로그인이 성공하였을때
Toast.makeText(
this@LoginActivity,
requestresult.second,
Toast.LENGTH_SHORT
).show()
val intent = Intent(
this@LoginActivity,
SubActivity::class.java
)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //SubActivity로 이동!
}
} else { //요청을 했지만 응답을 못받았을때
Toast.makeText(this@LoginActivity, "Login failed. Please try again.", Toast.LENGTH_SHORT).show()
}
}
}
}
[코드설명]
1. var requestresult = sendPostRequest(userid, userpass)
> sendPostRequest에서 반환된 Pair를 requestresult에 넣어줌.
만약 로그인 성공이면 Pair=[1, message]가 되겠지요?
2.if (requestresult.first == 1) {
> Pair의 첫 번째 값을 requestresult.first라고 표현. 로그인 성공 시에 1이 반환되고 같으면 if문 실행!
3.CoroutineScope(Dispatchers.IO). launch
> 코루틴 함수로 백그라운드 단에서 동작한다는 뜻입니다. 요청이 오고 가는 네트워크 동작과 화면에 메시지를 띄워주는 UI 동작이 서로 겹치게 되면 앱이 중단되는 현상이 일어납니다. 이를 방지하기 위해 코루틴을 사용하여 네트워크는 백그라운드 스레드에서, UI동작은 메인스레드에서 실행되도록 설정합니다.
4.withContext(Dispatchers.Main) {
> 위에서 설명한 UI를 Main스레드에서 실행시킬 수 있는 함수입니다. 이 안에서 실행되는 코드는 Main스레드에서 실행이 됩니다.
[5. 전체 코드]
[LoginActivity.kt]
package com.example.test_app
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.webkit.JavascriptInterface
import android.widget.CheckBox
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.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.InputStream
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
class LoginActivity : AppCompatActivity() {
private var mBinding: LoginMainBinding? = null //mBinding이 null을 가질수 있다.
private val binding get() = mBinding!! //!! 를 붙여줌으로써 null 이 아니라는것을 보장.
override fun onCreate(savedInstanceState: Bundle?) { //해당 액티비티가 최초 실행되었을때 수행
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.login_main) // xml 화면 뷰를 연결한다.
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.tvtitle)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
mBinding = LoginMainBinding.inflate(layoutInflater) //xml파일과 액티비티 연결
setContentView(binding.root) // xml의 부모 바인딩을 가져온다.
// binding.tvtitle.setText("") //텍스트의 값을 변경한다
binding.btnLogin.setOnClickListener { //로그인 버튼 클릭시 동작
val userid: String = binding.loginid.text.toString() //에딧테스트에 입력되있는값
val userpass: String = binding.loginpass.text.toString() //에딧테스트에 입력되있는값
CoroutineScope(Dispatchers.IO).launch {
var requestresult = sendPostRequest(userid, userpass) //서버로 id ,password 를 보내준 후 응답을 받음
Log.d("requestresult: ",requestresult.toString())
withContext(Dispatchers.Main) {
if (requestresult.first == 1) { //요청을 한후 응답을 받았을때
// 로그인 결과에 대한 Toast 메세지 출력 ↓
if(requestresult.second=="Wrong ID or Password") { //로그인이 틀렸을때
Toast.makeText(
this@LoginActivity,
requestresult.second,
Toast.LENGTH_SHORT
).show()
}else { //로그인이 성공하였을때
Toast.makeText(
this@LoginActivity,
requestresult.second,
Toast.LENGTH_SHORT
).show()
val intent = Intent(
this@LoginActivity,
SubActivity::class.java
)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //SubActivity로 이동!
}
} else { //요청을 했지만 응답을 못받았을때
Toast.makeText(this@LoginActivity, "Login failed. Please try again.", Toast.LENGTH_SHORT).show()
}
}
}
}
//TODO: 저장된 데이터를 로드
loadData() //저장되어 있던 값을 setText
val autoLoginCheck = findViewById<CheckBox>(R.id.auto_login) //체크박스 객체생성
autoLoginCheck.setOnCheckedChangeListener { _, isChecked -> //체크박스가 체크되어있는지 확인
if (isChecked) { // 체크 되어있으면 현재 아이디값을 /data/data/<your_package_name>/shared_prefs/경로로 저장.
saveData()
}
}
}
private fun loadData() {
val pref = getSharedPreferences("pref", 0)
binding.loginid.setText(
pref.getString(
"id",
""
)
) // 1번째 인자는 키 값, 2번째는 키 값에 데이터가 존재하지 않을경우 데체 값
binding.loginpass.setText(
pref.getString(
"password",
""
)
) // 1번째 인자는 키 값, 2번째는 키 값에 데이터가 존재하지 않을경우 데체 값
}
private fun saveData() {
val pref = getSharedPreferences("pref", 0)
val edit = pref.edit()//수정모드
edit.putString("id", binding.loginid.text.toString()) // 1번째 인자에는 키 값을 , 2번째 인자에는 실제 담아둘 값
edit.putString("password", binding.loginpass.text.toString())
edit.apply() // 값을 저장 완료
}
// 서버로 HTTP 요청을 보내는 함수
fun sendPostRequest(userid: String, password: String): Pair<Int,String> {
val url = URL("http://192.168.0.9/login.php")
val postData = JSONObject()
postData.put("id", userid) //json 형식으로 보낸다는것을 주의!! php 에서 $_POST로는 사용불가!
postData.put("pass", password)
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
}
}
[login_main.xml]
(LoginActivity.kt의 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/login_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
tools:context=".LoginActivity">
<TextView
android:id="@+id/tvtitle"
android:layout_width="215dp"
android:layout_height="129dp"
android:layout_marginTop="16dp"
android:fontFamily="@font/kascript"
android:text="Octagram"
android:textAlignment="center"
android:textAllCaps="false"
android:textAppearance="@style/Animation.Design.BottomSheetDialog"
android:textColor="#32AF3F"
android:textSize="48sp"
android:textStyle="bold|italic"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.223" />
<EditText
android:id="@+id/loginid"
android:layout_width="229dp"
android:layout_height="61dp"
android:ems="10"
android:hint="아이디를 입력하세요"
android:inputType="textPersonName"
android:textColor="#8BC34A"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.449" />
<EditText
android:id="@+id/loginpass"
android:layout_width="229dp"
android:layout_height="61dp"
android:ems="10"
android:hint="비밀번호를 입력하세요"
android:inputType="textPersonName"
android:textColor="#8BC34A"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.54" />
<Button
android:id="@+id/btn_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="76dp"
android:text="Login"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginid" />
<ImageView
android:id="@+id/iv_bug"
android:layout_width="84dp"
android:layout_height="73dp"
android:layout_marginStart="162dp"
android:layout_marginTop="144dp"
android:layout_marginEnd="165dp"
android:contentDescription="logo"
app:layout_constraintBottom_toTopOf="@+id/project_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_login"
app:srcCompat="@drawable/octopus" />
<TextView
android:id="@+id/project_title"
android:layout_width="161dp"
android:layout_height="22dp"
android:layout_marginStart="125dp"
android:layout_marginEnd="125dp"
android:layout_marginBottom="16dp"
android:contentDescription="madeby"
android:text="by.Dr_Octopus"
android:textAlignment="center"
android:textColor="#FF5722"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_bug" />
<CheckBox
android:id="@+id/auto_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="157dp"
android:layout_marginEnd="158dp"
android:checked="false"
android:text="자동로그인"
android:textColor="#4CAF50"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_login" />
</androidx.constraintlayout.widget.ConstraintLayout>
[login.php]
<?php
include 'dbcon.php';
//login.php
session_start();
session_cache_expire(60);// 세션 유지시간 1시간
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); // Json 형식에서 배열로 디코딩 ,$_POST를 사용할 수 없기 때문에 이것을 사용.
$userid = $input['id'];
$userpass = $input['pass'];
$result = mysqli_query($db, "select * from userlog where userid='" . $userid . "' and userpw='" . $userpass . "'");
$userdata = mysqli_fetch_assoc($result);
if ($userdata) {
$_SESSION['username'] = $userdata['username']; // 세션에 유저정보 저장
$_SESSION['userid'] = $userdata['userid'];
$_SESSION['userpw'] = $userdata['userpw'];
echo json_encode(['status' => 'success', 'data' => $userdata, 'message' => 'wellcome!! ' . $userdata['username']]);
} else {
echo json_encode(['status' => 'error', 'message' => 'Wrong ID or Password']);
}
?>
긴 글 읽어주셔서 감사합니다!
'모바일 앱개발(Kotlin-PHP-Mysql)' 카테고리의 다른 글
[Android Studio] 회원가입 기능 구현 - Kotlin (0) | 2024.10.05 |
---|---|
[Android Studio] 로그인 한 아이디 저장하는 기능 구현-Kotlin (0) | 2024.10.03 |
[Android Studio] 같은 네트워크 환경에서 열어놓은 아파치 서버로 POST요청 보낼때 URL작성시 주의할 점! (0) | 2024.10.01 |
[Android Studio] layout폴더와 activity_main.xml 이 없을때 해결방법 (0) | 2024.09.27 |
[Android Studio]안드로이드 스튜디오 무선 기기연결로 작업하기! (1) | 2024.09.24 |