아티클

Jetpack compose 접근성 구현방법 알아보기 1부

2022-06-23 18:54:09

안녕하세요, 엔비전스입니다.

Android 네이티브 애플리케이션을 만드는 크게 레이아웃을 기반으로 하는 View System과 최근 정식 출시한 선언형 UI, Jetpack Compose로 구분할 수 있습니다. View System의 접근성 구현에 대해서는 널리 아티클 및 포럼을 통하여 꽤 많은 내용들을 다루었기 때문에 앞으로 당분간은 Jetpack Compose로 앱 구현 시 접근성 적용 방안에 대해서 기술하기로 하였습니다.

각 접근성 이슈 사례별 코드 예제에 대해서는 기존과 같이 포럼 팁을 통하여 기술하고, 본 아티클에서는 접근성과 관련된 API의 특징 및 참고사항 위주로 다루도록 하겠습니다.

 

접근성 구현에서의 특징

Image, Icon 등에 대체 텍스트 작성 의무화

Image, Icon과 같은 composable 사용 시에는 반드시 대체 텍스트, 즉 contentDescription을 삽입해야만 합니다. 그렇지 않으면 필수 항목이 채워지지 않아 빌드를 할 수 없습니다. 적어도 이미지나 아이콘이 들어가는 요소에는 대체 텍스트를 반드시 추가하도록 한 것입니다.

 

간단한 접근성 유형 커스터마이징

View System에서는 커스텀 뷰에 대한 요소 유형을 정의하려면 반드시 onInitializeAccessibilityNodeInfo 메서드를 통하여 AccessibilityNodeInfo 객체를 초기화하고 그 안에서 변경을 해야 했습니다. WEB, iOS에서 단 한 줄의 코드로 변경할 수 있는 것과 비교한다면 코드가 너무 길고 비효율적입니다.

그래서 접근성 구현에 대해 잘 모르는 경우에는 대체 텍스트 형태로 요소 유형 정보를 제공하는 경우가 많아 접근성이 개선되었음에도 스크린 리더의 기능을 제대로 사용할 수 없다는 부분에서 많은 불편함이 있는 것 또한 사실입니다. 그런데 compose는 semantics라는 modifier를 활용하면 iOS, WEB과 비슷하게 직관적이고 간단한 방법으로 role을 변경할 수 있습니다.

 

직관적이고 간결한 코드

접근성과 관련된 코드가 조금 더 직관적입니다. View System에서 사용되는 접근성 구현과 관련된 몇 가지 코드와 비교를 해 보겠습니다. (아래 표 참조)

View System(기존)

Jetpack Compose

 

setAccessibilityClassName

role

importantForAccessibility

clearAndSetSemantics

ViewCompat.replaceAccessibilityAction(ACTION_CLICK)

onClickLabel

 

대체 텍스트

접근성에 있어서 가장 중요하면서도 가장 잘 지켜지지 않는 것이 대체 텍스트 작성입니다. Compose에서도 대체 텍스트는 contentDescription 속성으로 추가할 수 있는데 View System과 달리 주의사항이 있습니다.

 

View System과 다른 텍스트 버튼 구현 방법

더보기, 이전, 다음과 같이 맥락에 따라 레이블에 대한 설명이 다소 모호한 경우에는 화면에 텍스트가 있음에도 공지사항 더보기, 이전 곡 재생과 같이 조금 더 자세한 대체 텍스트를 삽입하기도 합니다.

이미지나 아이콘이 아닌 일반 텍스트로 버튼을 구현하는 경우 Button 요소 하위에 Text 요소를 넣는 형태로 구현을 하게 되는데, 대체 텍스트를 반드시 버튼이 아닌 버튼 하위의 텍스트 요소에 주어야 한다는 것입니다.

만약 버튼 자체에 주게 되면 대체 텍스트와 화면에 있는 텍스트를 동시에 읽어주게 됩니다. View System에서는 버튼 뷰에 텍스트를 작성하는 android:text 속성으로 텍스트를 작성하기 때문에 실수할 일이 없지만, compose는 여러 composable들을 합성하여 사용하는 형태이기 때문에 주의가 필요합니다.

 

contentDescription은 전역 속성이 아니다

Image, Icon과 같은 직접 텍스트를 삽입하지 않는 일부 composable들은 편의를 위해 semantics modifier를 거치지 않고 contentDescription을 곧바로 사용 가능하지만, Button, RadioButton, Text과 같이 기본적으로 텍스트가 들어가는 요소들은 semantics modifier를 거쳐야만 대체 텍스트를 삽입할 수 있습니다.

 

텍스트 미포함 요소에 대한 대체 텍스트 작성 의무화

앞에서 설명한 것처럼 아이콘이나 이미지에는 반드시 대체 텍스트를 넣어야 합니다. 만약 장식용 이미지여서 스크린 리더 사용자에게 의미가 없는 경우에는 null 값으로 처리하면 됩니다. 다만, 단순히 빌드를 위해 모든 대체 텍스트를 null로 처리하는 일은 반드시 없어야 하겠습니다.

 

초점 합치기

View System에서 클릭 이벤트가 없는 단순 여러 개의 TextView가 하나의 LinearLayout 안에 들어 있다면 screenReaderFocusable 속성을 사용하여 마치 하나의 객체인 것처럼 초점을 합칠 수 있었습니다. Compose에서도 클릭 속성이 없는 여러 개의 텍스트 composable이 Row와 같은 레이아웃 안에 들어 있다면 mergeDescendants = true 값을 통하여 하위 초점들을 하나로 합칠 수 있습니다.

다만 View System과 동일하게 클릭 이벤트가 있는 composable 하위에 클릭 이벤트가 없는 이미지, 텍스트 등이 있으면 mergeDescendants에 true 값을 주지 않아도 자동으로 초점이 하나로 합쳐집니다. 예를 들어 Button 안에 Text 구조에서 버튼 하위에 버튼에 대한 텍스트가 존재하고 클릭 이벤트를 버튼에만 주었다면 초점은 하나인 것입니다.

그러나 Row 안에 CheckBox, Text가 있는 구조에서, Row에서 클릭 이벤트를 수신하고, CheckBox에서 onCheckedChange 이벤트를 수신하고 있다면, 클릭 이벤트가 부모와 자식에 모두 존재하므로 체크박스와 텍스트의 초점이 분리됩니다. 이러한 구조에서는 mergeDescendants =true 속성을 적용해도 초점이 합쳐지지 않습니다. 접근성 서비스 입장에서는 클릭 속성이 각각 있어서 하나로 합칠 수 없다고 판단하기 때문입니다.

따라서 위와 같은 구조에서 Talkback의 초점을 하나로 합치려면 체크박스와 같은 이벤트를 체크박스가 아닌 상위 레이아웃에 주고 체크박스에 대한 자체 이벤트는 null로 설정해 주어야 합니다. 그리고 상위 레이아웃에 요소 유형 및 상태 정보를 함께 줍니다. 이와 관련된 자세한 내용은 포럼 팁을 참고합니다.

 

요소 유형 및 상태 정보

현재 Compose에서 변경할 수 있는 요소 유형은 button, checkbox, tab, radioButton, image이며 상태 정보는 toggleable, selectable입니다. View System에서는 탭은 요소 유형으로 줄 수 없어 roleDescription을 사용하였는데 compose에서는 해당 유형 정보가 포함되었습니다. 상태 정보에 경우에는 포럼 팁에서도 소개한 것과 같이 stateDescription을 통해 문자열로 상태 값을 정의할 수도 있습니다.

참고:

  1. 제목 요소로 정의하는 heading은 요소 유형으로 분류되지 않습니다. 따라서 TalkBack 설정에서 요소 유형을 읽지 않도록 설정해도 제목이라고 읽어줍니다.
  2. 라디오 버튼, 탭과 같은 여러 개중 하나를 선택하는 composable을 구현할 때는 선택 정보를 selectable로, 체크박스, 토글 버튼과 같은 중복 선택이 가능한 composable은 toggleable을 사용합니다.

2부에서 계속 이어집니다.

댓글 0
댓글을 작성하려면 해주세요.