ListView: 정의, Android 애플리케이션의 간단한 리스트

저자: IT Sectr 게시일: 2026-02-23 읽는 시간: 8 분

ListView는 세로 리스트를 표시하기 위한 기본 Android 컴포넌트로, API 1(Android 1.0)부터 사용할 수 있습니다. ListView는 ArrayAdapter를 사용하여 항목 리스트를 만들고 사용자 정의 셀 레이아웃을 지원합니다. ListView가 무엇인지, 구성 방법, 그리고 이 오래되었지만 여전히 사용되는 컴포넌트가 숨기고 있는 함정을 알아보겠습니다. IT Sectr에서는 레거시 프로젝트에서 ListView를 접하며 Android 4.4와의 하위 호환성을 유지하고 있습니다. 최신 대안과 비교하려면 RecyclerView에 관한 문서를 읽어보세요.

주요 내용

  • ListView는 세로 리스트용 내장 Android SDK 컴포넌트로, Android 1.0에서 도입되었습니다.
  • ArrayAdapter는 toString() 또는 사용자 정의 레이아웃을 통해 데이터 배열을 ListView에 바인딩하는 표준 어댑터입니다.
  • SimpleAdapter는 Map 데이터의 리스트용 어댑터로, ViewModel 없이 간단한 화면에 편리합니다.
  • onItemClickListener는 어댑터에 추가 구현이 필요 없는 내장 클릭 핸들러입니다.
  • convertView는 ListView의 View 재사용 메커니즘으로, 개발자가 수동으로 구현해야 합니다( ViewHolder와 유사).

Android에서 ListView란?

ListView는 android.widget 패키지의 ViewGroup으로, 스크롤 가능한 항목 리스트를 표시하도록 설계되었습니다. ListView는 AdapterView 패턴을 구현합니다. 데이터는 어댑터(Adapter)를 통해 전달되며, 데이터 항목을 화면에 표시되는 View로 변환합니다. ListView는 첫 번째 버전(API 1)부터 Android SDK에 존재하며 추가 라이브러리가 필요하지 않습니다.

아키텍처는 RecyclerView보다 간단합니다. LayoutManager가 내장되어 있고(세로 스크롤만 가능), 변경 애니메이션이 없으며, 구분선은 XML 속성 android:divider로 설정됩니다. ListView는 자동으로 OverScrollView와 포커스 강조를 추가하여 프로토타이핑을 단순화합니다. Statista(2025)에 따르면, Play Store 상위 100개 Android 앱 중 약 15%가 여전히 ListView를 포함하고 있습니다 — 대부분 최소한의 지원을 받는 레거시 프로젝트입니다.

역사 및 관련성

ListView는 2008년부터 2014년까지 Android의 주요 리스트 컴포넌트였으며, 2014년 Google I/O에서 RecyclerView가 발표되었습니다. 부분적으로 사용 중단(soft-deprecated)되었음에도 불구하고, ListView는 모든 Android 버전에서 계속 사용할 수 있으며 SDK API 35에서 @Deprecated로 표시되지 않았습니다. 프로젝트가 Android 4.4(API 19) 이하를 지원하는 경우, RecyclerView에는 AndroidX 및 Gradle 종속성이 필요하므로 ListView가 유일한 내장 선택입니다.

어댑터 유형: ArrayAdapter 및 SimpleAdapter

ArrayAdapter는 ListView에서 가장 인기 있는 어댑터입니다. List<T>와 각 항목의 레이아웃을 받습니다. 기본적으로 ArrayAdapter는 각 객체에서 toString()을 호출하고 simple_list_item_1(단일 TextView)에 텍스트를 표시합니다. 사용자 정의 표시를 위해서는 getView()를 재정의하거나 생성자에 사용자 정의 레이아웃을 전달해야 합니다.

kotlin
// ArrayAdapter로 ListView(간단한 텍스트)
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val countries = listOf(
        "러시아", "미국", "독일",
        "프랑스", "일본", "중국", "브라질"
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val adapter = ArrayAdapter<String>(
            this,
            android.R.layout.simple_list_item_1,
            countries
        )
        binding.listView.adapter = adapter

        // 클릭 처리
        binding.listView.onItemClickListener =
            AdapterView.OnItemClickListener { _, _, position, _ ->
                Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
            }
    }
}

SimpleAdapter는 Map 데이터의 리스트용으로 설계되었습니다. List<Map<String, Any>>와 Map 키를 레이아웃의 위젯 ID에 바인딩하기 위한 from/to 배열을 받습니다. SimpleAdapter는 별도의 어댑터 클래스가 필요 없는 빠른 프로토타입에 편리합니다. 단점은 타입 안전성 부족과 사용자 정의 로직의 복잡성입니다.

xml
<!-- ListView 레이아웃 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@color/divider"
        android:dividerHeight="1px"
        android:scrollbars="vertical" />
</LinearLayout>

ViewHolder로 사용자 정의 어댑터 만들기

사용자 정의 어댑터는 ListView의 getView() 메서드를 재정의합니다. RecyclerView는 이를 onCreateViewHolder + onBindViewHolder로 나누지만, ListView에서는 두 단계가 결합되어 있으므로 개발자가 convertView(재사용 가능한 View)와 setTag() 메서드를 통해 ViewHolder 패턴을 독립적으로 구현해야 합니다.

kotlin
// 데이터 모델
data class Product(
    name: String,
    price: String,
    imageResId: Int
)

// ViewHolder로 사용자 정의 어댑터
class ProductAdapter(
    context: Context,
    private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {

    // 캐싱을 위한 ViewHolder
    private class ViewHolder(view: View) {
        val nameText: TextView = view.findViewById(R.id.tvName)
        val priceText: TextView = view.findViewById(R.id.tvPrice)
        val imageView: ImageView = view.findViewById(R.id.ivProduct)
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val view = convertView ?: LayoutInflater.from(context)
            .inflate(R.layout.item_product, parent, false)
            .also { it.tag = ViewHolder(it) }

        val holder = view.tag as ViewHolder
        val product = getItem(position)

        holder.nameText.text = product.name
        holder.priceText.text = product.price
        holder.imageView.setImageResource(product.imageResId)

        return view
    }
}

RecyclerView와의 주요 차이점: ListView에서 getView() 메서드는 항목이 화면에 나타날 때마다 호출됩니다. convertView와 ViewHolder를 사용하지 않으면 스크롤할 때마다 새 View 객체가 생성되어 50개 이상 항목 리스트에서 메모리 누수 및 지연이 발생합니다. 사용자 정의 레이아웃이 있는 ListView에서는 view.tag를 사용한 ViewHolder가 필수 최적화입니다.

ListView의 한계: Google이 RecyclerView를 권장하는 이유

ListView에는 Google이 RecyclerView에서 수정한 네 가지 아키텍처 제한 사항이 있습니다: (1) 세로 방향만 가능 — 그리드, 가로 리스트 또는 계단식 없음; (2) 세부 알림 없음 — 데이터 변경 시 notifyDataSetChanged()를 통해 전체 다시 그리기가 트리거되어 떨림 발생; (3) 항목 추가/제거 애니메이션 없음; (4) 책임 분리 없음 — LayoutManager가 내장되어 있고 ItemAnimator가 없음.

xml
<!-- item_product.xml — ListView 항목 레이아웃 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="12dp">

    <ImageView
        android:id="@+id/ivProduct"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:scaleType="centerCrop" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingStart="12dp">

        <TextView
            android:id="@+id/tvName"
            android:textSize="16sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/tvPrice"
            android:textSize="14sp"
            android:textColor="@color/price" />
    </LinearLayout>
</LinearLayout>

실제 영향: 최대 20개 항목의 리스트에서는 ListView가 RecyclerView만큼 빠르게 작동합니다. 100개 이상 항목에서는 차이가 눈에 띄게 나타납니다. diff 계산 부족과 강제 ViewHolder로 인해 ListView의 FPS가 떨어지기 시작합니다. 자주 업데이트되는 리스트(채팅, 피드, 로그)에는 ListView가 적합하지 않습니다 — Paging 3과 함께 RecyclerView를 사용하세요. IT Sectr에서는 50개 이상의 항목이 있는 레거시 화면을 작업할 때 ListView를 RecyclerView로 마이그레이션합니다.

XML 속성을 통한 ListView 구성

ListView는 코드 없이 빠른 구성을 위한 XML 속성 세트를 제공합니다: android:divider(구분선 색상/드로어블), android:dividerHeight(두께), android:scrollbars(스크롤바), android:fastScrollEnabled(핸들러로 빠른 스크롤), android:choiceMode(선택 모드: singleChoice, multipleChoice). android:entries 속성을 사용하면 어댑터 없이 리소스 배열(string-array)에서 리스트를 채울 수 있습니다.

속성설명예시 값
android:divider구분선 색상 또는 드로어블@color/gray_light
android:dividerHeightpx/dp 단위 구분선 두께1dp
android:entries리소스의 문자열 배열@array/countries
android:choiceMode항목 선택 모드singleChoice
android:fastScrollEnabled빠른 스크롤true
android:overlapAnchor팝업 리스트 앵커(API 21+)true

선택 모드: choiceMode="singleChoice"는 선택된 항목이 강조 표시되는 RadioButton 스타일을 활성화합니다. choiceMode="multipleChoice" — 여러 항목에 대한 체크박스입니다. 선택 데이터는 listView.checkedItemPosition 또는 listView.checkedItemIds를 통해 사용할 수 있습니다. 중요: choiceMode에서 ListView는 배경에 StateListDrawable을 사용합니다 — 표준 파란색이 디자인과 맞지 않으면 자체 선택기를 설정하세요.

ListView vs RecyclerView: 기능 비교

선택은 ListView와 RecyclerView 사이에서 프로젝트 요구 사항에 따라 달라집니다. RecyclerView가 현대적인 표준이지만, ListView는 레거시 코드 유지 관리 비용이 최소화된 간단한 화면에서 정당화될 수 있습니다. 주요 매개변수 비교:

매개변수ListViewRecyclerView
라이브러리 크기SDK에 내장(≈60KB)AndroidX(종속성 포함 ≈300KB)
최소 버전API 1API 14(AndroidX 통해)
방향세로만세로, 가로, 그리드, 계단식
ViewHolder수동 구현아키텍처상 필수
애니메이션페이드 전환만DefaultItemAnimator(add, remove, move, change)
세부 업데이트없음(notifyDataSetChanged만)notifyItemInserted/Removed/Changed
클릭 리스너onItemClickListener(내장)ViewHolder 통해(내장 아님)
성능최대 50개 항목 리스트에 충분1000+ 항목에서 안정적인 60FPS

결론: 새 프로젝트에는 RecyclerView를 사용하세요. ListView는 마이그레이션이 불합리하게 비싼 레거시 코드에만 남겨두세요. Material Design 3 가이드라인(2026)에 따르면, 모든 복잡성의 리스트는 최신 성능 및 접근성 표준을 충족하기 위해 RecyclerView를 사용해야 합니다.

자주 묻는 질문

ListView는 사용 중단되었나요?

ListView는 Android SDK API 35에서 @Deprecated로 표시되지 않았지만, Google은 새 프로젝트에서 사용을 권장하지 않습니다(soft-deprecated). 이 컴포넌트는 모든 Android 버전에서 계속 작동합니다. RecyclerView는 2014년부터 공식 대체품입니다. 새 프로젝트를 시작하는 경우 RecyclerView를 선택하세요. 기존 프로젝트를 유지하는 경우 ListView는 변경 없이 계속 작동합니다.

ListView에 헤더/푸터를 추가하는 방법은?

ListView는 addHeaderView(View, Object, boolean)addFooterView(View, Object, boolean) 메서드를 지원합니다. 헤더와 푸터는 일반 항목 리스트에 추가되지만 어댑터에는 참여하지 않습니다 — 항상 리스트의 시작과 끝에 표시됩니다. 중요: 어댑터를 설정(setAdapter())하기 전에 헤더/푸터를 추가하세요. 그렇지 않으면 포함되지 않습니다. 헤더를 추가한 후 항목 위치 ID는 헤더 수만큼 이동합니다.

ListView가 ScrollView 내에서 스크롤되지 않는 이유는?

ListViewScrollView 모두 세로 제스처를 처리하여 충돌이 발생합니다. 외부 ScrollView가 스크롤을 가로채서 ListView가 스크롤되지 않습니다. 해결책: (1) ListView를 ScrollView 안에 중첩하지 마세요 — NestedScrollingChild와 함께 RecyclerView를 사용하세요. (2) ScrollView를 NestedScrollView(API 21+)로 교체하세요. (3) ScrollView 내에서 고정 높이(android:layout_height="...dp")의 ListView를 사용하세요. 하지만 이렇게 하면 응답성이 손상됩니다.

ListView를 지우고 데이터를 업데이트하는 방법은?

ListView에서 데이터 지우기는 어댑터 유형에 따라 다릅니다. ArrayAdapter의 경우: adapter.clear()adapter.addAll(newList)adapter.notifyDataSetChanged()를 호출합니다. 사용자 정의 어댑터의 경우: 데이터 리스트를 업데이트하고 notifyDataSetChanged()를 호출합니다. 참고: listView.invalidateViews()는 View를 다시 그리기만 하고 데이터는 업데이트하지 않습니다. 항상 어댑터를 통해 데이터를 변경하고 직접 변경하지 마세요.

ListView 항목의 높이를 변경하는 방법은?

ListView 항목의 높이는 항목 레이아웃에서 설정됩니다. 레이아웃이 layout_height="wrap_content"를 사용하는 경우 높이가 내용에 맞게 조정됩니다. 모든 항목에 고정 높이를 사용하려면 getView()에서 LayoutParams를 설정하는 사용자 정의 어댑터를 사용하세요: view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight). 다양한 높이의 경우 다른 뷰 유형으로 RecyclerView를 사용하세요.

요약

  • ListView는 세로 리스트용 기본 Android SDK 컴포넌트로, API 1부터 사용 가능합니다.
  • ArrayAdapter는 문자열 리스트용 가장 간단한 어댑터입니다. 사용자 정의 레이아웃에는 재정의된 getView()가 필요합니다.
  • ListView의 ViewHolder는 convertView와 view.tag를 통해 수동으로 구현됩니다 — 필수 성능 요구 사항입니다.
  • ListView는 choiceMode(single/multiple), header/footer, fastScroll 및 어댑터 없이 android:entries를 지원합니다.
  • ListView 한계: 세로 방향만, 애니메이션 없음, 변경 시 전체 다시 그리기.
  • ListView는 사용 중단으로 표시되지 않았지만 Google은 모든 새 프로젝트에 RecyclerView를 권장합니다.
  • 최대 50개 항목 리스트에는 ListView로 충분합니다. 그보다 복잡한 경우 RecyclerView를 사용하세요.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기