널리 알리는 기술 소식 다양한 접근성과 사용성, UI 개발에 대한 소식을 널리 알리고 참여하세요!
Spread your knowledge!

포럼 우리 모두의 소중한 의견이 모이는 곳입니다.

검색하기
  • tip
    [Android native] onInitializeAccessibilityEvent 메서드 이해하기
    Webacc NV 2021-07-24 18:36:20

    우리가 널리 아티클 및 포럼에서 여러 번 다루었던 onInitializeAccessibilityNodeInfo 는 톡백에서 읽어주는 요소 유형이나 상태정보 혹은 접근성 액션과 같은 노드 정보를 수정할 때 사용하는 메서드 입니다. 

    오늘 함께 살펴보고자 하는 것은 onInitializeAccessibilityEvent 입니다.

    해당 메서드는 메서드 이름만 봐도 알 수 있듯이 접근성 이벤트와 관련된 메서드로 특정 접근성 이벤트가 발생했을 때를 캐치하여 무언가의 작업을 할 때 사용합니다.

    예를 들어 우리가 클릭 리스너가 있는 뷰를 이중탭했다고 생각해 봅시다.

    그러면 톡백에서 두둑 하는 사운드가 발생합니다. 이것은 접근성 이벤트 중 클릭 이벤트가 발생했다는 것입니다.

    또는 한 손가락 쓸기를 통해서 특정 요소에 포커스 하였고 톡백에서 포커스 한 내용을 읽었다고 하십시다.

    이때 접근성 이벤트 중 AccessibilityFocused 이벤트가 발생합니다.

    이와 같은 접근성 이벤트가 발생했을 때 이를 조건문으로 활용하여 원하는 기능을 구현할 수 있으며 이때 해당 메서드를 사용합니다.

    적용방법은 간단합니다.

    onInitializeAccessibilityEvent 메서드 및 super 메서드를 오버라이드 한 다음 if 문을 사용하여 접근성 이벤트 타입 중 클릭, 접근성 포커스됨, 뷰에 포커스됨 등의 이벤트가 있으면 무언가를 해줘 라고 구현하면 됩니다. 다음 예시를 참고합니다.

            ViewCompat.setAccessibilityDelegate(nextTrack, new AccessibilityDelegateCompat() {
                @Override
                public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
                    super.onInitializeAccessibilityEvent(host, event);
                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
                        Handler handler = new Handler(Looper.getMainLooper());
                        handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                timePosition.announceForAccessibility(timePosition.getText());
                            }
                        }, 500);
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 액션 핸들러를 이용하여 커스텀 액션 적용하기
    Webacc NV 2021-07-08 17:30:04

    지난번에 iOS 커스텀 액션 구현 방법에 대해 공유한 적이 있습니다.

    해당 예제에서는 선택자, 즉 이미 구현된 함수 메서드를 가져와서 accessibilityCustomActions 변수 안에 배열 형태로 삽입함으로써 사용자가 커스텀 액션을 이중탭하였을 때 실행하고자 하는 기능을 삽입하는 방식이었습니다.

    그런데 이 방법 외에도 iOS 에서는 액션 핸들러를 이용하여 커스텀 액션 내에서 실행될 것들을 직접 구현할 수도 있습니다. 

    따라서 오늘은 해당 액션 핸들러를 사용하는 예제를 공유하려고 합니다.

    액션 핸들러를 사용하더라도 accessibilityCustomActions 변수 안에 배열 형태로 액션을 넣는 것은 같습니다.

    그런데 액션 핸들러를 사용할 때는 선택자가 아닌 액션 네임과 각 액션 핸들러가 들어가게 됩니다.

    액션 네임은 보이스오버에서 읽어줄 커스텀 액션 네임이며 핸들러는 기능 실행에 대한 것입니다.

    다음과 같이 코드를 구현할 수 있습니다.

     func makeAccessibilityCustomActions() -> [UIAccessibilityCustomAction] {
     let action = UIAccessibilityCustomAction(name: actionName) { (action) -> Bool in
                UIAccessibility.post(notification: .announcement, argument: "\(actionName)됨")
                self.setFavorite()
                self.accessibilityCustomActions = self.makeAccessibilityCustomActions()
                return true
            }
            return [action]
        }
    

    현재는 하나의 액션만 넣었지만 let action2, let action3 과 같이 여러 액션을 만들어서 리턴해 주면 해당 개수만큼 커스텀 액션이 늘어나게 됩니다.

    단 액션 네임이 관심 선택, 관심 해제와 같이 상황에 따라 변경되어야 하는 경우에는 스트링 변수를 사용하여 네임을 만든 다음 액션 네임이 변경되어야 할 시점에 스트링 네임을 변경하고 그것을 다시 accessibilityCustomActions 배열에 넣어 주어야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 뷰에 onTouchListener 구현 시에는 performClick 적용 필요
    Webacc NV 2021-06-19 16:30:52

    동영상 플레이어에서 화면 터치를 통해 숨겨진 화면 컨트롤을 다시 보이게 하거나 숨기는 작업 등을 구현할 때 onTouchListener 를 사용하게 됩니다. 그런데 이러한 onTouchListener 사용시에는 톡백 사용자는 이중탭을 해도 기능 실행이 전혀 안 됩니다. 따라서 다음 코드 예시와 같이 onTouchListener 사용 시에는 손가락 떼기 이벤트 발생 시 performClick 메서드를 함께 적용해 주어야 합니다.

    자세한 설명은 7월달에 공개될 동영상 플레이어 접근성 적용하기 아티클을 참고해 주시기 바랍니다.

      public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
          case MotionEvent.ACTION_DOWN:
            isTouching = true;
            return true;
          case MotionEvent.ACTION_UP:
            if (isTouching) {
              isTouching = false;
              performClick();
              return true;
            }
            return false;
          default:
            return false;
        }
      }
      // performClick 메서드를 오버라이드 할 때 실행될 동작을 아래에서 정의합니다.
      @Override
      public boolean performClick() {
        super.performClick();
        return toggleControllerVisibility();
      }

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 버튼과 텍스트가 분리된 접근성 개선하기 두 번째
    Webacc NV 2021-06-11 17:08:22

    작년에 버튼 이미지와 텍스트 레이블 초점이 분리된 경우의 접근성 개선 방안에 대해 다룬 적이 있었습니다.

    해당 방법대로 접근성 개선을 할 경우 보이스오버 사용자가 버튼과 텍스트 레이블을 하나의 포커스로 읽을 수 있습니다.

    그러나 위와 같은 방법 적용시 다음과 같은 문제가 있습니다.

    저시력 사용자가 보이스오버를 켠 상태에서 화면에 있는 이미지 혹은 텍스트 레이블 터치 시 내용을 읽어주지 않음.

    이는 두 요소 중 하나의 요소를 접근성 초점에서 완전히 제거했기 때문에 화면에는 두 영역이 있으나 보이스오버에서는 한 영역만 초점이 제공되기 때문입니다.

    따라서 오늘은 다른 방법을 추가적으로 공유를 하려고 합니다.

    1. 버튼과 버튼에 해당하는 텍스트가 있는 요소를 별도의 컨테이너 안으로 옮깁니다.

    즉 예를 들어 하나의 컨테이너에 6개의 객체들이 있는데 그 중 두 객체가 버튼과 이미지라고 합시다.

    그렇다면 기존 컨테이너 안에 또 다른 buttonAccessibilityContainer 와 같은 UIView 컨테이너를 만들고 버튼 이미지와 레이블 텍스트를 해당 컨테이너 안으로 옮기는 것입니다.

    2. 버튼과 텍스트만 들어 있는 컨테이너의 isAccessibilityElement true 로 설정합니다.

    그러면 보이스오버는 하위 두 요소에는 초점을 제공하지 않습니다.

    3. 해당 컨테이너에서 accessibilityLabel, accessibilityTraits 를 설정합니다.

    이렇게 하면 보이스오버에서도 하나의 초점으로 버튼 이미지와 레이블을 읽을 수 있고 접근성 프레임 영역도 두영역이 다 하나로 잡히게 됩니다.

    스토리보드에서 컨테이너를 추가로 만들어 초점을 하나로 합치고자 하는 객체를 넣은 다음 다음과 같은 함수를 만들어 사용할 수도 있겠습니다.

        private func setContainerViewAccessibility(containerView: UIView, traitsView: UIView, label: String) {
            
            containerView.isAccessibilityElement = true
            containerView.accessibilityLabel = label
            containerView.accessibilityTraits = traitsView.accessibilityTraits
        }

    이러한 함수를 만들었다면 다음과 같이 적용이 가능합니다.

    self.setContainerViewAccessibility(containerView: self.confirmView, traitsView: self.confirmButton, label: self.confirmLabel.text ?? "확인")

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] UIAccessibilityNotification .announcement 코드 예제
    Webacc NV 2021-06-09 16:57:48

    접근성을 개선할 때 참고하실 수 있도록 여러 상황에 대한 코드 예제를 팁 형태로 올려드리고 있습니다.

    보이스오버에서 특정 상황에서 알림 메시지를 자동으로 보이스오버가 말하도록 해야 할 때 사용하는 메서드에 대해 소개는 하였으나 정작 이에 대한 코드 예제가 없어 이를 공유하게 되었습니다.

    이전 배너, 다음 배너 버튼을 누를 때마다 배너가 넘어가면서 배너의 내용을 자동으로 읽어주도록 하는 것을 예로 들어 예제 코드를 공유합니다.

    알림 메시지에 대한 접근성 적용시 참고하시기 바랍니다.

        @IBAction func next(_ sender: UIButton) {
            self.count = self.count + 1
            if self.count > self.banners.count - 1  {
                self.count = 0
            }
      
            let x = UIScreen.main.bounds.width * CGFloat(self.count)
            self.scrollView.contentOffset = CGPoint(x: x, y: 0)
            sleep(1) // 알림 메시지를 주기 전 약간의 딜레이 주기
            UIAccessibility.post(notification: .announcement, argument: self.banners[self.count])
        }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 편집창, 체크박스와 레이블이 분리된 경우 labelFor 속성 활용하기
    Webacc NV 2021-06-03 18:05:15

    편집창이나 체크박스를 구현할 때 레이블을 해당 뷰 안에 넣는 경우도 있지만 텍스트뷰와 분리하는 경우도 많습니다.

    이런 경우 톡백에서 체크박스나 편집창에 초점을 주면 어떤 체크박스나 편집창인지를 읽지 못합니다.

    물론 체크박스나 편집창에서 한 손가락 오른쪽 혹은 왼쪽 쓸기를 했을 때 이에 해당하는 텍스트뷰를 읽어주기는 하지만 여러개의 편집창이 있거나 혹은 컨트롤 단위로 이동했을 때 바로 해당 레이블을 듣지 못하게 되어 접근성 수정이 필요합니다.

    방법은 아주 간단합니다.

    1. TextView 에 labelFor 속성을 추가합니다. labelFor 에는 텍스트뷰와 연결되는 편집창 혹은 체크박스의 id 를 연결합니다.

    2. 해당 체크박스에는 1번에서 연갈한 id 를 마크업합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 탭하여 실행 가능한 이미지뷰에 반드시 대체 텍스트를 제공해야 하는 이유
    Webacc NV 2021-06-01 18:18:55

    아이폰, 안드로이드 모두 이미지뷰에 대체 텍스트를 삽입하지 않으면 보이스오버, 톡백에서 해당 이미지뷰는 장식용으로 간주하고 초점 자체가 가지 않습니다.

    그런데 안드로이드의 경우에는 해당 이미지뷰에 클릭 리스너 이벤트가 추가되는 순간, 즉 탭할 수 있는 동작이 들어가는 순간 대체 텍스트가 없다 하더라도 이미지뷰에 초점을 받을 수 있게 됩니다.

    물론 톡백에서 이미지뷰에 포커스를 하면 라벨 지정되지 않음 버튼, 혹은 id 값이 있다면 btnCancel 버튼과 같이 읽어줄 것입니다.

    하지만 보이스오버의 경우에는 이미지뷰에 UITapGestureRecognizer 이벤트가 추가되어 이중탭하여 무언가를 실행할 수 있게 되었다 하더라도 안드로이드와 달리 대체 텍스트가 없으면 접근성 초점이 제공되지 않습니다.

    따라서 실행 가능한 이미지뷰에 대체 텍스트가 없으면 보이스오버 사용자는 해당 객체를 실행조차 못하는 접근성 문제가 발생하게 됩니다.

    따라서 대체 텍스트 삽입의 중요성에 대해 우리는 더욱 생각하며 개발을 해야 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 라디오버튼 접근성 적용하기
    Webacc NV 2021-05-31 16:55:01

    여러 팁을 통해 커스텀 스위치, 커스텀 체크박스, 커스텀 탭 등의 접근성 적용 예제에 대해 다룬 적이 있습니다.

    사실 커스텀 라디오 버튼 역시 구현 방법은 비슷하지만 샘플 코드가 있으면 개발 시에 참조하기 편리하므로 아래에 커스텀 라디오 버튼에 대해서도 샘플 코드를 공유하려고 합니다.

    참고: 본 예제의 라디오버튼은 이미지뷰로 적용하였으며 단품, 세트 중에서 선택하는 커스텀 라디오 버튼이라고 가정하겠습니다.

    접근성 구현 시 참고가 되었으면 좋겠습니다.

            // 이미지뷰의 접근성 정보 수정을 위해 AccessibilityDelegate 객체 만들기
            button1.accessibilityDelegate = object : View.AccessibilityDelegate() {
                override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
                    super.onInitializeAccessibilityNodeInfo(host, info)
                    info?.className = RadioButton::class.java.name
                    info?.isCheckable = true // 체크, 체크 해제에 대한 음성 안내
                    info?.isChecked = button1.isSelected // 조건문에 따라 checked true false 적용
                    info?.isSelected = false // 선택된 요소에 selected 적용한 경우 체크됨과 중복되므로 해제
    
                }
            }
    
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 탭레이아웃 라이브러리에 적용된 탭 요소 접근성 적용과 커스텀 탭에 대한 접근성 대응
    Webacc NV 2021-05-31 15:13:57

    안드로이드 탭 레이아웃 접근성 기능 업데이트에 대해 말씀드린 적이 있었습니다.

    지난번에도 언급한 바와 같이 탭 요소를 탭이라고 읽어주도록 업데이트가 되었는데 원칙적으로는 안드로이드 접근성 API 에는 탭이라는 접근성 역할이 없습니다.

    그럼 라이브러리에서 톡백이 어떻게 탭이라고 읽도록 구현했을까요?

    그에 대한 정답은 바로 roleDescription 입니다.

    구글에서도 톡백의 각 탭을 탭으로 읽도록 하기 위해서 AccessibilityNodeInfoCompat roleDescription 메서드를 사용한 것입니다.

    따라서 탭을 구현할 때 탭레이아웃을 사용하면 접근성 구현이 별도 필요하지 않습니다.

    하지만 기본 탭레이아웃을 사용하지 않고 텍스트뷰 등으 탭으로 사용한 경우에는 다음 예시와 같이 roleDescription 을 사용하여 탭 요소를 탭으로 읽도록 구현할 수 있습니다.

        ViewCompat.setAccessibilityDelegate(customTab, new AccessibilityDelegateCompat() {
                @Override
                public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
                    super.onInitializeAccessibilityNodeInfo(host, info);
                    info.setRoleDescription("탭");
                }
            });

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 확장/축소 접근성 적용 예제
    Webacc NV 2021-05-31 12:20:52

    작년 8월 즈음에 안드로이드에서의 확장/축소 접근성 적용에 대해 한번 다룬 적이 있었습니다.

    이번 시간에는 확장 축소가 적용된 샘플 코드를 공유해 보려고 합니다.

    코드 적용에 대한 자세한 설명은 8월에 발행될 접근성 블로그 아티클을 참고해 주시기 바랍니다.

    참고: 이번에 다룰 코드 예시에서는 과일 버튼이 있다고 가정하고 과일 버튼은 확장 축소가 가능한 버튼이며 기본적으로는 축소되어 있습니다.

            // 접근성 정보 수정 
            fruitButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
                // 확장/축소 정보 삽입
                @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
                    super.onInitializeAccessibilityNodeInfo(host, info);
                    info.setClassName(Button.class.getName());
                    
                    if (isFruitContainerExpanded) {
                        // 확장된 상태이므로 축소 액션 삽입
                        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
                    } else {
                        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
                    }
                }
    
    // 톡백에서 작업 메뉴를 열어 확장 축소 적용 가능하도록 구현
                @Override
                public boolean performAccessibilityAction(View host, int action, Bundle args) {
                    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;
                }

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 버튼, 레이블과 같은 요소의 상위 컨테이너에 접근성 초점을 주는 의미
    Webacc NV 2021-05-28 13:27:36

    iOS 보이스오버는 기본적으로 UIView, UICollectionView 와 같은 컨테이너에는 접근성 초점이 제공되지 않습니다. 

    그 이유는 해당 컨테이너들은 접근성에서는 의미를 가진 요소가 아니며 버튼, 레이블, 텍스트와 같은 요소들을 품고 있는 요소들이기 때문입니다.

    그런데 만약 이러한 컨테이너에 접근성 초점을 주면 어떻게 될까요?

    containerUiView.isAccessibilityElement = true

    이렇게 하면 해당 컨테이너 안에 포함된 그 어떤 요소에도 접근성 초점이 제공되지 않습니다.

    따라서 접근성 초점을 줄 때는 상당히 주의가 필요합니다.

    하지만 상황에 따라서는 이러한 상위 컨테이너 뷰에 접근성 초점을 주어야 하는 경우도 있습니다.

    대표적인 예가 하위 컨테이너 뷰들을 접근성 초점에서 재구성해야 할 때입니다.

    하위 뷰들의 초점 순서가 논리적이지 못하거나 하위 뷰들의 헤더와 데이터가 별도의 뷰로 구현되어 있거나 분리되어 있어야 할 뷰들이 하나로 합쳐져 있는 것 등 이미 구현된 뷰를 접근성에서 재구성해야 할 때 사용을 합니다.

    즉 가상의 접근성 요소들을 만드느 것입니다.

    앞으로 몇 차례에 걸쳐 해당 주제에 대해 다루어 보도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] accessibilityTrait 변경 시 유의사항
    Webacc NV 2021-05-26 17:27:57

    커스텀 컨트롤에서 각 요소의 유형이나 상태를 접근성 서비스에서 적절하게 읽어줄 수 있도록 하기 위해 우리는 accessibilityTraits 속성을 많이 변경하곤 합니다. 

    그런데 안드로이드나 웹과는 달리 iOS accessibilityTraits 속성은 요소 유형, 상태정보 속성을 함께 포함하고 있습니다.

    따라서 웹이나 안드로이드는 특정 요소에 대한 상태정보를 변경할 때 역할(role) 속성이 이미 정의되어 있다면 상태 정보만 상황에 따라 변경해 주면 됩니다.

    그러나 iOS에서는 위에서 설명한 것처럼 accessibilityTraits 를 재정의하는 순간 기존의 모든 역할, 상태정보 등은 초기화 되므로 이에 대한 주의가 필요합니다.

    예를 들어 UIButton 으로 되어 있는 요소에 선택됨 정보를 주어야 한다고 생각해 봅시다.

    선택됨 정보는 accessibilityTraits = .selected 를 통해서 줄 수 있습니다.

    그러나 바로 위의 코드와 같이 적용하면 기존 버튼 속성은 사라져버립니다.

    우리는 버튼과 선택됨을 함께 읽도록 해야 하므로 accessibilityTraits = ].button, .selected] 와 같이 속성을 주어야 하는 것입니다.

    혹은 accessibilityTraits 추가시에는 insert, 삭제 시에는 remove 를 사용하는 것도 방법이 될 수 있습니다.

    댓글을 작성하려면 해주세요.
  • qna
    직군별 체크리스트 문의드립니다.
    연연 2021-05-21 17:59:16

    안녕하세요. 널리의 접근성 자료와 아티클들 잘 보고 있습니다.

     

    널리에서 공유하고 있는 웹 접근성 교육의 '직군별 체크리스트'를 업무 시에 유용하게 참고하고 있습니다.

    감사합니다.

     

    직군별 체크리스트에서 직군별로 유의해야 할 접근성 지침 항목이 뚜렷하게 구분되진 않을 것이라 생각합니다.

    어떤 기준으로 구분하였는지 여쭤보고 싶네요.

    기준을 참고하여 모바일 접근성 지침에도 적용해 보고 싶습니다.

    감사합니다. 

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 토글버튼 접근성 구현
    Webacc NV 2021-05-07 19:06:02

    즐겨찾기 켜짐/꺼짐, 알람 켜짐/꺼짐과 같은 토글 버튼을 구현할 때 스위치나 토글버튼 클래스를 사용하지 않고 커스텀뷰로 구현하는 경우에는 톡백을 사용하는 스크린 리더 사용자가 현재 상태가 꺼짐 상태인지 켜짐 상태인지 상태정보를 알 수 없습니다.

    이때 상태정보를 대체 텍스트로 제공하기보다는 AccessibilityNodeInfo 객체를 수정하여 상태정보를 읽을 수 있도록 하는 것이 사용성 측면에서 훨씬 도움이 됩니다. 

    AccessibilityNodeInfo 객체의 활용에 대해서는 여러 번 언급하였지만 예제 코드를 공유하는 차원에서 아래에 다시 변경하는 방법을 정리합니다.

    // AccessibilityDelegate 객체 만들기
    checkBox.setAccessibilityDelegate(checkBoxAccessibilityDelegate);
    
        final View.AccessibilityDelegate checkBoxAccessibilityDelegate = new View.AccessibilityDelegate() {
    // onInitializeAccessibilityNodeInfo 메서드 오버라이드 하기
            @Override
            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
                super.onInitializeAccessibilityNodeInfo(host, info);
    // 토글 버튼으로 클래스 네임 설정
                info.setClassName("android.widget.ToggleButton");
                //setCheckable을 적용해야 음성 안내
                info.setCheckable(true);
    // 켜짐, 꺼짐 변수에 setChecked 대입
                info.setChecked(isChecked);
            }
        };
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [HTML] 본문 바로가기 구현 시 tabindex="-1"을 함께 적용하는 경우
    Webacc NV 2021-05-07 11:56:04

    일반적으로 웹페이지 가장 처음에는 본문 바로가기 링크를 구현하며 구현하는 방법은 두 가지 정도로 분류할 수 있겠습니다.

    1. 해시: <a href="#content">본문 바로가기</a> 와 같은 형식으로 마크업을 하는 방식입니다.

    2. 스크립트 방식: <a href="#content" onclick="$('content').tabIndex=-1;$('content').focus();return false;">본문으로 바로가기</a>

    2번처럼 구현할 경우 위의 예시에서처럼 tabindex="-1" 속성을 함께 주어야 하는데 해당 속성을 메인 콘텐츠 전체를 감싸고 있는 div에 주게 되면 스크린 리더 사용자 입장에서는 화살표 키와 탭키를 함께 사용하여 내용 탐색 시에 문제가 있습니다.

    예시: 본문 콘텐츠 내에 a, b, c 3개의 링크가 있고 b 링크 다음에 여러 줄의 텍스트가 있다고 가정해 봅시다.

    화살표키를 이용해서 b 링크 다음에 있는 텍스트를 탐색하다가 탭키를 누르면 당연히 c 링크로 이동을 해야 할 것입니다.

    그러나 초점은 a 링크로 이동합니다.

    왜일까요?

    tabindex="-1" 속성이 본문을 감싸는 콘텐츠에 들어가 있기 때문에 b 링크 다음에 있는 텍스트를 화살표를 이용하여 접근하는 순간 디브 자체에 초점을 받는 것이나 마찬가지가 됩니다.

    따라서 b 링크 아래의 텍스트 콘텐츠를 읽고 있더라도 초점은 본문 전체에 받고 있으므로 탭키를 누르면 첫 번째 링크인 a로 이동하는 것입니다.

    이러한 문제를 해결하려면 본문 바로가기는 href="#content" 와 같이 제공하거나 반드시 스크립트를 사용하려면 본문을 구성하는 첫 요소에 tabindex="-1" 속성을 적용해야 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] TableView 내에서 다른 화면으로 이동하는 버튼 구현 시 유의사항
    Webacc NV 2021-05-06 18:06:36

    A 화면에서 B 화면으로 전환되는 요소들을 테이블뷰로 구현하는 경우가 있습니다.

    이때 상황에 따라 여러 가지 경우의 수가 있겠지만 각 셀에 레이블을 주는 경우가 대부분일 것입니다.

    그런데 보이스오버에서 B 화면으로 갔다가 다시 뒤로 돌아오면 이전에 눌렀던 요소를 선택됨이라고 읽는 경우를 가끔 발견하곤 합니다.

    그것은 테이블뷰 구현 시에 tableView:didSelectRowAtIndexPath 메서드를 사용하였거나 혹은 tableView:willSelectRowAtIndexPath 사용 시 return을 nil로 반환하지 않았기 때문입니다.

    위의 코드처럼 사용 시에는 셀이 선택되었다는 것을 나타내는 것이기 때문에 스타일을 선택됨으로 적용하지 않더라도 선택됨 속성이 추가되어 보이스오버에서는 선택됨이라는 상태정보를 읽게 되며 이것은 사용자에게 적절한 정보가 되지 못합니다.

    따라서 셀이 선택됨을 나타내지 않을 경우에는 tableView:willSelectRowAtIndexPath, 리턴 값은 nil로 사용해야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android chrome] aria-current 정식 지원 관련
    Webacc NV 2021-04-27 10:54:35

    지난 번에 aria-current 속성이 크롬 브라우저 카나리아 버전에서 지원이 시작되었다는 것을 공유한 적이 있습니다.

    이제는 해당 속성이 크롬 정식 버전에서도 지원됩니다.

    따라서 톡백 최신 버전과 크롬 브라우저 최신 버전을 사용하면 aria-current="page, true" 등의 속성이 추가된 요소의 선택 정보를 음성으로 들을 수 있습니다.

    다만 크롬 정식 버전을 사용하지 않는 커스텀 웹뷰에서는 해당 속성이 지원되지 않습니다.

    이는 aria-current 속성은 톡백에서 지원을 못한 것이 아니라 브라우저 API에서 지원하지 못한 이슈이기 때문입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [모바일 웹] 안드로이드 모바일웹, 크롬 인스펙터에서 요소 빠르게 찾기(스크린리더 사용자를 위한 팁)
    Webacc NV 2021-04-20 12:33:46

    이번 팁은 스크린 리더 사용자를 ㅜ이한 팁입니다.

    스크린 리더 사용자가 접근성 테스트 업무를 하면서 이슈 리포팅을 할 때 모바일 웹을 윈도 크롬으로 요소 검사를 하기가 쉽지 않습니다.

    모바일과 윈도 크롬을 연결하고 인스펙터를 실행하면 모바일 화면이 컴퓨터 화면에 표시되고 'Select an element in the page to inspect it'을 체크하면 요소 검사를 하고 싶은 텍스트를 마우스로 찍으면 해당 요소로 빠르게 이동이 가능하지만 스크린 리더에서는 해당 화면 자체의 접근이 불가능하기 때문입니다.

    게다가 문자열 찾기를 실행해도 그 문자에 해당하는 요소로 자동으로 초점이 이동되지 않습니다.

    하지만 NVDA 스크린 리더에서 제공하는 OCR 기능을 이용하면 어느정도 쉽게 해당 기능을 수행할 수 있습니다. 

    1. 모바일과 PC를 연결하고 크롬 주소창에 chrome:inspect 입력을 합니다.

    2. 여러 버튼 중 디바이스를 누른 다음 가상커서를 켠 상태에서 화살표키를 아래로 내리다보면 나의 디바이스 이름이 나옵니다. 나오지 않는다면 USB 디버깅 이슈를 확인하시기 바랍니다.

    3. 디바이스에서 엔터를 누릅니다.

    4. 화살표를 내리다보면 현재 내가 모바일에서 접속한 페이지가 있고 그 아래에 inspect 가 있습니다. inspect 에 커서를 맞추고 엔터를 누릅니다. 그러면 크롬 요소 검사 창이 표시됩니다.

    5. 문서 처음으로 이동한 다음 버튼 단위로 이동하여 Select an element in the page to inspect it 을 체크합니다.

    6. 가상커서를 켠 상태에서 g 를 누르면 Screencast view of debug target 그래픽이라고 읽어주는 곳이 있습니다.

    6. NVDA 키와 r을 누르면 해당 이미지 문자 인식을 합니다.

    7. 문자 인식이 완료되면 화면을 살펴본 다음 요소 검사를 하고자 하는 문자에 맞추고 마우스 포인터를 해당 글자에 보낸 다음 왼쪽 마우스 클릭 단축키를 눌러주면 해당 요소로 이동하게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 접근성 테스트 시에 세 손가락 탭을 통해 대략적인 컨테이너 파악해보기
    Webacc NV 2021-04-14 15:04:05

    안드로이드 앱 접근성 진단시에는 현재 톡백이 읽어주는 요소에 대해 대략적으로 접근성 디버깅을 할 수 있으므로 특정 오류를 파악하기 수월하지만 iOS 의 경우에는 소스코드가 없는 이상 디버깅 자체가 불가능합니다.

    그러나 완벽하지는 않지만 현재 보이스오버가 읽어주는 요소가 어떤 컨테이너에 속해 있는지 대략적으로 알 수 있는 방법이 있습니다.

    바로 세 손가락 한 번 탭을 이용하는 것입니다.

    세 손가락을 한 번 탭 하면 현재 요소가 위치한 곳이 화면 어디쯤인지, 그리고 이미지인 경우 이미지 설명 등을 제공하게 되는데 그것과 별개로 TableView, ScrollView 컨테이너에 속해 있는 경우에는 이에 대한 정보도 알려줍니다. 

    스크롤뷰인 경우: x페이지 중 y페이지.

    TableView 인 경우: 총 x행부터 y행까지.

    NavigationBar, UIView 와 같은 일반 컨테이너에 속해 있을 경우에는 해당 정보 자체를 알려주지 않습니다. 

    따라서 특정 요소에서 오류가 발생할 경우 대략적으로 그 오류가 속한 컨테이너를 알면 오류 파악이 더 용이할 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 볼륨 버튼으로 특정 이벤트 구현 시 SeekBar 로 읽어주지 않게 하는 예제
    Webacc NV 2021-04-12 16:45:01

    지난 시간에 이어서 오늘은 볼륨 버튼으로 촬영, 항목 순서 이동 등의 이벤트를 구현할 때 톡백에서 해당 요소를 SeekBar 로 읽어주지 않도록 하는 예제를 공유하려고 합니다.

    ViewCompat.setAccessibilityDelegate(view, new AccessibilityDelegateCompat() {
    //roleDescription 을 사용하려면 ViewCompat.setAccessibilityDelegate 클래스를 사용해야 합니다.
                @Override
                public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
                    super.onInitializeAccessibilityNodeInfo(host, info);
                    info.setClassName(SeekBar.class.getName());
                    info.setRoleDescription("button");
                    info.setTooltipText("볼륨키로 실행 가능");
                }
    });

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 볼륨 버튼으로 특정 동작 실행 이벤트 구현 시 참고사항
    Webacc NV 2021-04-09 18:26:20

    지난번 안드로이드 톡백에서 볼륨 버튼으로 이전, 다음 배너 넘기기를 하는 방법에 대해 공유한 적이 있습니다.

    해당 방법은 배너뿐만 아니라 볼륨 버튼으로 특정 요소가 실행되도록 해야 하는 경우에 편리하게 사용이 가능합니다.

    예: 위/아래로 이동, 카메라 앱에서의 촬영 버튼 등.

    그런데 이 방법을 사용하였을 때 겪게 되는 한 가지 고민이 있습니다.

    바로 요소 유형 문제인데요.

    요소 유형을 무조건 SeekBar 즉 슬라이드 라고 읽는다는 것입니다.

    예를 들어 촬영 버튼을 볼륨키로도 실행할 수 있도록 한다고 했을 때 톡백에서는 촬영 슬라이더 라고 읽어주므로 정확한 역할 정보를 주지 못합니다.

    이를 해결하기 위해서는 AccessibilityNodeInfoCompat 을 사용하고 setClassName 을 SeekBar 로 주는 것과 더불어 roleDescription 을 함께 주면 됩니다.

    roleDescription 은 톡백에서 읽는 역할 자체를 커스텀으로 주는 것으로 기존에 있던 요소 유형을 완전히 무시하게 됩니다.

    다음 팁에서는 해당 메소드를 적용한 예제를 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Flutter] appBar title에 관하여
    Webacc NV 2021-04-02 19:02:24

    앞으로 틈나는대로 플러터의 접근성에 대한 팁을 공유할까 합니다.

    플러터는 구글에서 개발한 플랫폼으로 하나의 코드만으로도 iOS, Android 두 플랫폼 모두에서 개발이 가능한 장점이 있습니다.

    플러터 역시 여러 접근성 API를 제공하므로 이에 대한 정보를 틈틈이 다루어보고자 하는 것입니다.

    오늘 처음으로 다루어보고자 하는 것은 상단의 화면 제목으로 사용되는 Scaffold.appBar > title 속성입니다.

    해당 속성은 화면 상단의 제목을 표시하는 용도로 사용하며 안드로이드로 치면 activity 내의 setTitle이 추가된 화면과 같고 iOS로 치면 NavigationBar 영역에서의 화면 제목을 표시하는 것과 같다고 보시면 됩니다.

    iOS에서는 NavigationBar 타이틀을 사용하면 보이스오버가 해당 화면 제목을 자동으로 머리말이라고 읽어주지만 안드로이드에서는 setTitle 사용해도 톡백에서 제목 유형으로 읽어주지 않는 특징이 있습니다.

    그런데 플러터에서는 화면 제목을 앞에서 설명한 바와 같이 appBar title 속성으로 제공하면 안드로이드 톡백에서도 해당 요소를 제목으로 읽어줍니다. 

    이것을 플러터식 접근성 용어로 설명하면 Semantics 내의 header 속성이 true로 설정됩니다.

    이 부분에 대해서는 앞으로 조금씩 설명드리겠습니다.

    따라서 플러터로 앱 개발 시에 화면 제목을 appBar title 속성으로 사용하면 별다른 접근성 구현 없이도 안드로이드에서도 제목으로 읽어준다는 장점이 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 음성검색 접근성 구현 시 setTitle 메소드 사용과 관련된 변경 사항
    Webacc NV 2021-03-24 09:38:32

    지난 번 안드로이드 앱 음성검색 화면 접근성 구현 예제에 대해 설명을 한 적이 있습니다.

    음성 검색 실행 시 액티비티 화면이 변경되는 경우에는 setTitle 값을 "" 으로 비워 두라고 말씀을 드렸습니다.

    그런데 안드로이드 11에서는 그렇게 비울 경우 앱 이름을 읽는 문제가 있습니다.

    따라서 setTitle 안에 " " 형태로 스페이스를 하나 비워 두어야 아무런 화면 제목을 읽지 않으며 스크린 리더 사용자도 음성검색을 제대로 수행할 수 있습니다.

    해당 이슈에 대해 참고하시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] onRequestSendAccessibilityEvent 적용 예제
    Webacc NV 2021-03-19 11:49:29

    며칠전에 onRequestSendAccessibilityEvent 메소드에 대해 살펴보았습니다. 

    오늘은 구체적으로 해당 이벤트를 적용하여 롤링이 되고 있는 이미지뷰에 포커스 했을 때 롤링을 멈추는 방법에 대해 공유하려고 합니다.

    현재 이미지 3개가 롤링되고 있는 상태이며 해당 이미지 3개는 flipper 라는 부모 뷰 안에 들어가 있다고 가정하겠습니다.

            flipper.setAccessibilityDelegate(new View.AccessibilityDelegate() {
    
                @Override
    
                public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) {
                    if (event.getEventType() == TYPE_VIEW_ACCESSIBILITY_FOCUSED) { //포커스가 접근할때 불리는 이벤트입니다.
                        flag = false;
                    } else if (event.getEventType() == TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED) {
                        flag = true;
                    }
                    return super.onRequestSendAccessibilityEvent(host, child, event);
                }
            });

     

     

    댓글을 작성하려면 해주세요.
  • news
    [Android native] 접근성 검사기 업데이트 소식(여러 화면 자동 캡처 후 접근성 결과 확인)
    Webacc NV 2021-03-16 10:05:50

    접근성 검사기는 접근성에 대해 잘 모르는 사용자도 손쉽게 특정 앱의 접근성 이슈들을 자동으로 진단할 수 있도록 만든 애플리케이션입니다.

    앱 설치 후 접근성 설정에서 서비스를 활성화 하면 접근성 검사 버튼이 항상 따라다니게 되므로 접근성 검사를 하고 싶은 화면에서 언제든지 검사를 수행할 수 있습니다.

    물론 정교한 테스트는 접근성 테스터의 손을 거쳐야 하지만 대체 텍스트 유무, 명도대비, 터치 영역과 같은 부분들은 충분히 활용이 가능합니다.

    그런데 최근 업데이트 된 접근성 검사기에서는 여러 화면을 연속으로 촬영하여 접근성 결과를 한꺼번에 출력하는 기능이 추가되었습니다.

    기록을 누른 다음 다른 화면으로 이동하기 위해 탭을 하면 자동으로 화면 변화를 인지해서 촬영을 해 주는 기능입니다.

    촬영을 다 마친 후에는 알리 패널에서 기록 중지를 눌러 촬영된 결과들ㅇㄹ 볼 수 있고 메일 등으로 공유도 가능합니다.

    모바일 앱에서는 아직 대체 텍스트가 없는 앱들이 상당히 많습니다.

    따라서 이 기능을 충분히 활용하여 대체 텍스트에 대한 누락은 미연에 방지할 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • news
    [PC Chrome] 정식 버전에서의 aria-current 속성 정식 지원에 관한 소식
    Webacc NV 2021-03-15 16:32:59

    몇 달전에 aria-current 지원에 관련하여 공유한 적이 있습니다.

    PC 크롬에서 페이지 내에서 버튼이나 링크를 눌렀을 때 페이지가 새로고침 되지 않고 aria-current 속성만 업데이트 되는 경우에는 변경된 aria-current 이벤트를 제대로 캐치하지 못한다는 것이었습니다. 

    이는 크롬에서 IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED 이벤트를 발생시켜 주지 않아 일어나는 문제였는데 크롬에서 이를 개선해 주었으며 정식 버전에도 반영되었습니다.

    따라서 89.0.4389.82 버전 이상을 사용하신다면 적어도 NVDA 에서는 aria-current 속성이 완벽히 지원되는 것을 확인할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] onRequestSendAccessibilityEvent 메소드에 관하여
    Webacc NV 2021-03-11 16:53:21

    어제 아티클을 통해서 몇 차례에 걸쳐 스크롤 이벤트가 없는, 이미지가 변경되는 형식의 롤링 배너 접근성 적용에 대해 다룬다고 말씀을 드렸습니다.

    우리의 목표는 접근성 포커스가 배너 이미지에 머무르면 다른 이미지로 변경되지 않도록 하는 것입니다.

    그런데 이렇게 하려면 한 가지 고민이 필요합니다.

    배너 이미지가 여러 개인데 그 중 하나에 포커스를 하고 있는 것을 어떻게 캐치를 하는가 입니다.

    이때 사용할 수 있는 메소드가 onRequestSendAccessibilityEvent 입니다.

    이 메소드는 접근성 이벤트를 캐치할 때 사용하는데 child views 즉 자손 뷰들 중에 뭔가 이벤트가 일어나는 것을 캐치할 때 사용을 합니다.

    그러니까 예를 들어 이미지가 10개가 있고 그 이미지가 몇 초 간격으로 변경된다고 생각해 봅시다.

    우리는 변경되는 이미지에 대한 것을 캐치해서 접근성 이벤트를 조작해야 합니다.

    그러기 위해서 이미지를 품고 있는 부모 뷰에 onRequestSendAccessibilityEvent 메소드를 사용할 수 있다는 것입니다.

    이렇게 하면 자손 뷰에서 뭔가 이벤트가 발생할 때 그것을 캐치하고 조작하는 접근성 구현이 가능해집니다.

    다음 팁에서는 구체적인 예시를 살펴보겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [android native] 스크롤 이벤트가 없는 롤링 배너의 접근성 개선에 대한 아이디어
    Webacc NV 2021-03-10 12:04:18

    널리 아티클을 통해, 그리고 팁을 통해 몇 차례 다룬 적이 있지만 모바일에서 롤링배너가 삽입되는 경우 접근성과 관련해서 여러 가지 고민을 하게 됩니다.

    그 고민 중 하나는 접근성을 위해서는 롤링되고 있는 배너를 정지할 수 있는 버튼을 두어야 하지만 모바일이라는 특성상 화면이 좁기 때문에 해당 버튼을 배치하기가 어렵다는 것입니다.

    그래서 네이티브 앱에서는 스크린 리더가 켜져 있으면 배너가 정지된 상태로 제공되는 방법이 있어 이에 대해 다루었습니다.

    하지만 단순히 배너를 정지 상태로 제공하는 것 말고도 한 가지 더 방법이 있습니다.

    바로 접근성 포커스가 배너에 머무르고 있는 동안에는 배너가 롤링되지 않도록 하는 것입니다.

    단 해당 방법은 스크롤 이벤트가 제공되지 않는 배너에서만 적용해야 합니다.

    스크롤 이벤트가 적용되는 배너에서는 배너가 롤링될 때마다 톡백에서 스크롤 사운드를 발생시켜 화면 탐색에 상당한 방해를 주기 때문입니다.

    그럼 다음 시간부터 몇 차례에 걸쳐 해당 방법에 대해 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 커스텀 액션 적용 예제
    Webacc NV 2021-03-09 10:35:47

    지난 iOS 네이티브 앱에서의 커스텀 액션 작동 방법에 이어서 오늘은 커스텀 액션 적용 방법에 대해 다루어 보려고 합니다.

    사용자가 초점을 보낼 수 있는 특정 요소에 커스텀 액션을 적용하려면 UIAccessibilityCustomAction 클래스를 사용합니다.

    커스텀 액션 안에는 네임, 실행 대상, 실행할 메소드 세 가지의 값이 들어갑니다.

    네임은 말 그대로 보이스오버 드에서 출력될 커스텀 액션의 이름으로 삭제, 위로 이동 등이 예라 할 수 있습니다.

    타겟은 해당 액션을 이중탭하여 실행했을 때 어떤 요소에 동작을 적용할 것인지를 정해주는 것으로 일반적으로는 자기 자신, 즉 self 를 사용합니다.

    실행할 메소드는 사용자가 커스텀 액션에 맞추고 이중탭을 실행했을 때 어떤 동작을 할 것인지를 알려주는 것입니다.

    위의 설명을 바탕으로 아래에 커스텀 액션 코드 예시를 넣어 보았습니다.

    item.accessibilityCustomActions = [
                    UIAccessibilityCustomAction(
                            name: "삭제",
                            target: self,
                            selector: #selector(deleteItem)
                        ),
                        UIAccessibilityCustomAction(
                            name: "위로 이동",
                            target: self,
                            selector: #selector(moveUpItem)
                        ),
                    UIAccessibilityCustomAction(
                        name: "아래로 이동",
                        target: self,
                        selector: #selector(moveDownItem)
                    )
                ]
            }

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] isAccessibilityFocused() 메소드 활용하기
    Webacc NV 2021-03-05 16:51:40

    안드로이드 앱 접근성을 구현하다보면 현재 접근성 서비스가 포커스 하고 있는 요소를 가지고 와야 하는 경우가 있을 수 있습니다.

    예를 들면 현재 콘텐츠를 다운받는 화면에 있다고 가정해 봅시다.

    다운로드 진행률이 ProgressBar 클래스로 구현되어 화면에 표시되고 있다면 안드로이드 11 톡백에서는 사용자가 특별한 접근성 구현 없이도 초점을 이동하여 퍼센트 확인이 가능할 것이고 10 이하버전에서는 변경되는 퍼센트 값에 대한 대체 텍스트를 해당 클래스에 넣어 줌으로써 접근성 이슈는 해결됩니다.

    그런데 다운로드 진행률을 ProgressBar 클래스를 사용하지 않고 아이콘으로 표시되도록 구현했다면 해당 아이콘 뷰에 대체 텍스트 형태로 진행률을 표시해 주어야 합니다.

    문제는 대체 텍스트 형태로 구현을 하면 사용자가 해당 아이콘 뷰에 포커스 했을 때 현재 퍼센트는 알 수 있지만 변경되는 퍼센트를 바로바로 확인할 수 없습니다.

    announceForAccessibility 메소드를 사용하는 것이 해결책이 될 수 있지만 그럴 경우 사용자가 해당 아이콘에 포커스 하고 있지 않을 때에도 계속 변경되는 퍼센트를 읽어 이 또한 정신 없게 만드는 원인이 됩니다.

    따라서 좀더 사용성을 고려했을 때 초점이 퍼센트 아이콘에 가 있을 때에만 업데이트 되는 퍼센트 정보를 읽게 하는 것이 접근성을 높이는 방법 중 하나입니다.

    이때 사용할 수 있는 조건문이 isAccessibilityFocused() 입니다.

    즉 해당 아이콘 뷰가 접근성 초점을 받고 있다면 무엇인가를 하라는 명령어 입니다.

    따라서 우리는 해당 조건문 안에서만 announceForAccessibility 메소드를 통해 업데이트 되는 퍼센트 정보를 읽게 할 수 있으며 사용자가 초점을 다른 곳으로 이동하면 해당 정보는 읽지 않게 됩니다..

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 커스텀 액션 이용하기
    Webacc NV 2021-03-03 18:18:01

    지난번에는 몇 차례에 걸쳐 안드로이드에서의 접근성 커스텀 액션에 대해 살폈습니다.

    이번에는 iOS 에서의 커스텀 액션 사용방법과 구현 방법에 대해 함께 살펴보고자 합니다.

    우선 커스텀 액션에 대한 개념은 안드로이드와 같으므로 여기서는 설명하지 않습니다.

    오늘 함께 살펴보고자 하는 것은 iOS에서는 커스텀 액션을 어떤 식으로 접근할 수 있는가 입니다.

    개발자가 커스텀 액션을 구현한 요소 혹은 시스템 자체적으로 커스텀 액션을 가진 요소에 포커스를 맞추면 보이스오버의 로터가 동작 로터로 자동 변경됩니다.

    로터란 보이스오버를 컨트롤할 수 있는 여러 옵션으로 한 글자씩 읽기, 단어 단위로 읽기, 음성 속도 변경하기 등이 포함됩니다.

    로터는 두 손가락 시계 혹은 시계 반대방향 돌리기를 통해서 다른 로터로 변경하고 한 손가락 위 또는 아래 쓸기를 통해 옵션을 변경할 수 있는데 예를 들어 사용자가 음성 속도 로터로 변경했다 하더라도 커스텀 액션을 가진 콘텐츠에 위치하면 한 손가락 위 또는 아래 쓸기 옵션이 커스텀 액션으로 변경됩니다.

    커스텀 액션이 있는 요소에 포커스 하면 상세정도에서 동작 피드백 설정을 어떻게 했느냐에 따라 음성으로 동작이 있음을 알려주거나 사운드로 알려주거나 아무런 피드백도 하지 않습니다.

    한 손가락 위 또는 아래 쓸기로 실행할 액션을 선택하고 이중탭하면 그 요소가 자체적으로 가지고 있는 클릭 이벤트가 아닌, 커스텀 액션으로 구현한 이벤트가 실행됩니다.

    다음 팁에서는 커스텀 액션 구현 방법에 대해 설명하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android Web] TalkBack에서의 aria-current 속성 지원 소식 관련
    Webacc NV 2021-02-25 09:48:36

    웹에서 aria-current는 탭 컨트롤을 제외한 내비게이션 메뉴, 회원가입과 같은 곳에서의 현재 단계를 표시할 때 사용하는 속성으로 여러 요소 중 어떤 요소가 선택된 요소인지를 알릴 때 사용합니다.

    그런데 모바일 웹에서 톡백으로는 해당 속성이 지원되지 않아 aria-current 속성을 넣어 준다 하더라도 안드로이드 스크린 리더 사용자는 어떤 요소가 선택된 요소인지 알 수 없었습니다.

    그런데 이번에 구글에서 이 문제를 수정해 주었습니다.

    해당 이슈는 톡백 문제라기보다는 크롬 웹뷰에서 aria-current 속성을 톡백에게 제대로 전달하지 못해 발생한 문제로 현재는 크롬 카나리에서 수정된 버전을 체험해 볼 수 있습니다.

    따라서 약 두어 달 정도 지나면 정식 버전에도 적용되지 않을까 예상해 봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 안드로이드 11에서의 RatingBar 접근성 지원 업데이트
    Webacc NV 2021-02-23 09:28:14

    예전에 이곳 팁을 통하여 RatingBar 클래스의 접근성 구현에 대해 다룬 적이 있습니다.

    RatingBar 는 평점 데이터를 받을 때 사용하는 클래스로 일반적으로 리뷰 작성 화면에서 어느 정도의 만족도를 줄 것인지를 물어볼 때 흔히 사용합니다.

    그런데 안드로이드 10 버전까지는 접근성 구현을 하지 않으면 평점을 올리거나 내리는 것을 드래그 형식으로 제스처를 해야 했기 때문에 스크린 리더 사용자가 조작하기가 어려워 이를 대응할 수 있는 방법을 설명했었습니다.

    그러나 안드로이드 11에서는 RatingBar 구현 시 시스템 자체적으로 볼륨키를 이용하여 평점을 올리거나 내릴 수 있도록 수정되었습니다.

    따라서 AccessibilityNodeInfo 클래스 변경은 안드로이드 10 이하에서만 적용하면 됩니다.

    다만 각 퍼센트별 1점, 별 한 개 등의 점수는 contentDescription 을 통하여 마크업해 주는 것이 좋습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] frequentlyUpdated trait 특징
    Webacc NV 2021-02-22 10:13:11

    각 플랫폼마다 특정 영역의 업데이트 되는 텍스트를 스크린 리더가 자동으로 읽어주도록 하는 속성들을 제공하고 있습니다.

    웹은 우리가 너무나 잘 알고 있는 aria-live 이며 안드로이드 역시 accessibilityLiveRegion 입니다. 

    iOS에서는 frequentlyUpdated accessibilityTrait 속성이 이와 비슷하다고 할 수 있습니다.

    그러나 이 속성은 웹, 안드로이드에서 말하는 라이브리전과는 차이가 있습니다.

    웹, 안드로이드의 라이브리전은 초점과 상관 없이 개발자가 지정해 놓은 영역의 콘텐츠가 업데이트 되면 자동으로 읽어주는 반면 iOS의 frequentlyUpdated 는 지정된 요소에 초점을 유지하고 있을 때만 업데이트 되는 콘텐츠를 읽어주도록 합니다.

    따라서 초점 받은 객체와 상관 없이 웹이나 안드로이드처럼 업데이트 되는 콘텐츠를 읽도록 하려면 AccessibilityAnnouncement 를 사용해야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] adjustable trait 사용 시에는 업데이트 값을 accessibilityValue로 마크업
    Webacc NV 2021-02-19 12:45:45

    iOS 에는 안드로이드와 달리 대체 텍스트에 해당하는 accessibilityLabel 외에 accessibilityValue가 있습니다.

    말 그대로 레이블에 대한 값이 있을 경우에는 이를 대체 텍스트 안에 넣지 않고 밸류 안에 넣도록 권고하고 있는 것입니다.

    특히 커스텀으로 슬라이더를 만들어서 접근성 구현 시에는 변경되는 값은 반드시 accessibilityValue 값으로 넣어 주어야 합니다.

    그렇게 하지 않고 대체 텍스트 안에 다 넣으면 사용자가 슬라이더를 업데이트 할 때 업데이트 되는 정보를 읽어주지 못합니다.

    예를 들어보겠습니다.

    뮤직 플레이어에서 재생 구간 조절 슬라이더를 UISlider를 사용하지 않고 커스텀 클래스로 슬라이더를 구현했다고 가정해 봅시다.

    그러면 이 슬라이더의 접근성 구현을 위해 대략적으로 다음과 같은 작업들이 필요할 것입니다.

    1. 보이스오버 사용자가 한 손가락 위 또는 아래 쓸기를 통해 재생 구간 이동을 할 수 있도록 하기 위하여 해당 요소의 accessibilityTrait 를 .adjustable로 설정.

    2. 한 손가락 위로 쓸기 혹은 아래로 쓸기를 할 때의 이벤트를 구현하기 위해 accessibilityIncrement, accessibilityDecrement 구현.

    3. 해당 요소에 재생 구간 이라는 레이블과 현재 시간을 나타내는 밸류 값 적용.

    3번에서 재생구간의 값을 나타내는 시간을 대체 텍스트로만 제공하면 보이스오버 사용자가 한 손가락 위 또는 아래 쓸기를 했을 때 변경되는 값을 실시간으로 들을 수 없게 됩니다.

    이는 슬라이더에서 값이 변경될 때는 accessibilityValue 정보만 가지고 와서 실시간으로 음성 출력하기 때문입니다.

    따라서 값에 해당하는 정보는 accessibilityValue 안에 넣어 주는 것이 필요합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] playSound trait 기능에 관하여
    Webacc NV 2021-02-18 11:41:53

    오랜만에 iOS 접근성 API 팁을 공유합니다.

    보이스오버에서는 사용자가 보이스오버 사운드를 오프로 설정하지 않는 이상 특정 요소를 이중탭하면 보이스오버 클릭 사운드를 출력합니다.

    이는 해당 요소가 가지고 있는 이벤트 실행과는 별개로 보이스오버는 해당 요소에 대한 클릭 이벤트를 수행했다는 알림을 제공해 주는 피드백입니다.

    그런데 음성 검색 화면에서 음성 듣기를 누를 때 자체 사운드 피드백을 가지고 있다고 가정해 봅시다.

    그러면 사용자가 음성 듣기를 이중탭하면 보이스오버 클릭 사운드, 음성 듣기 시작 사운드가 동시에 출력될 것입니다.

    위의 예시와 같이 특정 객체를 이중탭할 때 자체 사운드를 가지고 있어서 보이스오버의 클릭 사운드를 필요로 하지 않는 경우에는 .playSound accessibilityTrait 을 사용할 수 있습니다.

    자체 사운드를 가지고 있는 요소에 accessibilityTraits = [.button,.playSound] 와 같이 적용하면 그 요소를 이중탭할 때는 보이스오버 클릭 사운드가 출력되지 않게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 안드로이드 11에서의 ProgressBar 접근성 지원 업데이트
    Webacc NV 2021-02-17 10:47:22

    안드로이드에서 프로그레스바 클래스 사용시 화면에 표시되는 퍼센트에 대한 대체 텍스트를 추가 하지 않으면 톡백에서 퍼센트 정보를 읽지 못한다는 팁을 공유한 적이 있습니다.

    그런데 안드로이드 11에서는 ProgressBar 클래스에 대한 접근성 지원이 업데이트 되어서 대체 텍스트가 없어도 퍼센트 요소에 포커스 하면 현재 표시된 퍼센트를 음성으로 알려줄 수 있도록 수정되었습니다.

    따라서 ProgressBar 요소에 대한 퍼센트 정보 대체 텍스트는 안드로이드 10 이하 버전에서만 삽입되도록 다음 예시와 같이 구현할 수 있겠습니다.

                    if (Build.VERSION.SDK_INT < 30) {
                        pgsBar.setContentDescription("0%");
                    }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 액션 외에 톡백에서 자동으로 상황에 맞는 메뉴를 추가하는 경우 리스트 정리
    Webacc NV 2021-02-16 10:10:49

    3회에 걸쳐 안드로이드에서의 접근성 커스텀 액션에 대해 살펴보았습니다.

    그런데 커스텀 액션을 리서치 하다보면 한 가지 질문이 제기될 수 있습니다.

    커스텀 액션 외에 특정 컨트롤에서 톡백의 로컬 컨텍스트 메뉴를 열면(갤럭시 안드로이드 11에서는 음성안내지원메뉴로 통합) 이전 페이지, 다음 페이지, 수정 옵션 등의 상황에 맞는 옵션이 표시되는 경우들이 있는데 이것은 접근성 구현을 별도 한 것인가 입니다.

    정담은 그렇지 않습니다.

    커스텀 액션은 접근성 구현을 별도로 한 것이지만 다음의 레이아웃 혹은 컨트롤을 사용하면 해당 컨트롤에 대한 상황에 맞는 메뉴를 접근성 서비스에서 지원해 줍니다.

    1. ViewPager를 사용하여 여러 페이지 구현: 이전 페이지, 다음 페이지로 가기 메뉴를 표시해 줍니다.

    2. URLSpan 혹은 clickableSpan: 해당 속성에는 링크 URL이 들어가게 되는데 웹 브라우저로 연결할 수 있는 링크 열기를 실행해 줍니다.

    3. EditText: 복사, 잘라내기, 붙여 넣기와 같은 편집 관련 수정 옵션을 표시해 줍니다. 단 EditText에 contentDescription을 사용하면 해당 메뉴는 표시되지 않습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] TabLayout 접근성 지원 업데이트 관련
    Webacc NV 2021-02-15 09:26:11

    iOS는 TabBar를 통해, 안드로이드는 TabLayout을 통해 탭을 구현하는 경우가 많습니다.

    그런데 안드로이드는 네이티브 탭레이아웃을 사용하더라도 톡백이 각 요소를 탭이라고 읽어주지 않아 접근성 구현이 추가적으로 필요했습니다.

    한 가지 기쁜 소식은 탭 레이아웃을 추가할 때 최신 안드로이드 라이브러리를 사용하면 접근성 구현을 별도로 하지 않더라도 톡백에서 각 탭의 요소 유형을 탭이라고 읽어주며 현재 포커스 하고 있는 탭이 총 몇 번째 탭 중 몇 번째 탭인지도 함께 알려줍니다.

    즉 모듈 라이브러리에 다음과 같이 material 버전이 1.2.1 이상이어야 합니다.

    implementation 'com.google.android.material:material:1.2.1'

    따라서 접근성 테스트 시에 탭레이아웃을 사용했음에도 탭이라고 읽어주지 않는다면 모듈의 material 버전을 확인하고 업데이트해 보시기 바랍니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 액션 적용 예제
    Webacc NV 2021-02-10 18:11:21

    두 번의 팁에 걸쳐서 커스텀 액션의 필요성과 커스텀 액션을 실행하는 방법에 대해서 살펴보았습니다.

    오늘은 커스텀 액션을 직접 만들어보는 예제를 공유하려고 합니다.

    1. res > value > actions.xml 파일을 만듭니다. 

    2. xml 안에는 다음 예시와 같이 각 액션의 네임을 지정해 줍니다. 이것은 AccessibilityNodeInfo 객체 내에 AccessibilityAction 으로 추가될 이름입니다.

       <item name="action_move_up" type="id"/>

     

    3. 액션을 만들고자 하는 객체에 다음 예시와 같이 AccessibilityNodeInfo 객체를 만들고 그 객체 안에서 xml에서 만든 액션을 추가합니다.

    ViewCompat.setAccessibilityDelegate(contentText, object :AccessibilityDelegateCompat(){
                   override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfoCompat?) {
                       super.onInitializeAccessibilityNodeInfo(host, info)
                       info?.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat(R.id.action_delete, "삭제"))
                       info?.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat(R.id.action_move_up, "위로 이동"))
    }            

     

    참고: 액션에 대한 글자가 좋아요, 좋아요 취소와 같이 상황에 따라 변경되는 경우에는 likeAction 과 같이 String 객체를 만들어 상황에 맞게 대입합니다.

    4. 마지막으로 만들어 놓은 각 액션을 사용자가 실행하면 무엇이 동작되게 할 것인지를 정의해야 하므로 다음 예시와 같이 performAccessibilityAction 메소드를 사용하여 각 액션에 따른 동작을 정의합니다.

                   override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
                       when(action){
                           R.id.action_delete -> {
                               itemDeleteListener?.onItemDelete(adapterPosition)
                           }
                           else -> {
                               return super.performAccessibilityAction(host, action, args)
                           }

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 액션 실행 방법
    Webacc NV 2021-02-09 09:30:53

    커스텀 액션을 적용하는 기술에 대해 알아보기 전에 우선 톡백에서 커스텀 액션을 실행하는 방법부터 먼저 설명하겠습니다.

    커스텀 액션은 톡백에서 작업 이라는 용어로 번역되어 있습니다.

    따라서 특정 객체에 포커스 했을 때 그 객체가 커스텀 액션을 가지고 있는 경우에는 톡백에서 '작업 사용가능, 위쪽 오른쪽 스와이프 동작으로 보기'라는 힌트 메시지를 출력해 줍니다.

    그러면 해당 객체에는 이중탭하여 실행할 수 있는 기본 동작 외에 이전 팁에서 설명한 것과 같이 톡백과 같은 접근성 서비스에서 실행할 수 있는 추가 작업들이 존재한다는 이야기가 됩니다.

    따라서 톡백의 안내와 같이 위쪽 오른쪽 연속 쓸기 동작을 하면 로컬 컨텍스트가 열리고 그 안에서 작업을 선택하면 개발 시 적용한 커스텀 액션 목록이 출력되게 됩니다.

    참고: 

    1. 조만간 널리 아티클을 통해 다루겠지만 안드로이드 11버전이 탑재된 갤럭시에 포함된 톡백에서는 로컬 컨텍스트메뉴와 글로벌 컨텍스트메뉴가 음성안내지원메뉴로 통합되었으므로 작업 역시 해당 메뉴에서 호출할 수 있습니다.

    2. 톡백 설정, 즉 음성안내지원설정에서 맞춤동작설정에 들어가면 커스텀 액션을 호출하는 제스처를 별도로 할당할 수도 있습니다.

    이렇게 하면 굳이 톡백 메뉴를 거치지 않고 작업 리스트를 바로 호출할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 액션 활용에 관하여
    Webacc NV 2021-02-05 18:39:06

    앞으로 몇 차례에 걸쳐서 커스텀 액션에 대해 다루어 보려고 합니다.

    커스텀 액션은 접근성 서비스에서만 클릭할 수 있는 액션을 별도로 만드는 것을 말합니다.

    커스텀 액션을 만드는 이유는 두 가지가 있습니다.

    1. 한 콘텐츠에 대해 반복되는 여러 하위 버튼들이 많을 때: 팟캐스트 리스트가 있다고 생각해 봅시다. 

    각 팟캐스트를 누르면 콘텐츠가 재생됩니다. 그런데 각 팟캐스트마다 좋아요, 공유, 댓글 달기, 리스트에서 삭제, 폰으로 다운로드와 같은 객체들이 있다고 생각해 봅시다.

    그러면 스크린 리더 사용자가 10개의 팟캐스트를 탐색하려면 한 손가락 옆으로 쓸기를 몇 번 해야 할까요?

    2. 드래그 & 드롭과 같이 기존 제스처 사용으로는 수행하기 어려운 객체인 경우: 아이템의 순서를 이동하는 등의 드래그 & 드롭 기능은 스크린 리더 사용자가 수행하기 매우 어렵습니다.

    그렇다고 화면 상에 각 요소마다 위로 이동, 아래로 이동이라는 버튼을 두는 것은 모바일이라는 환경을 고려할 때 디자인 배치가 쉽지 않습니다.

    다음 팁에서는 커스텀 액션을 사용하기 위한 톡백 환경설정에 대해 다루겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] accessibilityElementsHidden 메소드 사용에 관하여
    Webacc NV 2021-01-28 10:02:47

    예전에 iOS 네이티브에서 accessibilityViewIsModal 에 대해 소개한 적이 있습니다.

    딤드 레이어를 구현할 때 해당 속성을 사용하면 선언된 컨테이너와 같은 계층에 있는 다른 컨테이너 내의 요소들은 보이스오버에서 초점을 받지 않도록 할 수 있어 접근성 구현 시 자주 사용됩니다.

    그런데 레이어가 하나의 컨테이너만 포함하지 않는 경우에는 이 속성을 사용할 수 없습니다.

    레이어가 열렸을 때 레이어 컨테이너와 닫기 컨테이너가 분리되어 있을 경우가 대표적인 예라 하겠습니다.

    이 때 사용할 수 있는 속성이 accessibilityElementsHidden 입니다.

    이 속성은 웹에서 aria-hidden 속성과 비슷합니다.

    즉 해당 속성을 주면 그 속성이 적용된 하위 모든 컨테이너의 초점을 숨깁니다.

    따라서 레이어가 여러 컨테이너에 걸쳐 있을 경우에는 accessibilityElementsHidden을 사용하여 레이어가 관련이 없는 모든 컨테이너를 보이스오버 초점에서 제외할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML] overflow hidden으로 화면의 콘텐츠를 숨기는 경우
    Webacc NV 2021-01-27 09:36:38

    웹페이지에서 상황에 따라 화면에서 콘텐츠를 숨기는 방법은 여러 갖가 있습니다.

    visibility hidden, display none, HTML hidden 태그 등을 사용하는 경우에는 별도 접근성 대응을 하지 않아도 숨겨진 콘텐츠는 스크린 리더에서도 읽지 않습니다.

    그러나 overflow hidden으로 콘텐츠를 숨기는 경우에는 화면에서는 해당 콘텐츠가 보이지 않지만 스크린 리더에서는 화면에서는 보이지 않는 콘텐츠를 읽을 수 있는 문제가 있습니다.

    따라서 overflow hidden을 사용하여 특정 콘텐츠를 보여주거나 숨기는 경우에는 이에 대한 접근성 대응이 필요합니다.

    1. 숨겨진 콘텐츠가 키보드 초점을 받지 않는 텍스트 콘텐만 존재하는 것우에는 aria-hidden true 속성을 사용합니다.

    2. 숨겨진 콘텐츠가 텍스트가 아닌 링크와 같은 초점을 받는 객체인 경우에는 aria-hidden true 속성과 함께 tabindex -1 속성을 함께 적용합니다.

    물론 화면에서 콘텐츠가 다시 보여지는 경우에는 이러한 속성들은 false가 되어야 할 것입니다.

    아래는 텍스트 콘텐츠가 overflow hidden 으로 숨겨진 콘텐츠 접근성 적용과 관련된 예시 입니다.

    스크린 리더를 실행한 상태에서 테스트 해 보시기 바랍니다.

    overflow hidden 접근성 적용 예제

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] isTextEntryKey 적용 예제
    Webacc NV 2021-01-26 09:28:20

    지난 시간에 이어서 오늘은 각각의 버튼으로 된 커스텀 키패드가 있다고 가정하고 해당 키패드를 이중탭이 아닌 손가락을 떼면 바로 입력이 되게 하는 예제를 공유하도록 하겠습니다.

    키보드는 그리드 레이아웃 매니저를 이용하였고 keyButton 객체로 키보드를 구성했다고 가정합니다.

                keyButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
                    override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
                        super.onInitializeAccessibilityNodeInfo(host, info)
                        info?.isTextEntryKey = true
                    }
                }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] isTextEntryKey 접근성 메소드 소개
    Webacc NV 2021-01-22 15:02:36

    안드로이드 플랫폼에서는 기본적으로 톡백에서 키보드를 입력할 때 입력하고자 하는 글자에 포커스 하고 손가락을 떼면 바로 입력이 되는 방식을 취하고 있습니다.

    iOS 플랫폼에서는 보이스오버에서 키보드 입력 시 손가락을 떼면 바로 입력이 되게 할 것인지 아니면 일반적인 접근성 제스처와 같이 두 번 탭을 해야 입력이 되게 할 것인지를 사용자가 설정할 수 있습니다.

    이처럼 플랫폼별 접근성 지원에서의 특징을 우리는 이해할 필요가 있습니다.

    그런데 안드로이드의 경우 송금을 하기 위한 금액 입력 키패드나 인증서 비밀번호 입력을 위한 키패드를 커스텀 키보드로 구현하는 경우가 종종 있습니다.

    이러한 커스텀 키보드는 버튼으로 만들어지는 경우도 있고 구현하는 방법은 다양합니다.

    문제는 이러한 커스텀 키보드 사용시에는 사용자가 일일이 각각의 키에서 이중탭을 해야 하는 번거로움이 있다는 것입니다.

    만약 톡백에게 이것이 키보드야 라고 알려줄 수 있다면 일반 키보드처럼 특정 키에서 손가락을 떼면 바로 입력이 되도록 톡백이 처리를 해줄 것입니다.

    이때 사용할 수 있는 메소드가 바로 isTextEntryKey() 입니다.

    이 메소드를 적용하면 톡백은 다음과 같이 처리를 해줍니다.

    1. 요소 유형을 읽지 않습니다. 

    즉 버튼으로 키보드를 만들었다면 각 숫자를 버튼이라고 읽어줄 것입니다. 

    그러나 키보드라는 것을 톡백에게 알려주면 버튼이라는 요소 유형 정보를 읽지 않습니다.

    2. 활성화 하려면 이중탭하세요 라는 힌트 메시지를 출력하지 않습니다.

    3. 키보드에서 손가락을 떼면 바로 입력이 됩니다.

    다음 팁에서는 구체적으로 적용하는 예시에 대해 살펴보겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] screenReader focusable 사용 예시와 사용 시 주의사항
    Webacc NV 2021-01-07 10:03:36

    지난 팁에서는 screenReaderFocusable 사용의 필요성에 대해 함께 생각해 보았습니다.

    오늘은 해당 속성 사용 방법과 유의할 점에 대해 생각해 보겠습니다.

    1. screenReaderFocusable true 속성을 사용하면 해당 컨테이너 하위의 모든 view 들이 하나의 초점으로 제공되므로 하위의 모든 view를 하나의 초점으로 합치는 것에 문제가 없을 경우에만 사용해야 합니다.

    2. screenReaderFocusable 속성을 사용하는 컨테이너(ConstraintLayout, LinearLayout 등)에 importantForAccessibility YES 로 마크업을 해야 합니다. 이는 기본적으로 해당 레이아웃들은 접근성 API 에서 무시를 하기 때문에 해당 속성이 없으면 screenReaderFocusable 속성이 있어도 하위 뷰들의 초점이 하나로 합쳐지지 않습니다.

    다만 해당 컨테이너 레이아웃에 contentDescription, 즉 대체 텍스트를 마크업하는 경우에는 importantForAccessibility YES 속성은 없어도 됩니다. 다만 이렇게 대체 텍스트와 screenReaderFocusable true 속성을 함께 주면 하위의 모든 뷰들을 읽는 것이 아니라 해당 컨테이너의 대체 텍스트만 읽게 됩니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML 접근성 기초 & 해외 아티클 번역] 문서와 콘텐츠의 언어 (2/2)
    Webacc NV 2021-01-06 14:04:29

    [지난글] [HTML 접근성 기초 & 해외 아티클 번역] 문서와 콘텐츠의 언어 (1/2)

    [원문 링크] WebAim: Document and Content Language

    지난 글에 이어 "문서와 콘텐츠의 언어" 아티클의 번역 2부를 올립니다.

     

    적절한 언어 태그 고르기

    국제 규격에 의해 정의된 8000개 이상의 언어 코드는 다양한 언어, 방언, 지역 등을 포함합니다. IANA(인터넷 할당 번호 관리 기관: Internet Assigned Numbers Authority)는 가능한 유효한 값의 정규 레지스트리를 제공합니다. W3C에서는 언어 태그를 고르기 위한 훌륭한 가이드를 제공합니다. 또한, 이 언어 하위 태그 조회 도구를 사용하여 유효한 lang 속성 값에 대해 검색할 수 있습니다. 사용된 언어 값이 접근성을 가장 잘 지원하는지 확인하려면 아래 지침을 반드시 읽으십시오.

     

    기본 언어

    기본언어는 웹 콘텐츠의 주요 언어입니다. 거의 모든 기본 언어는  두 자리 코드로 사용할 수 있습니다. 예를들자면, lang="en"은 영어, lang="de"는 독일어, lang="zh"는 중국어, 그리고 lang="ar"은 아랍어입니다.

    lang 속성 값은 가능한 짧게 유지합니다. 두 자리 기본 언어 코드가 콘텐츠 언어를 식별하기에 충분하다면, 해당 코드를 사용하세요.

     

    많은 기본 언어는 다른 보조 언어나 방언을 가지고 있습니다. 영어는 예를들어 영국, 호주 그리고 인도와 같은 변형된 언어를 가지고 있습니다. 중국어는 표준 중국어(Mandarin)과 광동어(Cantonese), 수 없이 많은 다른 언어 또는 방언이 있으며, 그 중 일부는 서로 알아들을 수 없습니다. 그럼에도 불구하고 웹페이지에서 기본 언어를 지정하는 것 만으로도 충분히 이를 지원할 수 있습니다.

     

    간단히 말해서, 대부분의 페이지 콘텐츠에 대한 언어는 적절한 자리 기본 언어 코드로 식별될 있습니다. 아주 드믈게 기본 언어 코드에 세 자리 코드를 사용할 수 있지만, 두 자리 코드가 없는 경우에만 사용할 수 있습니다.

     

    ISO 표준에서 일부 기본 언어에 대해 세 자리 코드를 정의할 수 있지만(예: 스페인어 "spa") 이러한 코드는 IANA 레지스트리에서 찾을 수 없으며, 스크린 리더에서 이러한 코드에 대한 지원이 매우 미흡합니다.

     

    확장 언어

    보조 언어나 확장 언어는 아랍어(ar), 중국어(zh), 말레이어(ms), 스와힐리어(sw), 우즈베크어(uz), 콘칸어(konkani), 그리고 수화 언어(sgn) 일곱 가지의 기본 언어에서 사용됩니다. 예를들어 광동어와 중국 표준어는 중국어(zh)의 확장 언어들입니다. 이는 광동어를 "zh-you", 중국 표준어인 lang="zh-cmn" 또는 세 자리 언어 식별코드, "you"와 "cmn"과 같이 같이 명시할 수 있게 합니다.

    스크린리더의 확장 언어에 대한 지원은 매우 미흡합니다.  위와 같이 기본 언어만을 사용하거나, 필요하다면 아래 섹션들을 참고하여 적절한 지역 하위 태그를 사용하세요.

     

    문자 하위 태그

    가끔, 언어의 기본 언어의 문자와 다른 문자를 사용하여 콘텐츠를 표현해야 하는 경우도 있습니다. 예를 들어서 '汉语'는 중국어 간자체로 "중국어"라는 단어입니다. 이것을 라틴어로 표기하면 "Hànyǔ"입니다. 'Hànyǔ'라는 단어는 lang="zh-Latn"을 사용하여 중국어로 식별되게 할 수 있습니다. 문자 하위태그는 항상 기본 언어 코드 뒤에 하이픈과 함께 네 자리 코드로 추가합니다.

     

    그런데, 스크린리더의 문자 하위 태그 지원이 미흡합니다. 그리고, 아주 드믈게 필요합니다.  스크린리더가 문자 식별자를 무시하고 기본 언어를 적용하는 경우가 많으며, 일반적으로 식별하는 것에 완전히 실패합니다. 예를들어 영어 페이지에서 <p lang="zh-Latn"> 'Hànyǔ'</p>는 "zh"라는 기본 언어 때문에 중국어로 간주될 수 있지만, 라틴어는 한자가 아니기 때문에 내용을 읽을 수 없을 겁니다(스크린리더는 라틴 알파벳이 아닌 한자를 예상합니다). 이러한 사례에서는 언어 속성의 값을 모두 생략하면 해당 페이지에 정의된 언어인 영어로 라틴 문자를 제대로 읽게 됩니다.

    간단히 말해서, 문자 하위태그 사용은 피하십시오.

     

    지역 하위 태그

    스페인어와 맥시코의 스페인어 차이를 강조하는 페이지와 같이 다양한 방언 또는 하위 언어로 콘텐츠를 구분해야 할 필요가 있거나, 지역적 차이가 뚜렷한 방언과 일치하는 서면 콘텐츠가 있다면 지역 하위 태그를 사용할 수 있습니다. 예를들어, lang="es-ES"는 맥시코에서 읽히는 스페인어를 나타내는 lang="es-MX"와 달리 스페인에서 일반적으로 사용되거나 읽히는 반도 스페인어를 식별합니다.

     

    만약 스크린리더가 스페인 반도 및 멕시코 스페인어에 해당하는 음성이 모두 설치되어있는 등, 지역적인 차이를 지원한다면 스크린리더에서 적절한 방언으로 전환하여 읽을 수 있습니다.

     

    그런데, 지역 하위 태그는 일반적으로 무시됩니다. 특히 스크린리더의 기본 설정 언어가 기본 언어과 일치하면 그렇습니다.

    이는 사용자가 동일한 언어의 다른 방언보다, 기본 방언을 더 선호하고, 더 잘 이해할 것으로 추정되기 때문입니다.

    예를 들어 영국의 수많은 스크린 리더 사용자들은 페이지가 lang="en-us"로 지정되어 있더라도 미국 웹 사이트에서 일반적으로 영국 영어 음성을 들을 수 있습니다.

     

    지역 하위 태그는 서로 다른 방언으로 내용을 구분해야 경우에만 사용해야 합니다. 중국 표준어와 광동어로 된 페이지(문자는 같지만 서로 이해할 수 없는 경우)는 일반적으로 각각 lang="zh-cn", lang="zh-hk"를 사용하여 콘텐츠를 차별화할 수 있습니다. 지역 하위 태그는 확장 언어 태그보다 훨씬 안정적으로 지원됩니다. 영어의 다양한 방언은 서로 이해할 수 있고, 스크린 리더 사용자가 선호하는 방언을 정의할 수 있기 때문에 영국, 호주, 미국, 또는 다른 영어 버전을 제공하는 사이트에서는 일반적으로 lang="en"을 사용하는 것 만으로도 충분합니다.

     

    짧은 요약

    허용되는 언어 속성 값에는 다른 변형이 있지만 위의 규칙은 대부분의 웹 페이지 내용에 적용됩니다.

    - 언어 식별자를 가능한 짧게 유지합니다. 대부분의 경우 자리 언어 식별자로 충분하고 최적입니다.

    - 일반적으로 자리 언어 식별자, 확장 언어 하위 태그 문자 하위 태그는 피해야 합니다.

    - 지역 하위 태그는 지역 방언과 기본 언어와  반드시 구별되어야 하는 상황에서 사용될 있습니다.

    스크린리더 지원

    일반적으로 두 글자 lang 속성값은 스크린리더 지원에 적합합니다. 세자리, 확장, 문자 및 지역 하위 태그에 대한 지원은 사용 중인 브라우저 및 스크린 리더 및 지원 및 설치된 언어 음성에 따라 다릅니다. 지원 여부가 의심스러우면 테스트해야 합니다.

    Span 또는 <img> 요소와 같은 인라인 요소에서의 언어변경 지원여부 또한 변수가 많습니다. 가능하면, p, blockquote같은 블록 형태의 요소에 lang 속성을 정의하는 것이 좋습니다.

    정의된 언어를 읽으려면 스크린리더가 해당 언어를 지원해야 합니다. 현대의 모든 스크린리더는 여러 언어를 지원합니다. 몇몇 스크린리더에서는 유저가 직접 언어와 언어팩을 설치하거나 설정해야만 합니다.

    스크린리더에서 일치하는 언어 음성이 설치되지 않았거나, 지원되지 않는 언어를 발견하면, 일반적으로 콘텐츠의 언어를 명시해 줍니다. 예를 들어, 스페인 어 언어 음성이 비활성 상태이거나 설치되지 않았다면 스크린 리더에서는 “스페인어”라고 음성으로 안내합니다.

     

    스크린 리더는 대부분 정의된 언어를 지원하지 않아도 발음이 가능한 콘텐츠 언어라면 읽기를 시도합니다. 폴란드어 컨텐츠를 예를 들자면, 폴란드어는 라틴 계열의 문자로 쓰이므로, 영어 기본 음성으로 설정된 스크린리더가 읽을 수 있습니다(폴란드어를 처음 배우는 초급반처럼 다소 부적절한 발음이나 억양등으로). 반면에 한자는 영어로 바로 발화할 수 없습니다. 그러므로 스크린리더는 중국어를 읽지 않을 것이지만, 사용자에게 중국어 콘텐츠가 있음을 알리기 위해 “중국어”라고 안내될 수 있습니다.

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] screenReaderFocusable 속성에 관하여
    Webacc NV 2021-01-05 09:45:18

    이름: 홍길동.

    나이: 30살.

    몸무게: 47kg.

    과 같은 화면이 있다고 가정해 봅시다.

    화면을 디자인하기에 따라 여러 가지의 상황이 나올 수 있겠으나 각각의 TextView를 분리하여 마크업하는 경우에는 스크린 리더 사용자 입장에서는 해당 콘텐츠를 읽는데 어려움이 있습니다.

    왜냐하면 각각의 TextView가 별도 마크업 되어 있으므로 위의 요소를 탐색하려면 한 손가락 오른쪽 쓸기를 6번 해야 하기 때문입니다.

    텍스트뷰를 감싸는 컨테이너 레이아웃 안에 해당 정보만 포함된 경우에는 이를 해결하는 방법으로 권장했던 것이 android:focusable true 속성이었습니다.

    이것은 웹으로 치면 상위 div 에 tbindex="0"을 주는 것과 같으며 이렇게 구현할 경우 키보드 포커스가 상위 컨테이너에 이동할 수 있기 때문에 하위의 모든 텍스트를 한꺼번에 읽을 수 있어 어느정도 해결방안으로 사용되었습니다.

    그러나 android:screenReaderFocusable API가 추가된 후부터는 해당 속성을 사용하는 것을 권장합니다.

    그 이유는 사실상 웹과 마찬가지로 안드로이드 역시 하드웨어 키보드로 탐색 시에는 탭키나 화살표키로 이동할 때는 실행 가능한 요소에만 이동이 되어야 하기 때문입니다.

    즉 focusable 속성을 사용할 경우 실행 가능하지 않은 요소에도 탭키로 이동 시 초점이 제공되므로 하드웨어 키보드 사용자 입장에서는 자연스러운 내비게이션 방식이 아닙니다.

    다음 팁에서는 screenReaderFocusable 사용 예제와 사용 시 주의 사항에 대해 다루겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 볼륨 버튼으로 이전, 다음 배너 넘기기 구현하기 코드 예시
    Webacc NV 2020-12-31 10:09:05

    오늘은 이전, 다음 배너를 볼륨 버튼으로 조작하도록 구현하는 코드 예시를 공유해 보고자 합니다.

    우선 배너 재생과 정지 버튼을 톡백이 켜졌을 때만 나타나게 하는 것에 대해서는 이미 다룬 적이 있으므로 생략합니다.

    그리고 배너 재생, 정지 버튼을 bannerButton 객체라고 가정하겠습니다.

    // bannerButton 을 톡백에서 SeekBar 로 읽도록 하기 위해 AccessibilityNodeInfo className 을 SeekBar 로 변경합니다.
    bannerButton.accessibilityDelegate =object : View.AccessibilityDelegate() {
                override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
                    super.onInitializeAccessibilityNodeInfo(host, info)
                    info?.className = SeekBar::class.java.name
    // 이제 톡백 사용자에게 해당 요소는 이중탭도 가능하지만 볼륨키 조절도 가능하다는 사실을 알리기 위해 아래 예시와 같이 tooltipText 속성을 추가해 줍니다.
                    info?.tooltipText = getString(R.string.bannerRolling)
                                                }
    // SeekBar 접근성 개선 팁에서 설명한 바와 같이 SeekBar 클래스네임을 적용하는 순간 볼륨키 위로(scrollForward), 아래로(scrollBackward) 이벤트를 사용하여 이에 대한 액션을 정의할 수 있습니다. 
                override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
                    if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
    // 다음 배너로 전환되는 메소드 구현.
    // 다음 배너의 텍스트를 읽어주도록 하기 위해 다음과 같이 announceForAccessibility 적용
                        bannerButton.announceForAccessibility(pagerList[currentPage])
                    }
                    else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
    // 이전 배너로 가는 메소드 구현
    // 이전 배너 내용을 톡백 사용자에게 알리기
                        bannerButton.announceForAccessibility(pagerList[currentPage])
                    }
                    return super.performAccessibilityAction(host, action, args)
                }
            }
    

     

    댓글을 작성하려면 해주세요.
  • tip
    [HTML 접근성 기초 & 해외 아티클 번역] 문서와 콘텐츠의 언어 (1/2)
    Webacc NV 2020-12-30 11:43:13

    [원문 링크]  WebAim: Document and Content Language

    언어 식별의 중요성

    스크린리더는 콘텐츠 언어가 식별되는 한, 다양한 언어를 말할 수 있습니다. 스크린리더가 정의된 언어를 지원하지 않거나, 발화할 수 없어도 사용자에게 해당 언어를 안내할 수 있습니다.

    문서의 언어를 정의하는 것은 Google 번역과 같이 콘텐츠 자동 번역 도구를 사용하는 것 또한 지원합니다.

    웹 콘텐츠 접근성 지침(WCAG)의 적합성 수준 A에 따르려면, 문서의 언어는 반드시 프로그래밍 방식으로 정의되어야 합니다. WCAG의 수준 AA을 준수하려면 현재 페이지의 주된 언어와는 다른 언어로 된 부분적인 영역 또한 콘텐츠 언어가 정의되어야 합니다. 이는 가능하다면 스크린리더가 해당 언어로 전환하여 말하게끔 합니다.

    페이지의 부분 언어(language of parts)를 명시하는 것은 문서의 기본 언어로는 일반적으로 이해하기 어려운 다른 언어의 내용에만 필요합니다. 예를들어, "Los Angeles"나 "piñata"는 영어 독자들이 이해하는 것에 어려움이 없는 스페인 단어이기 때문에, 영어 웹페이지에서 이 단어들을 스페인어로 식별할 필요가 없습니다.

    적절하게 콘텐츠 언어를 정의하는 것은 브라우저에서 "q" 태그를 사용했을 때, 큰 따옴표 부호를 다양한 언어에 맞게 표시하는 것을 허용합니다. 다음의 예는 독일어와 프랑스어로 정의된 것입니다. 브라우저가 언어에 맞게 현지화된 큰 따옴표 기호를 생성했습니다.

     큰 따옴표의 모양이 언어(lang)에 따라 변경된 모습, 위에는 독일어, 아래는 프랑스어

    언어가 지정된 경우 브라우저는 다음을 제공할 수 있습니다.

    • 비 라틴계 문자열에 적절한 문자를 제공합니다.
    • 현지화된 날짜 및 시간 입력서식을 제공합니다. (날짜:MM/DD/YYYY와 YYYY/MM/DD, DD/MM/YYYY, 시간: 24시간표기, 오후/오전 표기 등의 표기 형식)
    • 숫자를 표기할 때, 쉼표나 마침표 등 언어에 맞는 세 자릿수 구분자(Thousand Separator)를 제공합니다.
    • 입력 서식에서 언어에 맞는 철자 오류를 확인하여 알려줍니다.

    lang 속성

    lang 속성은 웹페이지의 언어를 식별하기 위해 하기 위해 사용합니다. 이 속성은 html 태그에 항상 추가해야 합니다. 이 속성에는 웹페이지의 자연어를 식별하는 값을 줍니다. <html lang="en">으로 예를 들자면 이 페이지는 "영문 페이지" 임을 명시하게 될 것입니다.

    비슷하게, 다른 HTML의 태그에도 해당 태그에 맞는 자연어를 lang 속성을 추가하여 표기할 수 있습니다. <p lang="ja">를 예로 들자면, 이 문단 태그는 일본어임을 표기하게 될 것입니다.

    연결되거나 탐색되는 태그에 언어를 지정하는 데에 "lang" 속성을 사용하지 마십시오. 영어 웹 페이지의 "스페인어로 번역" 링크 텍스트가 "Spanish"로 표시되있다면, 이것은 영어 단어이기 때문에 "lang" 속성을 사용하지 않습니다. 대신, 링크 텍스트가 "Espaøol"이라면 링크에 lang="es"가 정외되어야 합니다.

     

    내용이 길어서 1부는 "lang 속성" 섹션까지만을 다룹니다. 2부에서 "적절한 언어 태그 고르기" 섹션부터 다시 찾아 뵙도록 하겠습니다. 감사합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 볼륨 키로 이전, 다음 배너 넘기기 구현 방법
    Webacc NV 2020-12-30 09:53:44

    어제는 이전, 다음 배너 버튼을 별도로 구현하지 않고 배너 정지, 재생 하나의 버튼으로 배너 넘기기까지 구현할 수 있다는 것에 대해 살펴보았습니다.

    오늘은 구현 방법에 대해 다루어 보겠습니다.

    1. 볼륨키로 배너 넘기기 작업을 가능하게 하려면 반드시 해당 요소가 톡백에서 SeekBar 요소로 인식이 되어야 합니다.

    왜냐하면 SeekBar 로 인식이 될 때에만 볼륨키 조작에 대한 이벤트가 적용되기 대문이빈다.

    SeekBar, EditText 요소가 아닌 모든 요소에서의 볼륨키는 톡백이 실행되는 동안에는 항상 스마트폰 자체의 볼륨 조절로만 작동됩니다.

    물론 톡백에서 SeekBar 로 인식하도록 하기 위해 실질적으로 SeekBar 클래스를 사용하라는 것은 아니며 AccessibilityNodeInfo 객체 내의 클래스 네임을 SeekBar 로 변경해 주어야 한다는 의미입니다.

    2. 배너 재생, 정지 버튼은 버튼 클래스를 사용해서는 안 됩니다. 

    버튼 클래스를 사용할 경우 이미 클래스 자체에서 요소 유형을 가지기 때문에 AccessibilityNodeInfo 객체 내에서 클래스 네임을 변경할 수 없습니다.

    따라서 TextView 와 같은 뷰로 구현하고 버튼 스타일을 적용해 주어야 합니다.

    3. performAccessibilityAction 을 사용하여 접근성 서비스에서 볼륨키를 위 또는 아래로 조작했을 때의 이벤트를 구현하면 됩니다.

    다음 시간에는 구체적으로 코드를 통해서 적용 예시를 살펴보겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 이전, 다음 배너에 대한 접근성 적용에 대한 아이디어
    Webacc NV 2020-12-29 16:45:24

    널리 아티클을 통해서 네이티브 앱에 롤링 배너를 표시하는 경우 배너를 정지하거나 이전 혹은 다음 버내로 넘길 수 있는 기능이 없으면 스크린 리더 사용자는 롤링 배너 때문에 어려움을 겪는다는 부분에 대해 말씀드린 적이 있습니다.

    그리고 아티클을 통해 제시한 해결 방법은 톡백이 켜졌을 때를 탐지하여 톡백이 켜졌을 때만이라도 배너를 정지한 상태로 제공하고 이전, 다음 버튼을 두는 것에 대해 말씀을 드렸습니다.

    그런데 사실상 일반적으로는 스마트폰이라는 공간의 제약 때문에 롤링 배너에 대한 재생/정지, 이전, 다음 버튼을 두지 않는 것이 일반적입니다. 

    그런데 스크린 리더 사용자를 위한 대체 수단으로 버튼을 세 개 제공하는 것은 디자인의 고려가 많이 필요할 것입니다.

    따라서 세 개의 버튼을 다 제공하는 것이 좋겠지만 버튼을 한 개만 제공하고 해당 버튼으로 배너 일시정지, 배너 재생, 이전 배너, 다음 배너 기능을 다 수행할 수 있도록 한다면 디자인에 대한 고려가 훨씬 줄어들고 사용자의 조작도 간단하여 또 다른 해결 방법이 될 수 있습니다.

    대략적인 방법은 다음과 같습니다.

    1. 스크린 리더 사용자가 일반적으로 수행하는 이중탭 제스처로는 배너 재생, 정지를 토글합니다.

    2. 배너 재생/정지 버튼에 포커스 한 상태로 스마트폰의 볼륨키를 누르면 이전 혹은 다음 배너로 이동하고 해당 배너의 내용을 읽어줍니다.

    이에 대한 구현 방법에 대해서는 다음 팁에서 다루겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] AccessibilityNodeInfo setClassName 사용시 참고사항
    Webacc NV 2020-12-28 11:18:30

    널리 아티클 및 팁을 통해서 여러 번 말씀드린 바와 같이 컨트롤 요소 유형이 없는 view 를 톡백이 버튼, 라디오버튼과 같은 요소 유형으로 읽도록 하려면 뷰 자체를 다른 클래스로 변경하지 않더라도 setAccessibilityDelegate 객체를 만들어서 AccessibilityNodeInfo setClassName 을 Button.class.getName() 과 같이 구현할 수 있습니다.

    그런데 Button, SeekBar 와 같이 컨트롤 유형을 이미 가진 view 는 AccessibilityNodeInfo 의 클래스 네임을 변경하더라도 기존에 가진 속성이 변경되지 않습니다.

    즉 ImageView, TextView, LinearLayout 과 같이 뷰 자체에 컨트롤 유형이 들어가 있지 않은 경우에만 변경이 가능합니다.

    따라서 기존에 구현된 view 가 컨트롤 유형을 가지고 있으나 스크린 리더에서 읽어주는 요소 유형이 의미에 맞지 않아 다른 요소 유형으로 변경하고자 할 때는 view 자체를 다른 컨트롤로 변경해 주어야 합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 접근성 포커스 순서 지정하기 예시
    Webacc NV 2020-12-22 10:00:17

    2개의 텍스트뷰와 2개의 버튼뷰가 있다고 가정해 봅시다.

    두 텍스트 뷰는 위에 한 줄로 나란히 위치하며 텍스트는 각각 출발역, 도착역입니다.

    그 아랫줄에는 두 버튼이 있으며 텍스트는 각각 서울역, 동대구역입니다.

    우리는 톡백의 초점 순서를 출발역, 서울, 도착역, 동대구 순으로 재조정할 것입니다.

    1. 출발역, 도착역 텍스트뷰에 각각 id를 지정해야 합니다. 

    지난 팁에서 설명드렸듯이 포커스 순서를 조정할 때 id 기준으로 조정하기 때문입니다.

    여기서는 startStationLabel, destinationStationLabel 로 각각 지정하겠습니다.

    2. XML 레이아웃에서 서울, 동대구 두 버튼 뷰에 각각 다음과 같이 포커스 순서를 변경합니다.

            android:accessibilityTraversalAfter="@id/startStationLabel"
    android:accessibilityTraversalAfter="@id/destinationStationLabel"

    이렇게 하면 우리가 의도한 대로 포커스 순서가 조정됩니다.

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 초점 순서 변경하기
    Webacc NV 2020-12-21 09:54:44

    사용자가 한 손가락 오른쪽 쓸기를 통해서 콘텐츠를 탐색하면 기본적으로 왼쪽에서 오른쪽, 위에서 아래 순으로 접근성 초점이 이동됩니다.

    그러나 레이아웃 구조에 따라 위와 같은 방식으로 접근성 초점이 이동될 경우 스크린 리더 사용자가 레이아웃 파악이 어려운 경우가 있습니다.

    위에는 헤더 제목, 아래는 제목에 해당하는 클릭 요소가 있다고 가정해 봅시다. 

    예를 들면 위에는 출발역, 도착역 제목 텍스트가 왼쪽 오른쪽에 각각 표시되고 있으며 출발역, 도착역에 해당하는 요소, 서울, 동대구는 아래에 있습니다.

    그렇다면 포커스 순서를 조정해 주지 않으면 출발역, 도착역, 서울, 동대구 순으로 초점이 이동될 것이며 이것은 초점 순서가 논리적이지 않습니다.

    이때 사용할 수 있는 것이 accessibilityTraversalAfter 혹은 accessibilityTraversalBefore 입니다.

    before 혹은 after 뒤에는 순서를 조정하고자 하는 view의 id를 적어주면 됩니다.

    자세한 코드 예시는 다음 팁에서 설명하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native]앱에서 Talkback 사용여부 실시간으로 감지하기
    Webacc NV 2020-12-18 18:58:32

    Android에서는 사용자 디바이스가 Talkback 서비스를 사용중인지 확인할 수 있는 API를 제공합니다. Talkback을 감지하는 형태는 두 가지가 있습니다.

    1. 특정한 메소드가 실행될 때 서비스 값 검사(예: Activity의 onCreate 메소드나, 각종 사용자 지정 메소드)

    AccessibilityManager에는 Talkback 사용 여부를 bool값으로 반환하는 isTouchExplorationEnabled()가 있습니다. 이를 사용하여 스크린리더 사용 여부를 특정 코드 구간에서 수동으로 검사할 수 있습니다. onCreate 메소드의 setContentView 구문 다음에 사용하면 앱을 켰을 때 스크린리더가 켜져있는지 검사할 수 있습니다.

    class MainActivity AppCompatActivity{
        private AccessibilityManager A11yManager;
    
        @Override
    
        protected void onCreate(savedInstanceState){
          ...
          setContentView(R.layout.Activity_main);
          Context mainContext = getApplicationContext();// 서비스를 받아오기 위해 컨텍스트를 가져옵니다.
          A11yManager = (AccessibilityManager) mainContext.getSystemService(ACCESSIBILITY_SERVICE);//접근성 서비스를 가져옵니다.
          if(A11yManager.isTouchExplorationEnabled()){
            //Talkback이 켜져있으면 실행될 무언가를 작성합니다.
          }
        }
      }
    }

    2. 앱이 켜져있을 때 Talkback의 활성화 상태를 상시 감지하는 리스너 설치

    하지만, 시각장애인 사용자 모두가 Talkback을 계속 킨 상태로 쓰진 않습니다. 저시력 사용자는 Talkback을 자주 끄는 사람이 더러 있기 마련인데요. 만약, 앱을 켰을 때, Talkback이 꺼져있다면, Talkback용 앱 기능이 작동하지 않으니 사용자 입장에서 매우 아쉬울 수 있습니다.

    그럴때는 onTouchExplorationStateChangedListener를 사용할 수 있습니다.

    class MainActivity AppCompatActivity{
        private AccessibilityManager A11yManager;
    
        @Override
    
        protected void onCreate(savedInstanceState){
          ...
          setContentView(R.layout.Activity_main);
          Context mainContext = getApplicationContext();
          A11yManager = (AccessibilityManager) mainContext.getSystemService(ACCESSIBILITY_SERVICE);
          
          //앱이 켜졌을 때도 접근성 기능이 로드되게끔 위에서 설명한 대로 검사합니다.
          loadViewForTalkbackUsers(A11yManager.isTouchExplorationEnabled());
          
          //접근성 기능이 실시간으로 꺼지고 켜짐을 감지하기 위해 리스너를 등록합니다.
          A11yManager.onTouchExplorationStateChangedListener(new AccessibilityManager.onTouchExplorationStateChangedListener(){
    
             @Override
    
             public void onTouchExplorationStateChanged(boolean enabled){
                //리스너 콜백입니다. 콜백의 파라미터로 불 변수를 받고, 원하는 함수로 전달하여 기능을 구현하세요.
                LoadViewsForTalkbackUsers(enbned);
             }          
          });
        }
      }
      private loadViewsForTalkbackUsers(boolean condition){
        if(condition){
          // Talkback 켜짐이 감지되면 실행할 명령문을 작성
        }else
          // Talkback 꺼짐이 감지되면 실행할 명령문을 작성
        }
      }
    }

    다만, 주의할 점은, 리스너는 앱이 실행과 동시에 발생하지 않기 때문에 첫번째 방법과 함께 사용하여야 정상적인 작동이 가능합니다.

    만약 첫번째 방법을 같이 사용하지 않는다면, Talkback을 한 번 껐다 켜야 기능이 정상 작동되는 우스꽝스러운 현상이 생길 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] replaceAccessibilityAction 사용하여 활성화 하려면 이중탭하세요 힌트 바꾸기
    Webacc NV 2020-12-18 09:19:59

    얼마전에 몇 차례에 걸쳐 replaceAccessibilityAction 메소드의 용도와 사용 방법, 그리고 접근성 포커스가 갔을 때 편집창의 힌트 메시지를 변경하는 예제를 함께 살폈습니다.

    이번에는 이중탭할 수 있는 요소마다 톡백이 발화 하는 활성화 하려면 이중탭하세요 라는 힌트 메시지를 다른 것으로 변경하는 예제를 살펴보도록 하겠습니다.

    지난 번에 말씀드린 것처럼 replaceAccessibilityAction 안에는 적용하고자 하는 객체 이름과 접근성 액션, 메시지를 포함하는 액션의 경우 메시지, 그리고 액션을 수행했을 때 디폴트와 다르게 동작하도록 해야 할 때의 accessibilityViewCommand 가 들어가게 됩니다.

    여기서 우리가 변경할 것은 클릭에 관한 힌트 메시지이며 클릭 액션 자체는 변경하지 않을 것입니다.

    그럼 활성화 하려면 이중탭하세요 를 햄버거 주문 작업을 하려면 이중탭하세요 로 다음 예제를 통해 변경해 보도록 하겠습니다.

    ViewCompat.replaceAccessibilityAction(hamburgerOrderView, ACTION_CLICK, ("햄버거 주문"), null);

     

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 새로고침 제스처 구현 시에 버튼으로도 기능 실행을 할 수 있도록 고려해야 하는 이유
    Webacc NV 2020-12-14 09:35:14

    지난 주에는 콘텐츠 새로고침 구현 시에 스크린 리더 사용자가 이를 인지할 수 있도록 해야 하는 것에 대해 다루었습니다.

    오늘은 하드웨어 키보드 사용자 관점에서 조금 더 깊이 생각해 보려고 합니다.

    안드로이드는 윈도와 마찬가지로 블루투스 키보드와 같은 하드웨어 키보드를 이용하여 클릭 가능한 요소를 탭키나 화살표키로 이동하고 실행할 수 있습니다.

    또한 리스트뷰와 같은 스크롤이 가능한 콘텐츠에서 스크롤도 가능합니다.

    그래서 제스처 사용이 불편한 분들 중에는 키보드를 사용하여 앱을 사용하시는 분들도 있습니다.

    문제는 안드로이드에서 제공하는 RefreshLayout을 사용하는 경우에는 키보드를 통해서는 콘텐츠 새로고침을 할 수 없다는 것입니다.

    이를 해결하기 위해 새로고침이 구현된 화면에서는 새로고침을 키보드로도 수행할 수 있도록 버튼을 두는 것을 권장합니다.

    안드로이드 API 설명에 의하면 새로고침은 화면 전체를 새로고치는 기느을 하므로 단순히 새로고침 버튼을 두기보다는 화면 상단 오른쪽에 옵션더보기 메뉴(onCreateOptionsMenu)를 구현하고 그 팝업 메뉴 안에 새로고침 버튼을 두는 것을 권장하고 있습니다.

    하드웨어 키보드에서는 옵션 메뉴가 표시된 화면에서는 컨트롤 + ESC 키를 이용하여 해당 메뉴를 실행할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML - 해외 아티클 번역] placeholder는 레이블이 아닙니다.
    Webacc NV 2020-12-09 14:29:59

     

    [원문 출처] HTMHell - #24 A placeholder is not a label

    placeholder에 대한 오해와 잘못된 사용에 대해 다룬 해외 아티클을 번역하여 제공합니다.

    아래는 번역된 본문입니다. 다소 많은 의역이 있음을 참고해주시기 바랍니다.

    [나쁜 코드 예제]

    <input type="text" placeholder="First name">

    문제점 그리고 해결방안

    • 모든 입력 서식 요소는 레이블이 필요합니다. 스크린리더 사용자가 서식 영역을 접근할 때, 레이블이 서식 종류와 함깨 안내됩니다(출력 예시: <이름:성, 편집창>). 만약 레이블이 없다면 사용자가 무엇을 입력해야 하는지 모를 수 있습니다. (출력 예시: <편집창>).
    • 스크린리더에 따라서는 placeholder 속성을 레이블처럼 읽지만, 이것에 의존하는 것은 지양해야 합니다.
    • 보통 placeholder 글자는 낮은 명도대비의 밝은 회색으로 표시되기 때문에 저시력자나 강한 햇빛과 같은 환경에 있는 사람이 이 글자를 읽지 못할 수 있습니다.
    • ::placeholder 가상선택자를 사용하면 placeholder의 명도대비를 증가시킬 수 있지만, placeholder의 글자 명도대비가 너무 높으면자동으로 체워진 텍스트 값으로 착각할 수 있습니다.
    • <label> 요소를 사용, 표시하는 것은 입력 서식의 대상 크기를 키워주어 특히 터치로 조작하는 장치를 사용할 때 매우 큰 도움을 줄 수 있습니다.
    • 입력 서식에 오로지 placeholder만으로 레이블 기능을 대체한다면, 유저가 입력 서식에 내용을 입력했을 때 레이블이 사라지며, 특히 복잡하거나 가끔 사용되는 서식 모음에서 사용자로 하여금 단기 기억을 요구합니다.
    • 레이블이 없고, 입력값만 표시되는 입력 서식만이 보이기 떄문에 사용자는 서식을 제출하기 전에 어떤 입력 서식에 무엇을 입력했는지 알 수 없습니다.
    • 브라우저에서 자동으로 값을 체췄다면, 알맞는 편집창에 올바른 값이 들어갔는지 확인하기 위해 내용을 잘라내고 레이블을 확인한 후 다시 붙여넣어야합니다.
    • 만약, placeholder의 텍스트 길이가 입력 서식 영역보다 길다면 잘립니다.
    • 구글 번역과 같은 번역 도구에서 HTML의 속성값은 번역해주지 않을 수 있습니다.
    • 레이블은 입력 서식 위치와 겹치지 않고, 입력 서식 요소 전에 배치될 때 가장 잘 작동합니다.

    자세한 내용은 아래의 참고자료 섹션을 참고하시기 바랍니다(영문 페이지).

    [좋은 코드 예제]

     <label for="firstname">First name</label>
     <input type="text" id="firstname">

    [참고자료]

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 콘텐츠 새로고침 알림에 관하여
    Webacc NV 2020-12-09 09:21:59

    많은 안드로이드 앱에서 화면 새로고침 기능을 지원하고 있으며 대부분 한 손가락으로 화면을 아래로 당기면 해당 기능이 실행됩니다.

    톡백 사용자는 해당 기능을 두 손가락 아래로 쓸어내리기 제스처를 통해 수행할 수 있습니다.

    문제는 새로고침이 될 때 접근성 구현을 별도로 하지 않으면 스크린 리더 사용자는 콘텐츠가 새로 갱신되었다는 것을 전혀 알 수 없다는 것입니다.

    따라서 새로고침 동작이 일어난 경우에는 announceForAccessibility 메소드를 사용하여 이를 스크린 리더 사용자에게 알려 주어야 합니다.

    다음 예시를 참고합니다.

    
     

    mSwipeRefreshLayout = findViewById(R.id.swipeRefresh);
            mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    refreshList();
    mSwipeRefreshLayout.announceForAccessibility("콘텐츠 새로고침");
                }
            });

    댓글을 작성하려면 해주세요.
  • tip
    [NVDA & WEB] 가상커서(브라우즈모드) 상에서의 엔터 및 스페이스
    Webacc NV 2020-12-08 09:46:27

    지난 시간에는 센스리더에서 링크나 버튼과 같은 요소에서의 엔터, 스페이스가 어떤 이벤트로 동작되는지를 함께 살폈습니다.

    오늘은 NVDA에서의 동작 이벤트에 대해 생각해보겠습니다.

    결론부터 말씀드리면 NVDA는 가상커서(브라우즈모드) 상에서 버튼과 같은 요소에서 엔터나 스페이스를 누르면 모두 다 클릭 이벤트로 작동을 합니다.

    따라서 우리는 다음과 같은 이슈를 정리해볼 수 있습니다.

    1. 커스텀 버튼을 구현한 경우 스크린리더 가상커서를 활용하는 경우에는 키보드 접근성을 테스트 할 수 없습니다.

    즉 키보드 접근성을 테스트 하려면 가상커서나 브라우즈모드를 끄고 동작을 시켜 보아야 합니다.

    2. 특정 커스텀 요소가 클릭 이벤트가 아닌 마우스오버와 같은 동작으로 실행되는 경우에는 아무리 키보드 이벤트를 적용했다 하더라도 스크린 리더 사용자는 가상커서를 오프 하지 않는 이상 해당 요소를 실행할 수 없습니다.

    접근성 테스트 시에 참고가 되었으면 좋겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [센스리더] 인터넷에서의 커스텀 컨트롤 실행 관련
    Webacc NV 2020-11-30 10:06:02

    접근성 측면에서 될 수 있으면 항상 네이티브 컨트롤을 사용해야 하지만 어쩔 수 없이 커스텀 컨트롤을 사용하는 경우가 있습니다.

    예: <span role="button" tabindex="0">햄버거 선택하기</span>

    이러한 커스텀 버튼에 접근성을 적용하기 위해서는 키다운 이벤트를 통해서 키보드 접근성을 보장하는 것 또한 당연한 일입니다.

    그런데 센스리더에서 가상커서를 켠 상태로 이러한 커스텀 버튼에서 엔터를 누르면 키보드 이벤트가 실행될까요?

    아닙니다.

    센스리더에서 가상커서를 켠 상태로 엔터를 누르면 마우스 클릭 동작이 실행됩니다.

    따라서 센스리더에서 엔터를 눌러서 커스텀 요소가 실행이 된다고 해서 키보드 접근성을 보장한다 라고 할 수 없으며 정확한 테스트를 위해서는 반드시 CTRL + shift + f12를 눌러 가상커서를 해제한 다음 해당 요소를 테스트 해 보아야 합니다.

    다만 스페이스 키는 키보드 동작으로 실행이 됩니다.

    따라서 커스텀 버튼을 만든다고 가정할 때 키다운 이벤트를 걸어서 엔터와 스페이스로 실행할 수 있게 해 놓았다면 센스리더에서 가상커서를 켠 상태로 스페이스를 누르면 이 때는 키보드 이벤트가 실행되며 만약 스페이스에 대한 키보드 이벤트가 구현되지 않은 경우에는 실행이 안 되는 것입니다.

    다음 팁에서는 센스리더의 클릭 동작과 인터넷 익스플로러에서의 특징에 대해 살피겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Sense Reader and WAI-ARIA] aria-pressed 접근성 지원 관련
    Webacc NV 2020-11-25 18:40:35

    센스리더에서도 조금씩 WAI-ARIA 지원 속성이 늘어나고 있으며 최근에 대표적으로 추가된 속성이 role radio, role checkbox, aria-checked 등입니다.

    오늘은 aria-pressed 지원에 대해 잠시 살펴 보려고 합니다.

    aria-pressed 속성은 토글 버튼을 구현할 때 사용하며 눌려졌을 때와 누르지 않았을 때의 상태를 표시합니다.

    최근 센스리더에서는 aria-pressed 속성을 일부는 지원을 하여 aria-pressed true 일 때 선택이라는 상태 정보값을 음성 출력합니다.

    예를 들어 버튼 요소의 레이블이 음소거이고 aria-pressed true 이면 선택, 음소거 버튼 이 됩니다.

    하지만 false 일 때는 값을 전달하지 못합니다.

    센스리더의 WAI-ARIA 지원 범위를 궁금해 하시는 분들이 있어 팁으로 올려봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 음성검색화면 접근성 적용 예제
    Webacc NV 2020-11-23 10:12:56

    오늘은 지난 번 음성검색 접근성 구현 관련 설명을 보충하는 시간입니다.

    1. 먼저 음성 검색 화면으로 진입 시에 새로운 activity로 전환되는 경우에는 setTitle 값을 빈 값으로 두어 톡백이 액티비티 제목을 읽지 못하게 합니다.

    setTitle(" ");

    2. 음성검색 화면이 실행될 때 톡백이 그 어누 요소에도 포커스 되지 못하게 하기 위하여 다음 예시와 같이 모든 화면의 요소를 접근성 서비스에서 숨깁니다.

    ViewCompat.setImportantForAccessibility(getWindow().getDecorView(), viewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);

    3. 약 1초 뒤에 모든 숨긴 포커스는 원래대로 되돌립니다.

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            ViewCompat.setImportantForAccessibility(getWindow().getDecorView(), ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
        }
    }, 1000);
    

    4. 음성듣기가 완료되어 다른 activity로 갱신되지 않고 말을 이해하지 못했다는 등의 텍스트가 화면상으로 출력되는 경우에는 다음 예시와 같이 이를 톡백이 바로 읽을 수 있도록 처리합니다.

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mPlayer.start();
                    textView.setText(R.string.voiceNotUnderstand);
                    example1.setText(R.string.voiceListen);
                    textView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            example1.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }
    }, 1000);
                }
            }, 5000);

    위의 예시에서 접근성 이벤트가 두 개 들어간 것을 알 수 있습니다. 

    하나는 windowStateChanged 이벤트로 음성 듣기가 끝났을 때 텍스트 내용을 바로 읽을 수 있도록 한 것입니다.

    그리고 1초 정도 딜레이를 준 다음 viewFocused 이벤트를 통하여 듣기 버튼으로 초점을 보내주었습니다.

    위와 비슷한 방법으로 구현하면 스크린 리더 사용자가 톡백을 실행한 상태에서도 음성 검색을 무난하게 수행할 수 있을 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [모바일 스크린리더 & WAI-ARIA] 우리가 표준을 따라야 하는 이유와 WAI-ARIA의 한계
    Webacc NV 2020-11-20 11:44:11

    커스텀(Custom)은 주문 제작, 사용자 지정 등의 뜻을 갖고 있습니다. 웹 개발이나 앱 개발에 있어서도 마찬가지로 개발자의 입맛에 맞게 표준에는 정의되지 않은 커스텀 요소를 만들 수 있습니다.

    웹에서는 커스텀 요소의 접근성을 지키기 위해 WAI-ARIA 명세를 사용합니다. WAI-ARIA는 보조기술(이하 AT)에서 받아들일 수 있는 요소 정보, 상태 정보, 선택 정보 등, 요소에 필요한 정보를 제공하기 위해 사용합니다.

    WAI-ARIA를 사용하지 않고 커스텀 요소를 만들면 생기는 일

    만약 WAI-ARIA를 사용하지 않는다면, 스크린리더와 같은 AT에서는 아무리 디자인을 다르게 한들, div나 span으로 만들었다면 아무 요소도 아닌 것으로 인식할 것입니다. AT를 직접적으로 사용하는 스크린리더 사용자에게는 아무런 정보도 알리지 못할 것입니다.

    WAI-ARIA는 커스텀 요소를 만드는 만능의 도구가 아니다 (1) : WAI-ARIA는 정보를 제공할 뿐, 기능을 완전히 구현해 주지는 않습니다.

    WAI-ARIA를 마법의 지팡이인 듯 사용하지 마십시오. WAI-ARIA는 오로지  사람과 AT 소프트웨어에게 정보를 전달하는 것에 초점을 맞춘 명세입니다. AT 소프트웨어 중 특히 스크린리더에 지대한 영향을 미치므로, 스크린리더를 기준으로 말씀을 드리겠습니다.

    스크린리더는 요소의 정보를 전달받으면 가상 커서가 꺼지거나, dialog(대화상자) 역할처럼 현재 영역 외의 모든 영역을 숨겨준다거나 하는 기능을 수행할 수 있습니다. 하지만 요소에는 정보 전달만의 목적을 가진 요소만 있는 것은 아닙니다. OS나 웹에는 라디오 버튼이나, 탭 컨트롤, 메뉴바, 컨텍스트 메뉴, 트리뷰와 같은 요소는 상호작용 가능한 요소(Interactable Element)가 있습니다. WAI-ARIA는 이들에 대한 정보를 제공하나 상호작용할 수 있는 조작방법을 구현해주지는 않습니다.

    HTML이나 응용프로그램에서의 커스텀은 하나부터 열까지 전부 스스로 구현해야 한다는 것을 명심해야 합니다.

    WAI-ARIA는 커스텀 요소를 만드는 만능의 도구가 아니다(2) : 모바일 기기와의 상호작용을 일반적으로 구현하기 어렵다.

    1980년도부터 2000년대 중반까지, 웹이나 응용프로그램을 사용하는 기기는 데스크탑이나 랩탑(노트북)에 한정되어 있었습니다. 하지만, 2007년, 애플이 처음으로 아이폰을 선보여 스마트폰 시대를 예고했으며, 2009년부터 상품화와 대중화가 이루어진 스마트폰이 나오기 시작했습니다.

    모바일 환경에서는 모바일 OS에서 제공하는 네이티브 이벤트, 웹에서 제공하는 이벤트가 있습니다. PC 웹 환경에서는 웹에서의 이벤트만 신경쓰면 됬었기에 별 문제가 없었으며, WAI-ARIA를 사용할 때 별 문제가 없습니다.

    하지만, 모바일 기기의 AT는 해당 기기에 최적화된 조작법을 포함하는 경우가 많습니다. 예를들어 아이폰에서 슬라이더의 값을 조절할 때에는 위 또는 아래로 한 손가락을 쓸어 값을 조절할 수 있으며, 안드로이드 기기에서는 컨텍스트 메뉴를 열어 슬라이더의 값을 숫자로 직접 입력하여 조절하거나, 슬라이더에 초점을 두고, 음량 조절 버튼으로 슬라이더 값을 조절할 수 있습니다.

    하지만 role="slider"로 구현한 슬라이더에서는 이를 일반적으로 구현하여 접근성을 보장할 방법이 없습니다. 이는 모바일 OS에서 지원하는 이벤트이기 때문입니다. 그런데 role을 사용하면, 요소 유형 정보를 AT에 전달하기 때문에, 위 조작방법을 사용할 수 없는 커스텀 요소임에도 불구하고, 볼륨 버튼을 눌러 조절할 수 있다거나, iOS에서는 위 또는 아래로 쓸어서 조절할 수 있다는 안내 힌트가 자동으로 따라 붙게 됩니다.

    그러면 어떻게 해야 하는가?

    네이티브 앱에는 HTML에 없는 요소가 많습니다. 이러한 요소를 구현할 때에는 당연히 WAI-ARIA를 사용해야 맞는 것입니다. 방법이 이것 밖에 없으니까요. 하지만, 위에서 slider는 HTML5 네이티브 input 태그에서 충분히 구현할 수 있습니다. <input type="range" />는 role="slider"와 동일합니다.

    이렇게 대체 가능한 네이티브 HTML 태그가 있다면, 되도록 네이티브 태그를 사용해야 합니다. 네이티브 HTML 태그는 어떠한 OS든 이벤트가 대응되어 있기 때문입니다.

    특히 모바일 환경은 대부분 webkit 기반의 safari(iPhone)과 웹킷-크로미움 기반의 웹 브라우저(Android)를 사용합니다.  따라서 PC 브라우저 환경보다 CSS를 사용하여 통합된 디자인을 만드는 것이 보다 수월합니다. 커스텀 요소를 사용하는 이유가 디자인적인 부분이라면 네이티브 태그의 디자인을 수정해 보는 것이 더 바람직해 보입니다.

    만약, 어쩔 수 없이 모바일 페이지에서 커스텀 요소를 써야 한다면, AT 사용자를 위해 네이티브 요소를 함꼐 제공하고, 값을 공유하는 것도 생각해볼 만할 가치가 있을 것 같습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [센스리더 & HTML] 가상커서가 자동으로 꺼지는 것 업데이트 관련
    Webacc NV 2020-11-20 08:58:42

    스크린 리더는 탭키를 눌러서 라디오버튼이나 탭컨트롤과 같은 특정 요소를 만나면 가상커서를 자동으로 끕니다.

    왜냐하면 라디오버튼이나 탭컨트롤 등은 화살표를 눌러서 옵션을 변경하도록 규정하고 있기 때문입니다.

    탭컨트롤 등을 구현할 때 키보드 이벤트를 구현해야 하는 이유가 여기에 있습니다.

    센스리더에서도 탭키를 눌렀을 때 여러 줄 입력창이나 라디오버튼, 탭컨트롤을 만나면 가상커서를 자동으로 해제합니다.

    다만 별도 소리가 없어서 사용자가 그것을 자세히 인지를 하지 못할 뿐입니다.

    그런데 7.4 버전부터는 롤 슬라이더, 아리아로 구현한 메뉴 요소를 만나도 가상커서를 자동으로 끕니다.

    따라서 슬라이더나 메뉴 내비게이션을 반드시 아리아를 사용하셔야 한다면 키보드 접근성 고려가 필요합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 한 손가락 옆으로 쓸기 탐색으로 특정 영역의 요소들이 탐색되지 않는 경우
    Webacc NV 2020-11-19 14:44:17

    아이폰에서 특정 앱이 실행되면 한 화면에 당연한 이야기이지만 여러 요소들이 존재할 것입니다.

    그런데 해당 요소들은 화면에는 보이지 않지만 UIView, UITableView, UIScrollView 와 같은 부모 요소들이 존재하게 됩니다.

    이것을 컨테이너라고 부릅니다.

    따라서 한 화면에는 콘텐츠 유형에 따라 여러 컨테이너들이 존재하게 됩니다.

    그런데 문제는 한 손가락 오른쪽 쓸기로 순차 탐색 시에 특정 그룹, 즉 특정 컨테이너는 아예 포커스가 되지 않는 경우가 있다는 것입니다.

    따라서 스크린 리더 사용자는 해당 영역이 아예 없는 것처럼 생각할 수 있습니다.

    이것의 원인은 무엇일까요?

    접근성 초점이 가는 버튼과 같은 view 하위에 또 다른 하위 요소들을 구현했기 때문입니다.

    특정 컨테이너 그룹의 접근성 포커스를 수정하거나 그룹의 여러 요소들을 하나의 포커스로 만드는 등의 작업을 하지 않는 이상 컨테이너를 구성하는 테이블, 스크롤뷰 등에는 보이스오버 포커스가 되지 않는 것이 원칙입니다.

    즉, 컨테이너 그룹 자체 뷰가 접근성 포커스를 가지게 되면 기본적으로 하위 요소에 포커스가 안 됩니다.

    따라서 접근성 초점이 가는 요소 하위에 또 다른 무언가의 콘텐츠들을 두는 것은 보이스오버 입장에서는 컨테이너 부모에 포커스가 되는 상황이 되므로 스크린 리더 사용자에게 큰 문제를 주게 됩니다.

    따라서 view를 구성할 때 접근성 초점이 가는 요소 하위에 또 다른 하위 view를 만들지 않도록 주의가 필요합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 음성 검색 시에 톡백이 말하지 않게 하기
    Webacc NV 2020-11-18 09:52:19

    음성 검색을 누르면 마이크가 켜지면서 톡백이 화면의 내용을을 읽는 것 때문에 스크린 리더 사용자가 어려움을 겪는 경우가 많습니다.

    사실 톡백 입장에서는 화면이 변경되면 변경된 내용을 읽는 것이 당연할 것입니다.

    그래서 음성 검색 화면에서는 톡백이 아무 말도 못하도록 별도 접근성 구현이 필요합니다.

    접근성 구현을 해 주어야 하는 것은 다음과 같습니다.

    1. 화면이 전환될 때 액티비티 레이블을 톡백이 읽지 못하게 하기: 이 부분은 지난 팁에서 한번 다룬 적이 있는데 setTitle을 null값 즉 ""로 둡니다.

    2. 화면 전환 시 그 어떤 요소에도 포커스 되지 못하게 하기: 톡백은 화면 전환 시에 자동으로 화면의 제목 다음에 있는 첫 요소에 포커스를 위치시킵니다.

    화면 제목 이전에 위로 이동과 같은 버튼이 있다면 그 버튼에 포커스가 됩니다.

    우리는 이러한 요소에 톡백이 포커스 하지 못하도록, 그래서 아무 말도 하지 않도록 해야 하는 것입니다.

    3. 반드시 음성 듣기 시작과 종료에 대한 효과음을 재생해야 합니다.

    4. 음성 듣기 종료 시 다른 화면으로 전환되지 않고 현재 화면에 머무르는 경우에는 어떤 메시지가 발생했는지를 자동으로 읽게 해야 하며 듣기 버튼으로 초점을 이동시켜 주어야 합니다.

    다음 팁에서는 이를 적용하기 위한 예제 코드를 공유하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 콘텐츠 검색 화면 구현 시 검색어 삭제 관련
    Webacc NV 2020-11-13 17:01:11

    일반적으로 콘텐츠 검색 화면을 구현하는 경우에는 텍스트 입력창에 글자가 입력되어 있으면 검색어를 삭제하는 버튼을 표시하는 경우가 많습니다.

    그리고 텍스트가 삭제되면 해당 버튼은 화면에서 사라지는 형태입니다.

    이때도 접근성 구현을 해 주지 않으면 스크린 리더 사용자에게 이슈가 있는데 텍스트 삭제 후에 톡백의 포커스가 상단으로 튀어버리거나 톡백의 초점이 어디에 포커스 되었는지에 대한 음성 피드백을 받을 수 없다는 점입니다.

    따라서 이를 해결하기 위해 우리는 사용자가 검색어 삭제 버튼을 누르면 다음 예시와 같이 초점을 검색어 입력 편집창으로 보내주면 됩니다.

    public void deleteText(View view) {
        Button clearText = (Button)findViewById(R.id.clearText);
        EditText editText = (EditText)findViewById(R.id.editText);
    editText.getText().clear();
    editText.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
        }
    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 포커스 순서 변경 예제
    Webacc NV 2020-11-12 09:56:41

    어제는 포커스 순서를 변경하는 방법에 대해 살폈습니다. 

    오늘은 관련된 예제 코드를 공유하려고 합니다.

    먼저 제목 1, 내용 1, 제목 2 내용 2 네 개의 view가 있다고 가정해 봅시다.

    그리고 한 손가락 오른쪽 쓸기를 통해 이동할 때 제목 1, 제목 2, 내용 1, 내용 2 순으로 포커스가 된다고 가정해 봅시다.

    그러면 우리는 제목 1, 내용1과 같이 포커스 되도록 수정을 해야 할 것입니다.

    UIView에 다음과 같이 4개의 view를 포커스 순서대로 AccessibilityElements 배열에 담습니다.

        override var accessibilityElements: [Any]? {

            set{}

            get{

                return [header1!,

                        detail1!,

                        header2!,

                        detail2!,
    }

    }

    }

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 포커스 순서 재조정하기
    Webacc NV 2020-11-11 10:12:20

    스크린 리더 사용자가 한 손가락 오른쪽 혹은 왼쪽 쓸기를 하면 대부분 화면상의 레이아웃 순서대로 포커스가 이동합니다.

    그러나 특정 상황에서는 화면의 포커스 순서가 논리적이지 않아 수정이 필요한 경우가 발생하게 됩니다.

    이때는 포커스 문제가 발생하는 컨테이너의 하위 뷰들의 순서를 재정의해줌으로써 해당 문제를 해결할 수 있습니다.

    이를 위해서는 컨테이너의 각각의 view들을 accessibilityElements 안에 array 형태로 담습니다.

    여기서는 순서가 중요하며 array 안에 담겨 있지 않은 뷰들은 모두 무시됩니다.

    다음 팁에서는 구체적인 예시를 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] replaceAccessibilityAction을 활용한 톡백 사용자용 힌트 별도 지정
    Webacc NV 2020-11-10 09:48:06

    이제 replaceAccessibilityAction의 실제 적용사례를 나눌 차례입니다.

    지난 주에는 replaceAccessibilityAction의 문법에 대해 살폈었습니다.

    우리는 화면에 보여지는 편집창 레이블을 톡백에서는 다르게 읽어주도록 해볼 것입니다.

    먼저 휴대폰 번호를 입력하는 편집창이 있고 실제 레이블은 전화번호, - 제외 라고 가정해 봅시다.

    톡백에서는 -를 읽지 않으므로 톡백 사용자를 위해 전화번호, 숫자만 입력 이라고 힌트 메시지를 바꾸어볼 것입니다.

    톡백 사용자용 힌트 메시지를 별도 마크업하는 방법이 있으면 좋겠지만 현재는 접근성 API에서 이를 제공하고 있지 않으므로 우리는 접근성 액션 중에서 포커스라는 액션을 활용할 것입니다.

    즉 톡백이 전화번호 입력창에 포커스가 가면 즉 접근성 액션 중에서 접근성 포커스가 실행이 되면 이 때는 힌트 메시지를 변경하는 것입니다.

    그렇다면 다음과 같이 replaceAccessibilityAction을 활용할 수 있을 것입니다.

    ViewCompat.replaceAccessibilityAction(editText, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_ACCESSIBILITY_FOCUS, "", new AccessibilityViewCommand() {
        @Override
        public boolean perform(@NonNull View view, @Nullable CommandArguments arguments) {
            editText.setHint(R.string.talkBackHint);
            return false;
        }
    });
    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] 보이스오버가 자체적으로 만들어내는 대체 텍스트 구분하기
    Webacc NV 2020-11-09 15:58:47

    접근성을 테스트 할 때 가장 흔히 접하게 되는 이슈는 대체 텍스트일 것입니다.

    이미지에 대한 대체 텍스트가 없다면 스크린 리더 사용자는 해당 요소가 어떤 요소인지조차 알 수 없기 때문입니다.

    그런데 최근들어 iOS 보이스오버에서는 이미지 인식 기술을 이용하여 대체 텍스트가 없어도 일부 요소에 자동으로 대체 텍스트를 추가하여 읽어주는 경우가 종종 있습니다.

    대표적인 요소들이 뒤로, 닫기, 검색, 홈 등입니다.

    그래서 해당 기능을 잘 모를 경우 접근성을 테스트 하는 입장에서는 이러한 버튼들에 대체 텍스트가 들어간 것으로 생각할 수 있으나 보이스오버가 예상하는 정보를 주는 것일 수 있다는 것을 알아 두셔야 합니다.

    아래는 뒤로, 닫기, 검색과 같은 버튼들이 실제 대체 텍스트로 추가된 것인지 보이스오버가 이미지를 인식하여 읽어주는 것인지를 구분하는 방법입니다.

    1. 보이스오버가 이미지 인식 기술을 통해 유추한 대체 텍스트는 요소 유형 뒤에 읽어줍니다.

    예: 버튼, 뒤로.

    2. 구분하기 애매하다 판단되는 경우에는 해당 요소에 포커스 하고 세 손가락 한 번 탭을 해봅니다.

    세 손가락 한 번 탭은 현재 위치한 포커스가 화면의 어느 부분이 위치해 있는지를 알려주는 기능인데 이미지 인식 기술이 가능한 경우에는 보이스오버가 인식한 이미지에 대한 텍스트를 함께 읽어주고 그렇지 않은 경우에는 '화면 중앙'과 같은 위치 정보만 읽어주게 됩니다.

    따라서 위치 정보만 읽어준다면 개발팀에서 대체 텍스트를 추가한 것이라고 볼 수 있겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] replaceAccessibilityAction 사용법
    Webacc NV 2020-11-05 09:53:08

    오늘은 어제에 이어서 replaceAccessibilityAction 사용법에 대해 다룰 차례입니다.

    replaceAccessibilityAction에는 4가지의 값이 들어갑니다.

    1. AccessibilityAction: 어떤 접근성 액션을 대체할 것인지를 정해 주어야 합니다. 

    이것은 말 그대로 접근성 액션을 대체하는 것이기 때문에 해당 요소에 클릭이면 클릭, 롱클릭이면 롱클릭 등의 접근성 액션이 이미 들어가 있는 상태여야 하겠습니다.

    2. 레이블: 접근성 액션에 따라 톡백에서 디폴트로 가지고 있는 레이블 값을 변경할 때 사용합니다.

    예를 들어 클릭 액션에 대한 디폴트 레이블이 활성화 하려면 이중탭하세요 인데 이것을 재생하려면 이중탭하세요 로 변경한다면 레이블을 넣는 란에 "재생" 과 같이 값을 줍니다.

    참고로 지난번에 아티클에서도 다룬 적이 있는데 doubletap to 부분은 변경할 수 없습니다.

    레이블과 관련이 없는 접근성 액션을 대체하거나 레이블이 아닌 액션만 변경할 경우에는 레이블은 "" 로 넣으면 됩니다.

    3. AccessibilityViewCommand: 개발자가 디폴트로 구현한 액션이 아닌 접근성 서비스에서 액션을 따로 지정하는 경우에 사용합니다.

    쉽게 예를 들어 개발자가 완료 버튼 클릭 액션을 하면, 즉 톡백을 사용하지 않는 사용자가 탭을 하면 햄버거 주문이 바로 완료된다고 가정합시다.

    그렇다면 톡백 사용자도 이중탭을 하면 햄버거 주문이 완료될 것입니다.

    그런데 톡백을 실행하고 클릭 액션 즉 이중탭을 하면 햄버거 주문을 완료하겠는지 팝업을 띄우고 싶다고 가정합시다.

    이때 AccessibilityViewCommand를 사용할 수 있습니다.

    따라서 AccessibilityViewCommand 안에는 사용자가 해당 액션을 했을 때의 구현에 대한 자바 혹은 코틀린 코드를 넣게 됩니다.

    만약 AccessibilityViewCommand 는 변경하지 않고 레이블, 즉 힌트만 변경하고 싶다면 역시 AccessibilityViewCommand 는 null로 두면 됩니다.

    4. return true 혹은 false: true를 하게 되면 AccessibilityViewCommand 안의 코드를 실행한 다음에 원래 디폴트로 가진 액션을 함께 실행할 때 사용을 합니다.

    그러나 false를 하게 되면 AccessibilityViewCommand 안의 코드만 실행하고 종료됩니다.

    다음 시간에는 사용 예제를 통해 힌트 메시지를 변경하는 것에 대해 말씀드리겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] replaceAccessibilityAction이란
    Webacc NV 2020-11-04 10:44:37

    지난 팁에서 제가 replaceAccessibilityAction에 대해 다룰 것이라고 말씀을 드렸습니다.

    안드로이드는 각 요소의 유형에 따라 클릭, 롱클릭, 이전으로 스크롤, 다음으로 스크롤 등과 같은 접근성 액션이 붙게 됩니다.

    이러한 액션의 종류는 클릭, 롱클릭 외에도, 이전으로 스크롤, 다음으로 스크롤, 포커스, 포커스 초기화 등 대략 30개가 넘습니다.

    접근성 액션은 요소 유형에 따라 시스템에서 자동으로 추가되기도 하지만 상황에 따라 수동으로 액션을 추가하거나 삭제할 수도 있습니다.

    그런데 시스템에서 자동으로 추가되어 있는 접근성 액션의 레이블이나 동작을 다른 레이블이나 동작으 변경하고 싶을 때가 있습니다.

    예를 들어 클릭을 가지고 생각해봅시다.

    만약 버튼 하나를 만들었다면 action_click이라는 접근성 액션이 시스템에서 자동으로 부여됩니다.

    action_click 접근성 액션이 삽입되었기 때문에 버튼에 포커스 하면 활성화 하려면 이중탭하세요 라는 힌트가 출력되는 것입니다.

    그리고 클릭 액션의 동작 즉 이중탭은 기본적으로 접근성 액션에 대한 수정 작업을 하지 않는 한 시스템 클릭이 동작을 합니다.

    여기서 말하는 시스템 클릭이란 개발자가 일반적으로 클릭을 구현한 그 동작이 실행된다는 것입니다.

    그런데 클릭의 레이블을 활성화 하려면 이중탭하세요 가 아니라 재생 작업을 하려면 이중탭하세요 등으로 레이블을 변경하고싶거나 톡백의 클릭 액션과 톡백을 끄고 클릭을 했을 때의 액션을 다르게 지정해야 할 때가 있습니다.

    이럴 때 사용할 수 있는 것이 바로 replaceAccessibilityAction 입니다.

    정리하자면 replaceAccessibilityAction은 현재 포함되어 있고 디폴트로 동작하는 AccessibilityAction을 다르게 수정해야 할 때 사용하는 메소드입니다.

    그럼 다음 팁에서는 replaceAccessibilityAction의 코드 사용방법과 편집창에서 어떻게 적용할 수 있는지를 다루도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 편집창의 기존 레이블 오버라이드하기
    Webacc NV 2020-11-03 09:22:22

    안드로이드에서는 무언가를 입력할 수 있는 EditText 편집 영역을 톡백에서 수정창이라고 읽어주며 해당 수정창에는 android:hint 속성을 통해 무엇을 입력해야 하는지를 표시하게 됩니다.

    또한 EditText와 입력 레이블을 표시하는 view가 분리된 경우에는 HTML와 마찬가지로 스크린 리더, 즉 톡백이 편집창에만 포커스 해도 레이블을 읽을 수 있도록 labelFor 속성을 사용합니다.

    그런데 편집창에 따라서는 화면에 보여지는 레이블과 톡백이 읽어주는 레이블을 다르게 표시하고 싶을 때가 있을 수 있습니다.

    대표적인 예를 두 가지 들어보겠습니다.

    1. 전화번호 입력 편집창의 화면에 보여지는 텍스트가 전화번호(-) 제외 라고 표시되고 있을 때: 이때톡백은 -라는 글자를 읽어주지 않습니다.

    2. 검색어를 입력하는 편집창의 화면에 보여지는 텍스트가 메뉴검색 인경우: 자동완성을 지원함을 톡백 사용자에게 알리고 싶을 때.

    지난 팁에서 EditText에는 contentDescription을 사용하면 안 된다고 말씀을 드렸습니다.

    그리고 이미 androd:hint 속성이 제공되고 있을 때는 contentDescription 속성을 넣어도 톡백이 읽어주는 레이블이 contentDescription 레이블로 변경되지 않습니다.

    게다가 AccessibilityNodeInfo 객체 내의 setHintText를 변경해도 이미 android:hint 속성이 들어가 있기 때문에 레이블이 변경되지 않습니다.

    그렇다면 이 문제를 어떻게 해결해야 할까요?

    여러 방법이 있을 수 있겠지만 방법 중 하나가 바로 replaceAccessibilityAction을 사용하는 것입니다. 

    다음 팁에서는 replaceAccessibilityAction에 대해서 살펴보고 android:hint 속성 변경을 어떻게적용할 수 있는지 살피겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 웹뷰의 페이지 로딩 화면을 구현할 경우
    Webacc NV 2020-11-02 19:43:27

    안드로이드 웹뷰에 대한 팁은 이번이 마지막이 될 것 같습니다.

    지난 안드로이드 웹뷰 팁에서는 페이지 로딩 시에 페이지 제목을 자동으로 읽어주게 함으로써 스크린 리더 사용자가 페이지가 갱신되었음을 알리게 하는 방법에 대해 공유했습니다.

    오늘은 페이지 로딩중에 대한 이야기를 해보겠습니다.

    화면에 페이지가 로딩중이라는 것을 표시하는 경우에는 스크린 리더 사용자 역시 이를 알 수 있어야 합니다.

    일반적으로 페이지 로딩 구현은 ProgressBar를 많이 사용하기 때문에 ProgressBar가 화면에 표시되는 경우 다음 예시와 같이 대체 텍스트 추가 및 ProgressBar에 초점이 자동으로 이동되도록 구현해 주면 됩니다.

    이렇게 하면 페이지 로딩 시에 초점이 ProgressBar로 이동되며 로딩이 되고 있는 진행률을 실시간으로 읽어주게 됩니다.

    @Override
        public void onProgressChanged(WebView view, int newProgress) {
            progressBar.setProgress(newProgress);
            progressBar.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
            progressBar.setContentDescription(newProgress+"%");
        }
    });
    댓글을 작성하려면 해주세요.
  • tip
    [NVDA & WAI-ARIA] aria-current 속성 처리 업데이트 소식
    Webacc NV 2020-10-30 11:35:03

    몇주 전에 NVDA에서 크롬 브라우저 사용 시에 DOM 새로고침 없이 스크립트로 aria-current 속성이 변경되는 경우에 이를 NVDA 가상커서가 캐치하지 못한다는 이슈에 대해 공유하였습니다.

    그런데 NVDA 개발자버전에서 해당 이슈가 일부 수정되어 aria-current 변경된 값을 사용자가 인지할 수 있게 되었습니다.

    NVDA 개발자분의 말에 의하면 aria-current 속성이 변경될 때 크롬 브라우저에서는 IA2_EVENT_TEXT_ATTRIBUTE_CHANGED 이벤트를 발생시킨다고 합니다.

    Firefox 브라우저에서는 크롬과는 달리 IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED 이벤트를 발생시킨다고 합니다.

    따라서 NVDA에서 크롬 브라우저에서 발생시키는 IA2_EVENT_TEXT_ATTRIBUTE_CHANGED 이벤트를 캐치하도록 수정하여 어느정도 해결이 된 것입니다.

    그런데 제가 어느정도라는 말을 사용하는 이유는 aria-current 속성이 변경되었을 때 음성으로 그것을 바로바로 피드백 해 주지 못하기 때문입니다.

    다른 곳에 포커스를 했다가 다시 변경된 요소에 포커스를 해야만 변경된 aria-current 속성을 들을 수 있습니다.

    이것을 해결하려면 크롬 브라우저 자체에서 aria-current 속성이 변경될 때 firefox 브라우저와 같이 IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED 이벤트를 발생시켜야 할 것으로 보입니다.

    그래도 어느정도 변경된 aria-current 속성을 지원하게 된 것은 잘된 일이며 다음 업데이트 되는 NVDA 정식 버전에서 반영되길 기대해 봅니다.

    또한 크롬 브라우저에서의 이벤트 속성 변경 역시 기대해 봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 앱뷰에 웹뷰 삽입 시에 페이지 로딩 완료되면 웹페이지 제목 읽어주게 하기
    Webacc NV 2020-10-29 09:18:27

    어제는 한 화면에 웹뷰와 앱뷰 요소가 함께 있을 때 앱뷰 요소와 웹뷰 요소를 톡백으로 구분하는 방법에 대해 함께 살폈습니다.

    오늘은 앱뷰 안에 웹뷰 구현 시 접근성 적용에 대해 함께 생각해 보려고 합니다.

    크롬 브라우저에서는 웹뷰 내에서 특정 링크를 눌러 페이지가 로딩이 되면 로딩 완료시에 스크린 리더 초점이 웹뷰로 이동하는 것을 알 수 있습니다.

    이렇게 되면 스크린 리더 사용자는 두 가지를 알 수 있습니다.

    1. 페이지 로딩이 완료되었다는 것.

    2. 변경된 페이지 타이틀.

    하지만 앱뷰 안에 웹뷰를 구현하는 경우에는 기본적으로 페이지가 새로고침이 되어도 스크린 리더에서는 아무런 변화가 없으므로 페이지 로딩이 완료된 것을 알 수 없습니다.

    따라서 sendAccessibilityEvent 메소드를 활용하여 페이지 로딩이 완료되었을 때 스크린 리더 포커스를 웹뷰로 보내주는 접근성 구현이 필요하며 예시는 다음과 같습니다.

    public void onPageFinished(WebView view, String url) {
    new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                browser.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
            }
        }, 1000);
    }
    
    여기서 약간의 시간 딜레이를 준 이유는 딜레이를 주지 않으면 어떤 경우에는 웹페이지 쪽으로 초점이 이동되지 모하는 경우가 있기 때문이며 이에 대해서는 각 웹뷰별로 테스트가 필요할 수 있습니다.
    }
    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 앱뷰 내에 웹뷰를 포함시키는 경우
    Webacc NV 2020-10-28 09:18:25

    콘텐츠에 따라 앱뷰 내에 웹뷰를 삽입하여 화면을 구성하는 경우가 많습니다.

    접근성을 테스트 하고 접근성 문제에 대한 해결방안을 제시하려면 현재 탐색하고 있는 요소들이 앱뷰인지 웹뷰인지를 판단하는 것이 중요합니다.

    앱뷰냐 웹뷰냐에 따라 해결방안이 전혀 달라질 수 있기 때문입니다.

    또한 웹뷰를 구성할 때는 페이지 로딩 시에 페이지 제목을 자동으로 읽어줄 수 있도록 하는 접근성 구현이 필요합니다.

    이번에도 두 차례에 걸쳐 웹뷰에 관련된 팁을 공유해 보려고 합니다.

    오늘은 톡백으로 접근성 테스트 시에 현재 테스트하고 있는 요소가 웹뷰인지 앱뷰인지 확인하는 방법 몇 가지를 공유하고 다음 팁에서는 웹뷰 구현 시에 페이지 로딩 시 제목을 자동으로 읽어주도록 하는 구현 방법에 대해 다룰 것입니다.

    톡백으로 접근성 테스트 시에 현재 탐색하고 있는 요소들이 앱뷰인지 웹뷰인지 확인하는 방법은 다음과 같습니다.

    1. 앱뷰에는 제목에 대한 레벨이 없습니다. 따라서 제목 3, 제목 2와 같이 읽어준다면 웹뷰 콘텐츠 입니다.

    2. 현재 포커스 하고 있는 객체에서 한 손가락 아래 쓸기를 했을 때 웹뷰와 앱 뷰의 탐색설정 순서가 다릅니다. 앱뷰에서는 문자, 단어,, 줄(줄은 없을 수도 있음), 단락, 제목, 링크, 컨트롤 기본값 순으로 탐색값이 변경됩니다.

    웹뷰는 제목, 링크, 컨트롤, 문자, 단어, 줄, 기본값 순으로 탐색 단위가 변경됩니다.

    이러한 탐색 순서를 통해서 웹뷰인지 앱뷰인지를 판단한 다음 접근성 테스트를 진행한다면 좀 더 실질적인 해결방안을 도출할 수 있을 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] NestedScrollView 접근성 적용 예시 코드
    Webacc NV 2020-10-27 09:21:42

    지난 팁에서는 NestedScrollView 사용 시에 스크롤 동작에 대한 접근성을 구현하지 않으면 스크린 리더 사용자가 한 손가락 쓸기를 이용해서 탐색 시에 화면이 자동으로 스크롤 되지 않는다는 것에 대해 살폈습니다. 

    오늘은 이에 대한 접근성을 적용하기 위한 샘플 코드를 공유하려고 합니다.

    하나의 화면에 1, 2, 3, 4의 각 텍스트뷰가 있다고 가정해 봅시다.

    해당 텍스트뷰는 NestEdScrolView 안에 자식으로 포함되어 있고 현재 화면에는 1과 2만 표시되고 있습니다.

    기본적으로는 앞에서 말씀드린 바와 같이 톡백 사용자가 한 손가락 쓸기로 탐색을 하면 마치 화면에는 1과 2만 있는 것처럼 표시되고 더 이상 스크롤이 되지 않습니다.

    이것을 우리는 한 손가락 쓸기를 통해 숫자 2에 포커스가 되면 자동으로 스크롤이 되면서 3, 4가 보이게 하고 3에 포커스를 하면 역시 자동으로 스크롤이 되면서 이전 숫자들인 1과 2가 표시되도록 해볼 것입니다.

    1. 우선 위로 스크롤과 아래-로 스크롤 두 개의 메소드를 만듭니다.

    위로 스크롤 메소드:

    private void expandBottomBoard() {
        final NestedScrollView bottomBoard = findViewById(R.id.bottom_board);
        bottomBoard.post(new Runnable() {
            @Override
            public void run() {
                final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomBoard);
                if (behavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) {
                    behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                }
            }
        });
    }아래로 스크롤:
    private void collapseBottomBoard() {
        final NestedScrollView bottomBoard = findViewById(R.id.bottom_board);
        bottomBoard.post(new Runnable() {
            @Override
            public void run() {
                final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomBoard);
                if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                    behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }
        });
    }
    

    2. 이제 AccessibilityNode가 숫자 2에 포커스를 하면 위로 스크롤을 하게하고 3에 포커스를 하면 아래로 스크롤을 하도록 위의 두 메소드를 AccessibilityNodeInfo에 다음과 같이 적용시킵니다.

    TextView text1View = findViewById(R.id.text1);
    text1View.setAccessibilityDelegate(accessibilityDelegate1);
    TextView text2View = findViewById(R.id.text2);
    text2View.setAccessibilityDelegate(accessibilityDelegate2);
    
    final View.AccessibilityDelegate accessibilityDelegate2 = new View.AccessibilityDelegate() {
        @Override
        public boolean performAccessibilityAction(View host, int action, Bundle args) {
            if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
                expandBottomBoard();
            }
            if (super.performAccessibilityAction(host, action, args)) {
                return true;
            }
            return false;
        }
    

    };

    이렇게 하면 2라는 숫자에 포커스가 되면 자동으로 화면을 스크롤하게 되며 3이라는 숫자에 포커스 했을 때의 방법도 위와 유사합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] NestedScrollView와 스크롤 접근성 문제
    Webacc NV 2020-10-21 17:19:12

    스크롤뷰나 리스트뷰 등을 가진 콘텐츠는 기본적으로 스크롤 동작을 지원합니다. 

    톡백에서도 이러한 뷰 안에서는 사용자가 스크롤 제스처를 하지 않아도 자동 스크롤 기능을 오프 하지 않은 이상 한 손가락 쓸기를 통하여 화면 탐색 시에 보여지는 화면 그 이상의 콘텐츠가 존재할 경우 자동으로 스크롤 동작을 수행하게 됩니다.

    그런데 NestedScrollView 사용 시에는 하위에 여러 뷰가 존재하고 스크롤이 가능함에도 톡백에서 두 스크롤 제스처를 별도로 하지 않는 이상 화면이 스크롤되지 못합니다.

    따라서 스크린 리더 사용자는 NestedScrollView로 구현된 콘텐츠 탐색 시에 스크롤 되는 화면인지를 알기 어렵습니다.

    이 문제를 해결하려면 AccessibilityNodeInfo의 AccessibilityAction ACTION_FOCUS 속성을 사용하면 됩니다.

    즉 화면 하단의 NestedScrollView 하위 뷰에 포커스 되는 경우 자동으로 스크롤을 확장하도록 하는 것입니다.

    다음 팁에서는 이에 대한 샘플 코드를 공유하도록 하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [WAI-ARIA & NVDA] 크롬 브라우저에서의 aria-current 처리 관련
    Webacc NV 2020-10-19 10:07:25

    aria-current 속성은 주메뉴 중 어떤 페이지가 선택되어 있는지, 회원가입 단계에서 현재 어느 단계에 있는지, 롤링 배너 영역 중 현재 어느 배너가 롤링되고 있는지 등을 표시할 때 활용됩니다.

    aria-current 속성을 지원하는 NVDA와 같은 스크린 리더에서는 해당 속성이 부여된 요소에 포커스 하면 현재 페이지, 현재 단계 등의 정보를 읽어주게 되므로 스크린 리더 사용자가 여러 요소 중 무엇이 현재 선택되어 있는지를 바로 알 수 있게 됩니다.

    그런데 현재 NVDA와 크롬 브라우저 사용시 DOM 새로고침 없이 aria-current 속성 변경이 스크립트를 통해 이루어질 경우 NVDA 가상커서, 즉 브라우즈모드가 이를 캐치하지 못하는 이슈가 있습니다.

    이 부분에 대해서는 샘플 페이지를 통해 테스트해보실 수 있습니다.

    또한 이 부분에 대한 NVDA 버그를 리포트 한 상태이며 NVDA 깃허브 이슈 페이지를 통해 수정을 위한 논의 과정을 확인할 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [웹접근성] firefox 추가기능 axSHammer 개발과 WAI-ARIA 오남용 관련
    Webacc NV 2020-10-16 12:43:36

    웹접근성에서 빠질 수 없는 것이 WAI-ARIA 입니다.

    WAI-ARIA를 잘 접목하여 접근성을 개선하면 다양한 상황에서 스크린 리더 피드백을 컨트롤 할 수 있어 스크린 리더 사용자가 웹페이지를 효율적으로 탐색할 수 있도록 도울 수 있습니다.

    그러나 WAI-ARIA를 잘못 사용하면 사용하지 않는 것보다 더 사용성을 해치게 됩니다.

    특정 페이지를 언급할 수는 없지만 aria-hidden 속성이 잘못 마크업되어 있어 꼭 읽어야 할 콘텐츠를 읽지 못하거나 aria-live를 잘못 사용하여 수시로 광고 콘텐츠를 들어야 하는 등의 경험을 자주 하곤 합니다.

    WAI-ARIA를 사용할 때는 최소한으로, 적절한 유형으로 사용해야 하겠습니다.

    WAI-ARIA를 잘못 사용하는 것은 비단 우리나라 뿐만은 아닌 것 같습니다.

    그래서 James Teh 개발자가 axSHammer라는 firefox 브라우저 추가 기능을 개발하고 있다고 합니다.

    해당 추가기능을 사용하면 스크린 리더 사용자가 페이지를 탐색하다가 뭔가 WAI-ARIA를 잘못 사용하여 읽어주지 않거나 과하게 읽어준다고 판단되는 것이 있을 때 aria-live, aria-hidden, role aplication 등의 속성을 disable 시켜 페이지를 탐색할 수 있도록 돕는 것입니다.

    그럼 또 다음에 더 좋은 팁으로 찾아뵙겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML - WAI-ARIA] aria-hidden 속성의 이해
    Webacc NV 2020-10-14 12:09:30

    WAI-ARIA의 속성에는 모든 요소에 적용가능한 aria-hidden이라는 속성이 존재합니다. 이름 그대로 hidden, 숨김처리와 관련된 속성으로, 시각적으로는 보이지만, 스크린리더 상에서 해당 요소를 인식하지 못하도록 접근성 트리에서 숨기는 속성입니다.

    aria-hidden을 true로 요소에 적용하면 눈으로는 보고 접근할 수 있지만 마치 css에서 display:none이나 visibility:hidden 속성을 사용해서 숨긴 것처럼 스크린리더 상에서는 전혀 읽을 수 없는 상태가 됩니다.

    이번 글에서는 aria-hidden에 대한 오해나 다른 플랫폼에서는 가능하지만 aria-hidden 특성으로 인해 불가능한 기능을 구현하려고 한 사례 중점으로 aria-hidden에 대한 얘기를 Q&A 형식으로 풀어보고자 합니다.

     

    Q1. aria-hidden="true" 처리된 div 안에있는 자식 div에 aria-hidden = "false"를 사용했는데 스크린리더로 읽지 못해요. 왜 그런건가요?

    모바일, 안드로이드를 기준으로 설명드리자면, Android 접근성 객체에서는 importantForAccessibility의 값을 "no"로 지정하면 특정 자식만을 접근성 요소로 나타나게 하고, 이 외의 모든 것을 숨길 수 있습니다. 그런데, 아직 HTML에서는 이 기능을 손쉽게 구현할 수 없습니다. aria-hidden 특성상 aria-hidden="true"로 지정된 컨테이너의 하위 요소에 아무리 aria-hidden="false"값을 주어도 해당 요소를 표시할 수 없는 것이지요. 이 실수는 국내 웹페이지에서 레이어 팝업이나 대화상자를 커스텀으로 제작했을 때, 가장 많이 발견되는 사례입니다.

    따라서 이를 해결하려면 대화상자를 구현할 때에는 주 콘텐츠를 담는 wrapper 영역과 분리된 형제 구조의 div로 dimmed처리를 진행하고, absolute 포지셔닝으로 화면을 덮는 방식으로 시각적 효과를 구현한 다음, dialog가 펼처젔을 때, wrapper에 aria-hidden을 적용해 주는 것이 좋습니다.

     

    Q2. 사용자분께서 Tab 키로 저희 웹사이트를 탐색하던 아무런 음성도 출력하지 않을 때가 있다고 합니다. 왜 그럴까요?

    aria-hidden은 접근성 트리 상에서만 숨기는 것이지, 키보드 초점을 제거해주지는 않습니다. 따라서, 키보드 초점은 여전히 이동 가능한 상태이므로, Tab 키로 aria-hidden이 있는 요소를 탐색하게 되면 아무것도 안 읽게 되는 것이지요.

     

    Q3. 대화상자를 만들 때, 키보드 초점이 바깥 요소로 빠져나가는 것은 막았는데요. 모바일 스크린리더 초점이 대화상자 dimmed 레이어 아래로 빠저나가는 것은 어떻게 막나요?

    대화상자(role="dialog")를 사용하면, PC 스크린리더인 NVDA에서는 기본적으로 해당 요소 외의 다른 요소를 가상커서로 탐색할 수 없도록 자동으로 방지해줍니다. 하지만, Talkback은 현재 그렇지 않은 것으로 알고있습니다. 이럴 때 aria-hidden을 사용하면 됩니다.

    aria-hidden을 true값으로 설정하게 되면 화면을 쓸어 탐색하는 임의탐색이나, 한 손가락 오른쪽 또는 왼쪽으로 쓸어서 탐색하는 순차탐색 제스처에서 요소를 감지하지 않습니다. 물론, 아무리 모바일 페이지라고 하더래도, aria-hidden만을 적용해서는 안 됩니다. 터치가 불편한 지체장애인 사용자는 모바일도 블루투스 키보드를 통해 사용하는 분들이 많으며, 시각장애인 사용자 또한 마찬가지입니다.

     

    Q4. aria-hidden으로 처리된 특정 HTML 속성이나 aria 속성으로 id 참조가 가능한가요? 참조가 불가능하다면 어떤 문제가 생기나요?

    id로 참조하는 aria 속성(labelledby, describedby)은 참조가 가능하며, 아무런 문제가 생기지 않습니다. 하지만, table의 caption이나 figure의 figcaption, input이나 select, textarea에 사용되는 label 태그는 aria-hidden으로 숨기게 되면 특정 디바이스에서 해당 텍스트를 인식하지 못해 레이블을 읽지 못하는 문제가 생깁니다. 덧붙여서 aria-labelledby나 aria-describedby 속성은 CSS의 display:none; visibility:hidden; 또는 최신의 랜더링 숨김 처리 방식을 지정하는 content-visibility 속성을 사용하여 숨기더라도 문제없이 참조가 가능합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [HTML5 & 스크린 리더] required 속성 처리에 관하여
    Webacc NV 2020-10-12 12:01:16

    회원가입과 같은 폼컨트롤을  사용할 때 스크린 리더 사용자가 각 체크박스, 편집창과 같은 요소가 필수인지 아닌지를 알아야 하는 것은 두말할 나위가 없습니다.

    각 요소가 필수인지를 스크린 리더 사용자에게 알리는 방법은 '필수'와 같이 텍스트를 써주거나 aria-required, aria-invalid 속성을 사용하는 것 혹은 HTML5의 required 속성을 사용하는 방법 등이 있겠습니다.

    오늘은 HTML5의 required 속성 사용 시 스크린 리더의 읽어주는 것 관련하여 도움이 되실만한 팁을 몇 가지 정리해 보도록 하겠습니다.

    1. aria-required의 경우에는 aria-invalid 속성을 함께 사용해야 스크린 리더가 해당 요소가 필수 입력이라는 것과 조건에 맞게 입력하지 않았을 경우 입력 값이 유효하지 않음을 함께 읽어줍니다.

    하지만 HTML5의 required 속성을 사용하면 invalid 속성이 함께 포함되어 있습니다.

    즉 아이디 편집창이 있고 최소 입력값을 설정하는 minlength 값이 8로 설정되어 있으며 required 속성이 들어가 있으면 스크린 리더는 아이디 편집창이라는 것과 필수 입력이라는 것, 입력값이 유효하지 않다는 것을 함께 읽어주게 됩니다.

    다만 minlength 값을 충족하여 8자 이상이 되면 입력 값이 유효하지 않다는 invalid 속성은 살아지고 필수 입력이라는 정보만 남게 됩니다.

    2. 모바일 스크린 리더인 보이스오버와 톡백은 HTML5 required 속성을 일부만 지원합니다.

    즉 톡백의 경우는 invailid, 보이스오버는 required 속성만 읽어줍니다.

    이 부분은 각 스크린 리더에서 빠르게 개선되기를 바라봅니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 톡백에서 지원하는 요소 이름, 유형, 상태 말하기 순서 설정과 AccessibilityNodeInfo 정보변경 관련
    Webacc NV 2020-10-07 09:46:12

    지난 팁에서는 AccessibilityNodeInfo 컨트롤 유형 변경시에 반드시 컨테이너뷰가 아닌, 텍스트 혹은 대체 텍스트를 포함하는 커스텀 컨트롤을 사용한 뷰 자체를 수정해야 한다는 것에 대해 다루었습니다.

    이번 팁에서는 왜 그렇게 해야 하는지에 대해 다루어 보려고 합니다.

    톡백에서는 요소의 레이블, 유형, 상태정보가 있다고 가정했을 때 그것을 어떤 순서로 읽어줄 것인지를 설정하는 기능을 가지고 있습니다.

    만약 사용자가 유형, 이름, 상태 순으로 읽도록 설정하였다면 '체크박스, 햄버거 주문, 선택함'과 같이 읽어주게 됩니다.

    따라서 스크린 리더 사용자는 본인의 취향 및 정보 탐색의 성격에 따라 읽기 방식을 변경하여 사용합니다.

    그런데 요소 유형을 컨테이너뷰에 제공하게 되면 해당 컨테이너뷰에는 레이블을 포함하지 않기 때문에 사용자가 읽기 순서를 변경하더라도 무조건 요소 유형을 앞에 읽을 수밖에 없으며 사용자의 설정이 제대로 동작하지 않게 됩니다.

    즉 컨트롤, 레이블, 상태정보가 하나의 뷰에 포함되어 있어야만 톡백이 사용자의 읽기 순서 설정을 반영할 수 있습니다.

    위의 내용을 바탕으로 생각해보면 요소 유형이나 상태정보를 대체 텍스트로 넣지 않아야 하는 이유에 대해서도 우리는 알게 됩니다.

    요소 유형이나 상태 정보를 대체 텍스트로 포함하는 경우에도 톡백은 이를 레이블로만 인식하므로 사용자의 읽기 ㅓㄹ정을 반영하지 못합니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] AccessibilityNodeInfo 클래스 사용시 주의해야 할 사항
    Webacc NV 2020-10-06 14:13:06

    커스텀 컨트롤을 스크린 리더에서 체크박스, 버튼 등으로 읽도록 하기 위해 AccessibilityNodeInfo setClassName 메소드를 사용해야 할 경우가 종종 있습니다.

    그런데 해당 AccessibilityNodeInfo 객체를 만들 때는 반드시 컨테이너 뷰가 아닌, 그 컨트롤을 가지고 있는 뷰 자체, 즉 텍스트 혹은 대체 텍스트가 있는 뷰에 선언을 해 주어야 합니다.

    예를 들어보겠습니다.

    체크박스를 ImageView를 사용하여 커스텀으로 제공했다고 가정해 봅시다.

    그런데 그 이미지뷰 상위에는 <LinearLayout> 컨테이너가 있으며 해당 컨테이너에는 ImageView 하나만 있다고 가정해 봅시다.

    <LinearLayout>

     <ImageView>

     android:text="햄버거 먹기"

    </>

    </LinearLayout>

    이때 체크박스로 읽도록 AccessibilityNodeInfo 객체를 선언해 주어야 하는 것은 LinearLayout이 아니라 ImageView입니다.

    컨테이너 뷰에 선언을 해도 체크박스라고 읽긴 하지만 사용성에서 약간의 문제가 있습니다. 

    이 문제에 대해서는 다음 팁에서 말씀드리겠습니다.

    댓글을 작성하려면 해주세요.
  • qna
    table tr마크업 문의
    eirene100999 2020-10-05 17:31:09

     table을 작업하다가 문의사항이 있어 올려봅니다

    아래 <html 마크업 이미지>에 (빨간라인참고) table 첫번째 tr의 태그에 rowspan값을 tr갯수로 넣고 웹화면을 보았는데 깨지지 않아서요

    <html 마크업 이미지>에 있는 마크업처럼 접근성과 사용성방면으로 사용해도 되는지 궁금합니다

    (웹화면의 결과는 ie, FF 모두 동일하게 깨지지 않습니다)

     

    <html 마크업 이미지>

    샘플 html

     

    <웹 결과 화면>

    샘플 크롬화면

     

     

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] iOS 14.2에서의 특정 웹뷰 포커스 튀는 버그 해결 관련
    Webacc NV 2020-10-05 15:37:25

    얼마전 iOS 14로 업데이트 되면서 특정 웹뷰에서 스크린 리더 사용자가 한 손가락 쓸기로 화면 탐색 시에 보이스오버 포커스가 첫 요소로 튀는 이슈에 대해 공유한 적이 있습니다.

    지난 번에도 말씀드린 바와 같이 해당 이슈는 보이스오버에서의 특정 웹뷰 처리 시 버그이며 현재 해당 버그는 14.2 버전에서 해결되었습니다.

    물론 14.2 버전은 아직 개발자 버전으로만 배포되어 정식 출시되지는 않았지만 접근성 테스트 시에 특정 웹뷰에서 포커스가 상단으로 튀는 이슈 발생 시 참고 부탁드리며 이후 정식 버전 출시 때 업데이트 하시면 해당 이슈는 없을 것입니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 체크박스 접근성 적용 예제
    Webacc NV 2020-09-28 12:49:33

    지난 팁에 이어서 오늘은 안드로이드 커스텀 체크박스를 스크린 리더에서 체크박스 및 체크됨, 체크안 됨을 읽을 수 있도록 하는 예제 코드를 공유하려고 합니다.

    먼저 체크박스는 ImageView로 구현하였다고 가정하며 isChecked 라는 boolean 메소드를 통해서 상태가 변경됩니다.

    checkBox.setAccessibilityDelegate(checkBoxAccessibilityDelegate);
    final View.AccessibilityDelegate checkBoxAccessibilityDelegate = new View.AccessibilityDelegate() {
        @Override
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
            super.onInitializeAccessibilityNodeInfo(host, info);
            info.setClassName("android.widget.CheckBox");
            //setCheckable을 적용해야 음성 안내
            info.setCheckable(true);
            info.setChecked(isChecked);
        }
    };
    댓글을 작성하려면 해주세요.
  • tip
    [Android native] 커스텀 체크박스에 접근성 적용하기
    Webacc NV 2020-09-25 12:40:52

    여러 번 말씀드린 바와 같이 항상 커스텀 컨트롤을 사용해서 체크박스, 버튼, 라디오버튼 등을 구현하는 경우는 접근성 고려에 더 많은 고민이 필요합니다.

    그래서 접근성 구현 관점에서 생각해본다면 각 요소의 네이티브 컨트롤을 사용하는 것이 가장 좋은 것임은 두말할 나위가 없습니다.

    그 중 하나가 체크박스입니다. 

    체크박스 역시 네이티브 CheckBox 클래스를 사용하게 되면 접근성 구현에 대한 별다른 대응을 하지 않아도 됩니다.

    체크박스라는 유형, 체크가 가능하는 것, 현재 상태 등을 스크린 리더가 사용자에게 정확하게 알려주기 때문입니다.

    그런데 체크박스를 표준 컨트롤을 사용하지 않고 ImageView를 사용하여 구현했다고 가정해 봅시다.

    체크가 되었는지 그렇지 않은지는 이미지뷰에서 백그라운드 이미지 파일을 변경해서 표시했다고 가정합시다.

    그러면 스크린 리더 사용자는 해당 뷰가 체크박스인지 조차 알 수 없으며 

    체크가 되었는지도 알 수 없을 것입니다.

    이때 접근성을 구현하기 위해 AccessibilityNodeInfo 속성을 수정할 수 있습니다.

    1. 실제로 해당 뷰는 이미지뷰이지만 체크박스라는 것을 알려 주어야 하므로 AccessibilityNodeInfo 객체 안에서 클래스 네임을 체크박스로 변경해야 하고

    2. 이미지뷰는 아무런 상태를 가지지 않지만 해당 체크박스는 체크 혹은 체크해제를 할 수 있는 상태정보를 가진다는 것을 알려주기 위해 setCheckable을 true로 설정해야 하며 마지막으로

    3. 체크가 되었는지 혹은 해제가 되었는지를 알려주기 위해 setChecked를 조건문에 맞게 변경해 주어야 합니다.

    다음 팀에서는 샘플 예시 코드를 공유하겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] iOS 14 특정 웹뷰 콘텐츠 보이스오버 초점 이슈 관련
    Webacc NV 2020-09-21 16:11:40

    얼마전에 iOS 14 정식 버전이 출시되었으며 이번 버전에서도 여러 접근성과 관련된 업데이트가 있었습니다.

    그런데 현재 크롬, 사파리 등을 제외한 특정 웹뷰에서 보이스오버 초점이 웹뷰의 첫 요소로 튀어 버리는 이슈가 발생하고 있습니다.

    구체적인 증상은 다음과 같습니다.

    1. 특정 웹뷰에서 한 손가락 쓸기로 이동 시에 두 번째 링크에 포커스를 하면 초점이 첫 링크로 튀어버리는 이슈가 발생합니다.

    2. 한 손가락 오른쪽 혹은 왼쪽 쓸기가 아닌 특정 링크를 임의 터치를 통해 선택한 경우에는 포커스가 튀지 않습니다.

    해당 이슈를 애플 접근성팀에 문의한 결과 보이스오버로 특정 웹뷰에 포커스 한 경우 발생되는 버그이며 다음 버전에서 해당 이슈는 수정될 것이라 합니다.

    접근성 테스트 하시는 분들은 참고하시면 좋겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] RatingBar class 접근성 적용 예시
    Webacc NV 2020-09-16 18:44:23

    지난 글에서 RatingBar 클래스만 사용하게 되면 스크린 리더 사용자가 볼륨키로 점수를 조절할 수 없다는 것에 대해 말씀드렸습니다.

    그래서 톡백에서 이 RatingBar를 SeekBar로 인식할 수 있도록 다음 예시와 같이 수정할 수 있습니다.

    ratingbar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
            super.onInitializeAccessibilityNodeInfo(host, info);
            info.setClassName(SeekBar.class.getName());
        }
    });

    댓글을 작성하려면 해주세요.
  • tip
    [스크린리더] 가상커서의 이해 - 특정 요소에서 가상커서가 자동으로 꺼지는 이유
    Webacc NV 2020-09-14 15:06:56

    학습장애나 시각장애가 있는 개발자분도 이 세상엔 분명 많지만, 대다수는 그렇지 않습니다. 이는 스크린리더를 사용하는 사용자가 적다는 뜻으로도 이해할 수 있습니다. HTML5에서는 시멘틱이 강조되고, UI 형태에 따라 WAI-ARIA의 role을 사용하여 네이티브 앱에 있는 더 많은 유형을 지원할 수 있게 되었습니다.

    웹과 앱은 근본적인 차이점이 있습니다. Windows 응용프로그램에서는 가상커서라는 개념을 사용하지 않습니다. 대부분 스크린리더에서 가상커서와 동등한 개념을 가진 기능들은 웹 콘텐츠와 웹엔진을 기반으로 만들어진 소프트웨어(앱)에서만 동작합니다(예외적으로 Microsoft Narrator의 스켄 모드는 Windows 네이티브에서도 동작합니다).

    웹기반이 아니라면, 읽기포인터나 객체 탐색와 같은 스크린리더의 다른 탐색 기능을 활용하여 UI 텍스트를 읽고, Tab과 방향키, Space와 Enter만으로 모든 컨트롤을 조작하지요.

    하지만 웹에서는 가상커서라는 웹을 탐색하는 별도의 모드와 Windows의 기본 커서가 같이 동작합니다. 그리고 가상커서는 위, 아래, 왼쪽 오른쪽 화살표 키를 텍스트와 요소를 탐색하는 용도로 사용하며, 헤딩(h키) 헤딩 레빌 1부터 6까지(상단 숫자 1키부터 6키) 등, 일반적으로 텍스트를 입력하는 것 외의 아무 기능이 없는 키에 가상커서의 빠른 탐색키를 배치합니다.

    role은 단순히 스크린리더에 유형 이름만을 제공하지 않습니다. role은 요소를 읽고 사용자가 조작할 수 있는지(Read & Write), 혹은 읽기만 가능한지(Read Only). 간단하게 말해서 사용자가 상호작용할 수 있는 요소인지를 스크린리더에게 전달합니다. 그리고, 사용자가 조작이 가능하다면 기본 설정상 가상커서를 자동으로 끄게끔 되어있습니다.

    가상커서가 동작한다면 글자단위 읽기와 문단 읽기가 화살표 키에 할당되어 있어 라디오버튼을 방향키로 선택할 수 없고, 입력창에서 가상커서가 동작한다면 제목으로 바로가거나, 링크로 바로가는 키가 동작하여 글자를 입력할 수 없기 때문입니다. 

    한 문장으로 압축하여 설명드리자면, "이 요소 내에서는 다른 조작방법이 필요합니다"입니다.

    Windows에서 제공하는 tab, menu, slider 등 마우스로 클릭하여 선택하거나, 드래그하고, 특정 지점을 눌러 수치를 변경하는 작업 등을 수행하는 요소에는 반드시 키보드 조작법이 있으며, 이러한 키보드 조작이 필요한 요소를 위젯 요소(widget role)라고 합니다.

    이러한 위젯 요소는 대부분 키보드 조작이 있으므로 가상커서가 꺼짐을 유의하고, 가상커서가 꺼지는 요소라면 반드시 네이티브와 동일하거나 유사한 조작방법을 꼭 제공해 주세요.

    재생시간 조절 슬라이더라고 알려주고, 가상커서도 꺼주는데 오른쪽/왼쪽 방향키로 되감기 기능을 수행할 수 없으면 혼란스러우니까요 :(

     

    댓글을 작성하려면 해주세요.
  • tip
    [JavaScript] 스크린리더 사용자를 위한 재생 슬라이더 시간정보 업데이트에 관하여
    Webacc NV 2020-09-10 13:46:43

    웹페이지에서 재생 슬라이더를 만들 때, input[type="range"]과 맥을 같이 하는 role="slider" 역할을 사용하여 커스텀 슬라이더를 만들게 됩니다.

    role="slider"에는 총 네가지 보조 속성이 있는데, 실질적으로 스크린리더에 영향을 미치는 속성과 개발자 편의를 위한 속성이 있습니다.

    [개발자 편의를 위한 속성]에는 aria-valuemin, aria-valuemax가 있으며, [스크린리더에 영향을 미치는 속성]에는 aria-valuenow와 aria-valuetext가 있습니다.

    HTML 5에 추가된 input의 유형중 range에는 max와 min 속성값이 있습니다. 이와 마찬가지로 개발자가 slider를 개발할 때, 최소값 제한과 최댓값 제한을 Number(Element.getAttribute('aria-valuemin')), Number(Element.getAttribute('aria-valuemax')) 이렇게 가져와 이 밑으로 슬라이더의 현재값의 재한을 구현할 때 참조용으로 사용하는 것입니다.

    이 중에서 우리가 주목해야할 것은 aria-valuenow와 aria-valuetext입니다.

    먼저, aria-valuenow는 input에 value속성을 넣는 것과 같은 역할을 수행합니다. 값으로는 숫자만을 받으며, aria-valuemin과 vlauemax가 설정되있더라도, 강제로 유효하지 않은 값이 들어갈 수 있으므로, 재생 슬라이더를 구현할 때는 반드시 두 속성을 참조하여 인터렉션에 재한을 둬야합니다.

    aria-valuetext는 단위 등의 텍스트 보조 정보가 들어와야 할 때 사용하는 속성으로, 재생 슬라이더에서는 다음과 같은 텍스트를 전달하게 됩니다. 이 속성이 없다면, aria-valuenow의 숫자 값을 전달합니다.

    const volumeSlider = document.getElementById('slider-vol');
    const currentVolume = Number(slider.getAttribute('aria-valuenow'));
    volumeSlider.setAttribute('aria-valuetext',`${currentVolume} percent`);

    접근성 기능을 제공하고자 이들을 제공한 많은 슬라이더에서 오디오/비디오가 재생하는 동안 이 valuenow / valuetext를 개속 갱신하게끔 해놓은 것을 볼 수 있습니다. 그런데, 그렇게 개속 valuenow / valuetext를 갱신하면 약간의 문제가 있습니다.

    aria-valuenow /aria-valuetext는 aria-label처럼 초점을 받은 요소의 해당 속성값이 갱신될 때 마다 스크린리더 사용자에게 정보를 알립니다. 즉, 재생 슬라이더를 조절하려고 초점을 두고 있으면, aria-valuetext가 바뀔 때마다 정신이 없을 정도로 재생시간을 읽어주게 되는 것입니다.

    이 aria-valuenow / aria-valuetext는 일반적인 슬라이더처럼 키보드 동작이 발생했을 때(keydown, keyup)나 해당 요소에 초점이 갔을 때(focusin) 상태에서만 갱신이 되어야 합니다.

    따라서 위 문제를 해결하려면, 오디오가 업데이트되었을 때에는 valuetext를 갱신하지 않고, 슬라이더에서 값을 조절 좌/우 방향키 또는 Youtube 등에서 제공하는 되감기 키를 눌렀을 때나, 해당 슬라이더를 사용자가 조작하려고 초점을 보냈을 때만 이 값이 바뀌게끔 하면, 이러한 문제를 해결할 수 있으며, 스크린리더 사용자가 편하게 사용할 수 있는 커스텀 슬라이더를 만들 수 있습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [Android native] RatingBar 클래스 적용시 접근성 대응
    Webacc NV 2020-09-10 12:21:01

    만족도를 평가하는 별점을 구현할때 일반적으로 RatingBar 클래스를 사용합니다.

    그런데 RatingBar 클래스로 구현 시에는 스크린 리더 사용자가 별점을 주기 위해 해당 요소를 길게 이중탭한 상태로 오른쪽 혹은 왼쪽으로 스크롤해서 정확하게 원하는 점수에 맞추어 주어야 합니다.

    사실 이중탭한 상태로 왼쪽 오른쪽으로 슬라이드하면서 정확한 값에 맞추는 것이 스크린 리더 사용자에게 쉽지 않기 때문에 이에 대한 접근성 개선이 필요합니다.

    개선 방법은 접근성 노드 정보에서 해당 RatingBar 클래스를 SeekBar로 인식하도록 변경해 주면 됩니다.

    그렇게 변경하면 실제 클래스는 RatingBar이지만 접근성 노드 정보의 클래스는 SeekBar이기 때문에 볼륨키를 통해서 스크롤이 가능합니다.

    이는 SeekBar로 변경하는 순간 SeekBar가 가지고 있는 AccessibilityAction 정보, performAccessibilityAction 이벤트가 부여되기 때문입니다.

    코드 예시에 대해서는 다음 팁에서 다루겠습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [WAI-ARIA]aria-label보단 aria-labelledby를 사용해야하는 이유
    Webacc NV 2020-09-08 12:36:07

    아이콘 버튼에 스크린리더용 대체텍스트를 제공하는 방법에는 IR기법으로 버튼 내부에 있는 텍스트를 숨기는 방법과 aria-label로 버튼에 대체텍스트를 제공하는 방법이 있습니다.

    aria-label은 마치 aria-live를 제공한 것 처럼, 버튼을 눌렀을 때, 레이블 텍스트가 변경되면 이를 알리는 좋은 기능을 수행합니다. 단점도 존재하는데, 바로 웹사이트 번역기능을 사용하는 외국인에게 불친절하다는 점입니다.

    aria-label 속성값은 많은 웹사이트 번역기에서 번역하지 못합니다. 일반적인 텍스트 노드만을 번역하는 경우가 많기 때문으로 보입니다.

    반면에 aria-labelledby는 이미 입력된 html 요소의 값을 가져오기 때문에 번역이 가능한 대체텍스트를 제공하기 쉽습니다. 웹사이트 번역 API에서 이 텍스트들을 번역할 수 있도록 스크립트로 동적으로 레이블 요소를 생성하지 않고, 정적으로 작성하여 display:none으로 숨겨두는 것이지요.

    레이블용 요소들을 display:none으로 숨기더라도, id 레퍼런스에서 가저온 텍스트 값은 유효하기 때문에 시각적으로는 텍스트를 공개하지 않고 편리하게 레이블을 제공할 수 있습니다.

    만약, aria-roledescription같이 아이디 레퍼런스로 연결하는 속성이 별도로 없다면 자바스크립트에서 aria-labelledby처럼 스크립트 내부에서 미리 작성된 요소의 아이디를 document.querySelector('#...').innerText나 document.getElementById('id').innerText, JQuery에서 $('#id').text()등으로 불러와서 넣어주면 비슷한 효과를 낼 수 있을 것 같습니다.

    댓글을 작성하려면 해주세요.
  • tip
    [iOS native] accessibilityViewIsModal에 관하여
    Webacc NV 2020-09-07 20:46:01

    중첩된 화면에서 가려진 화면에 스크린 리더 초점이 가지 않게 하는 것은 접근성에서 굉장히 중요합니다.

    얼마전에 안드로이드 플랫폼에서의 적용 방법에 대해서는 이미 포럼에 공유를 했습니다.

    iOS에는 accessibilityViewIsModal 이라는 속성이 있습니다.

    이것을 true로 설정하면 같은 계층의 다른 형제들은 스크린 리더에서 포커스가 안 됩니다.

    따라서 A, B 컨테이너가 있다고 가정했을 때 현재 화면 상에서 B 컨테이너만 보여지는 경우에는 B 컨테이너에 accessibilityViewIsModal을 true로 설정함으로써 가려진 A 컨테이너에 포커스가 되는 것을 막을 수 있습니다.

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