새로운 프로젝트를 시작하기에 앞서 Nest.js, MySQL과 함께 사용할 ORM을 선택해야 하는 상황이 왔다.
다양한 ORM이 존재하지만 Prisma, TypeORM 둘 중 하나를 선택하기로 했다.
많은 글에서 두 ORM의 장단점을 비교하고 있지만, 성능 비교는 많지 않았고, 그마저도 최신자료는 없었다.
뭐 어쨋든 참고해 보자면 위 글은 2년 전 자료이고, PostgreSQL을 사용했다고 한다.
대략 요약하자면, 전반적으로 거의 비슷하나 Prisma가 근소하게 앞서고 스트레스 시나리오에서는 TypeORM이 압도적이라고 한다.
위 테스트를 참고하여 간단하게 테스트를 진행해 보았다.
어느 진영이 더 좋다고 평소에 생각한바는 전혀 없으며, 최대한 공정하게 진행하려 노력했습니다.
테스트 환경
PC - Macbook Air M2 RAM8GB SSD512GB
Prisma - 5.12.1
TypeORM - 0.3.20
MySQL - 8.3
진행 방법
Prisma와 TypeORM 각각의 데이터베이스를 생성하고,
한쪽 먼저
- CREATE * 5000
- FIND(단건) * 5000
- FIND(다건) * 5000
- UPDATE * 5000
- DELETE * 5000
모두 진행한 뒤,
다른 한쪽을 진행했다.
참고
- 모든 테스트들은 값이 작을수록 좋음
- 아래 테스트들에 등장하는 getFake~ 로 시작하는 함수들은 @faker-js/faker 패키지를 사용하여 만든 함수입니다.
- 각각 환경 구성은 Prisma와 TypeORM의 공식문서를 참조하여 구성하였습니다.
- 쿼리빌더와 같은 메서드는 사용하지 않았으며, 각각의 진영에서 제공하는 기본 함수들을 이용했습니다.
성능 테스트
데이터 생성
// Prisma
console.time('Create User - Prisma');
for (let i = 0; i < 5000; i++) {
await this.user.create({
data: getFakeUser()
});
}
console.timeEnd('Create User - Prisma');
// TypeORM
console.time('Create User - TypeORM');
for (let i = 0; i < 5000; i++) {
await this.userRepository.save(getFakeUser());
}
console.timeEnd('Create User - TypeORM');
데이터 단건 조회 by id
// Prisma
console.time('Find User - Prisma');
for (let i = 0; i < 5000; i++) {
await this.user.findUnique({
where: { id: getRandomId(1, 5000) }
});
}
console.timeEnd('Find User - Prisma');
// TypeORM
console.time('Find User - TypeORM');
for (let i = 0; i < 5000; i++) {
await this.userRepository.findOneBy({ id: getRandomId(1, 5000) });
}
console.timeEnd('Find User - TypeORM');
단건 조회의 경우에는 TypeORM이 압도적이다.
데이터 다건 조회 by 조건
// Prisma
console.time('Find Users - Prisma');
for (let i = 0; i < 5000; i++) {
const query: Prisma.UserFindManyArgs = {
where: {
job: {
contains: getRandomAlpha()
}
},
take: 10,
skip: 0
}
await this.$transaction([
this.user.findMany(query),
this.user.count({ where: query.where })
]);
}
console.timeEnd('Find Users - Prisma');
// TypeORM
console.time('Find Users - TypeORM');
for (let i = 0; i < 5000; i++) {
await this.userRepository.findAndCount({
where: {
job: ILike(`%${getRandomAlpha()}%`)
},
take: 10,
skip: 0
});
}
console.timeEnd('Find Users - TypeORM');
해당 테스트의 경우에는 Prisma에는 TypeORM의 findAndCount와 같은 Count쿼리를 함께 보내는 기능이 기본 메서드에 없어서 트랜잭션을 통해 진행했다.
그래서 결과가 차이가 심한 건가 싶어서 find만 진행하도록 변경해서 다시 요청해 보았다.
흠 ...
차이가 더 벌어졌다.
데이터 업데이트
// Prisma
console.time('Update User - Prisma');
for (let i = 0; i < 5000; i++) {
await this.user.update({
select: { id: true },
where: { id: getRandomId(1, 5000) },
data: { name: getRandomName() }
});
}
console.timeEnd('Update User - Prisma');
// TypeORM
console.time('Update User - TypeORM');
for (let i = 0; i < 5000; i++) {
await this.userRepository.update(
{ id: getRandomId(1, 5000) },
{ name: getRandomName() }
);
}
console.timeEnd('Update User - TypeORM');
해당 테스트의 경우에는
TypeORM의 경우 UPDATE 쿼리만 보낸 후 영향을 받은 row들의 숫자만 리턴하는 식이고,
Prisma는 Update 쿼리를 보낼 때, 전후로 Select 쿼리를 보낸다.
그래서 차이가 심한 것 같다.
TypeORM은 데이터가 존재하지 않아도 에러를 발생시키지 않고,
Prisma는 데이터가 없으면 에러를 발생시킨다.
데이터 삭제
// Prisma
console.time('Delete User - Prisma');
for (let i = 1; i <= 5000; i++) {
await this.user.delete({
where: { id: i }
});
}
console.timeEnd('Delete User - Prisma');
// TypeORM
console.time('Delete User - TypeORM');
for (let i = 1; i <= 5000; i++) {
await this.userRepository.delete({ id: i });
}
console.timeEnd('Delete User - TypeORM');
Delete의 경우에도 Update와 비슷한 경우라고 볼 수 있다.
그 외
성능 외의 비교는 많은 글이 존재하기에 딱히 의미는 없으나 간략하게 적어보자면,
다운로드 추세를 보면 기존에는 TypeORM이 더 많다고 알고 있었는데, Prisma가 넘어섰다.
Prisma의 경우에는 type-safety를 지원해서 잠재적인 휴먼에러를 막아준다고 한다.
그리고 이번에 테스트를 진행하면서 Prisma를 처음 사용해 보았는데, 굉장히 쉽고 편리하다고 느꼈다.
특히 model 정의할 때, 굉장히 깔끔했다.
개인적으로 사용성 측면에서는 Prisma가 우세하다고 생각된다.
복잡한 쿼리는 TypeORM이 좀 더 유연하게 작성할 수 있다고 한다.
하지만 최근 TypeORM이 0.3으로 넘어오면서 다양한 이슈가 존재하기에 잘 고려해보아야한다.
결론
각자의 프로젝트에서 중요한 포인트들과 비교하여 선택하면 되겠다.
- 복잡한 쿼리를 많이 사용하며 성능이 중요하다 - TypeORM
- 편리한 사용성과 낮은 진입장벽, 휴먼에러를 막아주는 게 중요하다 - Prisma
'개발일지 > Node.js' 카테고리의 다른 글
TypeORM + MySQL8 AsText does not exist (0) | 2024.03.12 |
---|---|
Nestjs - Shared modules : 의존성 주입을 통하여 모듈 공유하기 (0) | 2024.02.29 |
TypeORM - @Column({ unique : true }) vs @Unique() (0) | 2024.02.23 |
SQL Injection 방지 - 권장방식에는 다 이유가 있지.. (0) | 2024.01.31 |