firebase에 객체 저장 시 타입이 바뀌는 현상

Hyun·2023년 12월 1일
0

코틀린

목록 보기
3/3
post-thumbnail
data class KeyValueElement(
    val userId: String,
    val userNickName: String,
    val content: String,
    val createdDate: Date
) {
    operator fun get(s: String): String {
        return when (s) {
            "userId" -> userId
            "userNickName" -> userNickName
            "content" -> content
            "createdDate" -> createdDate.toString()
            else -> ""
        }
    }
}

data class Review(
    var markerId: String = "",
    var reviewList: MutableList<KeyValueElement> = mutableListOf()
) {
    fun addReview(userId: String, userNickName: String, content: String, createdDate: Date) {
        reviewList.add(KeyValueElement(userId, userNickName, content, createdDate))
    }
}

Firebase 에 위와 같은 Review 타입의 객체를 다음과 같이 저장했었다.

//*********************리뷰 등록 버튼*********************//
binding.submitCommentButton.setOnClickListener {
    //입력창 내용 가져오기
    ...
    val review = Review()
    review.markerId = id
    review.addReview(MainActivity.staticUserId.toString(), MainActivity.staticUserNickname, content, Date())
    //리뷰 등록
    val db = FirebaseFirestore.getInstance()
    val docRef = db.collection("reviews").document(id)
    docRef.get()
        .addOnSuccessListener { document: DocumentSnapshot ->
            if(document != null && document.exists()){
                //기존에 문서가 존재하는 경우, 기존의 reviewList 가져옴
                val existingReviewList = document.data?.get("reviewList") as MutableList<KeyValueElement>
                //새 리뷰를 기존 reviewList에 추가
                existingReviewList.addAll(review.reviewList)
                //firebase 문서 업데이트
                docRef.update("reviewList", existingReviewList)
                    .addOnSuccessListener {
                        ...
                    }
                    .addOnFailureListener { e ->
                        ,,,
                    }
            } else{
                //기존 문서가 존재하지 않는 경우, 새 문서 생성
                docRef.set(review)
                    .addOnSuccessListener {
                        ...
                    }
                    .addOnFailureListener { e ->
                        ...
                    }
            }
        }
}

위에서 보듯, 내가 set으로 Firebase 에 넣기 위해 사용한 데이터는 Review 클래스 타입의 객체였다.

그러나 Firebase 에 데이터를 저장한 후, Firebase에서 가져온 reviewList는 해시맵 객체를 가지고 있는 List 였다(넣을땐 KeyValueElement 객체를 가지는 List였음).
로그: MyAdapter: onBindViewHolder typeof reviewList[position]: class java.util.HashMap

그리고 객체의 타입이 바뀐만큼 KeyValueElement 객체의 createDate: Date 필드는 해시맵에서 Firebase 자체 타입인 com.google.firebase.Timestamp 타입의 값으로 바뀌었다.

data class ReviewData(
    val content: String? = null,
    val createdDate: com.google.firebase.Timestamp? = null,
    val userNickName: String? = null,
    val userId: String? = null
)
...
val reviewData = ReviewData(
    //저장할때는 KeyValueElement 타입이었지만, 가져올때는 HashMap 타입이다.

    //저장할때는 Timestamp 타입이었지만, 가져올때는 com.google.firebase.Timestamp 타입이다.
    createdDate = dataFromFirestore["createdDate"] as? com.google.firebase.Timestamp,
    userNickName = dataFromFirestore["userNickName"] as String?,
    userId = dataFromFirestore["userId"] as String?,
    content = dataFromFirestore["content"] as String?
)

따라서 db 에서 가져온 reviewList의 원소들을 변수에 담기 위해 위와 같은 클래스를
선언하여 db에서 가져온 reviewList 의 원소인 해시맵 객체를 아래와 같이 대입해주었다.

try {
    if (reviewList != null && position >= 0 && position < reviewList.size) {
        //reviewList[Position]이 애초에 DB로부터 HashMap 형태(KeyValueElement 타입 X!!)로 가져와졌기 때문에 해시맵을 이용해야 한다.
        //따라서 내가 임의로 만든 KeyValueElement 타입의 객체에 reviewList[position]을 대입할 수 없다.
        val dataFromFirestore: HashMap<String, Any> = reviewList[position] as HashMap<String, Any>
        Log.d("review", "MyAdapter: typeof reviewList: ${reviewList::class.java}")
        for ((key, value) in dataFromFirestore) {
            Log.d("review","Key: $key, Value: $value")
        }
        val reviewData = ReviewData(
            //저장할때는 KeyValueElement 타입이었지만, 가져올때는 HashMap 타입이다.

            //저장할때는 Timestamp 타입이었지만, 가져올때는 com.google.firebase.Timestamp 타입이다.
            createdDate = dataFromFirestore["createdDate"] as? com.google.firebase.Timestamp,
            userNickName = dataFromFirestore["userNickName"] as String?,
            userId = dataFromFirestore["userId"] as String?,
            content = dataFromFirestore["content"] as String?
        )
        val binding = (holder as MyAdapter.MyViewHolder).binding
        binding.commentUserId.text = reviewData.userNickName
        binding.commentContent.text = reviewData.content
    }
} catch (e: Exception) {
    e.printStackTrace()
    Log.d("review", "MyAdapter: onBindViewHolder error: ${e}")
}

이후 대입받은 객체를 이용해 뷰홀더의 (아이템) 뷰 객체에 데이터를 출력(입력)해주었다.

느낀점

  • 해시맵 객체를 가지고 일반 클래스 타입 객체에 대입하는 고생을 했다. try & catch 문을 통해 에러 발생 사유를 알 수 있었다. 다음부턴 빠르게 미리 적용해놓자.

  • 일반 클래스 타입의 객체를 db에 저장했다고 해서, db에서 해당 객체를 가져올때 당연히 동일한 데이터 타입, 동일한 필드 타입으로 가져올거라는 생각을 하지말자.

profile
better than yesterday

0개의 댓글