Skip to content
Migrating from NextAuth.js v4? Read our migration guide.

Credentials

외부 인증 메커니즘을 설정하거나 전통적인 사용자 이름/이메일과 비밀번호 방식을 사용하려면 Credentials 프로바이더를 사용할 수 있습니다. 이 프로바이더는 로그인 폼에 입력된 자격 증명(예: 사용자 이름/비밀번호, 이에 국한되지 않음)을 여러분의 인증 서비스로 전달하도록 설계되었습니다.

⚠️

사용자 이름과 비밀번호가 웹 애플리케이션에서 사용자를 인증하고 권한을 부여하는 주요 메커니즘으로 사용되던 시대는 지났습니다. 따라서 가능하다면, OAuth 프로바이더, 이메일 매직 링크, 또는 WebAuthn (패스키)와 같은 더 현대적이고 안전한 인증 메커니즘을 사용하는 것을 권장합니다.

그러나, 여러분의 애플리케이션과 사용 사례에 적합한 방식을 지원하고자 유연성을 유지할 예정이므로, 이 프로바이더를 제거할 계획은 없습니다.

💡

기본적으로, Credentials 프로바이더는 데이터베이스에 데이터를 저장하지 않습니다. 그러나 여전히 데이터베이스에 데이터를 생성하고 저장할 수 있으며, 비밀번호 암호화, 요율 제한 추가, 비밀번호 재설정 기능 추가 등 필요한 로직을 제공하기만 하면 됩니다.

Credentials Provider

먼저, Auth.js 설정 파일에서 Credentials 프로바이더를 초기화해 보겠습니다. 프로바이더를 임포트하고 providers 배열에 추가해야 합니다.

./auth.ts
import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials"
// 평문 비밀번호 문자열을 다루는 로직을 직접 구현해야 합니다. 주의하세요!
import { saltAndHashPassword } from "@/utils/password"
 
export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Credentials({
      // `credentials` 객체에 키를 추가하여 제출할 필드를 지정할 수 있습니다.
      // 예: 도메인, 사용자 이름, 비밀번호, 2FA 토큰 등
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        let user = null
 
        // 비밀번호를 솔트하고 해시하는 로직
        const pwHash = saltAndHashPassword(credentials.password)
 
        // 사용자가 존재하는지 확인하는 로직
        user = await getUserFromDb(credentials.email, pwHash)
 
        if (!user) {
          // 사용자를 찾을 수 없음. 첫 로그인 시도
          // 필요에 따라 여기서 사용자 등록을 할 수도 있습니다.
          throw new Error("Invalid credentials.")
        }
 
        // 사용자 프로필 데이터와 함께 사용자 객체 반환
        return user
      },
    }),
  ],
})

TypeScript를 사용한다면, authorize 콜백의 응답과 일치하도록 User 인터페이스를 확장할 수 있습니다. 이렇게 하면 jwt와 같은 다른 콜백에서 사용자를 읽을 때 타입이 정확히 일치합니다.

로그인 폼

마지막으로 간단한 로그인 폼을 만들어 보겠습니다.

./components/sign-in.tsx
import { signIn } from "@/auth"
 
export function SignIn() {
  return (
    <form
      action={async (formData) => {
        "use server"
        await signIn("credentials", formData)
      }}
    >
      <label>
        이메일
        <input name="email" type="email" />
      </label>
      <label>
        비밀번호
        <input name="password" type="password" />
      </label>
      <button>로그인</button>
    </form>
  )
}

자격 증명 검증

항상 서버 측에서 자격 증명을 검증해야 합니다. 예를 들어 Zod와 같은 스키마 검증 라이브러리를 활용할 수 있습니다.

npm install zod

다음으로, auth.ts 설정 파일에서 스키마와 파싱을 설정하고, Credentials 프로바이더의 authorize 콜백을 사용합니다.

./lib/zod.ts
import { object, string } from "zod"
 
export const signInSchema = object({
  email: string({ required_error: "이메일은 필수입니다." })
    .min(1, "이메일은 필수입니다.")
    .email("유효하지 않은 이메일입니다."),
  password: string({ required_error: "비밀번호는 필수입니다." })
    .min(1, "비밀번호는 필수입니다.")
    .min(8, "비밀번호는 8자 이상이어야 합니다.")
    .max(32, "비밀번호는 32자 이하여야 합니다."),
})
./auth.ts
import NextAuth from "next-auth"
import { ZodError } from "zod"
import Credentials from "next-auth/providers/credentials"
import { signInSchema } from "./lib/zod"
// 평문 비밀번호 문자열을 처리하기 위한 로직; 주의하세요!
import { saltAndHashPassword } from "@/utils/password"
import { getUserFromDb } from "@/utils/db"
 
export const { handlers, auth } = NextAuth({
  providers: [
    Credentials({
      // `credentials` 객체에 키를 추가하여 제출할 필드를 지정할 수 있습니다.
      // 예: 도메인, 사용자 이름, 비밀번호, 2FA 토큰 등
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        try {
          let user = null
 
          const { email, password } = await signInSchema.parseAsync(credentials)
 
          // 비밀번호를 솔트하고 해시하는 로직
          const pwHash = saltAndHashPassword(password)
 
          // 사용자가 존재하는지 확인하는 로직
          user = await getUserFromDb(email, pwHash)
 
          if (!user) {
            throw new Error("유효하지 않은 자격 증명입니다.")
          }
 
          // 사용자 데이터가 포함된 JSON 객체 반환
          return user
        } catch (error) {
          if (error instanceof ZodError) {
            // 자격 증명이 유효하지 않음을 나타내기 위해 `null` 반환
            return null
          }
        }
      },
    }),
  ],
})
Auth.js © Balázs Orbán and Team - 2025