앞의 편들에서 나는 눈으로 보지 못하는 사용자를 주로 떠올렸다. 그런데 접근성에는 또 하나의 큰 축이 있다. 마우스를 쓰지 못하는 사용자다. 손 떨림이 있거나, 팔을 다쳤거나, 화면 낭독기를 쓰는 사람은 마우스 대신 키보드로 웹을 조작한다. 이번 편은 마우스 없이 키보드만으로 웹을 쓸 수 있게 만드는 키보드 내비게이션을 다룬다.


나는 손목을 다쳐 며칠간 마우스를 못 쓴 적이 있다. 그때 키보드만으로 웹을 써보니, 평소엔 아무렇지 않던 사이트들이 갑자기 벽으로 변했다. 포커스가 어디 있는지 보이지 않고, 눌러야 할 버튼에 닿지도 못했다. 그 답답함을 겪고 나서 나는 키보드 접근성을 절대 소홀히 하지 않게 됐다. 이 편은 그 경험에서 얻은 원칙들의 기록이다.

1. 왜 키보드가 기준인가

키보드 접근성은 접근성의 관문 같은 것이다. 키보드로 쓸 수 있으면 화면 낭독기, 음성 조작, 특수 입력 장치까지 대부분 함께 통한다. 이 보조 장치들이 내부적으로 키보드 방식으로 동작하기 때문이다. 그래서 키보드 접근성을 챙기면 여러 사용자를 한꺼번에 품게 된다. 나는 키보드를 접근성의 공통 언어로 여기며 가장 먼저 챙긴다.


키보드 조작의 기본은 몇 가지 키로 이루어진다. 탭 키로 다음 요소로 이동하고, 시프트와 탭을 함께 눌러 이전 요소로 돌아간다. 엔터나 스페이스로 활성화하고, 화살표로 항목 사이를 오간다. 이 몇 가지 키만으로 모든 기능을 쓸 수 있어야 한다. 나는 새 화면을 만들면 마우스를 치우고 이 키들만으로 끝까지 써보는 점검을 습관으로 삼았다.


마우스 없이 모든 기능을 쓸 수 있어야 한다는 것은 접근성 지침의 핵심 요구이기도 하다. 특정 기능이 마우스로만 되고 키보드로는 안 된다면, 키보드 사용자에게 그 기능은 없는 것과 같다. 나는 이 요구를 지키기 위해 마우스로만 되는 동작이 하나라도 있는지 항상 점검한다. 드래그처럼 마우스에 기댄 동작에는 키보드 대안을 함께 마련한다.


키보드 사용자에게 가장 답답한 것은 지금 포커스가 어디 있는지 모르는 상황이다. 눈으로 보는 사람은 마우스 커서로 위치를 알지만, 키보드 사용자는 포커스 표시에 전적으로 의존한다. 포커스가 보이지 않으면 길을 잃는다. 나는 포커스 표시를 지우는 것이 키보드 사용자에게 얼마나 치명적인지 겪어보고 나서, 그 표시를 함부로 없애지 않게 됐다.


키보드 접근성은 눈으로 보는 사람에게도 이롭다. 긴 폼을 채울 때 탭 키로 칸을 옮기는 것이 마우스보다 훨씬 빠르다. 파워 유저는 키보드만으로 웹을 날아다닌다. 접근성을 위해 챙긴 키보드 조작이 효율까지 높이는 것이다. 나는 이 겹치는 이득을 겪으며 키보드 접근성을 소수를 위한 배려가 아니라 모두를 위한 기본으로 대하게 됐다.


정리하면 키보드 접근성은 여러 보조 장치가 공유하는 공통 언어이자 접근성의 관문이며, 모든 기능이 키보드로 되고 포커스가 늘 보여야 한다는 두 원칙 위에 선다. 나는 이 두 가지를 키보드 접근성의 뼈대로 삼는다. 이제 그 뼈대의 첫 기둥인 포커스 이동 순서부터 자세히 살펴보자.

2. 탭 순서는 문서 순서를 따른다

탭 키를 누르면 포커스가 다음 요소로 이동한다. 이때 이동하는 순서는 기본적으로 코드에 적힌 순서, 곧 문서 순서를 따른다. 코드에서 먼저 나온 요소가 먼저 포커스를 받는다. 이 순서가 눈으로 보는 화면 순서와 맞으면 키보드 사용자는 자연스럽게 위에서 아래로, 왼쪽에서 오른쪽으로 이동한다. 나는 이 일치를 기본 원칙으로 지킨다.


문제는 스타일로 화면 순서를 바꿨을 때 생긴다. 코드에서는 아래에 있는 요소를 스타일로 위로 끌어올리면, 눈에는 위에 보이는데 탭은 여전히 나중에 닿는다. 시각 순서와 탭 순서가 어긋나면 키보드 사용자는 포커스가 화면을 제멋대로 튀는 혼란을 겪는다. 나는 이 어긋남을 피하려고 스타일로 순서를 크게 뒤집는 것을 조심한다.


그래서 원칙은 시각 순서와 코드 순서를 일치시키는 것이다. 화면에 보이는 흐름대로 코드도 흐르게 짜면, 탭 순서는 저절로 자연스러워진다. 순서를 억지로 조정하기보다 애초에 코드 순서를 화면 순서에 맞추는 것이 훨씬 견고하다. 나는 화면을 설계할 때 시각 흐름과 코드 흐름을 함께 그려 두 순서가 어긋나지 않게 한다.


탭 순서는 화면 낭독기의 읽기 순서와도 연결된다. 낭독기 역시 문서 순서로 내용을 읽기 때문에, 코드 순서가 뒤죽박죽이면 낭독 순서도 뒤죽박죽이 된다. 즉 코드 순서를 바로잡으면 키보드 사용자와 낭독기 사용자를 함께 돕는 것이다. 나는 이 겹침을 보며 코드 순서를 접근성의 숨은 기둥으로 여기게 됐다.


순서를 조정해야 할 드문 경우도 있지만, 스타일로 순서를 크게 바꾸는 것은 최후의 수단이다. 대부분은 코드 순서를 다시 짜는 것으로 해결된다. 나는 순서 문제를 만나면 억지 조정보다 구조를 다시 보는 쪽을 택한다. 근본을 바로잡으면 곁가지 문제들이 함께 사라진다는 걸 여러 번 경험했다.


정리하면 탭 순서는 문서 순서를 따르므로 시각 순서와 코드 순서를 일치시키는 것이 핵심이고, 이 일치는 키보드 사용자와 낭독기 사용자를 함께 돕는다. 나는 순서를 억지로 조정하기보다 코드 흐름을 바로잡는다. 그렇다면 특정 요소의 포커스 여부를 직접 조절해야 할 때는 어떻게 할까. 그 도구를 살펴보자.

3. 포커스 순서를 다루는 속성

요소의 포커스 여부와 순서를 조절하는 속성이 tabindex다. 이 속성에 어떤 값을 주느냐에 따라 요소가 탭 순서에 들어가는 방식이 달라진다. 강력한 도구지만 잘못 쓰면 순서를 헝클어뜨리기 쉽다. 나는 이 속성을 배우면서 값마다 의미가 크게 다르다는 걸 알았고, 그 뒤로 값을 신중하게 고른다.


값이 tabindex="0"이면 요소가 자연스러운 탭 순서에 들어간다. 원래 포커스를 받지 못하는 요소를 문서 순서 그대로 포커스 흐름에 편입시킬 때 쓴다. 커스텀으로 만든 위젯을 키보드로 조작하게 하려면 이 값이 필요하다. 나는 상자 요소로 만든 특수 컨트롤을 포커스 가능하게 할 때 이 값을 붙여 자연 순서에 자연스럽게 끼워 넣는다.


값이 tabindex="-1"이면 요소가 탭 순서에서는 빠지지만 코드로는 포커스를 줄 수 있다. 사용자가 탭으로 도달하지는 못하되, 필요할 때 프로그램이 포커스를 옮길 수 있는 대상이 된다. 오류가 생겼을 때 오류 요약으로 포커스를 점프시키는 자리가 대표적이다. 나는 이 값을 프로그램적 포커스 전용 표시로 이해하고 그런 자리에만 쓴다.


양수 값은 쓰지 않는 것이 원칙이다. 양수를 주면 그 숫자 순서대로 포커스가 이동해, 문서 순서를 무시하고 억지로 우선순위를 매긴다. 이렇게 되면 순서가 꼬이고 유지보수가 지옥이 된다. 나는 양수 값을 접근성의 대표적인 함정으로 배웠고, 어떤 경우에도 쓰지 않는다. 순서가 필요하면 양수가 아니라 코드 순서를 바로잡는다.


정리하면 이 속성은 세 가지 쓰임만 기억하면 된다. 자연 순서 편입에는 영 값을, 프로그램 포커스 전용에는 음수 값을 쓰고, 양수 값은 절대 쓰지 않는다. 이 세 가지만 지켜도 순서 문제의 대부분이 사라진다. 나는 이 단순한 규칙을 지키면서 포커스 순서로 골머리를 앓는 일이 크게 줄었다. 다음은 어떤 요소가 원래 포커스를 받는지다.


포커스 문제를 다룰 때 나는 늘 근본을 먼저 본다. 순서가 이상하면 속성으로 억지로 맞추기 전에 코드 구조를 살핀다. 대개 구조를 바로잡으면 속성을 거의 쓰지 않고도 자연스러운 순서를 얻는다. 속성은 구조로 안 되는 틈을 메우는 보조 도구일 뿐이다. 나는 이 순서, 곧 구조가 먼저이고 속성이 나중이라는 원칙을 포커스에서도 지킨다.

4. 포커스 가능한 요소들

어떤 요소는 아무것도 하지 않아도 포커스를 받는다. 링크 요소 a, 버튼 요소 button, 입력 요소 input, 선택 요소, 여러 줄 입력 요소가 그렇다. 이 요소들은 태어날 때부터 키보드로 포커스가 가고 조작이 된다. 나는 이 사실이 시맨틱을 지켜야 할 또 하나의 이유라고 본다. 의미에 맞는 요소를 쓰면 키보드 조작이 공짜로 따라온다.


반대로 상자 요소처럼 원래 포커스를 받지 못하는 요소로 컨트롤을 만들면, 키보드 조작을 전부 손으로 되살려야 한다. 포커스를 받게 하는 속성을 붙이고, 역할을 알리고, 키 처리를 직접 짜야 한다. 나는 이 수고를 여러 번 겪고 나서, 포커스가 필요한 곳에는 처음부터 포커스를 갖는 요소를 쓰는 것이 훨씬 낫다는 걸 배웠다.


커스텀 위젯을 꼭 만들어야 할 때는 잃어버린 것을 되살리는 순서를 밟는다. 먼저 요소를 포커스 가능하게 만들고, 무슨 역할인지 보조기기에 알리고, 엔터와 스페이스, 화살표 같은 키 처리를 붙인다. 이 중 하나라도 빠지면 반쪽짜리가 된다. 나는 커스텀 위젯을 만들 때 이 항목들을 점검표로 두고 하나씩 확인한다.


포커스를 받는 요소라도 화면에서 숨기면 포커스도 함께 관리해야 한다. 접었다 펴는 메뉴 안의 링크가 접힌 상태에서도 포커스를 받으면, 사용자는 보이지 않는 곳으로 포커스가 사라지는 혼란을 겪는다. 나는 숨긴 영역의 요소는 포커스도 받지 못하게 처리해, 보이는 것과 포커스가 갈 수 있는 곳을 일치시킨다.


포커스 가능한 요소를 다룰 때 나는 늘 눈에 보이는 것과 포커스가 닿는 것을 맞추려 애쓴다. 보이는데 포커스가 안 가거나, 안 보이는데 포커스가 가는 어긋남이 키보드 사용자를 가장 헷갈리게 하기 때문이다. 이 둘을 일치시키는 것이 커스텀 위젯 접근성의 핵심이다. 나는 이 일치를 기준으로 삼아 포커스 흐름을 점검한다.


정리하면 링크, 버튼, 입력 같은 요소는 포커스를 공짜로 받으니 이를 쓰는 것이 최선이고, 커스텀 위젯은 포커스, 역할, 키 처리를 손으로 되살려야 하며, 보이는 것과 포커스가 닿는 곳을 일치시켜야 한다. 나는 이 원칙으로 커스텀 위젯을 다룬다. 마지막으로, 포커스가 빠져나오지 못하는 함정을 피하는 법을 살펴보자.

5. 키보드 함정을 피한다

키보드 접근성에서 반드시 피해야 할 것이 키보드 함정이다. 포커스가 어떤 영역에 들어간 뒤 탭으로도 빠져나오지 못하는 상태다. 마우스라면 다른 곳을 클릭해 벗어날 수 있지만, 키보드 사용자는 그 안에 갇혀 페이지 전체를 쓸 수 없게 된다. 나는 이 함정을 접근성에서 가장 치명적인 결함의 하나로 여긴다.


키보드 함정은 대개 잘못 만든 커스텀 위젯이나 임베드된 외부 콘텐츠에서 생긴다. 포커스가 그 안으로 들어가는 길만 있고 나오는 길을 만들지 않은 것이다. 나는 커스텀 위젯을 만들 때 들어가는 길과 나오는 길을 항상 짝으로 챙긴다. 탭으로 들어갈 수 있다면 탭으로 나올 수도 있어야 한다는 것이 기본이다.


다만 의도적으로 포커스를 가두는 경우가 하나 있다. 모달 창이다. 모달이 열리면 그 뒤 배경은 잠시 쓸 수 없어야 하므로, 포커스를 모달 안에 가두는 것이 오히려 올바른 동작이다. 대신 반드시 탈출구를 마련한다. 나는 모달에 이에스시 키로 닫는 길을 꼭 만들어, 가두되 언제든 빠져나올 수 있게 한다.


이 차이가 중요하다. 실수로 생긴 키보드 함정은 결함이지만, 모달의 포커스 가둠은 탈출구가 있는 의도된 설계다. 둘을 가르는 것은 빠져나올 방법의 유무다. 나는 포커스를 가두는 모든 곳에 반드시 명확한 탈출구가 있는지를 점검한다. 탈출구 없는 가둠은 어떤 이유로도 정당화되지 않는다.


키보드 함정을 찾는 가장 확실한 방법은 직접 탭만으로 페이지를 끝까지 돌아보는 것이다. 자동 검사 도구는 포커스가 실제로 어디에 갇히는지까지 잡아내지 못할 때가 많다. 나는 새 화면마다 마우스를 치우고 탭 키만으로 처음부터 끝까지 순회하며, 포커스가 어딘가에 갇히거나 사라지는 지점이 없는지 눈으로 확인한다.


정리하면 키보드 함정은 포커스가 빠져나오지 못하는 치명적 결함이므로 들어가는 길과 나오는 길을 짝으로 챙기고, 모달처럼 의도적으로 가두는 경우에도 반드시 탈출구를 둔다. 나는 탭만으로 페이지를 순회하며 함정을 찾는다. 이렇게 포커스가 잘 흐르게 만들었다면, 다음은 그 포커스를 어떻게 보이고 관리할지다. 다음 편은 포커스 관리를 다룬다.