Skip to content
Migrating from NextAuth.js v4? Read our migration guide.
가이드PagesCustom Signin

커스텀 로그인 페이지

커스텀 로그인 페이지를 추가하려면 Auth.js 설정에서 pages 객체에 페이지 경로를 정의해야 합니다. 여기서 정의한 경로에 실제로 라우트/페이지가 존재하는지 확인하세요!

추가로, auth.ts 설정에서 정의한 내용을 기반으로 동적으로 올바른 버튼을 렌더링하려면 provider.idprovider.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을 인수 없이 호출하면 커스텀 로그인 페이지가 나타납니다.

Custom Sign-in Page
Auth.js © Balázs Orbán and Team - 2025