본문 바로가기

카테고리 없음

[ 주간 프로젝트 진행 에러, 오류, 문제 등등 기록 ]

Topic =  프로젝트 진행 도중 별도로 TIL 작성이 어려워서, 주간에 어떤 문제들이 생겼는지만 모아두기, 정리는 추가로 할 것

 

 


 

➕ 추가 했었다가 뺀 기능이라 미련이 남아서 작성

 

새로고침 [ 1 ] DB의 변동이 생길 경우 이벤트 처리

 

        db.collection("Posts").addSnapshotListener { querySnapshot, exception ->
            if (exception != null) {

                Log.d("xxxx", " Home Frag 리스닝 에러 : $exception ")
                return@addSnapshotListener
            }

            Log.d("xxxx", " Home Frag SnapshotListener ")
            querySnapshot?.let {snapshot ->
                // 이벤트 처리
            }
        }
  • 필요한 DB의 Colletion 값으로 SnapshotListener를 정의함으로 DB의 변동이 생길 경우 이벤트 처리를 할 수 있으나 현재 프로젝트에서는 사용하지 않을 예정이기 때문에 이러한 기능이 있다고 알아두고 넘어가자.

 

 

Adapter 내부에서 storage를 가져와 스크롤 시 이미지가 늦게 불러와지는 문제
  • DB를 RecyclerView에 가져오는 구조 문제

문제 코드

    inner class HomePostRcvViewHolder(binding: RcvItemPostBinding) :
        RecyclerView.ViewHolder(binding.root) {
        val postTitle: TextView = binding.postTitle
        val postDesc: TextView = binding.postDesc
        val postPrice: TextView = binding.postPrice
        val postCategory: TextView = binding.postCategory
        val postImg: ImageView = binding.postImg

        fun bind(imagePath : String) {
            storage.reference.child("post").child("$imagePath").downloadUrl.addOnSuccessListener { uri ->
                Glide.with(itemView)
                    .load(uri)
                    .into(postImg)
            }.addOnFailureListener {
                Log.d("xxxx", " adapter bind Failure $it")
            }


        }
  • Adapter 내부에서 Storage 데이터를 받아오고 있어서 RecyclerView의 특성대로 스크롤이 지나간 다음 다시 해당 아이템의 위치로 돌아오면 다시 아이템을 불러오기 때문에 이미지를 불러오는 것과 Rcv 아이템 로드가 100 msec 이상의 차이가 났다

 

 

 

 


 

역직렬화

    fun postDownload (){
        viewModelScope.launch(Dispatchers.IO) {
            try {
                db.collection("Posts").orderBy("uploadDate",
                    Query.Direction.ASCENDING)
                    .get().addOnSuccessListener { querySnapshot ->
                    if (!querySnapshot.isEmpty) {
                        val documentSnapshot = querySnapshot.documents
                        Log.d("xxxx", " testA[0].data : ${documentSnapshot[0].data} ")

                        val rcvList: MutableList<Post> = mutableListOf()

                        for (document in documentSnapshot) {
                            rcvList.add(
                               Post(
                                    document.data?.get("uid") as String,
                                    document.data?.get("title") as String,
                                    document.data?.get("price") as String,
                                    document.data?.get("category") as String,
                                    document.data?.get("address") as String,
                                    document.data?.get("deadline") as String,
                                    document.data?.get("desc") as String,
                                    document.data?.get("imgs") as List<String>,
                                    document.data?.get("uploadDate") as String,
                                )
                            )
                        }
                        Log.d("xxxx", " result : $rcvList")
                        _postResult.postValue(rcvList)
                    }
                }
            }catch (e:Exception){
            }
        }
    }
  • 역직렬화를 통해 위에 코드에서 rcvList.add() 내부 코드를 아래와 같이 간단하게 작성할 수 있다.
for (document in documentSnapshot) {
    document.toObject<Post>()?.let { post ->
        rcvList.add(post)
    }
}

 

 

 

data class Post(
    val uid: String,
    ...
    var imgs: List<String>,
    ...
    val timestamp : Timestamp,

)
{
    constructor() : this("", ...  listOf(),Timestamp.now(), // 와 같이 Data class 모델의 각 value의 Default 값을 지정)
}

 

 

 

  • [ ] 게시글 추가 시 새로고침 안되도록 수정 ?:
    • 사용자 조금만 많아져도 새 게시글이 올라올 때마다 매번 Data가 요청되어 트래픽 ↑ 문제, 정렬 순서가 시간 순이 아닐 경우 새로운 아이템이 곧곧에서 추가될 수 있기 때문에 사용자 현재 스크롤 상태 저장하기가 어려울 수 있다

 

 

firestroe updata 동시에 추가할 경우 문제?

 

관심 목록 추가 시 문제 - 동시에 추가 해봤으나 특별한 문제 없음

  • 사용자 a와 사용자 b 가 게시글에 들어갈 때 해당 게시글의 LikeUsers가 없었는데 사용자 1, 2 둘 다 관심 목록을 추가하면 사용자 a는 LikeUsers에 사용자 a만 추가된 채로 업데이트 하고, 사용자 b도 LikeUsers에 사용자 b만 추가된 채로 업데이트 하기 때문에 실시간으로 두 유저의 좋아요를 업데이트 하지 못 할 수 있을 것 같았는데 정상적으로 2명 다 추가되었다
// 좋아요 버튼 클릭 이벤트
fun addFavoritePost(uid: String,postPath: Timestamp){
    db.collection("Posts").whereEqualTo("timestamp", postPath)
        .get()
        .addOnSuccessListener {querySnapshot ->
            Log.d("xxxx", "addFavoritePost: 첫번째는 성공 $querySnapshot")
            if (!querySnapshot.isEmpty) {

                val documentSnap = querySnapshot.documents[0]
                Log.d("xxxx", "addFavoritePost documentSanp : $documentSnap")
                val likeList : MutableList<String> = mutableListOf()

                likeList.addAll(documentSnap.data?.get("likeUsers") as List<String>)
                Log.d("xxxx", " Before add LikeUsers List : $likeList")

                if (likeList.contains(uid)){
                    //todo 이미 좋아요한 유저 이벤트 처리, 본인 게시물 좋아요 버튼 이벤트 처리.
                    Log.d("xxxx", "addFavoritePost: 이미 좋아요한 UID")
                } else {
                    likeList.add(uid)
                    documentSnap.reference.update("likeUsers",likeList).addOnSuccessListener {
                            Log.d("xxxx", " 좋아요 버튼 클릭 Successful, 좋아요 List에 UID 추가 " )
                        querySnapshot.documents
                        }
                        .addOnFailureListener {
                            Log.d("xxxx", " 좋아요 버튼 클릭 Failure 좋아요 List에 추가 X = $it ")
                        }
                    }
                }
            }
}

 

 

 

 


 

정렬 문제

 

PostModel 로 먼저 데이터를 받고 PostRcv로 변환하여 홈 게시글 목록에 뿌려주는데, PostRcv로 변환하면서 정렬 순서가 바뀌는 문제 발생

  • PostModel로 받을 때에는 orderBy 통해서 정렬해서 받는데 postRcv로 변환하고 나면 순서가 바뀐다.
private fun PostRcvConvertUri (post: Post, querySnapshot: QuerySnapshot, postRcvList:MutableList<PostRcv>,
                               resultLiveData: MutableLiveData<MutableList<PostRcv>>) {
    val postImgUris: List<String> = post.imgs
    val postImgList: MutableList<Uri> = mutableListOf()

    val downloadTasks = mutableListOf<Task<Uri>>()
    for (uri in postImgUris) {
        val downloadTask = storage.reference.child("post").child(uri).downloadUrl
        downloadTasks.add(downloadTask)
    }

    Tasks.whenAllSuccess<Uri>(downloadTasks)
        .addOnSuccessListener { uriList ->
            postImgList.addAll(uriList)

            if (postImgUris.size == postImgList.size) {
                val postRcv = PostRcv(
                    uid = post.uid,
                    title = post.title,
                    price = post.price,
                    category = post.category,
                    address = post.address,
                    deadline = post.deadline,
                    desc = post.desc,
                    imgs = postImgList,
                    nickname = post.nickname,
                    likeUsers = post.likeUsers,
                    token = post.token,
                    timestamp = post.timestamp,
                    state = post.state
                )

                postRcvList.add(postRcv)

                if (postRcvList.size == querySnapshot.size()) {
                    resultLiveData.postValue(postRcvList)
                    Log.d("xxxx", " 설마 되겠어?")
                    Log.d(
                        "xxxx",
                        " 설마 있겠어 1: ${postRcvList[0].imgs}, 2: ${postRcvList[1].imgs}"
                    )
                }
            }
        }
}

 

해결 방법 

var inserted = false
                    for ( index in postRcvList.indices){
                        if (postRcv.timestamp > postRcvList[index].timestamp){
                            postRcvList.add(index, postRcv)
                            inserted = true
                            break
                        }
                    }
                    if (!inserted){
                        postRcvList.add(postRcv)
                    }
  • timestamp 정렬 한번 더 실행
    • 구조 자체가 문제인 것 같아 임시적인 해결방법으로 급한 불을 꺼둔 것 같아서 프로젝트 도중에 시간이 나면 구조를 바꾸는 등 근본적인 원인을 해결 해보고 싶다.

 

 

 


 

 

Timestamp 변환

 

1. SimpleDateFormet으로 날짜 형식으로 바꾸기

        fun bind(imagePath: Uri,timestamp: Timestamp) {
            postImg.load(imagePath)
            // timestamp를 Date 객체로 변환
            val date: Date = timestamp.toDate()
            // SimpleDateFormat으로 원하는 형식으로 변환
            val dateFormat = SimpleDateFormat("yyyy-MM-dd")

            postDate.text = dateFormat.format(date)
        }

 

2. 현재 날짜와 비교하기 - 시간 차이로 보기

            // 2. 현재 날짜, 시간 기준으로 만들기 ( 1시간 전, 2일 전, 1달 전)
            val currentDateTime: Date = Date()
            val diff: Long = currentDateTime.time - date.time
            // 분 단위 차이
            val minutes : Long = diff / (1000 * 60)
            val hours : Long = minutes / 60
            val day : Long = hours / 24
            val month : Long = day / 30
  • month의 경우 30일인 날이 있고, 31일인 날이 있어서 month의 경우까지 들어가게 된다면 추가적인 로직이 필요해 보이는데
  • 30일로 12개월 360일 31일로 12개월 372일 365에서 오차가 더 적은 30일로 month를 지정해주자.

 

 

 


 

3. Storage에 Ur을 넣으려고 하는데 오류 발생

 

imageUpload Failure : com.google.firebase.storage.StorageException: An unknown error occurred, please check the HTTP result code and inner exception for server response.
  • 사용자가 게시글을 수정할 때 기존 이미지를 건드리지 않고 동일하게 재 업로드 할 경우 문제 발생.
  • Storage의 동일한 경로에 똑같은 파일이 있을 경우 문제가 생기는 것이라고 생각해서 기존의 Storage 경로에 접근해서 동이한 파일을 삭제하고 다시 시도해봤으나 여전히 똑같은 오류가 발생.
  • 진짜 원인은 Firebase Storage를 통해 가져온 URI가 공개 URI로 공유 및 다운로드 용으로 사용자들에게 제공 되는 것으로 해당 URI를 다시 Storage에 저장할 수 없는 것이다.

 

실패 코드 - 이것저것 짜집다 보니까 작은 스파게티가 작성됐다.

    // 포스트 수정된 DB 업로드
    // 선택한 이미지 Storage에 업로드
    fun uploadEditPost(uris : MutableList<Uri>, post : Post) {
        val imgs : MutableList<String> = mutableListOf()
        fun getTime(): String {
            val currentDateTime = Calendar.getInstance().time
            val dateFormat =
                SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS", Locale.KOREA).format(currentDateTime)

            return dateFormat
        }
        val time = getTime()
        uris?.let { uris ->
            Log.d("xxxx", "imageUpload 1. uris : $uris")
            for (i in uris.indices) {
                val uri = uris[i]
                val fileName = "${time}_$i"

                Log.d("xxxx", "imageUpload 2. uris for each : ${uri}")
                imgs.add(fileName)

                Log.d("xxxx", "imageUpload 3. path = ${time}_$i ")

            }
            post.imgs = imgs
            val previousList = mutableListOf<String>()
            db.collection("Posts").whereEqualTo("timestamp",post.timestamp).get()
                .addOnSuccessListener {querySnapshot ->
                    Log.d("xxxx", "uploadEditPost: 해당 게시글 $querySnapshot")
                    if (!querySnapshot.isEmpty){
                        previousList.addAll(querySnapshot.documents[0].data?.get("imgs") as List<String>)
                        for ( index in previousList.indices) {
                            storage.reference.child("post").child("${previousList[index]}").delete()
                                .addOnSuccessListener {
                                    Log.d("xxxx", " 기존 데이터 삭제 성공 ")

                                    for (i in uris.indices) {
                                        val uri = uris[i]
                                        val fileName = "${time}_$i"
                                    try {
                                        storage.reference.child("post").child("${time}_$i").putFile(uri)
                                            .addOnSuccessListener {
                                                // 추후에 필요한 기능 추가
                                            }
                                            .addOnFailureListener {

                                                Log.d("xxxx", " Edit Frag imageUpload Failure : $it ")
                                            }
                                    }catch (exception : StorageException){
                                        val errorCode = exception.errorCode
                                        Log.d("xxxx", " 파일 업로드 실패 errorCode: ${errorCode}")
                                    }
                                }
                            Log.d("xxxx", " previousList : $previousList")
                        }
                        querySnapshot.documents[0].reference.set(post)
                            .addOnSuccessListener {
                            Log.d("xxxx", "imageUpload: 게시글 수정 완료 ")

                            }
                            .addOnFailureListener {
                                Log.d("xxxx", "uploadEditPost: 게시글 수정 실패 $it")
                            }
                    }
                }
        }
    }}

 

 

[ 3 - 1 ] Stream 형식 변환 MainThread 문제

 

android.os.NetworkOnMainThreadException

  • 메인쓰레드에서 네트워크 요청을 해서 문제가 생겼다.

문제 코드 

    fun testA (uris : MutableList<URL>){
        val convertUriList = mutableListOf<Uri>()
        for (index in uris.indices){
            val connection = uris[index].openConnection() as HttpURLConnection
            connection.connect()

            val inputStream = connection.inputStream

            storage.reference.child("post").child("test_$index").putStream(inputStream)
                .addOnSuccessListener{
                    Log.d("xxxx", "testA 성공 : $it ")
                }
                .addOnFailureListener{
                    Log.d("xxxx", "testA 실패 : $it ")
                }
        }
    }

 

해결 코드

    fun testA (uris : MutableList<URL>){

        runBlocking {
            for (index in uris.indices){
                launch (Dispatchers.IO){
                val connection = uris[index].openConnection() as HttpURLConnection
                connection.connect()

                val inputStream = connection.inputStream


            storage.reference.child("post").child("test_$index").putStream(inputStream)
                .addOnSuccessListener{
                    Log.d("xxxx", "testA 성공 : $it ")
                }
                .addOnFailureListener{
                    Log.d("xxxx", "testA 실패 : $it ")
                }
            }
            }
        }
    }

 

정상적으로 데이터가 담긴 것을 확인할 수 있다.

 

 

 

[ 3 - 2 ] Content URI to URL ❌

 

[ 3 - 1 ] 에서 기존에 downloadUrl 를 통해 가져와서 사용하던 URL Image Data 들은 URI 로 정상적으로 변환이 되는데, 갤러리에서 새로 추가된 이미지는 Content URI 형식에서 URL 변환이 거의 불가능 하다 [ 변환하는 방법 알게되면 수정 ]

   위와 같은 이유로 

 

 

 

 

 

복합 쿼리 사용하기

 

 복합쿼리 추가하기 위한 코드

                db.collection("Posts")
                    .whereEqualTo("uid", Constants.currentUserUid)
                    .orderBy("timestamp",Query.Direction.DESCENDING)
                    .get()
                    .addOnSuccessListener { querySnapshot ->
                        if (!querySnapshot.isEmpty) {
                            // 성공 처리
                    }
                    .addOnFailureListener {
                        Log.d("xxxx", "postFeedDownload Failure : $it ")
                    }

 

색인을 지정하지 않았기 때문에 생기는 오류,

addOnFailureListener에 로그에 오류 메세지를 포함시켜놓으면 해당 로그캣에서 바로 색인을 추가할 수 있는 Firebase 콘솔 주소가 나온다 

 

  • 해당 주소로 이동하여 색인 만들기를 진행해주면 된다

 

 

  • 색인 생성 중에서 조금 기다려줘야 한다 생성까지 시간은 5분 이상 걸리는 것 같다
    • 맨 처음에 추가할 때 시간이 너무 오래 걸려서 오류가 발생한 줄 알았으나 원래 오래 걸리는 것 같다.
  • 색인이 정상적으로 생성 되었다면, 기존에 작성해두었던 복합 쿼리를 통해 정상적으로 데이터가 받아와진다.