김호쭈
DevForYou
김호쭈
전체 방문자
오늘
어제
  • 분류 전체보기 (321)
    • • 데이터베이스(DB) (9)
      • __SQL__ (9)
    • •알고리즘(Algorithm ) (117)
      • 문제풀이 (99)
      • 스터디 (14)
      • 알고리즘 팁 (4)
    • •Compter Science (57)
      • Operating System (25)
      • Computer Network (1)
      • Computer Vision (16)
      • Artificial Intelligence (14)
      • Software Technology (1)
    • • 독서 (36)
      • Design Pattern (24)
      • 객체지향의 사실과 오해 (1)
      • Object Oriented Software En.. (11)
    • • 개발 (26)
      • React (3)
      • node.js (6)
      • Django (11)
      • Spring boot (6)
    • • 개발Tip (4)
      • GitHub (0)
    • •프로젝트 (2)
      • 물물 (2)
    • •App (54)
      • 안드로이드 with Kotlin (50)
      • 코틀린(Kotiln) (4)
    • •회고 (8)
    • •취준일기 (3)
    • • 기타 (2)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • GitHubDesktop
  • KMU_WINK
  • 로컬저장소
  • 원격저장소
  • ㄱ
  • 깃허브데스크탑
  • local저장소
  • Remote저장소

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
김호쭈

DevForYou

[안드로이드&코틀린] 틴더앱 만들기#6, 파이어베이스 리스너 이해하기 ,매칭상태 구현하기
•App/안드로이드 with Kotlin

[안드로이드&코틀린] 틴더앱 만들기#6, 파이어베이스 리스너 이해하기 ,매칭상태 구현하기

2022. 5. 8. 01:49

 

#  결과물 미리보기

A유저와 B유저가 서로에게 like를 한 경우에 매칭결과에서 서로가 매칭 됐는지를 확인 할 수 있다.

이번챕터를 구현하기 위해서는 파이어베이스의 리얼타임데이터베이스를 어느정도 이해해하고있어야 했다. 저번시간도 그렇지만 이번시간에도 다시한번 복습하면서 파베를 익힐 수 있었다.

 

# 매칭 상태  관리하기

 A유저가 B유저에게 like를 하게되면, B유저의 likeBy에 A유저의 uid값이 저장 되게 된다. 그러면 B유저가 A유저에게 like를 할 때에 매칭을 구현하기 위해서는 자신(B)의 likeBy를 뒤져서 A의 uid값이 있는지를 확인해야 한다.

저번에 구현했던 like()에 추가적으로 구현해야한다.

private fun like() {
	// ...
    // ... 

    // like를 하게되면 나의 uid가 상대의 likeby에 들어가게 됨
    // 서로 like된 경우인지를 확인함
    // 내가 상대방을 like한 시점에, 상대방도 나에게 이미 like한 경우를 생각해야 함
    // 그렇기 때문에 현재 나의 likeby에 상대방의 uid가 있는지 확인하는 과정을 거쳐야함
    processMatchedUser(card.userId)
}

 

private fun processMatchedUser(otherUserUid : String) {
    val otherUser = userDB.child(getCurrentUserID()).child("likeBy").child("like").child(otherUserUid)
    otherUser.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
                if(snapshot.value == true) {
                    // 서로 like한 경우임
                    // 나와 other의 matched에 true를 추가해줌
                    // 나의 matched에 other의 uid값 추가
                    userDB.child(getCurrentUserID()).child("matched").child(otherUserUid).setValue(true)
                    // other의 matched에 나의 uid값 추가
                    userDB.child(otherUserUid).child("matched").child(getCurrentUserID()).setValue(true)
                }
            }
        override fun onCancelled(error: DatabaseError) {}
    })
}

addListenerForSingleValueEvent의 레퍼런스를 가져오고자 하는 value의 key값을 딱 정해주게 된다면, 해당 값만 가져오기 때문에 따로 반복문을 사용하지 않아도 된다. 이렇게 사용하는 방식이 나름 유용하다.

## 리얼타임데이터베이스 리스너 작동원리

 아래 참고해놓은 포스팅에서 나름 얻고자 하는 정보를 얻을 수 있었다. 리스너들이 어떻게 snapshot을 가져오는지에 대해서 말이다.

먼저 가장 기본적인건, 리스너가 실행되는 최초의 순간에는 지정한 레퍼런스에 있는 모든 값을 다 가져오게 된다. 대신ChildEventListener는 조금 특이하게, 각각의 value들이 하나의 snapshot이 되어 여러번 호출되고, 나머지 두개는 반복문으로 하나씩 읽어줘야 하는 것이다. 그 동작을 잘 이해하게 된다면 리얼타임데이터베이스는 문제 없이 사용할 수 있다.

 이제 매칭기록을 저장하도록 했다. 이 값을 가져오게 하고, 새로운 리사이클러뷰로 띄우기 위해 액티비트를 만들어줘야한다.

 

# 매칭결과 보여주기 - 리사이클러뷰 이용

 새로운 액티비티에서, db의 레퍼런스와 auth을 잘 초기화 시켜준다. 여기서는 리사이클러뷰를 위해 새로운 adapter를 하나 만들어줘야하고, 일반적인 형태의 layout이기 때문에 layoutManger는 있는걸 사용한다.

private fun initRecyclerView() {
    val recyclerView = findViewById<RecyclerView>(R.id.matchedResultRecyclerView)
    recyclerView.layoutManager = LinearLayoutManager(this)
    recyclerView.adapter = adpater
}

## 리사이클러뷰 - 재활용하기

class RecyclerViewAdapter : ListAdapter<CardItem, RecyclerViewAdapter.ViewHolder>(diffUtil) {

    inner class ViewHolder(private val view : View): RecyclerView.ViewHolder(view) {

        fun bind(cardItem: CardItem){
            view.findViewById<TextView>(R.id.matchedUserTextView).text = cardItem.name
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context).inflate(R.layout.item_matched_user,parent,false)
        return ViewHolder(inflater)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(currentList[position])
    }

    companion object  {
        val diffUtil = object : DiffUtil.ItemCallback<CardItem>() {
            override fun areItemsTheSame(oldItem: CardItem, newItem: CardItem): Boolean {
                return oldItem.userId == newItem.userId
            }

            override fun areContentsTheSame(oldItem: CardItem, newItem: CardItem): Boolean {
                return oldItem == newItem
            }
        }
    }
}

CardItemAdapter는 card-stack-view 라이브러리를 사용했지만, 리사이클러뷰를 그대로 사용했기 때문에 변수명들만 적절히 바꿔주면 재활용 가능했다.

## 매칭결과 가져오기

 매칭결과를 가져와야한다. 

 위 사진처럼, matched에 있는 값들을 각각 읽어 온 후, 리사이클러뷰를 만들기 위해 필요한 CardItem객체를 각각 생성해줘야하는데, 그러기 위해서는 name과 uid가 필요하다. 두번의 접근이 필요하다. addChildEventListener를 통해 각각의 uid값을 얻어 온 후 addListenerForSingleValueEvent을 통해 얻어온 uid값에 접근하여, name을 알아와야 한다. 해당 정보에서는 name을 알 수 없기 때문이다.

private fun getMatchedUsers() {
    val currentMatchedUser = userDB.child(getCurrentUserID()).child("matched")
    currentMatchedUser.addChildEventListener(object : ChildEventListener{
        override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
            if(snapshot.key?.isNotEmpty() == true){
                getUser(snapshot.key.toString())
            }
        }
    })
}
private fun getUser(uid : String) {
    userDB.child(uid).addListenerForSingleValueEvent(object : ValueEventListener{
        override fun onDataChange(snapshot: DataSnapshot) {
            Log.d("MATCHEDACTIVTY","#snapshot : ${snapshot.child("name").value}")

            userList.add(CardItem(uid,snapshot.child("name").value.toString()))
            adpater.submitList(userList)
            Log.d("USERLIST","${userList}")
        }

        override fun onCancelled(error: DatabaseError) {}
    })

로그를 찍어보면 잘 들어오는것을 확인 할 수 있다. 로그 찍는게 귀찮아도, 미리 안찍으면서 확인하면 뒤로가면 더 복잡해지는거 같다

 

# 마치며

- 5월6일에 스마일 라식했다. 편하다.

- 학교 연계 하계인턴 목록이 떴는데, 안드로이드 네이티브 앱 관련 회사가 없다. 지원을 해야할지 고민중이다.

- 알파 프로젝트에서 갤러리 이미지를 가져와서 DB에서 관리한 후, 클릭할때마다, 카드뷰에다가 띄워줘야하는데 그걸 위해서 또 열심히 공부해봐야 겠다.

 

 

# 참고

각 리스너가 초기에 어떠한 값을 받아오는지 정리된 글. < 상당히 도움 됨 >

 

[Android, Kotlin] Firebase DB에서의 각 Listener별 차이점 (Value, Single, Child)

Firebase Realtime DB를 사용 할 때, 리스너를 등록하여 DB 내 값이 변경 될 때마다 해당 변경 사항을 동기화시켜 리사이클러뷰 등의 리스트를 업데이트 할 일이 잦다. 이 때 등록 할 수 있는 리스너는 3

hungseong.tistory.com

 

저작자표시 (새창열림)

'•App > 안드로이드 with Kotlin' 카테고리의 다른 글

[안드로이드&코틀린] 중고거래앱 만들기#2, 중고 물품 정보 리사이클러 뷰 만들기, 프래그먼트에 리사이클러뷰 추가하기, 홈 화면 구성하기, 뷰바인딩  (7) 2022.05.09
[안드로이드&코틀린] 중고거래앱 만들기#1, 프래그먼트 이용하여 하단메뉴바 만들기, setOnItemSelectedListener, supportFragmentManager, BottomNavigationView  (1) 2022.05.08
[안드로이드&코틀린] 틴더앱 만들기#5, 파이어베이스 읽어오기, 파이어베이스 변화 감지, 리스너 이해하기, addListenerForSingleValueEvent, addChildEventListener  (0) 2022.05.06
[안드로이드&코틀린] 틴더앱 만들기#4, CardStackView 사용하기, 깃허브 라이브러리 사용하기, 리사이클러뷰  (0) 2022.05.05
[안드로이드&코틀린] 틴더앱 만들기#3, 파이어베이스 리얼타임데이터베이스 사용, 초간단 editText 다이얼로그 만들기  (0) 2022.05.04
    '•App/안드로이드 with Kotlin' 카테고리의 다른 글
    • [안드로이드&코틀린] 중고거래앱 만들기#2, 중고 물품 정보 리사이클러 뷰 만들기, 프래그먼트에 리사이클러뷰 추가하기, 홈 화면 구성하기, 뷰바인딩
    • [안드로이드&코틀린] 중고거래앱 만들기#1, 프래그먼트 이용하여 하단메뉴바 만들기, setOnItemSelectedListener, supportFragmentManager, BottomNavigationView
    • [안드로이드&코틀린] 틴더앱 만들기#5, 파이어베이스 읽어오기, 파이어베이스 변화 감지, 리스너 이해하기, addListenerForSingleValueEvent, addChildEventListener
    • [안드로이드&코틀린] 틴더앱 만들기#4, CardStackView 사용하기, 깃허브 라이브러리 사용하기, 리사이클러뷰
    김호쭈
    김호쭈
    공부하고 정리하고 기록하기

    티스토리툴바