icon안동민 개발노트

기본적인 폼 유효성 검사 구현


 폼 유효성 검사는 사용자 입력의 정확성을 보장하고 잘못된 데이터가 서버로 전송되는 것을 방지하는 중요한 과정입니다.

 React에서는 클라이언트 사이드 유효성 검사를 구현하여 사용자 경험을 향상시킬 수 있습니다.

실시간 유효성 검사

 실시간 유효성 검사는 사용자가 입력하는 동안 즉시 피드백을 제공합니다.

import React, { useState } from 'react';
 
function RealTimeValidationForm() {
  const [email, setEmail] = useState('');
  const [emailError, setEmailError] = useState('');
 
  const validateEmail = (value) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!value) {
      setEmailError('Email is required');
    } else if (!emailRegex.test(value)) {
      setEmailError('Invalid email format');
    } else {
      setEmailError('');
    }
  };
 
  const handleEmailChange = (e) => {
    const value = e.target.value;
    setEmail(value);
    validateEmail(value);
  };
 
  return (
    <form>
      <div>
        <label htmlFor="email">Email:</label>
        <input
          id="email"
          type="email"
          value={email}
          onChange={handleEmailChange}
          aria-invalid={!!emailError}
          aria-describedby="email-error"
        />
        {emailError && <span id="email-error" className="error">{emailError}</span>}
      </div>
    </form>
  );
}

 이 예제에서는 이메일 입력 필드의 실시간 유효성 검사를 구현했습니다.

 사용자가 입력할 때마다 validateEmail 함수가 호출되어 유효성을 검사하고 적절한 에러 메시지를 표시합니다.

제출 시 유효성 검사

 폼 제출 시에 모든 필드의 유효성을 한 번에 검사할 수도 있습니다.

import React, { useState } from 'react';
 
function SubmitValidationForm() {
  const [formData, setFormData] = useState({ username: '', password: '' });
  const [errors, setErrors] = useState({});
 
  const validateForm = () => {
    let newErrors = {};
    if (!formData.username) newErrors.username = 'Username is required';
    if (!formData.password) newErrors.password = 'Password is required';
    else if (formData.password.length < 6) newErrors.password = 'Password must be at least 6 characters';
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };
 
  const handleSubmit = (e) => {
    e.preventDefault();
    if (validateForm()) {
      // 폼 제출 로직
      console.log('Form submitted:', formData);
    } else {
      console.log('Form has errors');
    }
  };
 
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="username">Username:</label>
        <input
          id="username"
          name="username"
          value={formData.username}
          onChange={handleChange}
          aria-invalid={!!errors.username}
          aria-describedby="username-error"
        />
        {errors.username && <span id="username-error" className="error">{errors.username}</span>}
      </div>
      <div>
        <label htmlFor="password">Password:</label>
        <input
          id="password"
          name="password"
          type="password"
          value={formData.password}
          onChange={handleChange}
          aria-invalid={!!errors.password}
          aria-describedby="password-error"
        />
        {errors.password && <span id="password-error" className="error">{errors.password}</span>}
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

 이 예제에서는 폼 제출 시 validateForm 함수를 호출하여 모든 필드의 유효성을 검사합니다.

 유효성 검사를 통과하지 못하면 폼 제출이 방지되고 에러 메시지가 표시됩니다.

커스텀 유효성 검사 규칙 생성

 복잡한 유효성 검사 규칙이 필요한 경우, 별도의 유효성 검사 함수를 만들어 사용할 수 있습니다.

const validationRules = {
  required: (value) => value.trim() !== '' || 'This field is required',
  minLength: (length) => (value) => 
    value.length >= length || `Must be at least ${length} characters`,
  maxLength: (length) => (value) => 
    value.length <= length || `Must be no more than ${length} characters`,
  pattern: (regex, message) => (value) => 
    regex.test(value) || message
};
 
function validateField(value, rules) {
  for (let rule of rules) {
    const result = rule(value);
    if (result !== true) return result;
  }
  return true;
}
 
// 사용 예:
const usernameRules = [
  validationRules.required,
  validationRules.minLength(3),
  validationRules.maxLength(20),
  validationRules.pattern(/^[a-zA-Z0-9_]+$/, 'Only alphanumeric characters and underscores are allowed')
];
 
const error = validateField(username, usernameRules);

 이 접근 방식을 사용하면 재사용 가능한 유효성 검사 규칙을 만들고 조합할 수 있습니다.

접근성을 고려한 유효성 검사

 접근성 있는 폼을 만들기 위해 다음 사항들을 고려해야 합니다.

  1.  적절한 레이블 사용 : 모든 입력 필드에 대해 명확한 레이블을 제공합니다.

  2.  ARIA 속성 활용 : aria-invalid, aria-describedby 등의 속성을 사용하여 스크린 리더 사용자에게 유효성 상태를 알립니다.

  3.  에러 메시지 연결 : 에러 메시지를 해당 입력 필드와 프로그래밍적으로 연결합니다.

  4.  키보드 접근성 : 모든 폼 요소와 에러 메시지에 키보드로 접근 가능해야 합니다.

<div>
  <label htmlFor="email">Email:</label>
  <input
    id="email"
    type="email"
    value={email}
    onChange={handleEmailChange}
    aria-invalid={!!emailError}
    aria-describedby="email-error"
  />
  {emailError && (
    <span id="email-error" className="error" role="alert">
      {emailError}
    </span>
  )}
</div>

유효성 검사 상태에 따른 UI 변경

 유효성 검사 결과에 따라 UI를 동적으로 변경할 수 있습니다.

<input
  className={emailError ? 'input-error' : 'input-valid'}
  // ... 다른 속성들
/>
<button type="submit" disabled={!isFormValid}>
  Submit
</button>

 이러한 방식으로 사용자에게 시각적 피드백을 제공하고, 폼이 유효하지 않을 때 제출을 방지할 수 있습니다.

주의사항

  1.  성능 고려 : 실시간 유효성 검사를 구현할 때 성능에 주의해야 합니다. 필요한 경우 디바운싱 기법을 사용하여 불필요한 검사를 줄일 수 있습니다.

  2.  서버 사이드 검증 : 클라이언트 사이드 유효성 검사는 편의성을 위한 것이며, 보안을 위해 반드시 서버 사이드에서도 유효성 검사를 수행해야 합니다.

  3.  일관성 : 애플리케이션 전체에서 일관된 유효성 검사 패턴과 에러 메시지를 사용하세요.

  4.  국제화 : 에러 메시지를 하드코딩하지 말고, 국제화(i18n) 라이브러리를 사용하여 다국어 지원을 고려하세요.

 React에서 폼 유효성 검사를 구현할 때는 사용자 경험, 성능, 그리고 접근성을 모두 고려해야 합니다.

 실시간 검사와 제출 시 검사를 적절히 조합하고, 명확하고 즉각적인 피드백을 제공함으로써 사용자 친화적인 폼을 만들 수 있습니다.

 또한, 재사용 가능한 유효성 검사 로직을 만들어 코드의 중복을 줄이고 일관성을 유지하는 것이 중요합니다.