HTML — 태그 하나가 기능을 대신한다 · 보내기를 눌렀는데 아무 일도 일어나지 않았다 · 이론
이름이 없는 칸은 없는 칸이다
한 줄 요약
폼 컨트롤의 접근 가능한 이름(accessible name) 은 마크업에 label 이라는 글자가 있느냐가 아니라, 정해진 순서를 따라 계산된 결과다. 그 계산을 모르면 label 을 다 써 놓고도 이름이 없는 칸을 만든다.
왜 이게 필요했나
가입 폼에서 사람이 막히는 자리는 늘 같다. 보내기를 눌렀는데 아무 일도 일어나지 않는다. 화면 어딘가가 빨개졌겠지만 스크린리더 사용자에게는 아무 소리도 나지 않았고, 키보드 사용자에게는 초점이 그대로 버튼에 있다.
이때 마크업을 열어 보면 label 도 있고 required 도 있다. 그런데 for="mail" 이고 입력칸은 id="email" 이다. 눈으로는 나란히 붙어 있으니 아무도 눈치채지 못한다. 기계에게 그 둘은 남남이고, 그 칸의 이름은 없다.
이름이 없는 칸을 스크린리더는 "편집" 이라고만 읽는다. 무엇을 넣는 칸인지 알 방법이 없다. 그리고 이 결함은 자동 검사 도구가 "label 요소가 있음" 으로 통과시켜 주는 경우가 많아서 오래 살아남는다.
어떻게 동작하나
HTML-AAM 은 컨트롤 종류마다 이름을 구하는 순서를 정해 두었다. 텍스트를 치는 입력칸과 textarea 는 이렇다.
1. aria-labelledby — 가리킨 요소들의 글자를 이어 붙인다2. aria-label — 속성값 그대로3. 연결된 label — for/id 또는 감싼 label 의 글자4. title — 마우스를 올려야 보이는 그것5. placeholder — 글자를 치면 사라지는 그것[HTML Accessibility API Mappings 4.1절](https://www.w3.org/TR/html-aam-1.0/)과 [Accessible Name and Description Computation](https://www.w3.org/TR/accname-1.2/)이 원본이다.
여기서 두 가지가 중요하다.
첫째, 앞 단계가 하나라도 값을 내면 뒤는 보지 않는다. 그래서 aria-label="이메일" 을 붙여 두면 눈에 보이는 label 글자가 무엇이든 스크린리더는 aria-label 만 읽는다. 화면에는 "회사 메일" 이라고 적혀 있는데 음성은 "이메일" 이라고 말하는 화면이 이렇게 만들어진다. 말로 조작하는 사용자는 보이는 글자를 부르므로, 이 어긋남은 그 사람에게 조작 자체가 안 되는 문제가 된다.
둘째, placeholder 도 이름이 되기는 한다. 그래서 검사 도구가 "이름 없음" 이라고 잡아 주지 않는다. 하지만 글자를 치는 순간 사라지는 이름이라, 긴 폼에서 되돌아왔을 때 무엇을 쓰던 칸인지 확인할 방법이 없다. 저시력 사용자에게는 대비도 모자란다.
묶음에는 묶음의 이름이 필요하다
라디오 버튼은 하나하나가 답이 아니라 여럿이 모여 질문 하나를 이룬다. 각 보기에 label 을 붙여도 "이메일 라디오 버튼, 선택됨" 까지만 읽힌다. 무엇을 고르는 중인지는 어디에도 없다.
fieldset 으로 묶고 legend 를 주면 그 묶음에 이름이 생긴다. 그러면 "알림 받을 방법, 이메일 라디오 버튼" 처럼 질문과 답이 함께 읽힌다.
오류는 색이 아니다
빨간 테두리는 색을 구분하지 못하는 사람에게 아무것도 아니고, 화면을 보지 않는 사람에게는 존재하지도 않는다. 프로그램이 읽을 수 있는 신호가 따로 필요하다.
aria-invalid="true"— 이 칸의 값이 지금 잘못됐다aria-describedby— 무엇이 잘못됐는지 적힌 요소를 가리킨다role="alert"또는aria-live— 나중에 나타난 글자를 읽어 준다
aria-describedby 는 여러 id 를 공백으로 나열할 수 있다. 그래서 도움말과 오류 메시지를 함께 가리키면 오류가 떠도 "열두 자 이상" 같은 규칙 안내가 사라지지 않는다. 오류가 났을 때 규칙을 지우는 구현이 의외로 흔하다.
현장에서 만나는 모습
[WCAG 의 Labels or Instructions](https://www.w3.org/WAI/WCAG22/Understanding/labels-or-instructions.html)는 "입력이 필요할 때 레이블이나 안내를 제공하라" 고만 말한다. 실무에서 이 한 줄이 깨지는 방식은 대개 셋이다.
1. 디자인이 예뻐서 label 을 지우고 placeholder 만 남긴다
2. 컴포넌트를 재사용하면서 id 를 고정값으로 박아 두어, 한 화면에 두 번 쓰면 id 가 겹친다. 겹친 순간 뒤쪽 label 은 앞쪽 칸을 가리킨다
3. 오류 메시지를 aria-describedby 로 바꿔 끼워서 규칙 안내가 사라진다
셋 다 마우스로 확인하면 멀쩡하다. 그래서 이 코스는 검사기를 돌려 이름이 어디에서 나왔는지를 눈으로 확인하는 데서 시작한다. 나온 출처가 label 이면 안전하고, placeholder 나 title 이면 사라지는 이름이며, 비어 있으면 그 칸은 존재하지 않는 칸이다.
그리고 autocomplete 를 제대로 주는 것도 접근성이다. 손을 쓰기 어려운 사람에게 타자 수는 그대로 비용이고, [HTML 표준의 autofill 필드 이름](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill)에 없는 값은 브라우저가 그냥 무시한다. autocomplete="e-mail" 은 아무 일도 하지 않는다.
다음 실습에서 할 것
가입 폼을 하나 쓰고, 칸마다 이름이 어디에서 나왔는지 계산기로 확인한다. placeholder 에서 나온 이름을 label 로 옮기고, 라디오를 fieldset 으로 묶고, 오류를 프로그램이 읽을 수 있게 만든다. 마지막에는 계산기 결과를 파일로 남긴다.