Form validators

Form validators (폼 검증자)

@mantine/form이 제공하는 미리 만들어진 검증 함수들(isNotEmpty, isEmail, matches, hasLength 등)을 다뤄요. 각 검증자는 작고 기본적인 검증을 제공하며, 복잡한 검증이 필요하면 다른 검증 방식을 사용해요. 검증자 하나씩 설명해 드릴게요.

출처: 문서

본문

사용법

@mantine/form 패키지는 검증 규칙 객체에서 사용할 수 있는 여러 함수를 내보내요. 검증 함수는 크기가 아주 작고 기본 검증을 제공해요. 복잡한 검증 요구사항이 있다면 다른 유형의 검증을 사용하세요.

import { useForm, isNotEmpty, isEmail, isInRange, hasLength, matches, isUrl, isOneOf } from '@mantine/form';
import { Button, Group, TextInput, NumberInput, NativeSelect } from '@mantine/core';

function Demo() {
  const form = useForm({
    mode: 'uncontrolled',
    initialValues: {
      name: '',
      job: '',
      email: '',
      favoriteColor: '',
      age: 18,
      website: '',
      role: '',
    },

    validate: {
      name: hasLength({ min: 2, max: 10 }, 'Name must be 2-10 characters long'),
      job: isNotEmpty('Enter your current job'),
      email: isEmail('Invalid email'),
      favoriteColor: matches(/^#([0-9a-f]{3}){1,2}$/, 'Enter a valid hex color'),
      age: isInRange({ min: 18, max: 99 }, 'You must be 18-99 years old to register'),
      website: isUrl('Invalid URL'),
      role: isOneOf(['developer', 'designer', 'manager'], 'Pick a valid role'),
    },
  });

  return (
    <form onSubmit={form.onSubmit(() => {})}>
      <TextInput
        label="Name"
        placeholder="Name"
        withAsterisk
        key={form.key('name')}
        {...form.getInputProps('name')}
      />
      <TextInput
        label="Your job"
        placeholder="Your job"
        withAsterisk
        mt="md"
        key={form.key('job')}
        {...form.getInputProps('job')}
      />
      <TextInput
        label="Your email"
        placeholder="Your email"
        withAsterisk
        mt="md"
        key={form.key('email')}
        {...form.getInputProps('email')}
      />
      <TextInput
        label="Your favorite color"
        placeholder="Your favorite color"
        withAsterisk
        mt="md"
        key={form.key('favoriteColor')}
        {...form.getInputProps('favoriteColor')}
      />
      <NumberInput
        label="Your age"
        placeholder="Your age"
        withAsterisk
        mt="md"
        key={form.key('age')}
        {...form.getInputProps('age')}
      />
      <TextInput
        label="Your website"
        placeholder="https://example.com"
        withAsterisk
        mt="md"
        key={form.key('website')}
        {...form.getInputProps('website')}
      />
      <NativeSelect
        label="Your role"
        data={['', 'developer', 'designer', 'manager']}
        withAsterisk
        mt="md"
        key={form.key('role')}
        {...form.getInputProps('role')}
      />

      <Group justify="flex-end" mt="md">
        <Button type="submit">Submit</Button>
      </Group>
    </form>
  );
}

선택적 오류 (Optional error)

아래 모든 검증 함수의 마지막 인자는 선택이에요. 오류를 설정하지 않으면 검증에 실패한 필드는 오류 메시지 없이 유효하지 않은 스타일만 가져요:

import { useForm, isNotEmpty, isEmail, isInRange, hasLength, matches, isUrl, isOneOf } from '@mantine/form';
import { Button, Group, TextInput, NumberInput, NativeSelect } from '@mantine/core';

function Demo() {
  const form = useForm({
    mode: 'uncontrolled',
    initialValues: {
      name: '',
      job: '',
      email: '',
      favoriteColor: '',
      age: 18,
      website: '',
      role: '',
    },

    validate: {
      name: hasLength({ min: 2, max: 10 }),
      job: isNotEmpty(),
      email: isEmail(),
      favoriteColor: matches(/^#([0-9a-f]{3}){1,2}$/),
      age: isInRange({ min: 18, max: 99 }),
      website: isUrl(),
      role: isOneOf(['developer', 'designer', 'manager']),
    },
  });

  return (
    <form onSubmit={form.onSubmit(() => {})}>
      <TextInput
        label="Name"
        placeholder="Name"
        withAsterisk
        key={form.key('name')}
        {...form.getInputProps('name')}
      />
      <TextInput
        label="Your job"
        placeholder="Your job"
        withAsterisk
        mt="md"
        key={form.key('job')}
        {...form.getInputProps('job')}
      />
      <TextInput
        label="Your email"
        placeholder="Your email"
        withAsterisk
        mt="md"
        key={form.key('email')}
        {...form.getInputProps('email')}
      />
      <TextInput
        label="Your favorite color"
        placeholder="Your favorite color"
        withAsterisk
        mt="md"
        key={form.key('favoriteColor')}
        {...form.getInputProps('favoriteColor')}
      />
      <NumberInput
        label="Your age"
        placeholder="Your age"
        withAsterisk
        mt="md"
        key={form.key('age')}
        {...form.getInputProps('age')}
      />
      <TextInput
        label="Your website"
        placeholder="https://example.com"
        withAsterisk
        mt="md"
        key={form.key('website')}
        {...form.getInputProps('website')}
      />
      <NativeSelect
        label="Your role"
        data={['', 'developer', 'designer', 'manager']}
        withAsterisk
        mt="md"
        key={form.key('role')}
        {...form.getInputProps('role')}
      />

      <Group justify="flex-end" mt="md">
        <Button type="submit">Submit</Button>
      </Group>
    </form>
  );
}

isNotEmpty

isNotEmpty는 폼 값이 비어있지 않은지 확인해요. 빈 문자열, 빈 배열, false, null, undefined 값은 비어있는 것으로 간주돼요. 문자열은 검증 전에 trim돼요:

import { isNotEmpty, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    name: '',
    acceptTermsOfUse: false,
    country: null,
    previousJobs: [],
  },

  validate: {
    // Empty strings are considered to be invalid
    name: isNotEmpty('Name cannot be empty'),

    // False value is considered to be invalid
    acceptTermsOfUse: isNotEmpty('You must accept terms of use'),

    // null is considered to be invalid
    country: isNotEmpty('Pick your country'),

    // Empty arrays are considered to be invalid
    previousJobs: isNotEmpty('Enter at least one job'),
  },
});

isEmail

isEmail은 /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,})+$/ 정규식으로 폼 값이 이메일인지 판단해요:

import { isEmail, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    email: '',
  },

  validate: {
    email: isEmail('Invalid email'),
  },
});

matches

matches는 폼 값이 주어진 정규식과 일치하는지 확인해요. 폼 값이 문자열이 아니면 검증이 실패해요:

import { matches, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    color: '',
  },

  validate: {
    color: matches(/^#([0-9a-f]{3}){1,2}$/, 'Invalid color'),
  },
});

isInRange

isInRange는 폼 값이 주어진 min-max 범위 안에 있는지 확인해요. 폼 값이 숫자가 아니면 검증이 실패해요:

import { isInRange, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    maxRange: 0,
    minRange: 0,
    minMaxRange: 0,
  },

  validate: {
    maxRange: isInRange({ max: 20 }, 'Value must be 20 or less'),
    minRange: isInRange({ min: 10 }, 'Value must be 10 or more'),
    minMaxRange: isInRange(
      { min: 10, max: 20 },
      'Value must be between 10 and 20'
    ),
  },
});

hasLength

hasLength는 폼 값의 길이가 주어진 min-max 범위 안에 있는지 확인해요. hasLength는 문자열, 배열, 그리고 length 속성을 가진 다른 어떤 객체와도 올바르게 동작해요. 문자열은 검증 전에 trim돼요:

import { hasLength, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    exact: '',
    maxLength: '',
    minLength: '',
    minMaxLength: '',
  },

  validate: {
    exact: hasLength(5, 'Values must have exactly 5 characters'),
    maxLength: hasLength(
      { max: 20 },
      'Value must have 20 or less characters'
    ),
    minLength: hasLength(
      { min: 10 },
      'Value must have 10  or more characters'
    ),
    minMaxLength: hasLength(
      { min: 10, max: 20 },
      'Value must have 10-20 characters'
    ),
  },
});

matchesField

matchesField는 폼 값이 다른 폼 필드의 값과 같은지 확인해요. matchesField는 원시 값(primitive)에서만 동작한다는 점에 주의하세요(배열과 객체는 비교할 수 없어요):

import { matchesField, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    password: '',
    confirmPassword: '',
  },

  validate: {
    confirmPassword: matchesField(
      'password',
      'Passwords are not the same'
    ),
  },
});

isJSONString

isJSONString은 폼 값이 유효한 JSON 문자열인지 확인해요:

import { isJSONString, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    json: '',
  },

  validate: {
    json: isJSONString('Invalid JSON string'),
  },
});

isUrl

isUrl는 폼 값이 유효한 URL인지 확인해요. 기본적으로 http와 https 프로토콜만 허용되고 localhost는 거부돼요. 첫 번째 인자로 옵션을 전달해 이 동작을 커스터마이즈할 수 있어요:

import { isUrl, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    website: '',
    internalUrl: '',
    ftpServer: '',
  },

  validate: {
    // Basic URL validation
    website: isUrl('Invalid URL'),

    // Allow localhost URLs
    internalUrl: isUrl(
      { allowLocalhost: true },
      'Invalid URL'
    ),

    // Allow custom protocols
    ftpServer: isUrl(
      { protocols: ['ftp', 'https'] },
      'Invalid FTP or HTTPS URL'
    ),
  },
});

isOneOf

isOneOf는 폼 값이 주어진 허용 값 목록에 포함되는지 확인해요. 비교는 엄격한 동등(strict equality)을 사용해요:

import { isOneOf, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    role: '',
    priority: 0,
  },

  validate: {
    role: isOneOf(
      ['admin', 'user', 'moderator'],
      'Invalid role'
    ),
    priority: isOneOf([1, 2, 3], 'Priority must be 1, 2, or 3'),
  },
});

isNotEmptyHTML

isNotEmptyHTML은 폼 값이 빈 HTML 문자열이 아닌지 확인해요. 빈 문자열, HTML 태그와 공백만 있는 문자열은 비어있는 것으로 간주돼요:

import { isNotEmptyHTML, useForm } from '@mantine/form';

const form = useForm({
  mode: 'uncontrolled',
  initialValues: {
    html: '',
  },

  validate: {
    html: isNotEmptyHTML('HTML cannot be empty'),
  },
});

더 알아보기 (Learn more)