TypeORM 시작하기

조성철 (JoSworkS)·2020년 2월 1일
15
post-custom-banner

이번 BillyZip 프로젝트에서는 Stroll에서 사용하였던 스택들은 물론 조금 더 다양한 스택들을 활용해보고자 하였다. Stroll에서 사용한 ORM인 Sequelize 대신에 타입스크립트와 더 잘 맞고 Active Record 패턴을 지원하는 typeORM을 사용하기로 하였다.

TypeORM을 사용하기에 앞서 TypeORM의 개요와 아래 링크를 통해 Active Record & Data Mapper 패턴을 알아보았다.
https://github.com/typeorm/typeorm/blob/master/docs/active-record-data-mapper.md

TypeORM 개요

express와 관계형 데이터베이스를 조합할 때 자주 사용되는 ORM으로 Sequelize가 있지만, 타입스크립트의 확대와 더불어 인기를 얻고 있는 ORM이 typeORM이다.

typeORM이라는 이름으로 인해 타입스크립트 전용의 ORM같이 느껴지지만, 자바스크립트(ES5, ES6, ES7)도 지원하고 있다.

TypeORM을 사용하여 개발을 하면 Python의 SQLAlchemy와 같은 Data Mapper 패턴과 Rails와 같은 Active Record 패턴으로 개발할 수 있다. 그리고 모델의 정의를 제대로 하면 타입을 정하는 메리트를 최대한으로 얻을 수 있고, 복잡한 모델간의 관계를 형성할 수 있다는 장점이 있다.

Active Record 패턴

TypeORM에서는 Active Record 패턴과 Data Mapper 패턴 두 종류의 패턴을 사용할 수 있으며 공식문서의 내용을 참고하여 서술하도록 하겠다.

먼저 Active Record 패턴은 모델 그 자체에 쿼리 메소드를 정의하고, 모델의 메소드를 사용하여 객체를 저장, 제거, 불러오는 방식이다.

아래는 공식문서의 예제 코드이며 BaseEntity라는 클래스를 사용하여 새로운 클래스에 상속하게 한 후 사용할 수 있다. 이를 통해 BaseEntity가 갖고 있는 메소드와 static으로 만들어 내는 커스텀 메소드를 이용할 수 있다.

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 예제코드이다.
아래와 같이 Acitve Record 패턴은 DB 엔티티에 직접 다룰 수 있도록 new 키워드를 이용하여 새로운 인스턴스를 만들어 사용하는 것이 가능하다.

이런걸 보면 자바스크립트의 방식을 이용하여 DB를 다룰 수 있게 해주는 ORM의 역할이 얼마나 개발자에게 편리함을 가져다 주는지 알 수 있다.

// example how to save AR entity
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.isActive = true;
await user.save();

// example how to remove AR entity
await user.remove();

// example how to load AR entities
const users = await User.find({ skip: 2, take: 5 });
const newUsers = await User.find({ isActive: true });
const timber = await User.findOne({ firstName: "Timber", lastName: "Saw" });
const timber = await User.findByName("Timber", "Saw");

Data Mapper 패턴

Data Mapper 패턴은 분리된 클래스에 쿼리 메소드를 정의하는 방식이며, Repository를 이용하여 객체를 저장, 제거, 불러온다.
Active Record 패턴과의 차이점은 모델에 접근하는 방식이 아닌 Repository에서 데이터에 접근한다는 것이다.

아래는 공식문서의 예제 코드이다. 먼저 클래스를 정의한다.

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

    @Column()
    isActive: boolean;

}

정의한 클래스를 Generic 타입을 이용하여 상속하게 한다.

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();
    }

}

그리고 getRepository()를 사용하여 만들어진 모델을 사용한다.

const userRepository = connection.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.find({ isActive: true });
const timber = await userRepository.findOne({ firstName: "Timber", lastName: "Saw" });

어떤 패턴으로 개발해야 하는가?

공식문서에 의하면 두 패턴은 각각의 장점이 있다고 한다. 아래 패턴별 특징을 고민하여 개발에 적용하면 좋을 듯하다. 나는 이번 프로젝트의 규모를 고려하여 Active Record 패턴으로 진행하고자 한다.

  • Active Record: 규모가 작은 애플리케이션에서 적합하고 간단히 사용할 수 있다.
  • Data Mapper: 규모가 큰 애플리케이션에 적합하고 유지보수하는데 효과적이다.
post-custom-banner

4개의 댓글

comment-user-thumbnail
2020년 3월 15일

설명이 너무 좋네요. 덕분에 TypeORM에 대해 많이 접근하게 되었습니다.
정말 감사드려요~^^

답글 달기
comment-user-thumbnail
2020년 8월 20일

설명이 너무 좋아요 ㅠㅠ 가뭄에 단비
도움이 많이 되었어요
감사합니다!!

답글 달기
comment-user-thumbnail
2021년 5월 9일

잘보고 갑니다 !!

답글 달기
comment-user-thumbnail
2021년 7월 27일

좋은 설명 감사합니다 !ㅎㅎ

답글 달기