TypeORM은 Active Record와 Data Mapper pattern을 둘 다 사용할 수 있다고 하였다. 그리하여 나는 두 패턴에 대해 공부해보고 내가 사용할 패턴을 선택하려고 한다.
Active Record의 경우, 모든 Query Method를 모델 내에 정의하고 사용하여 객체를 저장, 삭제 및 로드할 수 있다. 즉, SQL을 직접 작성하지 않고 데이터를 조작할 수 있다는 말이다. 그게 어떻게 가능한 것일까? 간단한 예제를 통해 알아보려고 한다.
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
isActive: boolean
static findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany()
}
}
Active Record Entity들은 모델 클래스를 정의할 때 BaseEntity를 Extends(상속)하기에 Repository나 Entity Manager를 사용하지 않아도 된다. 또한, BaseEntity는 표준 Repository 클래스의 메소드를 대부분 가지고 있다.
그리고 위 정의된 User Method는 아래와 같이 사용할 수 있다고 합니다.
const timber = await User.findByName("Timber", "Saw")
이어서 Data Mapper Pattern에 대해 정리하려고 한다. Data Mapper의 경우, Repository'클래스에 모든 쿼리 메서드를 정의하고, 객체를 저장, 제거 및 로드할 수 있다. 즉, SQL을 작성하지 않고, 데이터를 조작하기 위해 모델이 아닌 Repository를 사용한다. 아래의 예제를 통해 알아보도록 한다.
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
@Column()
firstName: string
@Column()
lastName: string
@Column()
isActive: boolean
}
User Class는 따로 상속 받는 클래스가 없다. 그리고 User에 대한 Repository는 다음과 같이 사용한다.
const userRepository = dataSource.getRepository(User)
// example how to save DM entity
const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await userRepository.save(user)
// example how to remove DM entity
await userRepository.remove(user)
// example how to load DM entities
const users = await userRepository.find({ skip: 2, take: 5 })
const newUsers = await userRepository.findBy({ isActive: true })
const timber = await userRepository.findOneBy({
firstName: "Timber",
lastName: "Saw",
})
이와 같이 Data Mapper의 경우, User 클래스를 선언하고 클래스에 대한 Repository를 가지고 와서 데이터를 조작하게 된다.
위에 설명한 내용이 이외에 찾아본 정보를 토대로 이 글을 마무리 해보려고 한다.
Active Record 패턴은 객체와 데이터베이스 레코드를 1:1 매핑하는 방식으로 동작한다. 각각의 엔티티는 데이터베이스 레코드에 대응하며, 해당 엔티티는 데이터베이스 연산(CRUD)을 직접 수행할 수 있다. 이 방식은 간단하고 직관적이지만, 복잡한 애플리케이션에서는 유지보수성이 낮아지고 확장성이 떨어질 수 있다.
Data Mapper 패턴은 객체와 데이터베이스 레코드를 서로 분리하여 매핑하는 방식으로 동작한다. 이 패턴에서는 엔티티가 데이터베이스와 상호작용하는 코드를 포함하지 않으며, 대신 데이터 매퍼(Data Mapper) 객체를 통해 데이터베이스와 통신한다. 이 방식은 확장성과 유지보수성이 높으며, 엔티티와 데이터베이스 스키마를 서로 다른 방식으로 변경해도 서로에게 영향을 주지 않는다. 그러나 구현이 복잡하고 코딩량이 많아질 수 있다.
요약하자면, Active Record 패턴은 단순하고 직관적이며 작은 규모의 애플리케이션에서 유용하다. Data Mapper 패턴은 복잡한 애플리케이션에서 확장성과 유지보수성이 뛰어나며, 코드량이 증가하더라도 적용하는 데 유용하다.
[TypeORM 공식 홈페이지]
https://typeorm.io/active-record-data-mapper#what-is-the-active-record-pattern