드디어, 얼추 틀은 다 완성되었다.
내가 구현하고자 했던 어플은 실시간 채팅 어플인데, 이걸 구현하려면 Firebase와의 연동이 필요했다.
차근차근 Firebase와 Android studio를 연동하는 방법을 알아보자.
Firebase 연동하기
1. Firebase database 생성
Firebase | Google's Mobile and Web App Development Platform
개발자가 사용자가 좋아할 만한 앱과 게임을 빌드하도록 지원하는 Google의 모바일 및 웹 앱 개발 플랫폼인 Firebase에 대해 알아보세요.
firebase.google.com
일단 첫 번째로 여기 들어가서 시작하기를 눌러 프로젝트를 하나 생성해주어야 한다.

나같은 경우는 프로젝트가 이미 만들어져 있어서 저렇게 뜨지만, 원래라면 아무것도 없기에 맨 왼쪽 버튼을 눌러서 프로젝트를 생성해주면 된다.
그 뒤에는 빌드 - Firestore Database & Realtime Database를 눌러 둘 다 생성해준다.

2. 앱 추가하기
Firebase Console 중간 화면을 보면, 앱 추가하기라는 버튼이 존재한다.

이 버튼을 눌러주면, 안드로이드 패키지 이름을 입력하라고 나오는데, 이거는 간단한다.
Android studio에서 제작한 프로젝트 패키지 이름을 그대로 적어주면 된다.
그 뒤에는 google-services.json을 다운받으라고 하는데, 이 구성 파일은 다운 받은 뒤에 프로젝트의 app 폴더 안에 넣어주어야 한다. 사실 원래는 이게 맞는데, 나같은 경우는 살펴보니까 debug 폴더를 참조하길래 여기에 넣어주었다.
3. build.gradle.kts 수정하기
여기까지 왔다면 이런 화면이 뜬다.

간단하게 여기서 하란대로 수정하면 연동이 완료된다.
DB 수정하기
전에 만들었던 SQL Database는 로컬에 한정된다는 단점이 있었다. 즉, 각자의 기기에서만 할당되는 로그인 정보여서 공유가 잘 되지 않았다. 그래서 코드를 수정해주었다.
package com.example.again
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import android.util.Log
import com.google.firebase.firestore.FirebaseFirestore
class User {
fun sendUserinfo(id: String, pw: String, name: String) {
val firestore = FirebaseFirestore.getInstance()
val user = User_info(
id = id,
password = pw,
name = name
)
firestore.collection("user")
.document(id)
.set(user)
.addOnSuccessListener {
Log.d("Firestore", "User successfully added!")
}
.addOnFailureListener { e ->
Log.e("Firestore", "Error adding user", e)
}
}
fun check_name(id: String, callback: (String?) -> Unit){
val firestore = FirebaseFirestore.getInstance()
firestore.collection("user").whereEqualTo("id", id).get()
.addOnSuccessListener { documents->
val document = documents.documents[0]
var name = document.getString("name")
callback(name)
}
}
fun check_duplicate(id : String, pw : String, isLogin :Boolean, callback : (Boolean) -> Unit) {
val database = FirebaseFirestore.getInstance()
if (!isLogin) {
database.collection("user").whereEqualTo("id", id).get()
.addOnSuccessListener { documents ->
if (!documents.isEmpty) {
callback(true)
} else {
callback(false)
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "중복 확인 실패: ${e.message}")
callback(false)
}
}
else{
database.collection("user").whereEqualTo("id", id).get()
.addOnSuccessListener { documents ->
if (!documents.isEmpty) {
val document = documents.documents[0]
val db_pw = document.getString("password")
println(db_pw)
println(pw)
if (db_pw == pw){
callback(true)
}
else {
callback(false)
}
} else {
callback(false)
}
}
.addOnFailureListener { e ->
Log.e("Firestore", "중복 확인 실패: ${e.message}")
callback(false)
}
}
}
}
이건 전체적인 Database를 관리하는 코드이다. 중복되는지 검사하는 코드도 있고, 유저의 이름을 검색해서 반환해주는 코드도 있다. 이 코드들은 유저의 정보를 Firestore에 저장한다.
이렇게 해주면 이제 유저 정보를 서버에 저장하기 때문에 서로 다른 기기에서도 상호작용이 가능해진다.
실시간 채팅 구현
package com.example.again
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import android.util.Log
class Chat {
fun sendMessage(sender: String, message: String){
val database = FirebaseDatabase.getInstance().getReference("chats")
val messageId = database.push().key
val chatMessage = ChatMessage(
id = messageId ?: "",
sender = sender,
message = message
)
messageId?.let{
database.child(it).setValue(chatMessage)
}
}
fun receiveMessages(onNewMessage: (ChatMessage) -> Unit){
val database = FirebaseDatabase.getInstance().getReference("chats")
database.addValueEventListener(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
for (data in snapshot.children) {
val message = data.getValue(ChatMessage::class.java)
message?.let{ onNewMessage(it)}
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("Chat", "Failed to read messages", error.toException())
}
})
}
}
전체적인 채팅을 관리해주는 코드이다. sendMessage 함수는 메시지를 전송했을 때 그 전송한 데이터들을 FirebaseDatabase에 저장해주고, 이 저장해둔 메시지를 receiveMessage 함수에서 받아서 화면에 표시되도록 해준다.
package com.example.again
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import android.content.Intent
import android.widget.TextView
import android.widget.EditText
import android.widget.Button
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.FirebaseApp
class Main : AppCompatActivity() {
private lateinit var editTextMessage: EditText
private lateinit var buttonSend: Button
private lateinit var recyclerView: RecyclerView
private lateinit var chatAdapter: ChatAdapter
private val chatMessages = mutableListOf<ChatMessage>()
override fun onCreate(savedInstanceState: Bundle?) {
var id = intent.getStringExtra("id").toString()
var dbHelper = DBHelper(this)
var user_name = ""
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)
editTextMessage = findViewById(R.id.editTextMessage)
buttonSend = findViewById(R.id.buttonSend)
recyclerView = findViewById(R.id.recyclerView)
chatAdapter = ChatAdapter(chatMessages)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = chatAdapter
User().check_name(id){ name->
if (name != null){
user_name = name.toString()
}
}
// 실시간 메시지 수신
Chat().receiveMessages { message ->
if (!chatMessages.contains(message)) {
chatMessages.add(message)
chatAdapter.notifyDataSetChanged()
}
}
// 메시지 전송 버튼 클릭 이벤트
buttonSend.setOnClickListener {
val message = editTextMessage.text.toString().trim()
if (message.isNotEmpty()) {
Chat().sendMessage(user_name, message)
editTextMessage.text.clear()
}
}
}
}
전체적인 채팅을 담당하는 메인 화면이다. 여기서는 recyclerView 객체를 ChatAdapter 어댑터와 연결해서 채팅이 전송될 때마다 그 채팅을 받아서 화면에 표시될 수 있도록 해준다.
package com.example.again
import androidx.recyclerview.widget.RecyclerView
import android.widget.TextView
import android.view.View
import android.view.ViewGroup
import android.view.LayoutInflater
class ChatAdapter(private val messages: List<ChatMessage>) :
RecyclerView.Adapter<ChatAdapter.ChatViewHolder>() {
class ChatViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val textViewMessage: TextView = view.findViewById(R.id.textMessage)
val userNameTextView : TextView = view.findViewById(R.id.textUserName)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChatViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_chat_message, parent, false)
return ChatViewHolder(view)
}
override fun onBindViewHolder(holder: ChatViewHolder, position: Int) {
holder.textViewMessage.text = messages[position].message
holder.userNameTextView.text = messages[position].sender
}
override fun getItemCount(): Int = messages.size
}
이 코드가 바로 ChatAdapter.kt 코드로, 실질적으로 채팅을 화면에 표시시켜주는 코드이다.
이제 앱이 실행되는 모습을 살펴보자.
디자인은..수정해야 할 것 같다.
어느 정도 기초가 잡혔으니까, 이제 모의해킹을 슬슬 시작해보아야 할 것 같다.
'기타 지식들 > 개발 관련' 카테고리의 다른 글
| Kotlin으로 SQLite 활용해서 회원가입 페이지 간단하게 구현하기 (1) | 2025.02.24 |
|---|---|
| Kotlin으로 입력 이벤트 구현하기 (0) | 2025.02.24 |
| Nox 앱 플레이어와 연계해서 frida 설치하기 (0) | 2025.02.23 |