웹접근성, 그리고 에디터에서 적용해보기

#에디터#웹접근성#HTML

일반 웹접근성 기본부터 에디터에서 달라지는 접근성까지

최근 면접에서 a 태그와 onclick의 차이점에 대한 질문을 받았습니다. 당시 저는 이벤트 처리 방식이나 DOM 관점에서만 답을 떠올렸습니다. 그런데 면접이 끝나고 나서 다시 생각해 보니, 그 질문은 단순히 "기술적으로 무엇이 다르냐"보다 사용자 입장에서 어떤 차이가 생기느냐, 즉 웹접근성 관점으로도 볼 수 있는 질문이었습니다.

예를 들어 이동이 목적이라면 div onclick보다 a href가 더 자연스럽습니다.

  • <a href="...">는 브라우저와 보조기술이 링크로 인식합니다.
  • 키보드로 Tab 이동이 가능하고, Enter로 활성화할 수 있습니다.
  • 브라우저가 기본 제공하는 링크 동작도 함께 따라옵니다. 예를 들면 새 탭 열기, 링크 복사, 방문 기록과의 연동 같은 것들입니다.
  • 반면 div onclick은 클릭은 처리할 수 있어도, 이런 기본 동작과 의미를 자동으로 얻지 못합니다.

비슷한 사례는 더 있습니다.

  • button 대신 div onClick을 사용하는 경우
  • input 대신 contenteditable만으로 입력 UI를 구성하는 경우
  • 시맨틱 태그 대신 전부 div로만 화면을 만드는 경우

저 역시 웹 기반 에디터를 개발하면서 이런 지점을 자주 마주쳤습니다. 특히 에디터는 일반적인 페이지보다 상호작용이 훨씬 많고, 키보드 사용 비중도 높고, 현재 위치나 상태를 더 정교하게 알려줘야 합니다. 그래서 같은 웹접근성이라도 일반 웹에서의 접근성에디터에서의 접근성은 결이 조금 다릅니다.

이번 글에서는 먼저 일반적인 웹접근성을 간단히 정리하고, 그 다음 제가 경험한 에디터에서의 웹접근성은 무엇이 달랐는지 적어보려고 합니다.


웹접근성 기본

에디터 이야기를 하기 전에, 먼저 일반적인 웹접근성에서 자주 만나는 기본 요소들을 짚고 가보겠습니다.

시맨틱 HTML은 생각보다 많은 것을 해결해준다

웹접근성을 이야기할 때 ARIA부터 떠올리기 쉽지만, 실제로는 올바른 HTML 요소를 사용하는 것이 먼저입니다.

예를 들어:

  • 이동이 목적이면 a
  • 동작 실행이 목적이면 button
  • 입력이면 input, textarea
  • 목록이면 ul, ol, li
  • 제목이면 h1~h6

이렇게 맞는 태그를 사용하면 브라우저와 스크린리더는 그 요소의 역할을 더 잘 이해합니다. 키보드 포커스, 기본 동작, 접근성 트리 노출도 대부분 함께 따라옵니다.

<a href="/docs">문서 보러 가기</a>
<button type="button">저장</button>
<textarea aria-label="문서 내용 입력"></textarea>

반대로 div 하나로 모든 것을 해결하려고 하면, 클릭은 붙일 수 있어도 의미는 자동으로 생기지 않습니다.

<div onclick="save()">저장</div>

이 경우에는 화면상으로는 버튼처럼 보여도, 보조기술 입장에서는 그냥 div일 뿐입니다. 결국 개발자가 직접 role, tabindex, 키보드 이벤트, 상태 전달까지 모두 챙겨야 합니다.


Landmark와 구조는 탐색 경험을 바꾼다

스크린리더 사용자는 화면을 "눈으로 훑는" 대신, 구조를 기준으로 이동합니다. 그래서 페이지의 큰 영역을 나눠 주는 것도 중요합니다.

<header>헤더</header>
<nav aria-label="주요 메뉴">...</nav>
<main>본문</main>
<footer>푸터</footer>

사실 이런 경우에는 굳이 role="banner", role="navigation", role="main"을 일일이 붙이지 않아도 됩니다. HTML 자체가 이미 시맨틱을 가지고 있기 때문입니다. 오히려 가능하면 네이티브 시맨틱을 우선 사용하고, ARIA는 부족한 부분을 보완할 때만 쓰는 편이 자연스럽습니다.


키보드만으로 사용할 수 있어야 한다

웹접근성에서 자주 언급되는 것 중 하나가 키보드 접근성입니다. 이건 단순히 "단축키가 있느냐"의 문제가 아니라, 마우스 없이도 기능에 도달하고 실행할 수 있느냐에 가깝습니다.

기본적으로는 다음이 충족되어야 합니다.

  • Tab으로 포커스 이동이 가능해야 한다.
  • Enter 또는 Space로 적절히 동작해야 한다.
  • 현재 포커스가 어디에 있는지 시각적으로 보여야 한다.

예를 들어 버튼이라면 브라우저가 기본적으로 이 동작을 제공해줍니다.

<button type="button">닫기</button>

하지만 div를 버튼처럼 쓰면 이런 기본 동작이 없습니다.

<div
  role="button"
  tabindex="0"
  onclick="closeDialog()"
  onkeydown="if(event.key === 'Enter' || event.key === ' ') closeDialog()"
>
  닫기
</div>

이렇게 보완할 수는 있지만, 원래 버튼이 할 일을 개발자가 대신 다 구현해야 합니다. 그래서 웹접근성은 종종 **"더 많이 구현하는 것"보다 "덜 억지로 만드는 것"**에 가깝다고 느꼈습니다.


스크린리더는 화면의 변화를 자동으로 다 알지 못한다

화면이 동적으로 바뀌는 현대 웹에서는, 시각적으로는 분명한 변화가 스크린리더 사용자에게는 전혀 전달되지 않는 경우가 많습니다.

예를 들어:

  • 저장이 완료되었을 때
  • 필터 결과가 변경되었을 때
  • 모달이 열렸을 때
  • 에러 메시지가 추가되었을 때

이럴 때는 aria-live 같은 속성을 사용해 변경을 알려줄 수 있습니다.

<div aria-live="polite" aria-atomic="true">저장이 완료되었습니다.</div>
  • aria-live="polite": 현재 읽고 있는 내용을 방해하지 않고, 적절한 시점에 읽어줍니다.
  • aria-live="assertive": 지금 읽던 내용을 끊고 즉시 전달합니다.
  • aria-atomic="true": 변경된 부분만이 아니라 전체 문장을 읽게 할 때 유용합니다.

또 텍스트 대신 아이콘만 있는 버튼은 이름을 따로 제공해야 합니다.

<button aria-label="닫기">×</button>

자동 검사 도구는 시작점이지 정답은 아니다

접근성을 점검할 때 보통 다음 도구들을 많이 사용합니다.

  • Lighthouse
  • WAVE
  • axe DevTools

이 도구들은 빠르게 누락된 속성이나 명백한 문제를 찾는 데 도움이 됩니다. 다만 실제 접근성은 자동 검사만으로 끝나지 않습니다. 특히 포커스 이동, 키보드 흐름, 스크린리더에서 들리는 문장, 동적 상태 변경 같은 부분은 직접 사용해보지 않으면 놓치기 쉽습니다.


에디터에서 웹접근성을 적용할 때 달라지는 점

여기까지는 일반 웹에서도 자주 만나는 접근성 이야기입니다. 그런데 에디터는 여기서 한 단계 더 복잡해집니다.

왜냐하면 에디터는 단순히 정보를 보여주는 화면이 아니라, 사용자가 작성하고, 선택하고, 이동하고, 편집하는 인터랙션 중심 UI이기 때문입니다.

제가 작업했던 환경에서도 일반 페이지와는 다른 고민이 있었습니다.

에디터는 "현재 어디에 있는지"를 더 잘 알려줘야 한다

일반 페이지에서는 현재 위치를 페이지 제목이나 섹션 구조 정도로 파악할 수 있습니다. 하지만 에디터는 다릅니다.

사용자는 지금:

  • 몇 번째 문단에 있는지
  • 어떤 텍스트를 선택 중인지
  • 어떤 서식이 적용돼 있는지
  • 표 안에 있는지
  • 리본/툴바와 본문 중 어디에 포커스가 있는지

같은 정보를 필요로 합니다.

즉, 에디터에서는 단순히 "이 버튼은 저장 버튼입니다"를 넘어서 지금 문서 맥락이 무엇인지를 전달하는 것이 중요합니다.

예를 들어 이런 식의 라이브 영역이 필요할 수 있습니다.

<div aria-live="polite" aria-atomic="true" class="sr-only">
  현재 3번째 문단입니다.
</div>

또는 서식 변경 시 이런 식의 안내가 있을 수 있습니다.

<div aria-live="polite" class="sr-only">굵게가 적용되었습니다.</div>

포커스 관리가 훨씬 중요해진다

일반 웹에서는 Tab 순서만 크게 어긋나지 않아도 어느 정도 사용할 수 있습니다. 하지만 에디터는 본문, 툴바, 다이얼로그, 사이드 패널, 컨텍스트 메뉴처럼 포커스가 오갈 곳이 많습니다.

그래서 단순히 "Tab 누르면 다음 요소" 정도로는 부족합니다.

필요했던 건 이런 것들이었습니다.

  • 다이얼로그가 열리면 포커스를 내부에 가두기
  • 다이얼로그가 닫히면 이전 위치로 복구하기
  • 동적으로 생긴 버튼이나 패널도 포커스 순서에 반영하기
  • 툴바와 편집 영역 사이를 자연스럽게 이동시키기

예를 들어 모달에서는 포커스 트랩이 필요합니다.

<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
  <h2 id="dialog-title">글꼴 설정</h2>
  <button>확인</button>
  <button>취소</button>
</div>

여기서 중요한 건 단순히 role="dialog"를 붙이는 것이 아니라, 실제로 Tab이 바깥으로 빠져나가지 않도록 제어하고, 닫혔을 때 원래 작업하던 위치로 되돌려 주는 것입니다.


단축키는 편의 기능이 아니라 핵심 사용 방식이 된다

일반적인 페이지에서는 키보드 접근성이 Tab, Enter, Space 정도에서 끝나는 경우가 많습니다. 하지만 에디터는 다릅니다. 키보드가 단순한 보조 수단이 아니라 주요 입력 수단이 됩니다.

예를 들면 이런 단축키들이 있습니다.

  • Ctrl+B, Ctrl+I, Ctrl+U: 굵게, 기울임, 밑줄
  • Ctrl+L, Ctrl+E, Ctrl+R, Ctrl+J: 정렬
  • Ctrl+Home, Ctrl+End: 문서 처음/끝 이동
  • Shift + 방향키: 선택 범위 확장
  • 표 안에서의 셀 이동
  • 툴바/리본 내부 탐색

예를 들어 굵게 단축키를 처리하는 코드는 이런 식일 수 있습니다.

function onKeyDown(event: KeyboardEvent) {
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'b') {
    event.preventDefault();
    toggleBold();
    announce('굵게가 적용되었습니다.');
  }
}

하지만 실제 제품 수준에서는 여기서 더 고려할 것이 많습니다.

  • Mac/Windows 키 차이
  • 입력 조합 중복
  • IME와의 충돌
  • 읽기 전용 모드 여부
  • 현재 선택 영역 존재 여부

즉, 에디터에서 단축키는 단순 편의 기능이 아니라 접근성과 생산성을 동시에 좌우하는 기본 인터페이스였습니다.


리본, 툴바, 패널도 하나의 인터랙션 영역이다

에디터에서는 본문만 중요한 것이 아닙니다. 서식 버튼, 탭, 그룹, 드롭다운, 토글 버튼이 모여 있는 리본 UI도 접근성 관점에서 잘 설계되어야 합니다.

이런 영역에서는 다음이 중요했습니다.

  • 툴바라는 의미를 전달하기
  • 탭의 선택 상태를 전달하기
  • 토글 버튼의 on/off 상태를 전달하기
  • 키보드로 순서 있게 탐색할 수 있게 하기

예를 들어 다음과 같은 구조가 가능합니다.

<div role="toolbar" aria-label="서식 도구 모음">
  <button aria-pressed="true">굵게</button>
  <button aria-pressed="false">기울임</button>
  <button aria-pressed="false">밑줄</button>
</div>

탭 UI라면 현재 선택 상태를 알려줘야 합니다.

<div role="tablist" aria-label="리본 탭">
  <button role="tab" aria-selected="true">홈</button>
  <button role="tab" aria-selected="false">삽입</button>
  <button role="tab" aria-selected="false">보기</button>
</div>

보이면 다 읽어주면 되는 것은 아니다

에디터에는 시각적으로만 의미가 있는 요소들도 많습니다.

예를 들면:

  • 눈금자
  • 장식용 가이드
  • 인쇄 미리보기용 오버레이
  • 비주얼 보조선
  • 실제 내용은 다른 곳에 있는데 화면에만 보이는 버블

이런 요소를 그대로 접근성 트리에 노출하면 오히려 방해가 됩니다. 그래서 때로는 "보이게 만드는 것"보다 숨겨야 할 것을 숨기는 것이 더 중요했습니다.

<div aria-hidden="true" class="ruler"></div>

접근성은 모든 것을 다 읽게 하는 작업이 아니라, 정말 필요한 정보만 남기고 나머지는 정리하는 작업이기도 했습니다.


마무리

면접에서 a 태그와 onclick의 차이점을 들었을 때, 저는 처음에 기술적인 차이만 떠올렸습니다. 하지만 다시 생각해 보니 그 질문은 결국 브라우저가 이해하는 의미, 사용자가 실제로 얻게 되는 경험, 보조기술이 해석할 수 있는 정보까지 포함한 질문이었습니다.

그리고 에디터를 개발하며 느낀 것도 비슷했습니다.

일반 웹에서는 시맨틱 태그, 키보드 접근, 스크린리더 대응만 잘해도 많은 문제가 해결됩니다. 하지만 에디터는 여기서 더 나아가야 합니다.

  • 현재 편집 맥락을 전달해야 하고
  • 복잡한 포커스 흐름을 관리해야 하고
  • 다양한 단축키와 상태를 일관되게 제공해야 하고
  • 시각적 요소와 실제 의미 있는 요소를 구분해서 노출해야 합니다.

결국 에디터의 웹접근성은 일반 웹접근성의 연장선에 있으면서도, 조금 더 인터랙션 중심적이고, 조금 더 상태 중심적이며, 조금 더 섬세한 문제였습니다.

이번 정리를 하면서 저도 예전에 했던 구현을 다시 다른 관점에서 돌아보게 됐습니다. 앞으로도 기능을 만들 때 "동작하느냐"에서 끝나지 않고, 누구에게 어떻게 사용될 수 있는가까지 함께 보려고 합니다.