# 결과물 미리보기
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에서 관리한 후, 클릭할때마다, 카드뷰에다가 띄워줘야하는데 그걸 위해서 또 열심히 공부해봐야 겠다.
# 참고
각 리스너가 초기에 어떠한 값을 받아오는지 정리된 글. < 상당히 도움 됨 >