심화 가이드
접근성 지원

선택

개요

콤보박스와 리스트박스를 결합해서 키보드 탐색과 스크린 리더를 지원하는 단일 선택 드롭다운을 만드는 패턴입니다.

사용법

선택 패턴은 사용자가 익숙한 옵션 중에서 값 하나를 골라야 할 때 적합합니다.

다음과 같은 경우에 이 패턴 사용을 고려하세요.

  • 옵션 목록이 고정된 경우(20개 미만): 사용자가 필터링 없이 훑어보고 고를 수 있습니다.
  • 옵션이 익숙한 경우: 검색하지 않고도 선택지를 알아볼 수 있습니다.
  • 폼에 표준 필드가 필요한 경우: 국가, 주, 범주, 상태를 선택할 때 사용합니다.
  • 설정과 구성: 환경설정이나 옵션을 위한 드롭다운 메뉴에 사용합니다.
  • 옵션 레이블이 명확한 경우: 각 선택지의 이름이 구분되고 쉽게 훑어볼 수 있습니다.

다음과 같은 경우에는 이 패턴을 피하세요.

  • 목록의 항목이 20개보다 많은 경우: 더 나은 필터링을 위해 자동 완성 패턴을 사용하세요.
  • 사용자가 옵션을 검색해야 하는 경우: 자동 완성은 텍스트 입력과 필터링을 제공합니다.
  • 다중 선택이 필요한 경우: 다중 선택 패턴을 사용하세요.
  • 옵션이 매우 적은 경우(2~3개): 라디오 버튼이 모든 선택지를 더 잘 보여줍니다.

주요 기능

선택 패턴은 콤보박스와 리스트박스 디렉티브를 결합해서 다음과 같이 접근성을 갖춘 드롭다운을 제공합니다.

  • 키보드 탐색: 방향키로 옵션을 탐색하고 Enter로 선택하며 Escape로 닫습니다.
  • 스크린 리더 지원: 보조 기술을 위한 ARIA 어트리뷰트를 기본 제공합니다.
  • 커스텀 표시: 선택된 값을 아이콘, 서식, 풍부한 콘텐츠로 표시합니다.
  • 시그널 기반 반응성: Angular 시그널로 상태를 반응형으로 관리합니다.
  • 스마트 위치 지정: CDK Overlay가 뷰포트 경계와 스크롤을 처리합니다.
  • 양방향 텍스트 지원: 오른쪽에서 왼쪽으로 쓰는 언어(RTL)를 자동으로 처리합니다.

예제

기본 선택

사용자가 값 목록에서 선택할 수 있는 표준 드롭다운이 필요할 때가 있습니다. 콤보박스와 리스트박스를 함께 사용하면 익숙한 선택 UI와 함께 접근성을 지원할 수 있습니다.

<input> 대신 div나 button과 같은 비대화형 호스트 엘리먼트에 ngCombobox 디렉티브를 직접 적용해서 텍스트 입력을 방지합니다. 사용자는 네이티브 select 엘리먼트와 마찬가지로 방향키와 Enter로 드롭다운을 조작합니다.

커스텀 표시를 적용한 선택

사용자가 선택지를 빠르게 알아볼 수 있도록 옵션에 아이콘이나 배지 같은 시각적 표시가 필요할 때가 많습니다. 옵션 안에 커스텀 템플릿을 사용하면 접근성을 유지하면서 풍부한 서식을 적용할 수 있습니다.

각 옵션은 레이블 옆에 아이콘을 표시합니다. 선택된 값의 표시도 선택한 옵션의 아이콘과 텍스트로 바뀌어 결과를 시각적으로 명확하게 보여줍니다.

비활성화된 선택

폼의 특정 조건이 충족되지 않았을 때 선택 컨트롤을 비활성화해서 사용자 조작을 막을 수 있습니다. 비활성화 상태는 시각적인 피드백을 제공하고 키보드 조작을 막습니다.

비활성화하면 선택 컨트롤은 비활성화된 모습을 표시하고 모든 사용자 조작을 차단합니다. 스크린 리더는 보조 기술 사용자에게 비활성화 상태를 안내합니다.

테스트

선택 패턴은 @angular/aria/combobox/testing의 ComboboxHarness와 @angular/aria/listbox/testing의 ListboxHarness를 함께 사용해서 테스트할 수 있습니다. 하네스로 선택 컴포넌트를 테스트하는 예제는 다음과 같습니다.

import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {ComboboxHarness} from '@angular/aria/combobox/testing';
import {ListboxHarness} from '@angular/aria/listbox/testing';
import {MySelectComponent} from './my-select'; // 테스트할 컴포넌트

describe('MySelectComponent', () => {
  let fixture: ComponentFixture<MySelectComponent>;
  let loader: HarnessLoader;

  beforeEach(async () => {
    TestBed.configureTestingModule({
      imports: [MySelectComponent],
    });

    fixture = TestBed.createComponent(MySelectComponent);
    await fixture.whenStable();
    loader = TestbedHarnessEnvironment.loader(fixture);
  });

  it('should allow selecting an option', async () => {
    // 선택 트리거 역할을 하는 콤보박스 하네스를 불러옵니다.
    const select = await loader.getHarness(ComboboxHarness);

    // 처음에는 닫혀 있는지 확인합니다.
    expect(await select.isOpen()).toBe(false);

    // 드롭다운을 엽니다.
    await select.open();
    expect(await select.isOpen()).toBe(true);

    // 팝업에서 리스트박스 하네스를 가져옵니다.
    const listbox = await select.getPopupWidget(ListboxHarness);
    const options = await listbox.getOptions();
    expect(options.length).toBe(3);

    // 두 번째 옵션을 클릭합니다.
    await options[1].click();

    // 드롭다운이 닫히고 값이 변경되었는지 확인합니다.
    expect(await select.isOpen()).toBe(false);
    expect(await (await select.host()).text()).toContain('Option 2');
  });
});

API 레퍼런스

자세한 API 문서는 다음 API 레퍼런스를 참고하세요.

위치 지정

선택 패턴은 CDK Overlay와 통합하여 팝업 위치를 지정합니다. cdkConnectedOverlay를 사용하면 뷰포트 경계와 스크롤을 자동으로 처리할 수 있습니다.