Convex 통합

이 문서는 Convex 문서에서 가져왔습니다. 자세한 내용은 해당 문서를 참조하세요.

사전 준비사항

Convex 프로젝트 생성

Convex + Better Auth를 사용하려면, 먼저 Convex 프로젝트가 필요합니다. 프로젝트가 없다면, 다음 명령을 실행하여 시작하세요.

npm create convex@latest

Convex에 대해 더 알아보려면 Convex 문서를 확인하세요.

convex dev 실행

설정 중에 CLI를 실행하면 Convex 배포가 아직 존재하지 않는 경우 초기화되며, 프로세스 전체에서 생성된 타입을 최신 상태로 유지합니다. 계속 실행 상태를 유지하세요.

npx convex dev

Convex + Better Auth 설치

다음 문서는 Next.js를 사용한다고 가정합니다.

Next.js를 사용하지 않는 경우, Convex의 설치 가이드를 참조하세요.

완전한 예제는 이 Github 저장소에서 Next.js와 함께 Convex + Better Auth 예제를 확인하세요.

설치

패키지 설치

컴포넌트, 고정된 버전의 Better Auth를 설치하고 최신 버전의 Convex를 확보하세요.

이 컴포넌트는 Convex 1.25.0 이상이 필요합니다.

npm install better-auth@1.3.8 --save-exact
npm install convex@latest @convex-dev/better-auth

컴포넌트 등록

Convex 프로젝트에 Better Auth 컴포넌트를 등록하세요.

convex/convex.config.ts
import { defineApp } from "convex/server";
import betterAuth from "@convex-dev/better-auth/convex.config";

const app = defineApp();
app.use(betterAuth);

export default app;

Convex 인증 설정 추가

Better Auth를 인증 제공자로 구성하는 convex/auth.config.ts 파일을 추가하세요.

convex/auth.config.ts
export default {
    providers: [
        {
            domain: process.env.CONVEX_SITE_URL,
            applicationID: "convex",
        },
    ],
};

환경 변수 설정

암호화 및 해시 생성을 위한 시크릿을 생성하세요. openssl이 설치되어 있다면 아래 명령을 사용하거나, 대신 버튼을 사용하여 무작위 값을 생성하세요. 또는 원하는 방식으로 생성하세요.

npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)

Convex 배포에 사이트 URL을 추가하세요.

npx convex env set SITE_URL http://localhost:3000

npx convex dev로 생성된 .env.local 파일에 환경 변수를 추가하세요. 이는 프레임워크 개발 서버에서 자동으로 인식됩니다.

.env.local
# \`npx convex dev\`에서 사용되는 배포
CONVEX_DEPLOYMENT=dev:adjective-animal-123 # team: team-name, project: project-name

NEXT_PUBLIC_CONVEX_URL=https://adjective-animal-123.convex.cloud

# NEXT_PUBLIC_CONVEX_URL과 동일하지만 .site로 끝남
NEXT_PUBLIC_CONVEX_SITE_URL=https://adjective-animal-123.convex.site

# 로컬 사이트 URL
SITE_URL=http://localhost:3000

Better Auth 인스턴스 생성

Better Auth 인스턴스를 생성하고 컴포넌트를 초기화하세요.

파일을 저장할 때까지 일부 TypeScript 오류가 표시됩니다.
convex/auth.ts
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
import { convex } from "@convex-dev/better-auth/plugins";
import { components } from "./_generated/api";
import { DataModel } from "./_generated/dataModel";
import { query } from "./_generated/server";
import { betterAuth } from "better-auth";

const siteUrl = process.env.SITE_URL!;

// 컴포넌트 클라이언트는 Convex와 Better Auth를 통합하는 데 필요한 메서드와
// 일반 사용을 위한 헬퍼 메서드를 가지고 있습니다.
export const authComponent = createClient<DataModel>(components.betterAuth);

export const createAuth = (
    ctx: GenericCtx<DataModel>,
        { optionsOnly } = { optionsOnly: false },
    ) => {
      return betterAuth({
        // createAuth가 옵션을 생성하기 위해서만 호출될 때 로깅을 비활성화합니다.
        // 이것은 필수는 아니지만, 이것이 없으면 로그에 많은 노이즈가 발생합니다.
        logger: {
            disabled: optionsOnly,
        },
        baseURL: siteUrl,
        database: authComponent.adapter(ctx),
        // 시작하기 위해 간단한 비인증 이메일/비밀번호 구성
        emailAndPassword: {
            enabled: true,
            requireEmailVerification: false,
        },
        plugins: [
            // Convex 플러그인은 Convex 호환성을 위해 필수입니다
            convex(),
        ],
    });
};

// 현재 사용자를 가져오는 예제 함수
// 자유롭게 편집, 생략 등을 하세요.
export const getCurrentUser = query({
    args: {},
    handler: async (ctx) => {
        return authComponent.getAuthUser(ctx);
    },
});

Better Auth 클라이언트 인스턴스 생성

클라이언트에서 Better Auth 서버와 상호작용하기 위한 Better Auth 클라이언트 인스턴스를 생성하세요.

src/lib/auth-client.ts
import { createAuthClient } from "better-auth/react";
import { convexClient } from "@convex-dev/better-auth/client/plugins";

export const authClient = createAuthClient({
    plugins: [convexClient()],
});

핸들러 마운트

Convex 배포에 Better Auth 라우트 핸들러를 등록하세요.

convex/http.ts
import { httpRouter } from "convex/server";
import { authComponent, createAuth } from "./auth";

const http = httpRouter();

authComponent.registerRoutes(http, createAuth);

export default http;

프레임워크 서버에서 Convex 배포로 인증 요청을 프록시하도록 라우트 핸들러를 설정하세요.

app/api/auth/[...all]/route.ts
import { nextJsHandler } from "@convex-dev/better-auth/nextjs";

export const { GET, POST } = nextJsHandler();

Convex 클라이언트 프로바이더 설정

앱을 ConvexBetterAuthProvider 컴포넌트로 래핑하세요.

app/ConvexClientProvider.tsx
"use client";

import { ReactNode } from "react";
import { ConvexReactClient } from "convex/react";
import { authClient } from "@/lib/auth-client"; 
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react"; 

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!, {
  // 선택적으로 사용자가 인증될 때까지 쿼리를 일시 중지
  expectAuth: true, 
});

export function ConvexClientProvider({ children }: { children: ReactNode }) {
  return (
    <ConvexBetterAuthProvider client={convex} authClient={authClient}>
      {children}
    </ConvexBetterAuthProvider>
  );
}

완료되었습니다!

이제 Convex와 함께 Better Auth를 사용할 준비가 되었습니다.

사용법

서버에서 Better Auth 사용

서버 렌더링, 서버 함수 또는 기타 Next.js 서버 코드에서 Better Auth의 서버 메서드를 사용하려면, Convex 함수를 사용하고 서버 코드에서 함수를 호출하세요.

먼저, 서버 코드에서 Convex 함수를 호출하기 위한 토큰 헬퍼입니다.

src/lib/auth-server.ts
import { createAuth } from "@/convex/auth";
import { getToken as getTokenNextjs } from "@convex-dev/better-auth/nextjs";

export const getToken = () => {
  return getTokenNextjs(createAuth);
};

다음은 Better Auth의 서버 메서드를 사용하는 Convex 함수의 예와, Convex 함수를 호출하는 서버 액션입니다.

convex/users.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";
import { createAuth, authComponent } from "./auth";

export const updateUserPassword = mutation({
  args: {
    currentPassword: v.string(),
    newPassword: v.string(),
  },
  handler: async (ctx, args) => {
    await createAuth(ctx).api.changePassword({
      body: {
        currentPassword: args.currentPassword,
        newPassword: args.newPassword,
      },
      headers: await authComponent.getHeaders(ctx),
    });
  },
});
app/actions.ts
"use server";

import { fetchMutation } from "convex/nextjs";
import { api } from "../convex/_generated/api";
import { getToken } from "../lib/auth-server";

// 서버 함수를 통한 인증된 뮤테이션
export async function updatePassword({
  currentPassword,
  newPassword,
}: {
  currentPassword: string;
  newPassword: string;
}) {
  const token = await getToken();
  await fetchMutation(
    api.users.updatePassword,
    { currentPassword, newPassword },
    { token }
  );
}

이 문서는 Convex 문서에서 가져왔습니다. 자세한 내용은 해당 문서를 참조하세요.