본문 바로가기
Android

안드로이드 UI의 진화: ListView에서 RecyclerView, 그리고 Compose까지 [Part 1 ListView]

by 중곰 2025. 1. 21.

 

안녕하세요. 중곰입니다.

 

안드로이드 UI 의 진화 ListView에서 RecyclerView, 그리고 Compose까지에서

Part.1 ListView 입니다.

 

개발 연차가 되어가지만 한번씩 내용을 정리하면서

글을 쓰기 위해서 정리해보았습니다.

 

안드로이드에서 UI 중 리스트 데이터를 보이기 위해 일반적으로 사용되는 UI는

ListView, RecyclerView, Compose(LazyColum)이 있습니다.

 

가장 먼저 ListView를 살펴봅니다.

ListView


  • 기본 제공되는 위젯 중 하나이며, 세로로 스크롤 가능한 화면을 표시할 때 사용
  • 리스트의 각 항목은 일반적으로 단일 뷰 (ex : TextView, ImageView) 또는 커스텀 뷰로 구성

특징

  • 리스트 데이터를 화면에 표시하기 위한 간단한 방법
  • 어댑터 : 데이터와 뷰를 연결하는데 ArrayAdapter or CursorAdapter 주로 사용
  • 적은양의 데이터나 정적 리스트에 적합

언제 사용

  • 간단한 리스트가 필요할 때
  • 데이터 항목이 적고, 스크롤 성능이 중요하지 않을 때
  • 간단한 어댑터를 사용해 빠르게 구현해야 할 때

간단한 예제

/** xml **/

<ListView
    android:id="@+id/listView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
/** MainActivity **/
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val listView = findViewById<ListView>(R.id.listView)
        val data = arrayOf("Item 1", "Item 2", "Item 3")
        val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, data)
        listView.adapter = adapter
    }
}

한계점

  • 뷰 재사용 문제
    • RecyclerView 처럼 효율적인 뷰 재사용을 제공하지 않음
    • 사용자 정의 ViewHolder를 구현해야만 재사용 최적화가 가능
  • 레이아웃 제한
    • 가로 방향 스크롤 및 Grid 레이아웃을 지원하지 않음
      • 기본적으로 세로 반향의 단일 열 레이아웃만 지원
  • 애니메이션 부족
    • 스크롤 애니메이션과 UI 업데이트가 제한적
  • 성능 저하
    • 대량의 데이터를 처리할 때 성능 문제가 발생
    • 스크롤 시 findViewById 호출이 빈번하여 비용이 증가
      • findViewById는 뷰를 계층 구조에서 검색하므로 호출 비용이 큼
  • 확장성 부족
    • 다양한 레이아웃 매니저(Linear, Grid, Staggerd 등)를 지원하지 않음
    • Item Decoration( 아이템 구분선) 과 같은 기능 제한적

ListView 성능 개선 방안

  1. ViewHolder 패턴 구현
    1. findVidwById 호출을 최소화하기 위해 ViewHolder 패턴을 사용해 각 뷰의 참조를 캐싱
    2. 예제
    class MyAdapter(context: Context, private val data: List<String>) : BaseAdapter() {
    
        private val inflater: LayoutInflater = LayoutInflater.from(context)
    
        override fun getCount(): Int = data.size
    
        override fun getItem(position: Int): Any = data[position]
    
        override fun getItemId(position: Int): Long = position.toLong()
    
        override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
            val viewHolder: ViewHolder
            val view: View
    
            if (convertView == null) {
                view = inflater.inflate(android.R.layout.simple_list_item_1, parent, false)
                viewHolder = ViewHolder(view.findViewById(android.R.id.text1))
                view.tag = viewHolder
            } else {
                view = convertView
                viewHolder = view.tag as ViewHolder
            }
    
            viewHolder.textView.text = data[position]
            return view
        }
    
        class ViewHolder(val textView: TextView)
    }
    
    
  2. RecyclerView로 전환
    1. ViewHolder 패턴을 내장하고 있어 개발자가 직접 구현하지 않아도 성능 최적화가 이루어짐

ListView 대안

  • RecyclerView
    • 가장 직접적인 대안
    • 주요 장점
      • 효율적인 뷰 재사용 (ViewHolder 패턴 내장)
      • 다양한 레이아웃 지원(LinearLayoutManager, GridLayoutManager)
      • 커스텀 애니메이션, 드래그 앤 드롭, 아이템 삽입/삭제 애니메이션 지원
  • ScrollView + LinearLayout
    • 데이터가 매우 적고 정적인 경우, ScrollView와 LinearLayout을 사용해 수동으로 리스트 구현 가능
    • 데이터 많을 경우 비효율적
  • Jetpack Compose (LazyColumn)
    • 선언형 UI 방식으로 ListView와 RecyclerView를 대체
    • 주요 장점
      • 적은 코드로 구현 가능
      • 변경된 상태만 기반으로 필요한 UI를 재구성하므로, 업데이트 성능 최적화
  • 재사용 및 업데이트 처리 비교
    • ListView
      • 기본적으로 전체 리스트를 다시 그리는 방식 (invalidate)으로 UI 업데이트
      • ViewHolder 패턴을 구현하지 않으면, 모든 뷰가 새로 생성되어 성능 저하
    • RecyclerView
      • ViewHolder 패턴으로 기존 뷰를 재사용하여 효율적 업데이트
      • notifyItemChanged 같은 메서드를 명시적으로 호출해야 하며, 뷰를 갱신하려면 데이터를 수동으로 바인딩
    • Compose
      • UI 요소는 기존 뷰를 재사용하는 대신 필요한 부분만 다시 그리며, 선언적으로 상태를 관리
      • LazyColumn 은 내부적으로 각 리스트 아이템을 상태 기반으로 트래킹하여 변경된 항목만 재구성
      • 개발자가 뷰 재사용을 명시적으로 관리하지 않아도, Compose의 상태 관리 메커니즘이 자동으로 처리
  • 비교항목 ListView vs RecyclerView  vs Jetpack Compose
    UI 업데이트 방식 전체 다시 그림 재사용(ViewHolder) 후 부분 갱신 변경된 상태만 재구성
    재사용 방식 없음(ViewHolder 직접 구현 필요) ViewHolder 기반 상태 기반 트래킹 (뷰 재사용 불필요)
    부분 업데이트 관리 복잡 (전체 갱신이 일반적) notifyItemChanged 등 명시적 호출 필요 상태 변화 시 자동 업데이트
    애니메이션 처리 직접 구현 필요 ItemAnimator 설정 필요 애니메이션 및 상태 변화가 자연스럽게 통합
    개발 복잡성 높은 유지보수 비용 상대적으로 더 구조화된 코드 선언형 방식으로 간소화된 코드

ListView를 살펴보면, 거의 기본 동작에 정적이 데이터와 짧은 데이터를 표기 하기 위한 위젯으로 봐도 될 것 같고, 리스트 데이터를 화면에 처리 하기에는 다소 어려움이 있기에 RecyclerView가 ListView에 가장 적합한 대안이 될 수 있다라고 생각합니다.

 

마지막 비교항목에서

다음 파트들의 스포가 된것 같지만,

해당 내용이 계속 중복이 되면서

정리해서 비교해보면 좋을 것 같습니다.

 

그럼 Part.2 RecyclerView에서 다시 만나 뵙겠습니다.

 

글을 읽어주셔서 감사합니다.

반응형