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

입력창을 설명하는 텍스트가 있는데도 스크린리더가 아무 이름도 읽지 않은 이유

  • #Accessibility
  • #Engineering Note
  • #HTML

문제 발생

폼을 만들고 설명 텍스트를 옆에 뒀습니다.

<span>이메일</span>
<input type="email" name="email" />

보기에는 문제가 없었지만,

  • 스크린리더가 입력창에 도달하면 이름 없이 "편집 텍스트"라고만 읽었고
  • 텍스트를 눌러도 입력창에 포커스가 가지 않았습니다

원인 분석

시각적 근접은 프로그래밍적 연결이 아닙니다. 브라우저와 보조 기술은 "옆에 있는 글자"를 이름으로 삼지 않습니다. 연결을 명시해야 합니다.

MDN은 두 가지 방법을 제시합니다.

명시적 연결for 속성이 컨트롤의 id와 일치합니다.

<label for="peas">I like peas.</label>
<input type="checkbox" name="peas" id="peas" />

암묵적 연결 — 컨트롤을 <label> 안에 넣습니다.

이렇게 연결하면 두 가지를 얻습니다. 문서 설명 그대로 — 레이블 텍스트가 시각적으로만이 아니라 프로그래밍적으로도 연결되어, 예를 들어 스크린리더가 사용자가 그 입력에 포커스했을 때 레이블을 읽어줍니다. 그리고 — 사용자가 레이블을 클릭하거나 탭하면 브라우저가 연결된 입력으로 포커스를 넘기며, 그 넓어진 클릭 영역은 터치 기기 사용자에게 이점이 됩니다.

해결 방안

  1. for/id로 연결합니다. id가 문서 안에서 유일해야 하므로, 목록에서 반복되는 폼에는 항목 id를 섞습니다.
<label for="email">이메일</label>
<input id="email" type="email" name="email" />
  1. React 등에서는 생성된 id를 씁니다. 컴포넌트가 여러 번 렌더될 수 있으므로 useId() 같은 API로 충돌을 피합니다.

  2. 레이블 안에 다른 상호작용 요소를 넣지 않습니다. 문서가 경고합니다 — 암묵적으로 연결된 폼 컨트롤 외에 앵커나 버튼 같은 상호작용 요소를 <label> 안에 두지 마십시오. 그렇게 하면 레이블과 연결된 폼 입력을 활성화하기 어려워집니다. "이용약관에 동의합니다(약관 보기)" 같은 문구에서 링크는 레이블 밖에 둡니다.

  3. 디자인상 레이블을 감춰야 하면 시각적으로만 숨깁니다. display: none은 접근성 트리에서도 사라집니다 — 화면에서만 숨기는 유틸리티를 씁니다.

  4. placeholder로 대체하지 않습니다. 입력을 시작하면 사라지고, 접근 가능한 이름으로 안정적으로 노출되지도 않습니다.

  5. 키보드와 스크린리더로 한 번 확인합니다. 각 입력에서 이름이 읽히는지, 레이블 클릭으로 포커스가 이동하는지만 봐도 대부분의 문제가 드러납니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

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