Next.js 통합

Better Auth는 Next.js와 쉽게 통합할 수 있습니다. 시작하기 전에 Better Auth 인스턴스가 구성되어 있는지 확인하세요. 아직 설정하지 않았다면 설치 가이드를 확인하세요.

API 라우트 생성

핸들러를 API 라우트에 마운트해야 합니다. /api/auth/[...all] 디렉터리 내에 라우트 파일을 생성하고 다음 코드를 추가하세요:

api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { GET, POST } = toNextJsHandler(auth.handler);

Better Auth 설정에서 경로를 변경할 수 있지만 /api/auth/[...all]로 유지하는 것을 권장합니다

pages 라우트의 경우 toNextJsHandler 대신 toNodeHandler를 사용하고 config 객체에서 bodyParserfalse로 설정해야 합니다. 다음은 예시입니다:

pages/api/auth/[...all].ts
import { toNodeHandler } from "better-auth/node"
import { auth } from "@/lib/auth"

// 바디 파싱 비활성화, 수동으로 파싱할 것입니다
export const config = { api: { bodyParser: false } }

export default toNodeHandler(auth.handler)

클라이언트 생성

클라이언트 인스턴스를 생성합니다. 파일 이름은 원하는 대로 지정할 수 있습니다. 여기서는 lib/ 디렉터리 내에 client.ts 파일을 생성합니다.

auth-client.ts
import { createAuthClient } from "better-auth/react" // better-auth/react에서 import해야 합니다

export const authClient =  createAuthClient({
    // 클라이언트 설정을 여기에 전달할 수 있습니다
})

클라이언트를 생성한 후에는 회원가입, 로그인 및 기타 작업을 수행할 수 있습니다. 일부 작업은 반응형입니다. 클라이언트는 nano-store를 사용하여 상태를 저장하고 상태가 변경될 때 컴포넌트를 다시 렌더링합니다.

클라이언트는 또한 better-fetch를 사용하여 요청을 만듭니다. 클라이언트에 fetch 설정을 전달할 수 있습니다.

RSC 및 서버 액션

auth 인스턴스에서 내보낸 api 객체에는 서버에서 수행할 수 있는 모든 작업이 포함되어 있습니다. Better Auth 내에서 만들어진 모든 엔드포인트는 함수로 호출할 수 있습니다. 플러그인 엔드포인트도 포함됩니다.

예제: 서버 액션에서 세션 가져오기

server.ts
import { auth } from "@/lib/auth"
import { headers } from "next/headers"

const someAuthenticatedAction = async () => {
    "use server";
    const session = await auth.api.getSession({
        headers: await headers()
    })
};

예제: RSC에서 세션 가져오기

import { auth } from "@/lib/auth"
import { headers } from "next/headers"

export async function ServerComponent() {
    const session = await auth.api.getSession({
        headers: await headers()
    })
    if(!session) {
        return <div>인증되지 않음</div>
    }
    return (
        <div>
            <h1>환영합니다 {session.user.name}님</h1>
        </div>
    )
}
RSC는 쿠키를 설정할 수 없으므로, 서버 액션이나 라우트 핸들러를 통해 클라이언트에서 서버와 상호작용할 때까지 쿠키 캐시가 새로고침되지 않습니다.

서버 액션 쿠키

서버 액션에서 signInEmail이나 signUpEmail과 같이 쿠키를 설정해야 하는 함수를 호출할 때 쿠키가 설정되지 않습니다. 서버 액션은 쿠키를 설정하기 위해 Next.js의 cookies 헬퍼를 사용해야 하기 때문입니다.

이를 간단하게 하기 위해 nextCookies 플러그인을 사용할 수 있습니다. 이 플러그인은 응답에 Set-Cookie 헤더가 있을 때마다 자동으로 쿠키를 설정합니다.

auth.ts
import { betterAuth } from "better-auth";
import { nextCookies } from "better-auth/next-js";

export const auth = betterAuth({
    //...설정
    plugins: [nextCookies()] // 배열의 마지막 플러그인으로 추가해야 합니다
})

이제 쿠키를 설정하는 함수를 호출하면 자동으로 설정됩니다.

"use server";
import { auth } from "@/lib/auth"

const signIn = async () => {
    await auth.api.signInEmail({
        body: {
            email: "user@email.com",
            password: "password",
        }
    })
}

미들웨어

Next.js 미들웨어에서는 API 호출이나 데이터베이스 호출로 요청을 차단하지 않도록 세션 쿠키의 존재 여부만 확인하여 리디렉션을 처리하는 것이 좋습니다.

이를 위해 Better Auth의 getSessionCookie 헬퍼를 사용할 수 있습니다:

getSessionCookie() 함수는 auth.ts에 지정된 인증 설정을 자동으로 참조하지 않습니다. 따라서 쿠키 이름이나 접두사를 사용자 정의한 경우 getSessionCookie()의 설정이 auth.ts에 정의된 설정과 일치하는지 확인해야 합니다.

import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";

export async function middleware(request: NextRequest) {
	const sessionCookie = getSessionCookie(request);

    // 이것은 안전하지 않습니다!
    // 사용자를 낙관적으로 리디렉션하기 위한 권장 접근 방식입니다
    // 각 페이지/라우트에서 인증 확인을 처리하는 것을 권장합니다
	if (!sessionCookie) {
		return NextResponse.redirect(new URL("/", request.url));
	}

	return NextResponse.next();
}

export const config = {
	matcher: ["/dashboard"], // 미들웨어가 적용될 라우트 지정
};

보안 경고: getSessionCookie 함수는 세션 쿠키의 존재 여부만 확인하며 유효성을 검증하지 않습니다. 보안을 위해 이 확인만 의존하는 것은 위험합니다. 누구나 수동으로 쿠키를 생성하여 우회할 수 있기 때문입니다. 보호된 작업이나 페이지에 대해서는 항상 서버에서 세션을 검증해야 합니다.

커스텀 쿠키 이름이나 접두사가 있는 경우 getSessionCookie 함수에 전달할 수 있습니다.

const sessionCookie = getSessionCookie(request, {
    cookieName: "my_session_cookie",
    cookiePrefix: "my_prefix"
});

또는 getCookieCache 헬퍼를 사용하여 쿠키 캐시에서 세션 객체를 가져올 수 있습니다.

import { getCookieCache } from "better-auth/cookies";

export async function middleware(request: NextRequest) {
	const session = await getCookieCache(request);
	if (!session) {
		return NextResponse.redirect(new URL("/sign-in", request.url));
	}
	return NextResponse.next();
}

각 페이지/라우트에서 인증 확인을 처리하는 방법

이 예제에서는 서버 컴포넌트 내에서 auth.api.getSession 함수를 사용하여 세션 객체를 가져온 다음 세션이 유효한지 확인합니다. 유효하지 않으면 사용자를 로그인 페이지로 리디렉션합니다.

app/dashboard/page.tsx
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
    const session = await auth.api.getSession({
        headers: await headers()
    })

    if(!session) {
        redirect("/sign-in")
    }

    return (
        <div>
            <h1>환영합니다 {session.user.name}님</h1>
        </div>
    )
}

Next.js 릴리스 15.1.7 이하 버전

전체 세션 객체가 필요한 경우 /get-session API 라우트에서 가져와야 합니다. Next.js 미들웨어는 Node.js API를 직접 실행할 수 없으므로 HTTP 요청을 해야 합니다.

이 예제는 better-fetch를 사용하지만 어떤 fetch 라이브러리든 사용할 수 있습니다.

import { betterFetch } from "@better-fetch/fetch";
import type { auth } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";

type Session = typeof auth.$Infer.Session;

export async function middleware(request: NextRequest) {
	const { data: session } = await betterFetch<Session>("/api/auth/get-session", {
		baseURL: request.nextUrl.origin,
		headers: {
			cookie: request.headers.get("cookie") || "", // 요청에서 쿠키 전달
		},
	});

	if (!session) {
		return NextResponse.redirect(new URL("/sign-in", request.url));
	}

	return NextResponse.next();
}

export const config = {
	matcher: ["/dashboard"], // 특정 라우트에 미들웨어 적용
};

Next.js 릴리스 15.2.0 이상 버전

15.2.0 버전부터 Next.js는 미들웨어에서 Node.js 런타임을 사용할 수 있습니다. 즉, 미들웨어에서 auth.api 객체를 직접 사용할 수 있습니다.

런타임 설정에 대한 자세한 내용과 활성화 방법은 Next.js 문서를 참조하세요. 새로운 런타임을 사용할 때는 주의하세요. 실험적 기능이므로 중대한 변경사항이 있을 수 있습니다.

import { NextRequest, NextResponse } from "next/server";
import { headers } from "next/headers";
import { auth } from "@/lib/auth";

export async function middleware(request: NextRequest) {
    const session = await auth.api.getSession({
        headers: await headers()
    })

    if(!session) {
        return NextResponse.redirect(new URL("/sign-in", request.url));
    }

    return NextResponse.next();
}

export const config = {
  runtime: "nodejs",
  matcher: ["/dashboard"], // 특정 라우트에 미들웨어 적용
};

On this page