Turso와 Drizzle ORM 설정하기
이 튜토리얼에서는 Turso 와 함께 Drizzle ORM을 사용하는 방법을 설명합니다.
This guide assumes familiarity with:
Drizzle ORM과 Drizzle kit 을 설치해야 합니다. 다음 명령어를 실행하여 설치할 수 있습니다:
npm i drizzle-orm
npm i -D drizzle-kit
yarn add drizzle-orm
yarn add -D drizzle-kit
pnpm add drizzle-orm
pnpm add -D drizzle-kit
bun add drizzle-orm
bun add -D drizzle-kit
환경 변수를 관리하기 위해 dotenv
패키지를 설치해야 합니다. 이 패키지에 대한 자세한 내용은 여기 에서 확인할 수 있습니다.
npm i dotenv
yarn add dotenv
pnpm add dotenv
bun add dotenv
@libsql/client
패키지를 설치해야 합니다. 이 패키지에 대한 자세한 내용은 여기 에서 확인할 수 있습니다.
npm i @libsql/client
yarn add @libsql/client
pnpm add @libsql/client
bun add @libsql/client
Turso CLI를 설치해야 합니다. 자세한 내용은 문서 를 참고하세요.
Turso 는 libSQL 을 기반으로 한 SQLite 호환 데이터베이스입니다. libSQL은 SQLite의 오픈 소스 포크입니다. Turso는 조직당 수십만 개의 데이터베이스로 확장할 수 있으며, 마이크로초 단위의 지연 시간으로 접근할 수 있도록 자체 서버를 포함한 모든 위치로의 복제를 지원합니다. Turso의 개념에 대해 더 자세히 알아보려면 여기 를 참고하세요.
Drizzle ORM은 libSQL 드라이버를 기본적으로 지원합니다. SQL 방언과 방언별 드라이버 및 구문을 수용하며, 가장 많이 사용되는 SQLite 스타일의 all
, get
, values
, run
쿼리 메서드 구문을 대부분 반영합니다.
Turso 데이터베이스를 설정하는 방법은 공식 문서 를 참고하세요.
Turso 회원가입 또는 로그인 회원가입:
turso auth signup
로그인:
turso auth login
새로운 데이터베이스 생성 turso db create <DATABASE_NAME>
명령어를 실행하여 새로운 데이터베이스를 생성합니다:
turso db create drizzle-turso-db
생성된 데이터베이스 정보를 확인하려면 다음 명령어를 실행하세요:
turso db show drizzle-turso-db
인증 토큰 생성하기 데이터베이스용 인증 토큰을 생성하려면 다음 커맨드를 실행하세요:
turso db tokens create drizzle-turso-db
이 커맨드와 관련된 옵션에 대해 더 자세히 알아보려면 공식 문서 를 참고하세요.
환경 변수 업데이트 .env
또는 .env.local
파일에 연결 URL과 인증 토큰을 추가하세요.
TURSO_CONNECTION_URL=
TURSO_AUTH_TOKEN=
Drizzle ORM을 데이터베이스에 연결하기 src/db
디렉토리에 index.ts
파일을 생성하고 데이터베이스 설정을 구성합니다:
import { config } from 'dotenv' ;
import { drizzle } from 'drizzle-orm/libsql' ;
// .env 또는 .env.local 파일에서 환경 변수 로드
config ({ path : '.env' });
// Drizzle ORM을 사용해 데이터베이스 연결 설정
export const db = drizzle ({
connection : {
url : process . env . TURSO_CONNECTION_URL ! , // TURSO 데이터베이스 연결 URL
authToken : process . env . TURSO_AUTH_TOKEN ! , // TURSO 인증 토큰
} ,
});
이 코드는 Drizzle ORM을 사용해 데이터베이스에 연결하는 기본 설정을 보여줍니다. 환경 변수 파일(.env
또는 .env.local
)에서 TURSO_CONNECTION_URL
과 TURSO_AUTH_TOKEN
을 읽어와 데이터베이스 연결을 구성합니다.
테이블 생성하기 src/db
디렉토리에 schema.ts
파일을 생성하고 테이블을 선언합니다:
import { sql } from 'drizzle-orm' ;
import { integer , sqliteTable , text } from 'drizzle-orm/sqlite-core' ;
// 사용자 테이블 정의
export const usersTable = sqliteTable ( 'users' , {
id : integer ( 'id' ) .primaryKey () , // 기본 키
name : text ( 'name' ) .notNull () , // 이름 (필수)
age : integer ( 'age' ) .notNull () , // 나이 (필수)
email : text ( 'email' ) .unique () .notNull () , // 이메일 (고유, 필수)
});
// 게시물 테이블 정의
export const postsTable = sqliteTable ( 'posts' , {
id : integer ( 'id' ) .primaryKey () , // 기본 키
title : text ( 'title' ) .notNull () , // 제목 (필수)
content : text ( 'content' ) .notNull () , // 내용 (필수)
userId : integer ( 'user_id' ) // 사용자 ID (외래 키)
.notNull ()
.references (() => usersTable .id , { onDelete : 'cascade' }) , // 사용자 삭제 시 연쇄 삭제
createdAt : text ( 'created_at' ) // 생성일
.default ( sql `(CURRENT_TIMESTAMP)` ) // 기본값: 현재 시간
.notNull () ,
updateAt : integer ( 'updated_at' , { mode : 'timestamp' }) // 수정일
.$onUpdate (() => new Date ()) , // 업데이트 시 현재 시간으로 자동 설정
});
// 사용자 삽입 타입
export type InsertUser = typeof usersTable .$inferInsert;
// 사용자 선택 타입
export type SelectUser = typeof usersTable .$inferSelect;
// 게시물 삽입 타입
export type InsertPost = typeof postsTable .$inferInsert;
// 게시물 선택 타입
export type SelectPost = typeof postsTable .$inferSelect;
Drizzle 설정 파일 설정하기 Drizzle 설정 파일 은 Drizzle Kit 에서 사용되는 파일로, 데이터베이스 연결 정보, 마이그레이션 폴더, 스키마 파일 등에 대한 모든 정보를 포함합니다.
프로젝트 루트에 drizzle.config.ts
파일을 생성하고 다음 내용을 추가하세요:
import { config } from 'dotenv' ;
import { defineConfig } from 'drizzle-kit' ;
// .env 파일에서 환경 변수 로드
config ({ path : '.env' });
export default defineConfig ({
schema : './src/db/schema.ts' , // 스키마 파일 경로
out : './migrations' , // 마이그레이션 파일이 생성될 경로
dialect : 'turso' , // 사용할 데이터베이스 dialect
dbCredentials : {
url : process . env . TURSO_CONNECTION_URL ! , // Turso 연결 URL
authToken : process . env . TURSO_AUTH_TOKEN ! , // Turso 인증 토큰
} ,
});
이 설정 파일은 Drizzle Kit이 데이터베이스와 상호작용하는 데 필요한 정보를 제공합니다. schema
는 데이터베이스 스키마를 정의한 파일의 경로를, out
은 마이그레이션 파일이 저장될 경로를 지정합니다. dialect
는 사용할 데이터베이스의 종류를 나타내며, dbCredentials
에는 데이터베이스 연결에 필요한 자격 증명이 포함됩니다.
데이터베이스에 변경 사항 적용하기 drizzle-kit generate
명령어를 사용해 마이그레이션을 생성하고, drizzle-kit migrate
명령어로 실행할 수 있습니다.
마이그레이션 생성:
npx drizzle-kit generate
생성된 마이그레이션은 drizzle.config.ts
에 지정된 migrations
디렉토리에 저장됩니다. 이 디렉토리에는 데이터베이스 스키마를 업데이트하는 데 필요한 SQL 파일과, 다양한 마이그레이션 단계에서의 스키마 스냅샷을 저장하는 meta
폴더가 포함됩니다.
생성된 마이그레이션 예제:
CREATE TABLE ` posts ` (
`id` integer PRIMARY KEY NOT NULL ,
`title` text NOT NULL ,
`content` text NOT NULL ,
`user_id` integer NOT NULL ,
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL ,
`updated_at` integer ,
FOREIGN KEY ( `user_id` ) REFERENCES `users` ( `id` ) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE ` users ` (
`id` integer PRIMARY KEY NOT NULL ,
`name` text NOT NULL ,
`age` integer NOT NULL ,
`email` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX ` users_email_unique ` ON `users` ( `email` );
마이그레이션 실행:
npx drizzle-kit migrate
또는, Drizzle kit push 명령어 를 사용해 데이터베이스에 직접 변경 사항을 적용할 수도 있습니다:
npx drizzle-kit push
IMPORTANT
푸시 명령어는 로컬 개발 환경에서 새로운 스키마 디자인이나 변경 사항을 빠르게 테스트해야 할 때 유용합니다. 마이그레이션 파일을 관리하는 오버헤드 없이 빠르게 반복 작업을 할 수 있습니다.
기본 파일 구조
이 프로젝트의 기본 파일 구조입니다. src/db
디렉토리에는 데이터베이스 관련 파일들이 위치하며, index.ts
파일에는 데이터베이스 연결 설정이, schema.ts
파일에는 스키마 정의가 포함되어 있습니다.
📦 <project root>
├ 📂 src
│ ├ 📂 db
│ │ ├ 📜 index.ts
│ │ └ 📜 schema.ts
├ 📂 migrations
│ ├ 📂 meta
│ │ ├ 📜 _journal.json
│ │ └ 📜 0000_snapshot.json
│ └ 📜 0000_watery_spencer_smythe.sql
├ 📜 .env
├ 📜 drizzle.config.ts
├ 📜 package.json
└ 📜 tsconfig.json
쿼리 예제
예를 들어, src/db/queries
폴더를 만들고 각 작업(삽입, 선택, 업데이트, 삭제)별로 파일을 분리합니다.
데이터 삽입
데이터 삽입 쿼리에 대한 자세한 내용은 문서 를 참고하세요.
import { db } from '../index' ;
import { InsertPost , InsertUser , postsTable , usersTable } from '../schema' ;
export async function createUser (data : InsertUser ) {
await db .insert (usersTable) .values (data);
}
export async function createPost (data : InsertPost ) {
await db .insert (postsTable) .values (data);
}
데이터 선택하기
선택 쿼리에 대해 더 알아보려면 문서 를 참고하세요.
import { asc , count , eq , getTableColumns , gt , sql } from 'drizzle-orm' ;
import { db } from '../index' ;
import { SelectUser , postsTable , usersTable } from '../schema' ;
export async function getUserById (id : SelectUser [ 'id' ]) : Promise <
Array <{
id : number ;
name : string ;
age : number ;
email : string ;
}>
> {
return db .select () .from (usersTable) .where ( eq ( usersTable .id , id));
}
export async function getUsersWithPostsCount (
page = 1 ,
pageSize = 5 ,
) : Promise <
Array <{
postsCount : number ;
id : number ;
name : string ;
age : number ;
email : string ;
}>
> {
return db
.select ({
... getTableColumns (usersTable) ,
postsCount : count ( postsTable .id) ,
})
.from (usersTable)
.leftJoin (postsTable , eq ( usersTable .id , postsTable .userId))
.groupBy ( usersTable .id)
.orderBy ( asc ( usersTable .id))
.limit (pageSize)
.offset ((page - 1 ) * pageSize);
}
export async function getPostsForLast24Hours (
page = 1 ,
pageSize = 5 ,
) : Promise <
Array <{
id : number ;
title : string ;
}>
> {
return db
.select ({
id : postsTable .id ,
title : postsTable .title ,
})
.from (postsTable)
.where ( gt ( postsTable .createdAt , sql `(datetime('now','-24 hour'))` ))
.orderBy ( asc ( postsTable .title) , asc ( postsTable .id))
.limit (pageSize)
.offset ((page - 1 ) * pageSize);
}
또는 관계형 쿼리 구문 을 사용할 수도 있습니다.
데이터 업데이트
데이터 업데이트 쿼리에 대해 더 알아보려면 문서 를 참고하세요.
import { eq } from 'drizzle-orm' ;
import { db } from '../index' ;
import { SelectPost , postsTable } from '../schema' ;
export async function updatePost (id : SelectPost [ 'id' ] , data : Partial < SelectPost >) {
await db .update (postsTable) .set (data) .where ( eq ( postsTable .id , id));
}
데이터 삭제
삭제 쿼리에 대한 자세한 내용은 문서 에서 확인할 수 있습니다.
import { eq } from 'drizzle-orm' ;
import { db } from '../index' ;
import { SelectUser , usersTable } from '../schema' ;
export async function deleteUser (id : SelectUser [ 'id' ]) {
await db .delete (usersTable) .where ( eq ( usersTable .id , id));
}