이전 포스팅에서 리사이클러뷰를 사용하여 유저들의 정보를 출력하는 방법에 대해서 배웠다.
리사이클러뷰 사용법에 대해 초점을 맞췄기 때문에 유저들의 정보를 임의로 만들어서 사용했었다.
그래서 이번에는 데이터베이스에 유저 정보를 저장하고 그 유저들의 정보를 리사이클러뷰에 출력해보려 한다.
view에서 데이터베이스에 바로 접근하는 것이 아닌 MVVM Repository 패턴을 활용할 예정이다.
- Repository에서는 요청받은 정보를 데이터베이스로부터 가져와 view model에 넘겨준다.
- view model에서는 model에 요청한 정보를 사용할 목적에 맞게 바꾸어 저장한다.
- view에서는 view model로 부터 원하는 정보를 가져와서 출력한다.
일단 이전 포스팅에 사용한 코드를 불러온다.
https://growth-coder.tistory.com/31
그리고 dependency에 다음 코드를 추가해준다.
implementation 'androidx.activity:activity-ktx:1.6.1'
MainActivity에 plain text 3개와 버튼을 추가하여 vertical chain을 걸어주었다.
<xml 파일 코드>
<?xml version="1.0" encoding="utf-8"?>
<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=".MainActivity">
<EditText
android:id="@+id/post_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="Name"
app:layout_constraintBottom_toTopOf="@+id/postAge"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/postAge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="Age"
app:layout_constraintBottom_toTopOf="@+id/postAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/post_name" />
<EditText
android:id="@+id/postAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="Address"
app:layout_constraintBottom_toTopOf="@+id/btnPost"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/postAge" />
<Button
android:id="@+id/btnPost"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toTopOf="@+id/recycler_view"
app:layout_constraintTop_toBottomOf="@+id/postAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteX="32dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="390dp"
android:layout_height="347dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnPost"
app:layout_constraintVertical_bias="0.806" />
</androidx.constraintlayout.widget.ConstraintLayout>
LiveData VS MutableLiveData
먼저 LiveData의 정의와 MutableLiveData와의 차이점을 알아야한다.
간단하게 LiveData는 생명 주기를 인식하며 데이터가 변화할 경우 데이터를 업데이트하여 최신 정보를 유지한다.
LiveData는 Observer와 함께 사용되는데 LiveData의 데이터가 변화할 경우 Observer에 데이터의 변화를 알려주는 역할을 한다.
이러한 LiveData는 읽기만 가능하고 값을 수정할 수는 없다.
그에 비해 MutableLiveData는 이름에서도 알 수 있듯이 값을 수정할 수 있다.
그러면 ViewModel에서는 둘 중 어떤 것을 사용할까? 정답은 둘 다 사용한다.
먼저 ViewModel에서는 데이터를 MutableLiveData로 정의하고 ViewModel 외부에서 값을 읽으려 할 때는 LiveData로 넘겨준다.
이렇게 구현할 경우 외부에서는 ViewModel의 데이터를 변경할 수 없고 오로지 ViewModel에서만 값을 변경할 수 있기 때문에 안정성이 높아진다.
Repository
repository에서는 데이터베이스에 접근을 해야하므로 Firebase를 연동시켜야 한다.
https://growth-coder.tistory.com/36
Repository는 데이터베이스에서 값을 가져와 ViewModel로 보내주는 역할을 한다.
여기에서 데이터베이스에 저장과 데이터 가져오기를 진행한다.
val userRef = Firebase.database.getReference("user")
class Repository {
fun observeUsers(usersData : MutableLiveData<ArrayList<Person>>){
userRef.addValueEventListener(object:ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
val users = ArrayList<Person>()
for(snap in snapshot.children){
val user = snap.getValue<Person>()
user?.let{ //user가 null이 아닐때만 배열에 넣기
users.add(it)
}
}
usersData.postValue(users) //데이터베이스에 넣어줌
}
override fun onCancelled(error: DatabaseError) {
}
})
}
fun addUserData(data : Person){ //데이터베이스에 저장
userRef.push().setValue(data)
}
}
MutableLiveData는 postValue라는 메소드를 가지고 있는데 이는 원하는 데이터를 MutableLiveData에 넣어주는 것이다.
위 코드를 보면 데이터베이스에서 user 정보들을 Person 클래스 형식에 맞추어 가져와 배열을 만들어서 다시 ViewModel의 MutableLiveData에 저장하는 것이다.
ViewModel
view에서는 데이터가 필요하면 ViewModel에서 가져와야한다.
ViewModel에서는 MutableLiveData 타입의 데이터를 가지고 있어야한다.
그 데이터의 값은 repository에서 가져온다. (repository는 데이터베이스에서 값을 가져온다.)
class DataViewModel : ViewModel() {
private val repository = Repository()
private val _people=MutableLiveData<ArrayList<Person>>()
init{
repository.observeUsers(_people) //레포지토리를 사용하여 데이터베이스에서 값 가져옴
}
val people : LiveData<ArrayList<Person>> get() = _people
fun addUser(data : Person){
repository.addUserData(data) //레포지토리를 사용하여 데이터베이스에 값 저장
}
}
이제는 view에서 ViewModel에 접근하여 값을 저장하고 가져오면 된다.
이렇게 MVVM 패턴을 활용하면 View와 독립적으로 데이터를 가공하고 전달할 수 있다.
View
이제 View에서는 observe를 활용하여 데이터의 변화를 계속 감지하면서 리사이클러뷰에 변화를 주면 된다.
class MainActivity : AppCompatActivity() {
val model : DataViewModel by viewModels() //viewmodel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnPost.setOnClickListener {
val user = run{
val name = binding.postName.text.toString()
val age = binding.postAge.text.toString()
val address = binding.postAddress.text.toString()
Person(name,age,address)
}
model.addUser(user)
}
model.people.observe(this){
val people = model.people.value //viewmodel에서 데이터 가져옴
people?.let{//people이 null이 아닐 때만 실행
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = UserAdapter(people)
}
}
}
}
이제 유저 정보를 추가해보면 view -> ViewModel -> Repository 순서로 데이터가 이동하여 최종적으로는 Repository에서 데이터베이스에 정보를 추가하게 된다.
이렇게 데이터가 변화하면 다시 Repository -> ViewModel -> view 순서로 데이터를 가져와서 실시간으로 갱신해준다.
ViewModel과 Repository는 다음 깃허브 주소에 올려두었다. 필요하면 자신의 코드에 맞게끔 변경하여 사용하면 좋을 듯하다.
https://github.com/ezcolin2/AndroidClass/tree/main/MVVM
'공부 > Android Studio' 카테고리의 다른 글
[Android/Kotlin] 리사이클러 뷰 아이템에 애니메이션 (view binding) (0) | 2023.01.03 |
---|---|
[Android/Kotlin] Foreground Service 사용 방법 (0) | 2022.12.24 |
[Android Studio] Firebase와 연결하기 (0) | 2022.12.13 |
[Android/Kotlin] Recycler view 사용법 (0) | 2022.12.08 |
[Android Studio] fragment 사용법 3 (bottom navigation) (0) | 2022.12.01 |
댓글