0349

FE

[SvelteKit] 프론트엔드 UX 개선의 디테일: tabindex="-1"와 .blur() 활용법

| 서론

훌륭한 사용자 경험(UX)은 종종 눈에 잘 띄지 않는 작은 디테일에서 시작됩니다. 특히 사용자가 키보드만으로 서비스를 원활하게 이용할 수 있도록 하는 '키보드 네비게이션'과, 현재 활성화된 요소를 명확히 보여주는 '포커스(Focus) 관리'는 매우 중요한 부분입니다.

최근 're-log' 프로젝트의 회고 작성 페이지를 개선하면서, 바로 이 포커스 관리를 통해 사용자 경험을 한 단계 끌어올릴 수 있었는데요. 이번 포스팅에서는 다소 생소할 수 있는 tabindex="-1" 속성과 .blur() 메서드를 어떻게 활용했는지 공유해 보겠습니다.

| 문제 상황: 원치 않는 포커스 이동과 잔상

회고 작성 페이지에는 여러 입력 섹션이 있고, 각 섹션 사이를 쉽게 이동할 수 있도록 "이전", "다음" 네비게이션 버튼을 추가했습니다. 하지만 이로 인해 두 가지 불편한 점이 발생했습니다.

  1. 키보드 함정 (Tab Trap): 사용자가 회고 내용을 입력하다가 Tab 키를 눌러 다음 입력창으로 이동하려고 할 때, 의도와 다르게 네비게이션 버튼(이전/다음)으로 포커스가 이동했습니다. 이 버튼들은 주로 마우스나 터치 사용자를 위한 보조 기능인데, 키보드 사용자의 주된 작업 흐름(폼 작성)을 방해하는 '함정'이 된 것입니다.

  2. 끈적한 포커스 (Sticky Focus): 사용자가 마우스로 "다음" 버튼을 클릭하면, 화면은 다음 섹션으로 스크롤되지만 포커스는 여전히 방금 클릭한 버튼에 남아 파란색 테두리(focus-ring)가 계속 표시되었습니다. 사용자의 시선과 작업 흐름은 이미 다음 섹션으로 넘어갔는데, 불필요한 포커스 잔상이 시각적인 노이즈를 만들었습니다.

이 두 가지 문제를 해결하기 위해 tabindex="-1"와 .blur()라는 간단하지만 강력한 해결책을 적용했습니다.

| 해결책 1: tabindex="-1"로 탭 순서에서 제외하기

첫 번째 '키보드 함정' 문제를 해결하기 위해 tabindex="-1" 속성을 사용했습니다.

1) tabindex란?

tabindex는 HTML 요소가 키보드 포커스를 받을 수 있는지, 그리고 탭 순서에서 몇 번째인지를 결정하는 속성입니다.

  • tabindex="0": 요소를 자연스러운 탭 순서에 포함시킵니다.

  • tabindex="1" (이상): 숫자 순서대로 강제적인 탭 순서를 부여합니다. (접근성 측면에서 권장되지 않음)

  • tabindex="-1": 요소를 탭 순서에서는 제외하지만, JavaScript(.focus())를 통해서는 포커스를 받을 수 있도록 합니다.

즉, tabindex="-1"는 "키보드(Tab)로는 접근할 수 없지만, 코드를 통해서는 제어할 수 있는" 상태를 만들어 줍니다.

2) 적용 결과

네비게이션 버튼에 이 속성을 추가함으로써, Tab 키를 이용한 순차적인 키보드 네비게이션 흐름에서 이 버튼들을 제외시켰습니다.

Svelte

HTML
<button
    tabindex="-1"
    class="navigation-button"
    on:click|preventDefault={ ... }
>
    <ArrowBigLeft size="18" />
</button>

이제 사용자는 Tab 키를 눌렀을 때 입력창 사이를 자유롭게 이동할 수 있으며, 네비게이션 버튼이 더 이상 작업 흐름을 방해하지 않습니다.

| 해결책 2: .blur()로 클릭 후 포커스 제거하기

두 번째 '끈적한 포커스' 문제는 .blur() 메서드로 해결했습니다.

1) .blur()란?

.blur()는 특정 HTML 요소에 적용된 포커스를 프로그래밍 방식으로 제거하는 JavaScript 메서드입니다. .focus()의 정반대 역할을 합니다.

2) 적용 결과

Svelte의 on:click 이벤트 핸들러에서, 섹션 이동 로직(scrollToSection)이 실행된 직후 클릭된 버튼 스스로의 포커스를 제거하도록 .blur()를 호출했습니다.

Svelte

JavaScript
<button
    tabindex="-1"
    class="navigation-button"
    on:click|preventDefault={(e) => {
        scrollToSection(nextTitle);
        
        // e.currentTarget은 현재 이벤트를 수신한 요소를 가리킵니다.
        (e.currentTarget as HTMLButtonElement).blur();
    }}
>
    <ArrowBigRight size="18" />
</button>

이제 버튼을 클릭하면 다음 섹션으로 부드럽게 이동함과 동시에, 버튼에 남아있던 포커스 링이 깔끔하게 사라집니다. 사용자는 시각적인 방해 없이 새로운 섹션의 내용에만 집중할 수 있습니다.

| 결론

  • tabindex="-1": 키보드 사용자의 주된 동선을 방해하지 않도록 보조적인 UI(버튼 등)를 탭 순서에서 제외할 때 사용합니다.

  • .blur(): 상호작용이 끝난 요소(주로 클릭된 버튼)의 포커스를 명시적으로 제거하여 시각적 노이즈를 줄일 때 사용합니다.

이처럼 tabindex 속성과 .blur() 메서드는 웹 접근성과 사용자 경험의 미세한 부분을 조율할 수 있는 강력한 도구입니다.

여러분의 프로젝트에서도 사용자와 상호작용하는 UI 요소들의 포커스 상태를 한번 점검해 보시면 어떨까요? 작은 디테일이 서비스의 완성도를 크게 높일 수 있습니다.