반응형

 

formik 소개


formik은 리액트에서 form을 다루는 코드들을 쉽게 작성할 수 있도록 도와주는 라이브러리입니다.

formik은 리액트에서 form을 다룰 때 3가지 까다로운(성가신) 점들을 해결해줍니다.


1. form의 state 관리 및 form의 안팎에서 value 접근
2. 벨리데이션(Validation)과 에러 메시지
3. submit 다루기

 

formik을 사용하면 form에서 사용하는 값들을 formik 내부에서 자체적으로 관리합니다. 고로 state를 따로 선언하고 관리할 필요가 없어집니다. 상태 값들을 formik 내부에서 관리하기 때문에 이 값을 얻기 위해서는 formik이 제공하는 api들을 이용해 함수를 호출하면 됩니다.
form과 관련하여 내부에서 관리하는 상태들로는 각 input field에 들어있는 사용자가 입력하는 value들, UI와 사용자의 접촉여부, 밸리데이션 통과 여부, submit 진행상태 등이 있습니다.
또한, 값을 입력해주는 setValues, 밸리데이션을 해주는 validateForm, 폼에 들어있는 값들을 초기화해주는 resetForm 등
편리한 함수들을 제공합니다. 뭔가 다양한 것들을 제공하는 것은 알겠는데 역시 예제를 봐야겠습니다.

 

 

재사용이 가능한 form UI component 만들기

 

먼저 도메인 지식이 관여하지 않는 순수 UI 컴포넌트를 만드려고합니다. 이렇게 만들면 어떤 도메인에서든 이 form 컴포넌트를 재사용할 수 있습니다.

 

일단 리액트 프로젝트를 준비하고 formik을 설치합니다.

npx create-react-app formik-ex
cd formik-ex

yarn add formik

 

먼저 form 컴포넌트를 만듭니다.

* src/components/form/CustomForm.jsx

import { Formik, Form } from "formik";

import Spinner from "../Spinner";

function CustomForm({
  initialValues,
  validationSchema,
  handleSubmit,
  children,
  style,
}) {
  const onSubmit = (values, actions) => {
    handleSubmit(values, actions)
      .then(() => {
        actions.resetForm({
          values: initialValues,
        });
      })
      .finally(() => {
        actions.setSubmitting(false);
      });
  };

  return (
    <Formik
      initialValues={initialValues}
      validationSchema={validationSchema}
      onSubmit={onSubmit}
    >
      {({ isSubmitting }) => {
        return (
          <Form style={style}>
            {isSubmitting && <Spinner />}
            {children}
          </Form>
        );
      }}
    </Formik>
  );
}

export default CustomForm;

 

initialValues는 form을 처음 렌더링할 때 미리 입력되어있는 초기값입니다.

validationSchema는 벨리데이션을 할 때 통과해야 하는 포맷을 정의해놓은 객체입니다.

onSubmit은 form에서 submit 이벤트가 발생할 때 formik에서 실행할 함수입니다.

submit handler는 props로 받도록 하고, onSubmit안에서 그것을 실행하도록 하였습니다. 또한 submit성공 시 form안에 있는 값들을 초기화시키고, submit 진행상태를 나타내는 submitting을 false로 변경하도록 하였습니다. submit이 발생하면 submitting이 true로 변경되는데 그것을 false로 되돌리는 것입니다.

저는 이것을 응답 대기중인 상태를 화면에 표시하기 위해 사용하였습니다. 코드에서 렌더링 하는 부분을 보면 Form안에 isSubmitting일 때 Spinner를 렌더링 하도록 하였습니다.

 

Spinner는 react-loader-spinner를 사용하겠습니다.

 

yarn add react-loader-spinner

 

* src/components/Spinner.jsx

import { Oval } from  'react-loader-spinner'

function Spinner() {
  return (
    <Oval
      height={80}
      width={80}
      color="#4fa94d"
      wrapperStyle={{}}
      wrapperClass=""
      visible={true}
      ariaLabel='oval-loading'
      secondaryColor="#4fa94d"
      strokeWidth={2}
      strokeWidthSecondary={2}
    />
  );
}

export default Spinner;

 

다음으로 text input field를 만들겠습니다.

 

* src/components/form/TextField.jsx

import { useField, useFormikContext } from "formik";

function TextField(props) {
  const [field, meta] = useField(props);
  const { isSubmitting } = useFormikContext();

  return (
    <div>
      <input
        type="text"
        {...field}
        {...props}
        onChange={event => {
          field.onChange(event);
        }}
        disabled={isSubmitting}
      />
      {meta.touched && meta.error && <div className="error">{meta.error}</div>}
    </div>
  );
}

export default TextField;

 

useField hook으로 받은 field를 input 태그에 넘겨서 input태그를 formik의 field로서 기능할 수 있도록 만듭니다.

그리고 props들도 넘겨줘서 해당 컴포넌트에 필요한 props들을 다 적용받을 수 있게 자유도를 줬습니다.

onChange에서는 field.onChange에 event를 넘겨 formik에서 필요한 로직을 실행하도록 하였고,

isSubmitting 상태일 때에는 disabled로 만들어 인풋을 제어할 수 없도록 하였습니다.

meta.touched는 인풋을 사용자가 한번이라도 만진 적이 있는지의 여부를 불리언 값으로 나타냅니다.

meta.error는 벨리데이션에 통과하지 못한 상태를 불리언 값으로 나타냅니다.

그래서 사용자가 만진 적이 있는 인풋인데 벨리데이션을 통과하지 못하면 에러 메시지를 표시하도록 하였습니다.

 

formik으로 만든 공통 컴포넌트들을 import 하기 쉽도록 한곳에 모아 관리하겠습니다.

 

* src/components/form/index.jsx

import CustomForm from "./CustomForm";
import TextField from "./TextField";

export { CustomForm, TextField };


이제 지금까지 만든 컴포넌트들을 어떻게 재사용할 수 있는지 확인하기 위해 다음 스탭으로 넘어가겠습니다.

 

 

 

 

 

 

회원가입 폼 만들기

 

구체적인 도메인 지식이 있는 요구사항이 들어간 폼을 만들겠습니다. 회원가입 폼, 로그인 폼, 상품 구매 폼, 게시글 작성 폼 등등 수없이 많은 구체적으로 정의할 수 있는 요구사항들이 있습니다. 이것들은 모두 앞서 만든 재사용 가능한 컴포넌트들을 불러와 쉽게 구현할 수 있습니다.

 

지금은 회원가입 폼을 예제로 같이 만들어보려고 합니다.

 

요구사항 정의

이메일: 필수값, 이메일 형식
비밀번호: 필수값, 8자 이상, 영문/숫자만 가능
비밀번호 확인: 필수값, 비밀번호 일치 여부 판단

 

 

벨리데이션 스키마 만들기

 

yup 소개

 

yup은 리액트에서만 사용가능한 라이브러리는 아니고 자바스크립트를 이용하는 라이브러리입니다.
밸리데이션을 위해 거쳐야하는 테스트들과 통과 실패 시 보여줄 에러 메시지를 스키마로 정의할 수 있습니다.
빌더 패턴을 사용하기 때문에 아주 가독성 좋은 간결한 코드를 쓸 수 있습니다.

 

yup을 설치합니다.

yarn add yup

 

yup을 이용해 만든 위 요구사항이 반영된 벨리데이션 스키마 코드는 다음과 같습니다.

const validationSchema = Yup.object({
    email: Yup.string()
      .required("이메일을 입력해주세요.")
      .matches(/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i, "올바른 이메일 형식이 아닙니다."),
    password: Yup.string()
      .required("비밀번호를 입력해주세요.")
      .matches(/^[0-9a-zA-Z]{8,}$/, "비밀번호는 영문/숫자만 가능, 8자 이상입니다."),
    passwordVerified: Yup.string()
      .required("비밀번호를 확인해주세요.")
      .oneOf([Yup.ref("password")], "비밀번호가 일치하지 않습니다."),
});

 

 

회원가입 폼 컴포넌트 만들기


지금까지 만든 것들을 이용해 회원가입 폼 컴포넌트를 만들어봅니다.

* src/signUp/Form.jsx

import { useMemo } from "react";
import * as Yup from "yup";

import { CustomForm, TextField } from "../components/form";

function Form() {
  const initialValues = useMemo(() => {
    return {
      email: "",
      password: "",
      passwordVerified: "",
    };
  }, []);
  const validationSchema = Yup.object({
    email: Yup.string()
      .required("이메일을 입력해주세요.")
      .matches(
        /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i,
        "올바른 이메일 형식이 아닙니다."
      ),
    password: Yup.string()
      .required("비밀번호를 입력해주세요.")
      .matches(
        /^[0-9a-zA-Z]{8,}$/,
        "비밀번호는 영문/숫자만 가능, 8자 이상입니다."
      ),
    passwordVerified: Yup.string()
      .required("비밀번호를 확인해주세요.")
      .oneOf([Yup.ref("password")], "비밀번호가 일치하지 않습니다."),
  });

  const handleSubmit = async (values, actions) => {
    alert("회원가입 submit 완료");
  };

  return (
    <CustomForm
      initialValues={initialValues}
      validationSchema={validationSchema}
      handleSubmit={handleSubmit}
      style={{ display: "flex", flexDirection: "column" }}
    >
      <h2>회원가입</h2>
      <div>
        <label>
          이메일:
          <TextField type="email" name="email" label="이메일" />
        </label>
      </div>
      <div>
        <label>
          비밀번호:
          <TextField type="password" name="password" label="비밀번호" />
        </label>
      </div>
      <div>
        <label>
          비밀번호 확인:
          <TextField
            type="password"
            name="passwordVerified"
            label="비밀번호 확인"
          />
        </label>
      </div>
      <button type="submit" style={{ width: 50 }}>
        계속
      </button>
    </CustomForm>
  );
}

export default Form;

 

이것과 똑같은 방식으로 재사용 가능한 컴포넌트들을 이용하여 로그인 폼, 게시글 작성 폼 등등 만들고 싶은 폼들을 만들 수 있습니다.

 

마지막으로 App.js에서 회원가입폼을 렌더링 하도록 만듭니다.

 

* App.js

import SignUpForm from "./signUp/Form";

function App() {
  return (
    <div className="App">
      <SignUpForm />
    </div>
  );
}

export default App;

 

 

 

 

 

 

동작여부 확인해보기

 

개발서버를 띄워서 잘 작동하는지 직접 테스트해보겠습니다.
터미널에서 yarn start를 입력하여 개발서버를 실행합니다.

localhost:3000에 접속했을 때, 우리가 만든 화면이 정상적으로 보여야 합니다.

 

이메일 인풋을 한번 클릭한 다음, 바깥 여백 영역을 클릭해봅니다. 그러면 벨리데이션이 작동하여 이메일 인풋에만 다음과 같이 에러 메시지가 등장합니다.

 


등록 버튼을 누르면 벨리데이션 결과에 따른 에러 메시지가 등장합니다.

 


잘못된 이메일 형식과 비밀번호를 입력하면 상황에 맞는 다른 에러 메시지가 나옵니다.

 


이번엔 올바른 형식으로 입력하고 등록 버튼을 누릅니다. 그러면 성공했다는 알랏이 뜹니다.

 

 

 

 

Advance

 

지금까지 만든 폼에 더 예쁜 디자인을 적용하기 위해 사용해볼만한 UI 라이브러리들이 있습니다.

 


Material UI


material design을 적용한 리액트 UI 라이브러리입니다.
주로 input, button 등을 디자인하는데 매우 유용합니다.

 

MUI Text field

 

MUI Button

 

https://cocoder16.tistory.com/81

 

리액트 UI 디자인 고민 Material-UI로 해결하기

디자인 고민을 덜어낼 수 있는 material design 소개 Material design은 구글이 개발한 디자인 언어입니다. 그냥 디자인이 아니라 디자인 언어라고 소개하는 점이 흥미롭습니다. 구글이 Material design을 만

cocoder16.tistory.com

 

https://mui.com/

 

MUI: The React component library you always wanted

MUI provides a simple, customizable, and accessible library of React components. Follow your own design system, or start with Material Design.

mui.com

 

 

sweetalert2

submit 이후 받은 응답에 따라 띄워줄 수 있는 alert을 예쁘게 꾸며줄 수 있는 라이브러리입니다.

 

sweetalert example

 

https://sweetalert2.github.io/

 

SweetAlert2

A beautiful, responsive, customizable and accessible (WAI-ARIA) replacement for JavaScript's popup boxes

sweetalert2.github.io

 

 

axios

 

이 포스팅 예제에서는 form submit할 때 async 함수를 호출해서 프로미스를 리턴하는 것으로 끝냈지만, 대부분 실제 애플리케이션에서는 서버에 데이터를 전송하는 형태입니다. react에서 form 제출 시 서버와 통신을 하기 위해서 ajax를 사용합니다. axios는 ajax를 쉽게 사용할 수 있는 라이브러리라서 소개합니다.

 

https://github.com/axios/axios

 

GitHub - axios/axios: Promise based HTTP client for the browser and node.js

Promise based HTTP client for the browser and node.js - GitHub - axios/axios: Promise based HTTP client for the browser and node.js

github.com

 

 

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기