안녕하세요! 저번 포스팅 '게시글 읽기 페이지 구현' 에 이어서 CRUD 기능을 구현해보겠습니다. 사실 게시글 읽기 기능 자체가 CRUD중 Read에 해당하는 기능인지라 이번 포스팅에선 Create & Update & Delete 기능을 구현해보겠습니다.
0. 완성된 동작 미리보기
0.1 C_(Create) 기능
게시판 내의 작성버튼 클릭 | 게시글 작성 |
0.2 R_(Read) 기능
작성된 게시글 클릭 | 게시물 내용 확인 |
0.3 U_(Update) 기능
게시글 수정 버튼 클릭 | 게시글 수정 |
0.4 D_(Delete) 기능
게시글 내의 삭제버튼 클릭 | 삭제된 게시글 확인 |
1. R_(read) 기능 구현
사실 read기능은 저번 포스팅에서 이미 구현해두었습니다. Read기능 설명까지 이 게시물에 들어가게 될경우 한 포스팅에 너무 많은 정보가 들어갈것을 우려하여 '게시글 읽기 페이지 구현' 포스팅 링크를 달아드리겠습니다. 밑에 Create&Update&Delete 를 보시기전에 먼저 보고오시는것을 추천합니다!
[Andorid Studio] 게시글 읽기 페이지 구현(CRUD->R)
하지만 전에 게시물에 비하여 '게시글 수정버튼 클릭시 동작 ' , '삭제 버튼 클릭시 동작' 같은 추가된 동작들이 일부있으므로 ReadActivity.kt 전체 코드를 작성해두겠습니다! 밑에 내용들 이해하는데 참고용으로 써주세요!
[ReadActivity.kt 전체코드]
package com.example.test_app
import ...
class ReadActivity : AppCompatActivity() {
private var mBinding: ActivityReadBinding? = null //mBinding이 null을 가질수 있다.
private val binding get() = mBinding!! //!! 를 붙여줌으로써 null 이 아니라는것을 보장.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_read)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.post_read_layout)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
mBinding = ActivityReadBinding.inflate(layoutInflater) //xml파일과 액티비티 연결
setContentView(binding.root) // xml의 부모 바인딩을 가져온다.
Log.d("post_idx: ", intent.getIntExtra("idx",-1).toString()) //게시글의 인덱스값을 가져온다.
val idx:Int =intent.getIntExtra("idx",-1)
CoroutineScope(Dispatchers.IO).launch {
val postinfo: ArrayList<Post> = sendPostRequest_read(idx).second //post요청은 IO스레드에서
withContext(Dispatchers.Main) { //UI 업데이트는 Main단에서
binding.postTitle.setText(postinfo.get(0).title)
binding.postContent.setText(postinfo.get(0).content)
binding.btnBack2.setOnClickListener { //뒤로가기 버튼
super.onBackPressed() //뒤로가기 기능
}
}
}
binding.btnUpdate.setOnClickListener { //게시글 수정하기 버튼을 눌렀을때 동작하는 함수
val idx:Int =intent.getIntExtra("idx",-1) //일단 게시글의 idx를 담아둔다.
CoroutineScope(Dispatchers.IO).launch{
val postinfo=sendPostRequest_read(idx).second //게시글 정보를 가져온다.
withContext(Dispatchers.Main){
val intent = Intent(this@ReadActivity, UpdateActivity::class.java)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
intent.putExtra("idx",postinfo.get(0).idx) //다음 액티비티로 idx값 전달
intent.putExtra("title",postinfo.get(0).title) //다음 액티비티로 게시글 제목 전달
intent.putExtra("content",postinfo.get(0).content) //다음 액티비티로 게시글 내용 전달
intent.putExtra("writer",postinfo.get(0).writer) //다음 액티비티로 게시글 내용 전달
startActivity(intent) //SubActivity로 이동!
}
}
}
binding.btnDelete.setOnClickListener {
val idx:Int = intent.getIntExtra("idx",-1)
CoroutineScope(Dispatchers.IO).launch{
val reqeustResponse=sendPostRequest_delete(idx) //idx값에 해당하는 글을 삭제요청을 한다.
withContext(Dispatchers.Main){
Toast.makeText(this@ReadActivity,reqeustResponse.second,Toast.LENGTH_SHORT).show()
val intent = Intent(this@ReadActivity, SubActivity::class.java)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //SubActivity로 이동!
finish() // ReadActivity종료
}
}
}
}
// 서버로 HTTP 요청을 보내는 함수
private fun sendPostRequest_read(idx: Int): Pair<Int,ArrayList<Post>> { //인덱스에 해당하는 게시물 1개만 가져오는 함수
val url = URL("http://192.168.0.10/post_read.php")
val postData = JSONObject()
postData.put("idx", idx) //json 형식으로 보낸다는것을 주의!! php 에서 $_POST로는 사용불가!
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 응답을 문자열로 가져옴
}
//JSON 응답을 처리
val jsonResponse= JSONObject(response.toString())//응답을 다시 json객체로 변환
val status=jsonResponse.getString("status")//응답에서 key=status 인 데이터 저장
val postArray=jsonResponse.getJSONArray("posts")//응답에서 posts라는 이름의 배열 저장
//게시글 정보를 List로 변환 <PostAdapter와의 호환을 위하여 변환해야함>
val postList= ArrayList<Post>() //Post형식의 배열을 가진 동적List 생성
for (i in 0 until 1){
val postJson=postArray.getJSONObject(0)//i번째 게시글 정보를 가져온다.
val post= Post(
title=postJson.getString("title"),
writer=postJson.getString("writer"),
gender = postJson.getInt("gender"),
date = postJson.getString("date"),
idx=postJson.getInt("idx"),
content = postJson.getString("content")
)
//Post 객체 생성 후 리스트에 추가
postList.add(post)
}
Log.d("HTTP_POST", "Response: $response") // 결과값 Logcat 으로 확인가능!
return@with Pair(1,postList)
}
} else {
Log.e("HTTP_POST", "Error: $responseCode") //에러발생시 에러코드 출력!
return@with Pair(0, ArrayList<Post>())
}
}
return result
}
private fun sendPostRequest_delete(idx:Int): Pair<Int,String> {
val url = URL("http://192.168.0.10/post_delete.php")
val postData = JSONObject()
val sessionPref=getSharedPreferences("sessionId", MODE_PRIVATE)
val sessionId = sessionPref.getString("sessionId",null) //세션 ID값 저장.
postData.put("idx",idx)
postData.put("sessionId",sessionId)
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 응답을 문자열로 가져옴
}
//JSON 응답을 처리
val jsonResponse= JSONObject(response.toString())//응답을 다시 json객체로 변환
val message=jsonResponse.getString("message") // 응답에서 message값 가져오기
Log.d("HTTP_POST", "createResponse: $response") // 결과값 Logcat 으로 확인가능!
return@with Pair(1,message)
}
} else {
Log.e("HTTP_POST", "Error: $responseCode") //에러발생시 에러코드 출력!
return@with Pair(0,"에러가 발생하였습니다.")
}
}
return result
}
}
2. C_(Create) 기능 구현
> 구성파일 : CreateActivity(Kotlin) , acrivity_create.xml , post_create.php
2.1 CreateActivity(Kotlin)
package com.example.test_app
import ...
class CreateActivity : AppCompatActivity() {
private var mBinding: ActivityCreateBinding? = null //mBinding이 null을 가질수 있다.
private val binding get() = mBinding!! //!! 를 붙여줌으로써 null 이 아니라는것을 보장.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
mBinding = ActivityCreateBinding.inflate(layoutInflater) //xml파일과 액티비티 연결
setContentView(binding.root) // xml의 부모 바인딩을 가져온다.
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
binding.btnCreate.setOnClickListener{ //게시글 작성 버튼 클릭시
val postTitle=binding.postTitle2.text.toString()//edittext에 저장된 값 불러오기
val postContent=binding.postContent2.text.toString()
CoroutineScope(Dispatchers.IO).launch { //입출력 스레드 동작
val requestResult = sendPostRequest_create(postTitle, postContent)
Log.d("createResponse : ",requestResult.second)
withContext(Dispatchers.Main) { // UI 스레드 동작
if (requestResult.second == "게시글이 작성되었습니다!") {
Toast.makeText(this@CreateActivity, requestResult.second, Toast.LENGTH_SHORT).show()
val intent = Intent(this@CreateActivity, SubActivity::class.java)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //SubActivity로 이동!
finish()
} else {
Toast.makeText(this@CreateActivity, requestResult.second, Toast.LENGTH_SHORT).show()
}
}
}
}
binding.btnBack2.setOnClickListener{ //back 버튼 클릭시
super.onBackPressed()
}
}
private fun sendPostRequest_create(title: String,content:String): Pair<Int,String> {
val url = URL("http://192.168.0.10/post_create.php")
val postData = JSONObject()
val sessionPref=getSharedPreferences("sessionId", MODE_PRIVATE)
val sessionId = sessionPref.getString("sessionId",null) //세션 ID값 저장.
postData.put("title", title) //json 형식으로 보낸다는것을 주의!! php 에서 $_POST로는 사용불가!
postData.put("content",content)
postData.put("sessionId",sessionId)
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 응답을 문자열로 가져옴
}
//JSON 응답을 처리
val jsonResponse= JSONObject(response.toString())//응답을 다시 json객체로 변환
val message=jsonResponse.getString("message") // 응답에서 message값 가져오기
Log.d("HTTP_POST", "createResponse: $response") // 결과값 Logcat 으로 확인가능!
return@with Pair(1,message)
}
} else {
Log.e("HTTP_POST", "Error: $responseCode") //에러발생시 에러코드 출력!
return@with Pair(0,"에러가 발생하였습니다.")
}
}
return result
}
}
2.2 activity_create.xml
↓ activity_create.xml 의 UI
↓ activity_create.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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CreateActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/post_create_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3C3A3A"
tools:context=".CreateActivity"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp">
<EditText
android:id="@+id/post_content2"
android:layout_width="321dp"
android:layout_height="450dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="92dp"
android:background="#E9DADA"
android:gravity="center"
android:hint="게시글 내용"
android:inputType="textMultiLine"
android:textColor="#000000"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.505"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/post_title2"
app:layout_constraintVertical_bias="0.03" />
<EditText
android:id="@+id/post_title2"
android:layout_width="321dp"
android:layout_height="63dp"
android:layout_marginTop="100dp"
android:background="#E9DADA"
android:gravity="center"
android:hint="제목"
android:textColor="#000000"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/btn_back2"
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" />
<Button
android:id="@+id/btn_create"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="작성"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/post_content2" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
2.3 post_create.php
<?php
include 'dbcon.php';
//subpage.php
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); // Json 형식에서 배열로 디코딩 ,$_POST를 사용할 수 없기 때문에 이것을 사용.
$post_title = $input['title'] ?? NULL; //입력값이 없으면 빈 문자열로 처리
$post_content = $input['content'] ?? NULL;
$sessionid = $input['sessionId'] ?? NULL;
if ($sessionid) {
session_id($sessionid); //전달받은 세션ID를 현재 세션에 저장
session_start();
session_cache_expire(60);// 세션 유지시간 1시간
}
$sql = "Insert into post_info (post_title,post_writer,gender,content) values ('$post_title','" . $_SESSION['username'] . "', " . $_SESSION['gender'] . ", '$post_content')";
if ($post_title != NULL && $post_content != NULL) {
mysqli_query($db, $sql);
echo json_encode(['status' => 'success', 'message' => '게시글이 작성되었습니다!']);
} else if (!isset($_SESSION['username']) || !isset($_SESSION['userid'])) {
echo json_encode(['status' => 'error', 'message' => '로그인이 필요합니다!']); //로그인 안하고 작성시 에러 출력
exit();
} else {
echo json_encode(['status' => 'error', 'message' => '에러가 발생하였습니다!']);
}
?>
3. U_(Update) 기능 구현
구성파일 > UpdateActivity.kt , activity_update.xml , post_update.php
3.1 UpdateActivity.kt
package com.example.test_app
import ...
class UpdateActivity : AppCompatActivity() {
private var mBinding: ActivityUpdateBinding? = null //mBinding이 null을 가질수 있다.
private val binding get() = mBinding!! //!! 를 붙여줌으로써 null 이 아니라는것을 보장.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
mBinding = ActivityUpdateBinding.inflate(layoutInflater) //xml파일과 액티비티 연결
setContentView(binding.root) // xml의 부모 바인딩을 가져온다.
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// ↓ read페이지에서 수정버튼을 눌렀을때 넘어오는 값들
val idx = intent.getIntExtra("idx",-1) //게시글 인덱스 번호
val title= intent.getStringExtra("title") //String? 을 반환 //게시글 제목
val content=intent.getStringExtra("content") //String? 을 반환 // 게시글 내용
val writer=intent.getStringExtra("writer") //게시글 작성자
binding.postTitle.setText(title) //넘어온 게시글 제목을 현재 edittext에 써준다.
binding.postContent.setText(content)//넘어온 게시글 내용을 현재 edittext에 써준다.
binding.btnUpdate.setOnClickListener {
CoroutineScope(Dispatchers.IO).launch{
val updateTitle= binding.postTitle.text.toString() //수정한 제목을 저장
val updateContent=binding.postContent.text.toString() // 수정한 내용을 저장
val requestResponse=sendPostRequest_update(updateTitle?:"",updateContent?:"",writer?:"",idx) //String? 이기때문에 title값없을시 "" 이라는 공백추가.
withContext(Dispatchers.Main){
if(requestResponse.second=="게시글이 수정되었습니다!"){
Toast.makeText(this@UpdateActivity,requestResponse.second,Toast.LENGTH_SHORT).show() // 토스트 메시지 띄우기
val intent = Intent(this@UpdateActivity, SubActivity::class.java)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //SubActivity로 이동!
finish() // update 액티비티 종료!
}else{
Toast.makeText(this@UpdateActivity,requestResponse.second,Toast.LENGTH_SHORT).show() // 토스트 메시지 띄우기
val intent = Intent(this@UpdateActivity, SubActivity::class.java)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //SubActivity로 이동!
finish() // update 액티비티 종료!
}
}
}
}
binding.btnBack2.setOnClickListener { //뒤로가기 버튼 클릭시 뒤로가기
super.onBackPressed()
}
}
private fun sendPostRequest_update(title: String,content:String,writer:String,idx:Int): Pair<Int,String> {
val url = URL("http://192.168.0.10/post_update.php")
val postData = JSONObject()
val sessionPref=getSharedPreferences("sessionId", MODE_PRIVATE)
val sessionId = sessionPref.getString("sessionId",null) //세션 ID값 저장.
postData.put("title", title) //json 형식으로 보낸다는것을 주의!! php 에서 $_POST로는 사용불가!
postData.put("content",content) //게시글 내용
postData.put("sessionId",sessionId) //세션id 서버로 전송
postData.put("idx",idx) //게시글 인덱스번호
postData.put("writer",writer) //게시글 작성자
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 응답을 문자열로 가져옴
}
//JSON 응답을 처리
val jsonResponse= JSONObject(response.toString())//응답을 다시 json객체로 변환
val message=jsonResponse.getString("message") // 응답에서 message값 가져오기
Log.d("HTTP_POST", "createResponse: $response") // 결과값 Logcat 으로 확인가능!
return@with Pair(1,message)
}
} else {
Log.e("HTTP_POST", "Error: $responseCode") //에러발생시 에러코드 출력!
return@with Pair(0,"에러가 발생하였습니다.")
}
}
return result
}
}
3.2 activity_update.xml
↓ activity_update.xml 의 UI
↓ activity_update.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/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UpdateActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/post_update_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#3C3A3A"
tools:context=".UpdateActivity">
<EditText
android:id="@+id/post_content"
android:layout_width="321dp"
android:layout_height="450dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="92dp"
android:background="#E9DADA"
android:gravity="center"
android:hint="게시글 내용"
android:inputType="textMultiLine"
android:textColor="#000000"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/post_title"
app:layout_constraintVertical_bias="0.0" />
<EditText
android:id="@+id/post_title"
android:layout_width="321dp"
android:layout_height="63dp"
android:layout_marginTop="100dp"
android:background="#E9DADA"
android:gravity="center"
android:hint="제목"
android:textColor="#000000"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/btn_back2"
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" />
<Button
android:id="@+id/btn_update"
android:layout_width="115dp"
android:layout_height="65dp"
android:layout_marginTop="30dp"
android:text="수정"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/post_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
3.3 post_update.php
> 넘어온 sessionId 에 해당하는 username과 게시글 작성자를 비교하는 인증절차를 거칩니다.
<?php
include 'dbcon.php';
//subpage.php
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); // Json 형식에서 배열로 디코딩 ,$_POST를 사용할 수 없기 때문에 이것을 사용.
$post_title = $input['title'] ?? NULL; //입력값이 없으면 빈 문자열로 처리
$post_content = $input['content'] ?? NULL;
$sessionid = $input['sessionId'] ?? NULL;
$idx = $input['idx'] ?? NULL;
$post_writer = $input['writer'] ?? NULL;
if ($sessionid) {
session_id($sessionid); //전달받은 세션ID를 현재 세션에 저장
session_start();
session_cache_expire(60);// 세션 유지시간 1시간
}
$sql = "update post_info set post_title='$post_title',content='$post_content' where idx=$idx"; // 게시글 수정 sql 구문
if ($_SESSION['username'] == $post_writer) {
mysqli_query($db, $sql);
echo json_encode(['status' => 'success', 'message' => '게시글이 수정되었습니다!']);
} else {
echo json_encode(['status' => 'error', 'message' => '권한이 없습니다.']);
}
?>
4. D_(Delete) 기능 구현
구성요소 > ReadActivity.kt(게시글 읽기페이지 내 함수로 구현) , post_delete.php
4.1 ReadActivity.kt(sendPostRequest_delete(idx:Int) 함수로 구현)
package com.example.test_app
import ...
class ReadActivity : AppCompatActivity() {
private var mBinding: ActivityReadBinding? = null //mBinding이 null을 가질수 있다.
private val binding get() = mBinding!! //!! 를 붙여줌으로써 null 이 아니라는것을 보장.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_read)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.post_read_layout)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
mBinding = ActivityReadBinding.inflate(layoutInflater) //xml파일과 액티비티 연결
setContentView(binding.root) // xml의 부모 바인딩을 가져온다.
Log.d("post_idx: ", intent.getIntExtra("idx",-1).toString()) //게시글의 인덱스값을 가져온다.
val idx:Int =intent.getIntExtra("idx",-1)
CoroutineScope(Dispatchers.IO).launch {
val postinfo: ArrayList<Post> = sendPostRequest_read(idx).second //post요청은 IO스레드에서
withContext(Dispatchers.Main) { //UI 업데이트는 Main단에서
binding.postTitle.setText(postinfo.get(0).title)
binding.postContent.setText(postinfo.get(0).content)
binding.btnBack2.setOnClickListener { //뒤로가기 버튼
super.onBackPressed() //뒤로가기 기능
}
}
}
/* 게시글 수정시 작동하는 함수는 delete함수 하고있으니설명상 생략!*/
binding.btnDelete.setOnClickListener { //게시글 삭제 버튼 클릭시
val idx:Int = intent.getIntExtra("idx",-1)
CoroutineScope(Dispatchers.IO).launch{
val reqeustResponse=sendPostRequest_delete(idx) //idx값에 해당하는 글을 삭제요청을 한다.
withContext(Dispatchers.Main){
Toast.makeText(this@ReadActivity,reqeustResponse.second,Toast.LENGTH_SHORT).show()
val intent = Intent(this@ReadActivity, SubActivity::class.java)//다음 화면으로 이동하기 위한 인텐트 객체 생성.
startActivity(intent) //SubActivity로 이동!
finish() // ReadActivity종료
}
}
}
}
// 서버로 HTTP 요청을 보내는 함수
/* 게시글 읽어오는 sendPostRequest_read 함수는 delete설명중이니 가독성을 위해 생략! */
private fun sendPostRequest_delete(idx:Int): Pair<Int,String> {
val url = URL("http://192.168.0.10/post_delete.php")
val postData = JSONObject()
val sessionPref=getSharedPreferences("sessionId", MODE_PRIVATE)
val sessionId = sessionPref.getString("sessionId",null) //세션 ID값 저장.
postData.put("idx",idx)
postData.put("sessionId",sessionId)
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 응답을 문자열로 가져옴
}
//JSON 응답을 처리
val jsonResponse= JSONObject(response.toString())//응답을 다시 json객체로 변환
val message=jsonResponse.getString("message") // 응답에서 message값 가져오기
Log.d("HTTP_POST", "createResponse: $response") // 결과값 Logcat 으로 확인가능!
return@with Pair(1,message)
}
} else {
Log.e("HTTP_POST", "Error: $responseCode") //에러발생시 에러코드 출력!
return@with Pair(0,"에러가 발생하였습니다.")
}
}
return result
}
}
4.2 post_delete.php
>select문으로 가져온 게시글 작성자와 현재 세션의 사용자의 이름이 같으면 삭제절차 진행.
<?php
include 'dbcon.php';
//subpage.php
$inputJSON = file_get_contents('php://input');
$input = json_decode($inputJSON, TRUE); // Json 형식에서 배열로 디코딩 ,$_POST를 사용할 수 없기 때문에 이것을 사용.
$sessionid = $input['sessionId'] ?? NULL;
$idx = $input['idx'] ?? NULL;
if ($sessionid) {
session_id($sessionid); //전달받은 세션ID를 현재 세션에 저장
session_start();
session_cache_expire(60);// 세션 유지시간 1시간
}
$sql = "select post_writer from post_info where idx=$idx"; // 게시글 작성자 정보 가져오기
$result = mysqli_query($db, $sql);
$row = mysqli_fetch_array($result);
$post_writer = $row['post_writer'];
if ($_SESSION['username'] == $post_writer) {
$sql2 = "delete from post_info where idx=$idx";
mysqli_query($db, $sql2);
echo json_encode(['status' => 'success', 'message' => '게시글이 삭제되었습니다!']);
} else {
echo json_encode(['status' => 'error', 'message' => '권한이 없습니다.']);
}
?>
긴 글 읽어주셔서 감사합니다!
'모바일 앱개발(Kotlin-PHP-Mysql)' 카테고리의 다른 글
클래스 - 객체 - 인스턴트 : 확실히 알고 넘어가자! (feat.붕어빵) (0) | 2024.10.21 |
---|---|
[Android Studio] PHP 서버와 통신시 세션ID 활용하는 방법 -Kotlin (0) | 2024.10.17 |
[Android Studio] 게시글 읽기 페이지 구현(CRUD->R) - Kotlin (1) | 2024.10.13 |
[Android Studio] 게시판 기능 구현 + 리사이클 뷰 생성 (Kotlin) (0) | 2024.10.11 |
[Android Studio] 무선 디버깅 연결이 안될때 수동으로 연결하는법. (0) | 2024.10.07 |