티스토리 뷰

반응형

[nextjs/ts/react] 로그인 페이지 [완성] -기본 구성 설명-

 

 

 

-react-hook-form을 활용하여 간편하게 form 예외처리 및 데이터 처리 구현

-Input / Button 등을 컴포넌트 처리하여 깔끔하게 구현

-useMutation hook을 만들어서 api 데이터 처리를 명료하게함

-useEffect를 활용하여, useMutation을 통하여 결과값을 비동기적으로 받아서 처리함

-nextjs(서버사이드) 수준에서 nextjs의 api기능을 활용하여 서버/클라이언트 일원화함

-withHandler라는 함수를 통하여, middleware를 구현하여 각 예외처리를 구현

-tailwindCSS를 활용하여 직관적으로 UI를 구현

 

추 후 추가

-cookie token을 활용한 session 처리(로그인 유지)

-전체적인 컴포넌트 화

 

#Enter.tsx -로그인 페이지 본페이지

import Button from "@components/Button";
import Input from "@components/Input";
import useMutation from "@libs/client/userMutation";
import type { NextPage } from "next";
import { useRouter } from "next/router";
import { useEffect } from "react";
import { useForm } from "react-hook-form";

const Enter: NextPage = () => {
  const router = useRouter();

  const [enter, { loading, data, error }] = useMutation("/api/users/enter");

  useEffect(() => {
    console.log(data?.ok);
    if (data?.ok) {
      router.push("/");
    }
  }, [data, router]);

  const goMainPage = () => {
    router.push("/");
  };

  interface EnterForm {
    email?: string;
    password?: string;
  }

  const onValid = (validForm: EnterForm) => {
    console.log("wow");
    if (loading) return;
    enter(validForm);
  };

  const { register, handleSubmit, reset, watch } = useForm<EnterForm>();
  console.log(watch());
  return (
    <div className="flex flex-col items-center rounded-3xl bg-white pb-10">
      <div className=" p-10 text-2xl font-bold">
        <span>@SamsungEngineering CMS</span>
      </div>
      <form onSubmit={handleSubmit(onValid)}>
        <Input
          register={register("email", {
            required: true,
          })}
          name="아이디를 입력해주세요"
          label="아이디"
          type="text"
          required
        />
        <Input
          register={register("password", {
            required: true,
          })}
          name="패스워드를 입력해주세요"
          label="패스워드"
          type="password"
          required
        />
        <div className="mt-5 flex items-center   justify-center ">
          <Button text={loading ? "Loading" : "LOGIN"} />
        </div>
        {console.log(data?.error)}
        {data?.error != undefined ? (
          <p className="mt-1 flex items-center justify-center text-xs font-bold text-red-700 ">
            {data?.error}
          </p>
        ) : (
          ""
        )}
      </form>
      <div className="mt-8 flex flex-col items-center justify-center">
        <span className="text-sm">
          문의가 있는 경우, 아래의 이메일로 연락부탁드립니다.
        </span>
        <span className="text-sm">test@gmail.com</span>
      </div>
    </div>
  );
};

export default Enter;

 

#api-enter

import client from "@libs/server/client";
import withHandler, { ResponseType } from "@libs/server/withHandler";
import { NextApiRequest, NextApiResponse } from "next";

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  let user;
  const { email, password } = req.body;
  if (email == "gofogo" && password == "1") {
    console.log(user);
    return res.status(200).json({ ok: true });
  } else {
    return res
      .status(404)
      .json({ ok: false, error: "계정정보가 일치하지 않습니다" });
  }
}
export default withHandler("POST", handler);

 

#withHandler

import { NextApiRequest, NextApiResponse } from "next";

export interface ResponseType {
  ok: boolean;
  error?: string;
  [key: string]: any;
}

export default function withHandler(
  method: "GET" | "POST" | "DELETE",
  fn: (req: NextApiRequest, res: NextApiResponse<ResponseType>) => void
) {
  return async function (
    req: NextApiRequest,
    res: NextApiResponse<ResponseType>
  ) {
    if (req.method !== method) {
      console.log("405");
      return res
        .status(405)
        .json({ ok: false, error: "정상적인 접근 경로가 아닙니다." });
    }
    try {
      console.log("계정을 체크한다.");
      await fn(req, res);
    } catch (error) {
      console.log(error);
      return res
        .status(500)
        .json({ ok: false, error: "서버에 문제가 있습니다." });
    }
  };
}

 

#Button Component

interface ButtonProps {
  text: string;
}

export default function Button({ text }: ButtonProps) {
  return (
    <button className=" h-10 w-64 rounded-sm bg-gray-300 text-sm font-medium text-white hover:bg-red-200 ">
      {text}
    </button>
  );
}

 

#Input Component

import { UseFormRegisterReturn } from "react-hook-form";

interface InputProps {
  label: string;
  name: string;
  type: string;
  register: UseFormRegisterReturn;
  required: boolean;
}

export default function Input({
  label,
  name,
  register,
  type,
  required,
}: InputProps) {
  return (
    <div className="flex flex-col  p-2 pt-0">
      <span className="p-1 text-xs font-extrabold text-gray-500">{label}</span>
      <input
        required={required}
        {...register}
        type={type}
        className=" peer h-8 border-2"
      ></input>
      <span className="hidden text-xs font-medium text-red-600 peer-invalid:block">
        {name}
      </span>
    </div>
  );
}

 

#react #nextjs #middleware #tailwind #react-hook

반응형
댓글
반응형