오늘은 firestore에 저장되어있는 하나의 document안에 새로운 field를 만들고 그안에 객체 형태의 댓글(유저id, 유저이름, 댓글내용, 올린날짜, 이미지)을 리스트형태로 저장해보았다.
data class CampCommentEntity(
val userId: String,
val userName: Any?,
val content: String,
val date: String,
val imageUrl: Uri?,
)
먼저 객체화할때 사용해준 CampCommentEntity이다.
commentSend.setOnClickListener {
UserApiClient.instance.me { user, error ->
if (user?.id != null) {
val userDocRef = db.collection("users").document("Kakao${user.id}")
userDocRef
.get()
.addOnSuccessListener {
val userId = "Kakao${user.id}"
val userName = it.get("nickName")
val content = commentEdit.text.toString()
val date = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(
Date()
)
val myImage = if(selectedImage.visibility == View.VISIBLE){
myImage
} else{
""
}
val myImageUri = Uri.parse(myImage)
val myComment = CampCommentEntity(userId, userName, content, date, myImageUri)
viewModel.uploadComment(myId!!, myComment)
commentEdit.text.clear()
//여기서
selectedImage.setImageURI(null)
selectedImage.visibility = View.GONE
}
} else {
SnackbarUtil.showSnackBar(it)
}
}
}
commentSend버튼 클릭시 먼저 사용자의 로그인 여부를 확인해줬다.
비로그인 상태일시 -> 로그인유도 스낵바
로그인 상태일시 -> 진행
firestore에 저장되어있는 로그인한 사용자의 id, 유저의 이름,
작성한 내용과 현재날짜, 그리고 이미지를(여기서 이미지는 아직 에러덩어리.. 무시해주세요)가져와줬다.
그리고 5개지 변수를 viewModel에 캠핑장의id와 함께 넘겨주었다.
fun uploadComment(myId: String, myComment: CampCommentEntity) {
val db = Firebase.firestore
val campRef = db.collection("camps")
.whereEqualTo("contentId", myId)
campRef
.get()
.addOnSuccessListener { querySnapshot ->
val document = querySnapshot.documents[0]
val commentList = document.get("commentList") as? MutableList<Map<String, Any?>> ?: mutableListOf()
val newComment = mapOf(
"userId" to myComment.userId,
"userName" to myComment.userName,
"content" to myComment.content,
"date" to myComment.date,
"img" to myComment.imageUrl.toString()
)
commentList.add(newComment)
document.reference.update("commentList", commentList)
.addOnSuccessListener {
Log.d("CampDetailViewModel", "댓글 업로드 완료")
}
.addOnFailureListener { e ->
Log.d("CampDetailViewModel", "댓글 업로드 실패: $e")
}
}
.addOnFailureListener { e ->
Log.d("CampDetailViewModel", "캠핑장 쿼리중 오류 발생 : $e")
}
}
뷰모델에서 받아온 파라미터를 가지고 commentList라는 새로운 field를 만들고, 그안에 객체형태의 리스트 값을 넣어줬다.
이렇게 하면 입력한 댓글이 사용자의 이름, id, 댓글내용, 날짜, 이미지와 함께 firestore에 업로드 할 수 있다.
여기서 문제는 화면을 전환해야지 방금 작성한 댓글이 보인다는 것이다. 이를 해결하기위해!
private lateinit var listenerRegistration: ListenerRegistration
...
fun registerRealtimeUpdates(myId: String) {
listenerRegistration = FirebaseFirestore.getInstance()
.collection("camps")
.whereEqualTo("contentId", myId) // 필터링 조건에 맞게 설정
.addSnapshotListener { snapshot, exception ->
if (exception != null) {
Log.e("CampDetailViewModel", "Listen failed", exception)
return@addSnapshotListener
}
if (snapshot != null && !snapshot.isEmpty) {
// 실시간 업데이트가 발생했을 때 RecyclerView에 반영
val comments = mutableListOf<CampCommentEntity>()
for (doc in snapshot) {
val commentList = doc.get("commentList") as? MutableList<Map<String, Any?>> ?: mutableListOf()
for (comment in commentList) {
val userId = comment["userId"] as String
val userName = comment["userName"] as String
val content = comment["content"] as String
val date = comment["date"] as String
val imageUrlString = comment["img"] as String
val imageUrl = Uri.parse(imageUrlString)
val data = CampCommentEntity(userId, userName, content, date, imageUrl)
comments.add(data)
}
}
// RecyclerView에 데이터를 업데이트
_campComment.value = comments
} else {
Log.d("CampDetailViewModel", "No such document")
}
}
}
viewModel에서 새로운 함수를 하나 만들어주고, 해당함수를 init에서 불러와줬다.
listenerRegistration은 firebase에서 지원하는 실시간 데이터 변화를 감지하고 변화시켜준다.
이렇게 사용하고 꼭 해야하는 게 있는데, viewModel이 아닌 Activity나 Fragment에서 사용한다면 onDestroy()에서, viewModel에서 지금처럼 사용한다면 아래처럼
override fun onCleared() {
super.onCleared()
listenerRegistration.remove()
}
꼭 제거를 시켜줘야 메모리릭이 발생하지 않는다.