배경
회사에서 진행 중인 프로젝트에서 UX 향상을 위해 키보드 사용성 개선 작업을 진행했어요. Input 컴포넌트 하위에 검색 결과가 리스트 형태로 노출되고, 해당 리스트 아이템들을 키보드로 쉽게 탐색하고 선택할 수 있는 UX를 구현하는 것이 목적이었어요.
기본적인 동작은 잘 작동했지만, 키보드 사용성을 강화하면서 새로운 UX적인 불편함이 발생했어요.
바로, 키보드로 포커싱한 리스트 아이템에서 다시 Input으로 포커스를 이동할 때, Input에 값이 있음에도 불구하고 커서가 항상 맨 앞으로 고정된다는 점이었어요.

Pain Point
이 이슈는 단순히 백로그 성격의 문제일 수 있지만, 개인적으로는 중요한 UX 결함이라고 느꼈어요.
사용자가 가장 불편하게 느낄 수 있는 지점은 다음과 같아요:
“타이핑을 잘못해서 다시 수정하려고 커서를 옮겼는데, 예상과 다르게 커서가 맨 앞으로 이동한다면?”
이런 경험은 사용자가 기대하는 UX와 실제 동작이 다르다고 느끼게 만들고, 불필요한 인지적 스트레스를 유발해요 🫠
해결방법
이 문제를 해결하기 위해 리서치를 진행하던 중, 아래와 같은 사실을 알게 되었어요.
input요소에는 전체 선택을 위한select메서드가 있어요.- 커서 위치를 특정 지점으로 이동하거나 범위를 지정해 선택할 수 있는
setSelectionRange메서드도 존재해요. setSelectionRange는 WHATWG forms spec에 따라text,search,url,tel,password타입에서만 사용할 수 있어요.
🔍 WHATWG란?
WHATWG는Web Hypertext Application Technology Working Group의 약자로, 웹 표준을 정의하는 그룹이에요.
웹 표준은 브라우저 간 호환성을 보장하고, 웹 개발자들이 참고할 수 있는 명확한 지침을 제공해요.
키보드 사용성을 챙길때 제작한 React.Ref도 있었고, 기본적으로 Input 컴포넌트에서 onFocus 메서드를 지원해주니 포커스 이벤트 발생 시, 커서를 Input 맨 뒤로 이동시키면 되겠다고 판단했어요.
하지만… 예상대로 동작하지 않았어요 🥲
실패 원인 분석
생각을 조금 더 정리해보니, 실패 원인은 브라우저의 렌더링 타이밍에 있었어요.
focus 이벤트가 발생하고 onFocus 핸들러가 호출되는 시점에는 브라우저가 아직 Input의 포커스 상태나 커서 위치 정보를 완전히 반영하지 않은 상태일 수 있어요.
이로 인해 setSelectionRange를 즉시 호출해도 커서 위치가 예상대로 설정되지 않는 문제가 있었어요.
해결방법: setTimeout
저는 해당 문제를 해결하기 위해 setTimeout을 사용했어요. setTimeout는 콜백함수의 실행시점을 현재 실행중인 코드블록 다음 순서로 실행하기 때문이에요.
상세 이유: setTimeout API는 MacroTaskQueue로 분류되어 MicroTaskQueue에 등록된 핸들러가 먼저 실행되어 렌더링 업데이트가 처리된 후 실행되기 때문이에요. 비슷한 원리의 해결방법으론 requestAnimationFrame가 있어요.
const DELAY_TIME = 0;
const INPUT_END_INDEX = inputRef.current?.value.length ?? 0;
const handleFocusToEnd = () => {
setTimeout(() => {
inputRef.current?.setSelectionRange(INPUT_END_INDEX, INPUT_END_INDEX);
}, DELAY_TIME);
}
setTimeout의 delay를 0초로 두고 실행하게되면 Input에 포커스 되고 값이 반영되는 시점 이후의 이벤트 루프에서 코드를 실행하게 되기때문에 커서 위치를 원하는 대로 이동시킬 수 있어요.

마무리
이번 버그를 해결하면서 단순한 커서 위치 조정 문제를 넘어, 브라우저 렌더링 흐름, 이벤트 루프, WHATWG 명세까지 사고를 확장할 수 있었어요. 마이너한 이슈일 수 있지만 생각보다 얻은 지식이 많아서 좋았어요.
