티스토리 뷰

Front End/🤢 Next.js

next-auth

James Wetzel 2025. 3. 19. 20:14

1. 필요한 패키지 설치

터미널에서 다음 명령어를 실행하여 NextAuth, MySQL2(프로미스 지원), bcryptjs 패키지를 설치합니다.

npm install next-auth mysql2 bcryptjs

2. NextAuth API 라우트 생성

Next.js의 API 라우트를 사용하여 인증 엔드포인트를 생성합니다. (Next.js 13 기준으로 pages 디렉터리를 사용하는 경우의 예제이며, app 디렉터리 사용 시에는 약간의 구조 차이가 있습니다.)

파일 경로: pages/api/auth/[...nextauth].ts

"use server";

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import type { NextAuthOptions } from "next-auth";
import mysql from "mysql2/promise";
import { compare } from "bcryptjs";

/**
 * NextAuth.js 설정 객체
 *
 * - CredentialsProvider: 사용자 이메일/비밀번호로 인증 처리
 * - 데이터베이스 연결: MySQL 데이터베이스의 "members" 테이블에서 사용자 정보 조회
 */
export const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: {
          label: "Email",
          type: "text",
          placeholder: "email@example.com",
        },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials) {
        // 환경 변수에 저장된 DB 접속 정보를 사용합니다.
        const connection = await mysql.createConnection({
          host: process.env.MYSQL_HOST, // 예: localhost
          user: process.env.MYSQL_USER,
          password: process.env.MYSQL_PASSWORD,
          database: process.env.MYSQL_DATABASE,
        });

        // members 테이블에서 사용자를 이메일로 조회
        const [rows]: any = await connection.execute(
          "SELECT * FROM members WHERE email = ?",
          [credentials?.email]
        );

        // 사용자가 존재하는 경우
        if (Array.isArray(rows) && rows.length > 0) {
          const user = rows[0];
          // 비밀번호 검증 (저장된 비밀번호가 bcrypt 해시된 값이라고 가정)
          const isValid = await compare(credentials!.password, user.password);
          if (isValid) {
            // 필요한 사용자 정보를 반환합니다.
            return {
              id: user.id,
              name: user.name,
              email: user.email,
            };
          }
        }

        // 인증 실패시 null 반환
        return null;
      },
    }),
  ],

  // JWT 세션 전략을 사용합니다.
  session: {
    strategy: "jwt",
  },

  // 필요에 따라 사용자 정의 로그인 페이지 경로를 지정할 수 있습니다.
  pages: {
    signIn: "/auth/signin",
  },

  // JWT 및 session 콜백을 통해 토큰이나 session 객체에 사용자 식별자를 추가합니다.
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
      }
      return token;
    },
    async session({ session, token }) {
      if (token && session.user) {
        session.user.id = token.id as number;
      }
      return session;
    },
  },
};

export default NextAuth(authOptions);

설명

  • CredentialsProvider
    • 사용자가 입력한 이메일과 비밀번호를 받아 MySQL 데이터베이스의 "members" 테이블에서 해당 사용자를 조회합니다.
    • bcryptjs의 compare 함수를 이용해 입력한 비밀번호와 저장된 해시된 비밀번호를 비교합니다.
    • 인증에 성공하면 사용자 정보를 반환하고, 실패하면 null을 반환합니다.
  • 세션과 JWT 콜백
    • 인증 후 JWT 토큰 및 session에 사용자 식별자(id)를 추가하여 후속 인증 처리 시 사용할 수 있습니다.
  • 환경 변수
    • 데이터베이스 접속 정보는 .env 파일에 저장한 후 process.env를 통해 접근합니다.

3. 환경 변수 설정 (.env)

프로젝트 루트에 .env 파일을 생성하고 다음과 같이 데이터베이스 접속 정보를 추가합니다.

MYSQL_HOST=localhost
MYSQL_USER=your_mysql_username
MYSQL_PASSWORD=your_mysql_password
MYSQL_DATABASE=your_database_name

4. 사용자 정의 로그인 페이지 생성 (선택 사항)

pages/auth/signin.tsx 파일을 생성하여 로그인 UI를 구성할 수 있습니다.

"use client";

import { signIn } from "next-auth/react";
import { useState } from "react";
import React from "react";

/**
 * 사용자 정의 로그인 페이지 컴포넌트
 *
 * @returns {JSX.Element} 로그인 폼 UI
 */
export default function SignIn(): JSX.Element {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const result = await signIn("credentials", {
      email,
      password,
      redirect: true,
      callbackUrl: "/",
    });
    console.log(result);
  };

  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-100">
      <form onSubmit={handleSubmit} className="p-6 bg-white rounded shadow-md">
        <h1 className="text-2xl mb-4">로그인</h1>
        <div className="mb-4">
          <label className="block mb-1">이메일</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            className="border p-2 w-full"
            required
          />
        </div>
        <div className="mb-4">
          <label className="block mb-1">비밀번호</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            className="border p-2 w-full"
            required
          />
        </div>
        <button type="submit" className="w-full bg-blue-500 text-white p-2 rounded">
          로그인
        </button>
      </form>
    </div>
  );
}

설명

  • NextAuth의 signIn 함수를 사용하여 자격 증명 제공자("credentials")로 로그인 요청을 보냅니다.
  • 로그인 성공 시, callbackUrl에 지정된 경로(예: "/")로 리다이렉트합니다.

5. NextAuth.js 실행 및 테스트

  • 개발 서버를 실행하고(npm run dev) 브라우저에서 /auth/signin 페이지로 접근하여 로그인 폼이 정상적으로 표시되는지 확인합니다.
  • 올바른 자격 증명을 입력하면, MySQL의 "members" 테이블에서 사용자가 조회되고, 인증이 성공하면 설정한 콜백에 따라 세션과 JWT가 생성됩니다.

 

728x90
반응형