실측 리포트
접근성 검사 엔진을 실제로 돌려 얻은 수치입니다. 샘플 페이지 9개, 판정이 어려운 경계 사례 44개, 그리고 이 사이트 자신의 페이지 30개를 검사했습니다.
왜 이 리포트를 만들었나
검사 도구가 "문제가 있다"고 말하려면, 그 도구의 판정이 얼마나 맞는지부터 보여 줘야 합니다. 이 리포트는 세 가지를 확인합니다. 결함 수준이 다른 페이지에 점수가 그 순서대로 나오는지, 판정이 헷갈리기 쉬운 경계 사례에서 틀리지 않는지, 그리고 이 사이트가 스스로의 검사를 통과하는지입니다. 세 번째를 확인하다가 엔진 결함 5가지와 문서 감사기 화면의 실제 접근성 결함을 찾았고, 모두 고친 뒤 다시 측정했습니다.
1. 샘플 페이지 9개
로그인 폼·기사 페이지·상품 목록 세 유형을 "양호·보통·불량" 세 수준으로 직접 작성했습니다. 실제 사이트에서 가져온 페이지가 아닙니다. 수준 구분은 작성자가 의도한 결함의 양이고, 점수는 작성 후 엔진을 돌려 나온 값을 그대로 실었습니다.
| 유형 | 수준 | 점수 | 지적 | 치명 | 높음 | 보통 | 낮음 | 걸린 규칙 |
|---|---|---|---|---|---|---|---|---|
| 로그인 폼 | 양호 | 100 | 0 | 0 | 0 | 0 | 0 | 없음 |
| 로그인 폼 | 보통 | 81 | 4 | 2 | 0 | 1 | 1 | button-no-name img-alt-missing link-vague-text form-autocomplete |
| 로그인 폼 | 불량 | 39 | 14 | 4 | 2 | 7 | 1 | click-not-focusable html-lang-missing img-alt-missing title-missing contrast-low viewport-no-zoom focus-outline-removed form-placeholder-only heading-none link-vague-text landmark-missing |
| 기사 페이지 | 양호 | 100 | 0 | 0 | 0 | 0 | 0 | 없음 |
| 기사 페이지 | 보통 | 92 | 4 | 0 | 0 | 4 | 0 | heading-skip img-alt-suspicious link-vague-text table-no-header |
| 기사 페이지 | 불량 | 53 | 11 | 3 | 1 | 6 | 1 | html-lang-missing img-alt-missing title-missing contrast-low focus-outline-removed heading-none link-vague-text landmark-missing |
| 상품 목록 | 양호 | 100 | 0 | 0 | 0 | 0 | 0 | 없음 |
| 상품 목록 | 보통 | 82 | 3 | 2 | 0 | 1 | 0 | button-no-name img-alt-missing form-placeholder-only |
| 상품 목록 | 불량 | 13 | 18 | 8 | 2 | 7 | 1 | click-not-focusable html-lang-missing img-alt-missing link-no-name title-missing contrast-low viewport-no-zoom focus-outline-removed form-placeholder-only heading-none link-vague-text landmark-missing |
세 유형 모두 양호 > 보통 > 불량 순서로 점수가 나왔습니다. 수준별 평균은 양호 100점, 보통 85점, 불량 35점입니다. 이번 엔진 수정 전후로 샘플 점수는 하나도 바뀌지 않았습니다. 고친 결함은 아래 경계 사례에서만 드러나는 종류였기 때문입니다.
이 결과를 과하게 믿으면 안 되는 이유
양호 샘플 세 개가 모두 100점인 것은 엔진의 규칙을 아는 사람이 작성했기 때문입니다. 정답을 아는 사람이 낸 시험 문제를 맞힌 셈이라, 엔진의 정확도를 보여 주는 증거가 아닙니다. 이 샘플이 보여 주는 것은 "결함이 많을수록 점수가 낮아진다"는 방향성까지입니다.
엔진 규칙 35개 중 이 샘플에서 한 번이라도 걸린 규칙은 17개입니다. 나머지 18개(input-image-alt, area-alt, svg-no-name, link-no-href, form-label-missing, heading-empty, skip-link-missing, duplicate-id, iframe-no-title, positive-tabindex, meta-refresh, autoplay-media, deprecated-motion, aria-ref-broken, aria-role-invalid, aria-hidden-focusable, role-button-no-tabindex, dialog-not-marked)는 이 샘플로 확인하지 못했고, 회귀 테스트로만 검증합니다.
2. 경계 사례 44개: 찾아서 고친 엔진 결함 5가지
이 사이트의 페이지를 검사기에 넣어 보니, 문제가 없는 곳을 지적하는 경우가 나왔습니다. 원인을 추적해 보니 엔진의 판정 방식에 결함이 있었습니다. 같은 유형의 경계 사례를 모아 수정 전 엔진(tools/hub/baseline/에 보관)과 수정 후 엔진에 똑같이 넣은 결과입니다.
| 판정 | 원인 | 사례 | 수정 전 | 수정 후 |
|---|---|---|---|---|
| 개인정보 입력칸 판정(autocomplete) | 영문 이름을 부분 문자열로 비교. hotel 안의 tel, filename 안의 name을 개인정보 칸으로 봄 | 28 | 11 | 1 |
| 본문 바로가기 링크 판정 | 앵커를 부분 문자열로 비교. #domain·#maintenance 안의 main을 본문 바로가기로 인정 | 9 | 5 | 0 |
| CSS 변수 배경의 대비 판정 | CSS 변수를 풀지 못하면 배경을 흰색으로 가정. 강조색 버튼의 흰 글자를 "흰색 위 흰 글자 1.00:1"로 지적 | 3 | 1 | 0 |
| 숨겨진 SVG의 이름 판정 | SVG 자신의 aria-hidden만 보고 조상의 aria-hidden은 보지 않음 | 2 | 1 | 0 |
| 팝업(모달) 판정 | id에 dialog가 든 문서 영역(규칙 설명 카드)을 팝업으로 오인 | 2 | 1 | 0 |
고친 방식은 공통입니다. 영문 단어는 부분 문자열이 아니라 단어 단위로 비교하고, 값을 알 수 없을 때는 가정해서 지적하지 않고 건너뜁니다. 판단할 수 없는 것을 지적하는 오탐이 놓치는 것보다 사용자에게 더 해롭다고 보았기 때문입니다. 대가로 생기는 미탐은 아래 한계에 적었습니다.
경계 사례 44개 전체 보기
개인정보 입력칸 판정(autocomplete) — 규칙 form-autocomplete
| 입력 | 정답 | 수정 전 | 수정 후 |
|---|---|---|---|
<input name="name"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="fullname"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="first_name"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="email"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="user_email"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="phone"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="tel"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="zipcode"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="address"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="birthday"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="username"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="nickname"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="customer_name"> | 지적 | 지적 맞음 | 지적 맞음 |
<input placeholder="Your name"> | 지적 | 지적 맞음 | 지적 맞음 |
<input name="hotel"> | — | 지적 틀림 | — 맞음 |
<input name="intel_chip"> | — | 지적 틀림 | — 맞음 |
<input name="filename"> | — | 지적 틀림 | — 맞음 |
<input name="file_name"> | — | 지적 틀림 | — 맞음 |
<input name="hostname"> | — | 지적 틀림 | — 맞음 |
<input name="telegram_id"> | — | 지적 틀림 | — 맞음 |
<input name="gmail_label"> | — | 지적 틀림 | — 맞음 |
<input name="rename_to"> | — | 지적 틀림 | — 맞음 |
<input name="company_name"> | — | 지적 틀림 | — 맞음 |
<input name="product_name"> | — | 지적 틀림 | — 맞음 |
<input id="ruleName"> | — | — 맞음 | — 맞음 |
<input id="pasteName"> | — | — 맞음 | — 맞음 |
<input name="keyword"> | — | — 맞음 | — 맞음 |
<input name="postcode_tag"> | — | 지적 틀림 | 지적 틀림 |
본문 바로가기 링크 판정 — 규칙 skip-link-missing
| 입력 | 정답 | 수정 전 | 수정 후 |
|---|---|---|---|
<a href="#main" class="skip">본문 바로가기</a> | — | — 맞음 | — 맞음 |
<a href="#content">콘텐츠로 이동</a> | — | — 맞음 | — 맞음 |
<a href="#contents">본문으로</a> | — | — 맞음 | — 맞음 |
<a href="#main-content">Skip to content</a> | — | — 맞음 | — 맞음 |
<a href="#domain">도메인 안내</a> | 지적 | — 틀림 | 지적 맞음 |
<a href="#maintenance">점검 안내</a> | 지적 | — 틀림 | 지적 맞음 |
<a href="#remain">남은 기간</a> | 지적 | — 틀림 | 지적 맞음 |
<a href="#discontent">불만 접수</a> | 지적 | — 틀림 | 지적 맞음 |
<a href="#contents-list">목차</a> | 지적 | — 틀림 | 지적 맞음 |
CSS 변수 배경의 대비 판정 — 규칙 contrast-low
| 입력 | 정답 | 수정 전 | 수정 후 |
|---|---|---|---|
var(--accent) 배경 위 흰 글자 | — | 지적 틀림 | — 맞음 |
조상 배경이 var()인 흰 글자 | — | — 맞음 | — 맞음 |
#6f97ff 배경 위 흰 글자(2.78:1) | 지적 | 지적 맞음 | 지적 맞음 |
숨겨진 SVG의 이름 판정 — 규칙 svg-no-name
| 입력 | 정답 | 수정 전 | 수정 후 |
|---|---|---|---|
<span aria-hidden="true"><svg> | — | 지적 틀림 | — 맞음 |
숨기지 않은 이름 없는 <svg> | 지적 | 지적 맞음 | 지적 맞음 |
팝업(모달) 판정 — 규칙 dialog-not-marked
| 입력 | 정답 | 수정 전 | 수정 후 |
|---|---|---|---|
<section id="dialog-help" aria-labelledby> | — | 지적 틀림 | — 맞음 |
<div class="modal"> (역할 없음) | 지적 | 지적 맞음 | 지적 맞음 |
3. 이 사이트 자신을 검사한 결과
허브와 두 도구의 페이지 30개를 각 페이지의 실제 CSS와 함께 엔진에 넣었습니다(이 리포트 페이지는 자기 자신을 측정하는 중이라 제외). 수정 전 엔진과 수정 전 CSS로 검사하면 11개 페이지에서 24건이 지적됩니다. 지적마다 원인을 확인해 엔진 오탐과 실제 결함을 나눴습니다.
| 규칙 | 지적 수 | 판단 |
|---|---|---|
contrast-low | 10 | 엔진 오탐 — CSS 변수 배경을 흰색으로 가정(2번 표의 대비 판정). 다만 아래 4번에서 보듯 같은 자리에 다크 테마의 실제 결함이 따로 있었음 |
focus-outline-removed | 9 | 실제 결함 — 문서 감사기 파일 업로드 영역이 키보드 포커스 테두리를 없애고 있었음. 3px 테두리로 복구 |
svg-no-name | 2 | 엔진 오탐 — 이미 aria-hidden으로 감싼 장식 아이콘 |
form-autocomplete | 2 | 엔진 오탐 — 검사 기준 이름(ruleName)·자료 이름(pasteName) 칸을 사람 이름 칸으로 봄 |
dialog-not-marked | 1 | 엔진 오탐 — 규칙 사전의 "팝업 규칙" 설명 카드 |
엔진을 고치고 업로드 영역의 포커스 표시를 되살린 뒤, 수정 후 엔진으로 같은 30개 페이지를 다시 검사한 결과 지적은 0건입니다. 하지만 이것이 "이 사이트에 접근성 문제가 없다"는 뜻은 아닙니다. 이 사이트는 색을 CSS 변수로 지정하므로, 엔진은 대비 판정을 대부분 건너뜁니다. 그래서 대비는 다음 4번처럼 따로 계산했습니다.
4. 엔진이 못 보는 것을 직접 계산: 테마별 글자 대비
CSS 파일에서 라이트·다크 테마의 색 토큰을 읽어 WCAG 공식으로 대비를 계산했습니다. 기준은 일반 글자 4.5:1입니다.
| 위치 | 수정 전 라이트 | 수정 전 다크 | 수정 후 라이트 | 수정 후 다크 |
|---|---|---|---|---|
| 주 버튼·활성 탭·건너뛰기 링크 | 6.14 | 2.78 | 6.14 | 7.07 |
| 삭제 버튼(마우스 올림) | 3.95 | 3.23 | 4.98 | 6.09 |
| 심각도 배지: 치명 | 6.54 | 2.85 | 6.54 | 6.90 |
| 심각도 배지: 높음 | 3.19 | 2.21 | 6.18 | 8.91 |
| 심각도 배지: 보통 | 6.14 | 2.78 | 6.14 | 7.07 |
| 심각도 배지: 낮음 | 3.19 | 2.62 | 6.16 | 7.51 |
수정 전에는 다크 테마에서 강조색 위 흰 글자가 모두 기준에 못 미쳤고, 라이트 테마에서도 "높음"·"낮음" 배지와 삭제 버튼이 미달이었습니다(합계 9곳). 배경색은 그대로 두고, 배경마다 기준을 넘는 글자색(흰색 또는 짙은 색)을 계산해 토큰으로 지정했습니다. 접근성 검사기 쪽은 처음부터 테마와 무관한 진한 색 토큰을 써서 같은 계산에서 모두 통과했습니다.
| 위치 | 라이트 | 다크 |
|---|---|---|
| 본문 글자 | 15.78 | 15.55 |
| 보조 글자 | 6.94 | 7.84 |
| 카드 안 보조 글자 | 7.50 | 7.29 |
| 링크 | 8.66 | 11.97 |
| 카드 안 링크 | 9.36 | 11.13 |
| 주 버튼 글자 | 9.36 | 11.57 |
| 표의 "맞음" 표시 | 6.53 | 11.13 |
| 표의 "틀림" 표시 | 7.56 | 8.03 |
5. 남은 한계
- 남은 오판 1건:
<input name="postcode_tag">는 우편번호 칸이 아닌데도 지적합니다. 이름에postcode라는 단어가 그대로 들어 있어, 이름만으로는 칸의 용도를 구별할 수 없습니다. - CSS 변수 색:
var(--색이름)으로 지정한 글자·배경은 값을 풀지 못해 대비 판정을 건너뜁니다. 요즘 사이트는 대부분 CSS 변수를 쓰므로 이 사이트의 대비 검사는 생각보다 많은 곳을 보지 못합니다. 4번처럼 토큰을 직접 계산하는 것이 보완책입니다. - 사람 이름 칸의 미탐: 앞말이 사람을 가리키는 단어(customer, member 등)일 때만 이름 칸으로 봅니다.
receiver_nm처럼 약어로 쓴 칸은 놓칩니다. - aria-hidden이 붙은 글자: 요소 자신에 aria-hidden이 있으면 대비 검사에서도 빠집니다. 화면낭독기에서만 숨긴 것이지 눈에는 보이므로 엄밀히는 대비를 봐야 하지만, 대부분 장식용 글자(WCAG 1.4.3의 예외)라 현재 동작을 유지했습니다.
- 정적 분석: 자바스크립트가 만든 화면, 마우스를 올렸을 때만 나오는 색, 배경 이미지 위 글자는 판정하지 않습니다.
- 문서 감사기: AI 판정의 정확도는 이 리포트에서 측정하지 않았습니다. 측정하려면 정답이 정해진 문서 묶음과 반복 실행이 필요한데, 아직 준비하지 못했습니다. 회귀 테스트 42건은 AI 응답을 정리·검증하는 코드만 확인합니다.
다시 측정하는 법
node tools/a11y/test_rules.js # 엔진 회귀 테스트 (307건)
node tools/hub/build_hub_pages.mjs # 이 리포트를 다시 측정해 생성