사용자는 읽지 않는다, 판단한다
프론트엔드 개발을 오래 하다 보면 하나의 불편한 진실에 도달합니다. 사용자는 우리가 정성 들여 배치한 텍스트를 '읽지' 않는다는 것. 시선은 이미 자기만의 경로를 갖고 있고, 그 경로 위에 놓인 시각적 신호(signal)가 판단을 결정합니다. 이번에 흥미롭게 본 세 가지 사례는 모두 같은 질문에서 출발합니다. "사용자의 눈 — 혹은 귀 — 은 인터페이스를 어떤 순서로 소비하는가?"
시선이 곧 인풋이 되는 UI
dev.to에 올라온 Eye-Tracking UI 프로젝트는 HTML·CSS·JS만으로 '눈동자가 커서를 대체하는' 인터페이스를 프로토타이핑합니다. 구조 자체는 단순합니다 — .eye 안에 .pupil을 두고, mousemove 이벤트로 벡터를 계산해 transform으로 동공을 움직이는 방식이죠. 그런데 사실 이건 Figma에서 볼 때는 귀여운 데모인데, 실제 구현하면 꽤 까다로운 지점이 많습니다.
첫째, requestAnimationFrame 없이 매 mousemove마다 DOM을 갱신하면 레이아웃 스래싱이 일어납니다. 둘째, 실제 eye-tracker를 연결하면 gaze 데이터의 jitter(떨림)가 심해서 dwell detection — 시선을 일정 시간 고정했을 때 클릭으로 인식하는 로직 — 의 임계값 튜닝이 핵심이 됩니다. 이 글에서 "dwell time을 UI 설정으로 노출하라"고 권고하는 부분은 정확합니다. 사용자 입장에서는 0.3초와 0.8초의 차이가 '의도한 클릭'과 '실수'의 경계거든요.
접근성 관점에서 보면 더 흥미롭습니다. 시선 추적 UI는 운동 장애가 있는 사용자에게는 마우스·키보드의 대안이 될 수 있지만, 동시에 시각장애인에게는 완전히 무의미한 인터페이스이기도 합니다. 결국 '시선 기반 인터랙션'이라는 한 가지 모달리티만으로는 부족하고, 항상 fallback을 설계해야 한다는 걸 이 프로젝트가 역설적으로 보여줍니다.
눈이 아닌 귀로 읽는 인터페이스
그 fallback의 극단적 사례가 Flutter로 만든 시각장애인용 기타 튜너 앱입니다. 기존 기타 튜너는 시각적 게이지에 100% 의존하는데, 전맹 사용자에게 이건 그냥 빈 화면이죠. 이 앱은 이중 청각 피드백 구조로 설계됐습니다. 스크린 리더(TalkBack/VoiceOver)가 "6번줄 20Hz 낮습니다"라고 상태를 안내하고, 동시에 비프음의 간격이 목표 주파수에 가까워질수록 좁아지도록 만들었습니다.
개발자 관점에서 주목할 원칙이 세 가지 있습니다.
- 포커스 임의 이동 금지 —
Semantics의OrdinalSortKey로 논리적 순서만 제어하고, 포커스를 코드에서 강제로 옮기지 않았습니다. 이거, 웹에서tabindex="-1"로 포커스를 프로그래밍적으로 잡아끄는 패턴이 얼마나 스크린 리더 사용자를 혼란스럽게 하는지 아시는 분은 공감하실 겁니다. - 동적 위젯 최소화 —
BottomSheet,ExpansionTile같은 열리고 닫히는 컴포넌트를 의도적으로 배제했습니다. 시각 중심 UI에서는 "있으면 좋은" 패턴이지만, 스크린 리더에서는 매번 DOM 트리가 바뀌면서 읽기 위치를 잃어버리는 원인이 됩니다. - TTS 대신 liveRegion 사용 — 직접 음성을 재생하는 대신 OS 레벨의 스크린 리더에 위임함으로써, 사용자가 이미 익숙한 속도·음성 설정을 존중합니다.
Android에서 비프음과 마이크 입력이 충돌해 고음 인식이 안 되는 문제를 FFT 필터링으로 해결한 부분도 인상적입니다. 사실 이건 px 단위의 문제는 아니지만, "기기마다 다르게 동작하는 것"을 코드로 메워야 하는 프론트엔드 개발자의 숙명이 플랫폼만 바뀐 것이죠.
히어로 섹션 이후의 3초, '신뢰'의 설계
시선 추적이든 청각 피드백이든, 결국 사용자가 인터페이스에 '머무르는가'의 문제로 귀결됩니다. dev.to의 히어로 섹션 이후 사용자 판단 글은 이 지점을 정면으로 다룹니다. 핵심 주장은 간단합니다. 히어로 섹션은 스크롤 허가를 주고, 그 다음 섹션은 체류 허가를 준다.
저자는 자신의 사이트에서 히어로 다음에 "My vision, My idea"를 장황하게 설명하는 섹션을 배치했다가, 사용자가 이탈하는 걸 확인하고 이를 한 줄로 교체했습니다.
"Free browser-based tools for quick daily tasks. No login. No limits. No friction."
이 변경이 효과적이었던 이유를 프론트엔드 관점으로 번역하면 이렇습니다. 사용자의 시선은 스크롤 직후 스캐닝 모드에 있고, 이때 필요한 것은 '읽을 거리'가 아니라 패턴 매칭에 성공하는 짧은 시각적 단서입니다. 이걸 우리가 컴포넌트로 구현할 때 의미하는 바는 명확합니다. 히어로 직후 섹션의 font-size, line-height, max-width — 이 세 가지가 '읽기'가 아닌 '스캔'에 최적화되어 있는지 1px 단위로 확인해야 한다는 겁니다.
세 가지 사례가 가리키는 하나의 원칙
시선 추적 UI는 "눈이 어디를 보는지"를, 청각 피드백 튜너는 "눈을 쓸 수 없을 때 어떻게 인지하는지"를, 히어로 이후 판단은 "인지한 뒤 무엇을 결정하는지"를 다룹니다. 세 주제를 관통하는 설계 원칙은 결국 하나입니다.
인터페이스의 신뢰는 '사용자가 다음에 무슨 일이 일어날지 예측할 수 있느냐'에서 온다.
시선 UI에서 dwell time이 일관되지 않으면 사용자는 "내가 의도한 건지 실수인지" 불안해합니다. 스크린 리더에서 포커스가 임의로 뛰면 사용자는 현재 위치를 잃어버립니다. 히어로 이후에 모호한 문장이 나오면 사용자는 "여기가 맞나?"를 의심합니다. 전부 예측 가능성의 실패이고, 전부 사용자가 떠나는 이유입니다.
내일의 구현을 위한 체크리스트
이 세 사례에서 바로 가져갈 수 있는 것들을 정리하면:
- 인터랙션 fallback을 항상 설계할 것 — gaze가 안 되면 mouse, mouse가 안 되면 keyboard, keyboard가 안 되면 screen reader. CSS의
@media (prefers-reduced-motion)처럼, 입력 모달리티에도 progressive enhancement가 필요합니다. - 동적 위젯 도입 전에 접근성 비용을 계산할 것 — Accordion, Bottom Sheet, Tooltip이 추가될 때마다
aria-expanded,aria-live, 포커스 트랩 구현 공수가 따라옵니다. 정말 필요한 인터랙션인지 기획 단계에서 한 번 더 물어봐야 합니다. - 히어로 다음 섹션은 '설득'이 아니라 '확인' — 컴포넌트 설계 시 이 영역의 정보 밀도를 의도적으로 낮추고, 시각적 위계(visual hierarchy)에서 '스캔 가능한 구조'를 최우선으로 놓아야 합니다.
결국 우리가 만드는 건 화면이 아니라 사용자의 인지 흐름 위에 놓이는 경험입니다. 시선이 닿든, 귀가 듣든, 손가락이 스크롤하든 — 그 흐름이 끊기는 순간 사용자는 조용히 떠납니다. 그리고 그 이탈은 Lighthouse 점수에도, 히트맵에도, 전환율에도 찍힙니다. 1px의 어긋남보다 더 무서운 건, 사용자 인지 경로 위의 0.3초 공백이라는 걸 이 세 편의 글이 다시 한번 상기시켜 줍니다.