키보드 탐색
tabindex 양수 지정이 초점 순서를 파괴하는 원리와 복구 순서
tabindex="1" 이상의 양수 지정이 DOM 트리 기반의 자연스러운 초점 흐름을 왜곡하는 원리와, tabindex="0" 및 DOM 구조 재배치로 키보드 접근성을 복구하는 실무 절차를 설명합니다.
tabindex 속성에 1 이상의 양수를 지정하면 전체 문서의 키보드 초점 흐름이 파괴됩니다. 대화형 요소는 tabindex="0" 또는 -1만 사용하고 DOM 순서로 초점을 정렬해야 합니다.실무 검증 기록
키보드 조작 및 초점 경로 검증
본 기록은 공개 fixture와 재현 조건을 기준으로 정리한 기술 설명입니다. 실제 브라우저·스크린리더 조합의 동작은 별도 수동 검토가 필요합니다.
| 검증 상태 | 공개 fixture 기반 재현 완료 |
|---|---|
| 검증 기준 | 공개 fixture 및 재현 조건 |
| 검증 범위 | tabindex 양수 지정으로 인한 초점 순서 왜곡 및 복구 검증 및 명시된 입력값에 한정 |
| 수동 확인 | 실제 브라우저·키보드·스크린리더 환경에서 별도 확인 필요 |
| fixture에서 확인한 기대 결과 | [실패 상태] 시각 순서와 키보드 순서가 역전 ➔ [수정 목표] 양수 tabindex 제거 및 실제 Tab 순서 기록 |
검증에 사용된 실제 픽스처 / 재현 조건
tabindex="1", tabindex="2"와 DOM 순서- 서드파티 임베드 위젯(iframe, 결제 모듈) 내부의 자체 tabindex 양수 제어 불가
- Shadow DOM 내부 격리 초점 순서
HTML 마크업에 tabindex='1', tabindex='2'와 같은 양수(positive) tabindex를 사용하면, 브라우저가 DOM 소스 코드의 자연스러운 순서를 무시하고 양수 속성값을 가진 요소부터 우선 방문하여 시각적 흐름과 키보드 탐색 순서가 심각하게 역전됩니다. 양수 tabindex 제거 및 논리적 DOM 순서 재배치 원리를 분석합니다.
양수 tabindex의 브라우저 렌더링 메커니즘과 초점 왜곡
웹 브라우저는 기본적으로 HTML 문서의 DOM 트리 순서(Document Order)에 따라 키보드 초점을 이동시킵니다. a, button, input, select 등 대화형(Interactive) 요소는 기본적으로 tabindex="0" 상태로 취급되어 상단에서 하단으로 자연스럽게 탐색됩니다.
하지만 단 하나의 요소라도 tabindex="1" 이상의 양수 값을 가지게 되면, 브라우저는 양수 tabindex가 적용된 모든 요소를 번호 오름차순(1 -> 2 -> 3...)으로 최우선 탐색 대상으로 설정합니다. 그 결과 사용자가 상단 헤더에서 탭 키를 누르는 즉시 페이지 중간이나 사이드바의 양수 tabindex 요소로 초점이 갑자기 이탈(Focus Jump)한 뒤, 모든 양수 요소 탐색이 끝나서야 비로소 일반 DOM 순서로 돌아오는 심각한 키보드 내비게이션 단절이 일어납니다.
실무 리디자인 과정에서 발생하는 초점 왜곡과 동적 레이어 파열
실무 현장에서 양수 tabindex 오류는 UI 리디자인이나 급한 핫픽스 과정에서 자주 발생합니다. 예컨대 로그인 버튼이나 결제 버튼을 시각적으로 돋보이게 옮기면서 탭 순서만 임시로 맞추려고 tabindex="1"을 부여하면, 사용자가 페이지에 진입하자마자 로고나 상단 메뉴를 건너뛰고 해당 버튼으로 초점이 튀게 됩니다.
양수 tabindex 문제는 고정된 정적 페이지보다 동적 컴포넌트가 추가될 때 더욱 치명적입니다. 폼 입력 필드에 tabindex="1", "2"가 설정된 상태에서 모달 팝업이나 드롭다운 메뉴가 열리면, 스크린리더 사용자는 모달 내부 요소 대신 페이지 배경의 양수 tabindex 요소로 초점이 강제 이동되는 현상을 겪게 됩니다. 이는 WCAG 2.2 성공 기준 2.4.3 (Focus Order, Level A)과 성공 기준 2.1.1 (Keyboard, Level A)을 동시에 위반하는 결과를 낳습니다.
키보드 초점 순서를 정상화하는 3단계 실무 리팩터링
양수 tabindex 결함을 근본적으로 수정하기 위해서는 다음 3단계 리팩터링 절차를 준수합니다.
- 전수 검색 및 제거: 코드베이스 전체에서 tabindex="1" 이상의 모든 양수 속성을 검색하여 완전히 제거합니다.
- DOM 순서와 CSS Flex/Grid 정렬: 시각적 배치(Flex의 order 속성이나 Grid의 grid-area/order 속성)와 DOM 순서가 다른 경우, HTML DOM 요소의 물리적 위치를 시각적 흐름에 맞춰 재배치합니다.
- 프로그램적 초점 제어: 팝업, 탭 패널, 모달 등 동적 인터랙션이 필요한 경우 tabindex="-1"과 JavaScript focus() 메서드를 활용하여 초점을 이동시킵니다.
자동화 정적 분석과 팀 단위 재발 방지 가이드라인
양수 tabindex 문제는 한 명의 개발자가 임시방편으로 추가한 코드가 페이지 전체의 접근성을 무너뜨릴 수 있으므로 빌드 파이프라인에서 자동 검출하는 환경을 갖추는 것이 중요합니다.
React나 JSX 기반 코드베이스에서는 ESLint의 eslint-plugin-jsx-a11y 패키지의 jsx-a11y/tabindex-no-positive 규칙을 활성화하여 pull request 단계에서 양수 tabindex 커밋을 자동으로 차단할 수 있습니다. 또한 CI/CD 파이프라인에 axe-core 등의 접근성 자동화 툴을 연동하면 초점 이동 오류를 미리 예방할 수 있습니다.
초점 유실 및 상호작용 실패 원인 분석
| 초점 조작 실패 상황 | 정상 초점 제어 복구 방법 |
|---|---|
| 시각적 배치와 탭 순서가 맞지 않을 때 핫픽스로 tabindex="1", tabindex="2" 등 양수 값을 부여하는 것 | 양수 tabindex 사용을 전면 금지하고, CSS 시각적 스타일이 아닌 HTML DOM 구조 자체를 논리적 순서로 수정합니다. |
| 서로 다른 컴포넌트 개발자가 각각 독립적으로 tabindex="1"을 중복 부여하여 동일 양수 번호끼리 DOM 순서대로 탐색되는 엉킨 초점 흐름을 만드는 것 | 코드 리뷰 체크리스트와 ESLint의 jsx-a11y/tabindex-no-positive 규칙을 도입하여 CI 단계에서 양수 tabindex 커밋을 자동 차단합니다. |
수정 후 키보드 조작 회귀 점검 순서
- 프로젝트 코드베이스 전체에서 regex 패턴(tabindex=["']?[1-9]\d*)을 검색하여 양수 tabindex 속성을 모두 선별합니다.
- 선별된 양수 tabindex 속성을 제거하고, 해당 컴포넌트의 HTML DOM 작성 순서를 시각적 탐색 순서와 동일하게 재정렬합니다.
- 키보드의 Tab 키와 Shift+Tab 키만을 사용하여 페이지 상단부터 하단까지 순차 탐색하며 초점 무단 이동이나 가림 현상이 없는지 재검사합니다.
수동 키보드 탐색 시 주의할 한계
- CSS flex-direction: row-reverse 또는 grid 재배치로 시각적 순서와 DOM 순서가 달라진 경우, tabindex가 0이더라도 초점 순서 괴리가 발생합니다.
- 모달 팝업 내부로 초점을 가두기 위해 양수 tabindex를 사용하는 것은 안티패턴이며 JS 키다운 이벤트 트랩을 사용해야 합니다.