아티클

[모바일 접근성] #대체 텍스트와 #보조기술과의 호환성 알아보기

2022-12-07 09:59:53

안녕하세요. 네이버 접근성팀 서미연입니다.
모바일 접근성 대체 텍스트와 보조기술과의 호환성에 대해 알아봅니다.

시각장애인 사용자는 스크린리더 프로그램을 사용하여 콘텐츠 정보를 인식하고 사용합니다. 다음 그림에 보이는 바와 같이 스크린리더는 각각의 콘텐츠가 갖고 있는 정보를 음성으로 알려줍니다. 콘텐츠의 용도를 알 수 있는 텍스트 정보(●) 예: 블루라이트 필터, 콘텐츠가 어떤 컨트롤인지 버튼인지, 토글 버튼인지 등 유형 정보(▲) 예: 스위치, 콘텐츠 유형에 따른 상태 정보(◼︎) 예:사용 안 함, 콘텐츠를 사용하기에 필요한 힌트 정보(★) 예:전환하려면 두 번 탭 하세요. 를 음성으로 알려주는 것을 볼 수 있습니다.

iOS와 Android 음성정보 예시 텍스트정보(●),유형정보(▲), 상태정보(◼︎), 힌트정보(★)

대체 텍스트


텍스트가 아닌 요소에 대체 텍스트는 필수~!

관련 지침 : 텍스트가 아닌 콘텐츠는 대체 가능한 텍스트와 함께 제공되어야 한다.

디자인을 거쳐 텍스트가 아닌 이미지로 개발되어 제공되는 경우가 많은데요. 특히나 모바일의 경우 화면이 협소하기 때문에 메타포 이미지를 통해 콘텐츠를 제공하는 경우가 많습니다. 이런 텍스트가 아닌 콘텐츠는 반드시 어떤 용도의 콘텐츠인지 텍스트 정보(●)를 추가해 주어야 접근성 사용자가 콘텐츠를 이해할 수 있습니다. iOS는 accessibilityLabel로 Android는 contentDescription으로 대체 텍스트 정보를 제공해주세요.

iOS

  • 방법1. Xcode의 Accessibility 패널에서 Label 제공
    Xcode의 Accessibility 패널
    ① Accessibility 에서 Enabled 을 선택해 접근성 기능을 활성화한 상태에서
    ② Label 에 콘텐츠의 의미를 명확하게 전달할 수 있는 대체 텍스트를 작성합니다.
     

  • 방법2. 코드로 Label 제공

    var.isAccessibilityElement = true   // ① 접근성 요소 활성화
    var.accessibilityLabel = "대체 텍스트" // ② 대체 텍스트 정보
    


 

Android 

  • 방법1. Android Studio Properties 창에서 contentDescription 제공
    Android Studio Properties 창

  • 방법2. 코드로 contentDescription제공

    android:contentDescription = "대체 텍스트" // UI 레이아웃 XML에서 제공

대체 텍스트 관심 갖아야 할 정보들~! 

숫자 정보의 의미를 알 수 있게 대체 텍스트를 제공해 주세요. 

텍스트 정보가 있는 요소중에 대체 텍스를 제공해야 하는 경우가 있습니다. 콘텐츠에 텍스트 정보가 이미 있는데 왜 대체 텍스트를 추가 제공해야 하는지 궁금하실텐데요. 만약 음성으로 "1"이라고 알려주었다면 이 숫자는 무엇을 의미할까요? 1위(순위)일 수도 있고 1점(점수)일 수도 있고 1일(날짜)일 수도 있습니다. 주변 시각 정보를 통해 비장애인은 숫자 무엇을 의미하는지 판단할 수 있습니다. 하지만 스크린리더 사용자는 읽어주는 정보로만 판단해야 하기 때문에 숫자 정보가 무엇을 의미하는지 대체 텍스트 정보를 제공해 주어야 합니다.

  • 단위 정보를 넣어주세요.
    수정전 스크린리더 음성 정보 예) 1
    수정후 스크린리더 음성 정보 예) 1, 1, 1

  • 숫자 용도 정보를 넣어주세요.
    수정전 스크린리더 음성 정보 예) 1점
    수정후 스크린리더 음성 정보 예) 별점 1점 , 평점 1점

  • 시간 또는 날짜의 경우 시분초, 년월일 단위 정보를 넣어주세요.
    수정전 스크린리더 음성 정보 예) 20.01.01
    수정후 스크린리더 음성 정보 예) 20년 01 01

시간 또는 날짜와 같은 데이터의 경우 디바이스에서 읽어주는 방식에 따라 전혀 다른 정보를 읽어줄 수 있습니다.
안드로이드의 경우 두 자리 숫자 시간 정보(00:00)를 스크린리더로 출력하면 시 분으로 읽어주어 1분 15초 영상을 1시 15분으로 잘못된 정보를 제공받게 돼 시간 정보를 오해하게 됩니다.
시간 또는 날짜 정보는 다음과 같이 SimpleDateFormat으로 시간 또는 날짜 정보를 변환하여 대체 텍스트로 제공하는 것이 바람직합니다.

  SimpleDateFormat fmt = new SimpleDateFormat("mm분 ss초");
  textview.setContentDescription(fmt.format(date));

다음 그림에서 자막 정보를 보시면 숫자만 제공한 시간은 텍스트 그대로 제공되고 있고 SimpleDateFormat으로 제공한 시간 정보는 분초로 대체 텍스트가 제공되는 것을 알 수 있습니다. 음성으로 들으면 숫자만 제공한 시간 정보는 1시 15분으로 읽히고  SimpleDateFormat으로 제공한 시간 정보는 1분 15초 읽힙니다.

SimpleDateFormat 예시

 

색상으로만 구분한 정보에 대해서도 대체 텍스트를 제공해 주세요.

이전 색에 무관한 인식 아티클에서 읽음과 안 읽음 상태 정보를 색으로만 구분하면 색맹 사용자가 콘텐츠를 구분하는데 어려움이 있다고 알려드린 바가 있었는데요. 새 글에는 구분점을 추가하거나 텍스트의 굵기, 밑줄, 기울기 등 다양한 패턴으로 제공하여 색 이외 구분할 수 있도록 제공해야 합니다. 

이때 색에 무관한 인식 외 대체 텍스트에서도 읽음과 안 읽음 정보를 챙겨주셔야 합니다. 색에 무관한 인식까지도 잘 챙겼는데 대체 텍스트를 챙기지 않는다면 스크린리더 사용자에게 읽음과 안 읽음 정보 누락이 발생하게 되겠죠~ 

다음 그림과 같이 읽음과 안 읽음 정보 대체 텍스트 챙겨주세요. 

새글 대체 텍스트 사례 읽은글 대체 텍스트 사례

 

한글로 대체 텍스트를 제공해 주세요.

국내 이용 서비스의 대체 텍스트는 한글로 제공해야 합니다. 한글 서비스 콘텐츠의 대체 텍스트는 한글로 제공해야 합니다. 당연한 말이지요? 당연한 것 같지만 익숙하게 영어로 제공되는 경우를 많이 봅니다. top, back, delete, new 등등 익숙하게 사용되는 영어입니다. 저는 한국어가 가장 멋있습니다.

이런 경우는 어떨까요? 시각적으로 영어로 제공하는 경우,  한글화할 수 있는 의미는 정보는 한글로 제공하는 것을 권장합니다. 영어를 모른 사용자라면 해당 콘텐츠를 이해할 수 없기 때문에 한글로 제공하는 것을 권장합니다. 영어를 축약해서 사용하는 경우도 같은 맥락으로 한글로 제공해야 올바르게 이해할 수 있습니다. 

영어 콘텐츠의 대체 텍스트 예시, new는 신규 또는 새글로, top는 위로 또는 처음으로, re는 답변 또는 댓글로 제공

 

대체 텍스트에 이건 넣지 말아주세요~!

대체 텍스트에 "이미지"와 "버튼" 유형 정보를 넣지 말아주세요. 기본적으로 스크린리더에서 컨트롤에 맞는 유형 정보로 음성 안내가 되므로 대체 텍스트에 "이미지" 또는 "버튼" 을 제공할 경우 유형 정보가 두 번 반복해서 제공되게 됩니다. ~~ 이미지, 이미지 또는 ~~ 버튼, 버튼 이렇게요.  그렇기 때문에 유형 정보는 대체 텍스트에 제공하지 않아야 합니다. 

"이미지"와 "버튼" 외 모든 콘텐츠의 유형 정보상태 정보힌트 정보는 대체 텍스트가 아닌 각각의 쓰임과 의미에 맞게 제공되어야 합니다. 대체 텍스트에 유형 정보, 상태 정보, 힌트 정보를 넣어 음성만으로 비교한다면 정보를 제공받는 측면에서는 아무런 문제가 없어 보이지만, 정보를 재사용하는 측면에서 컨트롤 단위로 탐색을 해야 하거나, 힌트 정보를 제공받고 싶지 않아 설정을 통해 제거한다거나 하는 등 스크린리더 사용자의 사용 패턴 또는 설정에 따라 정보를 구분할 수 있도록 제공하는 것이 바람직합니다.

iOS VoiceOver 상세 설정 Android TalkBack 상세 설정
iOS VoiceOver 상세 설정 Android TalkBack 상세 설정

 

 

 

보조기술과의 호환성

 

 

 


음성 정보 순서는 iOS의 경우 이름, 값, 유형 정보, 힌트 정보(Label, Value, Traits, Hint) 순이고 Android의 경우 일반적으로 상태, 이름, 유형, 힌트 순이지만 사용자가 설정을 통해 상태, 이름, 유형 순서를 변경할 수 있습니다. iOS의 경우 예외적으로 Selected 상태 정보를 우선 읽어 주고, Android의 경우도 사용자가 순서를 변경 한다 하더라도 android:labelfor 속성을 적용한 컨트롤과 일부 컨트롤은 상태 정보를 우선 읽어줍니다.

컴포넌트 또는 요소의 용도(Role)나 상태(Status)정보가 제공되지 않는 경우 콘텐츠를 이용하기 어려워요~!

관련 지침 : 사용자 인터페이스 컴포넌트는 보조 기술을 이용하여 사용할 수 있도록 해야 한다.

유형 정보상태 정보 는 콘텐츠를 파악하기 위한 중요정보로서 접근성에서는 이를 제공하지 않을 경우 접근성 오류로 진단하고 있습니다.

 

유형 정보

컨트롤 용도에 따라 스크린리더 사용자는 그에 맞는 제스처를 사용하여 콘텐츠를 이용합니다. 그렇기 때문에 올바른 유형 정보를 제공하는 것은 매우 중요합니다. 예를 들어 값을 조절하는 요소를 커스텀 UI로 제공하고 유형 정보를 제공하지 않을 경우 사용자는 해당 컨트롤을 올바르게 이용할 수 없을 거예요.

iOS

  • 방법1. Xcode의 Accessibility 패널에서 Traits 제공
    ① Accessibility 에서 Enabled 을 선택해 접근성 기능을 활성화한 상태에서
    ② Traits 에서 유형정보 선택
    Xcode Accessibility 패널 Traits
     

  • 방법2. 코드로 Traits 제공

    var.accessibilityTraits = [.header] // ② 유형 정보
    

    유형 정보가 두 개 이상 적용돼야 할 경우

    var.accessibilityTraits = [.header, .button] // ② 중복 유형 정보
    

    예를 들어 다음과 같이 제목이면서 이동하는 버튼일 경우 .header와 .button 두 개의 유형정보를 제공하는것이 바람직합니다.
    중복 유형 정보 예시

  • Traits 리스트

Traits 내용
Button 버튼
Link 링크, 특정 웹 페이지로 이동하는 요소에 적용
Image 이미지
Selected 선택됨 상태 정보
Static Text 정적 텍스트
Search Field 검색 영역, 검색어를 입력하는 텍스트 필드
Plays Sound 활성화 시 VoiceOver 피드백 사운드를 제거하고 자체 사운드를 재생
Keyboard Key 커스텀 키보드
Summary Element 애플리케이션이 로딩될 때 첫 화면에서 사용자에게 알려주어야 할 내용을 VoiceOver가 자동으로 음성 출력하도록 적용
User Interaction Enabled 비활성화 상태 정보
Updates Frequently 실시간 내용이 업데이트 되는 객체에 사용
예)다운로드 율
Starts Media Session 사운드에 집중하도록 하기 위하여 보이스오버의 음성 정지. 포커스가 위치하고 있을 때만 적용됨.
Adjustable 값을 조절하는 요소에 적용
예)슬라이더
Allows Direct Interaction 그림을 그리는 영역, 악기를 연주하는 영역과 같이 VoiceOver 의 제스처들이 무시되어야 하는 영역에 사용.
VoiceOver의 터치 기능을 정지
Causes Page Turn 자동 페이지 넘기기
Header 제목


 

Android

  • 방법1. setClassName 제공
    컨트롤의 유형, 용도 정보를 사용자 정의하여 사용하고자 할 경우 accessibilityNodeInfo 에 setClassName을 사용합니다.

    ① View 에 다음과 같이 AccessibilityDelegate 를 설정

    button.setAccessibilityDelegate(buttonAccessibilityDelegate);
    

    ② setClassName 설정

    View.AccessibilityDelegate buttonAccessibilityDelegate = new
    View.AccessibilityDelegate() {
    @Override
           public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
           super.onInitializeAccessibilityNodeInfo(info);
           info.setClassName(Button.class.getName());
           }
        }
    });
    

     

  • 방법2. roleDescription 제공
    기존 유형 정보를 갖고 있는 컨트롤에 다른 유형 정보를 덮어쓰기 하고자 할 경우 AccessibilityNodeInfoCompat에 roleDescription을 사용합니다. 예를 들어 다음과 같이 버튼 클래스에 탭 정보를 제공하고자 할 경우입니다.

    ① View 에 다음과 같이 AccessibilityDelegate 를 설정

    ViewCompat.setAccessibilityDelegate(bt, new CustomLabelsDelegate());
    

    ② roleDescription 설정

    @Override
     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
     super.onInitializeAccessibilityNodeInfo(host, info);
     // Role 지정
     info.setRoleDescription("탭");
     }
    

유형 정보 탐색 

유형 정보는 사용자가 콘텐츠를 올바르게 인식할 뿐만 아니라 탐색할 때 매우 유용합니다. iOS의 경우 VoiceOver의 로터라는 기능을 통해 사용자가 유형 정보 단위로 탐색을 해 빠르게 탐색할 수 있으며, Android 또한  읽기 제어 기능 맞춤설정을 통해 제목, 컨트롤 등등 단위로 빠르게 탐색하고 인식할 수 있습니다.

iOS VoiceOver 로터 Android TalkBack 읽기 제어 기능 맞춤설정
iOS VoiceOver의 로터 기능 Android TalkBack 읽기 제어 기능 맞춤설정

 

Header (머리말 또는 제목) 정보를 제공해 주세요.

사용자 탐색 기능을 위해 제목에 해당하는 텍스트에 Static Text보다는 Header (머리말 또는 제목) 정보를 제공하는 것을 권장합니다. iOS에는 머리말이라고 하며, Android에서는 제목으로 읽힙니다

iOS

Traits를 Header로 머리말 정보를 제공합니다.

 text.accessibilityTraits = [.header]

 

Android

accessibilityHeading을 통해 제목 정보를 제공합니다.

  • XML
android:accessibilityHeading = true; // UI 레이아웃 XML에서 제공
  • Java
sectionTitleText.setAccessibililtyHeading = true;

클로바 Android, 웨일 브라우저 iOS 등 제목 정보를 제공하여 사용자가 콘텐츠 구조를 이해하고 빠르게 탐색할 수 있도록 노력하고 있습니다.

웨일브라우저 iOS 머리말 유형 정보 사례 예시 클로바 Android 제목 유형 정보 사례 예시

 

상태 정보

컨트롤 기능에 따른 상태 정보를 올바르게 제공해야 스크린리더 사용자는 그에 맞는 선택을 할 수 있습니다.

iOS

iOS의 경우 상태 정보는 Traits에서 Selected(선택됨)와 User Interaction Enabled(흐리게시됨) 뿐입니다.
Xcode Accessibility 패널 Traits에서 상태정보

이외 필요에 따라서 상태 정보를 제공해야 하는 경우alue에 작성합니다. accessibilityValue는 컨트롤의 값을 표현하기도 하지만 선택 정보, 상태 정보, 활성화 정보 등을 제공하기도 합니다.

  • 확장/축소의 경우 accessibilityValue로 제공할 수 있습니다.

    @objc func onNumberSectionClicked(){
          if (numberSection.tag == UNSELECTED) {
            openNumberSection()
               var.accessibilityValue = "확장됨"
        } else if (numberSection.tag == SELECTED ) {
               closeNumberSection()
              accessibilityValue = "축소됨"
         }
     }
    

     

  • 그룹 객체 중 현재 위치한 개수 /전체 개수 정보를 표시할 때 accessibilityValue로 제공할 수 있습니다.
    예를 들어 라디오 버튼과 같이 여러 개 중 하나를 선택해야 하는 버튼의 경우 사용자가 그룹 중 하나라는 것을 인식할 수 있게 개수 정보를 제공하면 좋습니다.
    예) 버튼, 1/3

    radio1.accessibilityTraits = [.button, .selected]
    radio1.accessibilityValue = "1/3"
    radio2.accessibilityTraits = [.button]
    radio2.accessibilityValue = "2/3"
    

    웨일 브라우저 iOS의 경우 다크 모드 설정에서 그룹 객체 중 하나를 선택해야 하는 버튼으로  accessibilityValue로 그룹 객체 중 현재 위치한 개수와 전체 개수 정보를 제공하고 있습니다.

    웨일 브라우저 iOS 라디오 버튼 예시

  • 업데이트 정보를 표시할 때 accessibilityValue로 제공할 수 있습니다.

    업데이트정보 앱 예시

    btn.accessibilityLabel = "메일"
    btn.accessibilityValue = "새로운 메일" + updatedCountValue + "개"
    


 

Android

accessibilityNodeInfo 클래스를 통해 상태 정보를 제공합니다.

  • 선택됨 상태 정보는 setSelected 메서드를 통해 제공합니다.
    view.setSelected(true);
    

 

  • 확장/축소 상태 정보는 ACTION_COLLAPSE/ACTION_EXPAND 상수로 제공합니다.

    collapseExpand.setAccessibilityDelegate(cExpandAccessibilityDelegate);
    
    View.AccessibilityDelegate cExpandAccessibilityDelegate = new
    View.AccessibilityDelegate() {
    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    super.onInitializeAccessibilityNodeInfo(info);
    if (isFruitContainerExpanded) {
    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
    } else {
         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
       }
    }
    
    
    // 커스텀 액션 코드 추가
    public boolean performAccessibilityAction(View host, int action, Bundleargs) {
      if (super.performAccessibilityAction(host, action, args)) {
         return true;
      }
      if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) {
         isFruitContainerExpanded = false;
         collapseFruitContainer();
     } else if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
         isFruitContainerExpanded = true;
         expandFruitContainer();
         return true;
     }
     return false;
    }
    });
    

    확장/축소를 구현하면 자동으로 커스텀 액션이 추가되므로 이중 탭이 아닌 커스텀 액션을 통해 실행 시에도 작동될 수 있도록 이벤트를 추가해야 합니다. (커스텀 액션은 하나의 객체에 여러 액션을 추가적으로 구현하여 스크린리더 사용자가 단순 액션으로 기능을 사용할 수 있도록 제공하는 기술입니다. 커스텀 액션에 대해서는 다음 포스트에서 자세히 다룰 예정입니다.)

    확장/축소 상태 정보는 다음과 같은 확장/축소 기능을 하는 공지사항 UI에 적용하면 좋습니다.
    확장/축소 기능을 갖고 있는 공지사항 UI

    이외 기타 상태 정보는 다음 링크를 참고해주세요.
    안드로이드 개발자 센터 accessibilityNodeInfo

힌트 정보

음성 정보 예시 이미지를 보면 요소에 따라 동작과 관련된 힌트 정보를 제공하는 것을 볼 수 있습니다.

"편집하려면 이중 탭하십시오."
"설정을 끄거나 켜려면 이중 탭하십시오. "
"활성화 하려면 두 번 탭하세요. 두번 탭하고 길게 누르세요."
"전환하려면 두 번 탭하세요. "

힌트 정보는 일반적으로 동작과 관련된 정보를 기본적으로 제공하지만, 콘텐츠를 사용하기에 필요한 부가적인 설명을 제공할 수 있습니다. 예를 들어 프로필 아이디의 경우 해당 아이디에 Label이 아이디 값만 있어 눌렀을 때 어떠한 결과가 나타날지 선택하기 전에는 알지 못합니다. 이럴 경우 힌트 정보를 통해 선택 시 프로필 설정 창으로 이동한다는 것을 제공하면 좋습니다. 주의사항으로 사용자의 설정에 따라 힌트 정보를 제공받지 못할 수도 있으므로 주요 정보가 아닌 부가적인 설명을 제공해야 합니다.

iOS

  • 방법1. Xcode의 Accessibility 패널에서 Hint 제공
    Hint 에 부가적인 설명을 작성합니다.
    Xcode의 Accessibility 패널에서 hint 제공

  • 방법2. 코드로 Hint 제공

    btn.isAccessibilityElement = true                   // 접근성 요소 활성화
    btn.accessibilityLabel = "서미연"                    // 대체 텍스트 정보
    btn.accessibilityHint = "프로필 설정창으로 이동"         // Hint 정보
    

Android

  • replaceAccessibiiltyAction 메서드를 상속받아 힌트 메세지를 추가 제공할 수 있습니다.

    ViewCompat.replaceAccessibilityAction(hamburgerOrderView, ACTION_CLICK, ("추가 힌트 메시지"), null);
    

    힌트 메시지 전체가 변경되는 것이 아니라 기본 힌트 메시지에 추가 힌트메시지가 더해져 음성안내가 출렵됩니다.
    변경 전 음성 : 활성화 하려면 두 번 탭하세요.
    변경 후 음성: 추가 힌트 메시지 작업하려면 두 번 탭하세요.
     

    다음 링크를 통해 플랫폼별 힌트 메시지 주의사항 및 특징을 더 자세히 알아볼 수 있습니다.
    iOS, Android 플랫폼에서의 접근성 힌트 메시지 제공 방법 1부 - 플랫폼별 힌트 메시지 제공의 특징 알아보기

여기까지 대체 텍스트와 보조 기술 호환성 관련하여 유형 정보, 상태 정보, 힌트 정보에 대해 간략하게 알아보았습니다. 

긴 글을 읽어주셔서 감사합니다.


내가 개발한 앱! 어떤 정보들이 읽히는지 알고 싶다면 바로 스크린리더를 켜고 들어보세요. 그리고 접근성 개선을 시작해주세요.
[모바일 스크린리더 기본 사용법 #Android TalkBack]
[모바일 스크린리더 기본 사용법 #iOS VoiceOver]


 

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