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

This guide assumes familiarity with:

Drizzleλ₯Ό MySQL λ°μ΄ν„°λ² μ΄μŠ€μ™€ ν•¨κ»˜ μ‚¬μš©ν•˜λ €λ©΄ mysql2 λ“œλΌμ΄λ²„λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

**곡식 μ›Ήμ‚¬μ΄νŠΈ**에 λ”°λ₯΄λ©΄, mysql2λŠ” μ„±λŠ₯에 μ΄ˆμ μ„ 맞좘 Node.js용 MySQL ν΄λΌμ΄μ–ΈνŠΈμž…λ‹ˆλ‹€.

Drizzle ORM은 drizzle-orm/mysql2 νŒ¨ν‚€μ§€λ₯Ό 톡해 mysql2λ₯Ό 기본적으둜 μ§€μ›ν•©λ‹ˆλ‹€.

기본 파일 ꡬ쑰

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

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

1단계 - mysql2 νŒ¨ν‚€μ§€ μ„€μΉ˜ν•˜κΈ°

npm
yarn
pnpm
bun
npm i drizzle-orm mysql2 dotenv
npm i -D drizzle-kit tsx

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

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

DATABASE_URL=
팁

아직 MySQL λ°μ΄ν„°λ² μ΄μŠ€κ°€ μ—†κ³  ν…ŒμŠ€νŠΈμš©μœΌλ‘œ ν•˜λ‚˜λ₯Ό λ§Œλ“€κ³  μ‹Άλ‹€λ©΄, Dockerμ—μ„œ MySQL을 μ„€μ •ν•˜λŠ” 방법에 λŒ€ν•œ κ°€μ΄λ“œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.

Dockerμ—μ„œ MySQL μ„€μ • κ°€μ΄λ“œλŠ” μ—¬κΈ°μ—μ„œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. κ°€μ΄λ“œλ₯Ό 따라 μ„€μ •ν•œ ν›„, λ°μ΄ν„°λ² μ΄μŠ€ URL을 μƒμ„±ν•˜κ³ (κ°€μ΄λ“œμ— μ„€λͺ…λ˜μ–΄ 있음) λ‹€μŒ 단계λ₯Ό μ§„ν–‰ν•˜μ„Έμš”.

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

src/db 디렉토리에 index.ts νŒŒμΌμ„ μƒμ„±ν•˜κ³  연결을 μ΄ˆκΈ°ν™”ν•˜μ„Έμš”:

mysql2
mysql2 with config
your mysql2 driver
import 'dotenv/config';
import { drizzle } from "drizzle-orm/mysql2";

const db = drizzle(process.env.DATABASE_URL);
IMPORTANT

DDL λ§ˆμ΄κ·Έλ ˆμ΄μ…˜κ³Ό ν•¨κ»˜ μ œκ³΅λ˜λŠ” λ‚΄μž₯ migrate ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” 단일 client 연결을 μ‚¬μš©ν•˜λŠ” 것을 κ°•λ ₯히 ꢌμž₯ν•©λ‹ˆλ‹€.

쿼리 λͺ©μ μ˜ 경우, λΉ„μ¦ˆλ‹ˆμŠ€ μš”κ΅¬μ— 따라 client λ˜λŠ” pool을 자유둭게 μ‚¬μš©ν•˜μ„Έμš”.

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

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

import { int, mysqlTable, serial, varchar } from 'drizzle-orm/mysql-core';

export const usersTable = mysqlTable('users_table', {
  id: serial().primaryKey(), // 고유 μ‹λ³„μž, μžλ™ 증가
  name: varchar({ length: 255 }).notNull(), // 이름, μ΅œλŒ€ 255자, ν•„μˆ˜
  age: int().notNull(), // λ‚˜μ΄, ν•„μˆ˜
  email: varchar({ length: 255 }).notNull().unique(), // 이메일, μ΅œλŒ€ 255자, ν•„μˆ˜, 고유
});

5단계 - 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: 'mysql',
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
});

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

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

npx drizzle-kit push

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

팁

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

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

npx drizzle-kit generate

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

npx drizzle-kit migrate

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

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

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

import 'dotenv/config';
import { drizzle } from 'drizzle-orm/mysql2';
import { eq } from 'drizzle-orm';
import { usersTable } from './db/schema';

const db = drizzle(process.env.DATABASE_URL!);

async function main() {
  // μƒˆλ‘œμš΄ μ‚¬μš©μž 데이터 생성
  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();

8단계 - 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 μ„€μΉ˜ λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ„Έμš”.