편리한 input 다루기 - React Hook Form

column clash·2021년 7월 26일
1

yarn add react-hook-form@6 (현재 7버젼까지 나왔음, 개인적으로는 아직 6이 편함)

밑에는 회원가입 폼이지만, 대부분의 input 요소에 사용하면 좋은 라이브러리.
select 느 radio 도 react-hook-form 으로 될 뿐 아니라 css library 도 잘 적용됨.

import React, { useState, useRef } from "react";
import { useForm } from "react-hook-form";
import axios from "axios";

function register() {
  const { register, watch, errors, handleSubmit } = useForm(); // useForm({ mode: "onChange" });
  // console.log(watch("email"));
  const userpwd = useRef();
  userpwd.current = watch("userpwd");

  const [loading, setLoading] = useState(false);

  const onSumit = async (data) => {
    setLoading(true);
    console.log(data);
    const { email, name, userpwd, phone } = data;
    try {
      const response = await axios.post("/api/user/user", {
        email,
        name,
        userpwd,
        phone,
      });
      console.log(response);
    } catch (error) {
      alert(error.response.data);
    }
    setLoading(false);
  };
  return (
    <div style={{ width: "1024px", margin: "50px auto" }}>
      <form onSubmit={handleSubmit(onSumit)}>
        <label>Name</label>
        <input
          name="name"
          type="text"
          placeholder="EX) 홍길동"
          ref={register({
            required: true,
            pattern: /^[가-힣]{2,7}$/,
          })}
        />
        {errors.name && errors.name.type === "required" && (
          <p>이름을 입력해주세요.</p>
        )}
        {errors.name && errors.name.type === "pattern" && (
          <p>이름 형식에 맞지 않습니다. 한글로 올바르게 이름을 입력해주세요.</p>
        )}
        <label>Email</label>
        <input
          name="email"
          type="email"
          placeholder="EX ) mindcrying@naver.com"
          ref={register({
            required: true,
            pattern:
              /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i,
          })}
        />
        {errors.email && <p>이메일을 입력해주세요.</p>}
        <label>Phone</label>
        <input
          name="phone"
          type="number"
          placeholder="연락처는 숫자로만 입력해주세요. ex) 01012345678"
          ref={register({
            required: true,
            pattern: /(^02.{0}|^01.{1}|[0-9]{3})([0-9]+)([0-9]{4})/g,
          })}
        />
        {errors.phone && (
          <p>연락처는 01012345678 처럼 숫자로만 입력해주세요.</p>
        )}
        <label>Password</label>
        <input
          name="userpwd"
          type="password"
          ref={register({
            required: true,
            pattern:
              /(?=.*\d{1,50})(?=.*[~`!@#$%\^&*()-+=]{1,50})(?=.*[a-zA-Z]{2,50}).{8,16}$/,
          })}
        />
        {errors.userpwd && errors.userpwd.type === "required" && (
          <p>비밀번호를 입력해주세요.</p>
        )}
        {errors.userpwd && errors.userpwd.type === "pattern" && (
          <p>
            비밀번호는 8~16자로 영문 소문자, 숫자, 특수기호를 조합해서
            사용하세요.
          </p>
        )}

        <label>Password Confirm</label>
        <input
          name="userpwd_confirm"
          type="password"
          ref={register({
            required: true,
            validate: (value) => value === userpwd.current,
          })}
        />
        {errors.userpwd_confirm &&
          errors.userpwd_confirm.type === "required" && (
            <p>비밀번호 확인을 해주세요.</p>
          )}
        {errors.userpwd_confirm &&
          errors.userpwd_confirm.type === "validate" && (
            <p>비밀번호가 일치하지 않습니다.</p>
          )}
        <input type="submit" disabled={loading} />
      </form>
    </div>
  );
}

export default register;

인풋으로 보통은 onChange 이벤트로 통제를 하거나,
useInput 이라는 커스텀 훅을 한번 만들어두고, 사용하는 경우가 많았으나,
아무래도 react-hook-form 으로 사용한 후에는,
그런 번거러운 짓을 굳이 할필요가 없다고 할 수 있다.

로그인, 게시판 등을 만들 때도 유용하고 그외에도 유용하다고 할 수 있겠다.

리액트를 사용한다면 적극 사용할만한 매우 매우 좋은 라이브러리! (성능도 좋다.)

https://react-hook-form.com/

아래는 현재 next-auth 사용 중, next-auth 는 소셜로그인이 매우 편리하게 되있는
반면에, 직접 회원가입을 받는 부분은 모두 손수 작업을 해야해서
시간에 쫒겨서 비밀번호만 crypto 로 최대한 암호화 해서,
만들어둔 api 이다. 최적화된 코드는 아니니까 혹시나 이 포스트를 보신 분 중
그대로 갖다쓰려는 분들은 좀 더 나은 코드를 구글링 하는 것을 추천한다
(이 글에 이 내용을 넣는 것은 다음에 내가 좀 더 참고할 목적으로
적어둔다.)

현재 몽구스의 모델에, email 은 unique 로 설정되어있는데,
이미 사용자가 존재할 경우, 11000 에러메세지를 던져주는데 그걸 저렇게
클라이언트에 보내서, alert 메세지를 던져주었다.

import createHandler from "@/middleware";
import Post from "@/models/post";
import User from "@/models/user";
import crypto from "crypto";

const handler = createHandler();

handler.post(async (req, res) => {  
  crypto.randomBytes(64, (err, buf) => {
    crypto.pbkdf2(
      req.body.userpwd,
      buf.toString("base64"),
      100000,
      64,
      "sha512",
      (err, key) => {
        req.body.userpwd = key.toString("base64");
        req.body.salt = buf.toString("base64");
        var users = new User(req.body);
        users
          .save()
          .then((user) => {
            return res.status(200).json({ data: users });
          })
          .catch((error) => {
            error.code === 11000
              ? res
                  .status(400)
                  .send(
                    "이미 사용자가 있는 이메일입니다. 메일은 고유해야합니다. 다른 메일을 등록해주세요."
                  )
              : res
                  .status(400)
                  .send(
                    "다시 한번 시도해주세요. 반복해서 이상이 생길 시 카톡으로 알려주세요."
                  );
          });
      }
    );
  });
});

현재, 클라이어트에서 정규식 등, 점검을 하고,
백단에서는 이메일이 존재하는 지 여부만 체크하고, 저장을 하게 되어있다.

보통의 실무에서는 클라이언트-백단 모두 검증로직을 돌리는 경우가 많지만, 서브프로젝트라
간단하게 암호화만 신경쓰고 패스!

모델

import mongoose, { Schema } from "mongoose";

const MODEL_NAME = "User";

const schema = new Schema(
  {
    email: {
      type: String,
      required: true,
      unique: [true, "email must be unique"],
    },
    userpwd: String,
    phone: String,
    salt: String,
    name: String,
    posts: [
      {
        type: mongoose.Schema.Types.ObjectId,
        ref: "Post",
      },
    ],
    comments: [
      {
        type: mongoose.Schema.Types.ObjectId,
        ref: "Comment",
      },
    ],
    image: String,
    createdAt: Date,
    updateAt: Date,
  },
  {
    timestamps: true,
  }
);

export default mongoose.models[MODEL_NAME] ||
  mongoose.model(MODEL_NAME, schema, "users");
profile
풀스택 개발 중...

0개의 댓글