본문 바로가기
Frontend/React

React 유효성 검사

by Forsaken Developer 2023. 2. 9.
728x90
728x90

유효성 검사의 시점

개발자의 시각에서 폼은 그 입력은 넓고 다양한 상태를 나타낼 수 있어서 굉장히 복잡할 수 있다.

하나 이상의 입력 값이 모두 유효하지 않을 수도 있으며 모두 유효할 수도 있고 심지어는

서버로 요청를 보낸 뒤에 특정 값이 사용 가능한지 비동기 유효성 검사를 해야해서 상태를 알 수 없을 수 있다.

그러면 언제 유효성을 검사를 해야할지가 문제이다.

1. 폼이 완전하게 제출되었을 때

사용자 입력의 유효성을 검증할 수 있습니다

폼이 완전하게 제출된 뒤에 유효성 검증을 할 때에는 사용자가 입력하는 중에 완전히 입력하지도 않았는데 입력이 틀렸다고 하지 않을 수 있다.

사용자가 입력을 마칠 때 까지 기다린 뒤에 에러를 보여주게 되므로 불필요한 경고를 줄일 수 있다.

단점은 피드백이 늦어 사용자가 값을 제출한 뒤에 문제가 있는 부분을 알려주게 된다면 사용자는 잘못된 입력 값이 있는 그 전으로 돌아가서 값을 다시 입력해야 한다.

2. input 요소가 포커스를 잃었을 때

input 요소에서 포커스를 잃었을 때 입력 값의 유효성을 검증하는 경우에 전체 폼이 제출되고

경고 메시지를 보내기 전에 사용자가 유효한 값을 입력할 수 있다.

아니면 사용자가 특정 입력을 끝내자마자 바로 그 시점에 에러를 보낼 수 있다.

즉, 전체 폼이 제출되기 전까지 기다리는 것이 아니라 하나의 특정한 입력을 마칠 때 까지만

기다린다.

이러한 방법은 사용자가 아무것도 입력하지 않은 폼에 대해서 매우 유용하다.

그러나 이 방법의 단점은 포커스를 잃을 때에만 입력 값의 유효성을 검증하면 사용자가 그 전에 유효하지 않은 값을 입력하고 나서 고치는 중에도 사용자의 입력 값이 유효한지 아닌지 알려줄 수 없다.

3. 사용자가 key를 입력할 때

키를 한번씩 칠 때마다 사용자가 입력한 값의 유효성에 대해 바로 피드백을 주는 방식인데 단점은 사용자가 유효한 값을 입력하기도 전에 경고를 보낸다는 점이다.

키를 한번씩 칠 때마다 유효성을 검증한다면 사용자가 그 폼에 처음 접근했을 때 이 폼에 아무것도 입력되지 않은 상태임에도 수많은 에러들을 마주하게 된다.

유효성 검사

input에 값이 비어있을 때 빈문자열을 form에 제출하면 안될 것이다.

if (enteredName.trim() !== "") {
      setEnteredNameIsValid(true);
    }
{!enteredNameIsValid && <p className="error-text">이름은 공백일 수 없습니다.</p>}

이 enteredNameIsValid 상태의 초기값은 true로 처음에는 입력을 유효하다는 의미이며 유효하지 않으면 바뀐다.

이렇게 한 이유는 단지 처음부터 에러를 띄우지 않기 위한 목적이다.다

여기에서 useEffect를 이용해 enteredNameIsValid이 true일 때 무언가 실행할 때를 생각해보면 처음부터 해당 내용이 실행될 것이다.

이는 단지 에러에 피드백을 주기 위해서 사용되고 유효성이 처음에는 false인 것이 조금 더 자연스럽다.

따라서 사용자가 입력 창을 건드렸는지 확인할 수 있는 새로운 상태를 추가한다.

폼이 제출되는 순간 모든 입력값이 사용자가 확인했고 건드려졌다고 볼 수 있기 때문에 onSubmit 이벤트를 통해서 touch state를 true로 설정한다.

setEnteredNameTouched(true);

touch 상태와 valid상태를 조합해서 입력 창을 건드렸고 값이 유효하지 않다면 유효하지 않은 상태를 설정한다.

const nameInputIsInvalid = !enteredNameIsValid && enteredNameTouched;
{nameInputIsInvalid && <p className="error-text">이름은 공백일 수 없습니다.</p>}

input 포커스를 잃었을 때도 마찬가지로 설정할 수 있다.

input의 onBlur 이벤트를 이용해서 입력 창을 건드렸다가 입력 창 밖을 선택하면 touch상태를 true로 설정한다.

const onBlurHandler = () => {
    setEnteredNameTouched(true);

if (event.target.value.trim() !== "") {
      setEnteredNameIsValid(true);
    }
  };

많은 경우에 여러 입력을 받아야 하며 전체 폼이 유효한지 확인하는 것도 필요하다.

왜냐하면 전체 양식이 유효하기 위해서는 모든 입력이 유효해야 하고 하나의 입력이라도 유효하지 않으면 전체 양식은 유효하지 않게 된다.

formIsValid라는 변수를 추가해서 기본값은 false로 하고 모든 입력이 유효할 때만 formIsValid가 true가 되도록 한다.

let formIsValid = false;

  if(enteredNameIsValid) {
    formIsValid =true;
  }

formIsValid의 상태에 따라 Submit 버튼 자체를 비활성화 할 수도 있다.

<button disabled={!formIsValid}>Submit</button>

input custom hooks

폼을 다룰 때 최종적으로는 비슷한 구조를 가진 코드를 쓰게된다.

이를 해결하는 한 가지 방법은 input에 대한 컴포넌트를 만들어서 그 컴포넌트 안에서 유효성 검증

로직과 상태들을 관리하는 방법이다.

이렇게 할 때 까다로워 지는 점은 전체 폼의 유효성을 관리이다.

모든 입력을 개별적인 것으로 다뤄서 각각의 입력에 대한 유효성을 알면서도 전체 폼이 유효한지 알 수 있는 방법이 필요한데 prop을 사용해야 한다.

하지만 이 방법보다는 커스텀 훅을 이용하는 것이 더 바람직하다.

import { useState } from 'react';

const useInput = (validateValue) => {
  const [enteredValue, setEnteredValue] = useState('');
  const [isTouched, setIsTouched] = useState(false);

  const valueIsValid = validateValue(enteredValue);
  const hasError = !valueIsValid && isTouched;

  const valueChangeHandler = (event) => {
    setEnteredValue(event.target.value);
  };

  const inputBlurHandler = (event) => {
    setIsTouched(true);
  };

  const reset = () => {
    setEnteredValue('');
    setIsTouched(false);
  };

  return {
    value: enteredValue,
    isValid: valueIsValid,
    hasError,
    valueChangeHandler,
    inputBlurHandler,
    reset
  };
};

export default useInput;

사용하는 컴포넌트에서도 구조 분해 할당을 통해서 더욱 간결하게 사용이 가능하다.

const {
    value: enteredName,
    isValid: enteredNameIsValid,
    hasError: nameInputHasError,
    valueChangeHandler: nameChangedHandler,
    inputBlurHandler: nameBlurHandler,
    reset: resetNameInput,
  } = useInput((value) => value.trim() !== '');

이렇게 커스텀 훅을 만들어서 필요한 로직을 훅 안에 넣고 필요한 값을 얻는 방법을 통해서 컴포넌트를 가볍게 만들고 코드를 줄일 수 있다.

728x90
반응형

댓글