[React] 양식(form) 및 사용자 입력 작업

SuamKang·2023년 8월 3일
0

React

목록 보기
26/34
post-thumbnail

일반적으로 리액트에서 사용자의 입력 이벤트를 받고자 할 경우 여러가지 방법이 있겠지만, 그중에서도 사용자의 입력사항이 유효한지 안한지에대한 검증이 필수적으로 빠지지 않고 들어간다.

그렇게 해야 사용자에게 올바른 피드백을 주어 더 나은 사용자 경험을 이루어 낼 수 있다고 생각하기 때문이다.


유효성 검사의 종류와 패턴


폼 양식은 단순하고 심플해 보이지만, 시각에 따라 다양한 상태를 나타낼 수 있기때문에 굉장히 복잡해 질 수도 있다.

예를들면, 하나 이상의 입력값이 모두 유효하지 않을 수도 있고, 모두 유효할 수도 있으며 심지어는 서버로 http요청을 보낸뒤 특정 값이 사용 가능한지 확인해야하는 비동기 유효성 검사를 이용해야해서 상태를 알 수 없을 수도 있다.


그럼 사용자의 입력에 따라 유효성 검증을 확인해야할 상황들이 어떤것들이 있을까?

1. 폼이 완전하게 제출 되었을 때(When form is submitted)

- 사용자에게 경고/에러 메시지를 보여주기 전에 유효한 값을 입력하도록 유도할 수 있다.
- ex) 이메일 입력 or 비밀번호 입력시 전부 입력 하고 제출한다음 에러 메세지 보내기
- 그러나 피드백이 조금 늦을 수 있다는 단점이 있다.

2. 사용자가 값을 입력하고 input요소가 focus를 잃었을 때(When a input is losinig focus)

- 전체 폼이 제출되기 전까지 기다리지 않고 특정 입력을 끝낼때 그 시점에 에러메시지를 보낼 수 있다.
- 사용자가 건드리지 않은 폼에 대해 다룰때 유용하다.
- 그러나 focus를 잃을 때에만 입력값의 유효성을 검증하면 입력하고 고치는 와중에는 피드백을 줄 수 없다는 단점이 있다.

3. 사용자가 키입력을 한번씩 칠 때마다(On every key stroke)

- 2번의 단점을 보완하는 경우 사용하면 좋다. 키를 한번씩 칠 때마다 유효성 검증이 이루어 지기 원할 경우 사용한다.
- 그러나 이를 단적으로 사용하게 되면 사용자가 입력하지도 않은 상태임에도 유효성 검증에 실패하며 에러를 마주하게 된다.

위 3가지 상황에 따라 유효성 검증이 다르게 들어가지만,
적절히 섞어 사용하게 된다면, 적절한 피드백과 함께 빠르고 좋은 사용자 경험을 줄 수 있게된다.


유효성 검증하기


폼으로 input 입력값을 받을 때,
주로 사용되는 2가지 방법을 정리해 보았다.
이 둘은 입력된 값으로 하고자 하는 일에 따라 다르게 쓰인다.


1. useState를 이용해 state를 활용하여 모든 키 입력때마다 값을 읽어들여야 할때!

-> 이는 즉각적인 유효성 검증을 할때나, 입력된 값을 초기화하고 싶을 경우에 많이 사용한다.

2. useRef를 이용해 ref객체를 활용하여 값이 필요할 때 읽어야 하는 경우에!

-> 이는 해당 입력값으로 폼 제출 될때 한번만 필요한 경우
-> state처럼 초기화도 가능하지만 ref로 초기화를 하는 행동은 직접 DOM에 접근해서 변경하는 작업이라 지양해야 함


먼저 useRef를 활용한 경우를 살펴보자.

import { useRef } from 'react';


const SimpleInput = (props) => {

  const nameInputRef = useRef();

  const formSubmitHandler = (event) => {
    event.preventDefault();

    const enteredValue = nameInputRef.current.value;
    console.log(enteredValue); 
  };

  return (
    <form onSubmit={formSubmitHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          ref={nameInputRef}
          value={enteredValue}
        />
      </div>
      <div className="form-actions">
        <button>Submit</button>
      </div>
    </form>
  );
};

export default SimpleInput;

이렇게 설정 해줄 수 있지만, 즉각적인 유효성 검증을 위해서라면 ref로는 작업이 불가하기때문에 state를 사용하여 다시 입력값을 받아 보자.


useState를 활용한 경우

import { useState } from "react";

const SimpleInput = (props) => {

  const [enteredName, setEnteredName] = useState("");

  const nameInputChangeHandler = (event) => {
    setEnteredName(event.target.value);
  };
  

  const formSubmitHandler = (event) => {
    event.preventDefault();

    if (enteredName.trim() === '') return;

    console.log(enteredName);

    setEnteredName("");
  };

  return (
    <form onSubmit={formSubmitHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          value={enteredName}
          onChange={nameInputChangeHandler}
        />
      </div>
      <div className="form-actions">
        <button>Submit</button>
      </div>
    </form>
  );
};

export default SimpleInput;

formSubmitHandler로 폼 입력을 받고 보내주는 이벤트 함수에 보통 필요한 유효성 검증을 해주는데 그저 이름 텍스트를 받는 경우라면 비어있는지만 확인해주거나 이메일이나 휴대폰 번호 등 복잡한 검증 조건이 필요하면 그 조건을 넣어 주어야 한다. 위는 그저 비어있지만 않게 해주었다.


검증 피드백 제공하기


기본적인 유효성 검증을 추가하기 위해 더 많은 상태들을 추가 할 수있다.

아래 코드와 같이 입력값에대한 유효성 여부를 나타내는 상태를 추가하여 관리 해주어도 된다.

그리고 그 상태에 따라 에러 메시지를 보여주거나 스타일 속성을 변환 시켜주어 사용자에게 올바른 피드백을 줄 수 있다.

import { useState } from "react";

const SimpleInput = (props) => {

  const [enteredName, setEnteredName] = useState("");
  const [enteredNameIsValid, setEnteredNameIsValid] = useState(true); 
  
  const nameInputChangeHandler = (event) => {
    setEnteredName(event.target.value);
  };
  
  const formSubmitHandler = (event) => {
    event.preventDefault();

    if (enteredName.trim() === '') {
      setEnteredNameIsValid(false);
      return;
    }

    setEnteredNameIsValid(true);
    setEnteredName("");
  };

  const nameInputClasses = enteredNameIsValid
    ? "form-control invalid"
    : "form-control";

  return (
    <form onSubmit={formSubmitHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          value={enteredName}
          onChange={nameInputChangeHandler}
          onBlur={nameInputBlurHandler}
        />
        {!enteredNameIsValid && (
          <p className="error-text">텍스트를 입력해야 합니다.</p>
        )}
      </div>
      <div className="form-actions">
        <button>Submit</button>
      </div>
    </form>
  );
};

export default SimpleInput;

우선 이렇게 설정을 하게되면 처음에는 에러메시지가 보이지 않고 아무 입력없이 제출하면 isValid상태가 false로 변하면서 에러 텍스트가 보이게 되며, 또한 유효하지 않은 상태에 대해서 css클래스 스타일을 다르게 주어 조건에따라 에러 블럭이 보이도록 표시 까지 더해주었다.

수정하기


하지만 위에서 enteredNameIsValid 상태의 초기값을 true로 하여 제출하기 전까지 에러나 유효성 검증이 시작되진 않지만 이는 문제가 될 수도 있다고 판단했다.

const SimpleInput = (props) => {
  const [enteredName, setEnteredName] = useState("");
  const [enteredNameIsValid, setEnteredNameIsValid] = useState(true);

useEffect(() => {
  if(enteredNameIsValid) {
    console.log('유효성 검증 완료?')
  }
}, [enteredNameIsValid])

...

};

만약 이를 useEffect를 사용하여 enteredNameIsValid 상태값이 바뀔 때마다 enteredNameIsValid이 true인 조건에서 무엇인가를 해야하는 경우에(http 요청 등등) 참이기 때문에 조건에 부합하여 실행 될것이다.

이렇게 된다면 원하지않는 상황이 연출 될 수도 있기 때문에 좋지 못한 코드일 수 있겠다.


이를 해결하기 위해서 기존의 enteredNameIsValid를 enteredNameToouched로 수정해서 상태를 바꾸어 보자.
이 상태를 보고 해당 입력값이 건드려진 입력창인지 아닌지에 따라 그에 맞는 피드백을 주려 할 것이다.

이렇게 사용자가 입력을 시도했는지 안했는지를 판단할 상태를 추가하고 이 두가지를 통해 유효한 검증을 나타내는 조건을 만들어서 적용하게 되면 상태를 많이 쓰지않고 유효성을 검증할 수 있을것 같았다.

import { useState } from "react";

const SimpleInput = (props) => {

  const [enteredName, setEnteredName] = useState("");
  const [enteredNameTouched, setEnteredNameTouched] = useState(false);

  // 유효성상태를 굳이 만들지않고 입력값이 없는 상태인 경우로 지정해줄 수 있을것이다. 
  왜냐하면 어차피 입력값 상태나 인풋창을 건드린 상태 여부가 업데이트 되게되면 컴포넌트가 리랜더링하여 최신상태로 반영해 주기 때문이다.

  // 유효한 조건
  const enteredNameIsValid = enteredName.trim() !== "";

  // 유효하지 않는 조건  -> 터치를 해서 잘못된 텍스트를 입력 받았을 때만!
  const nameInputIsInvalid = !enteredNameIsValid && enteredNameTouched;

  // 인풋 요소 키 입력값 받기
  const nameInputChangeHandler = (event) => {
    setEnteredName(event.target.value);
  };

  // onBlur를 통해 input요소가 focus를 잃었는지 여부를 판단할 수 있다. -> JS이벤트
  const nameInputBlurHandler = (event) => {
    setEnteredNameTouched(true);
  };

  // 받은 입력값 보내기
  const formSubmitHandler = (event) => {
    event.preventDefault();

    setEnteredNameTouched(true); // 폼을 제출했다는건 사용자가 전체 폼을 확인했다는 의미로 건드려진것으로 여겨야 할듯

    if (!enteredNameIsValid) return;

    console.log(enteredName);

    setEnteredName("");
    setEnteredNameTouched(false); // 터치상태여부를 초기화 시켜주어야 유효하지 않는조건이 false가 되면서 제출후에 다시 에러메시지나 에러블러가 처리되는걸 고칠 수 있게된다.
  };

  // css 클래스로 유효성 검증 조건 지정하기
  const nameInputClasses = nameInputIsInvalid
    ? "form-control invalid"
    : "form-control";

  return (
    <form onSubmit={formSubmitHandler}>
      <div className={nameInputClasses}>
        <label htmlFor="name">Your Name</label>
        <input
          type="text"
          id="name"
          value={enteredName}
          onChange={nameInputChangeHandler}
          onBlur={nameInputBlurHandler}
        />
        {nameInputIsInvalid && (
          <p className="error-text">텍스트를 입력해야 합니다.</p>
        )}
      </div>
      <div className="form-actions">
        <button>Submit</button>
      </div>
    </form>
  );
};

export default SimpleInput;

이렇게 수정하게 되면 두가지 상태를 통해 유효성 검증을 빠르게 할 수 있고 적절한 시점에 사용자에게 피드백도 줄 수 있게 된다.

profile
마라토너같은 개발자가 되어보자

0개의 댓글