TypeORM vs Prisma: Node.js ORM 심층 비교
반응형
목차
개요
TypeORM과 Prisma는 Node.js 생태계에서 가장 인기 있는 ORM(Object-Relational Mapping) 도구입니다. 각각의 특징과 차이점을 살펴보고, 프로젝트 상황에 맞는 선택을 할 수 있도록 도와드리겠습니다.
기본 개념 비교
TypeORM
- 전통적인 ORM 패턴 따름
- Active Record 또는 Data Mapper 패턴 선택 가능
- 데코레이터 기반의 스키마 정의
- JavaScript/TypeScript로 작성된 코드베이스
Prisma
- 새로운 접근 방식의 차세대 ORM
- 독자적인 스키마 정의 언어(Prisma Schema)
- 강력한 타입 안정성
- 자동 생성되는 타입-세이프 클라이언트
스키마 정의 방식
TypeORM 엔티티 정의
// TypeORM
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
email: string;
@Column()
name: string;
@OneToMany(() => Post, post => post.author)
posts: Post[];
}
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToOne(() => User, user => user.posts)
author: User;
}
Prisma 스키마 정의
// Prisma
model User {
id Int @id @default(autoincrement())
email String @unique
name String
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
타입 안정성
TypeORM
// TypeORM - 타입 추론이 때때로 불완전
const users = await userRepository.find({
where: {
email: 'test@example.com',
// 오타나 잘못된 필드 이름이 런타임에 발견될 수 있음
namee: 'John' // ❌ 컴파일 시점에 오류 감지 어려움
}
});
Prisma
// Prisma - 완벽한 타입 안정성
const users = await prisma.user.findMany({
where: {
email: 'test@example.com',
namee: 'John' // ✅ 컴파일 시점에 오류 감지
}
});
쿼리 작성 방식
TypeORM 쿼리
// TypeORM
// 복잡한 쿼리
const posts = await postRepository
.createQueryBuilder('post')
.leftJoinAndSelect('post.author', 'author')
.where('author.email = :email', { email: 'test@example.com' })
.andWhere('post.createdAt > :date', { date: new Date('2024-01-01') })
.orderBy('post.createdAt', 'DESC')
.take(10)
.getMany();
// 기본 CRUD
const user = await userRepository.save({
email: 'test@example.com',
name: 'John Doe'
});
Prisma 쿼리
// Prisma
// 복잡한 쿼리
const posts = await prisma.post.findMany({
where: {
author: {
email: 'test@example.com'
},
createdAt: {
gt: new Date('2024-01-01')
}
},
include: {
author: true
},
orderBy: {
createdAt: 'desc'
},
take: 10
});
// 기본 CRUD
const user = await prisma.user.create({
data: {
email: 'test@example.com',
name: 'John Doe'
}
});
성능 비교
메모리 사용량
// TypeORM
// 메모리에 전체 엔티티를 로드
const users = await userRepository.find();
// Prisma
// 필요한 필드만 선택적으로 로드
const users = await prisma.user.findMany({
select: {
id: true,
name: true
}
});
쿼리 최적화
// TypeORM - N+1 문제 발생 가능
const posts = await postRepository.find();
for (const post of posts) {
const author = await post.author; // N+1 쿼리 발생
}
// Prisma - 자동 최적화
const posts = await prisma.post.findMany({
include: {
author: true // 단일 쿼리로 최적화
}
});
실제 사용 예제
TypeORM으로 구현한 게시판
// TypeORM 예제
@Entity()
export class Board {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
title: string;
@Column()
content: string;
@ManyToOne(() => User, user => user.boards)
author: User;
@CreateDateColumn()
createdAt: Date;
}
@Injectable()
export class BoardService {
constructor(
@InjectRepository(Board)
private boardRepository: Repository<Board>
) {}
async createBoard(data: CreateBoardDto, author: User): Promise<Board> {
const board = this.boardRepository.create({
...data,
author
});
return await this.boardRepository.save(board);
}
}
Prisma로 구현한 게시판
// schema.prisma
model Board {
id String @id @default(uuid())
title String
content String
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
}
// BoardService
@Injectable()
export class BoardService {
constructor(private prisma: PrismaService) {}
async createBoard(data: CreateBoardDto, authorId: string): Promise<Board> {
return await this.prisma.board.create({
data: {
...data,
author: {
connect: { id: authorId }
}
}
});
}
}
장단점 분석
TypeORM 장점
- 유연성
- 다양한 데이터베이스 지원
- Active Record/Data Mapper 패턴 선택 가능
- 복잡한 쿼리 빌더 제공
- 생태계
- 오랜 기간 검증된 안정성
- 풍부한 레퍼런스와 커뮤니티
- NestJS와의 긴밀한 통합
- 마이그레이션
- 자동 마이그레이션 생성
- 세밀한 마이그레이션 제어 가능
TypeORM 단점
- 타입 안정성
- 불완전한 타입 추론
- 런타임 오류 가능성
- 성능
- N+1 쿼리 문제
- 메모리 사용량 이슈
- 복잡성
- 상대적으로 가파른 학습 곡선
- 복잡한 관계 설정
Prisma 장점
- 타입 안정성
- 완벽한 타입 추론
- 컴파일 타임 오류 검출
- 개발자 경험
- 직관적인 API
- 우수한 IDE 지원
- 자동 완성 지원
- 성능
- 자동 쿼리 최적화
- 효율적인 데이터 로딩
Prisma 단점
- 유연성
- 제한된 데이터베이스 지원
- 복잡한 쿼리의 제한적 지원
- 학습 곡선
- 새로운 스키마 언어 학습 필요
- 독자적인 접근 방식
- 마이그레이션
- 제한적인 마이그레이션 제어
- 복잡한 마이그레이션 시나리오 처리의 어려움
선택 가이드
TypeORM이 적합한 경우
- 복잡한 레거시 데이터베이스와의 통합
- 세밀한 쿼리 제어가 필요한 경우
- Active Record 패턴 선호
- 다양한 데이터베이스 지원 필요
Prisma가 적합한 경우
- 새로운 프로젝트 시작
- 타입 안정성 중시
- 빠른 개발 속도 필요
- 단순하고 명확한 API 선호
마치며
두 ORM은 각각의 장단점이 있으며, 프로젝트의 요구사항과 팀의 선호도에 따라 선택할 수 있습니다. TypeORM은 전통적이고 유연한 접근 방식을, Prisma는 현대적이고 타입 안전한 접근 방식을 제공합니다.
참고 자료
https://octoping.tistory.com/37
반응형
댓글