🗄️ TypeORM8. 이론 & 대안 (Prisma · Drizzle · MikroORM)05 — TypeORM vs Sequelize (JS 시대 vs TS 시대)

05 — TypeORM vs Sequelize (JS 시대 vs TS 시대)

한 줄 답: Sequelize는 2010년 JavaScript 시대에 태어난 ActiveRecord 스타일 ORM이고, TypeORM은 2016년 TypeScript 시대에 태어난 DataMapper 스타일 ORM이다. 타입 지원·패턴·생태계근본적으로 다르다. 레거시 Sequelize 코드베이스가 아니라면 새 TypeScript 프로젝트에서 Sequelize를 선택할 이유는 거의 없다.


Why — 왜 시대 차이가 이렇게 큰가

시기사건
2010Sangria Ostowski가 Sequelize 시작 — Node.js + JavaScript 시대
2011Sequelize 1.0 — ActiveRecord 패턴 (Rails 영향)
2014TC39 Decorator Stage 1 제안서 등장
2014Microsoft TypeScript 1.0
2016Umed Khudoiberdiev TypeORM 시작 — decorator + reflect-metadata + DataMapper
2018NestJS 등장 — TypeORM이 사실상 표준
2020Sequelize TypeScript 지원 강화 시도 (v6) — 부분적 성공
2023Sequelize 7 alpha — TypeScript 완전 재작성 (alpha 단계 길게 지속)
2024TypeORM ~33k stars, Sequelize ~29k stars (Sequelize는 완만한 정체)

Sequelize는 TypeScript 시대로 갈아타려는 중이지만 — 2024년 현재도 v7은 alpha다. TypeORM은 처음부터 TypeScript로 태어났다근본 차이가 메우기 어렵다.


How — 두 도구의 근본 차이

1) 모델 정의

Sequelize (v6, 일반적 사용):

import { Sequelize, DataTypes, Model } from 'sequelize'
 
interface UserAttributes {
  id: number
  email: string
  name?: string
}
 
class User extends Model<UserAttributes> {
  declare id: number
  declare email: string
  declare name?: string
}
 
User.init({
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
  email: { type: DataTypes.STRING, allowNull: false },
  name: { type: DataTypes.STRING, allowNull: true },
}, { sequelize, modelName: 'User' })
  • 클래스 정의Model.init 호출interface3중으로 필요.
  • 타입과 런타임 스키마가 분리되어 있어 동기화가 사용자 책임.

TypeORM:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
 
@Entity()
class User {
  @PrimaryGeneratedColumn()
  id!: number
 
  @Column()
  email!: string
 
  @Column({ nullable: true })
  name?: string
}
  • 클래스 한 곳에 모든 것. 데코레이터가 타입과 스키마를 동시에 표현.

2) 패턴 차이 — ActiveRecord vs DataMapper

Sequelize (ActiveRecord):

const user = await User.findByPk(1)
user.email = 'x@x.com'
await user.save()       // ← 객체가 *자기* save를 호출

TypeORM (DataMapper):

const user = await userRepo.findOne({ where: { id: 1 } })
user.email = 'x@x.com'
await userRepo.save(user)   // ← *외부 게이트웨이*가 save

3) 타입 정확도

Sequelize:

const user = await User.findOne({
  where: { email: 'x@x.com' },
  attributes: ['id', 'email']
})
// user의 타입은? — User | null (attributes 효과 *반영 안 됨*)
console.log(user?.name)   // ← TS 에러 없음. 런타임에 *undefined*

TypeORM:

const user = await userRepo.findOne({
  where: { email: 'x@x.com' },
  select: { id: true, email: true }
})
// user의 타입은? — User | null (select 효과 *반영 안 됨*)
// ← 비슷한 한계지만 *fields 추론은 TypeORM이 살짝 더 시도함*

판정: *Sequelize의 타입은 v6까지 부분적. v7 alpha에서 개선 중이지만 정식 출시 전. TypeORM도 부분이지만 더 낫다. 둘 다 Prisma만큼 정확하진 않다.


What — 구체적 시나리오로 비교

시나리오 A — 새 TypeScript 프로젝트

Sequelize:

  • interface + class + Model.init 3중 정의
  • v6의 declare 키워드 boilerplate 많음
  • v7 alpha를 쓸지 말지 고민

TypeORM:

  • 데코레이터 한 곳에 표현
  • NestJS 공식 통합
  • 자료 풍부

판정: TypeORM 압승. 새 TS 프로젝트에 Sequelize를 선택할 이유는 거의 없다.

시나리오 B — 레거시 JavaScript 코드베이스

Sequelize:

  • 기존 코드가 그대로 동작
  • gradual TypeScript 도입 가능

TypeORM:

  • JS 코드를 모두 TS로 옮기는 작업 필요
  • 데코레이터는 기본적으로 JS에서 잘 안 됨 (Babel 플러그인 필요)

판정: Sequelize 유지가 합리적. 큰 JS 코드베이스의 점진 이주는 Sequelize가 더 부드럽다.

시나리오 C — Rails/Django 출신 개발자

Sequelize: ActiveRecord 패턴이 Rails ActiveRecord와 거의 같다. user.save()자연스럽다. TypeORM: DataMapper 패턴이 Repository를 한 번 더 거친다. Rails 개발자에게는 낯설다.

판정: Rails 출신 팀이면 Sequelize가 학습 비용이 낮다. 하지만 큰 코드베이스에서 결합 폭발은 ActiveRecord의 알려진 한계.

시나리오 D — 마이그레이션 도구

Sequelize:

npx sequelize-cli migration:generate --name add-user-age
# ← *수동으로* SQL 작성. 자동 generate 없음.

TypeORM:

npm run typeorm migration:generate -- -n AddUserAge
# ← 엔티티 diff로 *자동 SQL 생성*

판정: TypeORM 우세. Sequelize는 Umzug에 의존하는데 자동 생성 기능이 약하다.

시나리오 E — 트랜잭션

Sequelize:

await sequelize.transaction(async (t) => {
  const user = await User.findByPk(1, { transaction: t })
  // ← *모든 쿼리에 { transaction: t } 명시적으로* 전달해야 함 (CLS 옵션 켜야 자동)
  user.email = 'x@x.com'
  await user.save({ transaction: t })
})

TypeORM:

await dataSource.transaction(async (manager) => {
  const user = await manager.findOne(User, { where: { id: 1 } })
  user.email = 'x@x.com'
  await manager.save(user)
  // ← *manager가 transaction context를 자동 관리*
})

판정: TypeORM이 더 깔끔. Sequelize는 transaction 전달의 boilerplate가 많다 (CLS namespace 옵션을 켜면 개선되지만 추가 설정).


What-if — 잘못 선택하면

1) 새 TS 프로젝트인데 Sequelize

interface + class + Model.init 3중 정의boilerplate. TS 타입과 런타임 스키마 동기화 책임이 개발자에게. 대응: TypeORM 또는 Prisma.

2) Rails 출신 팀인데 무리하게 TypeORM

→ DataMapper의 userRepo.save(user)낯설어 코드가 Rails 스타일로 작성됨 — BaseEntity 상속 남용. 대응: TypeORM의 ActiveRecord 모드를 켜거나 Sequelize 유지.

3) Sequelize v7 alpha를 production에

→ 2024년 현재도 alpha. breaking change남아 있다. production 위험. 대응: v6 유지 또는 다른 ORM으로 이주.

4) 큰 코드베이스에서 ActiveRecord 패턴 남용

→ 도메인 객체가 영속성을 안다. 단위 테스트 DB 필요, 결합 폭발. 대응: Sequelize라도 Repository 레이어를 직접 짜 DataMapper 흉내.

5) “Sequelize도 충분히 type-safe”라고 과신

findAll<...>제네릭 누수, attributes 옵션의 타입 미반영. production 런타임 에러. 대응: 진지한 타입 정확도가 필요하면 Prisma. 부분 타입으로 충분하면 TypeORM.


Insight — 흥미로운 이야기

”Sequelize의 14년

Sequelize는 2010년 시작해 14년째 운영 중이다. Node.js의 거의 모든 시기를 살아냈다 — CommonJS 시대, ES5 시대, Promise 도입, async/await 도입, TypeScript 부상.

오래 살아남음이 매력이면서 부담이다. 과거 API 호환을 유지해야 해서 근본 재설계가 어렵다. v7이 완전 재작성인 이유 — 하지만 그 재작성이 alpha 단계만 2년 지속 중인 이유이기도.

”TypeORM은 Sequelize 후속이 아니다”

흔한 오해 중 하나는 *“TypeORM이 Sequelize의 TypeScript 버전”*이라는 것. 틀렸다. TypeORM은 *Hibernate (Java) + Doctrine (PHP)*의 DataMapper 계보에서 왔다. Sequelize는 Rails ActiveRecord 계보에서 왔다.

두 도구는 DNA가 다른 ORM이다. 데코레이터 차이보다 DataMapper vs ActiveRecord의 패턴 차이가 더 본질적.

”Sequelize v7의 재탄생 시도

Sequelize v7 (2022~2024 alpha)은 완전 TypeScript 재작성이고 — decorator 기반 모델 정의옵션으로 추가한다. 이게 무엇을 닮았나? TypeORM. 그리고 MikroORM.

// Sequelize v7 (alpha) — decorator 스타일
import { Attribute, NotNull } from '@sequelize/core/decorators-legacy'
 
class User extends Model {
  @Attribute(DataTypes.INTEGER)
  @PrimaryKey
  @AutoIncrement
  declare id: number
 
  @Attribute(DataTypes.STRING)
  @NotNull
  declare email: string
}

시장이 옳다고 본 것이 데코레이터였고 Sequelize도 결국 그 길을 따른다. 하지만 후발 주자의 한계가 명확하다 — 기존 v6 사용자를 어떻게 끌고 갈 것인가공개 이슈다.


요약 + 다이어그램

TypeORM vs Sequelize = TS 시대 vs JS 시대. Sequelize는 Rails ActiveRecord의 후예, TypeORM은 Hibernate DataMapper의 후예DNA가 다르다. 새 TypeScript 프로젝트면 TypeORM 또는 Prisma, 레거시 JS 코드베이스면 Sequelize 유지.

다음 문서: 06-when-not-to-use-typeorm.mdx — 그럼 TypeORM을 쓰지 말아야 하는 자리는?