Typeorm과 class-validator로 엔티티 저장 시 검증 로직 구현하기

Minchan Kim·2020년 3월 22일
6
post-thumbnail

Typeorm 엔티티를 검증할 수 없을까?

최근에 typescirpt로 백엔드 서버를 제작하는데 관심이 생겨, 공부를 하던 중 typeorm이라는 typescript와 정말 잘 어울리는 orm 라이브러리를 발견하였다.

Typeorm 문서
Typeorm 깃허브

예전에 Spring boot 프레임워크를 공부하면서 사용했던 Jpa와 많이 비슷해서인지, 학습을 하는데 크게 어렵지는 않았다.

Jpa에서는 hibernate-validator 라는 라이브러리로 엔티티의 수정 사항을 DB에 반영하기전에, 각 프로퍼티에 올바른 값이 들어가있는지 검증할 수 있었다.
typeorm에서는 자체적으로 이러한 검증 모듈을 지원하진 않으나, class-validator를 이용하여 검증할 수 있었다.

class-validator - npm

검증 가능한 Validation Entity

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에 저장 전 검증이 필요한 엔티티 클래스는 이 클래스를 상속하여 검증이 가능하다.

Example Code

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를 활용하여 정말 쉽고 빠르게 엔티티 검증 로직을 구현할 수 있었다.

profile
즐겁게 코딩하고 싶다

0개의 댓글