Firebase Console 화면에서 좌측의 Realtime Database를 클릭한다.
데이터베이스 만들기 버튼을 클릭하면 설정 팝업이 뜬다. 첫 번째로 실시간 데이터베이스 위치를 설정 해준다.
두 번째로는 보안 규칙을 설정해주는데 잠금 모드일 경우 데이터는 기본적으로 비공개가 되며 인증 된 사용자만 읽기/쓰기가 가능하다.
테스트 모드일 경우는 데이터가 기본적으로 공개 되며 참조 사용자는 누구나 읽기/쓰기가 가능하다.
데이터베이스가 만들어지면 규칙 수정 탭에서 read/write를 true로 수정해준다.
모듈(앱 수준) Gradle 파일에서 데이터베이스 라이브러리의 종속 항목을 추가한다.
dependencies {
implementation platform('com.google.firebase:firebase-bom:31.2.2')
implementation 'com.google.firebase:firebase-database-ktx'
}
📌 사용자가 글 작성시에 Database에 데이터를 Write하고 저장된 데이터를 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 = ""
)
// 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>
// 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
}
}
}
// FBRef.kt
class FBRef {
companion object {
private val database = Firebase.database
val contentRef = database.getReference("content")
}
}
// 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() : 읽기가 취소되면 호출된다.
📌 리스트 화면에서 글쓰기 버튼을 클릭할 경우 글쓰기 화면인 ContentWriteActivity.kt로 이동한다. 작성하기 버튼을 클릭하면 사용자가 입력한 제목/내용 텍스트와 현재 시간을 가져와서 데이터베이스에 데이터를 Write한다.
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>
// 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>
// 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
}
}
Firebase 데이터베이스를 확인 해보면 content 목록 아래에 입력한 값들이 저장 된 것을 확인 할 수 있다.