Next Auth를 사용하여 소셜 로그인 구현하기

쭌로그·2025년 3월 30일
1

NextAuth.js

사이드 프로젝트를 진행하며 네이버 소셜로그인 파트를 담당하게 되었습니다.
네이버 소셜 로그인을 구현하는 방법을 찾아보던 중, Next-Auth라는 라이브러리를 알게되었습니다.
Next-Auth는 네이버 뿐만이 아닌, 구글, 카카오 등 다양한 소셜 로그인을 지원하는 3rd-party 라이브러리입니다. 이를 통해 팀원들도 간단하게 소셜 로그인을 구현할 수 있을 것 같아 도입을 제안하고 사용중에 있습니다.

Next Auth의 장점

  1. 다양한 제공자 지원: Google, Naver, Kakao 등 다양한 Oauth 제공자를 지원합니다.
  2. 세션 관리: 클라이언트와 서버 간 세션을 간단하게 관리할 수 있습니다.
  3. 보안 강화: JWT 및 세션 기반 인증을 지원합니다.
  4. 확장성: 사용자 정의 콜백 및 DB 통합 기능을 지원합니다.

설치 및 기본 설정

  1. NextAuth.js 설치
    해당 명령어를 실행하여 NextAuth를 설치합니다.
  • npm
    npm install next-auth
  • yarn
    yarn add next-auth
  1. API 라우트 생성
    App Router에 대한 설정만 기술하겠습니다.
    App Router에서는 /app/api 디렉토리를 사용하여 API 라우트를 정의할 수 있습니다. NextAuth.js의 인증 API를 설정하기 위해서는 app/api/auth/[...nextauth]/route.js 파일을 생성해야합니다.
import NextAuth from 'next-auth';
import NaverProvider from 'next-auth/providers/naver';

const handler = NextAuth({
  providers: [
    NaverProvider({
      clientId: process.env.NAVER_CLIENT_ID!,
      clientSecret: process.env.NAVER_CLIENT_SECRET!,
      profile(profile) {
        return {
          id: profile.response?.id ? profile.response?.id : profile.id,
          name: profile.response?.name ? profile.response?.name : profile.name,
          email: profile.response?.email ? profile.response?.email : profile.email,
          image: profile.response?.profile_image ? profile.response?.profile_image : profile.image,
        };
      },
    }),
  ],
  callbacks: {
    async signIn() {
      try {
        return true;
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
      } catch (_) {
        return false;
      }
    },
    async redirect({ url, baseUrl }) {
      // 에러가 있는 경우 처리
      if (url.includes('error=Callback')) {
        return `${baseUrl}/login`;
      }
      // 취소된 경우 처리
      if (url.includes('error=AccessDenied')) {
        return `${baseUrl}/login`;
      }
      // 기본 리다이렉트
      if (url.startsWith(baseUrl)) {
        return url;
      }
      return baseUrl;
    },
    async jwt({ token, user }) {
      // 네이버 로그인 시 받아오는 정보를 토큰에 저장
      if (user) {
        token.id = user.id;
        token.name = user.name;
        token.email = user.email;
        token.image = user.image;
      }
      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user = {
          ...session.user,
          name: token.name as string,
          email: token.email as string,
          image: token.image as string,
        };
      }
      return session;
    },
  },
  pages: {
    signIn: '/login',
    error: '/login', // 에러 페이지를 로그인 페이지로 지정
  },
});

export { handler as GET, handler as POST };

App Router에서는GetPost 메서드를 명시적으로 export해야 사용할 수 있습니다.

  1. 환경변수 설정
    Oauth 제공자를 사용할 경우 Client ID와 Secret ID를 저장해야합니다.

Naver Oauth 애플리케이션 정보

//JWT 토큰 암호화를 위해 설정해아하는 값
NEXTAUTH_SECRET='mysecretkey'
//배포 환경에서는 실제 URL로 변경해야합니다.
NEXTAUTH_URL='http://localhost:3000'

NAVER_CLIENT_ID=네이버 클라이언트 ID..
NAVER_CLIENT_SECRET=네이버 시크릿 ID..
  1. 클라이언트에서 사용하기
    App Router에서는 SessionProvider를 사용하여 세션 정보를 공유할 수 있습니다. 보통 세션 정보는 앱 전체에서 공유하기 때문에 /app/layout.tsx에 적용했습니다.
// /app/providers.tsx
'use client';

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

export function Providers({ children }: { children: React.ReactNode }) {
  return <SessionProvider>{children}</SessionProvider>;
}
// /app/layout.tsx
import type { Metadata } from 'next';
import './globals.css';
import TanstackQueryProvider from '@/providers/TanstackQueryProvider';
import MswInitializer from '@/providers/MswInitializer';
import { Providers } from '@/app/providers';
export const metadata: Metadata = {
  title: '빵잇나우',
  description: '빵잇나우',
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <MswInitializer />
        <TanstackQueryProvider>
          <Providers>{children}</Providers>
        </TanstackQueryProvider>
      </body>
    </html>
  );
}
  1. 로그인/로그아웃 기능 구현
'use client';

import { useState } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import Image from 'next/image';
import Topbar from '@/components/topbar/Topbar';
import Button from '@/components/button/Button';
import naverIcon from '@/assets/icons/naver.svg';
import Alert from '@/components/common/Alert';
import { signIn, signOut } from 'next-auth/react';

export default function LoginPage() {

  const handleSignIn = async (provider: string) => {
    try {
      signIn(provider, {
        callbackUrl: '/',
        redirect: true,
      });
    } catch (_) {
      setAlertTitle('로그인 실패');
      setAlertSubtitle('로그인 중 문제가 발생했습니다. 다시 시도해주세요.');
      setShowAlert(true);
    }
  };
  

  return (
    <div className="flex flex-col max-h-[100%] bg-white">
      <div className="flex flex-col items-center justify-center gap-4 mt-6">
        <div className="flex items-center w-full my-3 px-5">
          <div className="flex-1 h-px bg-gray-300" />
          <span className="px-3 text-sm text-gray-500">또는</span>
          <div className="flex-1 h-px bg-gray-300" />
        </div>

        <div className="flex gap-4">
          <button
            onClick={() => handleSignIn('naver')}
            className="w-14 h-14 bg-green-500 rounded-full flex items-center justify-center">
            <Image src={naverIcon} width={24} height={24} alt="네이버 로그인" />
          </button>
           <button
            onClick={() => signOut()}
            className="w-14 h-14 bg-green-500 rounded-full flex items-center justify-center">
             로그아웃
          </button>
        </div>
      </div>
    </div>  
  );
}

세션 정보 가져오기

  1. 클라이언트
    현재 로그인 된 사용자 정보를 확인하려면 useSeession훅을 사용하여 사용자 정보를 가져올 수 있습니다.
// app/components/UserProfile.js
"use client";

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

export default function UserProfile() {
  const { data: session } = useSession();

  if (!session) {
    return <p>로그인되지 않았습니다.</p>;
  }

  return (
    <div>
      <p>사용자 이름: {session.user.name}</p>
      <p>이메일: {session.user.email}</p>
    </div>
  );
}
    1. 서버 컴포넌트
      App Router의 서버 컴포넌트에서는 getServerSession을 사용하여 세션 정보를 가져와야합니다.
// app/dashboard/page.js
import { getServerSession } from "next-auth";
import { authOptions } from "../api/auth/[...nextauth]/route";

export default async function Header() {
  const session = await getServerSession(authOptions);

  if (!session) {
    return <p>로그인해주세요.</p>;
  }

  return (
    <div>
      <p>환영합니다, {session.user.name}!</p>
    </div>
  );
}
  • authOptions: API라우트에서 정의한 NextAuth 설정을 가져옵니다.
  • 서버 컴포넌트는 클라이언트 측 라이브러리를 사용할 수 없기 때문에 세션 데이터를 서버에서 직접 처리합니다.

정리

NextAuth를 사용하니 회사에서 Okta를 사용한 Oauth를 직접 구현했을 때보다 훨씬 간편하게 구현할 수 있었습니다. 게다가 다양한 로그인을 지원하니 여러 로그인을 구현할 때는 속도가 훨씬 빨라져 좋은 라이브러리라고 생각됩니다. 다음에는 백엔드와 연동하는 방법을 기술하여 Spring Session에 저장하는 방식을 기술해보겠습니다.

참고

Next-auth를 이용한 로그인 구현
NextAuth 공식 문서

profile
매일 발전하는 프론트엔드 개발자

0개의 댓글