접근성 기능이 필요한 사용자를 지원할 수 있도록, Android 프레임워크에서는 사용자에게 앱의 콘텐츠를 표시하고 사용자를 대신하여 앱을 조작할 수 있는 접근성 서비스를 만들 수 있습니다.
Android는 다음을 포함하여 여러 시스템 접근성 서비스를 제공합니다.
- TalkBack: 시력이 약하거나 시각 장애가 있는 사용자를 지원합니다. 합성된 음성을 통해 콘텐츠를 알리고 앱에서 사용자 동작에 응답하여 작업을 실행합니다.
- 스위치 제어: 거동 장애가 있는 사용자를 지원합니다. 상호작용 요소를 강조표시하고 사용자가 버튼을 누르면 이에 응답하여 작업을 실행합니다. 1개 또는 2개의 버튼만 사용하여 기기를 제어할 수 있게 합니다.
접근성 기능이 필요한 사용자가 앱을 성공적으로 사용하도록 지원하려면 앱이 이 페이지에 설명된 권장사항을 따라야 합니다. 이 권장사항은 더욱 접근성 높은 앱 만들기에 설명된 가이드라인을 기반으로 합니다.
이어지는 섹션에서 설명하는 각 권장사항은 앱의 접근성을 더욱 향상합니다.
- 요소에 라벨 지정
- 사용자는 앱 내에 있는 상호작용이 가능한 중요한 각 UI 요소의 콘텐츠 및 목적을 이해할 수 있어야 합니다.
- 접근성 작업 추가
- 접근성 작업을 추가하면 접근성 서비스 사용자가 앱에서 중요한 사용자 플로우를 완료하도록 할 수 있습니다.
- 내장된 접근성 기능 사용하기
- Compose는 기본적으로 다양한 접근성 동작을 제공합니다. 미리 정의된 접근성 동작을 활용하여 추가 작업 없이 구성요소에 접근할 수 있도록 합니다. Compose는 기본 기능으로 지원되지 않는 더 구체적인 접근성 요구사항을 지원하는 방법도 제공합니다.
- 색상 이외의 큐 사용
- 사용자는 UI의 요소 카테고리를 명확하게 구분할 수 있어야 합니다. 카테고리를 구분하려면 패턴 및 위치를 색상과 함께 사용하여 카테고리의 차이를 표현합니다.
- 미디어 콘텐츠의 접근성 높이기
- 앱의 동영상 또는 오디오 콘텐츠를 사용하는 사용자가 시각 또는 청각적인 큐에 전적으로 의존할 필요가 없도록 앱의 동영상 또는 오디오 콘텐츠에 설명을 추가해 보세요.
요소에 라벨 지정
앱에서 상호작용이 가능한 각 UI 요소에 관한 설명이 포함된 유용한 라벨을 사용자에게 제공하는 것이 중요합니다. 각 라벨은 특정 요소의 의미와 목적을 나타내는 요소의 시맨틱을 설명해야 합니다. TalkBack과 같은 스크린 리더는 사용자에게 이러한 라벨을 알릴 수 있습니다.
대부분의 경우 Compose API와 Material에는 기본 접근성 지원이 마련되어 있습니다. 하지만 UI 요소의 시맨틱 속성을 수동으로 지정해야 하는 경우 semantics 수정자와 contentDescription 속성을 사용하세요. 시맨틱에 관한 자세한 내용은 시맨틱을 참고하세요.
다음 섹션에서는 몇 가지 다른 라벨 지정 기법을 설명합니다.
수정 가능한 요소
텍스트 필드와 같은 수정 가능한 요소에 라벨을 지정할 때는 이 텍스트 예를 스크린 리더에서 사용할 수 있게 하는 것 외에도 요소 자체에 올바른 입력의 예를 제공하는 텍스트를 표시하는 것이 좋습니다. 이러한 상황에서는 힌트 텍스트라고도 하는 자리표시자 텍스트를 사용할 수 있습니다.
다음 예에서 TextField에는 힌트 텍스트를 제공하는 placeholder 파라미터가 있습니다.
val usernameState = rememberTextFieldState() TextField( state = usernameState, lineLimits = TextFieldLineLimits.SingleLine, placeholder = { Text("Enter Username") } )
또한 텍스트 필드에는 사용자가 입력해야 하는 내용을 설명하는 상응하는 설명 라벨이 있는 경우가 많습니다.
다음 예에서 TextField에는 접근성 설명을 제공하는 label 매개변수가 있습니다.
TextField( state = rememberTextFieldState(initialText = "Hello"), label = { Text("Label") } )
텍스트 및 사용자 입력에 관한 자세한 내용은 텍스트 필드 구성을 참고하세요.
컬렉션의 요소
컬렉션의 요소에 라벨을 추가할 때 각 라벨은 고유해야 합니다. 라벨이 고유해야 라벨을 알릴 때 시스템의 접근성 서비스에서 화면에 표시되는 요소를 정확히 하나만 참조할 수 있습니다. 이러한 대응 관계를 통해 사용자가 UI를 순환했을 때 또는 이미 찾은 요소로 포커스를 이동했을 때 이를 알 수 있습니다.
예를 들어 LazyColumn 또는 LazyRow가 있는 경우 다음 스니펫과 같이 semantics 수정자를 사용하여 각 항목에 고유한 collectionItemInfo를 할당합니다.
MilkyWayList( modifier = Modifier .semantics { collectionInfo = CollectionInfo( rowCount = milkyWay.count(), columnCount = 1 ) } ) { milkyWay.forEachIndexed { index, text -> Text( text = text, modifier = Modifier.semantics { collectionItemInfo = CollectionItemInfo(index, 0, 0, 0) } ) } }
목록 및 그리드의 시맨틱 속성에 대한 자세한 내용은 목록 및 항목 정보를 참고하세요.
관련 콘텐츠 그룹
앱에서 노래의 세부정보나 메시지의 속성과 같이 자연스러운 그룹을 형성하는 여러 UI 요소를 표시하는 경우 상위 컨테이너 (예: Column, Row, Box) 내에 이러한 요소를 정렬합니다. 상위 컨테이너의 semantics 수정자를 사용하여 mergeDescendants을 true로 설정합니다.
이렇게 설정하면 접근성 서비스에서 내부 요소의 콘텐츠 설명을 하나의 알림에 차례로 표시할 수 있습니다. 관련 요소를 통합하면 보조 기술 사용자가 화면에 표시된 정보를 더 효율적으로 찾을 수 있습니다.
다음 스니펫에서 Row 컴포저블은 상위 컨테이너 역할을 합니다.
Row 내에는 블로그 게시물의 메타데이터(작성자의 아바타, 작성자의 이름, 예상 읽기 시간)를 보여주는 관련 요소가 있습니다.
mergeDescendants을 true로 설정하면 이러한 내부 요소가 그룹화되므로 접근성 서비스에서 이를 하나의 단위로 처리할 수 있습니다.
@Composable private fun PostMetadata(metadata: Metadata) { // Merge elements below for accessibility purposes Row(modifier = Modifier.semantics(mergeDescendants = true) {}) { Image( imageVector = Icons.Filled.AccountCircle, contentDescription = null // decorative ) Column { Text(metadata.author.name) Text("${metadata.date} • ${metadata.readTimeMinutes} min read") } } }
이전 예와 같이 관련 요소를 그룹화할 때는 상위 컨테이너만 상호작용 가능하게 만드세요. 내부 하위 요소에 clickable 또는 focusable 수정자를 추가하지 마세요. 대신 수정자를 상위 Row 또는 Column에 적용합니다.
접근성 서비스는 내부 요소의 설명을 하나의 표현으로 알려주기 때문에 각 설명을 최대한 짧게 유지하면서 요소의 의미를 전달하는 것이 중요합니다.
참고: 일반적으로 그룹의 콘텐츠 설명을 만들 때는 하위 요소의 텍스트를 집계하지 마세요. 이렇게 하면 그룹의 설명이 깨지기 쉬워지고 하위 요소의 텍스트가 변경되면 그룹의 설명이 더 이상 표시되는 텍스트와 일치하지 않을 수 있습니다.
목록 또는 그리드 컨텍스트에서 화면 리더는 목록 또는 그리드 요소의 하위 텍스트 노드의 텍스트를 통합할 수 있습니다. 이 공지사항은 수정하지 않는 것이 좋습니다.
시맨틱 병합에 관한 자세한 내용은 병합 및 지우기를 참고하세요.
텍스트 내 제목
일부 앱에서는 제목을 사용해 화면에 표시되는 텍스트 그룹을 요약합니다. 특정 요소가 제목을 나타내는 경우 semantics 수정자에서 heading 속성을 설정하여 접근성 서비스의 용도를 나타낼 수 있습니다.
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
접근성 서비스 사용자는 단락 또는 단어가 아닌 제목 사이를 탐색하도록 선택할 수 있습니다. 이러한 유연성을 통해 텍스트 탐색 환경이 개선됩니다.
heading 시맨틱 속성에 관한 자세한 내용은 제목을 참고하세요.
접근성 창(pane) 제목
Android 9(API 수준 28) 이상에서는 화면의 창(pane)에 접근성을 고려한 제목을 제공할 수 있습니다. 접근성을 위해 창(pane)은 시각적으로 구분되는 창(window)의 부분입니다.
접근성 서비스에서 창(window)과 유사한 창(pane) 동작을 이해할 수 있도록 앱의 창(pane)에 내용을 잘 나타내는 제목을 제공해야 합니다. 그러면 접근성 서비스에서 창(pane)의 모양이나 콘텐츠가 변경될 때 더 세분화된 정보를 사용자에게 제공할 수 있습니다.
ShareSheet( message = "Choose how to share this photo", modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .semantics { paneTitle = "New bottom sheet" } )
paneTitle 시맨틱 속성에 대한 자세한 내용은 창과 유사한 구성요소를 참고하세요.
장식 요소
UI의 요소가 시각적 간격용 또는 시각적 표시용으로만 존재하는 경우 접근성 서비스에서 무시할 수 있음을 나타내도록 요소에 적절한 속성을 설정합니다.
Image 또는 Icon 컴포저블의 경우 contentDescription = null를 설정합니다. 컨텍스트나 기능을 제공하지 않는 순전히 장식적인 요소의 경우 hideFromAccessibility를 사용할 수 있습니다. 이 시맨틱 속성은 접근성 서비스에 항목을 무시하도록 지시합니다.
대화형 컴포저블에 장식용 비대화형 하위 요소가 포함된 경우 clearAndSetSemantics를 사용하여 접근성 서비스가 이를 순회하지 않도록 합니다. clearAndSetSemantics는 요소와 그 하위 요소의 기본 시맨틱을 완전히 삭제합니다. 이렇게 하면 새 통합 접근성 요소를 정의할 수 있습니다. 일반적으로 복잡한 맞춤 구성요소에 이 접근 방식을 사용합니다.
다음 예에서 Icon와 Text는 맞춤 전환 버튼 내의 장식용 하위 요소입니다. 접근성 서비스가 이러한 하위 요소를 개별적으로 탐색하지 못하도록 하려면 부모 Row에서 clearAndSetSemantics를 사용하여 시맨틱을 지우면 됩니다. 이렇게 하면 접근성 서비스가 전체 Row를 탐색 가능한 전환 버튼으로 처리합니다.
// Developer might intend this to be a toggleable. // Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied, // a custom description is set, and a Role is applied. @Composable fun FavoriteToggle() { val checked = remember { mutableStateOf(true) } Row( modifier = Modifier .toggleable( value = checked.value, onValueChange = { checked.value = it } ) .clearAndSetSemantics { stateDescription = if (checked.value) "Favorited" else "Not favorited" toggleableState = ToggleableState(checked.value) role = Role.Switch }, ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null // not needed here ) Text("Favorite?") } }
시맨틱 삭제에 대한 자세한 내용은 시맨틱 삭제 및 설정을 참고하세요.
접근성 작업 추가
접근성 서비스 사용자가 앱에서 모든 사용자 플로우를 완료할 수 있도록 하는 것이 중요합니다.
맞춤 컴포저블의 상호작용으로 인해 명확하지 않은 방식으로 앱의 상태가 변경되는 경우 Modifier.clickable 또는 Modifier.combinedClickable에서 onClickLabel 또는 onLongClickLabel과 같은 매개변수를 사용하여 표준 탭 작업에 설명이 포함된 라벨을 제공하세요.
표준 탭에 매핑할 수 없는 복잡한 상호작용의 경우 customActions를 사용합니다.
예를 들어 앱에서 사용자가 항목을 다른 위치로 드래그하거나 목록에서 항목을 스와이프할 수 있다면 접근성 서비스에 작업을 노출하여 이러한 사용자 플로우를 완료할 또 다른 방법을 제공할 수 있습니다. 이렇게 하면 TalkBack, 음성 액세스 또는 스위치 제어 사용자가 동작을 통해서만 사용할 수 있는 작업을 실행할 수 있습니다.
Compose에서는 CustomAccessibilityAction를 사용하여 semantics 수정자의 customActions 속성을 통해 맞춤 접근성 작업을 정의할 수 있습니다.
예를 들어 앱에서 사용자가 항목을 스와이프하여 닫을 수 있다면 다음과 같은 맞춤 접근성 작업을 통해 이 기능을 노출할 수 있습니다.
SwipeToDismissBox( modifier = Modifier.semantics { // Represents the swipe to dismiss for accessibility customActions = listOf( CustomAccessibilityAction( label = "Remove article from list", action = { removeArticle() true } ) ) }, state = rememberSwipeToDismissBoxState(), backgroundContent = {} ) { ArticleListItem() }
맞춤 접근성 동작이 구현되면 사용자는 동작 메뉴를 통해 동작에 액세스할 수 있습니다.
맞춤 작업에 대한 자세한 내용은 맞춤 작업을 참고하세요.
작업을 쉽게 이해할 수 있도록 만들기
UI 요소가 길게 누르기와 같은 작업을 지원하는 경우 TalkBack과 같은 접근성 서비스는 이를 '두 번 탭한 후 길게 눌러 오래 누르기'로 알립니다.
이 일반적인 공지사항은 터치 앤 홀드 작업이 무엇을 하는지 사용자에게 아무런 맥락을 제공하지 않습니다.
이 공지사항을 더 유용하게 만들려면 작업에 대한 의미 있는 설명을 지정하세요.
Compose에서 clickable 및 combinedClickable과 같은 표준 상호작용 수정자에는 다음 예와 같이 작업에 대한 설명을 제공하는 데 사용할 수 있는 기본 제공 매개변수 (onClickLabel 및 onLongClickLabel)가 있습니다.
var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) } val haptics = LocalHapticFeedback.current LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier .combinedClickable( onClick = { activePhotoId = photo.id }, onLongClick = { haptics.performHapticFeedback(HapticFeedbackType.LongPress) contextMenuPhotoId = photo.id }, onLongClickLabel = stringResource(R.string.open_context_menu) ) ) } } if (contextMenuPhotoId != null) { PhotoActionsSheet( photo = photos.first { it.id == contextMenuPhotoId }, onDismissSheet = { contextMenuPhotoId = null } ) }
이렇게 하면 TalkBack에서 '컨텍스트 메뉴 열기'라고 알려주어 사용자가 작업의 목적을 이해할 수 있습니다.
semantics 수정자에서 라벨을 직접 지정할 수도 있습니다.
탭 및 클릭에 응답하는 방법에 관한 자세한 내용은 탭 및 누르기 및 대화형 요소를 참고하세요.
내장 접근성 기능 사용하기
앱의 UI를 디자인할 때는 내장된 접근성 기능을 활용하여 이미 존재하는 기능을 다시 구현하지 마세요. Material, Compose UI, Foundation API는 기본적으로 접근성 관련 관행을 많이 구현하고 제공합니다.
Jetpack Compose에서는 Button, Switch, Checkbox과 같은 내장 컴포저블을 사용하여 접근성 UI를 만듭니다. 이러한 구성요소는 앱의 접근성을 높이는 데 사용할 수 있는 role, stateDescription와 같은 semantics 수정자와 함께 사전 패키지로 제공됩니다.
맞춤 구성요소에 시맨틱 적용
맞춤 구성요소를 만들 때는 이 구성요소가 역할을 수행하는 데 필요한 접근성 지원의 종류를 염두에 두세요. clickable, toggleable, selectable과 같이 이미 사용 중인 표준 Compose API는 시맨틱 트리(semantics tree)를 자동으로 채우므로 충분한 경우가 많습니다.
하지만 일부 구성요소에는 표준 수정자가 제공하는 것보다 더 구체적인 정보가 필요합니다. 이 경우 특수 수정자 (예: triStateToggleable)를 찾거나, 없는 경우 하위 수준 Modifier.semantics를 사용하여 의미를 명시적으로 제공합니다.
예를 들어 세 가지 상태 (사용, 사용 안함, 미확정)가 있는 스위치인 TriStateSwitch을 생각해 보겠습니다.
표준 toggleable 수정자는 두 가지 상태를 가정하지만 triStateToggleable 수정자는 세 번째 상태의 복잡성을 처리합니다. 접근성 Role (Switch) 및 State를 자동으로 설정합니다. 이렇게 하면 접근성 서비스가 정확한 정보를 수신하고 시맨틱스를 수동으로 정의할 필요가 없습니다.
다음 코드 스니펫은 이 접근 방식을 사용하는 TriStateSwitch을 보여줍니다.
@Composable fun TriStateSwitch( state: ToggleableState, onClick: () -> Unit, modifier: Modifier = Modifier ) { // A real implementation would include custom drawing for the switch. // This example uses a Box to demonstrate the semantics. Box( modifier = modifier .size(width = 64.dp, height = 40.dp) // triStateToggleable handles the semantics (Role and State) // automatically, so explicit Modifier.semantics is not needed here. .triStateToggleable( state = state, onClick = onClick, role = Role.Switch ) // Add visual feedback based on the state .background( when (state) { ToggleableState.On -> Color.Green ToggleableState.Off -> Color.Gray ToggleableState.Indeterminate -> Color.Yellow } ) ) } // Usage within another composable: var state by remember { mutableStateOf(ToggleableState.Off) } TriStateSwitch( state = state, onClick = { state = when (state) { ToggleableState.Off -> ToggleableState.Indeterminate ToggleableState.Indeterminate -> ToggleableState.On ToggleableState.On -> ToggleableState.Off } } )
맞춤 구성요소를 빌드할 때는 접근성 목적으로 모든 관련 시맨틱 속성을 제공해야 합니다. 예를 들어 구성요소가 스위치나 버튼과 같은 표준 컨트롤을 모방하는 경우 이러한 속성에는 구성요소의 역할 (예: Role.Switch 또는 Role.Button), stateDescription(예: '사용', '사용 안함', '선택됨', '선택되지 않음'), 관련 작업 라벨이 포함됩니다. 자세한 내용은 맞춤 구성요소를 참고하세요.
색상 이외의 큐 사용
색약이 있는 사용자를 지원하려면 색상 이외의 큐를 사용해 앱 화면 내에 있는 UI 요소를 구별하세요. 이러한 기법에는 다양한 모양 또는 크기를 사용하거나 텍스트 또는 시각적 패턴을 제공하거나 요소의 차이점을 표시하는 오디오 기반 또는 터치 기반(햅틱) 반응을 추가하는 것이 포함될 수 있습니다.
그림 1에서는 활동의 두 가지 버전을 보여줍니다. 한 버전에서는 워크플로에서 실행 가능한 두 작업을 구분하는 데 색상만 사용합니다. 다른 버전에서는 두 옵션의 차이를 강조표시하기 위해 색상 외에 도형 및 텍스트를 포함하는 권장사항을 사용합니다.
미디어 콘텐츠의 접근성 높이기
동영상 클립 또는 오디오 녹음과 같은 미디어 콘텐츠가 포함된 앱을 개발한다면 다양한 유형의 접근성이 필요한 사용자가 이 자료를 이해할 수 있도록 지원해 보세요. 특히 다음을 시도해 보세요.
- 사용자가 미디어를 일시중지 또는 중지하고 볼륨을 변경하며 자막을 전환할 수 있는 컨트롤을 포함합니다.
- 동영상에서 워크플로를 완료하는 데 필수적인 정보를 표시하는 경우 동일한 콘텐츠를 스크립트와 같은 대체 형식으로 제공합니다.
추가 리소스
앱의 접근성을 높이는 방법을 자세히 알아보려면 다음 추가 리소스를 참고하세요.