[React] 유효성과 Submit을 담당하는 Form 컴포넌트를 만들어보자!

fgStudy·2022년 6월 30일
0

프론트엔드 공부

목록 보기
2/6
post-thumbnail

웹사이트를 만드는데 Form은 로그인, 회원가입 등등 여러 곳에서 사용된다.
따라서 Form 컴포넌트는 공통 컴포넌트이므로 Form 컴포넌트를 인터페이스로 분리할 필요성이 있다!

자세한 설명이 궁금하다면 Github 링크를 통해 확인하길 바란다.


🤯 Form 컴포넌트는?

Form은 기본적으로 아래 두 가지가 필요하다.

  • Input, Button
  • Submit Handler

이 때 Form이 모든 입력창의 값이 유효성을 통과하는지를 border 테두리로 보여주고, Button은 모든 입력창의 값이 유효성을 통과할 시 활성화된다고 하자.


📚 Form 컴포넌트에서 필요한 자원들은?

Form의 User Flow은 아래와 같다,

  1. 각 Input이 유효성을 통과하는지 확인
    • 유효성을 통과하지 못할 시 border Red
  2. 모든 입력창이 유효성을 통과하면 Button 활성화
  3. Button 클릭 시 Submit Handler 실행

위의 Flow를 통해 우리는 Form 컴포넌트에서 각 입력창의 유효성검증을 위한 정규표현식과, Submit한 후 수행할 작업들이 필요하다는 것을 확인할 수 있다.

따라서 Form 컴포넌트에서 Input Field Data와 Submit Callback을 주입함으로써 Form 컴포넌트를 선언적으로 사용할 수 있다.



🎨 Form 컴포넌트 로직

  • Form 컴포넌트는 Input과 Submit Button이 존재한다.
  • Form 컴포넌트를 사용 시 Form 컴포넌트에서 표현될 입력창들에 대한 데이터 전달
    • { name, placeholder, type, regex } 배열 (Field Data)
    • Submit Callback
  • Form 컴포넌트에서는 전달받은 Field Data 배열을 이용해 Input Field 구성
    • 전달받은 regex를 이용해 각 입력창의 유효성 통과 여부 체크
  • Form 컴포넌트의 Submit Button은 모든 입력창들이 유효성을 통과할 시 활성화
    • 입력창의 value가 변경될 때마다 각 입력창의 유효성 검사
  • Submit Button을 클릭 시, 전달 받은 Submit Callback 호출

🐔 Form 컴포넌트 전체 코드

export default function Form({ fieldData, submitCallback }) {
  let isSubmit = true;
  const inputValues = {};

  const FormInputs = fieldData.map((data, idx) => {
    const { name, placeholder, type, regex } = data;
    const [value, setValue] = useState('');
    inputValues[name] = value;

    const isValidated = value !== '' && validator(value, regex);
    isSubmit &&= isValidated;

    return (
      <S.Input
        key={idx}
        name={name}
        placeholder={placeholder}
        type={type}
        value={value}
        onChange={({ target: { value } }) => setValue(value)}
        validated={(value === '' || isValidated).toString()}
      />
    );
  });

  const submitHandler = e => {
    e.preventDefault();
    submitCallback(isSubmit, inputValues);
  };

  return (
    <>
      <S.InputWrap>{FormInputs}</S.InputWrap>
      <S.Button disabled={!isSubmit} onClick={submitHandler}>
        로그인
      </S.Button>
    </>
  );
}


🎂 사용 예제 - Login Form

위의 codesandbox에서 Login페이지에서 Form 컴포넌트를 사용하였다.


👀 Login Form

// src/constants/fieldData
// Login Form Field 데이터 전달
export const loginField = [
  {
    name: 'email',
    placeholder: '전화번호, 사용자 이름 또는 이메일',
    type: 'text',
    regex: REGEX_EMAIL,
  },
  {
    name: 'password',
    placeholder: '비밀번호',
    type: 'password',
    regex: REGEX_PW,
  },
];

// src/hook/useAuth
// Login Callback 전달
export function useAuth() {
  const navigate = useNavigate();
  const dispatch = useUserDispatch();

  const loginCallback = getLoginCallback(navigate, dispatch);
  const logoutCallback = getLogoutCallback(navigate, dispatch);

  return { loginCallback, logoutCallback };
}

const getLoginCallback =
  (navigate, dispatch) => async (isValid, inputValues) => {
    if (!isValid) return;
    const registerUserList = await getRegisterUserList();
    const isUserExist = await checkIsUserExist(
      inputValues,
      registerUserList,
    );

    if (!isUserExist) {
      alert('아이디 또는 비밀번호를 잘못 입력하셨습니다!');
      return;
    }

    dispatch({
      type: 'LOGIN',
      userId: extractIdFromEmail(inputValues.email),
    });

    alert('로그인 성공하였습니다.');
    navigate('/', { replace: true });
  };

const getLogoutCallback = (navigate, dispatch) => async () => {
  alert('로그아웃 되었습니다.');
  dispatch({
    type: 'LOGOUT',
  });

  navigate('/login', { replace: true });
};

// src/page/Login
export default function Login() {
  const { token } = useUserState();

  return (
    <>
      {token && <Navigate to="/" replace={true} />}
      <BackGround>
        <S.Container>
          <LoginFormLayout />
        </S.Container>
      </BackGround>
    </>
  );
}

Form을 사용하는 LoginForm에는 Input Field를 만들 loginField와 submit후 수행할 loginCallback을 전달받는다.

Login Form 컴포넌트를 만들어줄 필요 없이, Field Data와 Submit Callback을 전달하기만 하면 되므로 선언적이다는 장점이 있다.

profile
지식은 누가 origin인지 중요하지 않다.

0개의 댓글