백앤드(Back-End)/Nest.js

[Nest.js] Nest.js API 만들기 (5) - TypeORM 개념 및 설치

RyanSin 2021. 11. 13. 16:49
반응형

- 개요

안녕하세요. 이번 시간에는 TypeORM을 Nest.js에 적용하는 시간을 가져 보도록 하겠습니다.

 

TypeORM은 ORM 기술 중 하나로 "객체와 관계형 데이터 베이스를 매핑(연결)을 통해 객체 모델과 관계형 모델 간 불일치를 해결" 함으로써 객체와 데이터베이스의 변형에 유연하게 사용할 수 있는 기술입니다.


ORM(Object Relational Mapping)

TypeORM


- 설치 및 설정

TypeORM을 사용하기 위해서는 몇 가지 모듈이 필요합니다.

 

  1. @nestjs/typeorm - Nest.js에서 TypeORM을 사용하기 위해 연동시켜주는 모듈
  2. typeorm - 실제 TypeORM(TypeORM은 JavaScript를 지원합니다)
  3. mysql2 - MySQL 데이터베이스 연결 시 사용
#npm
npm install mysql2 typeorm @nestjs/typeorm

#yarn
yarn add mysql2 typeorm @nestjs/typeorm

 

TypeORM은 다양한 데이터베이스를 지원합니다.

더보기

RDBMS : MySQL / MariaDB / Postgres / CockroachDB / SQLite / Microsoft SQL Server / Oracle / SAP Hana / sql.js

NoSQL : MongoDB

 

TypeORM 파일 설정

src/configs 폴더를 생성하고 파일 이름은 본인이 편한 이름으로 생성해주시면 됩니다. 저는 typeorm.config.ts로 생성했습니다.

(ts 확장자는 무조건 해주셔야 합니다. 컴파일 시 제외될 수 있습니다.)

 

TypeORM 환경 파일

 

typeorm.config.ts 파일 안에는 우리가 실제 연결 시 필요한 설정 값들은 작성합니다.

 

import { TypeOrmModuleOptions } from '@nestjs/typeorm';

export const typeORMConfig: TypeOrmModuleOptions = {
  type: 'mysql', //Database 설정
  host: 'localhost',
  port: 3306,
  username: 'root',
  password: '1234',
  database: 'Ryan',
  entities: ['dist/**/*.entity.{ts,js}'], // Entity 연결
  synchronize: true, //true 값을 설정하면 어플리케이션을 다시 실행할 때 엔티티안에서 수정된 컬럼의 길이 타입 변경값등을 해당 테이블을 Drop한 후 다시 생성해준다.
};

위 설정 옵션 중에 synchronize 옵션은 배포 단계에서는 false로 설정해야 합니다.

그렇지 않으면... 데이터베이스가 초기화가 진행돼서 데이터가 전부 없어지는 상황이 발생하게 됩니다...(서비스가... 망할 수 있어요 ㅠㅠ)

 


설정 파일 연결

우리가 만든 파일 정보를 Nest.js에서 사용하기 위해서는 app.module.ts 파일에 연결해야 합니다.

 

import {
  Module,
  NestModule,
  MiddlewareConsumer,
  RequestMethod,
} from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeORMConfig } from './configs/typeorm.config';

import { UserModule } from './user/user.module';
import { AuthMiddleware } from './middleware/auth.middleware';
import { UserController } from './user/user.controller';

@Module({
  imports: [
    TypeOrmModule.forRoot(typeORMConfig), // TypeORM 설정 파일 연결
    UserModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(AuthMiddleware)
      //exclude 함수는 제외 하고싶은 라우터를 등록합니다.
      .exclude({ path: 'user/create_user', method: RequestMethod.POST }) // 유저 생성
      .exclude({ path: 'user/user_all', method: RequestMethod.GET }) // 유저 전체 조회
      .forRoutes(UserController); // 1.유저 컨트롤러 등록
    // .forRoutes('user'); // 2.유저 컨트롤러 경로 등록 -> 위 1번과 동일
  }
}

TypeOrmModule과 typeORMConfig 파일 가져와 imports 항목에 추가합니다.

forRoot안에 설정한 파일은 모든 모듈에 적용이 됩니다.

 

 

- Entity 생성 및 Repository 연결

지난 시간까지 User를 Memory에 저장을 통해 임시로 진행했었습니다. 이 부분을 실제 TypeORM 엔티티로 변경해보겠습니다.

 

저는 디렉토리 구조를 아래와 같이 가져갑니다.

 

Eentity & Repository

Entity 생성

import {
  BaseEntity,
  Column,
  Entity,
  PrimaryGeneratedColumn,
  CreateDateColumn,
  UpdateDateColumn,
  DeleteDateColumn,
  Unique,
} from 'typeorm';

@Entity({ name: 'user' })
@Unique(['user_id'])
export class User extends BaseEntity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ type: 'varchar', length: 50, comment: '유저 아이디' })
  user_id: string;

  @Column({ type: 'varchar', length: 255, comment: '유저 비밀번호' })
  password: string;

  @Column({ type: 'varchar', length: 255, comment: 'salt' })
  salt: string;

  @Column({ type: 'varchar', length: 30, comment: '유저 이름' })
  name: string;

  @Column({ type: 'tinyint', comment: '유저 나이' })
  age: number;

  @CreateDateColumn({ name: 'create_at', comment: '생성일' })
  createdAt: Date;

  @UpdateDateColumn({ name: 'update_at', comment: '수정일' })
  updatedAt: Date;

  @DeleteDateColumn({ name: 'delete_at', comment: '삭제일' })
  deletedAt?: Date | null;
}
옵션

@Entity - 해당 클래스는 DB user 테이블과 매핑시킬 때 사용

@Unique - 유니크 컬럼을 설정할 때 사용(배열 형태로 원하는 컬럼 값을 지정하면 된다)

@PrimaryGeneratedColumn - uuid 값을 지정하면 해당 컬럼은 uuid 타입으로 설정이 되며, Auto Increment 타입으로 설정

                                                    Auto_Increment : @PrimaryGeneratedColumn()

                                                    UUID: @PrimaryGeneratedColumn('uuid')

@Column - 해당 클래스 속성과 DB user 테이블 컬럼과 매핑시킬 때 사용

@CreateDateColumn - 데이터가 생성되는 시간을 기록할 때 사용

@UpdateDateColumn - 데이터가 수정되는 시간을 기록할 때 사용

@DeleteDateColumn - 데이터가 삭제되는 시간을 기록할 때 사용(실제 삭제되지 않는다. 백업 서버가 없다면 해당 옵션을 사용!!)

 

 


Repository 생성 및 연결

Nest.js는 저장소 패턴(Repository Pattern)을 지원합니다. Repository 계층에서는 DB 작업을 다룹니다.

 

지난 시간에 Nest.js가 클라이언트 요청을 처리하는 로직을 설명했었습니다. 

 

"1. @Controller -> 2.@Service -> 3. Repository"

 

@Controller 계층에서는 클라이언트 요청 정보를 처리하고 @Service 계층에서는 비즈니스 로직을 담당하며, Repository 계층에서는 DB 작업을 다룬다고 생각하시면 됩니다.

 

UserRepository 

import { EntityRepository, Repository } from 'typeorm';
import { User } from '../entity/user.entity';

@EntityRepository(User)
export class UserRepository extends Repository<User> {}

 

연결

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserRepository } from 'src/repository/user.repository';
import { UserController } from './user.controller';
import { UserService } from './user.service';

@Module({
  imports: [TypeOrmModule.forFeature([UserRepository])], //UserRepository 등록
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

우리는 UserModule에서 사용해야하기 때문에 UserModule에 UserRepository를 등록합니다.

 

이렇게 등록하고 UserService에 등록하고 사용하면 됩니다.

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from 'src/repository/user.repository';
import { CreateUserDto, UpdateUserDto } from './dto/user.dto';
import { User } from 'src/entity/user.entity';

@Injectable()
export class UserService {
  //생성자 부분에 가져와 사용한다.
  constructor(
    @InjectRepository(UserRepository) private userRepository: UserRepository,
  ) {}
}

 

여기까지 문제 없이 진하셨다면 UserService 계층에서 Repository에 접근이 가능합니다.

 

다음 시간에는 기존에 만들었던 API를 변경해보겠습니다.