nextjs+typescript 기반 CMS 개발 프로젝트
[nextjs/ts/react] 로그인 페이지 [완성] -기본 구성 설명-
개발자 고포고
2022. 3. 14. 15:11
반응형
[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
반응형