TypeORM
왜 생겼나?
TypeORM은 Type + ORM으로 이해할 수 있다. 먼저, ORM(Object Realational Model) 은 OOP와 RDB간의 데이터 호환을 위한 시스템으로, 객체형 모델과 관계형 모델간의 불일치를 해소시키기위해 사용된다. 대표적으로 Sequelize나 JPA가 대표적이다. typeORM은 Javascript나 typescript에서 주로 사용되는 orm 중 하나이며, Hibernate,Doctrine,Entity FrameWork에 영향을 받아 제작되었다고 한다. (TypeORM is highly influenced by other ORMs, such as Hibernate, Doctrine and Entity Framework.)
Active Record & Data Mapper
TypeOrm의 Docs를 보게되면 가장 먼저 볼 수 있는 단어가 Active Record와 Data Mapper이다. TypeOrm은 두가지 패턴을 모두 지원한다고 설명하고 있다.
Active Record
Activie Record Pattern은 모델을 통해 데이터베이스에 접근하는 패턴이다. 정의한 모델내에 쿼리를 메소드 형태로 정의하고, 외부에서는 이를 사용한다. Active Record 패턴은 반드시 BaseEntity
를 상속해야한다. BaseEntity
는 entity와 상호작용하기 위한 method들을 갖고있으며, stadard한 Repository의 기능을 대부분 포함한다.
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();
}
}
위와 같이 BaseEntity
를 상속받아 findByName이라는 쿼리를 포함한 모델을 작성한다.
const timber = await User.findByName("Timber", "Saw");
생성한 User.findByName 메서드는 위와같이 사용할 수 있다.
Active Record에 관한 자세한 내용은 이곳 에서 확인할 수 있다.
Data Mapper
DataMapper Pattern은 모델과 별개로 repositories
라는 class에 query들을 정의하는 패턴이다.
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
isActive: boolean;
}
위와같이 Data Mapper Pattern은 entity에 모델만 정의한다.
import {EntityRepository, Repository} from "typeorm";
import {User} from "../entity/User";
@EntityRepository()
export class UserRepository extends Repository<User> {
findByName(firstName: string, lastName: string) {
return this.createQueryBuilder("user")
.where("user.firstName = :firstName", { firstName })
.andWhere("user.lastName = :lastName", { lastName })
.getMany();
}
}
해당 모델을 위한 쿼리는 Repository
를 상속받아 작성한다.
const userRepository = connection.getCustomRepository(UserRepository);
const timber = await userRepository.findByName("Timber", "Saw");
사용법은 위와 같다.
어느것을 선택해야 하는가?
항상 그렇듯이 정답은 없다. 현재 개인의 상황에 맞춰 각 패턴을 사용하면된다. typeORM에서는 작고 간단한 앱에서는 Active Record를 크고 관리가 필요한 앱에서는 DataMapper를 추천한다.
사용법
기본적인 셋업과 사용법, 그리고 주로 데코레이터 등에 대한 설명은 공식문서에 굉장히 자세히 잘 설명되어 있다. 또 각각의 언어와 DB, 환경을 구분하여 별도의 예제를 제공하고 있다. ex) typescript , MongoDB.
적용
현재 내 프로젝트에는 typeORM은 DataMapper 패턴을 기반으로 작성되어 있으며, ORM을 별도의 Repo로 분리하였다. 이를 private npm에 올려둔 뒤, MSA형태로 구성되어있는 여러개의 레포에서 사용하고 있다.