Archive

TypeORM 과 API

livemehere 2022. 7. 31. 05:12

그동안 express로 서버개발을 꽤많이 해왔고, 다른 커뮤니티를 찾아보지 않아도 느꼈던, express의 자유도는 오히려 장기적으로, 엔터프라이즈 용으로는 분리한 점이 느껴졌다.

중간중간 Nest의 핵심 개념과 기능들을 정리해 보고자 한다.

 

공식문서

 

TypeORM - Amazing ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server,

 

typeorm.io

 

공식문서는 아니지만, 검색시 노출이 많이되는 꽤 괜찬은 문서

 

README_ko - typeorm

이 예시에서는 MySQL을 사용하고 있지만 지원되는 다른 데이터베이스를 사용할 수도 있다. 다른 데이터 베이스를 사용하려면 옵션의 type을 사용 중인 데이터베이스 타입으로 변경하기만 하면 된

orkhan.gitbook.io

 

DB연결하기

서버에서 가장중요한것이 DB와 연결된 API를 제공하는 것이라 생각한다.

인증, dto 이런것들 이 이후라고 생각한다.

 

mysql + typeorm 설치

npm install --save @nestjs/typeorm typeorm mysql2

 

docker db 설치

설치후 스키마도 하나 생성해준다.

docker run --name my-mysql -e MYSQL_ROOT_PASSWORD=1234 -d -p 3306:3306 mysql:latest

 

app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PostsModule } from './posts/posts.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: '1234',
      database: 'kong',
      synchronize: true,
      keepConnectionAlive: true,
      logging: 'all',
      charset: 'utf8mb4',
      autoLoadEntities: true,
    }),
    PostsModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
  • type: db 종류
  • host: db 주소
  • port: db포트(mysql은 보통 3306)
  • username: db 사용자이름
  • password: db 사용자 비번
  • database: db 데이터베이스 이름
  • entities: 만든 엔티티들 여기에 넣기
  • autoLoadEntities: true면 TypeOrmModule.forFeature에 넣은 엔티티들은 자동 연결됨
  • synchronize: true면 entity에 따라 실제 db 테이블 재생성(기존 테이블 다 날라가니 주의)
  • logging: true면 typeorm이 만들어주는 쿼리가 콘솔에 표시(개발 시에는 켜두는 게 매우 유용)
  • keepConnectionAlive: true여야 핫 리로딩시에도 db 연결이 끊어지지 않음
  • charset: db의 언어 설정(utf8mb4로 할것)

엔티티 생성(테이블 생성)

Repository pattern

TypeORM을 사용하는 패턴은 여러개가 있다. 그중에서 개인적으로 repository 패턴을 선호한다.(이유는 처음이걸로 접했으니까..)

각 Entity는 각각의 Repository(저장공간)이 있다.

 

Entity(테이블)정의

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

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

  @Column()
  title: string;

  @Column({ type: 'text' })
  content: string;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}

(밑에서 forFeature([Arficle])) DI시 -> 자동으로 디비에 테이블이 생성됨

모듈, 컨트롤러, 서비스 생성하기

nest에서는 큰 기능 범주로 모듈을 나누고, 모듈 내에서 요청과 응답을 처리하는 controller, 비지니스로직이 들어가는 service 3가지로 구성된다.

 

nest g mo posts // 모듈 생성
nest g co posts // 컨트롤러 생성
nest g s posts // 서비스 생성

테스트 파일을 제거하면 3개의 파일이 핵심이다.

API 생성하기

provider는 단순히 @Injectable() 데코레이터 주석이 달린 클래스를 말한다. provider 객체는 런타임에 NestJS에 의해 다른 모듈로 주입될 수 있다.
provider는 service를 포함해 repository, factory, helper 등이 될 수 있다.

@Injectable() 데코레이터가 붙어있는 클래스는 constructor를 통해서 의존성을 주입할 수 있다. 이렇게 하면 클래스 내부에서 this를 통해서 해당 서비스에 접근할 수 있게 된다.

import { Controller, Get, Post } from '@nestjs/common';
import { PostsService } from './posts.service';

@Controller('posts')
export class PostsController {
  constructor(private readonly postsService: PostsService) {}

  @Get()
  findAll() {
    return this.postsService.findAll();
  }

  @Post()
  create() {
    return this.postsService.createOne();
  }
}
import { Injectable } from '@nestjs/common';

@Injectable()
export class PostsService {
  findAll() {
    return 'findAll';
  }

  createOne() {
    return 'create';
  }
}

TypeORM Dependency Injection

Post데코레이터와 겹쳐서 Entity의 이름을 Article로 변경하였습니다
@Module({
  imports: [TypeOrmModule.forFeature([Article])],
  controllers: [PostsController],
  providers: [PostsService],
})
import { Body, Controller, Get, Post } from '@nestjs/common';
import { PostsService } from './posts.service';

@Controller('posts')
export class PostsController {
  constructor(private readonly postsService: PostsService) {}

  @Get()
  findAll() {
    return this.postsService.findAll();
  }

  @Post()
  create(@Body() body: any) {
    return this.postsService.createOne(body);
  }
}

 

조회, 생성

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Article } from '../entity/article.entity';
import { Repository } from 'typeorm';

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Article)
    private articleRepository: Repository<Article>,
  ) {}

  findAll() {
    return this.articleRepository.find();
  }

  async createOne(body) {
    const { title, content } = body;
    const newArticle = this.articleRepository.create({ title, content });
    return this.articleRepository.save(newArticle);
  }
}

 

수정,삭제

@Patch(':id')
update(@Param('id') id: string, @Body() body: any) {
return this.postsService.updateById(+id, body);
}

@Delete(':id')
remove(@Param('id') id: string) {
return this.postsService.removeById(+id);
}
updateById(id: number, body: any) {
return this.articleRepository.update(id, body);
}

removeById(id: number) {
return this.articleRepository.delete(id);
}

 

반응형