최근에 typescirpt로 백엔드 서버를 제작하는데 관심이 생겨, 공부를 하던 중 typeorm이라는 typescript와 정말 잘 어울리는 orm 라이브러리를 발견하였다.
예전에 Spring boot 프레임워크를 공부하면서 사용했던 Jpa와 많이 비슷해서인지, 학습을 하는데 크게 어렵지는 않았다.
Jpa에서는 hibernate-validator 라는 라이브러리로 엔티티의 수정 사항을 DB에 반영하기전에, 각 프로퍼티에 올바른 값이 들어가있는지 검증할 수 있었다.
typeorm에서는 자체적으로 이러한 검증 모듈을 지원하진 않으나, class-validator를 이용하여 검증할 수 있었다.
src/entity/ValidationEntity.ts
import { BaseEntity, BeforeInsert, BeforeUpdate } from 'typeorm';
import { validateOrReject } from 'class-validator';
export abstract class ValidationEntity extends BaseEntity {
@BeforeInsert()
@BeforeUpdate()
async validate(): Promise<void> {
await validateOrReject(this);
}
}
abstract class로 정의하여 ValidationEntity를 직접 생성할 수 없도록 하였다.
ValidationEntity는 BaseEntity를 상속받는다.
@BeforeInsert, @BeforeUpdate decorator로 DB에 반영되기전에 validate 메서드를 실행하도록 지정하였다.
validateOrReject 함수는, 대상 엔티티를 검증하여, 올바른 값이 아닐 시에 에러를 발생시키는 함수다. 객체 자신을 인자로 넘겨 검증하도록 하였다.
DB에 저장 전 검증이 필요한 엔티티 클래스는 이 클래스를 상속하여 검증이 가능하다.
typeorm cli로 기본적으로 생성된 프로젝트를 약간 수정하였다.
전체 코드가 있는 레포지토리 url은 아래와 같다.
https://github.com/devminchan/typeorm-with-class-validator
src/entity/User.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { ValidationEntity } from './ValidationEntity';
import { Length, Min, IsEmail } from 'class-validator';
@Entity()
export class User extends ValidationEntity {
@PrimaryGeneratedColumn()
id: number;
@Length(3)
@Column()
firstName: string;
@Length(3)
@Column()
lastName: string;
@Min(18)
@Column()
age: number;
@IsEmail()
@Column()
email: string;
}
ValidationEntity를 상속받은 User 클래스의 프로퍼티에 validation decorator를 적용한 모습이다.
각 데코레이터의 기능을 짤막하게 요약하면 다음과 같다.
@Length: 프로퍼티의 length 최솟값, 최댓값을 설정할 수 있다(인자가 하나만 있을 시에는 최솟값).
@Min: 이름처럼 프로퍼티 값의 최솟값을 설정할 수 있다.
@IsEmail: 데코레이터는 프로퍼티 값이 email인지 판별한다.
src/index.ts
import 'reflect-metadata';
import { createConnection } from 'typeorm';
import { User } from './entity/User';
createConnection()
.then(async connection => {
console.log('Inserting a new user into the database...');
const user = new User();
user.firstName = 'minchan';
user.lastName = 'Kim';
user.age = 20;
user.email = 'minchan.dev@gmail.com';
await connection.manager.save(user);
console.log('Saved a new user with id: ' + user.id);
console.log('Loading users from the database...');
const users = await connection.manager.find(User);
console.log('Loaded users: ', users);
console.log('Here you can setup and run express/koa/any other framework.');
})
.catch(error => console.log(error));
위와 같이 index.ts를 수정하고 실행해보면, 정상적으로 DB에 저장되는 모습을 확인할 수 있다.
여기서 email 값을 이메일 아닌 다른 string으로 수정해보자.
user.email = '이메일 아님';
그런 뒤 실행하면 다음과 같이 콘솔창에 오류가 나타날 것이다. 또한 DB에도 반영되지 않는다.
❯ npm run start
> new-typeorm-project@0.0.1 start /Users/kimminchan/projects/to-test
> ts-node src/index.ts
Inserting a new user into the database...
[ ValidationError {
target:
User {
firstName: 'minchan',
lastName: 'Kim',
age: 20,
email: '이메일 아님' },
value: '이메일 아님',
property: 'email',
children: [],
constraints: { isEmail: 'email must be an email' } } ]
typeorm 상속 기능과 class-validator를 활용하여 정말 쉽고 빠르게 엔티티 검증 로직을 구현할 수 있었다.