서버의 변경사항을 바로 앱에서 반영하시는 건 간단한 작업이 아닙니다. 먼저 서버에서 데이터의 변경을 알 수 있도록 하는 시스템이 필요합니다. 이게 없으면 데이터 변경되었는지를 알 수 없으니, 처리를 못하겠죠. 한가지 방법은 클라이언트가 서버의 데이터를 변경할 때 서버에서 제공하는 API를 통해서만 할 수 있게 제한하고 이 API에서 데이터의 변경이 가해지는 걸 감지할 수 있겠죠. 물론 DB를 직접 조작하는 경우가 생길 경우 이것도100% 처리가 가능하지는 않습니다. 이런 것 까지 감안하려면 메세지큐같은 걸 중간에 두고 여기에서 처리를 하게 하는 수 밖에는 없는데, 이러면 시스템이 점점 더 커지게 되겠죠. 그래서 많은 회사들이 클라우드 시스템(AWS, AZURE)을 사용하는 겁니다.
만약 데이터가 많지 않는 소규모의 프로젝트라면 파이어베이스를 사용할 수도 있습니다. 파이어베이스 자체가 웹소켙 기반이기 때문에 DB 변경시 클라이언트에게 자동으로 푸시가 가게됩니다.
다른 비교적 쉬운 방법으로는 GraphQL을 사용하는 겁니다. GraphQL서버는 기본적으로 push 기능을 탑재하고 있기 때문에 파이어베이스와 비슷하게 데이터 변경시 푸시처리가 가능해 집니다. 물론 파이이베이스 보다는 할게 훨씬 많습니다.
그리고 리사이클러뷰가 갱신안되는 이유는 아무래도 Dialog에서 사용하는 updatedList변수가 갱신이 안되었기 때문이 아닌가 싶네요. 해당 변수가 어디에 선언되어 있고 갱신되는지 확인이 안되네요.
그리고 한가지 코드 개선사항을 말씀드리면, 어댑터나 뷰홀더 안에 itemClick같은 액션을 처리하는 코드를 위치시키지 말라는 겁니다. 이렇게 하면 코드의 재사용성도 떨어지게 되고 액션관련 동작과 어댑터/뷰홀더가 뒤섞여서 가독성도 떨어집니다. 액션에 해당하는 부분은 호출하는 쪽으로 일임하세요.
class ListFragment : Fragment() {
...
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
adapter.onTransferClick = { item ->
showTransferConfirmDialog(item)
}
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
refreshData()
return view
}
private fun refreshData() {
val call = RetrofitClient.instance.getData(phoneNumber)
call.enqueue(object : Callback<List<YourDataClass>> {
....
})
private fun showTransferConfirmDialog(item: YourDataClass) {
/ / Create a custom dialog
val dialog = Dialog(holder.itemView.context)
dialog.setContentView(R.layout.popup_dialog)
// 확인 버튼 클릭 리스너
dialog.findViewById<Button>(R.id.bt_ok).setOnClickListener {
updateStatus2(item.seq_callboard)
dialog.dismiss()
}
// 무브 클릭시 이관 API보내기
dialog.findViewById<Button>(R.id.bt_move).setOnClickListener {
showTranferERPConfirmDialog(item)
dialog.dismiss()
}
// 날짜지정 버튼 클릭 리스너
dialog.findViewById<Button>(R.id.bt_adddate).setOnClickListener {
Log.d(MainActivity.TAG, "ADD")
dialog.dismiss()
}
// Find the TextViews in the custom dialog
val tvDialogSeqcallboard = dialog.findViewById<TextView>(R.id.tv_dialog_seq_callboard)
val tvDialogRegdate = dialog.findViewById<TextView>(R.id.tv_dialog_reg_date)
val tvDialogRequestname = dialog.findViewById<TextView>(R.id.tv_dialog_request_name)
val tvDialogCustomer = dialog.findViewById<TextView>(R.id.tv_dialog_customer)
val tvDialogRequestdept = dialog.findViewById<TextView>(R.id.tv_dialog_request_dept)
val tvDialogContents = dialog.findViewById<TextView>(R.id.tv_dialog_contents)
val tvDialogtvSasreceivedate = dialog.findViewById<TextView>(R.id.tv_dialog_sas_receivedate)
val tvDialogSasdamdtel = dialog.findViewById<TextView>(R.id.tv_dialog_sas_damdtel)
// Set the text of the TextViews in the custom dialog
tvDialogSeqcallboard.text = item.seq_callboard
tvDialogRegdate.text = item.reg_date
tvDialogRequestname.text = item.request_name
tvDialogCustomer.text = item.customer
tvDialogRequestdept.text = item.request_dept
tvDialogContents.text = item.contents
tvDialogtvSasreceivedate.text = item.sas_receivedate
tvDialogSasdamdtel.text = item.sas_damdtel
// Show the custom dialog
dialog.show()
}
private fun updateStatus2(callNo: String) {
val postNo = RetrofitClient.instance.getOk(callNo.toString())
postNo.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
Log.d(MainActivity.TAG, "OK:"+callNo)
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Log.e("Ok Error", "Error: $t")
}
})
}
private fun showTranferERPConfirmDialog(item: YourDataClass) {
Log.d(MainActivity.TAG, "MOVE")
val itemCallNo2 = item.seq_callboard
val moveDialog = Dialog(holder.itemView.context)
moveDialog.setContentView(R.layout.move_list)
val lvMove = moveDialog.findViewById<ListView>(R.id.listvw)
// ERP 이관 목록
val adapter = ArrayAdapter<String>(holder.itemView.context, android.R.layout.select_dialog_item,
listOf("테스터1"."테스터2","테스터3"))
lvMove.adapter = adapter
// 무브 클릭시 이관 API보내기
lvMove.setOnItemClickListener { _, _, position, _ ->
transferItem(item)
moveDialog.dismiss()
}
moveDialog.show()
}
private fun transferItem(item: YourDataClass) {
// POST 요청
val call = RetrofitClient.instance.getMoveOk(itemCallNo2.toString(), adapter.getItem(position).toString())
call.enqueue(object : Callback<ResponseBody> {
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if (response.isSuccessful) {
Log.d(MainActivity.TAG, "POST Success")
adapter.updateData(updatedList) //<---
} else {
Log.e(MainActivity.TAG, "POST Failed")
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
Log.e("POST Error", "Error: $t")
}
})
}
}
class ItemAdapter(private var itemList: List<YourDataClass>, private val listener: OnClickListener? = null) : RecyclerView.Adapter<ItemAdapter.ViewHolder>(), View.OnClickListener {
var onItemClick: ((YourDataClass) -> Unit)? = null
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_school, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
...
holder.itemView.setOnClickListener {
onItemClick?.invoke(item)
}
}
...
}