기억의 실마리
2023. 2. 18. 17:22

TypeORM

TypeORM에서 ORM은 Object-Relational Mapping(객체- 관계 맵핑)의 약자이다.

객체와 관계형 데이터베이스의 데이터(이하 SQL)를 자동으로 맵핑해준다.

객체지향 프로그래밍은 클래스를 사용한 객체모델이며

SQL은 테이블을 사용한 모델이다. 서로 다른 형식의 모델이지만

ORM을 통해서 객체 간의 관계를 기반으로 SQL을 자동 생성한다.

 

 

 

TypeORM에 대한 견해

fastify와 express를 SQL를 맵핑할때 자주 사용되는 ORM으로 Sequelize, Knex, TypeORM 등이 있다.

요즘 Typescript가 인기를 얻어 급부상하면서 타입을 지정하여 모델을 정의하는

장점을 최대한 끌어낼 수 있기 때문에  TypeORM의 선택이 적합하다는 생각이 들었다.

특히나 서버와 데이터는 보안이 아주 중요하기 때문에 클라이언트로부터의 모든 요청이

거짓말일 수도 있다는 가정으로부터 데이터를 걸러서 받아야한다. 때문에 Typescript와 TypeORM의

엄격하고 규격화된 타입만을 요청할 수 있도록 만들어 메리트를 극대화시킬 수 있을 것이라 생각했다.

 

 

 

TypeORM Install

1. npm install  &  start

//기본설치
$ npm install typeorm --save

//nest.js에서 사용시
$ npm install --save @nestjs/typeorm typeorm mysql2

//프로젝트 시작
$ npm start

 

 

 

사용예시

※ 모두 공식문서의 예제 코드이다.

 

Active Record 패턴

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

BaseEntity클래스를 사용하여 Use클래스에 상속한 후 사용할 수 있으며 이를 통하여

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

 

생성자함수 new를 사용하면 새로운 인스턴스를 만들어 DB스키마를 직접 다룰 수 있다.

const userRepository = dataSource.getRepository(User)

const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await userRepository.save(user)

await userRepository.remove(user)

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 패턴

분리된 클래스에 쿼리 메소드를 정의한다. 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
}

 

getRepository()를 사용해서 접근할 수 있다.

const userRepository = dataSource.getRepository(User)

const user = new User()
user.firstName = "Timber"
user.lastName = "Saw"
user.isActive = true
await userRepository.save(user)

await userRepository.remove(user)

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",
})

 

 

Active Record  VS  Data Mapper

소프트웨어 개발과 관련하여 항상 염두에 해야 할 것은 애플리케이션을

어떻게 유지 관리할 것인지 생각하는 것이다. 이를 관점으로 보았을때

 

Data Mapper: 대규모 애플리케이션에서 더 효과적인 유지보수성을 지원한다.

 

Active Record: 소규모 앱에서 단순성을 유지하는 데 도움이 된다.

소규모앱의 경우 단순성이 유지됨으로서 유지보수성이 높아진다.

 

 

 

마치며...

전에 로그인기능 구현을 위해서 Nest.js서버와 MySQL을 다룬적이 있다. 이때 typeORM을 통해서 스키마를 만들고 클라이언트로부터 요청받은 정상적인 데이터를 저장하며 사용한 경험이 있다. 실제로 엄청나게 편리함을 느꼈고 앞으로도 객체지향형 서버를 다루게 되면 typeORM을 채택할 의향이 있다. 그리고 사용하게 된다면 반드시 Data Mapper방식으로 접근할지, Active Record방식으로 접근할지도 잘 선택해서 사용해야할 것 같다.

 

출처: https://typeorm.io/