dd/DRIZZLE_MIGRATION_COMPLETE.md

6.8 KiB

Drizzle ORM 마이그레이션 완료

🎉 변경 사항 요약

JWT 인증 시스템이 순수 SQL에서 Drizzle ORM으로 완전히 마이그레이션되었습니다.

📊 Before & After

Before (순수 SQL)

// ❌ 타입 안전성 없음
const user = await db
  .prepare('SELECT id, email, password_hash, nickname FROM users WHERE email = ?')
  .bind(email)
  .first<User>();

After (Drizzle ORM)

// ✅ 완전한 타입 안전성
const [user] = await drizzleDb
  .select()
  .from(users)
  .where(eq(users.email, email))
  .limit(1);

📦 설치된 패키지

{
  "dependencies": {
    "drizzle-orm": "^0.44.7",
    "jose": "^6.1.2"
  },
  "devDependencies": {
    "drizzle-kit": "^0.31.7"
  }
}

📁 새로운 파일 구조

프로젝트/
├── drizzle.config.ts                    # ✨ 신규: Drizzle 설정
├── drizzle/                             # ✨ 신규: 마이그레이션 디렉토리
│   ├── 0000_omniscient_lady_mastermind.sql
│   └── meta/
├── src/
│   └── lib/
│       └── server/
│           ├── schema.ts                # ✨ 신규: Drizzle 스키마
│           ├── db.ts                    # 🔄 수정: Drizzle 사용
│           └── auth.ts                  # 유지
├── AUTH_GUIDE.md                        # 🔄 업데이트
├── DRIZZLE_GUIDE.md                     # ✨ 신규
└── package.json                         # 🔄 스크립트 추가

🛠️ 새로운 NPM 스크립트

# Drizzle 마이그레이션 생성
pnpm db:generate

# 로컬 DB에 적용
pnpm db:push

# 프로덕션 DB에 적용
pnpm db:push:remote

# Drizzle Studio 실행
pnpm db:studio

# DB 쿼리 (로컬)
pnpm db:query

# DB 쿼리 (프로덕션)
pnpm db:query:remote

🔧 주요 변경 파일

1. src/lib/server/schema.ts (신규)

Drizzle 스키마 정의:

import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { sql } from 'drizzle-orm';

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  email: text('email').notNull().unique(),
  passwordHash: text('password_hash').notNull(),
  nickname: text('nickname').notNull(),
  createdAt: integer('created_at', { mode: 'timestamp' })
    .notNull()
    .default(sql`(strftime('%s', 'now'))`)
});

export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;

2. src/lib/server/db.ts (수정)

Drizzle ORM 사용:

import { drizzle } from 'drizzle-orm/d1';
import { eq } from 'drizzle-orm';
import { users, type User, type NewUser } from './schema';

export function getDb(d1: D1Database) {
  return drizzle(d1);
}

export async function getUserByEmail(db: D1Database, email: string) {
  const drizzleDb = getDb(db);
  
  const [user] = await drizzleDb
    .select()
    .from(users)
    .where(eq(users.email, email))
    .limit(1);
  
  return user || null;
}

3. drizzle.config.ts (신규)

Drizzle 설정:

import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  schema: './src/lib/server/schema.ts',
  out: './drizzle',
  dialect: 'sqlite'
});

테스트 완료

  • Drizzle ORM 설치 완료
  • 스키마 정의 완료
  • 마이그레이션 생성 완료
  • 로컬 DB 적용 완료
  • db.ts Drizzle로 변환 완료
  • TypeScript 빌드 성공
  • 모든 함수 타입 안전성 확보

🚀 실행 방법

1. 데이터베이스 초기화

# 이미 완료됨! 마이그레이션이 로컬 DB에 적용되었습니다.
pnpm db:push  # 필요시 재실행

2. 개발 서버 실행

pnpm cf:dev

3. 테스트

🎯 Drizzle ORM의 이점

1. 타입 안전성

// 컴파일 타임에 에러 발견
const user = await drizzleDb
  .select()
  .from(users)
  .where(eq(users.emial, email));  // ← 컴파일 에러!
//                ^^^^^ 오타

2. 자동 완성

IDE에서 테이블과 컬럼에 대한 완벽한 자동 완성 지원

3. 자동 마이그레이션

# 스키마 변경 후
pnpm db:generate  # SQL 자동 생성

4. SQL 인젝션 방지

모든 쿼리가 자동으로 파라미터화됨

5. 리팩토링 용이

테이블/컬럼명 변경 시 TypeScript가 모든 사용처를 찾아줌

📚 문서

  • DRIZZLE_GUIDE.md - Drizzle ORM 상세 가이드

    • 스키마 정의 방법
    • 고급 쿼리 예제
    • 마이그레이션 관리
    • Drizzle Studio 사용법
  • AUTH_GUIDE.md - JWT 인증 가이드 (Drizzle 반영)

    • 로컬 개발 환경 설정
    • 프로덕션 배포
    • 보안 고려사항
  • IMPLEMENTATION_SUMMARY.md - 전체 구현 요약

🔄 마이그레이션 히스토리

drizzle/
└── 0000_omniscient_lady_mastermind.sql  # 초기 users 테이블

💡 다음 단계 제안

이제 Drizzle ORM을 활용하여 쉽게 기능을 확장할 수 있습니다:

1. 프로필 기능 추가

export const profiles = sqliteTable('profiles', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  userId: integer('user_id').references(() => users.id),
  avatar: text('avatar'),
  bio: text('bio')
});

2. 게시글 시스템

export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  userId: integer('user_id').references(() => users.id),
  title: text('title').notNull(),
  content: text('content').notNull(),
  createdAt: integer('created_at', { mode: 'timestamp' }).notNull()
});

3. 댓글 기능

export const comments = sqliteTable('comments', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  postId: integer('post_id').references(() => posts.id),
  userId: integer('user_id').references(() => users.id),
  content: text('content').notNull()
});

모든 관계가 타입 안전하게 관리됩니다!

🎊 완료!

Drizzle ORM으로의 마이그레이션이 성공적으로 완료되었습니다!

  • 타입 안전성 확보
  • 개발 생산성 향상
  • 유지보수성 개선
  • 자동 마이그레이션 시스템 구축

이제 다음 명령어로 서버를 실행하세요:

pnpm cf:dev

생성된 파일:

  • drizzle.config.ts
  • src/lib/server/schema.ts
  • 🔄 src/lib/server/db.ts (Drizzle로 재작성)
  • drizzle/0000_omniscient_lady_mastermind.sql
  • DRIZZLE_GUIDE.md
  • 🔄 AUTH_GUIDE.md (업데이트)
  • 🔄 package.json (스크립트 추가)

기존 파일 백업:

  • 📦 src/lib/server/db-old.ts (순수 SQL 버전)