버튼의 역할과 동작이 일치하게 만들기
문제: 버튼이 아닌 요소에 클릭 이벤트 리스너를 부여함
일반 HTML 요소에 cursor: pointer
와 onclick
이벤트만 추가하는 것은 진짜 버튼이 아니에요. 이런 방식은 키보드 사용자는 키보드로 이동하지 못하게 되고, 스크린 리더 사용자는 버튼이라고 인식하지 못할 수 있어요.
링크에도 똑같이 적용돼요
<a>
태그를 사용하지 않고 하이퍼링크를 구현할 때도 같은 문제가 발생해요.
링크가 가능한 요소는 반드시 <a>
태그를 사용해야 하며, <button>
과 달리 block 요소를 자식으로 포함할 수 있으니 대체하여 사용하지 말아주세요.
잘못된 예시
다음과 같은 코드는 위에서 설명한 모든 접근성 문제를 발생시켜요.
jsx
<div
class="button-style"
style="cursor: pointer"
onclick="handleAnything()"
>
문의하기
</div>
✅ 개선하기
1. <button>
요소 사용하기
가장 좋은 방법은 HTML의 시맨틱 요소인 <button>
을 사용하는 것이에요. 이 요소는 기본적인 접근성 기능을 모두 제공해요.
- 키보드 포커스
- Enter/Space 키로 클릭
- 스크린 리더에서 "버튼"으로 인식
- 적절한 ARIA 속성 자동 제공
jsx
<button onClick={handleClick}>
문의하기
</button>
2. <button>
을 사용할 수 없을 때
내부에 block 요소가 있어서 <button>
을 사용할 수 없다면 다음처럼 직접 접근성 속성을 명시해줘야 해요.
jsx
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleClick();
}
}}
>
<div>내부 block 요소</div>
</div>
role="button"
: 스크린 리더에게 이 요소가 버튼임을 알려줍니다.tabIndex={0}
: 키보드로 포커스할 수 있게 합니다.onKeyDown
: 키보드 접근성을 위해 Enter나 Space 키 입력 시 클릭 이벤트를 발생시킵니다.
3. react-aria
사용하기
React-Aria 라이브러리의 useButton
훅을 사용하면 접근성 있는 버튼을 더 쉽게 구현할 수 있어요. 이 훅은 role
, tabIndex
, onKeyDown
등의 필수 접근성 설정들을 같이 제공해서 접근성 속성을 직접 처리하지 않아도 돼요.
jsx
import { useButton } from 'react-aria';
const buttonRef = useRef<HTMLDivElement>(null);
const { buttonProps } = useButton(
{
elementType: 'div',
onPress: handleClick
},
buttonRef
);
<div
ref={buttonRef}
{...buttonProps}
>
<div>내부 block 요소</div>
</div>