Accessibility API Updates
동기
기술이 발전하고 모바일 앱이 일상생활에서 점점 더 중요해짐에 따라, 접근성 있는 애플리케이션을 만드는 필요성도 그만큼 커졌다.
React Native의 제한된 접근성 API는 개발자들에게 항상 큰 고민거리였다. 그래서 우리는 더 포괄적인 모바일 애플리케이션을 쉽게 만들 수 있도록 접근성 API를 몇 가지 업데이트했다.
기존 API의 문제점
문제 1: 완전히 다르지만 유사한 두 가지 props - accessibilityComponentType (Android)와 accessibilityTraits (iOS)
accessibilityComponentType
와 accessibilityTraits
는 각각 Android의 TalkBack과 iOS의 VoiceOver에게 사용자가 상호작용하는 UI 엘리먼트의 종류를 알려주는 프로퍼티이다. 이 두 프로퍼티의 가장 큰 문제점은 다음과 같다:
- 두 프로퍼티는 사용 방법이 다르지만 동일한 목적을 가지고 있다. 기존 API에서는 두 프로퍼티가 플랫폼별로 분리되어 있어 불편할 뿐만 아니라 많은 개발자에게 혼란을 주었다. iOS의
accessibilityTraits
는 17가지 값을 지원하는 반면, Android의accessibilityComponentType
는 단 4가지 값만 허용한다. 또한, 두 프로퍼티의 값은 대부분 겹치지 않는다. 심지어 입력 타입도 다르다.accessibilityTraits
는 트레이트 배열이나 단일 트레이트를 전달할 수 있지만,accessibilityComponentType
는 단일 값만 허용한다. - Android의 기능이 매우 제한적이다. 기존 프로퍼티를 사용할 때 TalkBack이 인식할 수 있는 UI 엘리먼트는 "button", "radiobutton_checked", "radiobutton_unchecked" 뿐이었다.
문제 2: 접근성 힌트 부재
접근성 힌트는 TalkBack이나 VoiceOver를 사용하는 사용자가 접근성 엘리먼트에 액션을 수행할 때 어떤 일이 일어날지 이해하는 데 도움을 준다. 이러한 힌트는 설정 패널에서 켜거나 끌 수 있다. 이전에는 React Native의 API가 접근성 힌트를 전혀 지원하지 않았다.
문제 3: 반전 색상 무시하기
시각 장애를 가진 일부 사용자는 화면 대비를 높이기 위해 휴대폰에서 색상을 반전시켜 사용한다. 애플은 iOS에서 특정 뷰를 무시할 수 있는 API를 제공한다. 이를 통해 사용자가 반전 색상 설정을 켜도 이미지와 비디오가 왜곡되지 않는다. 그러나 이 API는 현재 React Native에서 지원되지 않는다.
새로운 API 설계
첫 번째 해결책: accessibilityComponentType
(안드로이드)와 accessibilityTraits
(iOS) 통합
accessibilityComponentType
와 accessibilityTraits
사이의 혼란을 해결하기 위해, 두 속성을 단일 속성으로 통합하기로 결정했다. 이는 두 속성이 기술적으로 동일한 기능을 목표로 했기 때문에 합리적이었다. 이를 통해 개발자는 접근성 기능을 구현할 때 플랫폼별 세부 사항을 고민할 필요가 없어졌다.
배경
iOS에서는 UIAccessibilityTraits
를 모든 NSObject에 설정할 수 있는 속성이다. 자바스크립트 속성을 통해 전달된 17개의 트레이트는 Objective-C의 UIAccessibilityTraits
요소로 매핑된다. 각 트레이트는 long int로 표현되며, 설정된 모든 트레이트는 OR 연산으로 결합된다.
반면 안드로이드에서 AccessibilityComponentType
는 React Native에서 만든 개념으로, 안드로이드의 속성과 직접적으로 매핑되지 않는다. 접근성은 접근성 델리게이트를 통해 처리된다. 각 뷰는 기본 접근성 델리게이트를 가지고 있다. 접근성 액션을 커스텀하려면 새로운 접근성 델리게이트를 생성하고, 커스텀하려는 특정 메서드를 오버라이드한 후, 해당 뷰의 접근성 델리게이트를 새 델리게이트로 설정해야 한다. 개발자가 AccessibilityComponentType
를 설정하면, 네이티브 코드는 전달된 컴포넌트를 기반으로 새로운 델리게이트를 생성하고, 뷰에 해당 접근성 델리게이트를 설정한다.
변경 사항
새로운 속성을 위해 두 속성의 상위 집합을 만들기로 했다. 기존 속성인 accessibilityTraits
를 주로 모델로 삼기로 결정했는데, 이는 accessibilityTraits
가 훨씬 더 많은 값을 가지고 있기 때문이다. 안드로이드에서 이 트레이트의 기능은 접근성 델리게이트를 수정함으로써 폴리필로 구현된다.
accessibilityTraits
는 iOS에서 설정할 수 있는 17개의 UIAccessibilityTraits
값을 가지고 있다. 하지만 이 중 일부는 새로운 속성에 포함하지 않았다. 이는 일부 트레이트를 설정했을 때의 효과가 잘 알려져 있지 않고, 많은 값이 실제로 거의 사용되지 않기 때문이다.
UIAccessibilityTraits
값은 일반적으로 두 가지 목적 중 하나를 가진다. UI 엘리먼트의 역할을 설명하거나, UI 엘리먼트의 상태를 설명한다. 우리가 관찰한 이전 속성의 사용 사례 대부분은 일반적으로 하나의 역할을 나타내는 값을 사용하고, 이를 "state selected", "state disabled" 또는 둘 다와 결합했다. 따라서 두 개의 새로운 접근성 속성을 만들기로 결정했다: accessibilityRole
과 accessibilityState
.
accessibilityRole
새로운 속성인 accessibilityRole
은 Talkback이나 Voiceover에 UI 엘리먼트의 역할을 알려준다. 이 속성은 다음 값 중 하나를 가질 수 있다:
none
button
link
search
image
keyboardkey
text
adjustable
header
summary
imagebutton
이 속성은 하나의 값만 전달할 수 있다. 일반적으로 UI 엘리먼트는 논리적으로 이 중 하나 이상의 역할을 가지지 않기 때문이다. 예외는 이미지와 버튼이므로, 두 가지를 결합한 imagebutton
역할을 추가했다.
accessibilityStates
새로운 속성인 accessibilityStates
는 Talkback이나 Voiceover에 UI 엘리먼트의 상태를 알려준다. 이 속성은 다음 값 중 하나 또는 둘 다를 포함하는 배열을 가진다:
selected
disabled
두 번째 해결책: 접근성 힌트 추가
이를 위해 accessibilityHint
라는 새로운 속성을 추가했다. 이 속성을 설정하면 Talkback이나 Voiceover가 사용자에게 힌트를 읽어준다.
accessibilityHint
이 속성은 String 형태로 읽을 접근성 힌트를 받는다.
iOS에서는 이 속성을 설정하면 뷰의 해당 네이티브 속성인 AccessibilityHint가 설정된다. 이후 iPhone에서 접근성 힌트가 켜져 있으면 Voiceover가 이 힌트를 읽는다.
Android에서는 이 속성을 설정하면 힌트 값이 접근성 라벨 끝에 추가된다. 이 구현의 장점은 iOS의 힌트 동작을 모방한다는 점이지만, 단점은 iOS처럼 설정에서 이 힌트를 끌 수 없다는 것이다.
Android에서 이렇게 결정한 이유는 일반적으로 접근성 힌트가 특정 동작(예: 클릭)에 해당하기 때문이며, 플랫폼 간 동작을 일관되게 유지하고 싶었다.
문제 3의 해결책
accessibilityIgnoresInvertColors
애플의 AccessibilityIgnoresInvertColors API를 자바스크립트로 노출했다. 이제 색상 반전을 원하지 않는 뷰(예: 이미지)에서 이 속성을 true로 설정하면 색상이 반전되지 않는다.
새로운 사용법
이 새로운 속성들은 React Native 0.57 버전부터 사용할 수 있다.
업그레이드 방법
현재 accessibilityComponentType
와 accessibilityTraits
를 사용하고 있다면, 새로운 속성으로 업그레이드하는 방법을 단계별로 안내한다.
1. jscodeshift 사용하기
가장 간단한 사용 사례는 jscodeshift 스크립트를 실행하여 해결할 수 있다.
이 스크립트는 다음 인스턴스를 대체한다:
accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}
다음과 같이 변경한다:
accessibilityRole= “trait”
또한 이 스크립트는 AccessibilityComponentType
인스턴스를 제거한다. (AccessibilityComponentType
을 설정한 모든 곳에서 AccessibilityTraits
도 함께 설정했다고 가정한다.)
2. 수동 코드 변환 사용하기
AccessibilityTraits
를 사용했지만 AccessibilityRole
에 해당하는 값이 없는 경우, 그리고 AccessibilityTraits
에 여러 특성이 전달된 경우에는 수동으로 코드를 변환해야 한다.
일반적으로,
accessibilityTraits= {["button", "selected"]}
위 코드를 아래와 같이 수동으로 교체한다.
accessibilityRole="button"
accessibilityStates={["selected"]}
이러한 속성들은 이미 Facebook 코드베이스에서 사용되고 있다. Facebook의 코드 변환은 놀랍도록 간단했다. jscodeshift 스크립트가 약 절반의 인스턴스를 수정했고, 나머지 절반은 수동으로 수정했다. 전체 과정은 몇 시간도 걸리지 않았다.
업데이트된 API가 유용하게 쓰이길 바란다! 그리고 계속해서 앱을 접근 가능하게 만들길 바란다! #포용성