같은 페이지를 두 검사 도구에 넣었더니 하나는 "검토가 필요합니다", 다른 하나는 "실패입니다"라고 합니다. 접근성 자동 검사를 둘 이상 써 본 팀이라면 한 번쯤 겪었을 장면이죠. 어느 쪽이 맞을까요?

이 질문을 숫자로 확인해 봤습니다. W3C가 승인한 접근성 검사 규칙 37개에는 "이 HTML은 통과, 이 HTML은 실패"라고 답이 정해진 승인 예제가 558개 붙어 있습니다. 이걸 axe-core와 IBM Equal Access에 하나씩 돌렸어요. 두 도구가 모두 검사한 430개 중 413개에서 실패 여부가 같았습니다(판단 보류까지 구분하면 407개). 갈린 17개는 한쪽이 판단을 미뤘거나, 한쪽이 ACT가 문제 삼지 않는 코드를 실패로 본 경우가 대부분이었고요. 이 비교의 기준이 된 게 ACT 규칙입니다. 그 규칙을 쓰는 양식인 ACT Rules Format 1.1은 2026년 2월 W3C 권고안, 그러니까 W3C의 정식 웹 표준이 됐습니다.

눈금 색이 서로 다른 줄자 여러 개가 엉켜 있는 모습 - 같은 대상을 재도 도구마다 숫자가 조금씩 다르게 나옵니다
눈금 색이 서로 다른 줄자 여러 개가 엉켜 있는 모습 - 같은 대상을 재도 도구마다 숫자가 조금씩 다르게 나옵니다
사진: Unsplash의 Patricia Serna

접근성 검사 도구 결과는 왜 서로 다를까

WCAG(웹 콘텐츠 접근성 지침)는 사람이 읽으라고 쓴 문서입니다. 예를 들어 성공 기준 4.1.2를 줄여 말하면 "모든 사용자 인터페이스 컴포넌트는 이름과 역할을 프로그램이 알아낼 수 있어야 한다"입니다. 맞는 말인데, 이걸 코드로 옮기려면 정할 게 많아요.

  • div에 onclick만 붙어 있으면 "사용자 인터페이스 컴포넌트"로 볼까?
  • 화면 밖으로 밀어낸 링크도 검사 대상일까?
  • 스크립트가 포커스를 다른 곳으로 옮겨 버린다면, 그건 정적인 HTML만 보고 알 수 있을까?

도구를 만드는 회사는 이런 질문에 하나하나 답을 정해야 합니다. 그리고 답은 회사마다 다릅니다. 같은 페이지에서 결과가 갈리는 건 대부분 여기서 시작돼요.

같은 도구라도 버전이 바뀌면 답이 바뀌기도 합니다. 구글 web.dev 접근성 강좌의 실습 페이지에는 aria-hidden="true"와 tabindex="-1"이 함께 붙은 입력창이 있는데, 강좌는 2022년 Lighthouse가 이걸 문제로 잡았다고 적어 두었습니다(자세한 이야기는 web.dev 접근성 강좌 점검 글에 적었습니다). 같은 코드를 2026년 Lighthouse 13.4.1에 넣으면 통과합니다. 지금의 판정은 이 글에서 다룰 ACT 규칙과 같은 답이에요. tabindex="-1"이면 Tab 키로는 닿지 않으니까요.

ACT 규칙이란? 접근성 검사 방법을 적는 공통 양식

ACT는 Accessibility Conformance Testing(접근성 적합성 테스트)의 약자입니다. 이름이 비슷한 두 가지가 섞여 쓰이는데, 구분하면 이렇습니다.

데이터 표
구분무엇인가누가 만드나
ACT Rules Format검사 규칙을 쓰는 양식을 정한 W3C 표준W3C 접근성 지침 작업 그룹(AG WG)
ACT Rules그 양식으로 쓴 실제 검사 규칙들ACT Rules 커뮤니티 그룹이 쓰고, AG WG가 검토해 승인

레시피 카드 양식과 레시피의 관계라고 보면 됩니다. 양식이 "재료, 순서, 주의사항 칸을 둔다"고 정하면, 그 양식에 맞춰 수십 개의 레시피가 쓰이는 식이죠. 2026년 10월 7일 기준 W3C가 관리하는 규칙 목록에는 승인된 규칙 37개, 제안 단계 규칙 50개, 폐기된 규칙 7개가 있습니다.

ACT 규칙 하나는 이렇게 생겼습니다

규칙 하나를 열어 보면 생각보다 단순합니다. 「aria-hidden 요소에는 순차 포커스 탐색 대상이 없어야 한다」(ID 6cfa84)를 예로 들어 볼게요. 규칙마다 이런 6자리 ID가 붙습니다.

ACT 규칙의 판정 순서 다이어그램 - 검사할 페이지에서 적용 대상을 고르고, 대상이 없으면 해당 없음, 있으면 기대 조건을 확인해 통과 또는 실패로 판정합니다. 검사자가 확정하지 못하면 판단 보류로 남깁니다. 아래에는 가정, 보조기술 지원, 테스트 케이스가 규칙 문서에 함께 적힌다고 표시돼 있습니다
ACT 규칙의 판정 순서 다이어그램 - 검사할 페이지에서 적용 대상을 고르고, 대상이 없으면 해당 없음, 있으면 기대 조건을 확인해 통과 또는 실패로 판정합니다. 검사자가 확정하지 못하면 판단 보류로 남깁니다. 아래에는 가정, 보조기술 지원, 테스트 케이스가 규칙 문서에 함께 적힌다고 표시돼 있습니다

먼저 적용 대상(Applicability)이 이 규칙이 볼 요소를 고릅니다. 여기서는 aria-hidden="true"가 붙은 요소예요. 해당하는 요소가 하나도 없으면 결과는 "해당 없음(inapplicable)"입니다. 고른 요소마다 기대(Expectation)를 확인합니다. 이 규칙의 기대는 "그 요소 자신과 안쪽 어디에도 Tab 키로 닿는 것이 없어야 한다"이고, 지키면 통과(passed), 아니면 실패(failed)입니다.

규칙 문서에는 이 밖에도 몇 칸이 더 있습니다. 가정(Assumptions)에는 규칙이 맞으려면 성립해야 하는 전제를, 보조기술 지원(Accessibility Support)에는 브라우저나 스크린 리더마다 동작이 다른 부분을 적어요. 그리고 통과·실패·해당 없음 예제 HTML, 곧 테스트 케이스가 붙습니다. 이 규칙의 W3C 페이지에는 15개가 있습니다.

제일 쓸모 있는 건 테스트 케이스입니다. 규칙이 말로만 있으면 해석이 갈리지만, "이 HTML은 통과, 이 HTML은 실패"라는 예제가 있으면 도구가 그 답을 맞히는지 기계적으로 확인할 수 있거든요. 이 글의 측정도 이 예제들로 했습니다.

참고로 1.1 스펙은 결과(outcome)를 통과·실패·해당 없음에 판단 보류(cantTell)와 미검사(untested)까지 다섯 가지로 정의합니다. 판단 보류는 "적용 대상인지, 기대를 지켰는지 검사자가 확정하지 못했다"는 뜻이에요. 자동 검사 도구에서는 axe의 incomplete, IBM의 POTENTIAL처럼 "검토 필요"로 나오는 결과가 여기에 해당합니다. 뒤에서 보겠지만 두 도구 결과가 갈린 17건 중 8건이 이 판단 보류에서 나왔습니다.

ACT Rules Format 1.1에서 무엇이 바뀌었나

1.1은 2026년 2월 5일 W3C 권고안이 됐습니다. 1.0(2019년 10월 31일)이 나온 지 6년여 만이고, 스펙 스스로 "1.0과 대부분 호환되지만 완전히 호환되지는 않는다"고 밝힌 변화는 두 가지입니다. 지금 W3C에 올라 있는 규칙 87개(폐기 제외)는 모두 1.1 양식으로 적혀 있어요.

1.1은 2024년 6월 18일 첫 공개 초안(FPWD), 2025년 8월 19일 후보 권고안(CR)을 거쳐 권고안이 됐고, 후보 권고안 이후로는 내용이 바뀌지 않았습니다.

변화 1. 적합성 요구사항과 2차 요구사항을 나눴습니다

규칙 문서에는 "이 규칙이 어떤 WCAG 성공 기준과 연결되는가"를 적는 칸(Accessibility Requirements Mapping)이 있습니다. 1.1부터는 이 연결을 두 종류로 나눠 적습니다.

  • 적합성 요구사항(Conformance Requirements): 규칙이 그 기준을 직접 판정합니다. 규칙이 실패하면 그 기준도 충족하지 못한 것이고, 통과했다면 충족했을 수도 있고 추가 확인이 필요할 수도 있습니다.
  • 2차 요구사항(Secondary Requirements): 관련은 있지만 그 기준을 판정하려고 만든 규칙은 아닙니다.

대비 규칙을 보면 차이가 바로 보입니다.

적합성 요구사항과 2차 요구사항 비교 다이어그램 - 텍스트 최소 대비 규칙 afw4f7은 1.4.3 대비(최소)를 직접 판정하고, 1.4.6 대비(강화)와는 관련만 있습니다. 대비 5:1인 본문 크기 텍스트는 규칙을 통과하지만 7:1을 요구하는 1.4.6은 충족하지 못합니다
적합성 요구사항과 2차 요구사항 비교 다이어그램 - 텍스트 최소 대비 규칙 afw4f7은 1.4.3 대비(최소)를 직접 판정하고, 1.4.6 대비(강화)와는 관련만 있습니다. 대비 5:1인 본문 크기 텍스트는 규칙을 통과하지만 7:1을 요구하는 1.4.6은 충족하지 못합니다

「텍스트는 최소 대비를 만족한다」(afw4f7)는 4.5:1(큰 글자는 3:1)을 확인합니다. 이 규칙이 실패하면 1.4.3 대비(최소)도 실패예요. 그런데 이 규칙을 통과했다고 1.4.6 대비(강화)까지 충족한 건 아닙니다. 1.4.6은 본문 크기 글자에 7:1을 요구하니까요. 그래서 1.4.6은 2차 요구사항으로 따로 적힙니다. W3C 규칙 페이지에도 이렇게 나옵니다.

W3C 웹사이트의 ACT 규칙 afw4f7 페이지 캡처 - Accessibility Requirements Mapping에 1.4.3 Contrast (Minimum)이, 그 아래 Secondary Requirements에 1.4.6 Contrast (Enhanced)가 적혀 있고, 이 성공 기준이 규칙보다 더 엄격하다는 설명이 붙어 있습니다
W3C 웹사이트의 ACT 규칙 afw4f7 페이지 캡처 - Accessibility Requirements Mapping에 1.4.3 Contrast (Minimum)이, 그 아래 Secondary Requirements에 1.4.6 Contrast (Enhanced)가 적혀 있고, 이 성공 기준이 규칙보다 더 엄격하다는 설명이 붙어 있습니다
W3C ACT 규칙 「Text has minimum contrast」 페이지 (2026년 10월 7일 캡처)

대비 규칙은 기준이 규칙보다 엄격한 경우였죠. 반대로 규칙이 기준보다 더 엄격해서 2차 요구사항이 되는 경우도 있습니다. 「ARIA 속성은 WAI-ARIA에 정의된 것이어야 한다」(5f99a7)는 1.3.1과 4.1.2를 2차 요구사항으로 둡니다. 이 규칙은 아무 역할도 하지 않는 ARIA 속성도 실패로 봅니다. 그래서 실패 예제 중 일부는 WCAG 기준으로는 문제가 없어요. 지금 규칙 87개 가운데 21개가 방향과 상관없이 2차 요구사항을 적어 두고 있습니다.

개발자에게 이게 왜 중요하냐면, 도구가 "1.4.6 위반"이라고 보고하지 않았다고 1.4.6을 지킨 게 아니기 때문입니다. 반대로 "1.3.1 관련 실패"가 떴다고 꼭 WCAG 위반인 것도 아니고요. 1.1부터는 그 차이를 규칙 문서에서 바로 확인할 수 있습니다.

변화 2. 사람의 판단이 필요한 적용 대상도 쓸 수 있게 됐습니다

1.0은 적용 대상을 객관적으로 적도록 했습니다. 태그 이름, 계산된 역할(role), 요소 사이 거리처럼 누가 봐도 결과가 같은 속성만 쓰라는 거죠. 1.1은 이걸 "객관적으로 적는 것이 좋다(should)"로 완화했습니다. 그리고 객관적으로 정의할 방법이 없을 때에 한해, 스펙이 직접 드는 예처럼 "장식용", "내비게이션 수단", "사전 녹화된" 같은 주관적인 개념도 쓸 수 있게 했습니다.

자동 도구 입장에선 오히려 어려워진 것 아니냐고 생각할 수 있어요. 그런데 ACT 규칙은 원래 자동 도구만을 위한 게 아닙니다. 사람이 손으로 하는 검사 방법론도 같은 양식으로 적고 비교하자는 게 목적입니다. 실제로 W3C가 도구별 ACT 규칙 구현 현황을 모아 두는 ACT 구현 보고서에는 미국 정부의 수동 검사 방법론인 Trusted Tester도 올라 있습니다. "이 이미지가 장식용인가"는 기계가 확정하기 어렵지만, 사람이 검사할 때는 분명히 필요한 판단이니까요.

여기까지가 표준 이야기였습니다. 이제 실제 도구에 넣어 볼 차례예요.

W3C 승인 테스트 케이스 558개로 axe와 IBM 비교하기

W3C는 승인한 규칙의 테스트 케이스를 JSON 파일 하나로 공개합니다. 2026년 10월 7일 기준 승인 규칙 37개에 W3C가 승인 표시를 단 테스트 케이스는 558개였어요(통과 196, 실패 178, 해당 없음 184). 그중 XML 문서 2개는 크롬이 자체 XML 뷰어 화면으로 바꿔 보여 주는 바람에 테스트 문서를 검사할 수 없어서 집계에서 뺐습니다. 나머지 556개를 두 도구에 돌렸습니다. 하나는 Lighthouse와 axe DevTools의 바탕 엔진인 axe-core(이번 측정은 4.14.0), 다른 하나는 IBM의 오픈소스 검사 엔진인 Equal Access(accessibility-checker-engine) 4.0.34입니다.

두 도구 모두 "우리 규칙 X는 ACT 규칙 Y에 해당한다"는 대응표를 코드 안에 갖고 있습니다. axe는 규칙마다 actIds, IBM은 act 필드에 적어 두죠. 판정은 이렇게 했어요.

  • 두 도구 모두 기본 설정 그대로 돌립니다. axe는 기본으로 꺼진 규칙과 실험(experimental) 규칙을 빼고, IBM은 기본 정책(IBM_Accessibility)에 든 규칙만 씁니다.
  • 해당 ACT 규칙에 대응하는 도구 규칙 결과만 봅니다.
  • 그중 하나라도 위반을 내면 실패, 위반은 없고 "검토 필요"(axe incomplete, IBM POTENTIAL·MANUAL)만 있으면 판단 보류, 둘 다 없으면 통과와 해당 없음을 묶어 "실패 아님"으로 셉니다. 단, IBM이 실패 이유별로 ACT 결과를 따로 매핑해 둔 규칙은 그 매핑을 따릅니다(예: 화면 확대를 막는 viewport 설정은 IBM이 "검토 필요"로 내지만, IBM 스스로 ACT 실패로 매핑해 둠).
  • 페이지를 다른 주소로 보내는 코드(meta refresh 등)는 이동을 막아, 검사가 테스트 문서 위에서 돌게 했습니다.
  • 그 결과를 테스트 케이스의 기대 결과와 비교합니다.

규칙 하나만 돌려 보고 싶다면 아래 스크립트로 충분합니다. Node.js와 크롬, 그리고 npm 패키지 두 개면 돼요. 결과부터 보고 싶다면 코드는 건너뛰고 바로 아래 출력만 봐도 괜찮습니다.

javascript
// check-act-rule.mjs: ACT 규칙 하나의 W3C 승인 테스트 케이스에 axe-core를 돌려 본다
// 준비: npm i [email protected] [email protected] (크롬이 설치돼 있어야 합니다)
// 실행: node check-act-rule.mjs 6cfa84
import { chromium } from 'playwright';
import fs from 'node:fs';

const RULE = process.argv[2] ?? '6cfa84';
const AXE = fs.readFileSync('node_modules/axe-core/axe.min.js', 'utf8');
const res = await fetch('https://www.w3.org/WAI/content-assets/wcag-act-rules/testcases.json');
const { testcases } = await res.json();
const browser = await chromium.launch({ channel: 'chrome' });

for (const tc of testcases.filter((t) => t.ruleId === RULE && t.approved)) {
  // 케이스마다 새 컨텍스트를 연다. 앞 케이스가 남긴 리소스가 다음 판정에 섞이지 않게
  const ctx = await browser.newContext();
  const page = await ctx.newPage();
  // meta refresh 같은 페이지 이동을 막아 테스트 문서에 머물게 한다
  await page.route('**/*', (route) => {
    const req = route.request();
    const leaving = req.isNavigationRequest() && req.frame() === page.mainFrame() && req.url() !== tc.url;
    return leaving ? route.abort('aborted') : route.continue();
  });
  await page.goto(tc.url);

  let outcome;
  if (await page.evaluate(() => !!document.getElementById('xml-viewer-style'))) {
    // 스타일시트 없는 XML 문서는 크롬이 자체 뷰어 화면으로 바꿔 보여 주므로 검사하지 않는다
    outcome = 'not measurable (Chrome XML viewer)';
  } else {
    // iframe 안까지 axe 주입(그사이 사라진 프레임은 건너뜀)
    for (const f of page.frames()) await f.evaluate(AXE + ';0').catch(() => {});
    outcome = await page.evaluate(async (rid) => {
      // 이 ACT 규칙에 대응한다고 axe가 밝힌 규칙 중 기본 설정에서 켜진 것만 돌린다
      // (공개 API axe.getRules()에는 켜짐 여부가 없어 내부 목록 _audit.rules를 읽는다)
      const ids = axe._audit.rules
        .filter((r) => (r.actIds ?? []).includes(rid))
        .filter((r) => r.enabled && !r.tags.includes('experimental'))
        .map((r) => r.id);
      if (ids.length === 0) return 'not implemented or off';
      const r = await axe.run(document, { runOnly: { type: 'rule', values: ids } });
      if (r.violations.length) return 'failed';
      if (r.incomplete.length) return 'cantTell';
      return 'passed/inapplicable';
    }, RULE);
  }
  console.log(`${tc.testcaseTitle.padEnd(24)} expected: ${tc.expected.padEnd(13)} axe: ${outcome}`);
  await ctx.close();
}

await browser.close();

6cfa84로 실행한 실제 출력입니다.

text
Passed Example 1         expected: passed        axe: passed/inapplicable
Passed Example 2         expected: passed        axe: passed/inapplicable
Passed Example 3         expected: passed        axe: passed/inapplicable
Passed Example 4         expected: passed        axe: cantTell
Passed Example 5         expected: passed        axe: passed/inapplicable
Passed Example 6         expected: passed        axe: passed/inapplicable
Failed Example 1         expected: failed        axe: failed
Failed Example 2         expected: failed        axe: failed
Failed Example 3         expected: failed        axe: failed
Failed Example 4         expected: failed        axe: failed
Failed Example 5         expected: failed        axe: failed
Failed Example 6         expected: failed        axe: cantTell
Inapplicable Example 1   expected: inapplicable  axe: passed/inapplicable
Inapplicable Example 2   expected: inapplicable  axe: passed/inapplicable
Inapplicable Example 3   expected: inapplicable  axe: passed/inapplicable

열다섯 개 중 두 개에서 axe가 판단을 보류했습니다. 이 두 개가 뒤에서 볼 첫 번째 장면이에요. 558개 전체를 두 도구에 돌리고 집계하는 스크립트는 데모 저장소에 올려 두었습니다. 크롬 탭 8개로 병렬 실행하니 2분 남짓 걸렸습니다(M 계열 Mac 기준).

결과: 승인 규칙 37개 중 axe 26개, IBM 24개가 일관됨

규칙 하나를 W3C ACT 구현 보고서의 "일관됨(consistent)" 정의를 본떠(성공 기준 보고 검사는 빼고) 이렇게 나눴습니다.

  • 일관됨: 통과·해당 없음 예제를 실패로 판정한 적이 없고, 실패 예제는 모두 실패 또는 판단 보류로 냈으며, 그중 하나 이상은 실패로 잡음
  • 부분: 기대와 다른 실패 판정은 없지만 실패 예제 일부를 놓침(실패 아님으로 처리)
  • 실패로 확정 못 함: 기대와 다른 실패 판정은 없지만 실패 예제를 하나도 실패로 확정하지 못함(모두 판단 보류이거나 놓침)
  • 기대와 다름: 통과·해당 없음 예제를 실패로 판정한 적이 한 번이라도 있음. ACT 기대와 다르다는 뜻이지, 도구의 판단이 꼭 틀렸다는 뜻은 아닙니다
  • 꺼짐: 대응 규칙은 있지만 기본 설정에서 실행되지 않음
  • 미구현: 대응하는 도구 규칙이 없음
두 도구의 ACT 승인 규칙 37개 결과 막대 그래프 - axe-core 4.14.0은 일관됨 26, 부분 2, 기대와 다름 3, 꺼짐 1, 미구현 5개입니다. IBM Equal Access 4.0.34는 일관됨 24, 실패로 확정 못 함 1, 기대와 다름 4, 꺼짐 2, 미구현 6개입니다
두 도구의 ACT 승인 규칙 37개 결과 막대 그래프 - axe-core 4.14.0은 일관됨 26, 부분 2, 기대와 다름 3, 꺼짐 1, 미구현 5개입니다. IBM Equal Access 4.0.34는 일관됨 24, 실패로 확정 못 함 1, 기대와 다름 4, 꺼짐 2, 미구현 6개입니다
데이터 표
항목axe-core 4.14.0IBM Equal Access 4.0.34
기본 설정에서 실행된 규칙31개29개
일관됨26개24개
실패 예제를 실패로 잡음140 / 159135 / 145
실패 예제를 판단 보류로 넘김79
실패 예제를 놓침121
통과·해당 없음 예제를 실패로 판정3 / 3286 / 297

도구마다 실행된 규칙이 달라 분모도 다릅니다. axe가 놓친 12개 중 9개는 1.4.6 대비(강화) 규칙에서 나왔는데, axe는 AAA 대비 규칙(color-contrast-enhanced)이 기본으로 꺼져 있어서 4.5:1 검사만 돌았기 때문입니다. IBM의 꺼짐 2개(스크롤 영역 키보드 접근, 예외 없는 새로고침 지연 금지)도 IBM 엔진에 규칙은 있지만 기본 정책에서 빠져 있는 경우예요. IBM의 "실패로 확정 못 함" 1개는 장면 3의 meta refresh 규칙으로, 실패 예제 4개를 모두 판단 보류로 냈습니다.

통과·해당 없음 예제를 실패로 판정한 IBM 6건은 이렇습니다. 이름 없는 svg 요소 자체를 지적한 것 3건(장면 2), 포커스 센티널 1건(장면 1), 없는 ID를 가리키는 aria-errormessage 1건, type="tel" 입력의 autocomplete="bday-day" 1건입니다. axe의 3건은 모달 대화상자가 열려 비활성(inert) 상태가 된 iframe 예제 1건, 그리고 장면 3에서 볼 meta 요소 두 개짜리 예제 2건입니다.

W3C의 ACT 구현 보고서에도 비슷한 숫자가 있습니다. axe-core 4.10.2는 승인 규칙 26개, Equal Access 3.1.42-rc.0은 21개가 일관됨으로 올라 있어요. 다만 같은 규칙 묶음은 아닙니다. 규칙 목록을 맞대 보면 axe는 26개 중 23개, IBM은 21개 중 20개가 겹칩니다. 어긋난 규칙을 하나씩 보면 이유가 몇 가지예요. 보고서에 실린 도구 버전이 이번 측정보다 오래됐고, W3C의 axe 결과는 기본으로 꺼진 AAA 대비·예외 없는 새로고침 규칙까지 켜고 낸 것이며, meta refresh 예제처럼 실행 환경에 따라 결과가 달라지는 경우도 있습니다. W3C는 도구가 보고하는 성공 기준이 맞는지도 따로 봅니다. 이 글의 숫자는 그 보고서를 대신하는 게 아니라, 2026년 10월 최신 버전을 같은 조건에서 나란히 놓아 본 결과입니다.

두 도구가 모두 실행된 규칙은 28개, 테스트 케이스로는 430개입니다. 이 가운데 17개 케이스(규칙 8개)에서 "실패냐 아니냐"가 갈렸어요. 4%이고, 실패 예제만 놓고 보면 141개 중 9개입니다. 갈린 17개를 나눠 보면 이렇습니다.

데이터 표
갈린 이유건수
한쪽이 판단을 보류함 (IBM 보류 6, axe 보류 2)8
한쪽이 ACT가 통과·해당 없음으로 보는 코드를 실패로 봄 (IBM 5, axe 2)7
한쪽이 실패를 놓침 (IBM 1, axe 1)2

어느 도구가 판단을 보류하는지는 규칙마다 달랐습니다. 구체적으로 세 장면을 보겠습니다.

axe와 IBM 판정이 갈린 세 장면

장면 1. 스크립트가 포커스를 옮기는 모달: axe는 보류, IBM은 실패

6cfa84의 Passed Example 4와 Failed Example 6은 거의 똑같은 HTML입니다. 모달 대화상자 뒤에 aria-hidden="true"로 숨긴 링크가 하나 있어요. 포커스 센티널(focus sentinel)이라고 부르는 링크로, 키보드 포커스가 모달 밖으로 새지 않게 붙잡는 용도입니다.

html
<!-- 두 예제에 공통으로 들어 있는 부분 -->
<div aria-hidden="true">
  <a href="#" id="sentinelAfter" style="position:absolute; top:-999em">
    Upon receiving focus, this focus sentinel should wrap focus to the top of the modal
  </a>
</div>

<!-- 통과 예제에만 있는 스크립트: 링크에 포커스가 오면 곧바로 모달 첫 입력창으로 돌려보낸다 -->
<script>
  document.getElementById('sentinelAfter').addEventListener('focus', () => {
    document.getElementById('dialogFirst').focus()
  })
</script>

차이는 저 focus 이벤트 핸들러 하나입니다. 핸들러가 있으면 Tab으로 숨긴 링크에 닿는 순간 포커스가 모달로 돌아가니 사용자는 그 링크를 만나지 않습니다(통과). 없으면 스크린 리더 사용자의 포커스가 접근성 트리에서 숨겨진 링크로 가서, 무엇에 와 있는지 알 수 없는 상태가 됩니다(실패).

데이터 표
예제ACT 기대axe-coreIBM
Passed Example 4 (핸들러 있음)통과판단 보류실패
Failed Example 6 (핸들러 없음)실패판단 보류실패

두 도구 모두 HTML 구조만 보고는 저 핸들러가 무슨 일을 하는지 알 수 없습니다. 그래서 axe는 두 경우 모두 "사람이 확인하라"고 넘기고, IBM은 두 경우 모두 "실패"라고 합니다. axe는 통과 예제를 실패로 몰지 않는 대신 실패 예제도 실패로 확정하지 못했고, IBM은 실패를 놓치지 않는 대신 통과 예제까지 실패로 잡았어요. 이 규칙에서는 그랬다는 얘기입니다. 다음 두 장면을 보면 역할이 바뀌는 규칙도 있습니다.

장면 2. 이름 없는 svg: IBM은 ACT보다 넓게 봅니다

7d6734 「명시적 역할이 있는 SVG 요소에는 접근 가능한 이름이 있어야 한다」는 IBM 판정이 ACT 기대와 가장 많이 어긋난 규칙입니다. 해당 없음 예제 하나를 보면 이유가 보여요.

html
<svg xmlns="http://www.w3.org/2000/svg">
  <circle cx="50" cy="50" r="40" fill="yellow"></circle>
</svg>

role 속성이 없으니 ACT 규칙 기준으로는 검사 대상이 아닙니다. 그런데 IBM은 svg 요소에 접근 가능한 이름이 없다며 실패로 판정했습니다. 이 규칙에서 IBM이 기대와 다르게 실패로 본 3건이 모두 같은 지적이었어요. 원에 역할과 aria-label이 다 있는 통과 예제에서도, 바깥 svg에 이름이 없다는 이유로 실패가 나왔습니다. IBM은 ACT 규칙보다 넓은 범위에서 svg 자체의 이름을 요구하는 셈입니다. ACT 기준으로는 기대와 다르지만, 그 요구 자체가 틀렸다고 보기는 어렵습니다.

반대로 이 규칙의 실패 예제 4(role="img"인 svg에 title이나 aria-label 없이 text 요소로만 글자를 넣은 경우)는 axe가 실패로 잡고 IBM은 놓쳤습니다. ACT 규칙은 SVG text 요소를 이름 계산에 쓰지 않는다고 보는데, IBM은 이 svg에 접근 가능한 이름이 있다고 판정했습니다.

장면 3. meta refresh: axe는 잡고, IBM은 보류

bc659a 「meta 요소에 새로고침 지연이 없어야 한다」의 실패 예제 4개는 <meta http-equiv="refresh" content="30">처럼 일정 시간 뒤 페이지를 바꿔 버리는 코드입니다. axe는 4개를 모두 실패로 잡았고, IBM은 4개 모두 판단 보류로 넘겼습니다. 이번에는 장면 1과 역할이 반대예요. 어느 도구가 판단을 미루는지는 규칙마다 다르다는 뜻입니다.

대신 이 규칙에서는 axe가 ACT 기대와 어긋난 곳도 있습니다. 통과 예제 2에는 meta 새로고침이 두 개 있어요.

html
<meta http-equiv="refresh" content="0; https://w3.org" />
<meta http-equiv="refresh" content="5; https://w3.org" />

ACT 규칙은 "첫 번째 유효한 meta만 따진다"고 보고, 첫 번째가 지연 없이 바로 이동하니 통과입니다. axe는 두 번째의 5초 지연을 실패로 지적했습니다. 효력이 있는 건 첫 번째 meta뿐이라 사용자가 5초를 기다릴 일은 없지만, 두 번째 줄이 쓸모없는 코드인 건 맞으니 axe의 지적이 해롭지는 않습니다.

두 도구가 엇갈리면 결국 사람이 판단해야 하는데, 그때 기준으로 삼을 수 있는 게 ACT 규칙의 예제입니다.

이 측정이 말해 주지 않는 것

ACT 테스트 케이스는 기능 하나만 떼어 낸 아주 작은 HTML입니다. 실제 웹페이지에서 어떤 오류가 얼마나 자주 나오는지는 반영하지 않아요. 예제가 많은 규칙(두 대비 규칙은 각각 32개, 34개입니다)이 숫자에 더 크게 잡히기도 하고요. 그래서 이 결과를 "어느 도구가 실제 사이트에서 오류를 더 많이 찾는다"로 읽으면 안 됩니다.

대응표도 도구가 스스로 밝힌 것을 그대로 썼습니다. 도구가 ACT 규칙과 연결해 두지 않은 다른 규칙으로 같은 문제를 잡더라도 이 집계에는 들어가지 않습니다. 그리고 기본 설정 기준이라, 꺼진 규칙을 켜면 숫자가 달라집니다. 판정도 페이지 단위로 셌습니다. 도구가 ACT 규칙이 보는 바로 그 요소를 짚었는지까지는 맞춰 보지 않았어요.

접근성 검사 도구 결과가 다를 때 개발자가 할 일

먼저 "검토 필요"를 통과로 읽지 마세요. 앞에서 본 incomplete와 POTENTIAL은 "모르겠다"는 뜻입니다. 이번 측정에서도 실패 예제를 판단 보류로 넘긴 경우가 axe 7번, IBM 9번 있었어요. CI에서 위반(violations) 0개만 보고 통과시키고 있다면, 검토 필요 항목도 따로 쌓아 두고 사람이 보게 하는 편이 안전합니다. Playwright 테스트에 axe를 붙이는 방법은 axe로 E2E 접근성 테스트하기에 정리해 두었습니다.

두 도구 결과가 갈리면 ACT 규칙 예제와 비교해 보세요. 도구 보고서의 규칙 이름으로 대응하는 ACT 규칙을 찾고, 그 규칙의 통과·실패 예제 중 내 코드와 가장 닮은 걸 고르면 됩니다. 위 스크립트에 규칙 ID만 바꿔 넣으면 내가 쓰는 도구가 그 규칙을 어떻게 판정하는지도 바로 볼 수 있어요.

AAA 기준까지 지켜야 한다면 도구의 기본 설정을 확인하세요. axe는 AAA 대비 규칙이 기본으로 꺼져 있어서, 켜지 않으면 대비가 4.5:1 이상 7:1 미만인 글자는 잡지 못합니다. 다른 도구도 기본 설정에서 빠진 규칙이 있을 수 있습니다.

도구를 고를 때는 구현 보고서도 참고할 만합니다. W3C ACT 구현 보고서에는 axe-core, IBM Equal Access, Alfa, QualWeb, SortSite 같은 도구가 규칙별로 얼마나 일관되게 구현했는지가 나옵니다. 2026년 10월 7일 기준으로 이 목록에 국내 도구는 없었습니다. 국내 자동 점검 도구끼리 비교할 때도 ACT 테스트 케이스는 좋은 공통 잣대가 될 수 있습니다.

마지막으로, 자동 검사는 평가의 일부일 뿐입니다. 어떤 화면을 골라 어떤 순서로 평가할지는 WCAG-EM 2.0 평가 방법론이, 검사 단위를 어떻게 나눌지는 WCAG 3의 원자 테스트와 종합 테스트가 다룹니다. ACT 규칙은 그 안에서 기계 검사와 사람 검사를 같은 방법으로 맞춰 보는 데 쓰입니다.

한 장 요약

  • 접근성 검사 도구 결과가 갈리는 건 WCAG 문장을 코드로 옮기는 방법이 도구마다 다르기 때문입니다. ACT 규칙은 그 방법을 같은 양식으로 적고, 통과·실패 예제 HTML을 함께 붙인 공통 검사 규칙입니다.
  • ACT Rules Format 1.1은 2026년 2월 5일 W3C 권고안이 됐습니다. 규칙과 WCAG 기준의 관계를 적합성 요구사항(직접 판정)과 2차 요구사항(관련만 있음)으로 나눠 적고, 객관적으로 정의할 수 없을 때는 사람의 판단이 필요한 적용 대상도 허용합니다.
  • W3C 승인 규칙 37개의 테스트 케이스를 기본 설정으로 돌린 결과(측정 가능한 556개), axe-core 4.14.0은 26개, IBM Equal Access 4.0.34는 24개 규칙에서 일관됐습니다.
  • 두 도구가 모두 검사한 430개 중 413개에서 실패 여부가 같았습니다. 갈린 17개는 한쪽의 판단 보류(8), ACT가 통과로 보는 코드를 실패로 본 것(7), 놓침(2)이었고, 어느 도구가 보류하는지는 규칙마다 달랐습니다.
  • "검토 필요"는 통과가 아닙니다. 결과가 갈리면 ACT 규칙 예제와 비교하고, 마지막 확인은 키보드와 스크린 리더로 직접 하세요.

질문으로 다시 보기

ACT 규칙(ACT Rules)이란 무엇인가요?
ACT는 Accessibility Conformance Testing의 약자입니다. WCAG 성공 기준을 실제로 어떻게 검사할지를 정해진 양식으로 적은 검사 규칙이에요. 어떤 요소를 볼지(적용 대상), 무엇을 확인할지(기대), 어떤 전제가 필요한지(가정)를 적고, 통과·실패·해당 없음 예제 HTML을 함께 붙입니다. 2026년 10월 기준 W3C가 승인한 규칙은 37개입니다.
ACT Rules Format 1.1에서 무엇이 바뀌었나요?
두 가지입니다. 첫째, 규칙과 WCAG 기준의 관계를 적합성 요구사항(Conformance Requirements, 규칙이 직접 판정하는 기준)과 2차 요구사항(Secondary Requirements, 관련만 있는 기준)으로 나눠 적게 했습니다. 둘째, 객관적으로 정의할 방법이 없을 때에 한해 '장식용'처럼 사람의 판단이 필요한 말로 적용 대상을 적는 것도 허용했습니다. 2026년 2월 5일 W3C 권고안이 됐고, 1.0과 완전히 호환되지는 않습니다.
axe와 Lighthouse, IBM 검사기 결과가 다르면 어느 쪽을 믿어야 하나요?
W3C 승인 규칙 테스트 케이스로 재 보니 두 도구는 대부분 같은 답을 냈고, 갈린 곳은 한쪽이 판단을 보류했거나 한쪽이 ACT가 문제 삼지 않는 코드를 실패로 본 경우가 많았습니다. 어느 도구가 판단을 보류하는지는 규칙마다 달랐어요. 결과가 갈린 항목은 해당 ACT 규칙의 예제와 비교해 보고, 최종 판단은 키보드·스크린 리더로 직접 확인하는 게 안전합니다.
자동 검사 도구가 ACT 규칙을 얼마나 지원하는지 어디서 확인하나요?
W3C의 ACT 구현 보고서(ACT Implementations) 페이지에서 도구별로 확인할 수 있습니다. 도구 제작사가 ACT 테스트 케이스를 돌린 결과를 제출하고, 규칙마다 일관되게 구현했는지(consistent)가 표시됩니다. 2026년 10월 7일 기준 국내 도구는 이 목록에 없었습니다.

참고 자료