← 글 목록

notes

UI - Aria

UI 접근성을 위한 ARIA role·attribute 사용 기준과 마크업 메모.

// ARIA 속성들의 타입 정의
type AriaRole = 
  | 'button'
  | 'checkbox'
  | 'menuitem'
  | 'tab'
  | 'alert'
  | 'progressbar'
  | 'tooltip';

interface AriaAttributes {
  'aria-label'?: string;
  'aria-expanded'?: boolean;
  'aria-hidden'?: boolean;
  'aria-selected'?: boolean;
  'aria-describedby'?: string;
  'aria-controls'?: string;
}

// ARIA 사용 예시 컴포넌트들
const AccessibleButton: React.FC = () => {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <button
      role="button"
      aria-expanded={isExpanded}
      aria-controls="content-panel"
      onClick={() => setIsExpanded(!isExpanded)}
    >
      메뉴 펼치기
    </button>
  );
};

const AccessibleTab: React.FC = () => {
  return (
    <div 
      role="tab"
      aria-selected={true}
      aria-controls="panel-1"
      tabIndex={0}
    >
      첫 번째 탭
    </div>
  );
};

const AccessibleAlert: React.FC = () => {
  return (
    <div
      role="alert"
      aria-live="assertive"
      aria-atomic="true"
    >
      중요한 알림이 있습니다!
    </div>
  );
};

ARIA의 주요 개념


interface ARIARoles {
  // 1. 랜드마크 역할
  main: "문서의 주요 콘텐츠를 나타냄",
  navigation: "탐색 섹션을 나타냄",
  banner: "헤더 영역을 나타냄",
  
  // 2. 위젯 역할
  button: "클릭 가능한 버튼",
  tablist: "탭 목록",
  menuitem: "메뉴 항목",
  
  // 3. 라이브 리전 역할
  alert: "중요한 알림",
  status: "상태 업데이트"
}

status


interface ARIAStates {
  expanded: boolean;   // 확장/축소 상태
  selected: boolean;   // 선택 상태
  checked: boolean;    // 체크 상태
  disabled: boolean;   // 비활성화 상태
  hidden: boolean;     // 숨김 상태
}

예시


const AccessibleAccordion: React.FC = () => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button
        aria-expanded={isOpen}
        aria-controls="accordion-content"
        onClick={() => setIsOpen(!isOpen)}
      >
        아코디언 버튼
      </button>
      
      <div 
        id="accordion-content"
        aria-hidden={!isOpen}
        style={{ display: isOpen ? 'block' : 'none' }}
      >
        아코디언 내용
      </div>
    </div>
  );
};

class ARIABestPractices {
  static rules = {
    rule1: "시맨틱 HTML을 우선 사용하기",
    rule2: "ARIA는 필요한 경우에만 추가하기",
    rule3: "role과 state를 올바르게 매칭하기",
    rule4: "키보드 접근성 함께 고려하기"
  };

  static example(): void {
    // ❌ 잘못된 사용
    const bad = <div role="button">클릭</div>;
    
    // ✅ 좋은 사용
    const good = <button>클릭</button>;
    
    // ✅ ARIA가 필요한 경우
    const complex = (
      <div 
        role="tabpanel"
        aria-labelledby="tab-1"
      >
        탭 내용
      </div>
    );
  }
}