Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

likeornament 님의 블로그

Auth.js(NextAuth.js) 사용법 본문

공부

Auth.js(NextAuth.js) 사용법

likeornament 2024. 12. 30. 20:19

Auth.js(이전 명칭: NextAuth.js)는 Next.js에서 인증 시스템을 구현하기 위해 설계된 오픈 소스 라이브러리다.

 

장점

  1. Login Provider로 구글, 네이버, 카카오 등의 소셜 로그인 등을 지원하기에 간편 로그인을 쉽게 구현할 수 있다.
  2. Credential Provider로 기본적인 로그인, 로그아웃을 지원하고 로그인에 따른 쿠키를 관리하기 쉽다.

위의 장점들 때문에 프로젝트에서 인증 시스템을 구현할 때 Auth.js를 사용했다.

이제 설치 방법과 구성요소, 사용법에 대해 알아보자.


1. 설치

$ npm i next-auth@beta
  • 위의 명령어로 auth.js 설치한다.
  • @auth/core 관련 작업은 Auth.js가 알아서 처리하기에 굳이 직접 설치할 필요는 없다.

 

npx auth secret

 

  • 터미널에 다음과 같이 입력하면 서명(Signing)암호화(Encryption) 관련 작업에 사용되는 키를 생성해 준다.

 

AUTH_SECRET=위에서 생성된 값 넣기
  • .env 파일에 AUTH_SECRET의 값으로 넣어준다.

 

 

2. 구성 요소

NextAuth() 호출로 Next.js 애플리케이션에서 인증 처리를 담당하는 함수들을 반환 받을 수 있다.

 

   1. handlers

  • 인증 처리 과정에서 GET, POST 요청을 처리하는 핸들러.
  • GET: 인증상태 확인, 로그인 페이지로의 리다이렉션 등.
  • POST: 로그인 시 사용자 인증 로직 처리

   2. auth

  • Auth.js의 핵심 인증 함수로, 사용자 세션 및 JWT 처리를 담당.

   3. signIn

  • 로그인 프로세스를 시작하는 함수.
  • 클라이언트에서 인증 페이지로 리다이렉션하거나, API 호출로 로그인 처리를 트리거한다.

   4. signOut

  • 현재 로그인 한 사용자의 세션을 종료하는 함수.
  • 서버와 통신하여 세션을 삭제하고 로그아웃 상태를 유지.

 

3. 사용 방법

1) [...nextauth]/route.ts

export { GET, POST } from '@/auth';
  • 프로젝트에 Auth.js를 추가하려면 app/api/auth에 API 라우터인 [...nextauth]를 추가해야 한다.
  • 그 아래에 route.ts 파일을 만들어서 위의 코드를 작성한다.
  • 이 코드는 src/auth.ts 파일에 정의된 GET, POST 핸들러를 가져와서 route.ts 파일을 통해 Next.js에 등록한다.

 

2) middleware.ts

import { auth } from "./auth"
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

export async function middleware(request: NextRequest) {
    // 세션 검증
    const session = await auth();
    
    // 세션이 없으면 로그인 페이지로 리다이렉트
    if (!session) {
      console.log('No active session for protected route');
      return NextResponse.redirect(new URL('/login', request.url));
    }
    
    return NextResponse.next();
}

export const config = {
  matcher: ['/post', '/likes', '/notice', '/myPage'],
}

 

  • middleware는 next.js에서 페이지를 렌더링하기 전에 서버 측에서 실행되는 함수이다.
  • middleware.ts는 src 폴더에 포함되거나, app과 같은 수준의 트리에 존재해야 한다.
  • config에 정의된 matcher에 해당되는 경로에 진입하면, 렌더링 이전에 middleware() 함수가 호출된다.

=> 즉, 위의 코드는 matcher에 정의된 경로로 진입할 때 세션이 있는지 확인하고, 없다면 "/login" 경로로 리다이렉션한다.

 

 

3) src/auth.ts

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { cookies } from "next/headers";
import cookie from 'cookie';

export const {
  handlers: { GET, POST }, 
  auth,
  signIn,
} = NextAuth({
  pages: { 
    signIn: '/login',
    newUser: '/register',
  },
  callbacks: {
    jwt({ token}) {
      return token;
    },
    session({ session}) {
      return session;
    },
  },
  providers: [ 
    CredentialsProvider({
      async authorize(credentials) {
        const authResponse = await fetch(`${process.env.NEXT_PUBLIC_BACKEND_API_SERVER}/login`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            loginId: credentials.username,
            pw: credentials.password,
          }),
        });
      
        let setCookie = authResponse.headers.get('set-cookie');
        if (setCookie) {
          const parsed = cookie.parse(setCookie);
          cookies().set('JSESSIONID', parsed['JSESSIONID'], {
            ...parsed,
            maxAge: 1800, //30분
            path: '/'
          });
        }
      
        if (!authResponse.ok) {
          console.error('Login failed:', authResponse.statusText);
          return null;
        }
      
        const responseText = await authResponse.text();
        let user;

        try {
          user = JSON.parse(responseText);
        } catch (error) {
          console.error('Failed to parse JSON:', error);
          return null;
        }
      
        if (!user.loginId || !user.email || !user.name || !user.profilePath) {
          console.error('User object is missing required properties:', user);
          return null;
        }
      
        return {
          id: user.loginId,
          email: user.email,
          name: user.name,
          image: user.profilePath,
          ...user,
        };
      }
      
    }),
  ],
  session: {
    maxAge: 1800
  }
});
  • auth.ts는 Auth.js의 설정 파일이다.
  • 인증 로직, 세션 관리, providers 등을 설정한다.

 

export const {
  handlers: { GET, POST },
  auth,
  signIn,
} = NextAuth({...});
  • NextAuth() 함수로부터 handlers(GET, POST), auth, signIn을 반환받는다.

 

pages: {
  signIn: '/login',
  newUser: '/register',
}
  • 사용자에게 보여줄 로그인 페이지(signIn)와 회원가입 페이지(newUser) 경로를 설정한다.
  • 해당 설정을 하지 않을 경우, Auth.js에서 제공하는 기본 페이지들이 생성된다.

 

callbacks: {
  jwt({ token }) {
    return token;
  },
  session({ session }) {
    return session;
  },
}
  • callbacks는 인증 및 세션 관리 중 호출되는 각 핸들러를 지정한다.
  • jwt: JWT가 생성되거나 업데이트될 때 호출되며 반환값은 쿠키에 저장된다.
  • session: 위의 jwt가 반환하는 token을 받아 세션이 확인될 때마다 호출된다.

=> 브라우저에 저장된 next-auth.session-token이라는 이름의 쿠키에 저장된 사용자 정보를 클라이언트에서 수정할 때 사용

 

CredentialsProvider({
  async authorize(credentials) { ... }
}),
  • CredentialsProvider는 아이디와 비밀번호 등을 사용하여 사용자를 인증하는 프로바이더이다.
  • 사용자 인증을 위한 백엔드 서버와 통신해서, 인증이 성공한 경우 사용자 객체를 반환한다.

 

let setCookie = authResponse.headers.get('set-cookie');
if (setCookie) {
  const parsed = cookie.parse(setCookie);
  cookies().set('JSESSIONID', parsed['JSESSIONID'], {
    ...parsed,
    maxAge: 1800, 
    path: '/'
  });
}

 

  • 백엔드로부터 로그인 성공 시 set-cookie 헤더로 세션 쿠키를 전달 받는다.
  • 헤더에서 쿠키를 가져와 Next.js의 cookies() API를 사용하여 세션 쿠키를 설정한다.

 

const responseText = await authResponse.text();
let user;

try {
  user = JSON.parse(responseText);
} catch (error) {
  console.error('Failed to parse JSON:', error);
  return null;
}
  • 서버로부터 받은 응답 본문을 JSON 형식으로 파싱하며, 파싱 실패 시 null을 반환

 

return {
  id: user.loginId,
  email: user.email,
  name: user.name,
  image: user.profilePath,
  ...user,
};
  • 로그인 성공 시 id, email, name, image라는 정해진 속성으로 유저 정보를 반환해야 한다.

 

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

const handleLogin = () => {
  signIn("credentials", {
    username: "user",
    password: "pass",
    callbackUrl: "/home",
  });
};
  • 로그인하는 방법은 next-auth/react에서 signIn 함수를 불러와 위의 코드처럼 사용하면 된다.

 

import { signOut } from "next-auth/react";

const handleLogout = () => {
  signOut({ callbackUrl: "/login" });
};
  • 로그아웃은 signOut 함수를 불러와 위의 코드처럼 사용하면 된다.

 

4) useSession 사용법

'use client';
import { SessionProvider } from "next-auth/react";

type Props = ({
  children: React.ReactNode;
});

export default function AuthSession({ children }: Props) {
  return <SessionProvider>{children}</SessionProvider>;
}
  • 로그인한 유저의 정보를 가져오려면 useSession() 훅을 사용하면 된다.(클라이언트 컴포넌트에서만 사용 가능)
  • useSession() 훅을 사용하기 위해선 먼저 AuthSession.tsx를 만들어야 한다.

 

<AuthSession>{children}</AuthSession>
  • 루트 레이아웃의 children을 이렇게 AuthSession으로 감싸야 프로젝트에서 useSession을 사용할 수 있다.

 

const { data: session, status } = useSession();
  • useSession은 세션 정보와 세션 상태를 반환한다.
  • data(세션 정보): 로그인된 사용자의 정보를 담고 있다.
  • status(세션 상태): "loading", "authenticated", "unauthenticated"의 세 가지 상태를 나타내는 문자열

 

import {auth} from "@/auth";

const session = await auth();
  • 서버 컴포넌트에서 세션 정보를 사용하고 싶다면 auth() 함수를 사용하면 된다.

'공부' 카테고리의 다른 글

Tanstack Query(React Query)에 대해 알아보자  (0) 2024.12.31