접근성이 뛰어난 Angular 앱 빌드

b23a30a52c1169e4.png

접근성은 사용자가 앱을 인식하고, 이해하며, 탐색하고, 상호작용할 수 있도록 해주는 기능으로 웹 개발에서 중요한 부분입니다. 실제로 미국 성인 4명 중 1명은 삶에서 중요한 활동에 영향을 미치는 장애를 가지고 있습니다. 전 세계 인구의 약 15%인 10억 명 이상이 어떠한 형태로든 장애를 가지고 있으며 약 2~4%는 중대한 어려움을 겪고 있습니다.

사람의 웹 사용에 영향을 주는 일반적인 상태로는 시각 장애나 저시력, 청각 장애, 난청 장애, 제한적인 운동 기능, 인지 장애, 색맹 등이 있으며 이 외에도 다양합니다.

이 과정에서 a11y는 접근성(accessibility)의 약어입니다. a 다음에 11개의 문자와 y를 나타냅니다.

접근성이 뛰어난 앱을 설계하는 데 따르는 문제와 사용할 수 있는 기법을 자세히 알아보려면 접근성을 참조하세요.

과정 목표

  • 권장사항과 기본 제공되는 기법을 사용하여 Dumling Shop Angular 데모 앱에서 일반적인 웹 접근성 문제를 해결합니다.
  • 모든 접근성 가이드라인, WCAG 2.0, ARIA 1.2를 충족하고 axe 및 Lighthouse 접근성 감사를 통과합니다.

818cc91d17fae486.png 510ca511c265da81.png

과정 내용

Angular 앱에서 사용자에게 영향을 미치는 8가지의 일반적인 접근성 문제에 대해 알아보고 문제를 확인 및 해결하는 방법을 살펴봅니다. 구체적으로 다음과 같은 내용을 살펴봅니다.

  • Chrome 개발자 도구, Lighthouse, axe를 사용해 앱 접근성 감사
  • 고유한 페이지 제목으로 단일 페이지 앱(SPA) 문제 해결
  • 저시력 사용자를 위해 채도가 낮은 대비 문제 해결
  • 스크린 리더가 페이지를 올바르게 탐색할 수 있도록 시맨틱 HTML 사용
  • 스크린 리더가 모든 컨트롤에 액세스할 수 있도록 Angular Material 및 중첩 해제 컨트롤 사용
  • 스크린 리더용 ARIA 지원 추가
  • Angular CDK a11y 패키지 가져오기 및 사용
  • 맞춤 구성요소 스크린 리더 탐색에 FocusTrap 사용
  • CDK LiveAnnouncer로 알림 음성 안내
  • 고대비 모드 사용자 감지 및 고대비 테마 구현

필요한 사항

코드 가져오기

이 프로젝트에 필요한 모든 코드는 GitHub 저장소에 있습니다. 시작하려면 코드를 클론하고 선호하는 개발 환경에서 여세요.

저장소 클론 및 앱 제공

이 Codelab을 진행하는 동안에는 VSCode 또는 로컬 IDE를 사용하는 것이 좋습니다.

  1. 새 브라우저 탭을 열고 https://github.com/googlecodelabs/angular-accessibility로 이동합니다.
  2. 저장소를 포크 및 클론하고 cd angular-accessibility/를 입력하여 해당 저장소로 이동합니다.
  3. 시작 코드 브랜치 git checkout get-started를 확인합니다.
  4. VSCode 또는 선호하는 IDE에서 코드를 엽니다.
  5. npm install을 실행하여 서버를 실행하는 데 필요한 종속 항목을 설치합니다.
  6. ng serve를 실행하여 서버를 실행합니다.
  7. 브라우저 탭을 열고 http://localhost:4200으로 이동합니다.

시작하기

시작점은 이 Codelab을 위해 설계된 기본 레스토랑 앱입니다. 이 Codelab의 개념을 보여주기 위해 코드를 단순화했으며 기능은 거의 없습니다.

93a2d45cdd58d830.png

데모 살펴보기

시작하려면 앱의 3가지 기능을 살펴보세요.

  1. 탐색 메뉴를 사용하여 매장, 스토리, 문의하기 경로를 확인하고 만두 회사에 관한 세부정보를 확인합니다.
  2. 테마를 변경하여 밝은 모드와 어두운 모드 간에 전환합니다.
  3. 주문할 수 있는 만두소, 수량, 색상을 맞춤설정합니다.
  4. 구매를 선택하여 맞춤설정한 주문을 콘솔에 로깅합니다.

Angular를 사용해 일반적인 웹 접근성 문제 해결

이 Codelab에서는 이 앱의 기존 기능에 대한 접근성에 중점을 둡니다. 먼저 앱에서 a11y 문제를 식별한 후 솔루션을 구현하여 🛑를 ✅로 바꿉니다.

수정해야 할 대상을 어떻게 알 수 있나요?

수동 테스트와 자동 테스트를 병행하여 접근성 문제를 인식하고 각 예시를 시작합니다.

현재 웹 버전에서는 접근성을 수동으로 테스트해야 합니다.

접근성 문제를 식별할 수 있는 도구는 있지만 앱의 접근성이 완전하다는 것을 보장할 수 있는 도구는 없습니다. 수동 테스트를 통해 논리적인 콘텐츠 순서와 기능 패리티가 포함된 다양한 a11y 개념을 테스트할 수 있습니다.

수동 테스트

이 과정에서 접근성을 수동으로 테스트하려면 컴퓨터에 기본 제공되는 스크린 리더를 사용 설정하고 키보드 탐색으로 앱을 탐색합니다. 자세한 내용은 시맨틱 및 스크린 리더를 참조하세요.

스크린 리더를 사용 설정하고 화면을 탐색하여 연습하세요.

MacOS에 기본 제공되는 VoiceOver를 사용할 수 있습니다. 사용 설정하려면 System Preferences(시스템 환경설정) > Accessibility(접근성) > VoiceOver > Enable VoiceOver(VoiceOver 사용 설정)를 클릭합니다. VoiceOver로 전환하려면 Command 키를 누른 상태에서 TouchID를 빠르게 세 번 누릅니다.

이 과정에서는 주로 문제를 수동으로 테스트하고 자동화된 도구를 사용하여 특정 자동화 기능을 검사합니다.

자동 테스트

또한 몇 가지 개발 도구를 사용하여 앱을 자동화하고 감사합니다. 이러한 도구를 사용하면 이미지에 대체 텍스트가 있는지 여부나 텍스트 색상의 대비율 등을 확인할 수 있습니다. 이러한 도구는 린터라고 생각할 수 있습니다. 대체 텍스트가 있음을 인식할 수 있지만 콘텐츠가 논리적이며 값을 제공하는지 직접 확인해야 합니다.

Lighthouse 및 Chrome 개발자 도구

  1. Chrome 개발자 도구를 엽니다.
  2. Lighthouse 탭을 선택하고 접근성 체크박스를 선택합니다.
  3. 보고서 생성을 클릭하여 a11y Lighthouse 감사를 실행합니다.

3935811012517f4e.png

Axe

  1. axe DevTools 확장 프로그램을 설치합니다. 확장 프로그램을 보려면 브라우저를 다시 시작해야 할 수 있습니다.
  2. Chrome 개발자 도구를 엽니다.
  3. axe DevTools 탭을 선택하고 Scan all of my page(모든 페이지 스캔)을 선택하여 axe DevTools 스캔을 실행합니다.

7033f0ef4c1c3210.png

린트 작업

Angular ESLint 규칙을 사용하여 코드에서 자동 생성된 a11y 속성을 린트 작업할 수 있습니다.

.eslintrc.json에서 접근성에 적용되는 다음 항목을 추가합니다.

"@angular-eslint/template/accessibility-alt-text": 2,
"@angular-eslint/template/accessibility-elements-content": 2,
"@angular-eslint/template/accessibility-label-for": 2,
"@angular-eslint/template/no-positive-tabindex": 2,
"@angular-eslint/template/accessibility-table-scope": 2,
"@angular-eslint/template/accessibility-valid-aria": 2,
"@angular-eslint/template/click-events-have-key-events": 2,
"@angular-eslint/template/mouse-events-have-key-events": 2,
"@angular-eslint/template/no-autofocus": 2,
"@angular-eslint/template/no-distracting-elements": 2

자세한 내용은 GitHub의 최신 ESLint 규칙을 참조하세요.

시작점

새로운 테스트 방법을 사용하면 Lighthouse 및 axe 감사, 수동 VoiceOver를 사용해 앱의 다음 문제를 식별할 수 있습니다.

1356f16f5505c9c9.png

접근성 감사:

  • 🛑 모든 페이지의 페이지 제목이 동일합니다.
  • 🛑 요소의 색상 대비가 충분해야 합니다.
  • 🛑 HTML에는 논리적 순서, 이름, 역할이 있어야 합니다.
  • 🛑 스크린 리더에는 중첩된 체크박스를 선택할 수 없습니다.
  • 🛑 스크린 리더가 슬라이더 값을 읽지 않습니다.
  • 🛑 스크린 리더의 포커스가 색상 선택 도구에 있으면 대화상자가 종료됩니다.
  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

간결하고 간결한 페이지 제목을 제공하면 a11y 서비스를 사용하는 사용자가 웹페이지의 콘텐츠와 목적을 빠르게 이해하는 데 도움이 됩니다. 페이지 제목은 시각적 장애가 있는 사용자에게 매우 중요한데, 이는 스크린 리더 소프트웨어가 말하는 첫 번째 페이지 요소이기 때문입니다.

Angular는 단일 페이지 앱이므로 새 페이지로 이동과 같은 대부분의 전환이 페이지 새로고침을 포함하지 않습니다. 즉, 기본적으로 각 페이지는 페이지 제목이 동일하게 읽히며 페이지의 콘텐츠나 목적을 이해하는 데 도움이 되지 않습니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 모든 페이지의 페이지 제목이 동일합니다.

다음 주석 아래에서 이러한 각 단계를 확인할 수 있습니다. TODO: #4. Define unique page titles.

문제 파악

문제를 파악하려면 스크린 리더를 사용 설정하고 매장, 스토리, 문의하기 탭을 탐색하여 페이지 제목을 확인하세요.

  1. VoiceOver를 켭니다.
  2. 페이지 탐색을 사용하여 페이지 간에 이동합니다.
  3. Angular에서 페이지 제목이 항상 a11y인지 확인합니다.

이는 페이지 제목이 고유해야 하기 때문에 문제가 됩니다. 사용자가 페이지를 탐색하지 않고도 페이지의 내용을 신속하게 파악할 수 있어야 하기 때문입니다.

cb92438126bb2be8.png

의미 있는 페이지 제목 추가

페이지 또는 뷰가 변경되면 페이지 제목을 적절하게 관리하는 것이 좋습니다. 이 문제를 해결하려면 Angular의 Title 서비스를 사용하여 각 페이지의 고유한 제목을 정의합니다.

  1. 정의된 세 개의 경로 각각에 고유한 제목을 추가합니다.

src/app/app-routing.module.ts

const routes: Routes = [
  { path: 'shop', component: ShopComponent, data: {title: 'Our Shop – a11y in Angular'} },
  { path: 'about', component: AboutComponent, data: {title: 'Our Story - a11y in Angular'} },
  { path: 'locate', component: LocationComponent, data: {title: 'Find Us - a11y in Angular'} },
  { path: '',   redirectTo: '/shop', pathMatch: 'full' },
  { path: '**', component: ShopComponent },
];
  1. Title 서비스를 사용하여 경로 데이터를 기반으로 페이지 제목을 설정합니다.

src/app/app.component.ts

import { Title } from '@angular/platform-browser';
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
import { filter, map } from 'rxjs/operators';

export class AppComponent implements OnInit {
  title = 'a11y in Angular';

  constructor(private titleService: Title,
              private router: Router,
              private activatedRoute: ActivatedRoute) {}

  ngOnInit(): void {
    const appTitle = this.titleService.getTitle();
    this.router
      .events.pipe(
        filter(event => event instanceof NavigationEnd),
        map(() => {
          const child = this.activatedRoute.firstChild;
          if (child?.snapshot.data.title) {
            return child?.snapshot.data.title;
          }
          return appTitle;
        })
      ).subscribe((title: string) => {
        this.titleService.setTitle(title);
      });
  }
}

변경사항 확인

스크린 리더를 다시 사용 설정하고 변경사항을 확인합니다. 이제 페이지마다 고유한 제목이 생깁니다.

5a85fdb689464137.png

접근성 감사:

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 🛑 요소의 색상 대비가 충분해야 합니다.
  • 🛑 HTML에는 논리적 순서, 이름, 역할이 있어야 합니다.
  • 🛑 스크린 리더에는 중첩된 과일 체크박스를 선택할 수 없습니다.
  • 🛑 스크린 리더가 슬라이더 값을 읽지 않습니다.
  • 🛑 스크린 리더의 포커스가 색상 선택 도구에 있으면 대화상자가 종료됩니다.
  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

디자인이 세련된 것처럼 보일 수 있지만 색맹처럼 시각 장애가 있는 사용자가 콘텐츠를 읽을 수 없다면 세련되었다고 할 수 없습니다. 웹 콘텐츠 접근성 가이드라인(WCAG 2.0)은 콘텐츠의 접근성을 보장하는 일련의 색 대비율을 정의합니다. Angular와 웹에서는 구성요소가 이러한 표준을 충족하고 저시력과 색맹인 사용자가 볼 수 있도록 해주는 색상 팔레트를 정의할 수 있습니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 요소의 색상 대비가 충분해야 합니다.

다음 주석 아래에서 이러한 각 단계를 확인할 수 있습니다. TODO: #5. Ensure adequate color contrast.

Chrome 개발자 도구를 사용하여 대비가 낮은 문제 식별

이 문제를 파악하려면 Chrome 개발자 도구를 사용해 앱의 요소를 검사합니다.

  1. 검사 도구를 사용해 메뉴 아이콘 버튼을 확인합니다. 대비가 WCAG 요구사항보다 한참 낮은 1.85임을 확인할 수 있습니다.

b2e27bca38a1649c.png

  1. Lighthouse의 접근성 감사 또는 axe의 스캔을 실행하여 이러한 대비율 문제를 확인합니다.

c1cbc422fcdc89b.png

Material 테마 색상 변경

구성요소의 색 구성표는 맞춤설정 Material 테마에 정의되어 있습니다. 색상 대비율 가이드라인을 준수하도록 테마 값을 업데이트합니다.

어두운 텍스트 색상을 사용하도록 Material 테마를 업데이트하여 아이콘의 대비율을 높입니다.

src/styles.scss

$light-primary: mat.define-palette(mat.$pink-palette, $default: A100, $lighter: 100, $text: 900);

Chrome 개발자 도구에서 기본 제공 접근성 도구를 사용하여 표준을 충족하는 색상을 찾거나 Sass에서 개별 색상 값을 업데이트할 수도 있습니다.

변경사항 확인

요소를 다시 검사하고 변경사항을 확인합니다. 이제 페이지에 고유한 페이지 제목이 생깁니다.

a990337d9f3127fd.png

접근성 감사

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 색상의 대비율이 충분합니다.
  • 🛑 HTML에는 논리적 순서, 이름, 역할이 있어야 합니다.
  • 🛑 스크린 리더에는 중첩된 과일 체크박스를 선택할 수 없습니다.
  • 🛑 스크린 리더가 슬라이더 값을 읽지 않습니다.
  • 🛑 스크린 리더의 포커스가 색상 선택 도구에 있으면 대화상자가 종료됩니다.
  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

기본 HTML 요소는 접근성에 중요한 여러 표준 상호작용 패턴을 캡처합니다. 단락을 스팬으로, div를 버튼으로 스타일을 지정할 수 있지만 시맨틱 HTML 요소를 사용하면 스크린 리더와 키보드 탐색 기능으로 HTML의 상호작용과 컨트롤을 파악할 수 있습니다.

Angular 구성요소를 작성할 때는 제대로 지원되는 동작을 다시 구현하기보다는 가능하면 이러한 기본 요소를 직접 재사용해야 합니다. 이렇게 하면 페이지의 적절한 콘텐츠 구조와 자연스러운 콘텐츠 흐름이 가능하고 사용자가 키보드를 효율적으로 사용하여 웹사이트를 탐색할 수 있도록 탭이 논리적인 순서로 배열됩니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 HTML에는 논리적 순서, 이름, 역할이 있어야 합니다.

다음 주석 아래에서 이러한 각 단계를 확인할 수 있습니다. TODO: #6. Use Semantic HTML.

문제 파악

  1. VoiceOver를 켭니다.
  2. 탭 탐색을 사용하고 스토리 탭을 클릭하여 이동합니다.
  3. 탭이 순서대로 되어 있지 않은지 확인합니다.
  4. 구매를 클릭합니다.
  5. 버튼이 버튼으로 인식되지 않는지 확인합니다.

69b5875f4d51d836.png

<div>를 <button>으로 변경

맞춤 <div>를 Material 버튼으로 바꿉니다.

src/app/shop/shop.component.html

<button mat-flat-button
  color="primary"
  class="purchase-button"
  (click)="fauxPurchase()">
  Purchase
</button>

heading 요소를 순차적으로 사용

텍스트를 재정렬하여 시맨틱 HTML을 사용하고 Angular Material 입력 체계를 사용하여 스타일을 적용합니다.

src/app/about/about.component.html

<h2>Who are we?</h2>
<p class="mat-subheading-2">Have you ever thought, "wow, I love dumplings"?</p>
<p class="right mat-subheading-1">Who hasn't.</p>
<p class="center mat-subheading-1">We took it one step further and created Dumpling Dumpling,</p>
<p class="center mat-subheading-1">double the dumpling, double the fun.</p>
<div class="spacer"></div>
<h2>How are we different?</h2>
<p class="mat-subheading-2">Handmade in San Francisco, California, we craft fully customizable dumplings. Glitter? Rainbows? Vegan? We do it all.</p>
<p class="right mat-subheading-2">This shop is concept only.</p>

변경사항 확인

스크린 리더를 다시 사용 설정하고 변경사항을 확인합니다. 이제 VoiceOver가 버튼을 인식하고 텍스트를 논리적인 순서로 읽습니다.

접근성 감사:

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 색상의 대비율이 충분합니다.
  • 시맨틱 HTML은 논리적 상호작용을 보장합니다.
  • 🛑 스크린 리더에는 중첩된 체크박스를 선택할 수 없습니다.
  • 🛑 스크린 리더가 슬라이더 값을 읽지 않습니다.
  • 🛑 스크린 리더의 포커스가 색상 선택 도구에 있으면 대화상자가 종료됩니다.
  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

접근성 서비스의 복잡한 상호작용 패턴 중 하나는 중첩된 컨트롤입니다. 메뉴 하위 항목 또는 중첩된 체크박스를 생각해 보세요. 사용자에게 옵션 하위 그룹을 선택하거나 상위 메뉴 항목으로 이동할 수 있다는 것을 어떻게 설명하나요?

Angular에서 메뉴 및 컨트롤을 간소화하면 컨트롤을 최대한 단순하게 만들어 탐색 가능한 구성요소를 생성할 수 있습니다. 이 예시에서는 Angular Material의 목록 상자를 사용하여 이 상호작용 패턴의 한 예시를 빌드합니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 스크린 리더에는 중첩된 체크박스를 선택할 수 없습니다.

다음 주석 아래에서 이러한 각 단계를 확인할 수 있습니다. TODO: #7. Create selectable controls with Angular Material.

문제 파악

이 문제를 파악하기 위해 스크린 리더를 켜고 중첩된 체크박스를 선택해 보겠습니다.

  1. VoiceOver를 켭니다.
  2. 다양한 채우기 버전을 선택합니다.
  3. VoiceOver에서 읽을 때 상위 체크박스가 하위 요소를 지정하지 않음을 확인합니다. 청경채 체크박스를 선택 해제했고 현재 채식 체크박스가 선택 해제되어 있는지 어떻게 확인하나요?

885a9bd0dbbd842a.png

Angular Material의 a11y

시맨틱 체크박스를 이 상호작용 패턴에 대한 기본 정보가 포함된 Angular Material 체크박스로 바꿉니다. 구성요소를 Material로 바꿔도 접근성이 보장되지는 않는다는 점에 유의하세요. 다른 구성요소와 마찬가지로 Material 비접근성을 구현하는 방법이 많이 있으므로 수동으로 테스트해야 합니다.

체크박스를 Material 체크박스로 바꾸기

  1. 먼저 새 채우기 목록과 변수를 추가하여 선택한 채우기 버전을 저장합니다.

src/app/shop/shop.component.ts

@Component(...)
export class ShopComponent implements OnInit {
  fillings: string[] = ['Bok Choy & Chili Crunch', 'Tofu & Mushroom', 'Chicken & Ginger', 'Impossible Meat & Spinach'];
  selectedFillings: string[] = [];

  fauxPurchase(): void {
    let flavor = '';
    this.selectedFillings.forEach(filling => {
      flavor = flavor + " " + filling
    })
  }
}
  1. <mat-selection-list>를 추가하여 다음과 같이 복잡한 HTML 체크박스 그룹을 변경합니다.

src/app/shop/shop.component.html

<mat-selection-list [(ngModel)]="selectedFillings"
  aria-label="Dumpling fillings">
  <mat-list-option *ngFor="let flavor of fillings"
    [value]="flavor"
    color="primary">
    {{ flavor }}
  </mat-list-option>
</mat-selection-list>

TODO 주석은 src/app/shop/shop.component.scss에서 사용하지 않는 Sass를 제거하여 스타일을 정리할 수 있는 위치도 보여줍니다.

변경사항 확인

스크린 리더를 다시 사용 설정하고 변경사항을 확인합니다. 이제 체크박스를 선택할 수 있고 스크린 리더로 더욱 직관적으로 탐색할 수 있습니다.

e9d473e1e7949442.png

접근성 감사:

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 색상의 대비율이 충분합니다.
  • 시맨틱 HTML은 논리적 상호작용을 보장합니다.
  • 스크린 리더로 모든 컨트롤을 사용할 수 있습니다.
  • 🛑 스크린 리더가 슬라이더 값을 읽지 않습니다.
  • 🛑 스크린 리더의 포커스가 색상 선택 도구에 있으면 대화상자가 종료됩니다.
  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

Angular 앱의 시맨틱 HTML 및 Material 구성요소를 수정했지만 일부 구성요소는 스크린 리더로 완전히 탐색하려면 특정 속성이 필요합니다.

Web Accessibility Initiative의 Accessible Rich Internet Applications 사양(WAI-ARIA 또는 ARIA)을 사용하면 기본 HTML로 관리할 수 없는 문제를 해결하는 데 도움이 됩니다. 이 사양을 활용해 요소가 접근성 트리로 변환되는 방식을 수정하는 속성을 지정할 수 있습니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 스크린 리더가 슬라이더 값을 읽지 않습니다.

다음 주석 아래에서 이러한 각 단계를 확인할 수 있습니다. TODO: #8. Provide control labels with ARIA.

문제 파악

이 문제를 확인하려면 스크린 리더를 사용 설정하고 슬라이더를 이동합니다.

  1. VoiceOver를 켭니다.
  2. 수량 슬라이더로 이동하여 값을 변경합니다.
  3. 값 라벨이 누락된 것을 확인합니다.

2d7dd751d8f835e6.png

ARIA 속성 사용

aria-label<mat-slider>를 사용하는 라벨 컨트롤:

src/app/shop/shop.component.html

<mat-slider
  aria-label="Dumpling order quantity slider"
  id="quantity"
  name="quantity"
  color="primary"
  class="quantity-slider"
  [max]="13"
  [min]="1"
  [step]="1"
  [tickInterval]="1"
  thumbLabel
  [(ngModel)]="quantity">
</mat-slider>

변경사항 확인

스크린 리더를 다시 사용 설정하고 변경사항을 확인합니다. 이제 슬라이더를 움직일 수 있습니다.

b21560426b30352c.png

접근성 감사:

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 색상의 대비율이 충분합니다.
  • 시맨틱 HTML은 논리적 상호작용을 보장합니다.
  • 스크린 리더로 모든 컨트롤을 사용할 수 있습니다.
  • 슬라이더는 ARIA 속성을 사용하여 라벨을 제공합니다.
  • 🛑 스크린 리더의 포커스가 색상 선택 도구에 있으면 대화상자가 종료됩니다.
  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

지금까지는 기본 제공되는 Angular 도구를 사용하여 일반적인 a11y 문제를 해결했습니다. 이제 CDK의 a11y 모듈을 살펴보겠습니다. 이 모듈이 더 복잡한 Angular 관련 문제를 해결하는 데 어떻게 도움이 되는지 알아보겠습니다.

이 섹션의 마지막 부분까지는 Angular a11y 모듈 도구를 사용해 이 과정을 계속 진행합니다.

다음 주석 아래에서 이러한 단계를 확인할 수 있습니다. TODO: #9. Add the power of @angular/cdk/a11y.

모듈 가져오기

앱에 모듈을 추가합니다.

src/app/app.module.ts

import { A11yModule } from '@angular/cdk/a11y';

@NgModule({
  declarations: [...],
  imports: [
    A11yModule
  ],
  providers: [...],
  bootstrap: [...]
})

'@angular/cdk/a11y'기능

a11y 모듈은 접근성 향상을 위해 다양한 도구를 제공하며 이러한 도구는 특히 구성요소 작성자에게 유용합니다.

다음 섹션에서는 FocusTrap, LiveAnnouncer, HighContrast의 세 가지 일반적인 서비스를 추가합니다.

@angular/cdk/a11y에서 제공하는 다른 모든 서비스에 대한 자세한 내용은 접근성을 참조하세요.

대화상자나 모달이 열려 있을 때는 사용자가 그 안에서만 상호작용합니다. 포커스가 대화상자 밖을 벗어나도록 허용하면 컨텍스트가 혼합되고 사용자가 페이지에서 어느 위치에 있는지 알 수 없는 상태가 됩니다.

Angular에서 cdkTrapFocus 지시어는 tab-키 포커스를 요소 안으로 가두는 역할을 합니다. 이 지시어는 포커스를 제한해야 하는 모달 대화상자처럼 구성요소에 접근 가능한 환경을 만드는 데 사용하기 위한 것입니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 스크린 리더의 포커스가 색상 선택 도구에 있으면 대화상자가 종료됩니다.

다음 주석 아래에서 이러한 단계를 확인할 수 있습니다. TODO: #10. Control focus with FocusTrap.

문제 파악

이 문제를 파악하려면 스크린 리더를 사용 설정하고 색상 선택 도구 대화상자를 엽니다.

  1. VoiceOver를 켭니다.
  2. 탭 탐색을 사용하여 색상을 변경합니다.
  3. 색상 선택 도구에서 직관적인 포커스 순서와 포커스 트래핑이 표시되는지 확인합니다.

2cad39ca0450a28e.png

FocusTrap 추가

cdkFocusTrap은 맞춤 구성요소의 포커스 순서를 트랩 및 제어하는 데 사용됩니다. mat-dialog-content를 사용하면 대화상자에 포커스를 가두어 대부분의 문제를 충분히 해결할 수 있습니다. cdkFocusInitial 속성을 추가하여 색상 선택 도구 대화상자 내의 만두 포장지 색상 <mat-selection-list>에서 초기 포커스 영역을 정의합니다.

src/app/shop/color-picker/color-picker-dialog/color-picker-dialog.component.html

<mat-selection-list #colors aria-label="Dumpling wrapper color" multiple="false" cdkFocusInitial>
  ...
</mat-selection-list>

변경사항 확인

스크린 리더를 다시 사용 설정하고 변경사항을 확인합니다. 처음에는 대화상자의 색상 변경에 포커스가 설정됩니다.

접근성 감사:

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 색상의 대비율이 충분합니다.
  • 시맨틱 HTML은 논리적 상호작용을 보장합니다.
  • 스크린 리더로 모든 컨트롤을 사용할 수 있습니다.
  • 슬라이더는 ARIA 속성을 사용하여 라벨을 제공합니다.
  • 색상 선택 도구에 올바른 포커스 트래핑이 있습니다.
  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

페이지의 내용이 변경되면 스크린 리더가 알림을 받아야 합니다. 양식을 제출하거나 구매를 완료하려고 하는데 오류가 표시되어 양식을 제출할 수 없고 사용자가 이러한 상황을 파악할 수 없다고 상상해 보세요. 사용자가 불편할 것입니다.

LiveAnnouncer는 스크린 리더 사용자가 알림 및 라이브 페이지 변경사항을 알 수 있도록 aria-live 영역을 사용하여 스크린 리더 사용자에게 메시지를 음성으로 안내하는 데 사용됩니다. aria-live 영역에 대한 자세한 내용은 W3C의 WAI-ARIA를 참조하세요. Angular에서 LiveAnnouncer를 서비스로 호출하는 것이 aria-live 속성을 사용하는 것보다 테스트하기 적합한 솔루션입니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 변경사항, 오류, 알림은 음성으로 안내하지 않습니다.

다음 주석 아래에서 이러한 단계를 확인할 수 있습니다. TODO: #11. Announce changes with LiveAnnouncer.

문제 파악

이 문제를 파악하려면 스크린 리더를 사용 설정하고 양식 입력란을 작성하지 않고 구매를 선택합니다.

  1. VoiceOver를 켭니다.
  2. 탭 탐색을 사용하여 색상을 변경하고 허위로 구매합니다.
  3. 대화상자를 종료할 때 어떤 색상을 선택했는지 알 수 없으며 구매가 음성으로 안내되지 않는 것을 확인합니다.

8ec0de4079feaae7.png

코드에 LiveAnnouncer 추가

LiveAnnouncer를 추가하고 색상 선택 및 허위 구매 내역을 모두 문자열로 음성 안내합니다. 실제 구현에서 타사 결제 시스템으로 이동하거나 양식 오류가 있으면 음성으로 안내될 수 있습니다.

  1. 색상이 선택되었을 때의 음성 안내를 추가합니다.

src/app/shop/color-picker/color-picker-dialog/color-picker-dialog.component.ts

import { LiveAnnouncer } from '@angular/cdk/a11y';

@Component(...)
export class ColorPickerDialogComponent implements OnInit {
  constructor(
    public dialogRef: MatDialogRef<ColorPickerDialogComponent>,
    @Inject(MAT_DIALOG_DATA) public data: ColorDialogData,
    private liveAnnouncer: LiveAnnouncer) { }

  public changeColor(color: string): void {
    this.liveAnnouncer.announce(`Select color: ${color}`);
    this.dialogRef.close();
  }
}
  1. 허위 구매가 이루어질 때의 음성 안내를 추가합니다.

src/app/shop/shop.component.ts

import { LiveAnnouncer } from '@angular/cdk/a11y';

@Component(...)
export class ShopComponent implements OnInit {

  constructor(private liveAnnouncer: LiveAnnouncer) { }

  fauxPurchase(): void {
    let flavor = '...';
    const fakePurchase = `Purchase ${this.quantity} ${flavor}dumplings in the color ${this.color}!`;

    this.liveAnnouncer.announce(fakePurchase);
  }
}

변경사항 확인

스크린 리더를 다시 사용 설정하고 변경사항을 확인합니다. 이제 오류에 대한 알림을 받을 수 있습니다.

접근성 감사:

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 색상의 대비율이 충분합니다.
  • 시맨틱 HTML은 논리적 상호작용을 보장합니다.
  • 스크린 리더로 모든 컨트롤을 사용할 수 있습니다.
  • 슬라이더는 ARIA 속성을 사용하여 라벨을 제공합니다.
  • 색상 선택 도구에 올바른 포커스 트래핑이 있습니다.
  • 변경사항, 오류, 알림을 음성으로 안내합니다.
  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

Microsoft Windows는 고대비 모드라는 접근성 기능을 지원합니다. 이 모드는 웹 앱을 포함한 모든 앱의 모양을 변경하여 대비를 크게 높입니다. Angular에서는 앱에 사용자의 환경설정을 반영하려고 합니다.

HighContrastModeDetector를 사용하면 브라우저가 현재 고대비 모드 환경인지 확인할 수 있습니다.

Internet Explorer, Microsoft Edge 및 Firefox에서 이 모드를 지원합니다. Chrome에서는 Windows 고대비 모드를 지원하지 않습니다. 이 서비스는 Chrome 고대비 브라우저 확장 프로그램으로 추가된 고대비 모드는 감지하지 않습니다.

이 섹션이 끝나면 앱에서 다음과 같은 감사를 통과하게 됩니다.

  • 🛑 고대비 모드가 사용 설정되지 않았습니다.

다음 주석 아래에서 이러한 단계를 확인할 수 있습니다. TODO: #12. Enable HighContrast mode.

문제 파악

이 문제를 파악하려면 Internet Explorer, Microsoft Edge 또는 Firefox에서 앱을 열고 고대비 모드를 사용하도록 설정한 다음 변경된 사항이 없는지 확인합니다.

  1. Internet Explorer, Microsoft Edge 또는 Firefox에서 앱을 엽니다.
  2. 고대비 모드를 사용 설정합니다.
  3. 애플리케이션이 변경되지 않은 것을 확인합니다.

고대비 모드 지원 추가

styles.scss에서 @angular/cdk/a11y에 제공된 cdk-high-contrast mixin을 사용하여 고대비 모드로 버튼에 윤곽선을 추가합니다.

src/app/shop/shop.component.scss

@import '~@angular/cdk/a11y';

.purchase-button {
    border-radius: 5px;
    background-color: mat.get-color-from-palette(mat.$pink-palette, A100);

    @include cdk-high-contrast {
      outline: solid 1px;
      background-color: mat.get-color-from-palette(mat.$pink-palette, 50);
    }
}

:host-context(.dark-theme) {
  .purchase-button {
    background-color: mat.get-color-from-palette(mat.$light-green-palette, A100);

    @include cdk-high-contrast {
      outline: solid 1px;
      background-color: mat.get-color-from-palette(mat.$light-green-palette, 50);
    }
  }
}

변경사항 확인

앱을 새로 고치고 변경사항을 확인합니다. 고대비 모드로 버튼에 윤곽선을 추가했습니다.

83c10ae8bd841e2e.png cb49574f76cdb85c.png

접근성 감사:

  • 모든 페이지에 고유한 페이지 제목이 있습니다.
  • 색상의 대비율이 충분합니다.
  • 시맨틱 HTML은 논리적 상호작용을 보장합니다.
  • 스크린 리더로 모든 컨트롤을 사용할 수 있습니다.
  • 슬라이더는 ARIA 속성을 사용하여 라벨을 제공합니다.
  • 색상 선택 도구에 올바른 포커스 트래핑이 있습니다.
  • 변경사항, 오류, 알림을 음성으로 안내합니다.
  • 고대비 모드가 사용 설정되었습니다.

수고하셨습니다. Angular 앱에서 일반적인 웹 접근성 문제를 해결했습니다. 🎉

모든 솔루션을 보려면 main 브랜치를 확인하세요.

147ed8c9c57b3a2.png 2044559004149c85.png 4d594c26474fe97.png

지금까지 Angular 애플리케이션에서 8가지의 일반적인 a11y 문제를 해결하는 데 필요한 주요 단계를 알아보았습니다.

자세히 알아보기

다음 Codelab을 확인해 보세요.

다음 자료를 읽어보세요.