커스텀 로그인 페이지
커스텀 로그인 페이지를 추가하려면 Auth.js 설정에서 pages
객체에 페이지 경로를 정의해야 합니다. 여기서 정의한 경로에 실제로 라우트/페이지가 존재하는지 확인하세요!
추가로, auth.ts
설정에서 정의한 내용을 기반으로 동적으로 올바른 버튼을 렌더링하려면 provider.id
와 provider.name
의 맵을 내보내야 합니다. 프로바이더를 providers
배열에 함수로 전달하거나, 함수를 호출한 결과로 전달할 수 있기 때문에, 이 예제의 providerMap
은 두 경우를 모두 처리합니다.
./auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
import Credentials from "next-auth/providers/credentials"
import type { Provider } from "next-auth/providers"
const providers: Provider[] = [
Credentials({
credentials: { password: { label: "Password", type: "password" } },
authorize(c) {
if (c.password !== "password") return null
return {
id: "test",
name: "Test User",
email: "test@example.com",
}
},
}),
GitHub,
]
export const providerMap = providers
.map((provider) => {
if (typeof provider === "function") {
const providerData = provider()
return { id: providerData.id, name: providerData.name }
} else {
return { id: provider.id, name: provider.name }
}
})
.filter((provider) => provider.id !== "credentials")
export const { handlers, auth, signIn, signOut } = NextAuth({
providers,
pages: {
signIn: "/signin",
},
})
이제 커스텀 로그인 페이지를 직접 만들 수 있습니다.
app/signin/page.tsx
import { redirect } from "next/navigation"
import { signIn, auth, providerMap } from "@/auth.ts"
import { AuthError } from "next-auth"
export default async function SignInPage(props: {
searchParams: { callbackUrl: string | undefined }
}) {
return (
<div className="flex flex-col gap-2">
<form
action={async (formData) => {
"use server"
try {
await signIn("credentials", formData)
} catch (error) {
if (error instanceof AuthError) {
return redirect(`${SIGNIN_ERROR_URL}?error=${error.type}`)
}
throw error
}
}}
>
<label htmlFor="email">
Email
<input name="email" id="email" />
</label>
<label htmlFor="password">
Password
<input name="password" id="password" />
</label>
<input type="submit" value="Sign In" />
</form>
{Object.values(providerMap).map((provider) => (
<form
action={async () => {
"use server"
try {
await signIn(provider.id, {
redirectTo: props.searchParams?.callbackUrl ?? "",
})
} catch (error) {
// 로그인은 여러 이유로 실패할 수 있습니다. 예를 들어 사용자가 존재하지 않거나,
// 사용자가 올바른 역할을 가지고 있지 않은 경우 등이 있습니다.
// 일부 경우에는 커스텀 에러 페이지로 리디렉션할 수 있습니다.
if (error instanceof AuthError) {
return redirect(`${SIGNIN_ERROR_URL}?error=${error.type}`)
}
// 그렇지 않으면 리디렉션이 발생할 경우 Next.js가 처리할 수 있도록
// 에러를 다시 던지고 Next.js가 처리하도록 합니다.
// 문서:
// https://nextjs.org/docs/app/api-reference/functions/redirect#server-component
throw error
}
}}
>
<button type="submit">
<span>Sign in with {provider.name}</span>
</button>
</form>
))}
</div>
)
}
그런 다음 애플리케이션 어디에서든 signIn
을 인수 없이 호출하면 커스텀 로그인 페이지가 나타납니다.