Next.js 에서 app/dir을 이용한 커스텀 회원가입을 구현한걸 기록으로 남긴다.
next.js, 몽고DB, next-auth를 이용해 만들어봤다.
1. next.js, 몽고DB를 설치해준다.
//next.js 설치
npx create-next-app
//몽고db 설치
npm i mongodb
2-1 회원가입 페이지
import SignUp from "@/app/components/Auth/SignUp";
import { Metadata } from "next";
//import { authOptions } from "@/app/lib/auth";
//import { getServerSession } from "next-auth";
//import { redirect } from "next/navigation";
export const metadata: Metadata = {
title: "회원가입",
description: "회원가입을 위한 페이지 입니다.",
};
export default async function SignUpPage() {
/*
아래의 코드는 나중에 next-auth를 이용해 페이지를 보호할때 사용.
const session = await getServerSession(authOptions);
if (session) {
redirect("/");
}
*/
return <SignUp />;
}
2-2 회원가입 컴포넌트.
"use client";
import { User } from "@/type/type";
import Link from "next/link";
import { useState } from "react";
import { checkUser } from "./use/check-user";
import { useRouter } from "next/navigation";
export default function SignUp() {
const router = useRouter();
const [userValue, setUserValue] = useState<User>({
email: "",
password: "",
name: "",
});
const [checkPassword, setCheckPassword] = useState("");
const [checkOutput, setCheckOutput] = useState("");
function changeHandler(e: React.ChangeEvent<HTMLInputElement>) {
setUserValue({
...userValue,
[e.target.name]: e.target.value,
});
}
function checkPasswordHandler(e: React.ChangeEvent<HTMLInputElement>) {
setCheckPassword(e.target.value);
}
async function signUpHandler(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setCheckOutput("가입중...");
//유저가 입력한 정보가 유효한지 확인하는 checkUser()
const data = checkUser(userValue, checkPassword);
if (!data.isValid) {
return setCheckOutput(data.message);
}
//회원정보를 DB에 저장하기 위해 fetch해준다.
const response = await fetch("/api/signup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(userValue),
});
const result = await response.json();
if (result.status === 201) {
router.push("/");
return;
//가입완료
} else {
//가입실패
setCheckOutput(result.message);
return;
}
}
return (
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
회원가입
</h2>
</div>
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form onSubmit={signUpHandler} className="space-y-6">
<div>
<div className="flex items-center justify-between">
<label
htmlFor="email"
className="block text-sm font-medium leading-6 text-gray-900"
>
이메일
</label>
<div className="text-sm">
<button className="font-semibold text-indigo-600 hover:text-indigo-500">
이메일 중복 확인
</button>
</div>
</div>
<div className="mt-2">
<input
value={userValue.email}
onChange={changeHandler}
id="email"
name="email"
type="email"
autoComplete="email"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<label
htmlFor="name"
className="block text-sm font-medium leading-6 text-gray-900"
>
닉네임
</label>
<div className="mt-2">
<input
value={userValue.name}
onChange={changeHandler}
id="name"
name="name"
type="text"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label
htmlFor="password"
className="block text-sm font-medium leading-6 text-gray-900"
>
비밀번호
</label>
</div>
<div className="mt-2">
<input
value={userValue.password}
onChange={changeHandler}
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label
htmlFor="password2"
className="block text-sm font-medium leading-6 text-gray-900"
>
비밀번호 확인
</label>
</div>
<div className="mt-2">
<input
value={checkPassword}
onChange={checkPasswordHandler}
id="password2"
name="password2"
type="password"
autoComplete="current-password"
required
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
{checkOutput && (
<p className="text-center text-red-500 font-bold">
{checkOutput}
</p>
)}
</div>
<div>
<button
type="submit"
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
회원가입
</button>
</div>
</form>
<p className="mt-10 text-center text-sm text-gray-600">
<Link
href="/auth/in"
className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
>
로그인
</Link>
</p>
</div>
</div>
);
}
3. app/api/signup/route.ts를 만들고 코드를 짜준다.
import { MongoDbSignUp } from "@/app/lib/signUp";
import { User } from "@/app/type/type";
import { NextResponse } from "next/server";
export async function POST(req: Request) {
try {
const data: User = await req.json();
const dbResponse = await MongoDbSignUp(data);
if (dbResponse.status !== 201) {
throw new Error(dbResponse.message);
}
return NextResponse.json({ message: "가입 성공", status: 201 });
} catch (e) {
if (e instanceof Error) {
return NextResponse.json({ message: e.message });
} else {
return NextResponse.json({ message: String(e) });
}
}
}
- req에 SignUp 컴포넌트에서 fetch한 데이터가 들어가있다.
- MongodbSignUp함수를 만들건데 이곳에 받은 데이터를보내준다.
3. app/lib/signUp.ts 의 MongoDbSignUp
import { User } from "../type/type";
import { hashPassword } from "./auth";
import { collectionUsers } from "./collectionName";
import { checkEmail, checkName, connectDatabase } from "./db";
export async function MongoDbSignUp(req: User) {
const client = await connectDatabase();
try {
const { email, name, password } = req;
if (
email.trim().length === 0 ||
!email.includes("@") ||
name.trim().length === 0 ||
!name ||
!password ||
password.trim().length === 0
) {
throw new Error("모든 정보를 채워주세요.");
}
const db = client.db();
const checkedEmail = await checkEmail(email);
const checkedName = await checkName(name);
if (checkedEmail) {
throw new Error(checkedEmail.message);
}
if (checkedName) {
throw new Error(checkedName.message);
}
const hashedPassword = await hashPassword(password);
await db.collection(collectionUsers).insertOne({
email: email,
name: name,
password: hashedPassword,
});
return { status: 201, message: "가입 성공" };
} catch (e) {
if (e instanceof Error) {
return { message: e.message };
} else {
return { message: String(e) };
}
} finally {
client.close();
}
}
-connectDatabase()와 hashPassword()는 이전 게시글에 코드가있으며 각각 데이터베이스에 연결하고 비밀번호를 암호화 해주는 함수다.
이렇게 해주면 몽고DB에 데이터가 저장된다.
'여러가지 정보 > 팁' 카테고리의 다른 글
Next.js 에서 커스텀 로그인 구현하기 (0) | 2023.06.19 |
---|---|
타입스크립트 (0) | 2023.05.29 |
Next.js에서 - 몽고DB사용하기 (1) | 2023.05.25 |
node 버전 관리 (0) | 2023.04.26 |
네틀리파이(Netlify)로 배포 후 F5(새로고침)시 not Found 에러 해결방법 (0) | 2023.03.24 |