Android DiffUtil과 ListAdapter 사용법 ✨

Android DiffUtil에 대한 이해와 활용 📱✨

DiffUtil은 Android RecyclerView에서 데이터 변경을 효율적으로 처리하는 데 도움을 주는 유틸리티 클래스입니다. 이 글에서는 DiffUtil의 기본 개념, 사용 방법 및 예제 코드를 통해 그 활용법을 알아보겠습니다.

 

DiffUtil이란? 🤔

DiffUtil은 두 리스트 간의 차이를 계산하여 RecyclerView의 아이템을 효율적으로 업데이트합니다. 이를 통해 불필요한 전체 리스트의 갱신을 방지하고, 성능을 개선할 수 있습니다.

 

왜 DiffUtil을 사용해야 할까요? ⚡

  • 성능 향상: 전체 리스트를 다시 그리지 않고 필요한 부분만 갱신합니다.
  • 애니메이션 효과: 변경된 아이템에 대해 부드러운 애니메이션을 제공합니다.
  • 쉬운 구현: 비교적 간단한 API로 손쉽게 적용할 수 있습니다.

 

DiffUtil 사용 방법 💡

1. DiffUtil.Callback 구현하기

DiffUtil을 사용하려면 먼저 DiffUtil.Callback을 상속받아 데이터를 비교하는 로직을 구현해야 합니다.

class MyDiffCallback(
    private val oldList: List<MyItem>,
    private val newList: List<MyItem>
) : DiffUtil.Callback() {

    override fun getOldListSize(): Int = oldList.size

    override fun getNewListSize(): Int = newList.size

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].id == newList[newItemPosition].id
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition] == newList[newItemPosition]
    }
}

 

2. DiffUtil을 사용하여 데이터 업데이트하기

이제 RecyclerView 어댑터에서 DiffUtil을 활용하여 데이터를 업데이트할 수 있습니다.

class MyAdapter(private var items: List<MyItem>) : RecyclerView.Adapter<MyViewHolder>() {

    fun updateItems(newItems: List<MyItem>) {
        val diffCallback = MyDiffCallback(items, newItems)
        val diffResult = DiffUtil.calculateDiff(diffCallback)

        items = newItems
        diffResult.dispatchUpdatesTo(this)
    }

    // 나머지 어댑터 메서드 구현...
}

 

3. 데이터 변경 시 업데이트 호출하기

val newItems = fetchDataFromServer() // 새로운 데이터 가져오기
myAdapter.updateItems(newItems) // 어댑터에 새로운 데이터 업데이트

 

ListAdapter란? 📋

ListAdapter는 RecyclerView.Adapter의 확장으로, DiffUtil을 내장하고 있어 데이터 목록의 변경을 자동으로 처리합니다. ListAdapter를 사용하면 DiffUtil을 직접 구현할 필요 없이 간단하게 데이터 변화를 관리할 수 있습니다.

 

ListAdapter 사용법 💡

1. ListAdapter 생성하기

ListAdapter를 사용하려면 먼저 ListAdapter를 상속받아야 합니다. 아래는 간단한 예제입니다.

class MyListAdapter : ListAdapter<MyItem, MyViewHolder>(MyDiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        // 뷰홀더 생성 로직
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        // 데이터 바인딩 로직
        val item = getItem(position)
        holder.bind(item)
    }
}

 

2. DiffUtil.Callback 구현하기

ListAdapter를 사용할 때는 DiffUtil.Callback을 구현하여 데이터 비교 로직을 작성합니다.

class MyDiffCallback : DiffUtil.ItemCallback<MyItem>() {
    override fun areItemsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
        return oldItem == newItem
    }
}

 

3. 데이터 업데이트 시 사용하기

ListAdapter의 submitList 메서드를 사용하여 새로운 리스트를 제출합니다.

이 메서드는 DiffUtil을 자동으로 호출하여 변경된 아이템을 처리합니다.

val newItems = fetchDataFromServer() // 새로운 데이터 가져오기
myListAdapter.submitList(newItems) // 새로운 데이터 제출

 

 

RecyclerView.Adapter와 ListAdapter의 차이점 ⚖️

항목 RecyclerView.Adapter ListAdapter
DiffUtil 사용 수동으로 구현 필요 내장된 DiffUtil 사용
성능 최적화 직접 관리 자동으로 최적화
코드 간결성 상대적으로 복잡함 간결하고 직관적
데이터 업데이트 방법 notifyDataSetChanged() 등 사용 submitList() 메서드 사용

 

ListAdapter와 DiffUtil 사용 시 주의할 점 ⚠️

  1. Immutable List 사용: submitList()에 전달하는 리스트는 변경 불가능한 리스트(Immutable List)여야 합니다. 변경 가능한 리스트를 사용하면 예상치 못한 동작을 초래할 수 있습니다.
  2. 중복 데이터 처리: areItemsTheSame와 areContentsTheSame 메서드에서 데이터의 고유성을 정확히 판단해야 합니다. 잘못된 비교 로직은 성능 저하와 UI 오류를 일으킬 수 있습니다.
  3. UI 업데이트 주의: submitList() 호출 후 UI가 즉시 반영되지 않을 수 있습니다. 데이터 리스트가 변경될 때는 UI 업데이트가 애니메이션과 함께 처리되므로, 비동기 작업에 주의해야 합니다.
  4. 리스트 크기 변화 주의: 리스트의 크기가 크게 변할 때, 성능에 영향을 미칠 수 있습니다. 가능한 경우 작은 변경 사항을 유지하여 submitList()를 호출하는 것이 좋습니다.
  5. 스레드 안전성: UI 스레드에서 submitList()를 호출해야 합니다. 배경 스레드에서 호출하면 예외가 발생할 수 있습니다.

 

결론 🎉

ListAdapter는 DiffUtil을 자동으로 처리해 주므로, RecyclerView에서 데이터의 변화를 관리하는 데 매우 유용합니다.

이 글이 ListAdapter와 DiffUtil에 대한 이해를 높이는 데 도움이 되었길 바랍니다.