본문 바로가기

프로그래밍/안드로이드

[Android/Firebase] Firestore 읽기, 쓰기

이번 글에서는 Firestore에 저장된 데이터를 읽어 리사이클러 뷰에 추가하는 것과, 앱에서 Firestore 데이터베이스에 데이터를 추가하는 법을 알아본다.


Firestore에 문서 추가

먼저 Firestore에 문서를 추가하는 작업을 한다.

 

1. '컬렉션 시작' 버튼 클릭
2. 원하는 컬렉션 이름 입력 후 다음 버튼 클릭
3. 문서 ID를 정하고 원하는 데이터를 채워넣는다

여기서는 이름(name)과 전화번호(number) 필드를 생성했다.

 

4. 저장을 누르면 문서가 추가된 것을 확인할 수 있다.

 

레이아웃, 리사이클러 뷰 어댑터

사용한 레이아웃 구성이다.

activity_main.xml
 
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat
    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=".MainActivity">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/btn_write"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:text="쓰기"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/btn_read"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="16dp"
            android:layout_marginBottom="8dp"
            android:text="읽기"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.appcompat.widget.LinearLayoutCompat>

activity_main.xml, 메인 액티비티

 

list_layout.xml
 
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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="wrap_content"
    android:layout_margin="4dp"
    app:cardCornerRadius="8dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/list_tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="TextView"
            android:textStyle="bold"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/list_tv_number"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="8dp"
            android:text="TextView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/list_tv_name" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

list_layout.xml, 리사이클러 뷰 아이템 레이아웃

 

다음은 리사이클러 뷰 아이템, 어댑터 클래스다

ListLayout.kt
 
// 리사이클러 뷰 아이템
class ListLayout (val name: String, val number: String)

이때 변수 타입은 Firestore 필드의 타입과 같게 한다.

 

ListAdapter.kt
 
// 리사이클러 뷰 어댑터
class ListAdapter(val itemList: ArrayList<ListLayout>): RecyclerView.Adapter<ListAdapter.ViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListAdapter.ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_layout, parent, false)
        return ViewHolder(view)
    }

    override fun getItemCount(): Int {
        return itemList.size
    }

    override fun onBindViewHolder(holder: ListAdapter.ViewHolder, position: Int) {
        holder.name.text = itemList[position].name
        holder.number.text = itemList[position].number
    }

    class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        val name: TextView = itemView.findViewById(R.id.list_tv_name)
        val number: TextView = itemView.findViewById(R.id.list_tv_number)
    }
}

 

안드로이드 앱에서 문서 읽기

먼저 Firestore 내의 문서를 읽는 방법을 알아본다. 이번에 할 일은 문서들을 읽어 리사이클러 뷰에 추가하는 것이다.

MainActivity.kt
 
class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding    // 뷰 바인딩
    val db = FirebaseFirestore.getInstance()    // Firestore 인스턴스 선언
    val itemList = arrayListOf<ListLayout>()    // 리스트 아이템 배열
    val adapter = ListAdapter(itemList)         // 리사이클러 뷰 어댑터

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        binding.rvList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
        binding.rvList.adapter = adapter

        binding.btnRead.setOnClickListener {
            db.collection("Contacts")   // 작업할 컬렉션
                .get()      // 문서 가져오기
                .addOnSuccessListener { result ->
                    // 성공할 경우
                    itemList.clear()
                    for (document in result) {  // 가져온 문서들은 result에 들어감
                        val item = ListLayout(document["name"] as String, document["number"] as String)
                        itemList.add(item)
                    }
                    adapter.notifyDataSetChanged()  // 리사이클러 뷰 갱신
                }
                .addOnFailureListener { exception ->
                    // 실패할 경우
                    Log.w("MainActivity", "Error getting documents: $exception")
                }
        }
    }

}

가져온 문서는 hashMap의 형태로 되어있으며 문서["필드명"]으로  값을 이용할 수 있다.

리사이클러 뷰에 가져온 데이터를 넣을 때는 as를 사용해서 형변환을 해주어야 한다.

 

실행 결과, 읽기 버튼을 누르면 데이터가 정상적으로 리사이클러 뷰에 추가되는 것을 확인할 수 있다.

 

실행 결과

 

안드로이드 앱에서 문서 쓰기

다음으로 안드로이드 앱에서 Firestore에 문서를 추가하는 방법을 알아본다. 다음은 쓰기 버튼 클릭 시 문서를 작성할 수 있는 대화상자가 나오는 코드다.

binding.btnWrite.setOnClickListener {
    // 대화상자 생성
    val builder = AlertDialog.Builder(this)

    // 대화상자에 텍스트 입력 필드 추가, 대충 만들었음
    val tvName = TextView(this)
    tvName.text = "Name"
    val tvNumber = TextView(this)
    tvNumber.text = "Number"
    val etName = EditText(this)
    etName.isSingleLine = true
    val etNumber = EditText(this)
    etNumber.isSingleLine = true
    val mLayout = LinearLayout(this)
    mLayout.orientation = LinearLayout.VERTICAL
    mLayout.setPadding(16)
    mLayout.addView(tvName)
    mLayout.addView(etName)
    mLayout.addView(tvNumber)
    mLayout.addView(etNumber)
    builder.setView(mLayout)

    builder.setTitle("데이터 추가")
    builder.setPositiveButton("추가") { dialog, which ->
    // EditText에서 문자열을 가져와 hashMap으로 만듦
    val data = hashMapOf(
        "name" to etName.text.toString(),
        "number" to etNumber.text.toString()
    )
    // Contacts 컬렉션에 data를 자동 이름으로 저장
    db.collection("Contacts")
        .add(data)
        .addOnSuccessListener {
            // 성공할 경우
            Toast.makeText(this, "데이터가 추가되었습니다", Toast.LENGTH_SHORT).show()
        }
        .addOnFailureListener { exception ->
            // 실패할 경우
            Log.w("MainActivity", "Error getting documents: $exception")
        }
    }
    builder.setNegativeButton("취소") { dialog, which ->

    }
    builder.show()
}

실행 결과

 

Firestore에도 정상적으로 추가되었다.

 

Firestore 내에 문서가 추가됨