본문 바로가기
source-code/FrontEnd

next input auto focus를 통한 UX 개선

by mattew4483 2023. 8. 17.
728x90
반응형

input UX

현재 반려동물 등록 페이지는 여러 개의 input이 row하게 배치된 형태다.
샵 관리, 특히 애견 미용샵 관리의 중심은 고객(반려동물)이기 때문에,
처음 앱을 시작한 사람이 최적의 UX를 경험하는 것이 무엇보다 중요했다.

덕분에 다른 앱들의 input page를 경험할 때마다 해당 UX를 고민하게 되었는데...
입력해야하는 input들이 많은데도 불구하고,
우리 앱보다 더 쉽게 정보를 입력한다는 느낌을 받았다.
왜지? → input의 onSubmit이 동작했을 때(한 input에서 완료or다음 키패드를 눌렀을 때)
다음 input으로 focus가 이동하는 기능이 우리 앱에는 없었다!!!

즉 기존 반려동물 등록 페이지에서는
하나의 input을 완료하고 → 제출 키패드 or 빈 공간을 눌러 keyboard를 hide시키고 → 다시 다음 input을 눌러 정보를 입력해야했는데...
이것이 적지 않은 불편함(적어도 크게 편하지는 않은)을 제공하고 있었다.

input focus auto change

반려동물 등록을 최대한 쉽고 직관적으로 만들기 위해,
해당 페이지에서 곧바로 입력할 수 있는 정보는 '이름'과 '전화번호'였다.
(다른 정보는 Collapse 형태로 숨겨져 있다)

따라서 '이름' input에서 '다음' 키패드를 눌러,
'전화번호' input으로 이동하는 기능을 구현하고자 했다.

 

returnKeyType
우선 keyboard의 제출 버튼을 '다음'과 '완료'로 변경!
얼마 전에 배웠던 걸 잘 써먹을 수 있었다. 후후.
https://velog.io/@mattew4483/React-Native-TextInput

여기서 또 배웠던 점!
keyboardType이 numeric, 즉 숫자 패드 키보드 일 때
returnKeyType을 지정하지 않으면 아무 버튼도 생기지 않지만,
returnKeyType={'done'}을 설정하게 되면...


우리가 흔히 접하는 '완료' 버튼이 키보드 상단에 위치하게 된다.
즉 빈 배경 클릭으로만 keyboard hide가 가능한 불편한 경험을 방지할 수 있는 것!
(앱에 존재하는 모든 numeric keyboard에 적용하리라 마음먹었다)

 

input focus
input의 focus를 제어한다는 말은
= 특정 DOM에 직접 접근을 해야한다는 의미고
= 이는 ref를 통해 가능하다!

따라서
'이름' input의 submit 함수가 호출되었을 때,
(React Native TextInput에서는 onSubmitEditing 속성으로 제어 가능)
ref를 통해 '전화번호' input에 접근해,
해당 input(ref.current일테다)에 focus 메서드를 실행하면 된다!

 

그런데 문제는...
페이지 속 input이 재사용을 위해 여러 컴포넌트들을 합친 형태였다는 것!
input의 UI(기본, 포커스, 에러 시 디자인 요소)를 담당하는 컴포넌트와
react hook form을 통한 상태관리 컴포넌트,
해당 페이지 속 공통 UI(라벨, unit라벨 등) 컴포넌트를 합친,
하나의 custom component를 만들어 사용하는 중이었고...
외부에서 input에 ref를 지정하기 위해서는 → props로 넘겨주는 수 밖에!

const CustomInput = (args) => {
	return (
    	<>
        	<Input.Label>
            	{...}
            	<Input.Input {...args}>
                	{...}
                </Input.Input>
            </Input.Label>
        </>
    )
}

const RegistPage = () => {
	const phoneNumberInputRef = useRef(null)
    const onNameInputSubmit = () => phoneNumberInputRef.current?.focus()
	return (
    	{...}
        // '이름' input
        <CustomInput name="name"/> 
        
        // '전화번호' input
        <CustomInput
        	name='phone_number'
        	ref={phoneNumberInputRef}
        />
    )
}

이런 식으로!

기쁜 마음으로 '이름' input에 완료 버튼을 누른 결과...
아무 일도 일어나지 않았다! 그리고 만난 경고창.


아하...! 함수형 컴포넌트에 ref를 props로 전달할 수 없다!
왜? → ref 란 네이밍은 key, children과 같이 react 내부적으로 설정된 special props!
따라서 해당 이름으로 props를 전달해줘봤자 이는무시되버리고 만다!
그렇다면 어쩌지?

forwardRef

처음 React 공식 문서를 읽으며 forwardRef를 만났을 때는...
ref를 props로 넘길 수 있다! 정도로 이해한 채 넘어갔더랬다.
(typically not necessary for most components in the application 만 믿고! 하하!)
하지만 몸소 체험한 필요성!

React.forwardRef를 통해 전달받은 ref를 하위 컴포넌트에 전달할 수 있다.

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

이 때 두번재 매개변수인 ref는
React.forwardRef 호출을 통해 컴포넌트를 정의했을 때만 존재한다.
(일반 함수형/클래스형 컴포넌트는 ref를 props로 넘길 수도, ref란 arg를 받을 수도 없다!)

따라서 우리도...

const CustomInput = React.forwardRef((args, ref) => {
	return (
    	<>
        	<Input.Label>
            	{...}
            	<Input.Input {...args} ref={ref}> //물론 해당 컴포넌트 역시 forwardRef 사용
                	{...}
                </Input.Input>
            </Input.Label>
        </>
    )
}


const RegistPage = () => {
	const phoneNumberInputRef = useRef(null)
    const onNameInputSubmit = () => phoneNumberInputRef.current?.focus()
	return (
    	{...}
        // '이름' input
        <CustomInput 
        	name="name"
            onSubmitEditing={onNameInputSubmit}
            /> 
            
        // '전화번호' input
        <CustomInput
        	name='phone_number'
        	ref={phoneNumberInputRef}
        /> 
       
    )
}

부모 컴포넌트에서 자식 컴포넌트에 ref를 넘겨준 후, 해당 DOM을 조작할 수 있게 되었다!
우리가 원하는 제출 시 자동 input focus까지 완성한 모습!!!

input UX

다 구현하고 보니... 확실히 편해짐을 느낄 수 있었다!
그리고 생긴 욕심, 더 재사용 가능하게 작성할 수는 없을까?

예를 들어 지금은 input 1에서 2로 focus를 이동했지만,
특정 페이지 내의 input 모두가 1,2,3 ... 으로 focus 이동이 이뤄져야 한다면?

그렇다면... input wrapper 컴포넌트를 만들고,
해당 컴포넌트의 children으로 input들을 받아,
반복 가능하게 접근해 (React.Children.map 정도를 쓸테다),
각 input에 맞는 index를 이용해 focus 순서를 지정하지 않을까... 하는 생각!
(아직 해보지는 않았다. 하하!)

728x90
반응형

'source-code > FrontEnd' 카테고리의 다른 글

Atomic Design  (0) 2023.08.17
webpack - loader, optimization  (0) 2023.08.17
FE 디자인 설계 관련 고찰  (0) 2023.08.17
CSS _ -webkit-tap-highlight-color 이슈  (2) 2021.07.20
CSS _ Animation  (1) 2021.06.28