개발자의 다양한 시각에 따라 form 안의 사용자 입력값을 관리하는데 굉장히 복잡해질 수 있습니다.
하나 이상의 사용자 입력 값이 모두 유효하지 않을 수도 있으며, 모두 유효할 수도 있고 심지어 서버로 요청을 보낸 뒤에 특정 값이 사용 가능한지 확인해야 하는 비동기 유효성 검사를 이용해야 해서 상태를 알 수 없을 수도 있습니다.
예를 들면 이메일 주소가 유효한지 확인해보는 경우가 있습니다.
실제로 하나의 form이 유효한지 확인하는 것이 아니라 form 안에 있는 모든 사용자 입력값에 대해 유효성 검사를 진행하게 되며, 이러한 각각의 사용자 입력값의 유효성이 모여 전체 form의 상태를 결정하게 됩니다.
form과 form 안에 있는 사용자 입력값이 유효하지 않을 때 특정한 입력값에 대해서 에러 메시지를 출력하고 문제가 되는 입력값을 강조해주어야 합니다.
그리고 하나 이상의 입력값이 유효하지 않다면 해당 입력값이 저장되지 않도록 해주어야 하며, 반대로 입력값이 유효하다면 저장되도록 해주어야 하는데 이때 언제 유효성 검사를 할지가 문제가 됩니다.
사용자 입력의 유효성 검사를 언제 할지 몇 가지 예로 살펴보겠습니다.
사용자 입력값 유효성 검사 1
form이 제출되었을 때 사용자 입력값의 유효성 검사를 할 수 있습니다.
form이 제출되었을 때 유효성 검사를 하게 되면 사용자에게 경고를 보여주기 전에 사용자가 유효한 입력값을 입력하게 할 수 있습니다.
즉 사용자가 이메일을 input 요소에 입력하는 중에 완전히 입력하지 않았음에도 불구하고 이메일이 틀렸다고 경 고을 띄우는 상황을 피할 수 있습니다.
사용자의 모든 입력이 끝난 뒤 경고를 보여주게 되므로 불필요한 경고를 줄일 수 있는 장점이 있지만 form 제출 후에 경고를 띄워주기 때문에 경고 피드백이 조금 늦을 수 있다는 단점이 있습니다.
사용자 입력값 유효성 검사 2
사용자가 값을 입력하고 input 요소가 포커스를 잃었을 때 사용자가 무엇을 입력했는지를 확인하고 사용자 입력값의 유효성 검사를 할 수 있습니다.
input 요소가 포커스를 잃었을 때 유효성 검사를 하게 되면 form을 제출하고 사용자에게 경고를 띄우기 전에 사용자 입력값에 대한 경고를 띄울 수 있으며 사용자의 특정 입력이 끝나자마자 바로 그 시점에서 경고를 보낼 수 있다는 장점이 있습니다.
즉 form 제출되기 전까지 기다리는 것이 아니라 하나의 특정 입력을 마칠 때까지만 기다렸다가 사용자 입력값의 유효성 검사를 진행하는 것이며 사용자가 손대지 않은 form에 대해서 매우 유용하게 사용할 수 있습니다.
하지만 이 방법은 포커스를 잃을 때에만 사용자 입력값의 유효성을 검사하기 때문에 사용자가 그전에 유효하지 않은 값을 입력하고 경고를 확인하여 입력값을 수정하는 중에는 알려줄 수 없다는 단점이 있습니다.
사용자 입력값 유효성 검사 3
input 요소가 변경될 때마다 사용자 입력값의 유효성 검사를 할 수 있습니다.
input 요소가 변경될 때마다 유효성 검사를 하게 되면 사용자가 키를 누를 때마다 바로 유효성에 대한 피드백을 해줄 수 있는 장점이 있지만, 사용자가 유효한 값을 입력하기도 전에 경고를 보낸다는 단점이 있습니다.
즉 키를 한 번씩 칠 때마다 유효성 검사를 하기 때문에 처음 사용자가 form에 접근하여 해당 form에 아무것도 입력하지 않은 상황에서 유효하지 않은 입력값이 되어 입력하기도 전에 수많은 경고를 사용자에게 띄우게 됩니다.
사용자 입력값 유효성 검사 3에 다른 사용자 입력값 유효성 검사를 합쳐 input 요소가 변경될 때마다 사용자 입력값의 유효성 검사를 한다면 사용자에게 입력값에 대한 피드백을 바로 주게 되어서 입력값이 유효해진 순간 사용자에게 알릴 수 있습니다.
다소 추상적일 수 있으니 코드와 함께 살펴보도록 하겠습니다.
첫 번째로 사용자 입력값 유효성 검사 1, 즉 form을 제출했을 경우 사용자에게 경고 메시지를 띄우는 방법을 살펴보도록 하겠습니다.
사용자 입력값을 저장할 때 State에 저장하거나 ref를 사용하여 저장할 수 있는데 두 개 중에 어떤 것을 사용해야 할지는 입력된 값으로 하고자 하는 일에 따라 달라집니다.
만약 사용자 입력값이 form이 제출되었을 때 한 번만 필요하다면 모든 키 입력마다 State 값을 업데이트하기엔 지나치거나 불필요하므로 ref를 사용하여 값을 저장하는 방법을 선택하는 것이 나을 수 있습니다.
반면 즉각적인 유효성 검증을 위해 키 입력마다 입력값이 필요하다면 ref로는 작업이 불가능하므로 State에 사용자 입력값을 저장하는 방법을 선택해야 됩니다.
또한 입력값을 초기화하고 싶은 경우 깔끔한 코드 작성을 위해 ref가 아닌 State에 사용자 입력값을 저장하는 방법을 선택하는 것이 나을 수 있습니다.
(DOM에 직접적으로 접근하여 조작하는 ref 사용은 지양하는 것이 좋습니다.)
때문에 사용자 입력을 State에 저장하는 방식으로 아래와 같이 코드를 작성해 주었습니다.
const SimpleInput = (props) => {
const [enteredName, setEnteredName] = useState("");
const handleNameInputChange = (event) => {
setEnteredName(event.target.value);
};
const handleFormSubmission = (event) => {
event.preventDefault();
console.log(enteredName);
// 입력값 초기화
setEnteredName("");
};
return (
<form onSubmit={handleFormSubmission}>
<div className="form-control">
<label htmlFor="name">Your Name</label>
<input
type="text"
id="name"
value={enteredName}
onChange={handleNameInputChange}
/>
</div>
<div className="form-actions">
<button>Submit</button>
</div>
</form>
);
};
export default SimpleInput;
이때 사용자가 아무런 입력을 하지 않고 form을 제출하게 되면 서버에는 공백의 입력값이 전송되게 됩니다.
때문에 입력값이 없을 때는 서버에 전송하지 않고 사용자에게 경고 메시지를 띄워줄 필요가 있습니다.
추가로 사용자 입력값의 유효성 검사 값을 저장하는 State를 생성해주었고 조건문을 사용하여 사용자의 입력값이 없으면 조건문 밑의 코드가 실행되지 않도록 해 주었습니다.
반환되는 jsx에는 유효성 검사값이 false이면 경고 메시지가 뜨게 해 주었습니다.
하지만 사용자 입력값의 유효성 검사 값을 저장하는 State의 기본값이 false이기 때문에 입력을 하지 않았음에도 불구하고 경고 메시지가 사용자에게 띄어져 있게 됩니다.
때문에 기본값을 true로 설정해주어야 form 제출을 했을 때 입력값이 없다면 false로 업데이트시켜주어 사용자에게 경고 메시지를 띄울 수 있게 됩니다.
저의 경우 사용자 입력값의 유효성 검사 값을 저장하는 State가 false 일 경우 경고 메시지와 함께 input 요소에 추가 class를 부여하여 사용자에게 추가적인 경고가 보이도록 해 주었습니다.
여기까지 하면 사용자가 아무런 입력을 하지 않고 form 제출을 하게 되면 위와 같은 결과가 나오게 됩니다.
현재까지 작성한 코드에 있는 유효성 검사 방식에는 하나의 단점은 위의 코드입니다.
사용자 입력값의 유효성 검사 값을 저장하는 State가 있고 해당 State의 초기값을 true로 설정해주어 아무것도 입력하지 않은 상황에서도 유효하다고 취급하고 유효하지 않으면 false로 바뀌게 됩니다.
그럼에도 해당 State의 기본값을 true로 설정한 이유는 입력값의 유효성을 출력할 때 외에는 별달리 필요하지 않기 때문인데 이점이 왜 문제가 되는지 살펴보도록 하겠습니다.
useEffect를 사용하여 enteredIsValid의 값이 바뀔 때마다 어떠한 액션을 실행할 경우를 가정해보겠습니다.
(콘솔을 HTTP 요청을 보내는 것이라고 가정하겠습니다.)
이 경우 해당 컴포넌트가 화면에 Mount 되었을 때 콘솔(HTTP)이 찍히게 됩니다.
이는 앞서 말한다로 enteredIsValid의 기본값을 true로 부정확하게 설정해주었기 때문입니다.
만약 useEffect가 없었다면 해당 편법을 이용했더라도 문제가 되지 않았을 것입니다.
이러한 문제로 추가적으로 코드를 수정하도록 하겠습니다.
enteredIsValid의 기본값을 false로 설정해 주었습니다.
만약 사용자가 input 요소를 건드리지 않고 form 제출을 했어도 이는 input 요소를 건드렸다고 판단하고 true로 바뀌게 해 주었습니다.
// 사용자의 입력값이 유효하지 않고 사용자가 input 요소를 건드렸을 경우만 유효하지 않다고 판단
// nameInputIsinvalid의 값은 불리언
const nameInputIsinvalid = !enteredNameIsValid && enteredNameTouched;
위와 같이 코드를 작성하게 되면
enteredNameIsValid가 false(사용자의 입력값이 유효하지 않음을 의미)이고 enteredNameTouched가 false(사용자가 input 요소를 건드리지 않았음을 의미)이면 두 번째 값인 false를 반환하기 때문에 상수 nameInputIsinvalid는 false를 할당받게 됩니다.
enteredNameIsValid가 true(사용자의 입력값이 유효함을 의미) 이면 첫 번째 값을 반환하기 때문에 상수 nameInputIsinvalid는 true를 할당받게 됩니다.
이제 컴포넌트가 Mount 되었을 때 경고 메시지를 띄우지 않게 설정할 수 있게 됩니다.
(코드는 길어졌지만 보다 많은 유스케이스를 다룰 수 있게 되었습니다.)
해당 방법의 유효성 검사가 상황에 따라 만족이 되지 않을 수 있습니다. form이 제출되었을 때는 경고 메시지를 보여주므로 괜찮지만 이는 반대로 form이 제출되었을 경우만 사용자에게 경고 메시지를 보여주므로 다소 늦은 피드백이 될 수 도 있기 때문입니다.
사용자에게 때문에 보다 나은 경험을 제공하기 위해서는 입력하는 중에 피드백을 받는 것이 더 괜찮은 방법이 될 수 도 있습니다.
두 번째로 사용자 입력값 유효성 검사 2, 즉 input 요소의 포커스를 잃었을 경우 사용자에게 경고 메시지가 뜨도록 코드를 작성해보겠습니다.
input 요소에 onBlur를 prop으로 전달하게 되면 해당 요소가 포커스를 잃었을 때 취할 수 있는 함수를 설정해줄 수 있습니다.
앞서 작성한 handleNameInputChange 함수에 유효성 검사를 추가하여 사용자 입력값이 공백이 아니면 enteredNameIsValid가 true로 업데이트되게 해 주었습니다.
하지만 handleNameInputChange 함수에서 enteredName State를 업데이트해주고 있기는 하지만 State는 리액트에서 비동기적으로 처리되므로 즉각적으로 반영되지 않아 State를 업데이트해주는
따라서 위와 같이 추가적으로 코드를 수정해주면 사용자에게 보다 나은 경험을 줄 수 있는 유효성 검사를 할 수 있게 됩니다.
'React' 카테고리의 다른 글
Redux의 State를 올바르게 사용하는 방법 (0) | 2022.11.08 |
---|---|
Redux 정리 (0) | 2022.11.08 |
Custom Hooks (0) | 2022.10.23 |
React Routing (0) | 2022.10.18 |
useEffect (0) | 2022.10.14 |