Material, Compose UI, Foundation API는 기본적으로 많은 접근성 권장사항을 구현하고 제공합니다. 특정 역할과 기능을 따르는 기본 제공 시맨틱스가 포함되어 있습니다. 즉, 대부분의 접근성 지원은 추가 작업이 거의 또는 전혀 없이 제공됩니다.
적절한 목적에 적절한 API를 사용한다는 것은 구성요소에 일반적으로 표준 사용 사례를 다루는 사전 정의된 접근성 동작이 포함되어 있음을 의미합니다. 하지만 이러한 기본값이 접근성 요구사항에 맞는지 항상 다시 확인하세요. 그렇지 않은 경우 Compose는 더 구체적인 요구사항을 처리하는 방법을 제공합니다.
Compose API의 기본 접근성 시맨틱과 패턴을 이해하면 접근성을 고려하여 사용할 수 있습니다. 또한 더 많은 맞춤 구성요소에서 접근성을 지원하는 데 도움이 됩니다.
터치 영역 최소 크기
사용자가 클릭, 터치 등의 방법으로 상호작용할 수 있는 화면상의 요소는 안정적으로 상호작용할 수 있도록 충분히 커야 합니다. 이러한 요소의 크기를 조절할 때 Material Design 접근성 가이드라인을 정확히 준수하도록 최소 크기를 48dp로 설정해야 합니다.
Checkbox, RadioButton, Switch, Slider, Surface와 같은 Material 구성요소는 이 최소 크기를 내부적으로 설정합니다. 단, 구성요소가 사용자 작업을 수신할 수 있는 경우에 한합니다. 예를 들어 Checkbox의 onCheckedChange 매개변수가 null이 아닌 값으로 설정된 경우 체크박스에는 너비와 높이가 최소 48dp인 패딩이 포함됩니다.
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }

onCheckedChange 매개변수가 null로 설정된 경우 구성요소와 직접 상호작용할 수 없으므로 패딩이 포함되지 않습니다.
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
Switch, RadioButton, Checkbox와 같은 선택 설정을 구현할 경우 일반적으로 컴포저블에 관한 클릭 콜백을 null로 설정하고, toggleable 또는 selectable 수정자를 상위 컴포저블에 추가하여 클릭 가능한 동작을 상위 컨테이너로 올립니다.
@Composable private fun CheckableRow() { MaterialTheme { var checked by remember { mutableStateOf(false) } Row( Modifier .toggleable( value = checked, role = Role.Checkbox, onValueChange = { checked = !checked } ) .padding(16.dp) .fillMaxWidth() ) { Text("Option", Modifier.weight(1f)) Checkbox(checked = checked, onCheckedChange = null) } } }

클릭 가능한 컴포저블의 크기가 터치 영역 최소 크기보다 작은 경우 Compose는 여전히 터치 영역 크기를 늘립니다. 이 작업은 컴포저블의 경계 밖으로 터치 영역 크기를 확장하여 이뤄집니다.
다음 예에는 클릭 가능한 아주 작은 Box가 포함되어 있습니다. 터치 영역 부분은 Box의 경계 밖으로 자동으로 확장되므로 Box 옆을 탭하면 여전히 클릭 이벤트가 트리거됩니다.
@Composable private fun SmallBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .size(1.dp) ) } }

서로 다른 컴포저블의 터치 영역이 서로 겹치지 않도록 항상 컴포저블에 충분히 큰 최소 크기를 사용해야 합니다. 이 예에서는 sizeIn 수정자를 사용하여 내부 상자의 최소 크기를 설정하는 것을 의미합니다.
@Composable private fun LargeBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .sizeIn(minWidth = 48.dp, minHeight = 48.dp) ) } }

요소가 더 작은 표시 크기를 유지해야 하는 경우 Material 3의 minimumInteractiveComponentSize 수정자를 대신 사용하세요. 각 측정기준에서 최소 48dp의 레이아웃 공간을 예약하고 해당 공간의 중앙에 요소를 배치하므로 요소는 그려진 크기를 유지하고 주변 요소는 공간을 만들기 위해 이동합니다. 수정자는 터치 영역 자체를 확장하지 않습니다. 앞에서 설명한 확장을 위해 레이아웃에 공간을 남겨둡니다.
Checkbox과 같은 Material 구성요소는 이 수정자를 내부적으로 사용합니다. 크기 수정자 앞에 배치합니다. 크기 수정자가 앞에 있으면 예약할 수 있는 공간이 제한되기 때문입니다.
@Composable private fun MinimumInteractiveBox() { var clicked by remember { mutableStateOf(false) } Row(verticalAlignment = Alignment.CenterVertically) { Box( Modifier .clickable { clicked = !clicked } .minimumInteractiveComponentSize() .size(24.dp) .background(if (clicked) Color.DarkGray else Color.Black) ) Text("Option") } }
그래픽 요소
Image 또는 Icon 컴포저블을 정의하는 경우 Android 프레임워크에서 앱에 표시되는 내용을 자동으로 파악할 수 없습니다. 그래픽 요소의 텍스트 설명을 전달해야 합니다.
사용자가 현재 페이지를 친구와 공유할 수 있는 화면이 있다고 가정해 보겠습니다. 이 화면에는 클릭 가능한 공유 아이콘이 포함되어 있습니다.

Android 프레임워크는 아이콘만 가지고는 시각 장애를 가진 사용자에게 아이콘에 대해 설명할 수 없습니다. Android 프레임워크에는 아이콘의 추가 텍스트 설명이 필요합니다.
contentDescription 매개변수는 그래픽 요소를 설명합니다. 사용자에게 표시되므로 현지화된 문자열을 사용합니다.
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
일부 그래픽 요소는 완전히 장식용이므로 사용자에게 전달하지 않아도 됩니다. contentDescription 매개변수를 null로 설정하면 Android 프레임워크에 이 요소에 연결된 작업 또는 상태가 없음을 나타냅니다.
@Composable private fun PostImage(post: Post, modifier: Modifier = Modifier) { val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1) Image( painter = image, // Specify that this image has no semantic meaning contentDescription = null, modifier = modifier .size(40.dp, 40.dp) .clip(MaterialTheme.shapes.small) ) }
contentDescription는 주로 이미지와 같은 그래픽 요소에 사용됩니다. Button 또는 Text와 같은 Material 구성요소와 clickable 또는 toggleable와 같은 실행 가능한 동작에는 고유한 동작을 설명하는 다른 사전 정의된 시맨틱스가 함께 제공되며 다른 Compose API를 통해 변경할 수 있습니다.
상호작용 요소
Material 및 Foundation Compose API는 사용자가 clickable 및 toggleable 수정자 API를 통해 상호작용할 수 있는 UI 요소를 만듭니다. 상호작용 가능한 구성요소는 여러 요소로 구성될 수 있으므로 clickable 및 toggleable는 기본적으로 하위 요소의 시맨틱스를 병합하여 구성요소가 하나의 논리적 항목으로 처리됩니다.
예를 들어 Material Button는 하위 아이콘과 일부 텍스트로 구성될 수 있습니다. Material Button은 하위 요소를 개별적으로 처리하는 대신 기본적으로 하위 요소 시맨틱을 병합하므로 접근성 서비스에서 이에 따라 그룹화할 수 있습니다.

마찬가지로 clickable 수정자를 사용하면 컴포저블이 하위 요소의 시맨틱스를 단일 항목으로 병합하여 상응하는 작업 표현과 함께 접근성 서비스로 전송합니다.
Row( // Uses `mergeDescendants = true` under the hood modifier = Modifier.clickable { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open", ) Text("Accessibility in Compose") }
상위 클릭 가능한 요소에 특정 onClickLabel를 설정하여 접근성 서비스에 추가 정보를 제공하고 작업을 더 세련되게 표현할 수도 있습니다.
Row( modifier = Modifier .clickable(onClickLabel = "Open this article") { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open" ) Text("Accessibility in Compose") }
TalkBack을 예로 들면 이 clickable 수정자와 클릭 라벨을 사용하면 TalkBack에서 더 일반적인 기본 피드백인 '두 번 탭하여 활성화' 대신 '두 번 탭하여 이 도움말 열기'라는 작업 힌트를 제공할 수 있습니다.
이 피드백은 작업 유형에 따라 달라집니다. 길게 탭하면 TalkBack 힌트인 'Double tap and hold to'가 제공되고 그 뒤에 라벨이 표시됩니다.
Row( modifier = Modifier .combinedClickable( onLongClickLabel = "Bookmark this article", onLongClick = { addToBookmarks() }, onClickLabel = "Open this article", onClick = { openArticle() }, ) ) {}
경우에 따라 clickable 수정자에 직접 액세스할 수 없지만 (예: 하위 중첩 레이어 어딘가에 설정된 경우) 공지 라벨을 기본값에서 변경하고 싶을 수 있습니다. 이렇게 하려면 semantics 수정자를 사용하여 공지사항을 수정하는 것과 clickable 설정을 분리하고 클릭 라벨을 설정하여 작업 표현을 수정합니다.
@Composable private fun ArticleList(openArticle: () -> Unit) { NestedArticleListItem( // Clickable is set separately, in a nested layer: onClickAction = openArticle, // Semantics are set here: modifier = Modifier.semantics { onClick( label = "Open this article", action = { // Not needed here: openArticle() true } ) } ) }
클릭 액션을 두 번 전달할 필요는 없습니다. clickable 또는 Button와 같은 기존 Compose API가 이를 처리합니다. 병합 로직은 존재하는 정보에 대해 가장 바깥쪽 수정자 라벨과 작업이 수행되는지 확인합니다. 이전 예에서 NestedArticleListItem은 openArticle() 클릭 작업을 clickable 시맨틱스에 자동으로 전달합니다. 두 번째 시맨틱 수정자 작업에서 클릭 작업을 null로 남겨둘 수 있습니다. 하지만 클릭 라벨은 첫 번째 시맨틱 수정자에 없었기 때문에 두 번째 시맨틱 수정자 onClick(label = "Open this document")에서 가져옵니다.
하위 시맨틱스가 상위 시맨틱스에 병합될 것으로 예상하지만 병합되지 않는 시나리오가 발생할 수 있습니다. 자세한 내용은 병합 및 삭제를 참조하세요.
커스텀 구성요소
맞춤 구성요소를 빌드할 때는 Material 라이브러리나 다른 Compose 라이브러리에서 유사한 구성요소의 구현을 검토하세요. 그런 다음 적절하게 접근성 동작을 모방하거나 수정합니다. 예를 들어 Material Checkbox를 자체 구현으로 바꾸는 경우 기존 Checkbox 구현을 보면 구성요소의 접근성 속성을 처리하는 triStateToggleable 수정자를 추가해야 한다는 것을 알 수 있습니다. 또한 기초 수정자를 많이 활용하세요. 기초 수정자에는 기본 제공 접근성 고려사항과 이 섹션에서 다루는 기존 Compose 관행이 포함되어 있습니다.
시맨틱스 지우기 및 설정 섹션에서 맞춤 전환 구성요소의 예를 확인할 수 있으며 API 가이드라인에서 맞춤 구성요소의 접근성을 지원하는 방법에 관한 자세한 정보를 확인할 수 있습니다.
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- Compose의 접근성
- Compose 레이아웃 테스트