bkdragon's log

제출 폼 본문

UI UX

제출 폼

bkdragon 2023. 8. 23. 23:41

제출 폼은 UI 요소라고 라기 보단 개발용어(?)의 느낌이다. 물론 아주 개인적인 의견이다.

 

재사용성 있게 만들어보고 싶어서 만들어보았다! 검증을 편하게 하기 위해 react-hook-form을 사용해서 만들었다. react-hook-form의 간단한 예제 코드로 역할도 할 수 있는 글이 되지 않을까싶다.

 

우선 전체를 담당하는 Form

 

form.tsx

interface Props<TFormData extends object> {
  title: string;
  onSubmit: (data: TFormData) => void;
  list: InputList<TFormData>;
}

const Form = <TFormData extends object>({
  title,
  onSubmit,
  list,
}: Props<TFormData>) => {
  const { control, handleSubmit } = useForm<TFormData>({ criteriaMode: 'all' });

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
        <h2>{title}</h2>
        {list.map(({ name, rules, type }, index) => (
          <CustomInput
            key={index}
            type={type}
            control={control}
            name={name}
            rules={rules}
          />
        ))}
        <button className={styles.form_button}>제출</button>
      </form>
    </div>
  );
};

export default Form;
  • list를 Props로 받아서 map을 통해 CustomInput 컴포넌트를 화면에 그린다. list에는 name, rules, type이 있다.
  • handleSubmit은 TFormData 타입의 데이터를 인자로 받는 submit 함수를 인자로 받아서 사용하면 된다.
  • 어떤 title, 어떤 list를 보내냐에 따라 로그인 폼, 결제 폼, 회원가입 폼이 될 수 있다.

 

fixture.ts

예시로 만든 고정물인데 로그인 폼에 대한 예시이다. 따라서 email과 password가 있다.

export interface LoginFormProps {
  email: string;
  password: string;
}

type Rules = Omit<
  RegisterOptions<FieldValues, string>,
  'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
>;

export type InputList<T extends object> = {
  name: Path<T>;
  type: React.HTMLInputTypeAttribute;
  rules?: Rules;
}[];

export const list: InputList<LoginFormProps> = [
  {
    name: 'email',
    type: 'text',
    rules: {
      pattern: {
        message: '형식에 맞게 입력해주세요.',
        value: new RegExp('[a-z0-9]+@[a-z]+.[a-z]{2,3}'),
      },
      required: '필수 항목 입니다.',
    },
  },
  {
    name: 'password',
    type: 'password',
    rules: {
      required: '필수 항목 입니다.',
      minLength: { value: 8, message: '8자리 이상이여야 합니다.' },
    },
  },
];
  • name에는 제네릭으로 받은 타입의 key 값이 들어간다.실제 사용할 때 LoginFormProps를 넘겨줄 것이기 때문에 email과 password가 name이 된다.
  • type은 input 태크의 type 어트리뷰트에 들어갈 수 있는 값들이 들어간다. (ex. text, password)
  • Rules는 react-hook-form에서 가져온 타입이다. 입력 값을 검증하기 위한 옵션들이다. 최대, 최소 길이, 정규식을 이용한 패턴 등을 설정할 수 있다. 

이제 이 list를 Form 컴포넌트에 넣어주면 저 rules에 맞게 검증이 이루어지는 CustomInput이 화면에 그려질 것이다. 이제 CustomInput을 보자.

 

CustomInput.tsx

import { ErrorMessage } from '@hookform/error-message';

import { useController } from 'react-hook-form';

const CustomInput = <TFormData extends object>({
  type,
  control,
  name,
  rules,
}: CustomInputProps<TFormData>) => {
  const {
    field: { onChange, value },
    formState: { errors },
  } = useController({
    name,
    control,
    rules,
  });

  return (
    <div>
      <input
        value={value}
        onChange={onChange}
        type={type}
        placeholder={name}
        className={styles.form_input}
      />
      <ErrorMessage
        errors={errors}
        name={name}
        render={({ messages }) =>
          messages &&
          Object.entries(messages).map(([type, message]) => (
            <p key={type} style={{ color: 'red' }}>
              {message}
            </p>
          ))
        }
      />
    </div>
  );
};

export default CustomInput;
  • 제어 컴포넌트로 사용하기 위해 useController 훅을 사용한다. 제어 컴포넌트는 컴포넌트(태그)에서 상태를 직접 다루는 것을 말한다.
  • 검증 결과 실패시 에러메시지를 보여주는 ErrorMessage 컴포넌트가 있다. react-hook-form 에서 제공된다.
  • ErrorMessage 사용법은 정해져있다. 사용하지 않더라도 errors 객체의 값을 map을 통해 뿌려주는 식으로 구현할 수 있다. 공식문서 링크를 남겨두겠다. 
  • https://www.react-hook-form.com/api/useformstate/errormessage/

 

 

다 만들었으니 이렇게 사용하면 된다.

<Form<LoginFormProps> title="Login" onSubmit={onSubmit} list={list} />

 

 

결과

 

UX 측면에서 이점이 있게 사용하려면 할 수 있을 것 같다. Modal 창을 이용한다던지 안내 문구를 추가해서 가이드를 제시한다던지 할 수 있을 것 같다.

 

이렇게 되면 다음 UI/UX 시리즈는 Modal 이려나?

'UI UX' 카테고리의 다른 글

Carousel  (0) 2023.09.12
Button (with Chakra)  (0) 2023.08.25
드롭 다운 메뉴  (0) 2023.08.21
아코디언 메뉴  (0) 2023.08.13
햄버거 버튼  (0) 2023.08.13