클라우드 호스팅 데이터베이스
JSON으로 저장되어 클라이언트에 실시간 동기화
앱에서 사용자 인증 시 필요한 백엔드 서비스와 사용하기 쉬운 SDK, 기성 UI 라이브러리 제공
UI 인증 : 전체 로그인 시스템을 추가할 때 권장하는 방법 - 인기 ID 제공업체를 이용한 로그인의 UI 흐름을 처리하는 인증 솔루션 제공
SDK 인증 : 이메일 및 비밀번호 기반 인증, ID 공급업체 통합, 전화번호 인증, 커스텀 인증 시스템 통합
서버개발에 사용되는 유연하고 확장 가능한 NoSQL DB
실시간 DB와 마찬가지로 실시간 리스너를 통해 클라이언트 애플리케이션 간에 데이터의 동기화를 유지하고 모바일 및 웹에 대한 오프라인 지원을 제공한다.
무료로 메시지를 안정적으로 전송할 수 있는 교차 플랫폼 메시징 솔루션
https://firebase.google.com/docs/android/setup?hl=ko
https://firebase.google.com/docs/database/android/read-and-write?hl=ko&authuser=0
연동은 해당레퍼런스를 참고한다.
class ChatActivity : AppCompatActivity() {
private val messageList = mutableListOf<ChattingItem>()
private val database = Firebase.database
//firebase에 message라는 테이블이 없으면 생성, 있으면 리턴
private lateinit var myRef: DatabaseReference
private lateinit var itemAdapter : ChatItemRecyclerViewAdapter
private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
val name = intent.getStringExtra("name") ?: "no_name"
itemAdapter = ChatItemRecyclerViewAdapter(messageList, name)
initFirebase()
binding.chatRecycler.apply {
adapter = itemAdapter
}
binding.sendBtn.setOnClickListener {
val msg = binding.messageEt.text.toString()
binding.messageEt.setText("")
//값이 있는 경우에만 보냄
if (TextUtils.isEmpty(msg) == false)
// messageList.add(chatItem)
myRef.push().setValue(ChattingItem("", name, msg, System.currentTimeMillis()))
}
}
private fun initFirebase() {
myRef = database.getReference("message")
val childEventListener = object : ChildEventListener {
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
Log.d(TAG, "onChildAdded: ${snapshot}")
messageList.add(getChatItem(snapshot))
//diffUtil 로 바꾸는 것이 효율적이긴 할 듯
itemAdapter.notifyItemInserted(messageList.size)
binding.chatRecycler.smoothScrollToPosition(messageList.size)
}
override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {
Log.d(TAG, "onChildChanged: ")
//바뀐 chatting item 가져옴
val chattingItem = getChatItem(snapshot)
var oldItem : ChattingItem = ChattingItem()
messageList.forEach{
if(it.firebaseKey == chattingItem.firebaseKey){
// list에서 바꿀 기존의 chatting item
oldItem = it
}
}
val index = messageList.indexOf(oldItem)
messageList[index] = chattingItem
itemAdapter.notifyItemChanged(index)
binding.chatRecycler.smoothScrollToPosition(messageList.size)
}
override fun onChildRemoved(snapshot: DataSnapshot) {
Log.d(TAG, "onChildRemoved: ")
val chatItem = getChatItem(snapshot)
val index = messageList.indexOf(chatItem)
messageList.removeAt(index)
itemAdapter.notifyItemRemoved(index)
binding.chatRecycler.smoothScrollToPosition(messageList.size)
}
override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {
Log.d(TAG, "onChildMoved: ")
}
override fun onCancelled(error: DatabaseError) {
Log.d(TAG, "onCancelled: ")
}
}
myRef.addChildEventListener(childEventListener)
}
private fun getChatItem(snapshot: DataSnapshot) : ChattingItem{
// val chatItem = snapshot.value as ChattingItem
val chatItem = snapshot.getValue(ChattingItem::class.java) ?: ChattingItem()
chatItem.firebaseKey = snapshot.key ?: ""
return chatItem
}
}
실시간 DB의 규칙을 수정한다.
Firebase의 authentication에 들어간다.
https://console.cloud.google.com/apis/credentials?hl=ko&project=myfirebasechat-4fae1
위 링크에서 사용자 인증 정보를 확인할 수 있다.
https://firebase.google.com/docs/auth/android/google-signin?hl=ko
해당 페이지에 링크로 넘어갈 수 있는 레퍼런스
https://github.com/firebase/snippets-android/blob/cb15737fe61389d2b58c65ae171cf83c26119cb3/auth/app/src/main/java/com/google/firebase/quickstart/auth/kotlin/GoogleSignInActivity.kt#L44-L45
https://developers.google.com/identity/one-tap/android/get-saved-credentials?hl=ko
private const val TAG = "ChatAuthLoginAc_싸피"
class ChatAuthLoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityChatLoginBinding
private lateinit var auth: FirebaseAuth
private lateinit var googleSignInClient: GoogleSignInClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityChatLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.enterBtn.isEnabled = false
binding.enterBtn.setOnClickListener {
val intent = Intent(this, ChatActivity::class.java).apply {
putExtra("name", binding.nameEt.text.toString())
}
startActivity(intent)
finish()
}
// 구글 로그인 절차 수행.
initAuth()
}
// 인증 초기화
private fun initAuth() {
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
googleSignInClient = GoogleSignIn.getClient(this, gso)
auth = Firebase.auth
signIn()
}
override fun onStart() {
super.onStart()
// Check if user is signed in (non-null) and update UI accordingly.
val currentUser = auth.currentUser
updateUI(currentUser)
}
// [END on_start_check_user]
// [START onactivityresult]
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
if (requestCode == RC_SIGN_IN) {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
try {
// Google Sign In was successful, authenticate with Firebase
val account = task.getResult(ApiException::class.java)!!
Log.d(TAG, "firebaseAuthWithGoogle:" + account.id)
firebaseAuthWithGoogle(account.idToken!!)
} catch (e: ApiException) {
// Google Sign In failed, update UI appropriately
Log.w(TAG, "Google sign in failed", e)
}
}
}
// [END onactivityresult]
// [START auth_with_google]
private fun firebaseAuthWithGoogle(idToken: String) {
val credential = GoogleAuthProvider.getCredential(idToken, null)
auth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success")
val user = auth.currentUser
updateUI(user)
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.exception)
updateUI(null)
}
}
}
// [END auth_with_google]
// [START signin]
private fun signIn() {
val signInIntent = googleSignInClient.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN)
}
// [END signin]
private fun updateUI(user: FirebaseUser?) {
if (user != null) {
binding.nameEt.setText(user.displayName.toString())
binding.enterBtn.isEnabled = true
} else {
binding.nameEt.setText("인증 실패")
}
}
companion object {
private const val RC_SIGN_IN = 9001
}
}