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 | 모델·관계·페치·캐시까지 챙긴다 |

| 헤드리스 ORM | Drizzle | 스키마는 코드, 쿼리는 SQL 1:1 |

| 쿼리 빌더 | Kysely·Knex | 스키마 없이 타입 안전 SQL 합성 |

| Raw 클라이언트 + 타입 헬퍼 | postgres.js·pg·mysql2 + Zod | tagged template + 런타임 검증 |

| 관계형 DSL | EdgeQL(EdgeDB)·SurrealQL | DB가 자체 쿼리 언어 |

이 글의 초점은 굵게 표시한 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:1** — `db.select().from(users).where(eq(users.email, '...'))` 같은 식. SQL을 아는 사람이 보면 무엇으로 컴파일될지 즉시 알 수 있다.

3. **헤드리스 + 작음** — 런타임 의존성이 거의 없다. Cloudflare Workers·Vercel Edge·Deno Deploy에 그대로 올라간다. 번들 크기는 압축 기준 7~15KB 수준.

Drizzle 스키마

// db/schema.ts

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 타입

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;

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

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로 런타임 검증

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

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

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

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)

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 | 거의 없음 |

| Kysely | SQL 빌더 |

| Drizzle | 스키마 + SQL 빌더 |

| Prisma | 풀 ORM |

| MikroORM·TypeORM | Active Record·Data Mapper |

8.2 마이그레이션 도구

| 도구 | 빌트인 마이그레이션 | 자동 생성 | 비고 |

| --- | --- | --- | --- |

| Prisma | `prisma migrate` | O | 셰도우 DB 필요 |

| Drizzle | `drizzle-kit` | O | 보수적 자동 생성 |

| Kysely | `Kysely` 마이그레이션 + 외부 도구 | X | Atlas·dbmate 권장 |

| postgres.js | 없음 | X | Atlas·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 | 최고 |

| Kysely | `sql` 템플릿 | 매우 좋음 |

| Drizzle | `sql` 템플릿 | 매우 좋음 |

| Prisma | TypedSQL·`$queryRaw` | 좋음(TypedSQL 이후 정상) |

| MikroORM·TypeORM | raw query | 보통 |

8.6 멀티 DB 지원

| 도구 | Postgres | MySQL | SQLite | 기타 |

| --- | --- | --- | --- | --- |

| Drizzle | O | O | O | D1·LibSQL·Bun·Neon·Planetscale 외 |

| Kysely | O | O | O | 어댑터 다수 |

| Prisma | O | O | O | SQL Server·MongoDB(제한) |

| postgres.js | O | X | X | Postgres 전용 |

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 migrate` → `drizzle-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

- [Drizzle ORM 공식](https://orm.drizzle.team/)

- [Drizzle ORM GitHub](https://github.com/drizzle-team/drizzle-orm)

- [Drizzle Kit Documentation](https://orm.drizzle.team/docs/kit-overview)

- [Drizzle Relations API](https://orm.drizzle.team/docs/rqb)

- [Kysely 공식](https://kysely.dev/)

- [Kysely GitHub](https://github.com/kysely-org/kysely)

- [kysely-codegen](https://github.com/RobinBlomberg/kysely-codegen)

- [Prisma 공식](https://www.prisma.io/)

- [Prisma TypedSQL Documentation](https://www.prisma.io/docs/orm/prisma-client/using-raw-sql/typedsql)

- [Prisma Postgres](https://www.prisma.io/postgres)

- [Prisma 엔진 재작성 — 공식 블로그](https://www.prisma.io/blog)

- [postgres.js GitHub — porsager/postgres](https://github.com/porsager/postgres)

- [node-postgres (pg) 공식](https://node-postgres.com/)

- [mysql2 GitHub](https://github.com/sidorares/node-mysql2)

- [MikroORM 공식](https://mikro-orm.io/)

- [TypeORM 공식](https://typeorm.io/)

- [Neon — Serverless Postgres](https://neon.tech/)

- [Cloudflare Hyperdrive](https://developers.cloudflare.com/hyperdrive/)

- [Cloudflare D1](https://developers.cloudflare.com/d1/)

- [Atlas — Modern DB migration](https://atlasgo.io/)

- [Vercel Postgres](https://vercel.com/docs/storage/vercel-postgres)

- [Zod](https://zod.dev/)

- [Hono — Edge web framework](https://hono.dev/)

- [T3 Stack](https://create.t3.gg/)

현재 단락 (1/380)

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

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