Supabase | The Open Source Firebase Alternative
Build production-grade applications with a Postgres database, Authentication, instant APIs, Realtime, Functions, Storage and Vector embeddings. Start for free.
supabase.com
Drizzle ORM - next gen TypeScript ORM.
Drizzle ORM is a lightweight and performant TypeScript ORM with developer experience in mind.
orm.drizzle.team
https://authjs.dev/getting-started/adapters
최종 파일구조는 다음과 같다
1. 올가니제이션을 만든다
2. 프로젝트를 만든다
3. drizzle에 들어가서 상단 documentation을 클릭한다
4. 좌측 Get Started를 누른 후 supabase를 누른다
5.순서대로 따라한다.
5-1. Step1은 프로젝트 안 cmd에 그대로 따라했다. yarn으로
5-2. 일단 step3로 넘어가서 첫번째 코드는 오류가 나서 두번째 if you need~ 문장 아래의 코드를 넣어서 수정했다.
(/db/index.ts도 만듦)
// /db/index.ts
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
const client = postgres(process.env.DATABASE_URL!)
const db = drizzle({ client });
export {db};
5-3. step 5로 넘어가 루트에 drizzle.config.ts를 만들고 아래 코드를 삽입했다
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
6. Auth.js로 들어가서 install 해준다
yarn add next-auth@beta
npx auth secret
npx auth secret을 하면 env.local이 생기는데 그 안의 코드를 env로 옮긴다
7. src/db/schema.ts를 만들고, src/db/auth.ts를 만들어 다음 코드를 넣는다
"use server"
import {NextAuthOptions} from "@/db/options"
import NextAuth from "next-auth"
export const {handlers, signIn, signOut, auth} = NextAuth(NextAuthOptions);
8. src/app/API/[...nextauth]/route.ts에 다음 코드를 넣는다
import { handlers } from "@/action/auth";
export const {GET, POST} = handlers;
9. /src/middleware.ts 파일에 원래 있던 내용을 삭제하고 아래 코드를 넣는다
export {auth as middleware} from "@/action/auth";
10. 다음을 설치한다(터미널에서)
yarn add drizzle-orm @/auth/drizzle-adapter
yarn add drizzle-kit --dev
11. https://authjs.dev/getting-started/adapters/drizzle#schemas 여기에 들어가서
이 코드를 복사해준다. 나는 아래와 같이 좀 수정했다.
import {
boolean,
timestamp,
pgTable,
text,
primaryKey,
integer,
} from "drizzle-orm/pg-core";
// import postgres from "postgres";
// import { drizzle } from "drizzle-orm/postgres-js";
// import type { AdapterAccountType } from "next-auth/adapters";
// const connectionString = "postgres://postgres:postgres@localhost:5432/drizzle";
// const pool = postgres(connectionString, { max: 1 });
// export const db = drizzle(pool);
export const users = pgTable("user", {
id: text("id")
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
name: text("name"),
email: text("email").unique(),
emailVerified: timestamp("emailVerified", { mode: "date" }),
image: text("image"),
});
export const accounts = pgTable(
"account",
{
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
type: text("type").$type<AdapterAccountType>().notNull(),
provider: text("provider").notNull(),
providerAccountId: text("providerAccountId").notNull(),
refresh_token: text("refresh_token"),
access_token: text("access_token"),
expires_at: integer("expires_at"),
token_type: text("token_type"),
scope: text("scope"),
id_token: text("id_token"),
session_state: text("session_state"),
},
(account) => [
{
compoundKey: primaryKey({
columns: [account.provider, account.providerAccountId],
}),
},
]
);
export const sessions = pgTable("session", {
sessionToken: text("sessionToken").primaryKey(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
expires: timestamp("expires", { mode: "date" }).notNull(),
});
export const verificationTokens = pgTable(
"verificationToken",
{
identifier: text("identifier").notNull(),
token: text("token").notNull(),
expires: timestamp("expires", { mode: "date" }).notNull(),
},
(verificationToken) => [
{
compositePk: primaryKey({
columns: [verificationToken.identifier, verificationToken.token],
}),
},
]
);
export const authenticators = pgTable(
"authenticator",
{
credentialID: text("credentialID").notNull().unique(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
providerAccountId: text("providerAccountId").notNull(),
credentialPublicKey: text("credentialPublicKey").notNull(),
counter: integer("counter").notNull(),
credentialDeviceType: text("credentialDeviceType").notNull(),
credentialBackedUp: boolean("credentialBackedUp").notNull(),
transports: text("transports"),
},
(authenticator) => [
{
compositePK: primaryKey({
columns: [authenticator.userId, authenticator.credentialID],
}),
},
]
);
12. options.ts파일로 가서 아래와 같이 수정합니다
import { NextAuthConfig } from "next-auth";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import Credentials from "next-auth/providers/credentials";
import { db } from ".";
export const NextAuthOptions: NextAuthConfig = {
adapter: DrizzleAdapter(db),
providers: [
Credentials({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
}
})
],
};
13.schema.ts의 users를 입맛에 맞게 바꿔줍니다.
14. package.json의 "scripts":{}안에 아래 코드를 추가했습니다
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint",
"db:generate": "drizzle-kit generate", //추가
"db:migrate": "drizzle-kit migrate",//추가
"db:push": "drizzle-kit push",//추가
"db:studio": "drizzle-kit studio",//추가
"db:full": "drizzle-kit full",//추가
"db:check": "drizzle-kit check"//추가
},
15. yarn db:push를 하면 schema에 있는게 그대로 db에 올라간다.
만약 supabase를 안쓴다면 yarn db:studio를 하면 아래와 같이 나오는데 왜 값이 안나오는지는 잘 모르겠다(선생님이 안알려주심 ㅠㅠ)
16. /scr/action/auth.ts를 만들고 아래와 같은 코드를 삽입했다.
import { error } from "console";
import {eq} from "drizzle-orm"
import {z} from "zod"
const signInSchema = z.object({
email: z.string().email(),
password : z.string.min(8),
});
export type TSignInActionParams = z.infer<typeof signInSchema>;
export const signInAction = async(data: TSignInActionParams) =>{
const {email, password} = signInSchema.parse(data);
const user = await db.query.users.findFirst({
where: eq(users.email, email),
});
if(!user){
return{error: "유저를 찾을 수 없습니다", data:null};
}
if(user.password !== password){
return{error:"일치하지 않는 비밀번호입니다", data:null}
}
return{success: "유저 로그인했습니다", data:"user"}
};
17. /src/db/index.ts 로 돌아와 코드를 아래처럼 수정했다
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import * as schema from "./schema"
const client = postgres(process.env.DATABASE_URL!)
const db = drizzle(client, { schema });
export {db};
18. /src/action/auth.ts
//yarn add drizzle-zod
import { eq } from "drizzle-orm";
import { users } from "@/db/schema";
import { z } from "zod";
import { db } from "@/db";
const signInSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
export type TSignInActionParams = z.infer<typeof signInSchema>;
export const signInAction = async (data: TSignInActionParams) => {
const { email, password } = signInSchema.parse(data);
const user = await db.query.users.findFirst({
where: eq(users.email, email),
});
if (!user) {
return { error: "유저를 찾을 수 없습니다", data: null };
}
};
export const signUpAction;
export const signUpSchema = createInsertSchema(users)
.omit({
id: true,
})
.extend({
email: z.string().email(),
password: z.string().min(8),
});
export type TSignInActionParams = z.infer<typeof signUpSchema>;
export const signUpAction = async (data:TSignInActionParams) => {
const {email, password} = signUpSchema.parse(data);
const user = await db.insert(users).values({email, password}),returning();
return {success:"유저 회원가입 완료",data: user[0]};
};
'코딩 > FrontEnd' 카테고리의 다른 글
React Query를 공부해봅시다 (0) | 2025.03.20 |
---|---|
useImperativeHandle | createPortal | forwardRef | Serveraction | middleWare에 대해 공부해봅시다 (0) | 2025.03.17 |
카카오맵 API 사용 중 Cannot read properties of undefined (reading 'maps') 오류 해결! (0) | 2024.12.14 |
The requested module does not provide an export named 해결법 (1) | 2024.12.12 |