마이챗. 카카오톡 모방 어플. 친구추가를 통한 1:1 채팅
바로 직전에 만들었던 Study Together라는 앱 내에 스터디 멤버들과 단체채팅을 하는 기능이 있었습니다.
단순히 채팅에 가입된 멤버들끼리만 할 수 있는 단체채팅이었기 때문에 기능을 좀 더 추가하여 채팅앱을 만들어보고자하였고
카톡에서 구현되어있는 채팅기능들을 구현함으로써 Firebase의 다양한 기능 및 Push Notification을 활용해보았습니다.
ID를 통해 친구를 추가하고, 추가한 친구와 1:1 대화를 할 수 있습니다.
상대방이 읽었는지 여부를 확인할 수 있고 Push Notification을 활용하여 채팅을 보냈을 경우 상대방에게 Push 알림이 갑니다.
Android측 :
-
Firebase
- Firestore : 회원정보 (User와 검색에 사용되는 Id)
- RealtimeDatabase : 채팅정보(Chatroom)
- Storage : 사용자 프로필 이미지
- Authentication : 이메일로 가입
- Cloud Messaging : FCM을 이용한 Push
-
Retrofit
- 스프링부트로 구현된 App Server에 push 요청을 하기 위해 사용
-
Glide
- 사용자의 프로필 이미지 표시
-
Dexter
- 권한 요청
서버측 :
- SpringBoot
- Firebase-admin
Firebase Authentication의 email로그인(회원가입)을 한 후
만약 친구추가를 위해 사용할 ID가 지정되어있지 않다면 ID를 설정하는 다이얼로그가 show됩니다.
친구목록 Fragment에서 자신의 프로필을 누르면 EditProfileActivity로 이동하게됩니다.
이동한 후 수정을 완료하였을 경우 수정완료버튼을 통해
이미지는 Storage에, User객체는 Image의 Uri를 받은 후 Firestore에 저장됩니다.
친구목록 창 상단 Toolbar의 +버튼을 통해 친구추가할 수 있습니다.
친구추가는 3가지 경우로 나뉩니다.
ㄱ. 새로운 친구를 검색한 경우
ㄴ. 이미 존재하는 친구를 검색한 경우
ㄷ. 자신을 검색한 경우
ㄱ,ㄴ,ㄷ 모두 다른 View를 보여줍니다.
추가되어있는 친구 목록에서 친구의 이름을 통해 찾을 수 있습니다.
SearchView에 OnQueryTextListener를 추가하고
onQueryTextChange를 override하여 입력된 문자가 변경될때마다 검색결과를 보여줍니다.
-
3가지 ViewHolder 이전 프로젝트와는 달리 RecyclerView의 ViewHolder를 3가지로 나눴습니다
ㄱ. 상대방 채팅의 ViewHolder
ㄴ. 내 채팅의 ViewHolder
ㄷ. 날짜 ViewHolder
날짜의 경우 temp를 이용해 Comment객체의 timestamp를 날짜로 표현하는 방식을 이용했습니다. -
읽음 표시
RealtimeDatabase를 이용해 Chatroom객체의 reference를 얻은 뒤, Comment객체를 얻어옵니다.
만약 읽은 상태라면 readUser에 자신의 uid와 함께 true값을 넣어줍니다.
현재는 1:1채팅만 구현해놓은 상태이므로, readUser.size != 2 라면 아직 상대방이 읽지 않은 상태이므로
나의 말풍선에 숫자 1을 나타내고
만약 readUser.size == 2 라면 상대방이 읽었다는것을 의미하므로
나의 말풍선에 숫자 1이 사라지게 됩니다. -
채팅방 가장 마지막 채팅
val treeMap = TreeMap(Collection.reverseOrder())
treeMap.put("comment의 key값", comment)
RealtimeDatabase에 Comment값이 들어갈때는 push()메서드를 이용하므로
key값이 시간에 따라 자동지정됩니다.
그러므로 TreeMap의 key값에 참조의 key값의(commentRef.push().key()) 내림차순으로 정렬하면
가장 최근의 Comment값을 얻을 수 있으므로 채팅목록에 표시될 가장 마지막 채팅과 시간을 나타낼 수 있습니다.
Firestore의 경우 FieldValue.serverTimestamp()를 통해 시간을 얻었지만
RealtimeDatabase의 경우 ServerValue.TIMESTAMP를 통해 서버시간을 얻습니다.
하지만 타입이 Map<String, String>이고, 정작 서버를 통해 그 값을 받을때는 Long형으로 받아지므로
Comment객체에 내에 있는 timestamp의 타입은 Any으로 선언해야합니다.
(넣어줄때는 Map으로 넣었다 하더라도 서버에는 Long으로 저장되고 받을때도 Long으로 받아지므로)
Firebase Cloud Messaging을 이용해 푸시 알림을 구현하였습니다.
로그인이 완료된 이후 token을 얻어 Firestore의 User객체에 token값을 추가합니다.
(서버에 token이 있을 경우 로그아웃을 하더라도 해당 토큰을 가진 기기로 푸시알림이 가기 때문에
로그아웃 시에는 token을 지워주어야합니다.)
상대방에게 채팅을 보내면 token과 보낸사람이름(title), 내용(content)가 Retrofit을 이용해 POST Method로 요청되고
서버측에서는 POST요청을 통해 받은 데이터들을 FCM서버에 요청하는 방식으로 진행됩니다.
MyChat은 바로 직전에 완성된 Study Together보다 조금 더 일찍 구상한 프로젝트이지만
도중에 잠시 중단하였다가 Study Together가 완성된 이후 다시 진행하게 되었습니다.
이번 프로젝트는 새로운 라이브러리나 API를 이용하기보다
기존에 얕게 알고 있었던 기능들에 대해 좀 더 활용할 수 있는 방향으로 진행하였습니다.
그 덕분에 코드를 쓰면서도 이해가 잘 되지 않았던 부분들을 배울 수 있는 좋은 기회가 되었습니다.
특히 Fragment와 Activity간의 생명주기에 대해 조금 더 익힐 수 있었고
ViewModel의 Onwer에 대한 개념을 좀 더 깊히 이해할 수 있게 되었습니다.
그 덕분에 공부 초반에는 어려웠던 개념들에 대해 더 깊히 이해할 수 있는 시간이 되었고,
특히 FCM 푸시 기능을 완벽하진 않지만 살짝 이용해볼 수 있는 좋은 계기가 되었다고 생각합니다.
감사합니다.