[Android / Kotlin] Firebase Realtime Database를 사용하여 읽기/쓰기

Subeen·2023년 3월 9일
0

Android

목록 보기
8/71
post-thumbnail

📍 Firebase Database 만들기

Firebase Console 화면에서 좌측의 Realtime Database를 클릭한다.

데이터베이스 만들기 버튼을 클릭하면 설정 팝업이 뜬다. 첫 번째로 실시간 데이터베이스 위치를 설정 해준다.

두 번째로는 보안 규칙을 설정해주는데 잠금 모드일 경우 데이터는 기본적으로 비공개가 되며 인증 된 사용자만 읽기/쓰기가 가능하다.
테스트 모드일 경우는 데이터가 기본적으로 공개 되며 참조 사용자는 누구나 읽기/쓰기가 가능하다.

데이터베이스가 만들어지면 규칙 수정 탭에서 read/write를 true로 수정해준다.

📍 결과 동영상

📍 앱에 Firebase 인증 추가

모듈(앱 수준) Gradle 파일에서 데이터베이스 라이브러리의 종속 항목을 추가한다.

dependencies {
    implementation platform('com.google.firebase:firebase-bom:31.2.2')
    
    implementation 'com.google.firebase:firebase-database-ktx'
}

📍 Database Read

📌 사용자가 글 작성시에 Database에 데이터를 Write하고 저장된 데이터를 RecyclerView를 사용하여 리스트 형식으로 보여주고자 한다.

👩🏻‍💻 RecyclerView 생성하기

// activity_content_list.xml
<layout>

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".board.ContentListActivity">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="자유 게시판"
            android:textColor="@color/black"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/textView" />

        <ImageView
            android:id="@+id/contentWriteBtn"
            android:layout_width="65dp"
            android:layout_height="65dp"
            android:layout_margin="30dp"
            android:src="@drawable/write_btn"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

👩🏻‍💻 데이터 클래스 생성하기

// ContentModel.kt
data class ContentModel(
    val title: String = "",
    val content: String = "",
    val time: String = ""
)

👩🏻‍💻 RecyclerView 아이템 생성하기

// item_content_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/titleArea"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/contentArea"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:textColor="@color/black"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/timeArea"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:textColor="#999999"
        android:textSize="11sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_margin="5dp"
        android:background="#999999" />

</LinearLayout>

👩🏻‍💻 Adapter 생성하기

// ContentAdapter.kt
class ContentAdapter(val items: MutableList<ContentModel>) :
    RecyclerView.Adapter<ContentAdapter.ViewHolder>() {
    // View Holder를 생성하고 View를 붙여주는 메서드 
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContentAdapter.ViewHolder {
        val v =
            LayoutInflater.from(parent.context).inflate(R.layout.item_content_list, parent, false)
        return ViewHolder(v)
    }

	// 생성된 View Holder에 데이터를 바인딩 해주는 메서드
    override fun onBindViewHolder(holder: ContentAdapter.ViewHolder, position: Int) {
        holder.bindItems(items[position])
    }

	// 데이터의 개수를 반환하는 메서드
    override fun getItemCount(): Int {
        return items.count()
    }

	// 화면에 표시 될 뷰를 저장하는 역할의 메서드
    // View를 재활용 하기 위해 각 요소를 저장해두고 사용한다.
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bindItems(items: ContentModel) {
            val title = itemView.findViewById<TextView>(R.id.titleArea)
            val content = itemView.findViewById<TextView>(R.id.contentArea)
            val time = itemView.findViewById<TextView>(R.id.timeArea)

            title.text = items.title
            content.text = items.content
            time.text = items.time

        }
    }
}

👩🏻‍💻 Database의 위치 참조

// FBRef.kt
class FBRef {
    companion object {
        private val database = Firebase.database

        val contentRef = database.getReference("content")
    }
}

👩🏻‍💻 RecyclerView에 Adapter 연결하기

// ContentListActivity.kt
class ContentListActivity : AppCompatActivity() {
    lateinit var binding: ActivityContentListBinding
    lateinit var contentAdapter: ContentAdapter
    private val contentList = mutableListOf<ContentModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_content_list)

        contentAdapter = ContentAdapter(contentList)
        // RecyclerView의 adapter에 ContentAdapter를 설정한다.
        binding.recyclerview.adapter = contentAdapter
        // layoutManager 설정
        // LinearLayoutManager을 사용하여 수직으로 아이템을 배치한다.
        binding.recyclerview.layoutManager = LinearLayoutManager(this)

		// 글쓰기 버튼을 클릭 했을 경우 ContentWriteActivity로 이동한다.
        binding.contentWriteBtn.setOnClickListener {
            startActivity(Intent(this, ContentWriteActivity::class.java))
        }

		// 데이터베이스에서 데이터 읽어오기 
        getFBContentData()
    }

    private fun getFBContentData() {
        val postListener = object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                contentList.clear()

                for (data in snapshot.children) {
                    val item = data.getValue(ContentModel::class.java)
                    Log.d("ContentListActivity", "item: ${item}")
                    // 리스트에 읽어 온 데이터를 넣어준다.
                    contentList.add(item!!)
                }
                contentList.reverse()
                // notifyDataSetChanged()를 호출하여 adapter에게 값이 변경 되었음을 알려준다.
                contentAdapter.notifyDataSetChanged()
            }

            override fun onCancelled(error: DatabaseError) {
            }
        }
        // addValueEventListener() 메서드로 DatabaseReference에 ValueEventListener를 추가한다.
        FBRef.contentRef.addValueEventListener(postListener)
    }
}

📌 데이터를 읽을 때는 ValueEventListener를 사용하여 경로의 전체 내용을 읽고 변경사항을 수신 대기한다. ValueEventListener는 데이터의 초기 상태를 확인하거나 데이터가 변경될 때 호출된다.

  • onDataChange() : 리스너가 연결될 때 한 번 트리거된 후 하위 항목을 포함하여 데이터가 변경될 때마다 다시 트리거 된다.
  • onCancelled() : 읽기가 취소되면 호출된다.

📍 Database Write

📌 리스트 화면에서 글쓰기 버튼을 클릭할 경우 글쓰기 화면인 ContentWriteActivity.kt로 이동한다. 작성하기 버튼을 클릭하면 사용자가 입력한 제목/내용 텍스트와 현재 시간을 가져와서 데이터베이스에 데이터를 Write한다.

👩🏻‍💻 style.xml 생성하기

EditText에서 marginLeft, marginRight 등 속성 값이 중복되는 코드들을 방지하고자 style을 적용하여 EditText의 속성을 정의 해줬다.

// res/value/style.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="ContentEditText" parent="Widget.AppCompat.EditText">
        <item name="android:layout_marginLeft">20dp</item>
        <item name="android:layout_marginRight">20dp</item>
        <item name="android:layout_marginTop">10dp</item>
        <item name="android:padding">5dp</item>
        <item name="android:background">@drawable/background_radius</item>
    </style>
// res/drawable/background_radius.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <stroke
        android:width="1dp"
        android:color="#999999" />
    <solid android:color="#00ff0000" />
    <corners android:radius="3dp" />
</shape>

👩🏻‍💻 글쓰기 화면 Layout 구성하기

// activity_content_write.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".board.ContentWriteActivity">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:gravity="center"
            android:text="자유 게시판 글쓰기"
            android:textColor="@color/black"
            android:textSize="18sp"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/titleArea"
            style="@style/ContentEditText"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:hint="제목"
            android:textSize="16sp" />

        <EditText
            android:id="@+id/contentArea"
            style="@style/ContentEditText"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:layout_marginBottom="20dp"
            android:gravity="top"
            android:hint="내용을 자유롭게 입력해 보세요."
            android:textSize="14sp" />

        <Button
            android:id="@+id/writeBtn"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_margin="20dp"
            android:background="@color/black"
            android:text="작성하기"
            android:textColor="@color/white"
            android:textSize="16sp" />

    </LinearLayout>
</layout>

👩🏻‍💻 글쓰기 화면 Activity 구현하기

// ContentWriteActivity.kt
class ContentWriteActivity : AppCompatActivity() {
    lateinit var binding: ActivityContentWriteBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_content_write)

        binding.writeBtn.setOnClickListener {
            val title = binding.titleArea.text.toString()
            val content = binding.contentArea.text.toString()
            val time = getTime()

			// push()는 목록을 만들어주며 랜덤한 문자열을 할당한다.
            val key = FBRef.contentRef.push().key.toString()

			// child()는 해당 키 위치로 이동하는 메서드로 child()를 사용하여 key 값의 하위에 값을 저장한다.
			// setValue() 메서드를 사용하여 값을 저장한다.
            FBRef.contentRef
                .child(key)
                .setValue(ContentModel(title, content, time))

            Toast.makeText(this, "게시글 입력 완료", Toast.LENGTH_SHORT).show()

            finish()
        }
    }

	// 시간을 구하는 함수
    fun getTime(): String {
        val currentDateTime = Calendar.getInstance().time
        val dateFormat =
            SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.KOREA).format(currentDateTime)

        return dateFormat
    }
}

📌 Database 데이터 확인하기

Firebase 데이터베이스를 확인 해보면 content 목록 아래에 입력한 값들이 저장 된 것을 확인 할 수 있다.

Firebase에 앱 연결
Android에서 데이터 읽기 및 쓰기

profile
개발 공부 기록 🌱

0개의 댓글