당신의 앱은 공평합니까 ?

Antoliny Lee·2026년 9월 7일

프로그래밍

목록 보기
4/4
post-thumbnail

우리는 설명하지 않아도 알 수 있다.

다르게 색칠되어있는 글씨와 맥락을 통해 "아 저건 링크겠구나" 라고 인지한다.
결제 화면에서 네 칸으로 나뉜 입력란을 보고 "아 저 네개의 입력이 하나의 카드 번호를 의미하겠구나" 라고 생각한다.
브라우저 상단 툴바에 있는 '<-' 버튼을 보고 "아 이전 페이지로 이동하는 버튼이겠구나" 라고 판단한다.

우리는 별도의 설명 없이도 화면에 존재하는 요소들의 의미를 이해할 수 있다.
하지만 과연 저시력자나 색각 이상자들은 별도의 설명없이 저 요소들을 이해할 수 있을까?

우리에게 "보인다는 이유만으로 느껴지는 특징" 들은 또 다른 누군가에겐 공백으로 받아들여진다.
누군가는 이런 의문을 가질 수 있을거 같다.

"두 대상을 비교하기에는 애초에 출발선이 다른거 아닌가요 ?"

안타깝게도, 이 말을 부정하기는 어려울거 같다.
세상은 불공평하다.
하지만 우리가 만드는 앱 안에서는, 그 불공평함을 조금이라도 줄여 나갈 수 있다.
공평한 세상을 만들어 나가는데 일조 할 수 있다는건, 프론트엔드 엔지니어가 가진 핵심적인 매력중 하나이지 않을까?


공평한 앱 만들기


필자는 '접근성' 이라는 개념을 django라는 오픈소스에 기여하면서 처음 알게되었다.
당시에 첫 '접근성' 기여를 했을때 다른 기여보다 더 큰 의미로 느껴졌다.

"내가 한 작업으로 인해 다른 누군가가 더 나은 경험을 할 수 있겠구나"

여기서 말하는 다른 누군가라는 대상이 명확했다.
"앱을 이용하는데 어려움이 있는 사람들"

어려운 누군가를 돕는건 일반적인 사람들을 대상으로 하는것보다 더욱 더 의미있고 보람찼다.
이 의미는 나를 django개발자에서 프론트엔드 엔지니어라는 직군으로 이끌게 만든 가장 큰 이유였다.
어쩌다 보니 운이 좋게 django에서 많은 접근성 개선들을 이뤄냈고 django 접근성 팀까지 합류하게 되었다.

내가 느꼈던 보람들을 다른 누군가도 느끼면서 더 공평한 앱을 만들어 나갔으면 좋겠다.
그렇기에 필자의 짧지만 흥미로운 경험 속에서 피어난 여러 접근성과 관련된 생각들을 공유해 보려고 한다.


더 명확하게 만들기


위 사진을 봤을때 어떤 요소가 링크인지 파악되는가 ?
우리는 간단하게 배경과 크게 다른 색상을 가진 텍스트 요소에 눈이 가게되고 해당 요소를 자연스럽게 "링크" 라고 예상하게 된다.
즉, 이 앱은 색상 만으로 많은 텍스트 중에 "링크" 를 구별한 것이다.
아주 흔한 경우다, 이렇게 보통 앱에서 "색상" 만으로 요소에 의미를 담는 경우가 존재한다.

이번에는 어떤가 ?
링크라는 의미를 담은 "청록색" 텍스트가 살짝 옅어지지 않았는가 ?
실제로 위 사진은 "녹색맹" 을 가진 색각 이상자의 시선에서 보는 동일한 앱의 모습이다.
슬슬, 색상만으로는 "링크" 라는 요소를 인지하기 어렵지 않은가 ?
아마 이 예시를 보게되면 더욱 더 공감하게 될것이다.
우리가 지금 보고있는 Velog는 링크를 "초록색 텍스트" 로 구별했다.
그렇다면 "녹색맹 을 가진 사람의 시선에서 Velog는 어떨까 ?"

"녹색맹" 을 가진 사람의 시선에서 Velog는 위와 같다.
이렇게 색상만으로 요소의 의미를 담아 구별할때는 주의가 필요하다.
왜냐하면 그 구별이 누군가에게는 유효하지 않기 때문이다.
그럼 어떻게 해야할까 ?

위 es-toolkit이라는 라이브러리 문서는, 링크를 "초록색 텍스트" 와 "밑줄" 로 표시한걸 알 수 있다.
Velog와 동일하게 "초록색" 이라는 색상에 요소의 의미를 담았지만 "밑줄" 이라는 포인트를 더 두었다.
그렇기에 "녹색맹" 을 가진 색각이상자의 시선에서도 링크를 명확히 구분할 수 있다.
또한 "녹색맹" 이 없는 우리의 시선에서도 이제는 "링크" 라는 요소가 더 명확하게 느껴지지 않는가 ?

보통 우리는 원래 밑줄이 있는 텍스트는 "링크" 라고 인지한다.
하지만 보통의 앱에서 밑줄이 디자인적으로 시각적인 잡음으로 느껴지는 경우가 많아 "색상" 만으로 링크를 구별하게 된다.
이 방식은 공평한 앱을 만들어 나가는 관점에서는 좋지 않은 선택이다.
여기서 핵심은 "색상" 만으로 요소에 의미를 담았다는 점이다.
하지만 앞에서도 말했듯이 이 구별점은 누군가에게는 무의미하다.
그렇기에 우리는 어떤 요소에 시각적인 포인트로 의미를 담게 된다면 "누구나 다 명확하게 인지할 수 있는 포인트" 를 하나 두어야한다.

물론 이것또한 트레이드오프가 존재한다.
"누구나 다 명확하게 인지할 수 있는 포인트" 를 두게 되면 보통 해당 요소가 더 특색있게 된다.
그 특색은 또 다른 누군가의 시선에서 앱에 애정을 떨어트리게 만드는 노이즈 적인 요소가 될 수 있다.
그렇기에 모든 요소를 특색있게 표현한다기 보다, 적절히 "특색" 이 필요한 부분에만 적용해야 한다.
또 텍스트의 의미가 누가봐도 링크라고 느껴지는 경우에는 제외하는 식으로 근거있는 선택을 통해 제외하거나 적용해야 한다.

실제로 django에서도 링크라는 이유만으로 모든 요소에 밑줄을 주지 않았다.
위와 같이 왼쪽 사이드바에 존재하는 텍스트들은 링크지만 "색상" 만으로 표현되어있다.
이유는 이미 해당 영역(네비게이션 사이드바)이 가진 의미만으로 내부 텍스트들을 링크로 추론할 수 있기 때문이다.


시각적인 특성을 음성으로도 설명하기


앱을 사용하다보면 특정 "형태" 만으로 해당 기능을 유추할 수 있을때가 많다.

이 앱에서 동그라미로 표시되어 있는 요소들은 "형태" 만으로 의미를 담은 요소다.
우리는 자연스럽게 해당 요소를 클릭하면 어떤 페이지가 열릴지 예상할 수 있지 않은가 ?

아마도 "왼쪽 괄호" 는 "상위 페이지로 이동하겠구나" 라고 예상할것이고 "종" 은 "알림 페이지로 이동하겠구나" 라는 예상을 할것이다.

이렇게 앱에서는 특정 요소들에 어떠한 시각적인 설명을 주지 않아도 "형태" 만으로 사용자가 자연스럽게 구별할 수 있는 요소들이 존재한다.

그렇다면 저시력자들처럼 아예 화면이 보이지 않거나 선명하지 않게 보이는 사람들 또한 이 "시각적인 형태" 만으로 요소를 구별할 수 있을까 ?

안타깝게도 저시력자들은 구별할 수 없을것이다.
어쩌면 당연한 사실이다.
"시각적인 형태", 즉 요소의 의미를 알려면 시각적으로 명확하게 보여야 한다는걸 의미한다.

이렇게 시각적인 이용이 어려운 분들을 위해 "스크린 리더기" 라는게 존재한다.
스크린 리더기는 의미그대로 화면의 요소들을 음성으로 제공한다.

그렇기 때문에 "시각적인 형태" 로 특정 요소에 의미를 담았다면 그 요소의 "특징" 을 우리는 "음성" 으로도 제공해야한다.
실제로 스크린리더로 "상위 페이지 이동" 이라는 의미를 담은 "왼쪽 괄호" 요소에 접근해 보자.

위와 같이 왼쪽 버전은 "상위 페이지로 이동" 이라는 메시지가 스크린리더에서 음성으로 제공되지만 오른쪽 버전은 "상위 페이지로 이동" 에 대한 어떠한 메시지도 존재하지 않는다.
만약 저시력자와 같은 스크린리더 사용자가 오른쪽 앱을 사용하게 된다면 "해당 요소가 어떤 역할을 하는지" 아예 인지하지 못할것이다.
즉, 공평하지 못한 앱인것이다.
이 차이는 단순히 해당 이미지 태그에 "alt" 속성으로 "상위 페이지로 이동" 이라는 값을 추가했을 뿐이다.
이렇게 우리는 시각적으로만 구분 가능한 "형태" 로 특정 요소를 구분했다면, 스크린리더 사용자또한 해당 요소를 구별할 수 있도록 "형태에 담긴 의미" 를 음성으로도 제공할 수 있도록 해야한다.

놀랍게도 이 앱에 "형태" 가 아닌 "색상" 으로 요소에 특정한 의미를 담은 요소또한 존재한다.
여기서 "색상" 또한 시각적인 요소에서 주는 차이점이라는걸 인지해야한다.
즉, 시각적인 요소에서 준 차이를 시각적으로 이용하지 못하는 스크린리더 사용자 또한 이해할 수 있도록 해야하지 않을까?

우리는 하단 탭에서 색상이 다른 요소만 봐도 "내가 홈 탭에 있구나" 라는걸 인지한다.
하지만 이 요소또한 "색상", "시각적인" 특징으로 요소에 의미를 담은것이다.
과연 스크린리더 사용자는 해당 요소에 접근했을때 시각적인 사용자 처럼 "내가 홈 탭에 있구나" 라는걸 인지할 수 있을까 ?

왼쪽 앱과 오른쪽 앱의 차이는 "current page" 라는 설명이 스크린 리더에서 제공되냐 안되냐의 차이이다.
왼쪽이나 오른쪽이나 홈 요소에 접근했을때 해당 요소가 탐색하는 블럭에 존재하며 "홈 이라는 곳으로 이동하는 링크" 라는걸 알 것이다.
하지만 "색상" 이 준 의미인 "현재 위치" 라는 의미는 왼쪽 앱에서만 알 수 있는것이다.
그렇기에 왼쪽 앱은 스크린리더 사용자 또한 "내가 홈 탭에 있구나" 라는걸 인지할 수 있다.

이 둘의 차이를 만들어낸건 aria-current 라는 속성이다.
여기서 aria 속성은 요소의 이름이나 상태 같은 정보를 스크린 리더 등 보조 기술에 전달한다.
즉, aria-current, 정확히 aria-current="page"는 네비게이션 안에 존재하는 여러 링크 중 현재 페이지를 가리키는 링크가 무엇인지 알려준다.
그렇기에 위와같이 스크린리더에서 차이를 만들어낸 것이다.

결국 핵심은 "시각적인 특징으로 요소에 의미를 담은" 경우에 해당 의미가 "시각장애인들, 스크린 리더 사용자들에게 까지도 전달되어야 한다" 는 것이다.
그러기 위해서 우리는 적절한 aria속성, role 속성, 시맨틱한 태그를 사용해야한다.


트레이드오프를 고려하기


앱을 만들다 보면 "다른 페이지로 이동하는 버튼" 을 만들어야 하는 경우가 많다.
여기서 핵심은 "다른 페이지로 이동" 이라는 점이다.
즉, "버튼" 이 아닌 "링크" 로 구별해야한다.
하지만 보통 기능은 "링크" 지만 생긴건 "버튼" 인 요소가 정말 많다.
여기서 우리는 이 요소를 "링크" 로 만들어야 할까? 아니면 "버튼" 으로 만들어야할까 고민하게 된다.

생각해보면 "링크" 로 만드나 "버튼" 으로 만드나 똑같은거 아닐까 ?
"링크" 로 만들경우 "버튼" 의 스타일을 주면 된다.
"버튼" 으로 만들경우 클릭했을때 "링크" 처럼 동작하게 하면 된다.

과연 정말 둘은 똑같을까 ?

"스터디 만들기" 버튼은 "스터디를 만드는 페이지로 이동시키는 역할을 한다"
이 요소를 버튼으로 만들었을때 스크린리더로 접근해 보았다.

스크린리더 사용자는 "스터디 만들기, button" 이라는 설명만으로 해당 요소를 명확히 이해할 수 있을까 ?
이 버튼은 "스터디 만들기 페이지로 이동" 하는 버튼인건가 ? 아니면 실제로 "스터디를 생성하는" 버튼인건가 ?
아마도 이 설명만으로는 이 버튼이 도대체 어떤 역할을 하는지 명확히 이해하지 못할것이다.
그리고 "버튼" 이라는 요소가 가진 기본적인 의미를 고려하면 사용자는 "스터디를 생성하는 버튼이겠구나 ~" 라고 인지할 가능성이 높다.
해당 버튼은 "스터디를 만드는 페이지로 이동시키는 역할" 을 하지만 스크린리더 사용자 입장에서는 해당 요소가 링크인지 전혀 인지하지 못한다.
즉, 공평하지 않은 앱인 것이다.

이 버튼을 개선해보자.
버튼태그를 유지하고 role="link" 속성을 주면 어떨까 ?
이번에는 스크린리더에서 해당 버튼이 링크라는것에 대한 설명이 제공되지 않을까 ?

role=link 속성을 추가했더니 이제는 "링크" 라는 설명이 제공된다.
이제 스크린리더 사용자도 해당 요소에 이동했을때 "스터디를 만드는 페이지로 이동하는 링크겠구나!" 라고 인지할 수 있다.
이 상황만 봐서는 "그래 꼭 시맨틱하지는 않더라도 role 속성을 주면 해결할 수 있구나 !" 라고 생각할 수 있다.
하지만 그렇지 않다.
만약 사용자가 "새 탭으로 열기" 를 시도한 상황이라고 가정해보자.

"스터디 만들기 버튼" 은 "새 탭으로 열기" 단축키를 사용해도 예상과 달리 새 탭이 열리지 않고 현재 탭에서 이동한다.
반대로 "링크 태그로 만든 스터디 참여하기" 는 "새 탭으로 열기" 기능이 잘 동작하는걸 확인할 수 있다.

이외에도 해당 요소에 우클릭했을때 나타나는 메뉴에서도 차이점이 존재한다.

이렇게 꼭 role 속성을 준다고 할지라도 완전히 "특정한 태그" 처럼 동작하는게 아니다.
role은 요소의 역할을 보조 기술에 전달하지만, 해당 HTML 요소의 기본 동작까지 만들어주지는 않는다.

이외에도 링크 태그를 사용한다면 별다른 구현없이 href속성에 값만 추가하면 될것이다.
하지만 버튼 태그를 사용한다면 다른 페이지로 이동하는 동작을 직접 구현해야한다.

이것만 봐서는 "당연히 처음부터 역할에 맞는 태그를 사용해야 하는거 아니야 ?" 라고 생각할 수 있다.
하지만 생각보다 역할에 맞지 않은 태그를 사용하고자 하는 유혹은 많다.
예를 들면 이미 추상화한 버튼 태그가 존재하고 해당 컴포넌트를 사용하면 깔끔하게 재사용할 수 있는 상황인 것이다.
그리고 가끔은 그 상황에 따라 유혹이 오히려 올바른 방향일 수 있다.
즉, 언제나 시맨틱한 태그를 사용해야 하는게 좋을 수도 있지만 상황상 그렇지 못할 때도 있다.

결국 이것또한 트레이드오프가 아닐까 ?

트레이드오프를 판단하려면 무엇을 얻고 무엇을 잃는지 알아야 한다.
문제는 접근성에서 잃는 것은 개발자의 눈에 쉽게 드러나지 않는다.
내가 구현의 편의를 위해 포기한 동작이, 누군가에게는 당연히 사용할 수 있어야 하는 기능일 수 있다.
즉, 판단 이후에 우리가 생각하지 못한 무언가가 언제나 존재할 가능성이 높다.
그렇게 우리가 미처 알아차리지 못한 선택의 비용은 앱의 공평함을 해치고, 누군가에게 큰 장벽을 만들어 낸다.


탐색하기 쉽게 만들기


이전 예제에서 또 다르게 문제를 해결하는 방법이 있다.
버튼태그를 링크로 감싸면 되는거 아닐까 ?
<a> 태그 내부에 <button> 태그를 두는 식이다.

이동은 <a> 태그로 하고 재사용해야할 디자인은 그대로 <button>을 사용하면 되지 않을까 ?

놀랍게도 이전에 고민했던 모든 문제가 해결된 것처럼 보인다.
스크린리더에서도 링크로 읽히며 새 탭으로 열기나 우클릭 메뉴처럼 링크태그가 가진 기본적인 브라우저 동작도 가능해졌다.

하지만 만약 스크린리더 사용자가 버튼만 찾아 탐색하는 기능을 사용한다면 어떻게 될까 ?

여전히 해당 요소는 버튼태그로 접근할 수 있다.
즉, <a> 태그 하위에 <button>을 두었기 때문이다.
그렇기에 여전히 스크린리더 사용자는 해당 링크를 버튼으로 인식할 수 있다.
HTML에서는 <a> 내부에 <button> 같은 상호작용 요소를 넣는 것을 허용하지 않는다.
만약 두게 된다면 스크린리더 관점에서는 위와같은 문제점을 마주하게 되는것이다.

사실 이 방식은 여러가지 문제점이 발생할 여지가 많지만 접근성 관점에서 문제가 한 가지 더 있다.

마우스를 쓰기 어려운 사람이 키보드만으로 해당 요소에 접근한다고 가정해보자.
해당 사용자들은 탭 이동을 통해 상호작용 해야할 요소에 도달한다.

위와 같이 실제로 탭 이동을 했을때 "스터디 만들기" 버튼은 두 번 이동되는걸 확인할 수 있다.
눈에는 하나로 보이는 동일한 요소지만 탭 이동시 두 번을 거쳐야 한다는 뜻이다.
왜냐하면 탭이동은 포커스 가능 요소나 tabindex 속성에 의해 결정된다.
해당 요소는 <a> 내부에 <button> 이 존재하기 때문에 포커스 가능요소가 두 개인 것이다.

이런 구조가 여러 번 반복된다면 어떨까?
사용자는 원하는 기능에 도달하기까지, 하나처럼 보이는 요소마다 두 번씩 포커스를 거쳐야 한다.
즉, 불필요한 탐색 과정을 더하게 된것이다.

여기서 중요한건 "의미" 뿐만이 아닌 "탐색" 또한 고려해야 한다는 점이다.

"어떻게 하면 탐색하기 쉬운 앱을 만들 수 있을까 ?"

tabindex와 같은 방법도 있지만 가장 핵심적인건 "시맨틱 태그" 이다.
시맨틱 태그를 사용하는게 설명뿐만이 아닌 탐색에서까지 도대체 어떤 장점이 있길래 그러는걸까 ?

스크린리더에서는 "네비게이션 이동", "버튼 이동", "링크 이동", "테이블 이동", "헤딩 이동" 등등 특정 역할이나 구조를 기준으로 이동하는 기능을 제공한다.

위와 같이 스크린리더에 있는 "로터 메뉴" 기능을 사용하여 요소를 탐색해 보았다.
랜드마크, 헤딩, 링크 등등 현재 페이지에 있는 특정 요소를 탐색하기 쉽게 만든다.
이것외에도 스크린리더에 "퀵 네비게이션" 이라는 기능이 있다.
해당 기능또한 단축키를 통해 네비게이션, 버튼 처럼 특정 역할이나 구조를 기준으로 요소를 탐색할 수 있다.

결국 요소를 탐색하기 쉽게 만들어주는 기능에서 요소들을 특정 집합으로 구분하는건 "각 요소의 역할과 의미" 다.
시맨틱 태그는 이러한 역할과 의미를 스크린리더에 전달하는 기본적인 방법이다.
또 시맨틱 태그를 사용하기 어려운 상황에서는 role 속성으로 해당 요소의 역할을 명시적으로 전달할 수도 있다.

한 가지 재밌는 예시가 있다.

django admin은 얼마전까지 위와 같이 픽커가 열렸을때 해당 요소에 접근하기 위해서는 모든 요소를 거쳐야만 했다.
지금 이야기하고 있는 "탐색" 과 관련해서 정말 좋지 않은 예시이다.
하지만 이러한 문제는 실제로 스크린리더를 실행하고 픽커를 사용해봐야 알 수 있다.
놀랍게도 이 문제는 15년동안 지속되어왔다는 점이다.
혹시나 내 앱도 이렇게 되어있는건 아닐까 ?


당신의 앱은 공평합니까 ?


세상에는 정말 다양한 접근성 문제가 존재한다.
글자와 배경의 색대비가 부족하면 저시력 사용자는 내용을 읽기 어렵다.
상호작용 요소의 클릭 영역이 너무 작으면 손 떨림이 있는 사용자는 원하는 요소를 정확히 누르기 어렵다.

하지만 막상 앱을 만들다 보면, 이런 문제를 해결하는 일에 얼마나 우선순위를 두어야 할지 고민하게 된다.

"접근성 문제는 후순위인거 아닌가요 ?"
사실 이 질문에 나는 근거있는 반박을 하지 못했다.
어쩌면 나 또한 자연스럽게 접근성 개선을 후순위로 두게 된다.
아무래도 기능구현이 우선순위고 일단은 동작해야하기 때문이다.

과거에는 이 사실에 "공평한 앱을 만들어 나가는 과정을 후순위로 둔다는게 과연 공평한걸까 ?" 라는 생각을 많이 했었다.

이 생각으로 부터 해방되기 위해서는 "애초에 세상은 공평하지 않다" 라는 사실을 받아들여야 했다.
대신 "그 공평하지 않은 세상을 조금이나마 더 공평하게 만들어 나가는것" 을 프론트엔드 엔지니어인 나의 책임이라고 여겼다.

접근성 개선의 우선순위는 상황에 따라 달라질 수 있다.
하지만 어떤 상황이더라도 확실한 한 가지가 있다.
우리는 화면에 보이는 무언가를 만들어 나갈 때마다 "의심" 하고 "점검" 해야 한다.

"나의 앱은 공평합니까 ?"

0개의 댓글