로그인 로직을 구현해 보자.
api/[...nextauth]/route.js
파일을 생성해 준다.import NextAuth from "next-auth/next";
import { PrismaClient } from "@prisma/client";
import { PrismaAdapter } from "@auth/prisma-adapter";
import CredentialsProvider from "next-auth/providers/credentials";
const prisma = new PrismaClient();
export const authOptions = {
adapter: PrismaAdapter(prisma),
providers: [],
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Next.js api route에서는 메서드 명으로 접근하기 때문에 위와 같이 export를 해준다.
이제 이메일과 비밀번호를 이용해, 로그인 로직을 구현하기 위해 providers
안에 CredentialsProvider
를 추가한다.
CredentialsProvider({
credentials: {
email: { label: "email", type: "text", placeholder: "email" },
password: { label: "password ", type: "password" },
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
throw new Error("이메일 혹은 비밀번호가 비어있습니다.");
}
const user = await prisma.user.findUnique({
where: {
email: credentials.email,
},
});
if (!user) {
throw new Error("이메일 혹은 비밀번호를 다시 확인해 주세요.");
}
const passwordsMatch = await bcrypt.compare(
credentials.password,
user.password!
);
if (!passwordsMatch) {
throw new Error("이메일 혹은 비밀번호를 다시 확인해 주세요.");
}
return user;
},
}),
사용하려는 필드를 CredentialsProvider
내부의 credentials
안에 생성해 준다. 내 경우에는 이메일과 비밀번호 필드만 생성했다.
credentials: {
email: { label: "email", type: "text", placeholder: "email" },
password: { label: "password ", type: "password" },
},
다음은 실제 로그인 로직이다.
먼저 클라이언트 단에서 확인을 했지만, 추가로 서버 측에서도 이메일과 비밀번호가 모두 존재하는지 확인 후, 이메일을 기반으로 존재하는 유저인지 확인 후, 비밀번호 일치를 확인하는 로직 순서다.
이전에 회원가입 로직에서 암호화하여 저장했기 때문에, 디코드하여 비교 후, 일치 시 user를 반환해 준다.
session: {
strategy: "jwt",
},
callbacks: {
async jwt({ token, user, session, account }) {
// 로그인 하면서 토큰의 user에 id 추가
if (user) {
return {
...token,
id: user.id,
};
}
return token;
},
async session({ session, token, user }) {
// session에 토큰에 저장한 id를 포함해서 반환하여 session에서 접근 가능하도록
return {
...session,
user: {
...session.user,
id: token.id,
},
};
},
},
로그인 로직은 authorize
=> jwt callback
=> session callback
순서로 실행된다.
기본 session의 user는 이메일, 사진, 이메일의 정보만 저장하고 있다.
여기에 다른 유저의 정보를 가져오고 싶다면, authorize
에서 반환한 user
를 jwt callback
단계에서 user의 id
를 추가하여 session callback
으로 전달하여 클라이언트 단에서 user의 id에 접근할 수 있도록 할 수 있다.
로그아웃은 제공하는 훅을 사용하면 쉽게 구현할 수 있다.
로그인이 되지 않은 상태로 접근하지 않아야 하는 경우, useSession의 onUnauthenticated
를 사용하면 초기 로드 상태에서 session이 없는 경우, 즉 로그인이 되지 않은 경우를 처리할 수 있다.
const { data: session } = useSession({
required: true,
onUnauthenticated() {
router.push("/login");
},
});