Skip to content

✍️ 필사 모드: TypeScript ORM·쿼리 빌더 2026 — Drizzle·Kysely·Prisma·postgres.js 심층 비교 (SQL에 얼마나 가까워야 하는가)

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

프롤로그 — "Prisma 또는 raw SQL"의 시대는 끝났다

2026년에도 신규 TypeScript 프로젝트의 첫 번째 회의에서 빠지지 않는 질문이 있다.

"DB 접근, 뭐 쓸 거예요?"

2022년만 해도 답은 보통 두 개였다. Prisma(가장 유명, 풀 ORM) 아니면 raw pg/mysql2 (직접 SQL, 타입은 알아서). 사이에는 TypeORM·Sequelize 같은 클래스 기반 ORM이 끼어 있었지만, TypeScript 친화도와 마이그레이션 경험 때문에 신규 프로젝트에서는 점점 안 쓰였다.

2026년은 다르다. 두 개의 새로운 강자가 자리를 굳혔다.

  • Drizzle ORM — "SQL에 가장 가까운" 헤드리스 ORM. 스키마를 TS 코드로 선언하고, 쿼리는 SQL 한 줄 한 줄에 1:1 대응되게 짠다. 번들 크기와 엣지 런타임에 강하다. 2026년 5월 기준 GitHub 스타 31k+, 주간 npm 다운로드 2M+.
  • Kysely — "스키마 없는 순수 쿼리 빌더". 어떤 DB 클라이언트(pg·mysql2·better-sqlite3)도 받아서 타입 안전한 쿼리 빌더로 감싼다. 마이그레이션·스키마 관리는 너의 일이다. GitHub 스타 12k+.

여기에 Prisma가 새로 등장한 두 트럼프 카드.

  • Prisma 엔진 재작성 — Prisma 6 이후 기존 Rust 기반 query engine이 TypeScript 네이티브 + Go 엔진 조합으로 단계적으로 교체됐다. 차가운 시작 지연이 줄고, 엣지 런타임 호환성이 정상화됐고, 번들 크기가 의미 있게 줄었다.
  • Prisma Postgres — Prisma 사가 직접 운영하는 매니지드 Postgres. unikernel·Bare metal 위의 분리 컴퓨트·스토리지 아키텍처로, free tier가 의외로 후하고 cold start가 빠르다.

그리고 모두를 흔드는 한 갈래.

  • postgres.js·pg·mysql2 직접 쓰기 — tagged template literal과 TypeScript의 satisfies·as const만 잘 쓰면, ORM 없이도 충분히 타입 안전하다는 흐름. "쿼리 빌더 자체가 추상화 과잉" 진영.

이 글은 2026년 5월 기준으로 이 도구들을 직접 비교한다. 추상화 정도, 마이그레이션, 엣지, 번들, escape hatch, 멀티 DB의 6축으로. 그리고 같은 쿼리를 네 가지 도구로 나란히 짜본다.


1장 · 풍경 — 도구 지도

먼저 도구를 분류한다. 모든 게 같은 자리에 있지 않다.

분류도구한 줄 요약
풀 ORM (Active Record·Data Mapper)Prisma·MikroORM·TypeORM·Sequelize모델·관계·페치·캐시까지 챙긴다
헤드리스 ORMDrizzle스키마는 코드, 쿼리는 SQL 1:1
쿼리 빌더Kysely·Knex스키마 없이 타입 안전 SQL 합성
Raw 클라이언트 + 타입 헬퍼postgres.js·pg·mysql2 + Zodtagged template + 런타임 검증
관계형 DSLEdgeQL(EdgeDB)·SurrealQLDB가 자체 쿼리 언어

이 글의 초점은 굵게 표시한 4그룹의 대표 — Prisma·Drizzle·Kysely·postgres.js — 다. MikroORM·TypeORM은 마지막 장에서 짧게 다룬다.

왜 이 4개인가

  • Prisma — 가장 유명하고, 신규 풀스택 프로젝트의 기본값에 가깝다. 2026년의 새 엔진과 Postgres 서비스를 합치면 다시 1군이다.
  • Drizzle — 2024~2026 사이 가장 빠르게 자란 도구. Vercel 공식 가이드, Hono·Cloudflare Workers 권장 스택, T3 stack 기본 선택지 중 하나.
  • Kysely — Prisma에서 도망 온 팀이 가장 먼저 만나는 도구. 마이그레이션 후기에 자주 등장한다.
  • postgres.js — Porsager가 만든 빠르고 작은 Postgres 클라이언트. Bun·Cloudflare Workers와 궁합이 좋다. ORM 없는 진영의 사실상 표준.

2장 · 추상화 스펙트럼 — 어디까지 감출 것인가

데이터 접근 라이브러리는 "DB와 코드 사이에 얼마나 두꺼운 층을 둘지"의 선택이다. 왼쪽이 SQL에 가깝고, 오른쪽이 객체에 가깝다.

SQL                                                              객체
 |                                                                |
postgres.js   --   Kysely   --   Drizzle   --   Prisma   --   MikroORM/TypeORM
(raw)             (빌더)         (헤드리스 ORM)   (풀 ORM)        (Active Record)

추상화가 두꺼울수록 좋은가? 단언컨대 아니다. 트레이드오프다.

  • 얇은 쪽 장점: SQL 그대로의 표현력, 성능 튜닝의 직진성, 학습/디버그 코스트가 결국 SQL 한 가지로 수렴.
  • 두꺼운 쪽 장점: 작성 속도, 도메인 객체와 직접 매핑, 관계·N+1 자동 처리, IDE 자동완성의 풍성함.
  • 얇은 쪽 단점: 관계 페치를 직접 짜야 함, 도메인 객체 매핑은 별도 작업.
  • 두꺼운 쪽 단점: 추상화가 부족할 때(window function·CTE·복잡한 join) escape hatch가 어색하고, 번들·콜드 스타트·런타임 호환성 모두 비용.

2026년의 분위기는 "SQL에 가깝게, 단 타입은 안전하게" 쪽으로 기울었다. Drizzle·Kysely·postgres.js의 성장이 그 증거다. 다만 Prisma 6의 엔진 재작성으로 두꺼운 쪽도 가벼워져서, "ORM이라서 무거워요" 논리는 옛말이 되어 가고 있다.


3장 · Drizzle — "TypeScript로 쓰는 SQL"

Drizzle은 2026년 신규 프로젝트에서 가장 많이 선택되는 데이터베이스 도구 중 하나다. 핵심 철학 3가지.

  1. 스키마는 TS 코드schema.ts에서 테이블·컬럼·관계를 선언한다. DB가 진실의 원천이 아니라, TS 파일이 진실의 원천이다.
  2. 쿼리는 SQL 1:1db.select().from(users).where(eq(users.email, '...')) 같은 식. SQL을 아는 사람이 보면 무엇으로 컴파일될지 즉시 알 수 있다.
  3. 헤드리스 + 작음 — 런타임 의존성이 거의 없다. Cloudflare Workers·Vercel Edge·Deno Deploy에 그대로 올라간다. 번들 크기는 압축 기준 7~15KB 수준.

Drizzle 스키마

// db/schema.ts
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core'

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
})

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  authorId: integer('author_id').notNull().references(() => users.id),
  title: text('title').notNull(),
  body: text('body').notNull(),
})

Drizzle Kit으로 마이그레이션

# drizzle.config.ts 작성 후
npx drizzle-kit generate   # 스키마 변경에서 SQL 마이그레이션 생성
npx drizzle-kit migrate    # 실제 DB에 적용
npx drizzle-kit studio     # 로컬 GUI

Drizzle의 강점

  • SQL escape hatch가 자연스러움sql 템플릿 태그로 임의 SQL을 끼워 넣고, 그 부분만 타입을 직접 정의할 수 있다.
  • 엣지 호환성 — 모든 어댑터(drizzle-orm/postgres-js, drizzle-orm/neon-http, drizzle-orm/d1)가 엣지 런타임에서 동작.
  • 여러 DB 지원 — PostgreSQL·MySQL·SQLite·D1·LibSQL·Neon·Planetscale·Vercel Postgres·Bun SQLite. 변환 비용이 거의 없다.

Drizzle의 약점

  • 관계 쿼리(Drizzle Relations API)는 발전 중 — 관계로 nested 쿼리를 짤 때 Prisma만큼 매끄럽진 않다. 다만 2025년 v2 출시로 거의 따라잡았다.
  • 마이그레이션 충돌 처리는 수동 — 협업 팀에서 마이그레이션 파일 충돌 시 손으로 머지해야 한다.
  • 러닝 커브 — SQL을 잘 모르면 Prisma보다 친절하지 않다.

4장 · Kysely — "스키마 없는 순수 쿼리 빌더"

Kysely는 다른 길이다. 스키마를 강제하지 않는다. 너의 DB 스키마는 너의 일이고(SQL 파일·Atlas·Sqitch·Liquibase 무엇이든), Kysely는 그 위에 타입 안전 쿼리 빌더만 제공한다.

// db/types.ts — 너의 DB 스키마와 매칭되는 TS 타입
import type { ColumnType, Generated } from 'kysely'

export interface Database {
  user: UserTable
  post: PostTable
}

export interface UserTable {
  id: Generated<number>
  email: string
  name: string
  created_at: ColumnType<Date, string | undefined, never>
}

export interface PostTable {
  id: Generated<number>
  author_id: number
  title: string
  body: string
}

Kysely의 핵심 트릭은 위 Database 인터페이스를 제네릭으로 받아서, 모든 쿼리에서 컬럼 이름·타입을 자동 추론하는 것이다.

kysely-codegen으로 타입 자동 생성

스키마 인터페이스를 손으로 쓰지 않으려면 kysely-codegen을 쓴다. 라이브 DB를 인트로스펙션해서 위 같은 TS 타입을 생성한다.

npx kysely-codegen --url postgres://user:pw@localhost/db --out-file db/types.ts

Kysely의 강점

  • SQL 그대로의 표현력 — window function·CTE·json_agg·UNION ALL 등 SQL 거의 전부가 1:1 표현 가능.
  • DB·마이그레이션 분리 — 마이그레이션 도구는 따로 골라 쓴다. Atlas·Sqitch·dbmate·Flyway 무엇이든.
  • 소형 번들 — 핵심 빌더만 가져오면 압축 약 14KB.
  • 런타임 의존성 최소 — Cloudflare Workers·Bun·Deno에서 무난.

Kysely의 약점

  • 스키마·타입의 진실 일치는 너의 일 — 마이그레이션 후 kysely-codegen을 실행하지 않으면 타입이 거짓말을 한다. CI에 묶거나, 마이그레이션 파일 후크에 자동화 필요.
  • 관계 페치는 명시적 — N+1·JOIN·json_agg 패턴을 직접 짠다. Prisma·Drizzle 만큼 짧지 않다.
  • 에코시스템 작음 — 플러그인·튜토리얼·서드파티 어댑터가 다른 두 도구보다 적다.

5장 · Prisma — 새 엔진과 Prisma Postgres

Prisma는 2024~2025 사이 두 번의 큰 변화가 있었다.

5.1 엔진 재작성 — Rust에서 TS 네이티브 + Go로

Prisma 5 시대의 query engine은 Rust로 작성된 별도 프로세스(또는 WASM)였다. 콜드 스타트가 느리고, 엣지 런타임 호환성이 어색했다. Prisma 6 이후 단계적 재작성으로 다음이 바뀌었다.

  • TypeScript 네이티브 클라이언트 — 쿼리 컴파일러 다수가 TS로 옮겨졌다. 클라이언트 코드 안에서 직접 SQL을 만든다.
  • Go 기반 lightweight 엔진 — Prisma Accelerate·Pulse 같은 매니지드 서비스 측에서 동작.
  • 번들 크기 감소 — 클라이언트 사이즈가 의미 있게 줄었다. 정확한 수치는 setup 별로 다르니 Prisma 자체 벤치마크와 릴리즈 노트를 참고하라.
  • 엣지 호환성 정상화 — Vercel Edge·Cloudflare Workers에서 직접 동작하는 어댑터가 안정화됐다.

5.2 TypedSQL — Prisma 안에서 raw SQL을 타입 안전하게

Prisma의 약점은 항상 "복잡한 SQL을 못 짠다"였다. TypedSQL은 그 약점을 직접 친다.

-- prisma/sql/findActiveUsers.sql
SELECT id, email, name
FROM "User"
WHERE last_seen_at > $1
ORDER BY last_seen_at DESC
LIMIT $2;
import { PrismaClient } from '@prisma/client'
import { findActiveUsers } from '@prisma/client/sql'

const prisma = new PrismaClient()
const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
const users = await prisma.$queryRawTyped(findActiveUsers(since, 50))
//    ^ users: Array of typed rows, inferred from the SQL parameters

.sql 파일이 빌드 타임에 검사되고, 파라미터·결과 타입이 자동 생성된다. SQL을 SQL로 쓰되, 타입 안전성은 유지.

5.3 Prisma Postgres — 매니지드 서비스

Prisma 사가 직접 운영하는 Postgres. 핵심 차별화는 3개.

  • 분리된 컴퓨트·스토리지 — Neon과 비슷한 아키텍처지만 unikernel 기반.
  • 콜드 스타트가 빠르다 — 서버리스 워크로드 친화적.
  • Prisma 도구체인과 직결prisma init 한 번에 DB 생성·스키마 푸시·클라이언트 생성까지.

매니지드 Postgres 시장은 이미 Neon·Supabase·PlanetScale Postgres·Vercel Postgres가 있어서 경쟁이 치열하지만, Prisma 사용자에게는 가장 친화적인 선택지가 됐다.

Prisma의 강점 (2026)

  • 압도적으로 친절한 학습 곡선·문서·튜토리얼.
  • 관계·페치·트랜잭션 API가 가장 매끄러움.
  • TypedSQL로 escape hatch가 정상화됨.
  • 엣지·번들 약점이 크게 줄었음.

Prisma의 약점 (2026)

  • 여전히 추상화 층이 가장 두꺼움 — 디버그할 때 SQL이 한 번 떨어졌다 도는 모델이 머릿속에 있어야 함.
  • 마이그레이션 도구(prisma migrate)는 강하지만, migrate dev의 자동 셰도우 DB 생성이 일부 호스팅 환경(권한 부족)에서 거슬리는 경우가 있음.
  • 라이선스·운영 비용 — Prisma Accelerate·Pulse 같은 매니지드 서비스가 있는데, 무료 한도 너머는 유료. 셀프 호스팅으로 가면 그만큼 일이 늘어남.

6장 · postgres.js·pg·mysql2 — ORM 없이 가기

가장 얇은 길. tagged template과 TypeScript의 추론·satisfies만으로 충분하다는 진영.

// db/index.ts
import postgres from 'postgres'

export const sql = postgres(process.env.DATABASE_URL!, {
  max: 10,
  idle_timeout: 20,
})

// 쿼리 — tagged template
type User = { id: number; email: string; name: string }
const users = await sql<User[]>`
  SELECT id, email, name
  FROM users
  WHERE email = ${'foo@example.com'}
  LIMIT 10
`
//   ^ users: User[]

postgres.js는 자체적으로 SQL injection 방지(파라미터 바인딩)와 prepared statement 캐싱을 한다. 타입은 손으로 선언하거나, kysely-codegen 같은 도구로 생성하거나, Zod로 런타임 검증한다.

Zod로 런타임 검증

import { z } from 'zod'

const UserRow = z.object({
  id: z.number(),
  email: z.string().email(),
  name: z.string(),
})

const rows = await sql<unknown[]>`SELECT id, email, name FROM users LIMIT 10`
const users = rows.map((r) => UserRow.parse(r))
//   ^ users: z.infer<typeof UserRow>[]

이 패턴의 장점은 DB가 실제로 무엇을 돌려주는지를 한 번 검증한다는 것. ORM·쿼리 빌더의 타입은 컴파일 타임 약속일 뿐, 실제 DB가 약속을 어겼는지(컬럼 NULL·잘못된 타입) 잡지는 못한다.

Raw 클라이언트의 강점

  • 가장 작고 가장 빠름 — 추가 추상화 0.
  • SQL 거의 전부 사용 가능 — 모든 Postgres 기능 그대로.
  • 엣지 호환성 최고 — postgres.js·pg 모두 Cloudflare Workers·Bun에서 잘 돈다. Neon HTTP 어댑터 등은 더 깔끔.
  • 러닝 커브가 SQL뿐.

Raw 클라이언트의 약점

  • 타입 안전은 너의 일.
  • 관계 쿼리는 직접 짜야 함.
  • 마이그레이션 도구도 직접 골라야 함.

7장 · 같은 쿼리, 네 가지 도구 — "email로 사용자 조회"

가장 단순한 쿼리부터. email로 user 한 명 찾고, 그 user의 최신 post 5개를 같이 가져오기.

7.1 Drizzle

import { db } from './db'
import { users, posts } from './db/schema'
import { eq, desc } from 'drizzle-orm'

async function findUserWithPosts(email: string) {
  const user = await db.query.users.findFirst({
    where: eq(users.email, email),
    with: {
      posts: {
        orderBy: [desc(posts.id)],
        limit: 5,
      },
    },
  })
  return user
  // user: { id, email, name, createdAt, posts: Array of post rows } or undefined
}

db.query.users.findFirst는 Drizzle Relations API. 내부적으로 한 번의 SQL로 json_agg나 LATERAL join을 만들어 보낸다.

7.2 Kysely

import { db } from './db'
import { jsonArrayFrom } from 'kysely/helpers/postgres'

async function findUserWithPosts(email: string) {
  const user = await db
    .selectFrom('user')
    .where('email', '=', email)
    .select((eb) => [
      'id',
      'email',
      'name',
      jsonArrayFrom(
        eb
          .selectFrom('post')
          .whereRef('post.author_id', '=', 'user.id')
          .orderBy('post.id', 'desc')
          .limit(5)
          .select(['post.id', 'post.title', 'post.body'])
      ).as('posts'),
    ])
    .executeTakeFirst()
  return user
}

Kysely는 sub-select 헬퍼로 nested JSON을 만든다. jsonArrayFrom은 Postgres json_agg 한 줄 컴파일. SQL이 무엇이 될지 머릿속에 그대로 그려진다.

7.3 Prisma

import { prisma } from './db'

async function findUserWithPosts(email: string) {
  const user = await prisma.user.findUnique({
    where: { email },
    include: {
      posts: {
        orderBy: { id: 'desc' },
        take: 5,
      },
    },
  })
  return user
}

읽기 가장 쉽다. Prisma가 내부적으로 알아서 효율적인 쿼리를 만든다(예전엔 N+1 우려가 있었지만 새 엔진에서는 join strategy를 명시적으로 선택할 수 있다).

7.4 postgres.js (raw)

import { sql } from './db'

type UserRow = {
  id: number
  email: string
  name: string
  posts: Array<{ id: number; title: string; body: string }>
}

async function findUserWithPosts(email: string) {
  const rows = await sql<UserRow[]>`
    SELECT
      u.id, u.email, u.name,
      COALESCE(
        json_agg(json_build_object('id', p.id, 'title', p.title, 'body', p.body))
          FILTER (WHERE p.id IS NOT NULL),
        '[]'::json
      ) AS posts
    FROM users u
    LEFT JOIN LATERAL (
      SELECT id, title, body
      FROM posts
      WHERE author_id = u.id
      ORDER BY id DESC
      LIMIT 5
    ) p ON TRUE
    WHERE u.email = ${email}
    GROUP BY u.id
  `
  return rows[0]
}

길지만 정확히 무엇이 도는지 안다. COALESCE(json_agg(...) FILTER (WHERE ...)) 같은 패턴은 raw에서는 매번 손으로 짜야 한다(헬퍼 만들면 줄어듦).

비교 — 같은 쿼리, 다른 추상화

도구줄 수읽기 난이도SQL 가시성타입 추론
Drizzle짧음쉬움자동
Kysely높음자동
Prisma가장 짧음가장 쉬움낮음자동
postgres.js가장 김SQL 의존가장 높음수동/Zod

8장 · 6축 비교 — 골라잡기 좋게

8.1 추상화 정도

도구추상화 정도
postgres.js거의 없음
KyselySQL 빌더
Drizzle스키마 + SQL 빌더
Prisma풀 ORM
MikroORM·TypeORMActive Record·Data Mapper

8.2 마이그레이션 도구

도구빌트인 마이그레이션자동 생성비고
Prismaprisma migrateO셰도우 DB 필요
Drizzledrizzle-kitO보수적 자동 생성
KyselyKysely 마이그레이션 + 외부 도구XAtlas·dbmate 권장
postgres.js없음XAtlas·Sqitch·Flyway 등

8.3 엣지 런타임 호환성 (Cloudflare Workers·Vercel Edge·Bun)

도구엣지 호환비고
Drizzle매우 좋음모든 어댑터가 엣지 우선
Kysely좋음드라이버 의존, 보통 잘 됨
postgres.js좋음Neon HTTP·Hyperdrive 결합 시 매우 좋음
Prisma좋음새 엔진 이후 안정
MikroORM·TypeORM보통~제한데코레이터·reflect-metadata 의존

8.4 번들 크기 (압축, 클라이언트 코어만)

대략적인 감(릴리즈에 따라 다름).

도구압축 후 크기(대략)
postgres.js매우 작음
Kysely작음
Drizzle작음
Prisma중간 (새 엔진 이후 큰 폭 축소)
MikroORM·TypeORM

8.5 SQL escape hatch

도구escape hatch매끄러움
postgres.js본인이 SQL최고
Kyselysql 템플릿매우 좋음
Drizzlesql 템플릿매우 좋음
PrismaTypedSQL·$queryRaw좋음(TypedSQL 이후 정상)
MikroORM·TypeORMraw query보통

8.6 멀티 DB 지원

도구PostgresMySQLSQLite기타
DrizzleOOOD1·LibSQL·Bun·Neon·Planetscale 외
KyselyOOO어댑터 다수
PrismaOOOSQL Server·MongoDB(제한)
postgres.jsOXXPostgres 전용

9장 · 실제 마이그레이션 후기 — Prisma → Drizzle / Kysely

9.1 Prisma → Drizzle (가장 흔한 경로)

이 경로를 택하는 팀이 늘었다. 이유는 두 가지.

  1. 엣지/번들 — 콜드 스타트와 응답 시간에서 차이가 보인다. 새 Prisma 엔진으로 격차는 줄었지만 0은 아니다.
  2. SQL escape hatch — sql 템플릿이 Prisma의 TypedSQL보다 자연스럽다. TypedSQL은 별도 .sql 파일이 필요하고, 동적 SQL에는 제약이 있다.

전형적인 마이그레이션 단계.

  • 1단계: 새 코드만 Drizzle로. 기존 Prisma 코드는 그대로.
  • 2단계: drizzle-kit으로 기존 DB introspect → 스키마 코드 생성.
  • 3단계: read 경로부터 점진 치환. write·트랜잭션은 나중에.
  • 4단계: 마이그레이션 도구 교체(prisma migratedrizzle-kit).

잘 안 되는 부분

  • Prisma migrations의 히스토리는 Drizzle Kit이 모른다 — 둘이 한동안 공존하는 동안 충돌이 안 나게 관리해야 함.
  • enum·복합 PK·partial index 같은 가장자리 — Drizzle introspection이 100%를 잡지 못하는 경우 있음. 수동 보정 필요.
  • 관계 쿼리 의미 미세 차이 — Prisma include와 Drizzle with이 같아 보여도 LATERAL join 전략이 다르면 결과 JSON 모양·정렬이 미세하게 다를 수 있음.

9.2 Prisma → Kysely (SQL 팀의 경로)

DB 스키마 운영을 이미 Atlas·Sqitch 같은 도구로 하고 있는 팀, 또는 SQL을 그대로 짜고 싶은데 타입은 받고 싶은 팀이 자주 선택.

  • 1단계: kysely-codegen으로 live DB에서 타입 생성.
  • 2단계: 가장 복잡한 쿼리(window·CTE·json_agg)부터 치환 — 가장 큰 가치를 가장 먼저 본다.
  • 3단계: 마이그레이션은 Atlas·dbmate 등으로 분리(이미 그 단계인 팀이 많음).
  • 4단계: read 단순 쿼리도 차차 옮김. write·트랜잭션은 마지막.

잘 안 되는 부분

  • 관계 쿼리는 길어진다 — Prisma 한 줄이 Kysely 5~15줄이 되기도. 헬퍼 함수 만드는 일이 필요.
  • Selectable<DB["user"]> 같은 유틸리티 타입 이해 비용 — 처음 보는 사람에게 좀 낯섦.
  • 에코시스템이 작다 — 플러그인·예제·서드파티 통합이 Prisma만큼 풍성하진 않음.

10장 · MikroORM·TypeORM — 여전히 살아 있지만

MikroORM

엔티티 클래스 기반 Data Mapper ORM. identity map·unit of work·풍성한 관계 API가 강점. DDD 스타일 프로젝트나 도메인 객체 중심으로 짜는 팀이 좋아한다. SQL과 거리는 가장 멀다.

  • 강점: 도메인 객체 중심, Active Record/Data Mapper 모두 지원, 트랜잭션·관계가 매끄럽다.
  • 약점: 무겁다, 데코레이터·reflect-metadata 의존, 엣지 친화도 보통.

TypeORM

가장 오래된 TS ORM 중 하나. 데코레이터 기반 Active Record + Data Mapper. 유지보수가 일정치 않고 알려진 버그가 오래 남는 경향이 있어 신규 프로젝트의 첫 선택지는 아니다. 그러나 이미 TypeORM으로 짠 코드가 많으면 빨리 떼지 못한다.

  • 강점: 친숙함, 풍부한 데코레이터 API.
  • 약점: 유지보수 risk, TS 5.x 변화·데코레이터 표준 변경에 둔감, 엣지 호환 어려움.

신규 프로젝트에서 TypeORM을 자발적으로 고르는 경우는 2026년에 드물다. 이미 있는 코드베이스는 마이그레이션 비용·이득을 따로 따져야 한다.


11장 · 무엇을 언제 골라야 하나 — 솔직한 결론

자, 정답은? 항상 그렇듯 "상황 따라 다르다". 다만 패턴이 있다.

Drizzle을 고를 때

  • 신규 프로젝트, 엣지 런타임(Cloudflare Workers·Vercel Edge) 우선.
  • SQL을 알고/배우려는 팀.
  • 멀티 DB(SQLite·Postgres·MySQL·D1·LibSQL) 가능성.
  • 번들 크기·콜드 스타트가 중요.

Kysely를 고를 때

  • 마이그레이션은 이미 Atlas·Sqitch·dbmate 등으로 운영.
  • 복잡한 SQL(CTE·window·json_agg)이 많고, SQL을 직접 짜고 싶음.
  • 스키마 관리·DDL을 ORM과 분리하고 싶음.
  • 작은 의존성·작은 번들을 원함.

Prisma를 고를 때

  • 신규 풀스택 팀, 학습 곡선 최소화 우선.
  • 관계가 복잡하고 include/페치 매끄러움이 중요.
  • Prisma Postgres·Accelerate·Pulse 같은 매니지드 도구체인을 활용하고 싶음.
  • TypedSQL로 복잡 SQL을 따로 다뤄도 괜찮음.

postgres.js·pg(raw)를 고를 때

  • 팀 전체가 SQL에 강함.
  • 추가 추상화를 의식적으로 거부.
  • 가장 작은 번들·가장 빠른 런타임이 핵심.
  • 런타임 검증(Zod)이나 codegen으로 타입을 별도 관리 가능.

안티 체크리스트 (피해야 할 패턴)

  • 한 프로젝트에 ORM 두 개 — 학습·디버그 비용 2배.
  • "Prisma 느려서 Drizzle로 갈아탔다"는 결정을 측정 없이 내림 — 새 엔진 이후엔 적용 안 되는 경우 많음.
  • ORM 골라놓고 거의 모든 쿼리를 $queryRaw로 짬 — 그러면 ORM이 아니라 무거운 raw 클라이언트.
  • 스키마는 Drizzle, 마이그레이션은 Prisma 같은 어색한 조합 — 진실의 원천이 두 개.
  • 엣지 우선 환경인데 데코레이터 ORM 채택 — 빌드·번들·런타임 모두 가시밭.

에필로그 — 추상화는 도구이지 종교가 아니다

ORM·쿼리 빌더 선택은 추상화 정도와 escape hatch 매끄러움의 트레이드오프다. 한쪽이 우월하지 않다. 다만 2026년 5월의 풍경은 분명하다.

  • SQL에 더 가까운 쪽이 늘었다 — Drizzle·Kysely·raw 진영이 자랐다.
  • 풀 ORM은 다이어트 중 — Prisma 새 엔진과 TypedSQL은 약점 두 개를 정통으로 친다.
  • 엣지는 기본값이 됐다 — 어떤 도구를 골라도 엣지 호환을 무시할 수 없다.
  • '한 도구가 모든 걸 해결'은 점점 깨졌다 — DB 클라이언트 + 마이그레이션 도구 + 타입 생성 도구를 따로 조합하는 게 흔해졌다.

결정 체크리스트

  • 엣지 우선인가? — Yes면 Drizzle·Kysely·postgres.js·새 Prisma.
  • 팀이 SQL에 강한가? — Yes면 Kysely·postgres.js, No면 Prisma·Drizzle.
  • 관계 페치가 핵심인가? — Yes면 Prisma > Drizzle Relations > Kysely.
  • 복잡 SQL(CTE·window)이 많은가? — Yes면 Kysely·postgres.js > Drizzle > Prisma TypedSQL.
  • 마이그레이션 도구는? — Prisma·Drizzle Kit 빌트인 vs Atlas·dbmate 외부.
  • 멀티 DB 가능성은? — Yes면 Drizzle·Kysely.
  • 학습 곡선 최소화? — Prisma.

안티 패턴

  1. 측정 없이 ORM 갈아타기 — 결정은 측정 후에.
  2. 한 코드베이스에 ORM 둘 공존 — 종착지 정하고 마이그레이션 일정 잡기.
  3. ORM 쓰면서 $queryRaw 90% — ORM이 아니라 무거운 raw.
  4. 스키마 진실의 원천 두 개 — DB와 코드의 진실은 하나여야.
  5. 마이그레이션 자동화 없이 codegen — 타입이 거짓말한다.
  6. 엣지 우선인데 무거운 데코레이터 ORM — 빌드·런타임 둘 다 비용.
  7. 관계가 복잡한데 raw — 직접 짜야 할 SQL이 산더미.

다음 글 예고

다음 글 후보: Drizzle Relations v2 깊게 — json_agg·LATERAL join이 어떻게 만들어지는가, Prisma TypedSQL 한 달 써본 후기 — 어디에 쓰고 어디에 안 쓰나, Cloudflare Workers + Hyperdrive + postgres.js 풀스택 사례.

"SQL을 알면 모든 도구가 친절해진다. 모르면 어떤 도구도 마법이 아니다."

— TypeScript ORM·쿼리 빌더 2026, 끝.


참고 / References

현재 단락 (1/380)

2026년에도 신규 TypeScript 프로젝트의 첫 번째 회의에서 빠지지 않는 질문이 있다.

작성 글자: 0원문 글자: 14,923작성 단락: 0/380