본문 바로가기
DB/typeORM

시작하기 - Entity 형성 및 연결(Nest.js & MySQL)

by 싯벨트 2022. 10. 10.
728x90

# nestjs 초기세팅

nest new typeorm (typeorm 자리에 원하는 프로젝트 이름을 넣는다)
- npm 을 선택하고
$ cd typeorm (폴더를 이동하여)
$ nest g res photos (리소스 전체(모듈, 컨트롤러, 서비스) 및 CRUD를 생성한다)
$ nest g res users
- REST API / CRUD endpoint 생성 동의(yes 입력)

# typeorm 초기세팅

[typeorm 공식문서 명시]

  • npm i --save typeorm
  • npm i reflect-metadata --save
  • npm i @types/node -D
  • npm i mysql2 --save

[nestjs 공식문서 명시]

  • npm i --save @nestjs/typeorm

# DB 연결

- TypeOrmModule.forRoot()에 DB 정보를 연결한다.
- AppModule을 export하면서 실행자 생성을 통해 TypeORM DataSource와 EntityManager 객체를 주입 없이 사용 가능하게 설정한다.

//app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { PhotosModule } from './photos/photos.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'root',
      entities: [],
      synchronize: true,
    }),
    UsersModule,
    PhotosModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {
  constructor(private dataSource: DataSource) {}
}

# 모델 만들기

- DB 의 테이블 역할을 할 모델을 만든다.

export class Photo {
  id: number;
  name: string;
  description: string;
  filename: string;
  views: number;
  isPublished: boolean;
}

# 엔티티 만들기

- @Entity() 데코레이터를 추가하여 모델을 엔티티로 만들고, 이것 해당 app 어디서든 사용할 수 있다.
- 엔티티로 load/insert/update/remove 는 물론 다른 동작들을 실행할 수 있다.

import { Entity } from 'typeorm';

@Entity()
export class Photo {
  id: number;
  name: string;
  description: string;
  filename: string;
  views: number;
  isPublished: boolean;
}

# 컬럼만들기

- @Column() 데코레이터를 추가하여 테이블에 있는 컬럼으로 명시한다.

import { Column, Entity } from 'typeorm';

@Entity()
export class Photo {
  @Column()
  id: number;

  @Column()
  name: string;

  @Column()
  description: string;

  @Column()
  filename: string;

  @Column()
  views: number;

  @Column()
  isPublished: boolean;
}

# Primary Key(Column) 설정

- 엔티티는 반드시 최소 1개 이상의 primary key를 가져야 한다.
- @PrimaryColumn() 데코레이터를 사용하고, id와 같은 auto-increment 옵션을 주고 싶다면, @PrimaryGeneratedColumn()데코레이터를 사용한다.

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

@Entity()
export class Photo {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  description: string;

  @Column()
  filename: string;

  @Column()
  views: number;

  @Column()
  isPublished: boolean;
}

# 컬럼 타입 설정

- string은 기본적으로 varchar(255)로 설정된다.
- 길이 추가 컬럼 데코레이터 안에 명시한다. {length:100}
- text, double 등 구체적인 타입을 따옴표로 명시한다.

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

@Entity()
export class Photo {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 100 })
  name: string;

  @Column('text')
  description: string;

  @Column()
  filename: string;

  @Column('double')
  views: number;

  @Column()
  isPublished: boolean;
}

# DB 시작하기

- 작성한 엔티티를 <DB 연결> 에서 TypeOrmModule.forRoot({}) 내부 속성 중, entities: [Photo] 처럼 기재한다.

#테이블 생성하기

- npm run start(:dev) 를 통해 서버를 실행해주면 DB에 테이블이 생성된다.
- table-plus와 같은 프로그램을 사용한다면 새로고침을 누르면 확인 가능하고, 터미널에서 mysql을 사용한다면 설정한 database에 들어가서 desc photo를 입력하면 테이블 구조를 확인할 수 있다.
- MySQL 에는 boolean 타입이 없고, 0과 1의 표현을 위해 tinyint 타입으로 저장된다.

# Repository 사용하기

- Photo 모듈에서 생성한 레포지토리를 사용할 수 있도록 TypeOrmModule.forFeature()로 주입한다.

import { Module } from '@nestjs/common';
import { PhotosService } from './photos.service';
import { PhotosController } from './photos.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Photo } from './entities/photo.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Photo])],
  controllers: [PhotosController],
  providers: [PhotosService],
})
export class PhotosModule {}

# Repository를 사용해서 데이터 추가하기 (with 포스트맨)

- 생성할 데이터를 위한 createPhotoDto 를 생성한다.
- 기본적인 CRUD를 service.ts와 controller.ts에 구현한다.
- 포스트맨에서 메소드와 Body 형식을 raw - JSON으로 설정하고, createPhotoDto와 일치하는 데이터를 보낸다.

//create-photo.dto.ts

export class CreatePhotoDto {
  name: string;
  description: string;
  filename: string;
  views: number;
  isPublished: boolean;
}
//photos.controller.ts

import { Controller, Get, Param, Delete, Post, Body } from '@nestjs/common';
import { CreatePhotoDto } from './dto/create-photo.dto';
import { PhotosService } from './photos.service';

@Controller('photos')
export class PhotosController {
  constructor(private readonly photosService: PhotosService) {}

  @Post()
  createPhoto(@Body() createPhotoDto: CreatePhotoDto) {
    this.photosService.createPhoto(createPhotoDto);
    return JSON.stringify({ message: 'Success_creating_photo' });
  }
  @Get()
  findAll() {
    return this.photosService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.photosService.findOne(+id);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.photosService.remove(+id);
  }
}
//photos.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreatePhotoDto } from './dto/create-photo.dto';
import { Photo } from './entities/photo.entity';

@Injectable()
export class PhotosService {
  constructor(
    @InjectRepository(Photo)
    private photoRepository: Repository<Photo>,
  ) {}

  async createPhoto(createPhoto: CreatePhotoDto): Promise<void> {
    await this.photoRepository.save(createPhoto);
  }

  findAll(): Promise<Photo[]> {
    return this.photoRepository.find();
  }

  findOne(id: number): Promise<Photo> {
    return this.photoRepository.findOneBy({ id });
  }

  async remove(id: number): Promise<void> {
    await this.photoRepository.delete(id);
  }
}

# 메소드와 data loading 형태

- find()

- findOneBy()

- findBy()

- save() : update 구현

update는 findOneBy로 바꿀 데이터를 불러오고, 불러온 데이터에서 변경할 키값을 새롭게 부여하고, 변경된 데이터를 다시 save한다.

// service

  async updateOne(id: number, body) {
    const photoOne = await this.photoRepository.findOneBy({ id });
    const key = Object.keys(body)[0];
    photoOne[`${key}`] = Object.values(body)[0];

    await this.photoRepository.save(photoOne);
    return photoOne;
  }
  
// controller

 @Patch(':id/update')
  updateOne(@Param('id') id: string, @Body() body) {
    return this.photosService.updateOne(+id, body);
  }

# remove 역시, 객체를 넣어서 지운다.

- delete() 는 삭제할 객체의 속성 입력 (2번 데이터 삭제)
- remove() 는 삭제할 객체 자체 입력 (4번 데이터 삭제)

//delete 사용

async remove(id: number): Promise<void> {
    await this.photoRepository.delete(id);
  }
  
//remove 사용

  async removeByRemove(id: number): Promise<void> {
    const targetPhoto = await this.photoRepository.findOneBy({ id });
    await this.photoRepository.remove(targetPhoto);
  }