Drizzle와 Xata μ‹œμž‘ν•˜κΈ°

This guide assumes familiarity with:

기본 파일 ꡬ쑰

이 ν”„λ‘œμ νŠΈμ˜ κΈ°λ³Έ 파일 κ΅¬μ‘°μž…λ‹ˆλ‹€. src/db 디렉토리 μ•ˆμ—λŠ” schema.ts νŒŒμΌμ— ν…Œμ΄λΈ” μ •μ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€. drizzle ν΄λ”μ—λŠ” SQL λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 파일과 μŠ€λƒ…μƒ·μ΄ μžˆμŠ΅λ‹ˆλ‹€.

πŸ“¦ <project root>
 β”œ πŸ“‚ drizzle
 β”œ πŸ“‚ src
 β”‚   β”œ πŸ“‚ db
 β”‚   β”‚  β”” πŸ“œ schema.ts
 β”‚   β”” πŸ“œ index.ts
 β”œ πŸ“œ .env
 β”œ πŸ“œ drizzle.config.ts
 β”œ πŸ“œ package.json
 β”” πŸ“œ tsconfig.json

1단계 - @xata.io/client νŒ¨ν‚€μ§€ μ„€μΉ˜

npm
yarn
pnpm
bun
npm i drizzle-orm @xata.io/client dotenv
npm i -D drizzle-kit tsx

2단계 - μ—°κ²° λ³€μˆ˜ μ„€μ •

ν”„λ‘œμ νŠΈ 루트 디렉토리에 .env νŒŒμΌμ„ μƒμ„±ν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° λ³€μˆ˜λ₯Ό μΆ”κ°€ν•˜μ„Έμš”:

DATABASE_URL=

3단계 - Xata ν΄λΌμ΄μ–ΈνŠΈ μƒμ„±ν•˜κΈ°

Xata ν΄λΌμ΄μ–ΈνŠΈλ₯Ό μƒμ„±ν•˜λ €λ©΄ Xata 곡식 λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

4단계 - Drizzle ORM을 λ°μ΄ν„°λ² μ΄μŠ€μ— μ—°κ²°ν•˜κΈ°

src/db 디렉토리에 index.ts νŒŒμΌμ„ μƒμ„±ν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€ 연결을 μ΄ˆκΈ°ν™”ν•˜λŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

import { drizzle } from 'drizzle-orm/xata-http';
import { getXataClient } from './xata'; // μžλ™ μƒμ„±λœ ν΄λΌμ΄μ–ΈνŠΈ

async function main() {
    const xata = getXataClient();
    const db = drizzle(xata);
}

main();

5단계 - ν…Œμ΄λΈ” 생성

src/db 디렉토리에 schema.ts νŒŒμΌμ„ μƒμ„±ν•˜κ³  ν…Œμ΄λΈ”μ„ μ„ μ–Έν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

import { integer, pgTable, varchar } from "drizzle-orm/pg-core";

export const usersTable = pgTable("users", {
  id: integer().primaryKey().generatedAlwaysAsIdentity(),
  name: varchar({ length: 255 }).notNull(),
  age: integer().notNull(),
  email: varchar({ length: 255 }).notNull().unique(),
});

6단계 - Drizzle μ„€μ • 파일 μ„€μ •ν•˜κΈ°

Drizzle μ„€μ • 파일 - Drizzle Kitμ—μ„œ μ‚¬μš©ν•˜λŠ” μ„€μ • 파일둜, λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 정보, λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 폴더, μŠ€ν‚€λ§ˆ 파일 λ“± λͺ¨λ“  정보λ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈ λ£¨νŠΈμ— drizzle.config.ts νŒŒμΌμ„ μƒμ„±ν•˜κ³  λ‹€μŒ λ‚΄μš©μ„ μΆ”κ°€ν•˜μ„Έμš”:

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!,
  },
});

7단계 - λ°μ΄ν„°λ² μ΄μŠ€μ— λ³€κ²½ 사항 μ μš©ν•˜κΈ°

drizzle-kit push μ»€λ§¨λ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ λ°μ΄ν„°λ² μ΄μŠ€μ— 직접 λ³€κ²½ 사항을 μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이 방법은 둜컬 개발 ν™˜κ²½μ—μ„œ μƒˆλ‘œμš΄ μŠ€ν‚€λ§ˆ λ””μžμΈμ΄λ‚˜ μˆ˜μ • 사항을 λΉ λ₯΄κ²Œ ν…ŒμŠ€νŠΈν•  λ•Œ μœ μš©ν•©λ‹ˆλ‹€. λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ νŒŒμΌμ„ 관리할 ν•„μš” 없이 λΉ λ₯΄κ²Œ 반볡 μž‘μ—…μ„ 진행할 수 μžˆμŠ΅λ‹ˆλ‹€:

npx drizzle-kit push

push μ»€λ§¨λ“œμ— λŒ€ν•΄ 더 μ•Œμ•„λ³΄λ €λ©΄ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

팁

λ˜λŠ” drizzle-kit generate μ»€λ§¨λ“œλ‘œ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ μƒμ„±ν•œ ν›„, drizzle-kit migrate μ»€λ§¨λ“œλ‘œ μ μš©ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€:

λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 생성:

npx drizzle-kit generate

λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 적용:

npx drizzle-kit migrate

λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ ν”„λ‘œμ„ΈμŠ€μ— λŒ€ν•΄ 더 μ•Œμ•„λ³΄λ €λ©΄ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

8단계 - λ°μ΄ν„°λ² μ΄μŠ€ μ‹œλ“œ 및 쿼리

src/index.ts νŒŒμΌμ„ μ‚¬μš©μž 생성, 쑰회, μˆ˜μ •, μ‚­μ œ 쿼리둜 μ—…λ°μ΄νŠΈν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

src/index.ts
import 'dotenv/config';
import { eq } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/xata-http';
import { getXataClient } from './xata';
import { usersTable } from './db/schema';

async function main() {
  const xata = getXataClient();
  const db = drizzle(xata);

  const user: typeof usersTable.$inferInsert = {
    name: 'John',
    age: 30,
    email: 'john@example.com',
  };

  // μ‚¬μš©μž 생성
  await db.insert(usersTable).values(user);
  console.log('μƒˆ μ‚¬μš©μžκ°€ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€!');

  // λͺ¨λ“  μ‚¬μš©μž 쑰회
  const users = await db.select().from(usersTable);
  console.log('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ λͺ¨λ“  μ‚¬μš©μžλ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€: ', users);
  /*
  const users: {
    id: number;
    name: string;
    age: number;
    email: string;
  }[]
  */

  // μ‚¬μš©μž 정보 μ—…λ°μ΄νŠΈ
  await db
    .update(usersTable)
    .set({
      age: 31,
    })
    .where(eq(usersTable.email, user.email));
  console.log('μ‚¬μš©μž 정보가 μ—…λ°μ΄νŠΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€!');

  // μ‚¬μš©μž μ‚­μ œ
  await db.delete(usersTable).where(eq(usersTable.email, user.email));
  console.log('μ‚¬μš©μžκ°€ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€!');
}

main();

9단계 - index.ts 파일 μ‹€ν–‰ν•˜κΈ°

TypeScript νŒŒμΌμ„ μ‹€ν–‰ν•˜λŠ” 방법은 μ—¬λŸ¬ 가지가 μžˆμ§€λ§Œ, μ—¬κΈ°μ„œλŠ” tsxλ₯Ό μ‚¬μš©ν•˜λŠ” 방법을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

이미 tsxλ₯Ό μ„€μΉ˜ν–ˆμœΌλ―€λ‘œ, 이제 쿼리λ₯Ό μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

index.ts 슀크립트 μ‹€ν–‰ν•˜κΈ°

<Npx>
  tsx src/index.ts
</Npx>
팁

TypeScript νŒŒμΌμ„ μ‹€ν–‰ν•  λ•Œ bun을 μ‚¬μš©ν•˜λŠ” 것을 μΆ”μ²œν•©λ‹ˆλ‹€. bun을 μ‚¬μš©ν•˜λ©΄ ν”„λ‘œμ νŠΈκ°€ CommonJS(CJS), ECMAScript Modules(ESM) λ˜λŠ” λ‹€λ₯Έ λͺ¨λ“ˆ ν˜•μ‹μœΌλ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλ”λΌλ„ μΆ”κ°€ μ„€μ • 없이 슀크립트λ₯Ό μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. bun으둜 슀크립트λ₯Ό μ‹€ν–‰ν•˜λ €λ©΄ λ‹€μŒ λͺ…λ Ήμ–΄λ₯Ό μ‚¬μš©ν•˜μ„Έμš”:

bun src/index.ts

λ§Œμ•½ bun이 μ„€μΉ˜λ˜μ–΄ μžˆμ§€ μ•Šλ‹€λ©΄, Bun μ„€μΉ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.