Android 앱 접근성(Accessibility) 완벽 가이드 — TalkBack, contentDescription, 글자 크기, 터치 영역과 자동화 테스트

Android 앱을 개발할 때 화면 디자인과 기능 구현에 집중하다 보면 놓치기 쉬운 부분이 있습니다.

바로 접근성(Accessibility)입니다.

접근성은 시각, 청각, 운동 능력 등에 제약이 있는 사용자뿐 아니라 다양한 환경에서 앱을 사용하는 모든 사람을 고려하는 설계입니다.

예를 들어 다음과 같은 상황을 생각해 볼 수 있습니다.

  • 화면의 작은 글씨를 읽기 어려운 사용자

  • 화면을 보지 않고 TalkBack으로 앱을 사용하는 사용자

  • 작은 버튼을 정확하게 누르기 어려운 사용자

  • 스마트폰의 글자 크기를 크게 설정한 사용자

  • 화면 확대 기능을 사용하는 사용자

  • 색상만으로 표시된 상태를 구분하기 어려운 사용자

앱이 정상적으로 실행되고 화면이 보기 좋다고 해서 모든 사용자가 편리하게 사용할 수 있는 것은 아닙니다.

특히 XML 기반 Android 앱에서는 contentDescription, importantForAccessibility, labelFor, accessibilityHeading 같은 속성을 적절히 설정하는 것이 중요합니다.

이번 글에서는 Kotlin + XML 기반 Android 앱에서 접근성을 개선하는 방법을 실전 코드와 함께 알아보겠습니다.

작성 기준: 2026년 10월. 예제는 Android Views(XML), Kotlin, RecyclerView, Fragment, Material Components, AndroidX를 기준으로 작성했습니다. minSdk 24 이상을 고려하며, 일부 접근성 API는 Android 버전에 따라 분기 처리가 필요합니다.


1. Android 접근성이란?

Android 접근성은 다양한 사용자가 앱의 기능과 정보를 이용할 수 있도록 지원하는 기술과 설계 원칙입니다.

대표적인 접근성 기능은 다음과 같습니다.

기능설명
TalkBack화면 요소를 음성으로 안내
글자 크기 조절시스템 설정에 따라 텍스트 확대
화면 확대화면 일부를 확대해 확인
스위치 제어외부 스위치 등으로 화면 조작
음성 접근음성 명령으로 앱 조작
고대비 표시화면 요소의 구분을 쉽게 함
접근성 포커스보조 기술이 탐색하는 요소 지정

Android 앱에서는 이러한 기능이 제대로 작동하도록 UI를 설계해야 합니다.

접근성은 별도의 특수 기능을 추가하는 작업만을 의미하지 않습니다.

기존 UI를 올바르게 구현하는 것만으로도 접근성을 크게 개선할 수 있습니다.


2. 접근성이 부족한 Android 앱에서 발생하는 문제

다음과 같은 앱을 생각해 보겠습니다.

조류 정보 앱

[이미지] 참새
         Passer montanus

         ♡ 좋아요

[이미지] 까치
         Pica serica

         ♥ 좋아요

일반 사용자는 이미지와 아이콘을 보고 기능을 이해할 수 있습니다.

하지만 TalkBack 사용자에게는 다음과 같은 문제가 발생할 수 있습니다.

  • 이미지에 대한 설명이 없음

  • 좋아요 버튼의 기능을 알 수 없음

  • 좋아요 상태를 구분할 수 없음

  • 목록 항목을 읽는 순서가 부자연스러움

  • 같은 정보가 여러 번 읽힘

이러한 문제를 해결하려면 화면에 표시되는 정보뿐 아니라 접근성 서비스에 전달되는 정보도 설계해야 합니다.

3. 접근성 구현의 기본 원칙

Android 앱 접근성을 개선할 때는 다음 원칙을 기억하는 것이 좋습니다.

  1. 모든 중요한 정보는 보조 기술로 확인할 수 있어야 합니다.

  2. 버튼과 입력 요소에는 의미 있는 이름이 있어야 합니다.

  3. 선택 상태와 활성화 상태를 구분할 수 있어야 합니다.

  4. 글자 크기가 커져도 내용이 잘리지 않아야 합니다.

  5. 터치 영역은 충분히 커야 합니다.

  6. 색상만으로 정보를 전달하지 않아야 합니다.

  7. 화면 탐색 순서가 논리적이어야 합니다.

  8. 접근성 기능을 켠 상태에서 실제로 테스트해야 합니다.


4. contentDescription이란?

contentDescription은 화면 요소의 의미를 접근성 서비스에 전달하는 속성입니다.

대표적으로 이미지 버튼이나 의미 있는 이미지에 사용합니다.

예를 들어 다음과 같은 이미지 버튼이 있다고 가정하겠습니다.

<ImageButton
    android:id="@+id/buttonFavorite"
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:src="@drawable/ic_favorite"
    android:background="?attr/selectableItemBackgroundBorderless" />

이 버튼은 화면에는 하트 아이콘으로 표시됩니다.

하지만 접근성 설명이 없다면 TalkBack 사용자가 버튼의 목적을 이해하기 어려울 수 있습니다.

다음과 같이 수정합니다.

<ImageButton
    android:id="@+id/buttonFavorite"
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:src="@drawable/ic_favorite"
    android:contentDescription="@string/add_favorite"
    android:background="?attr/selectableItemBackgroundBorderless" />

strings.xml에는 다음과 같이 작성합니다.

<string name="add_favorite">즐겨찾기에 추가</string>

이렇게 하면 버튼의 의미를 전달할 수 있습니다.


5. contentDescription을 모든 View에 설정해야 할까?

그렇지는 않습니다.

이미 화면에 표시되는 텍스트가 충분한 설명을 제공한다면 불필요하게 contentDescription을 추가하지 않는 것이 좋습니다.

예를 들어 다음과 같은 버튼입니다.

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/save" />

버튼의 텍스트가 '저장'이라면 일반적으로 별도의 contentDescription이 필요하지 않습니다.

오히려 잘못 설정하면 화면에 표시된 텍스트와 TalkBack 안내가 달라질 수 있습니다.

따라서 다음과 같이 구분하는 것이 좋습니다.

UI 요소권장 처리
텍스트가 있는 버튼기본 텍스트 활용
아이콘만 있는 버튼contentDescription 설정
의미 있는 이미지이미지 설명 제공
장식용 이미지접근성 탐색에서 제외
입력창적절한 레이블 연결
상태 표시 아이콘상태를 텍스트나 접근성 속성으로 전달

6. 장식용 이미지 접근성 처리

모든 이미지를 TalkBack에서 읽어야 하는 것은 아닙니다.

예를 들어 카드 배경이나 단순 장식 이미지는 사용자에게 별도의 정보를 제공하지 않습니다.

이러한 이미지는 접근성 탐색에서 제외할 수 있습니다.

<ImageView
    android:layout_width="match_parent"
    android:layout_height="120dp"
    android:src="@drawable/background_pattern"
    android:importantForAccessibility="no" />

이렇게 하면 불필요한 탐색 요소를 줄일 수 있습니다.

단, 이미지 자체가 중요한 정보를 제공한다면 접근성에서 제외해서는 안 됩니다.

예를 들어 조류의 종류를 구분하기 위한 사진이라면 이미지 설명을 제공하는 것이 좋습니다.


7. 동적으로 contentDescription 설정하기

RecyclerView에서는 데이터에 따라 이미지 설명이 달라질 수 있습니다.

예를 들어 조류 이미지라면 다음과 같이 설정할 수 있습니다.

binding.imageBird.contentDescription =
    "${bird.nameKo} 사진"

다만 목록에서 조류 이름이 이미 텍스트로 제공된다면 이미지 설명을 중복해서 읽게 될 수 있습니다.

이 경우 카드 전체의 접근성 구조를 고려해야 합니다.

예를 들어 목록 항목을 하나의 탐색 단위로 구성한다면 이미지 설명을 별도로 읽지 않도록 설계할 수 있습니다.


8. 좋아요 버튼의 접근성 상태 처리

좋아요 버튼은 단순히 버튼 이름만 제공하는 것으로 충분하지 않을 수 있습니다.

현재 좋아요 상태도 전달해야 합니다.

예를 들어 다음과 같은 상태가 있습니다.

좋아요 해제 상태
♡
좋아요 선택 상태
♥

Kotlin에서는 다음과 같이 처리할 수 있습니다.

fun updateFavoriteAccessibility(
    button: ImageButton,
    isFavorite: Boolean
) {
    button.contentDescription =
        if (isFavorite) {
            "즐겨찾기에서 제거"
        } else {
            "즐겨찾기에 추가"
        }

    button.isSelected = isFavorite
}

이렇게 하면 버튼의 기능과 선택 상태를 전달할 수 있습니다.

다만 isSelected만으로 모든 보조 기술에서 토글 상태가 동일하게 표현된다고 가정해서는 안 됩니다.

선택 가능한 토글 컨트롤이라면 CheckBox, ToggleButton, MaterialButton의 체크 가능 동작 등 의미에 맞는 컴포넌트를 우선 고려하는 것이 좋습니다.


9. 접근성 상태 설명 추가하기

Android에서는 접근성 상태를 별도로 설명할 수 있습니다.

예를 들어 좋아요 상태를 다음과 같이 전달할 수 있습니다.

ViewCompat.setStateDescription(
    binding.buttonFavorite,
    if (isFavorite) {
        "선택됨"
    } else {
        "선택되지 않음"
    }
)

ViewCompat.setStateDescription()을 사용하려면 호환되는 AndroidX Core 라이브러리가 필요합니다.

이렇게 하면 버튼 이름과 상태를 분리해 관리할 수 있습니다.

예를 들어 다음과 같은 정보를 제공할 수 있습니다.

즐겨찾기
선택됨
버튼

실제 음성 안내 문구와 순서는 TalkBack 버전 및 접근성 서비스에 따라 달라질 수 있습니다.


10. RecyclerView 접근성 개선

RecyclerView는 Android 앱에서 자주 사용하는 UI 컴포넌트입니다.

하지만 하나의 아이템 안에 이미지, 제목, 설명, 좋아요 버튼, 메뉴 버튼 등이 포함되어 있다면 접근성 탐색이 복잡해질 수 있습니다.

예를 들어 다음과 같은 카드가 있다고 가정하겠습니다.

┌──────────────────────────┐
│ [사진] 참새              │
│        Passer montanus   │
│                          │
│        ♡       ⋮         │
└──────────────────────────┘

이 카드에서 접근성 탐색 단위를 어떻게 구성할지 결정해야 합니다.

일반적으로 다음과 같은 구조가 적합할 수 있습니다.

  1. 조류 정보 카드

  2. 즐겨찾기 버튼

  3. 추가 메뉴 버튼

이렇게 하면 불필요한 중복 탐색을 줄일 수 있습니다.


11. RecyclerView 카드 설명 설정하기

카드 전체가 클릭 가능한 경우 다음과 같이 설명을 구성할 수 있습니다.

binding.root.contentDescription =
    "${bird.nameKo}, ${bird.scientificName}"

그리고 카드 내부에서 중복되는 텍스트는 별도 접근성 탐색에서 제외할 수 있습니다.

binding.textName.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_NO

binding.textScientificName.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_NO

단, 카드 전체를 하나의 탐색 요소로 만들 때는 실제로 클릭 가능한 자식 버튼이 정상적으로 탐색되는지 확인해야 합니다.

부모 View의 접근성 설정이 자식 컨트롤을 숨기지 않도록 주의해야 합니다.

특히 noHideDescendants는 모든 하위 요소를 접근성 탐색에서 제외하므로, 좋아요 버튼처럼 독립적인 조작이 필요한 자식이 있다면 사용하지 않는 것이 좋습니다.


12. RecyclerView ViewHolder에서 접근성 처리하기

다음은 ViewHolder에서 접근성 설명을 갱신하는 예제입니다.

class BirdViewHolder(
    private val binding: ItemBirdBinding
) : RecyclerView.ViewHolder(binding.root) {

    fun bind(bird: Bird) {

        binding.textName.text = bird.nameKo
        binding.textScientificName.text =
            bird.scientificName

        binding.root.contentDescription =
            "${bird.nameKo}, ${bird.scientificName}"

        binding.buttonFavorite.contentDescription =
            if (bird.isFavorite) {
                "${bird.nameKo} 즐겨찾기에서 제거"
            } else {
                "${bird.nameKo} 즐겨찾기에 추가"
            }

        ViewCompat.setStateDescription(
            binding.buttonFavorite,
            if (bird.isFavorite) {
                "선택됨"
            } else {
                "선택되지 않음"
            }
        )
    }
}

RecyclerView에서는 ViewHolder가 재사용됩니다.

따라서 데이터가 변경될 때마다 접근성 설명과 상태도 함께 갱신해야 합니다.

이전 아이템의 설명이 남아 있지 않도록 주의해야 합니다.


13. 광고 아이템의 접근성 처리

RecyclerView에 AdMob 광고를 함께 표시하는 앱에서는 광고 아이템도 고려해야 합니다.

광고가 포함된 목록에서는 콘텐츠와 광고를 사용자가 구분할 수 있어야 합니다.

예를 들어 광고 영역에 다음과 같은 레이블을 제공할 수 있습니다.

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/advertisement"
    android:textSize="12sp" />
<string name="advertisement">광고</string>

다만 AdMob SDK가 제공하는 광고 View 내부의 접근성 속성을 임의로 변경하거나 광고 요소를 숨겨서는 안 됩니다.

광고 SDK의 표시 정책과 접근성 동작을 유지하면서 앱 자체의 주변 UI를 적절히 구성해야 합니다.


14. 글자 크기는 sp를 사용하기

Android에서 텍스트 크기를 지정할 때는 일반적으로 sp 단위를 사용해야 합니다.

예를 들어 다음과 같습니다.

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/bird_name"
    android:textSize="16sp" />

sp는 사용자의 글자 크기 설정을 반영하는 단위입니다.

반면 dp를 텍스트 크기에 사용하는 것은 일반적으로 적절하지 않습니다.

글자 크기를 고정하면 사용자가 시스템에서 글자 크기를 확대해도 앱의 텍스트가 충분히 커지지 않을 수 있습니다.


15. 글자 크기가 커질 때 발생하는 문제

텍스트 크기를 sp로 지정했다고 해서 접근성 문제가 모두 해결되는 것은 아닙니다.

예를 들어 다음과 같은 레이아웃이 있다고 가정하겠습니다.

<TextView
    android:layout_width="120dp"
    android:layout_height="40dp"
    android:text="@string/long_title"
    android:textSize="18sp" />

사용자가 글자 크기를 크게 설정하면 텍스트가 잘릴 수 있습니다.

따라서 텍스트가 늘어날 수 있는 영역은 가능한 한 유연하게 구성해야 합니다.

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/long_title"
    android:textSize="18sp" />

필요한 경우 maxLines, ellipsize, ConstraintLayout의 제약 조건 등을 함께 검토해야 합니다.

하지만 중요한 정보가 잘리지 않도록 하는 것이 우선입니다.


16. 글자 크기 확대 테스트

Android에서는 사용자가 시스템 설정에서 글자 크기를 변경할 수 있습니다.

따라서 최소한 다음과 같은 환경을 테스트하는 것이 좋습니다.

  • 기본 글자 크기

  • 큰 글자 크기

  • 최대에 가까운 글자 크기

  • 화면 표시 크기 확대

  • 작은 화면 기기

  • 가로 화면

특히 다음 UI를 확인해야 합니다.

  • Toolbar 제목

  • BottomNavigationView 메뉴

  • RecyclerView 카드

  • Dialog 메시지

  • 설정 화면

  • 검색창

  • 버튼 텍스트

Android 14 이상에서는 비선형 글꼴 크기 조정이 적용될 수 있으므로, 글자 크기 확대 시 화면 배치가 예상과 다르게 변할 수 있습니다.


17. 터치 영역은 충분히 크게 만들기

작은 버튼은 사용자가 정확하게 누르기 어렵습니다.

Android 접근성 가이드에서는 일반적으로 터치 가능한 요소에 최소 48dp × 48dp 수준의 터치 영역을 권장합니다.

예를 들어 다음과 같은 버튼은 터치하기 어려울 수 있습니다.

<ImageButton
    android:layout_width="24dp"
    android:layout_height="24dp"
    android:src="@drawable/ic_close" />

아이콘 크기가 24dp라고 해서 버튼 전체를 24dp로 만들 필요는 없습니다.

다음과 같이 수정할 수 있습니다.

<ImageButton
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:padding="12dp"
    android:src="@drawable/ic_close"
    android:contentDescription="@string/close"
    android:background="?attr/selectableItemBackgroundBorderless" />

이렇게 하면 아이콘 크기는 유지하면서 터치 영역을 확보할 수 있습니다.


18. TouchDelegate로 터치 영역 확장하기

레이아웃 구조상 버튼 크기를 늘리기 어려운 경우에는 TouchDelegate를 사용할 수 있습니다.

예를 들어 다음과 같습니다.

binding.buttonClose.post {

    val parent = binding.buttonClose.parent as View

    val rect = Rect()

    binding.buttonClose.getHitRect(rect)

    val extra = 12.dpToPx(requireContext())

    rect.inset(-extra, -extra)

    parent.touchDelegate = TouchDelegate(
        rect,
        binding.buttonClose
    )
}

dp 변환 함수는 다음과 같이 작성할 수 있습니다.

fun Int.dpToPx(context: Context): Int {
    return (
        this * context.resources.displayMetrics.density
    ).toInt()
}

다만 TouchDelegate는 부모 View의 좌표계를 기준으로 동작합니다.

또한 하나의 부모 View에 여러 TouchDelegate를 단순히 설정하면 이전 설정이 덮어써질 수 있습니다.

따라서 가능한 경우에는 버튼 자체의 터치 영역을 충분히 확보하는 것이 더 단순합니다.


19. 색상만으로 상태를 구분하지 않기

예를 들어 좋아요 버튼을 다음과 같이 표시한다고 가정하겠습니다.

회색 하트 = 좋아요 해제
빨간 하트 = 좋아요 선택

색상만으로 상태를 구분하면 일부 사용자는 차이를 인식하기 어려울 수 있습니다.

따라서 다음과 같은 방법을 함께 사용하는 것이 좋습니다.

  • 아이콘 모양 변경

  • 텍스트 표시

  • 접근성 상태 설명

  • 선택 상태 전달

  • 충분한 명암 대비 확보

예를 들어 좋아요 상태를 다음과 같이 구성할 수 있습니다.

♡ 즐겨찾기 추가

♥ 즐겨찾기 선택됨

화면 디자인에 따라 텍스트를 항상 표시하기 어렵다면 TalkBack에 상태를 명확하게 전달해야 합니다.


20. 텍스트 명암 대비

텍스트와 배경의 명암 대비도 중요한 접근성 요소입니다.

WCAG 기준에서는 일반 텍스트에 4.5:1 이상의 명암 대비를 권장하며, 큰 텍스트에는 3:1 이상의 대비를 적용할 수 있습니다.

예를 들어 연한 회색 배경에 밝은 회색 텍스트를 표시하면 읽기 어려울 수 있습니다.

낮은 대비

밝은 배경 + 매우 연한 회색 텍스트
높은 대비

밝은 배경 + 어두운 텍스트

Material Design 색상 시스템을 사용하더라도 실제 조합의 명암 대비를 확인해야 합니다.

특히 비활성화 상태, 힌트 텍스트, 작은 설명 문구를 주의 깊게 검토하는 것이 좋습니다.


21. EditText 접근성 개선

입력창에서는 사용자가 어떤 정보를 입력해야 하는지 명확하게 전달해야 합니다.

예를 들어 다음과 같은 검색창입니다.

<EditText
    android:id="@+id/editSearch"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/search_bird"
    android:inputType="text"
    android:imeOptions="actionSearch" />

힌트만 사용하는 방식도 가능하지만, 입력값이 있는 상태에서는 힌트가 사라질 수 있습니다.

따라서 중요한 입력 항목에는 별도의 레이블을 제공하는 것이 좋습니다.


22. labelFor로 입력창 연결하기

다음과 같이 TextView와 EditText를 연결할 수 있습니다.

<TextView
    android:id="@+id/labelSearch"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/search_bird"
    android:labelFor="@id/editSearch" />

<EditText
    android:id="@+id/editSearch"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="text"
    android:imeOptions="actionSearch" />

이렇게 하면 입력창의 목적을 보조 기술에 더 명확하게 전달할 수 있습니다.

특히 회원가입, 설정, 검색 조건 입력 등 여러 입력 요소가 있는 화면에서 유용합니다.


23. 오류 메시지 접근성 처리

입력값에 오류가 발생했을 때 단순히 빨간색 테두리만 표시하는 것은 충분하지 않습니다.

사용자가 오류 내용을 확인할 수 있어야 합니다.

예를 들어 다음과 같이 처리할 수 있습니다.

binding.textInputLayout.error =
    "검색어를 입력해 주세요."

Material Components의 TextInputLayout을 사용하면 오류 메시지를 표시할 수 있습니다.

다만 오류가 표시되는 시점과 접근성 서비스의 안내 동작은 실제 기기에서 확인해야 합니다.

중요한 오류라면 사용자가 입력창으로 돌아왔을 때도 오류를 확인할 수 있도록 유지하는 것이 좋습니다.


24. BottomNavigationView 접근성

BottomNavigationView는 앱의 주요 화면을 이동하는 UI입니다.

일반적으로 Material Components에서 제공하는 BottomNavigationView는 기본적인 접근성 동작을 지원합니다.

하지만 메뉴 이름과 아이콘을 명확하게 설정해야 합니다.

예를 들어 다음과 같습니다.

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/menuHome"
        android:icon="@drawable/ic_home"
        android:title="@string/home" />

    <item
        android:id="@+id/menuSearch"
        android:icon="@drawable/ic_search"
        android:title="@string/search" />

    <item
        android:id="@+id/menuFavorites"
        android:icon="@drawable/ic_favorite"
        android:title="@string/favorites" />

</menu>

메뉴 이름이 너무 짧거나 의미가 불분명하면 사용자가 기능을 이해하기 어려울 수 있습니다.

예를 들어 '기타'보다는 실제 기능을 나타내는 '설정'이 더 명확할 수 있습니다.


25. Dialog 접근성 처리

Dialog는 사용자에게 중요한 정보를 전달하거나 선택을 요청하는 UI입니다.

예를 들어 다음과 같은 삭제 확인 Dialog가 있다고 가정하겠습니다.

MaterialAlertDialogBuilder(requireContext())
    .setTitle("메모 삭제")
    .setMessage(
        "선택한 메모를 삭제하시겠습니까?"
    )
    .setPositiveButton("삭제") { _, _ ->
        deleteMemo()
    }
    .setNegativeButton("취소", null)
    .show()

표준 Dialog 컴포넌트는 기본적인 접근성 동작을 지원합니다.

하지만 다음 사항을 확인해야 합니다.

  • 제목이 명확한지

  • 메시지가 충분한지

  • 버튼 이름이 구체적인지

  • Dialog가 열렸을 때 포커스가 적절한지

  • Dialog를 닫은 후 원래 화면으로 돌아갈 수 있는지

특히 사용자 정의 Dialog에서는 접근성 포커스와 탐색 순서를 별도로 확인해야 합니다.


26. 화면 제목을 접근성 Heading으로 지정하기

화면의 주요 제목은 접근성 탐색에서 제목으로 인식되도록 설정할 수 있습니다.

XML에서는 다음과 같이 지정할 수 있습니다.

<TextView
    android:id="@+id/textTitle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/bird_detail"
    android:textSize="22sp"
    android:accessibilityHeading="true" />

다만 android:accessibilityHeading은 Android 9(API 28)부터 제공되는 플랫폼 속성입니다.

minSdk 24 앱에서는 AndroidX의 호환 API를 이용하는 방법도 고려할 수 있습니다.

ViewCompat.setAccessibilityHeading(
    binding.textTitle,
    true
)

이렇게 하면 지원되는 환경에서 보조 기술이 제목 단위로 화면을 탐색하는 데 도움이 됩니다.


27. 접근성 탐색 순서

화면의 시각적인 순서와 TalkBack 탐색 순서가 다르면 사용자가 혼란을 느낄 수 있습니다.

예를 들어 다음과 같은 화면이 있다고 가정하겠습니다.

제목
검색창
검색 버튼
검색 결과 목록

일반적으로 TalkBack 탐색도 이 순서를 따르는 것이 자연스럽습니다.

하지만 복잡한 ConstraintLayout이나 겹치는 View가 있는 경우 탐색 순서가 예상과 다를 수 있습니다.

따라서 실제 TalkBack으로 탐색하면서 확인해야 합니다.

필요한 경우 accessibilityTraversalBefore, accessibilityTraversalAfter 등을 사용할 수 있습니다.

다만 이러한 속성을 과도하게 지정하면 레이아웃 변경 시 관리가 어려워질 수 있습니다.

가능한 한 자연스러운 View 계층 구조를 먼저 설계하는 것이 좋습니다.


28. 화면 변경 시 접근성 안내

사용자가 버튼을 눌렀을 때 화면 상태가 변경되는 경우가 있습니다.

예를 들어 다음과 같습니다.

좋아요 버튼 선택
      ↓
좋아요 상태 변경
      ↓
화면 아이콘 갱신

이때 화면만 변경되고 접근성 서비스에 아무 정보도 전달되지 않으면 사용자가 결과를 알기 어려울 수 있습니다.

필요한 경우 다음 API를 사용할 수 있습니다.

binding.root.announceForAccessibility(
    "즐겨찾기에 추가되었습니다."
)

다만 모든 상태 변경에 음성 안내를 강제로 추가하면 오히려 사용 경험이 나빠질 수 있습니다.

먼저 버튼의 상태 설명을 정확하게 제공하고, 추가 안내가 필요한 상황에서만 사용하는 것이 좋습니다.


29. ViewPager2 접근성

ViewPager2는 이미지 갤러리나 소개 화면에서 자주 사용합니다.

예를 들어 조류 상세 화면에서 여러 장의 사진을 표시할 수 있습니다.

참새 상세 화면

[사진 1 / 3]

[이전] [다음]

이 경우 현재 사진의 위치와 이미지 설명을 전달하는 것이 좋습니다.

예를 들어 다음과 같은 설명을 사용할 수 있습니다.

binding.imageBird.contentDescription =
    "참새 사진 ${position + 1} / $totalCount"

사진마다 다른 정보가 있다면 설명을 구체적으로 제공할 수 있습니다.

참새가 나뭇가지에 앉아 있는 사진, 1 / 3

단, 모든 사진에 단순히 '이미지'라고만 표시하면 충분한 정보를 제공하기 어렵습니다.


30. 이미지 설명은 얼마나 자세해야 할까?

이미지 설명은 사용 목적에 따라 달라집니다.

예를 들어 장식용 배경 이미지는 별도의 설명이 필요하지 않을 수 있습니다.

반면 조류 도감에서는 사진이 중요한 콘텐츠입니다.

따라서 다음과 같은 정보를 제공할 수 있습니다.

  • 조류 이름

  • 사진에서 확인할 수 있는 주요 특징

  • 사진 순서

  • 필요한 경우 촬영 장소나 상황

예를 들어 다음과 같습니다.

참새가 나뭇가지에 앉아 있는 사진.
갈색 머리와 검은색 뺨 무늬가 보입니다.
2 / 4

다만 이미지에 실제로 나타나지 않는 특징을 설명에 포함해서는 안 됩니다.

자동 생성한 이미지 설명을 사용하는 경우에도 정확성을 검토하는 것이 좋습니다.


31. 접근성 포커스와 입력 포커스의 차이

Android에서는 접근성 포커스와 일반 입력 포커스가 서로 다릅니다.

입력 포커스는 키보드 입력이나 버튼 조작을 위한 포커스입니다.

접근성 포커스는 TalkBack과 같은 보조 기술이 현재 탐색 중인 요소를 나타냅니다.

따라서 다음과 같은 코드를 실행했다고 해서 접근성 포커스까지 동일하게 이동한다고 보장할 수는 없습니다.

binding.editSearch.requestFocus()

접근성 포커스 이동은 화면 구조와 접근성 서비스의 동작을 고려해야 합니다.

특히 Fragment 전환이나 Dialog 표시 후에는 TalkBack이 어느 요소부터 읽는지 실제로 확인하는 것이 중요합니다.


32. Custom View 접근성 처리

직접 만든 Custom View는 기본 View보다 접근성 처리가 더 필요할 수 있습니다.

예를 들어 Canvas로 여러 버튼이나 선택 영역을 그린 경우를 생각해 보겠습니다.

화면에는 여러 항목이 보이지만 접근성 서비스에서는 하나의 View로만 인식될 수 있습니다.

이러한 경우에는 다음과 같은 기능을 고려해야 합니다.

  • 접근성 노드 제공

  • 항목별 이름과 상태 전달

  • 접근성 액션 정의

  • 키보드 탐색 지원

  • 접근성 포커스 관리

AndroidX의 ExploreByTouchHelper는 복잡한 Custom View에서 가상 접근성 요소를 제공할 때 사용할 수 있습니다.

다만 일반적인 화면에서는 표준 Button, TextView, ImageView 등을 조합하는 것이 구현과 유지보수 측면에서 유리합니다.


33. 접근성 테스트 도구

Android 앱 접근성은 코드만 확인해서는 충분하지 않습니다.

실제 기기와 테스트 도구를 함께 사용하는 것이 좋습니다.

대표적인 방법은 다음과 같습니다.

도구주요 용도
TalkBack실제 음성 탐색 테스트
Accessibility Scanner접근성 문제 자동 점검
Android Studio Layout InspectorView 계층 구조 확인
EspressoUI 동작 테스트
UI Automator시스템 UI를 포함한 테스트
Accessibility Test Framework접근성 검사 자동화

자동화 도구는 접근성 문제를 발견하는 데 도움이 됩니다.

하지만 모든 문제를 자동으로 찾을 수 있는 것은 아닙니다.

특히 화면 설명의 의미, 자연스러운 탐색 순서, 사용자 경험은 실제 사용 테스트가 필요합니다.


34. TalkBack으로 직접 테스트하기

Android 기기에서 TalkBack을 활성화한 뒤 앱을 실행합니다.

일반적으로 설정의 접근성 메뉴에서 TalkBack을 찾을 수 있습니다.

제조사와 Android 버전에 따라 메뉴 위치는 달라질 수 있습니다.

테스트할 때는 다음 항목을 확인합니다.

  1. 화면 제목을 이해할 수 있는가?

  2. 버튼의 목적을 알 수 있는가?

  3. 좋아요 상태를 구분할 수 있는가?

  4. 목록 항목이 자연스럽게 읽히는가?

  5. 검색창의 목적이 명확한가?

  6. Dialog를 정상적으로 조작할 수 있는가?

  7. 이미지의 중요한 정보를 확인할 수 있는가?

  8. 화면 이동 후 포커스가 자연스러운가?

  9. 뒤로 가기 기능을 사용할 수 있는가?

  10. 모든 주요 기능을 화면을 보지 않고 사용할 수 있는가?


35. Accessibility Scanner 사용하기

Accessibility Scanner는 Android 앱 화면에서 접근성 관련 문제를 점검하는 데 사용할 수 있는 도구입니다.

대표적으로 다음 항목을 확인하는 데 도움이 됩니다.

  • 작은 터치 영역

  • 부족한 명암 대비

  • 접근성 레이블 문제

  • 일부 클릭 가능한 요소의 설명 누락

하지만 자동 검사 결과가 항상 정답인 것은 아닙니다.

예를 들어 장식용 이미지에 설명이 없다고 해서 반드시 문제가 되는 것은 아닙니다.

따라서 검사 결과를 확인한 뒤 실제 UI의 의미와 사용 목적에 맞게 수정해야 합니다.


36. Espresso 접근성 테스트

Android UI 테스트에서는 Espresso를 이용해 화면 동작을 검증할 수 있습니다.

예를 들어 버튼이 표시되고 클릭 가능한지 확인할 수 있습니다.

@Test
fun favoriteButton_isDisplayed() {

    onView(
        withId(R.id.buttonFavorite)
    ).check(
        matches(isDisplayed())
    )
}

버튼에 접근성 설명이 있는지도 확인할 수 있습니다.

@Test
fun favoriteButton_hasDescription() {

    onView(
        withId(R.id.buttonFavorite)
    ).check(
        matches(
            withContentDescription(
                R.string.add_favorite
            )
        )
    )
}

이 예제는 버튼의 설명이 고정되어 있는 상황을 가정합니다.

실제 좋아요 버튼처럼 상태에 따라 설명이 달라진다면 테스트 데이터와 상태를 명확하게 설정해야 합니다.


37. 접근성 자동 검사 적용하기

Android Accessibility Test Framework를 사용하면 Espresso 테스트에서 접근성 검사를 수행할 수 있습니다.

예를 들어 다음과 같은 방식입니다.

AccessibilityChecks.enable()

이 코드는 접근성 검사 기능을 활성화하는 예제입니다.

실제 프로젝트에서는 해당 테스트 라이브러리 의존성을 추가해야 합니다.

또한 검사 범위와 동작은 라이브러리 버전에 따라 달라질 수 있습니다.

자동 검사는 테스트에서 발견할 수 있는 접근성 문제를 줄이는 데 도움이 되지만, TalkBack을 이용한 수동 테스트를 대체하지는 못합니다.


38. 접근성 테스트 시 자주 놓치는 문제

문제 1. 이미지 버튼에 설명이 없음

해결:

의미 있는 contentDescription을 제공합니다.

문제 2. RecyclerView에서 이전 아이템 설명이 남음

해결:

ViewHolder의 bind()에서 설명과 상태를 매번 갱신합니다.

문제 3. 글자 크기를 키우면 버튼 텍스트가 잘림

해결:

고정 높이를 줄이고 유연한 레이아웃을 사용합니다.

문제 4. 좋아요 상태를 색상으로만 표시

해결:

선택 상태와 접근성 상태 설명을 함께 제공합니다.

문제 5. 작은 닫기 버튼을 누르기 어려움

해결:

터치 영역을 48dp 수준으로 확보합니다.

문제 6. Dialog가 열린 뒤 탐색 순서가 이상함

해결:

표준 Dialog 컴포넌트를 우선 사용하고 TalkBack으로 확인합니다.

문제 7. 화면 전환 후 중요한 제목을 찾기 어려움

해결:

화면 제목과 접근성 탐색 구조를 정리합니다.


39. 개인 개발자에게 추천하는 접근성 개선 순서

기존 앱에 접근성을 적용할 때는 모든 화면을 한 번에 수정하기보다 단계적으로 진행하는 것이 좋습니다.

1단계. 주요 화면 점검

메인 화면, 검색 화면, 상세 화면, 설정 화면을 확인합니다.

2단계. 이미지 버튼 설명 추가

좋아요, 검색, 닫기, 메뉴 버튼을 우선 점검합니다.

3단계. RecyclerView 접근성 정리

카드와 자식 버튼의 탐색 구조를 개선합니다.

4단계. 글자 크기 확대 테스트

큰 글자 설정에서 화면이 잘리지 않는지 확인합니다.

5단계. 터치 영역 확보

작은 버튼의 터치 영역을 개선합니다.

6단계. 상태 표현 개선

색상뿐 아니라 텍스트와 접근성 상태를 제공합니다.

7단계. TalkBack 테스트

실제 음성 탐색으로 주요 기능을 사용해 봅니다.

8단계. 자동화 테스트 추가

Espresso와 Accessibility Test Framework를 활용합니다.


40. Android 접근성 체크리스트

  • 중요한 이미지에 설명 제공

  • 장식용 이미지 접근성 탐색 제외

  • 아이콘 버튼에 contentDescription 설정

  • 텍스트 버튼에 불필요한 설명 중복 방지

  • 좋아요·선택 상태 전달

  • RecyclerView ViewHolder 접근성 상태 갱신

  • RecyclerView 탐색 순서 확인

  • 광고 아이템 접근성 검토

  • 텍스트 크기에 sp 사용

  • 큰 글자 크기에서 텍스트 잘림 확인

  • 화면 표시 크기 확대 테스트

  • 터치 영역 충분히 확보

  • 색상 외의 상태 표현 제공

  • 텍스트 명암 대비 확인

  • EditText 레이블 연결

  • 오류 메시지 접근성 처리

  • BottomNavigationView 메뉴 이름 확인

  • Dialog 제목과 버튼 이름 확인

  • 화면 제목 Heading 설정

  • 화면 탐색 순서 확인

  • ViewPager2 이미지 설명 제공

  • 화면 변경 시 필요한 안내 제공

  • Custom View 접근성 검토

  • TalkBack 실제 테스트

  • Accessibility Scanner 검사

  • Espresso 접근성 테스트 추가

  • 작은 화면과 가로 화면 테스트

41. 마무리

Android 앱 접근성은 단순히 contentDescription을 추가하는 작업이 아닙니다.

사용자가 화면을 어떻게 이해하고 조작하는지까지 고려해야 하는 UI 설계 과정입니다.

특히 Kotlin + XML 기반 Android 앱에서는 다음 원칙이 중요합니다.

  1. 의미 있는 이미지와 버튼에 적절한 설명을 제공합니다.

  2. 장식용 이미지는 불필요한 접근성 탐색에서 제외합니다.

  3. 좋아요와 선택 상태를 명확하게 전달합니다.

  4. RecyclerView의 접근성 설명을 데이터에 맞게 갱신합니다.

  5. 텍스트 크기는 sp를 사용하고 확대 시 레이아웃을 확인합니다.

  6. 터치 영역은 충분히 크게 구성합니다.

  7. 색상만으로 중요한 상태를 구분하지 않습니다.

  8. 입력창과 오류 메시지에 명확한 설명을 제공합니다.

  9. TalkBack으로 실제 화면 탐색을 테스트합니다.

  10. 자동화 도구와 수동 테스트를 함께 사용합니다.

접근성은 특정 사용자만을 위한 기능이 아닙니다.

글자 크기를 크게 사용하는 사람, 한 손으로 스마트폰을 사용하는 사람, 밝은 야외에서 화면을 보는 사람 등 다양한 환경에서 앱을 더 편리하게 사용할 수 있도록 돕습니다.

또한 표준 Android 컴포넌트를 올바르게 사용하고 접근성 구조를 정리하면 UI 유지보수와 테스트에도 도움이 됩니다.

접근성의 핵심은 모든 사용자가 앱의 중요한 정보를 이해하고, 필요한 기능을 스스로 사용할 수 있도록 만드는 것입니다.


참고 자료

태그: Android, Kotlin, Accessibility, TalkBack, contentDescription, RecyclerView, ViewPager2, BottomNavigationView, Android XML, 접근성, 글자 크기, 터치 영역, Accessibility Scanner, Espresso, UI 테스트