관심사 태그를 선택하는 페이지에서, 사용자가 직접 태그를 작성해서 추가하는 기능을 구현하였다. '태그 추가' 버튼을 누르면 태그 내용을 작성할 수 있는 input이 등장한다.

문제는 input에 focus가 가지 않는다는 것이다. 사용자가 태그를 작성하려면, '태그 추가' 버튼을 누른 뒤에 한번 더 input을 클릭해야 한다. 또한 input에 커서가 보이지 않는다면 글자를 입력할 수 있는 영역인지 한눈에 알아보기 어려울 것이다. 사소한 부분일 수도 있지만 이런 디테일 하나하나가 사용자가 서비스를 더 오래 사용할 수 있도록 만든다고 생각한다.
자동 focus 기능을 구현하기 전에 고려해야 하는 것이 있다. 바로 접근성이다. 자동 focus 기능은 어떠한 경고 없이 사용자를 바로 해당 input으로 이동시킨다. 그렇기 때문에 스크린 리더를 사용하는 시각 장애인이나 인지 장애인에게 혼란을 야기할 수 있다. 또한 초점을 맞추기 위해 페이지가 스크롤 될 수 있고, 모바일에서는 키보드가 올라올 수 있다.
👇참고한 문서
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus
그렇다면 내가 구현하고자 하는 페이지에서는 어떨까? 내가 자동 focus 기능을 구현하고자 하는 페이지는 아주 짧고 간단한 내용을 담고 있다. 그렇기 때문에 페이지 스크롤은 발생하지 않을 것이다. 또한 내가 자동 focus 기능을 구현하고자 하는 input은 '태그 추가' 버튼을 클릭한 후에 등장한다. 그렇기에 글을 작성할 수 있는 영역이 등장할 것을 충분히 안내했다고 판단했다.
<input autofocus />
한 영역에서는 하나의 요소만 autofocus 속성을 가질 수 있다. 만약 한 영역의 여러 요소에 적용한다면 그 중 첫 번째 요소만 autofocus가 적용된다.
jsx에서는 autoFocus로 작성한다.
컴포넌트가 처음 렌더링 될 때만 적용된다.
👇참고한 코드
아래는 컴포넌트가 처음 렌더링 되고 3초 후 input에 autofocus가 적용 되도록 실험한 코드이다. 3초가 지난 후에도 input에 autofocus가 적용되지 않는 것을 볼 수 있다.
https://codepen.io/ericandrewlewis/pen/PbgwqJ?editors=1111
진행 중인 프로젝트에서는 input을 display="none"과 display="block"을 이용해서 화면에 동적으로 띄우고 있었다. 그렇기 때문에 input의 속성이 display="none"인 상태에서는 아직 마운트 되지 않았을 거라고 생각했다. 하지만 개발자 도구의 Elements 탭을 열어보니,

화면에서는 보이지 않았지만 이미 마운트되어있는 것을 알 수 있었다. 그렇기 때문에 autofocus를 적용해도 input에 focus가 가지 않았던 것이다.
display none을 적용한 요소는 렌더 트리에 존재하지 않지만, 돔 트리에는 존재하기 때문이다. 따라서 해시태그 input이 화면에 나타났을 때 자동으로 focus되지 않는다.
https://medium.com/the-crazy-coder/display-none-and-dom-e049b69164cb
<input ref={input => input && input.focus()}/>
짧고 간단하다. 하지만 단점이 있다. 매 렌더마다 함수가 새로 만들어지기 때문에 리소스 낭비가 발생할 수 있다. 정말 그런지 실험해보자.
ref={input => {
console.log("실행");
input && input.focus();
}}
인라인 함수 안에 console.log를 작성하고 input과 무관하게 렌더링을 발생시켜보았다. 태그 선택!

DOM에 접근해야 할 때, JavaScript에서는 getElementById, querySelector 같은 DOM Selector 함수를 사용한다. 그렇다면 리액트는? 리액트에서는 useRef라는 훅을 제공한다.
const refInput = useRef(null);
const onClick = () => {
refInput.current.focus();
}
return (
<>
<input ref={refInput}/>
<button onClick={onClick}/>
</>
);
👇참고한 문서
https://ko.reactjs.org/docs/hooks-reference.html#useref
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
const HandleShowInput = () => {
setInputDisplay("block");
refInput.current.focus();
};
return (
<>
<Input ref={refInput} display={inputDisplay}/>
<Button onClick={HandleShowInput}/>
</>
)
내가 기대했던 동작은 이렇다.
버튼 클릭 👉 HandleShowInput 함수 실행 👉 state 변경
display: none>display: block👉 input에 focus
하지만 이렇게 동작했다.
버튼 클릭 👉 HandleShowInput 함수 실행 👉 input에 focus 👉 state 변경
display: none>display: block👉 state가 변경되었으므로 리렌더링
그렇기 때문에 결국 화면에는 focus 되지 않은 input이 띄워졌다.
useEffect를 활용하여 focus를 줘보도록 하자!
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
useEffect(() => {
refInput.current.focus();
}, [inputDisplay]);
const HandleShowInput = () => {
setInputDisplay("block");
};
return (
<>
<Input ref={refInput} display={inputDisplay}/>
<Button onClick={HandleShowInput}/>
</>
)
refInput.current.focus()를 useEffect 안으로 이동시키고, 의존성 배열에 inputDisplay를 넣어 input의 display 속성이 바뀔 때마다 focus가 가도록 했다.
