NextAuth.js: Next.js에서 간편한 인증 구현

박은정·2025년 1월 31일
0

Next

목록 보기
6/7
post-thumbnail

1️⃣ NextAuth.js 란 무엇인가?

NextAuth.js는 Next.js 애플리케이션에서 간편하게 사용자 인증을 구현할 수 있도록 도와주는 라이브러리로, OAuth, 이메일, 자격 증명 기반 로그인을 쉽게 설정할 수 있습니다.
Next.js의 API Routes를 활용해서 서버측에서 인증을 처리하며, 로그인한 사용자의 세션 정보를 보통 JWT, 필요에 따라 데이터베이스에 저장하여 관리합니다. API Routes를 활용하기 때문에 서버리스 환경에서도 적용할 수 있습니다.
복잡한 인증로직을 직접 구현할 필요 없이, 보안성과 확장성을 갖춘 인증 시스템을 손쉽게 적용할 수 있습니다.

⭐️ 세션(Session)과 토큰(Token)

세션토큰
개념로그인 상태를 유지하는 방법로그인 상태를 인증하는 증명서
활용사용자가 로그인하면 로그인했다는 정보를 저장하기 때문에 사용자는 계속 로그인 상태를 유지하면서 다른 페이지로 이동할 수 있음서버가 로그인했다고 기억하는게 아니라, 클라이언트에게 JWT 토큰이라고 불리는 로그인 증명서를 줌

놀이공원 자유이용권 상황에 비유

세션 방식에서는 놀이공원이 방문객의 입장 정보를 직접 관리하며, 이용할 때마다 내부 시스템을 통해 확인하고, 토큰 방식에서는 방문객이 입장 증명서인 팔찌를 소지하여 놀이공원 측은 이용할 때마다 팔찌의 유효성만 확인합니다.
세션은 관리 및 확인 주체가 놀이공원 측이고, 토큰은 방문객이라고 볼 수 있습니다.

기능상황세션토큰
인증놀이공원 입구에서 표를 구매해서 본인확인을 한다
인증 성공 후놀이공원에 입장한다놀이공원 측은 방문했다는 정보를 내부 시스템에 저장한다놀이공원 측은 입장 확인을 마친 방문객에게 입장팔찌를 제공한다
요청때마다 확인놀이기구에 이용할 때마다직원이 내부 시스템을 조회해서 해당 방문객의 입장 여부를 확인한다직원은 방문객이 착용한 팔찌를 확인하여 입장 여부를 판단한다
정보삭제놀이공원을 떠날 때놀이공원 측은 내부 시스템에서 해당 방문객의 정보를 삭제한다방문객은 팔찌를 반납하거나 폐기한다

✅ NextAuth.js의 주요 기능

  • 다양한 로그인 제공: Google, Github, Facebook, X 등의 OAuth 인증 및 이메일 Magic Link, 사용자 정의 로그인인 Credentials 을 지원합니다.
  • 세션 관리: JWT 기반 인증 또는 서버 세션을 활용한 인증 정보를 안전하게 관리할 수 있습니다.
  • 확장성: Callbacks, Middleware 를 활용하여 다양한 방식으로 확장할 수 있습니다.

2️⃣ NextAuth.js를 사용하는 이유

웹 애플리케이션에서 인증은 필수적인 기능이지만, 직접 구현하려면 많은 시간과 노력이 필요합니다.
Next.js는 강력한 풀스택 프레임워크이지만, 자체적으로 인증 기능을 제공하지 않기 때문에 직접 구현하거나 라이브러리를 사용할 수 있습니다.

직접 구현할 때 문제점

OAuth 및 JWT 기반의 보안 로직을 직접 구현하는 것은 많은 시간이 필요하고, 복잡하며 유지보수가 어렵습니다.

타 인증 서비스와의 비교

Firebase Authentication, Auth0, Supabase 타 인증 서비스는 비용이 비쌀 수 있다는 단점과 함께 다음과 같은 커스텀 제한이 있습니다.

  • Firbase Authentication: 커스텀 인증 로직 적용이 어렵고 다중 인증 등의 고급 기능을 자유롭게 구현하기 어렵습니다
  • Auth0: 제공하는 호스팅 페이지를 거쳐야 하므로 완전한 커스텀 구현이 어렵습니다.
  • Supabase: Vercel 같은 서버리스 환경과의 완벽한 연동이 어렵고, OAuth 및 이메일 인증 이외의 방식 지원이 제한적입니다.

NextAuth.js의 장점

무료 오픈 소스이고, Next.js와 완벽하게 연동할 수 있습니다.
또한 OAuth, JWT, 이메일 로그인 등 다양한 인증 방식을 지원하며, 서버리스 환경에서 최적화된 인증을 제공합니다.

3️⃣ NextAuth.js의 동작원리

NextAuth.js는 Next.js의 API Routes를 활용해서 서버에서 인증을 처리합니다.

  1. 사용자가 로그인 요청을 보냅니다.
  2. NextAuth가 해당 요청을 처리하고 OAuth Provider 또는 자격 증명을 검증합니다.
  3. 인증이 성공하면 세션을 생성됩니다.
  4. 세션정보는 기본적으로 JWT 기반 인증을 사용하지만 필요에 따라 데이터베이스를 이용해 관리할 수 있습니다.
  5. 클라이언트는 세션 정보를 가져와 인증 상태를 유지합니다.
  6. 사용자가 로그아웃하면 해당 세션은 삭제됩니다.

4️⃣ NextAuth.js 사용방법

STEP 01. 라이브러리 설치

$ npm i next-auth

STEP 02. API Route 설정

src/app/api/auth/[…nextauth]/route.ts 파일을 생성해서 /api/auth 엔드포인트를 설정하면, NextAuth에서 필요한 다음과 같은 API 엔드포인트는 자동으로 생성합니다.

  • 로그인 처리: /api/auth/signin
  • 로그아웃 처리: /api/auth/signout
  • 현재 로그인된 사용자 세션 정보 반환: /api/auth/session
  • Google 로그인 이후 리디렉션 처리: /api/auth/callback/google
  • 인증 중 오류 처리: /api/auth/error

클라이언트에서 signIn() 을 호출하면 내부적으로 /api/auth/signin에 네트워크 요청을 보고, 해당 엔드포인트에선 로그인 검증을 수행하고 세션을 생성합니다.
이때 로그인이 성공하면 JWT 토큰을 쿠키에 저장해서, 이후 요청에서 인증할 수 있도록 합니다.
로그인 이후에 클라이언트에서 useSession() 훅을 호출하면 NextAuth 내부적으로 /api/auth/session 에 네트워크 요청을 보내서 현재 로그인 상태를 확인합니다.

Next.js 13 이상에서는 App Router를 사용하며, 12 이하에서는 Pages Router를 사용합니다.
App Router와 Page Router는 서버 동작 방식이 다르기 때문에 API Route 설정 또한 이에 맞게 달라집니다.

✅ Pages Router를 사용하는 Next.js 12이하 버전

export default NextAuth({}) 형식을 사용해서 단일 핸들러로 노출합니다.
GET, POST 요청을 모두 자동으로 처리합니다.

// src/pages/api/auth/[…nextauth].ts
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import { NextAuthOptions } from 'next-auth';

const authOptions: NextAuthOptions = {
	providers: [
    	GoogleProvider({
        	clientId: process.env.GOOGLE_CLIENT_ID, 
            clientSecret: process.env.GOOGLE_CLIENT_SECRET,
        }),
    ], 
    callbacks: {
    	async session({ session, token }) {
        	session.user.id = token.sub;
            return session;
        }
    },
};

const handler = NextAuth(authOptions);
export default handler;

✅ App Router를 사용하는 Next.js 13이상 버전

export default 를 사용하지 않고, export { handler as GET, handler as POST } 형식으로 API 핸들러를 분리하여 명시적으로 설정합니다.

// src/app/api/auth/[…nextauth]/route.ts
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import { NextAuthOptions } from 'next-auth';

export const authOptions: NextAuthOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async session({ session, token }) {
      session.user.id = token.sub;
      return session;
    },
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

STEP 03. 환경변수 설정

// .env.local
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
NEXTAUTH_URL=http://localhost:3000

Client ID & Secret 은 애플리케이션 단위로 발급되는 정보로, OAuth 인증을 사용할 때 서비스 제공자와 애플리케이션 간의 식별 및 보안 인증에 사용됩니다.

NEXTAUTH_URL 은 NextAuth.js 가 OAuth 인증한 뒤 리디렉션할 URL을 결정할 때 사용됩니다.
NEXTAUTH_URL 을 설정하지 않아도 NextAuth.js가 환경에 따라 자동으로 감지하겠지만, Vercel 등의 클라우드 환경에서는 정확한 URL을 명시하는 것이 안정적입니다.

STEP 04. 회원가입

NextAuth는 기본적으로 회원가입 기능을 제공하지 않습니다.
그래서 사용자는 NextAuth를 이용해서 로그인은 할 수 있지만, 새로운 계정을 만드는 기능은 개발자가 직접 만들어야 합니다.
하지만, 소셜로그인을 사용하면, 로그인할 때 자동으로 계정을 생성할 수 있습니다.

아이디와 비밀번호를 사용한 credentials 방법을 이용한다면, authorize() 메서드 내부에서 백엔드 API /auth/signup 를 호출해서 계정을 생성합니다.

// src/app/api/signup/route.ts
import { NextResponse } from ‘next/server’;

export async function POST(req: Request) {
	const { email, password } = await req.json();
	//  중간 인증과정 생략
	return NextResponse.json({ success: true, msg: ‘’ });
}

STEP 05. 로그인

클라이언트에서 signIn() 메서드를 호출하면, NextAuth는 내부적으로 /api/auth/signin API 에 로그인요청을 보내서 처리합니다.

// 회원가입 로직
import { signIn } from ‘next-auth/react’;

async function handleLogin(){
	// 기본적으로 redirect 값은 true로 리디렉션된다.
	const result = await signIn('cre’dentials, { email: ‘’, password: ‘’, redirect: false });
	if(result?.error) console.log(‘로그인 실패:, result.error);
	else console.log(‘로그인성공’);
}

STEP 06. 로그아웃

signOut() 메서드를 호출하면 NextAuth 내부적으로 /api/auth/signout API에 로그아웃 요청을 보내서 세션을 삭제하고 쿠키를 만료시킵니다.

// 로그아웃 로직
import { signOut } from ‘next-auth/react’;

<button onClick={()=>signOut()}>로그아웃</button>

5️⃣ 세션데이터 확인방법

✅ 클라이언트에서 세션데이터 확인하는 방법

useSession() NextAuth에서 제공하는 리액트 훅으로, 클라이언트 측 컴포넌트에서 현재 사용자의 세션 데이터를 가져오고 상태를 실시간으로 반영할 수 있어서 인증 상태가 변경되면 자동으로 업데이트됩니다.
NextAuth 가 자동으로 내부적으로 /api/auth/session 엔드포인트를 호출해서 세션을 가져옵니다.

import { useSession } from ‘next-auth/react’;

export default function Client(){
	const { data: session, status } = useSession();
	if(status === ‘loading’) return <p>로딩중…</p>;
	if(session) return <p>안녕하세요 {session.user.name}</p>
	return <p>로그인을 해주세요.</p>;
}

서버에서 세션데이터 확인하는 방법

클라이언트 요청이 들어오기 전에 서버에서 미리 세션 정보를 가져와서 페이지를 렌더링할 수 있습니다.
클라이언트가 아니라 서버에서 실행되기 때문에 보안적으로 안전합니다.

✅ Page Router를 사용하는 Next.js 12 이하 버전

Next.js 12 이하의 Page Router 방식에서는, 각 페이지 컴포넌트의 getServerSideProps 함수 내부에서 getServerSession 메서드를 사용하여 세션 데이터를 가져옵니다.

// src/pages/protected.tsx
import { getServerSession } from ‘next-auth’;
import { authOptions } from ‘@/api/auth/[…nextauth]export async function getServerSideProps(context) {
	const session = await getServerSession(context.req, context.res, authOptions);
	if(!session) return { redirect: { destination:/, permanent: false, } };
	return { props: { session }, };
}

export default function ProtectedPage({ session }){
	return <div>안녕하세요 {session.user.name}</div>
}

✅ App Router를 사용하는 Next.js13 이상 버전

Next.js 13부터는 App Router와 서버 컴포넌트 개념이 도입되었습니다.
이로 인해, 각 컴포넌트에서 직접 getServerSideProps 함수를 사용하는 대신, 별도의 auth.ts 파일을 생성해서 세션 관리를 중앙에서 처리할 수 있습니다.

// src/auth.ts
import { getServerSession } from ‘next-auth’;
import { authOptions } from ‘@/app/api/auth/[…nextauth]/route.ts

export async function auth(){
	return await getServerSession(authOptions);
}
src/app/protected/page.tsx
import { auth } from ‘@/auth’;
import { redirect } from ‘next/navigation’;

export default async function ProtectedPage(){
	const session = await auth();
	if(!session) redirect(/);
	return <div>안녕하세요 {session.user.name}</div>;
}

이러한 구조를 통해, 각 서버 컴포넌트에서 getServerSession 함수를 직접 호출하지 않고, auth.ts 파일의 auth 함수를 사용해서 세션을 확인하고 관리할 수 있습니다.

💡getServerSideProps

getServerSideProps는 Next.js의 Page Router에서 서버 사이드 렌더링을 구현하기 위한 함수입니다.
getServerSideProps 함수는 클라이언트의 페이지 요청마다 서버에서 실행되어 API 엔드포인트에서 데이터를 가져오고, 그 데이터를 페이지 컴포넌트에 props로 전달합니다.
페이지 컴포넌트는 getServerSideProps 함수에서 전달받은 props를 사용하여 렌더링해서 HTML을 생성합니다. (= 서버에서 HTML을 렌더링합니다.)
페이지 컴포넌트에서 생성된 HTML은 클라이언트에 전달되어 즉시 화면에 렌더링됩니다.

profile
새로운 것을 도전하고 배운것을 정리하려 합니다.

0개의 댓글