Next-Auth (with App Router)

derek·2024년 10월 21일
0

Next.js에서 NextAuth를 App Router와 함께 사용하는 방법

NextAuth를 사용하여 인증 기능을 구현할 때, Supabase 데이터베이스와 함께 사용할 경우 해당 OAuth를 이용해야 하지만, 다른 데이터베이스를 사용할 경우 NextAuth가 더 유용할 수 있습니다.

1. NextAuth 설치

App Router를 사용하기 위해 beta 버전을 설치합니다.

yarn add next-auth@beta

그 다음, 터미널에서 다음 명령어를 실행하여 인증 비밀키를 생성합니다.

npx auth secret

이 명령어를 실행하면 .env 파일에 자동으로 비밀키가 생성됩니다.

2. 인증 파일 생성

src/auth.ts 파일을 생성하고 다음 코드를 추가합니다.

import NextAuth from 'next-auth';

export const { handlers, signIn, signOut, auth } = NextAuth({
    providers: [],
});

그 다음, app/api/auth/[...nextAuth]/route.ts 파일을 생성하고 다음과 같이 작성합니다.

import { handlers } from "@/auth";

export const { GET, POST } = handlers;

3. 인증 Provider 설정

auth.ts 파일을 다음과 같이 수정하여 providers를 설정합니다.

import NextAuth, { User } from "next-auth";
import Credentials from "next-auth/providers/credentials";

export const { handlers, signIn, signOut, auth } = NextAuth({
    providers: [
        Credentials({
            name: "Credentials",
            credentials: {
                username: { label: "Username", type: "text", placeholder: "username" },
                password: { label: "Password", type: "password" },
            },
            async authorize(credentials): Promise<User | null> {
                const users = [
                    {
                        id: "test-user-1",
                        userName: "test1",
                        name: "Test 1",
                        password: "pass",
                        email: "test1@donotreply.com",
                    },
                    {
                        id: "test-user-2",
                        userName: "test2",
                        name: "Test 2",
                        password: "pass",
                        email: "test2@donotreply.com",
                    },
                ];
                const user = users.find(
                    (user) =>
                        user.userName === credentials.username &&
                        user.password === credentials.password
                );
                return user ? { id: user.id, name: user.name, email: user.email } : null;
            },
        }),
    ],
    basePath: "/api/auth",
    secret: process.env.AUTH_SECRET,
});

이제 http://localhost:3000/api/auth/signin에 접속하면 로그인 화면이 나타납니다.

4. 로그인 및 로그아웃 기능 구현

로그인 후 세션 정보를 확인하려면 다음 코드를 사용합니다.

import { auth } from "@/auth";

const session = await auth();

로그아웃하려면 http://localhost:3000/api/auth/signout에 접속하면 됩니다.

5. 서버 액션으로 사용하기

server-action.ts 파일을 생성하여 서버와 클라이언트 모두에서 사용할 수 있도록 설정합니다.

// server-action.ts
'use server';

import { signIn as _signIn, signOut as _signOut } from '@/auth';

export async function signIn() {
    await _signIn();
}

export async function signOut() {
    await _signOut();
}

6. 인증 버튼 컴포넌트 만들기

AuthButton.tsx 파일을 생성합니다.

// AuthButton.tsx
import { auth } from "@/auth";
import { SessionProvider } from "next-auth/react";
import { ClientAuthButton } from "./ClientAuthButton";

export const AuthButton = async () => {
    const session = await auth();

    return (
        <SessionProvider session={session}>
            <ClientAuthButton />
        </SessionProvider>
    );
};

ClientAuthButton.tsx 파일을 생성합니다.

// ClientAuthButton.tsx
'use client';

import { signIn, signOut } from '@/server-action';
import { useSession } from "next-auth/react";

export const ClientAuthButton = () => {
    const { data: session } = useSession();

    return session ? (
        <button onClick={async () => { await signOut(); await signIn(); }}>sign-out</button>
    ) : (
        <button onClick={async () => { await signIn(); }}>sign-in</button>
    );
};

7. 커스텀 인증 페이지 만들기

auth.ts 파일에 다음과 같이 커스텀 로그인 페이지 경로를 추가합니다:

pages: {
    signIn: '/signin', // 추가 
},

src/app/signin/page.tsx 파일을 생성하여 로그인 페이지를 만듭니다:

// src/app/signin/page.tsx
import { signInCredentials } from "@/server-action";

export default function SignInPage() {
    return (
        <>
            <h1>Sign In</h1>
            <form action={signInCredentials} style={{ display: "flex", flexDirection: "column", alignItems: "start", gap: "10px" }}>
                <label>
                    Username
                    <input name="username" type="text" />
                </label>
                <label>
                    Password
                    <input name="password" type="password" />
                </label>
                <button>Sign In</button>
            </form>
        </>
    );
}

그리고 src/server-action.tssignInCredentials 함수를 추가합니다:

'use server';

export async function signInCredentials(formData: FormData) {
    await _signIn("credentials", {
        username: formData.get('username'),
        password: formData.get('password'),
        redirectTo: "/",
    });
}

ClientAuthButton.tsx를 다음과 같이 수정합니다:

// app/ClientAuthButton.tsx
'use client';

import { signIn, signOut } from '@/server-action';
import { useSession } from "next-auth/react";
import { useRouter } from 'next/navigation';

export const ClientAuthButton = () => {
    const { data: session } = useSession();
    const router = useRouter();

    return session ? (
        <button onClick={async () => {
            await signOut();
            router.replace('/signin'); // 변경
        }}>Sign Out</button>
    ) : (
        <button onClick={async () => {
            router.replace('/signin'); // 변경
        }}>Sign In</button>
    );
};

이렇게 Next.js와 NextAuth를 이용해 App Router에서 인증 기능을 사용하는 부분을 만들어봤습니다

profile
derek

0개의 댓글