본문으로 건너뛰기
개발 머꼬
개발 노트HTML
hohyeon.dev16

라디오 버튼은 다 읽어주는데 정작 질문이 안 읽힌 이유

  • #Accessibility
  • #Engineering Note
  • #HTML

문제 발생

연락 수단을 고르는 라디오 그룹을 만들었습니다. 스크린리더로 들어보니 "이메일 라디오 버튼, 1/3"처럼 선택지만 읽히고, 무엇을 고르라는 질문인지는 끝까지 나오지 않았습니다.

<p>선호하는 연락 수단</p>
<input type="radio" id="c1" name="contact" value="email" />
<label for="c1">이메일</label>
<input type="radio" id="c2" name="contact" value="phone" />
<label for="c2">전화</label>

원인 분석

<p>는 그냥 문단입니다. 그 아래 컨트롤들과 아무 관계가 없고, 스크린리더가 폼 컨트롤을 하나씩 이동할 때는 화면에서 위에 있다는 이유로 읽히지 않습니다. <label>은 자기 for가 가리키는 한 개의 입력만 설명합니다.

MDN은 <fieldset>을 **"폼 안에서 여러 컨트롤과 label을 묶는 데 사용하는 요소"**로 정의하고, **"fieldset의 캡션은 그 안에 중첩된 첫 <legend> 요소가 제공한다"**고 설명합니다. <fieldset>의 암묵적 ARIA role은 group이고, radiogroup도 허용된 role로 나열되어 있습니다.

라디오 그룹 자체는 이름으로 만들어집니다. MDN의 설명대로 라디오 그룹은 그룹 안의 각 라디오 버튼에 같은 name을 주어 정의되고, 그룹이 만들어지면 하나를 고를 때 같은 그룹의 선택이 자동으로 해제됩니다.

해결 방안

  1. 질문을 <legend>로 옮깁니다. 첫 번째 자식이어야 캡션으로 인정됩니다.
<fieldset>
  <legend>선호하는 연락 수단</legend>
  <div>
    <input type="radio" id="c1" name="contact" value="email" required />
    <label for="c1">이메일</label>
  </div>
  <div>
    <input type="radio" id="c2" name="contact" value="phone" />
    <label for="c2">전화</label>
  </div>
</fieldset>
  1. required는 그룹에서 하나면 됩니다. 문서 그대로 — 같은 이름의 라디오 그룹 중 어느 하나에 required가 있으면 그 그룹에서 하나는 반드시 선택되어야 하며, 반드시 그 속성이 붙은 버튼일 필요는 없습니다. 전부에 붙이면 의미가 늘지 않고 유지보수만 늘어납니다.

  2. disabled를 그룹 단위로 씁니다. <fieldset disabled>는 문서 설명대로 자손인 모든 폼 컨트롤을 비활성화하고, 이들은 폼과 함께 전송되지 않으며 마우스 클릭·포커스 이벤트도 받지 않습니다. 개별 입력마다 disabled를 돌려 붙일 필요가 없습니다.

  3. <legend> 안의 컨트롤은 예외라는 걸 알아둡니다. 문서가 명시합니다 — <legend> 안의 폼 요소는 비활성화되지 않습니다. "섹션 전체 사용" 토글을 legend에 두는 패턴이 이것 덕분에 동작합니다.

  4. 시각 디자인 때문에 legend를 지우지 않습니다. 테두리·여백이 마음에 들지 않으면 border: 0으로 fieldset을 조정하고, legend는 남긴 채 시각적으로만 숨깁니다(VisuallyHidden). 지우는 순간 질문이 다시 사라집니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.