본문 바로가기
Android

[Android/Kotlin] 왜 notifyDataSetChanged 대신 DiffUtil을 사용해야 하는가?

by quessr 2024. 6. 28.

안드로이드 앱 개발에서 RecyclerView를 사용하여 데이터를 표시할 때, 데이터 변경 사항을 UI에 반영하는 방법은 매우 중요합니다. 흔히 사용되는 notifyDataSetChanged()와 더 효율적인 DiffUtil에 대해 설명하고, 왜 DiffUtil을 사용하는 것이 더 나은 선택인지 예시를 통해 알아보겠습니다.

1. DiffUtil이란?

DiffUtil은 안드로이드의 RecyclerView에서 두 데이터 세트를 비교하여 변경된 항목들을 찾아내고, 해당 변경 사항만 RecyclerView에 업데이트하는 유틸리티 클래스입니다. DiffUtil은 RecyclerView의 성능을 최적화하고, 더 나은 사용자 경험을 제공하는 데 중요한 역할을 합니다.

2. notifyDataSetChanged()의 문제점

notifyDataSetChanged()는 데이터 세트 전체에 변경이 발생했음을 RecyclerView에 알리는 메서드입니다. 이는 모든 아이템을 다시 바인딩하고, 재생성하므로 다음과 같은 문제가 발생할 수 있습니다:

  • 성능 저하: 전체 데이터 세트를 다시 로드하기 때문에, 특히 큰 데이터 세트의 경우 성능에 큰 영향을 미칩니다.
  • UI 깜빡임: 모든 아이템이 새로 고침되므로, 사용자에게 UI가 깜빡이는 것처럼 보일 수 있습니다.
  • 비효율적: 실제로 변경된 항목이 몇 개 안 되더라도 전체 데이터 세트를 업데이트합니다.

3. DiffUtil의 장점

DiffUtil은 이러한 문제점을 해결하기 위해 고안된 클래스입니다. 주요 장점은 다음과 같습니다:

  • 효율성: 변경된 항목만 업데이트하여 성능을 최적화합니다.
  • 부드러운 애니메이션: 변경된 항목만 업데이트하기 때문에 부드러운 UI 애니메이션을 제공합니다.
  • 정확성: 변경된 항목을 정확하게 찾아내어 업데이트합니다.

4. DiffUtil 사용 예시

다음은 DiffUtil을 사용하여 RecyclerView를 업데이트하는 예시입니다.

1. DiffUtil.Callback 구현

먼저, DiffUtil.Callback을 구현해야 합니다. 이 클래스는 두 데이터 세트를 비교하여 차이를 계산합니다.

class MyDiffUtilCallback(
    private val oldList: List<MyData>,
    private val newList: List<MyData>
) : 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을 사용하여 두 리스트를 비교하고, 차이점만을 업데이트합니다.

fun updateData(newList: List<MyData>) {
    val diffCallback = MyDiffUtilCallback(oldList, newList)
    val diffResult = DiffUtil.calculateDiff(diffCallback)

    oldList.clear()
    oldList.addAll(newList)

    diffResult.dispatchUpdatesTo(myAdapter)
}

 

이렇게 하면 RecyclerView는 변경된 항목만 업데이트하여 성능이 향상되고, 부드러운 사용자 경험을 제공합니다.

 

notifyDataSetChanged()는 간단하지만, 성능 저하와 UI 깜빡임 문제를 야기할 수 있습니다. DiffUtil을 사용하면 변경된 항목만 정확하게 업데이트하여 이러한 문제를 해결할 수 있습니다.

+ 24/07/09
위의 예시는 RecyclerView.Adapter에 DiffUtil.Callback 클래스를 작성하고 DiffUtil.calculateDiff를 호출하여 DiffUtil을 사용하는 방법입니다.


RecyclerView.Adapter에 DiffUtil을 사용해도 되지만, 고민을 해본 결과 ListAdapter를 사용하는 것이 더 효율적이라는 결론에 이르게 되었습니다. ListAdapter는 내부적으로 AsyncListDiffer를 사용하여 DiffUtil을 자동으로 처리해주기 때문에, 다음과 같은 장점이 있습니다:

  1. 간결성: ListAdapter는 DiffUtil을 내부적으로 처리하므로, 코드가 더 간결해집니다. 추가적인 DiffUtil.Callback 클래스를 작성하고 DiffUtil.calculateDiff를 호출하는 코드가 필요하지 않습니다.
  2. 효율성: AsyncListDiffer를 사용하여 비동기적으로 DiffUtil 계산을 수행하기 때문에 성능이 향상됩니다. 이는 RecyclerView가 대량의 데이터를 처리할 때 유용합니다.
  3. 코드 유지보수 용이성: ListAdapter를 사용하면 데이터 목록의 변경 사항을 자동으로 처리해주기 때문에, 더 적은 코드로 동일한 기능을 구현할 수 있습니다.

아래의 글에 ListAdapter와 DiffUtil을 같이 사용하는 방법을 기록 해 두었습니다.

https://quessr.tistory.com/63 

 

[Android/Kotlin] DiffUtil과 ListAdapter를 활용한 효율적인 RecyclerView 업데이트

안드로이드 개발에서 RecyclerView는 리스트 형태의 데이터를 표시하는 데 많이 사용됩니다. RecyclerView를 최적화하는 방법 중 하나는 DiffUtil과 ListAdapter를 사용하는 것입니다. 이 글에서는 DiffUtil과 L

quessr.tistory.com