본문 바로가기

개발 공부/React

NextAuth.js 사용자 인증 로직 (3) NextAuth 동작 방식

코드 출처 : https://github.com/Cluster-Taek/next14-boilerplate

 

login-form.tsx

'use client';

import Button from '@/components/common/button';
import { ILoginFormValue } from '@/lib/common/account';
import { sva } from '@/styled-system/css';
import { Box } from '@/styled-system/jsx';
import { signIn } from 'next-auth/react';
import { useSearchParams } from 'next/navigation';
import { Controller, useForm } from 'react-hook-form';

const LoginForm = () => {
  const loginFormStyle = LoginFormSva();
  const searchParams = useSearchParams();
  const {
    handleSubmit: formHandleSubmit,
    formState,
    control,
  } = useForm<ILoginFormValue>({
    defaultValues: {
      login: 'test@gmail.com',
      password: '1234',
    },
  });

  const handleSubmit = formHandleSubmit(async (data) => {
    await signIn('login-credentials', { ...data, callbackUrl: searchParams.get('callbackUrl') ?? '/' });
  });

  return (
    <Box className={loginFormStyle.wrapper}>
      <Box className={loginFormStyle.title}>Login</Box>
      <form className={loginFormStyle.form} onSubmit={handleSubmit}>
        <Controller
          control={control}
          name="login"
          rules={{ required: 'Email is required' }}
          render={({ field }) => <input type="text" {...field} className={loginFormStyle.input} placeholder="email" />}
        />
        {formState.errors.login && <Box className={loginFormStyle.error}>{formState.errors.login.message}</Box>}
        <Controller
          control={control}
          name="password"
          rules={{ required: 'Password is required' }}
          render={({ field }) => (
            <input type="password" {...field} className={loginFormStyle.input} placeholder="password" />
          )}
        />
        {formState.errors.password && <Box className={loginFormStyle.error}>{formState.errors.password.message}</Box>}
        <Button type="submit">Submit</Button>
      </form>
      {searchParams.get('error') === 'CredentialsSignin' && (
        <Box>
          <Box className={loginFormStyle.error}>Invalid email or password</Box>
        </Box>
      )}
    </Box>
  );
};

export default LoginForm;

...

 

signIn 함수가 어떻게 auth-options.ts에서 정의된 인증 핸들러를 찾아 요청을 처리하는가?

 

이를 이해하려면 NextAuth의 동작 방식을 알아야 합니다.

1. NextAuth와 API 라우트의 연결

NextAuth는 pages/api/auth/[...nextauth].ts 파일을 사용하여 모든 인증 요청을 처리합니다.
Next.js의 API 라우트는 파일 시스템 기반으로 동작하며, [...] 패턴은 동적 경로를 나타냅니다.

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

 

즉, 클라이언트에서 /api/auth/*로 보내는 모든 요청이 pages/api/auth/[...nextauth].ts에 정의된 NextAuth 핸들러로 전달됩니다.

 

2. signIn 함수의 역할

signIn 함수는 next-auth/react 라이브러리에서 제공하며, 특정 Provider에 인증 요청을 보냅니다.

await signIn('login-credentials', { ...data });
 
  • 첫 번째 인자: 'login-credentials'
    • NextAuth에서 설정된 Provider의 id입니다.
    • 이 값은 auth-options.ts에서 정의된 CredentialsProvider의 id: 'login-credentials'와 일치해야 합니다.
  • 두 번째 인자: 옵션 객체
    • 사용자 입력값(이메일, 비밀번호 등)을 포함.

signIn 함수는 내부적으로 /api/auth/callback/:provider 경로로 HTTP POST 요청을 만듭니다.

  • 여기서 :provider는 첫 번째 인자로 전달된 'login-credentials'입니다.

 

3. NextAuth 핸들러의 요청 처리

pages/api/auth/[...nextauth].ts는 NextAuth의 엔트리포인트입니다.

 

import NextAuth from 'next-auth';
import { authOptions } from '@/lib/auth-options';

export default NextAuth(authOptions);
  • authOptions는 NextAuth의 설정 객체로, auth-options.ts에서 정의된 내용을 가져옵니다.
  • 이 객체에는 인증 로직(Provider, Callbacks 등)이 포함되어 있습니다.

signIn 요청의 흐름

  1. 라우트 매칭: 클라이언트가 /api/auth/callback/login-credentials로 요청 → [...nextauth].ts가 해당 요청을 수신.
  2. Provider 탐색: NextAuth는 authOptions.providers에서 id: 'login-credentials'인 Provider를 찾음.
  3. authorize 호출: CredentialsProvider의 authorize 함수가 호출되어 인증 처리.

 

4. Provider의 식별과 연결

signIn에서 전달한 'login-credentials' 값이 Provider를 식별하는 핵심입니다.
이 값은 authOptions.providers에 정의된 Provider의 id와 일치해야 요청을 처리할 수 있습니다.

export const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      id: 'login-credentials', // Provider ID
      name: 'Credentials',
      credentials: {
        login: { label: 'Email', type: 'text' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials) {
        // 사용자 인증 로직
      },
    }),
  ],
};

 

 

 

 어떻게 요청이 이어지는가

  1. 클라이언트에서 signIn('login-credentials') 호출:
    • /api/auth/callback/login-credentials로 요청이 전송됩니다.
  2. NextAuth 핸들러가 요청 수신:
    • [...nextauth].ts 파일에서 NextAuth가 모든 인증 요청을 처리.
  3. Provider 매핑:
    • NextAuth는 authOptions.providers에서 id가 'login-credentials'인 Provider를 찾음.
  4. authorize 실행:
    • 해당 Provider의 authorize 함수가 호출되어 사용자 인증이 이루어짐.

이 모든 과정은 Next.js의 파일 기반 라우팅NextAuth의 설정 시스템 덕분에 자동으로 연결됩니다.

'개발 공부 > React' 카테고리의 다른 글

React.memo (1)  (1) 2025.01.22
NextAuth.js 요약  (1) 2025.01.19
NextAuth.js 사용자 인증 로직 (2)  (1) 2025.01.12
NextAuth.js 사용자 인증 로직 (1)  (0) 2025.01.11
Next.js usePathname, useRouter, useSearchParams  (0) 2025.01.08