[ Nest ] postgresDB를 이용한 CRUD 앱 만들기 (1)
🌈 프로그래밍/Nest JS

[ Nest ] postgresDB를 이용한 CRUD 앱 만들기 (1)

반응형

 

안녕하세요? 수구리입니다.

지난 Nest 포스팅에서 postgres 설치를 하고, pgAdmin을 통해서 Server와 DB까지 생성해보았으며,

프로젝트에서 postgres 모듈을 사용하기 위해 설치까지 진행해보았습니다!

지난 포스팅을 참조해주세요!

 

2021.11.16 - [프로그래밍/Nest JS] - [ Nest JS ] PostgreSQL 설치 pgAdmin으로 Server와 DB를 구축해보자

 

[ Nest JS ] PostgreSQL 설치 pgAdmin으로 Server와 DB를 구축해보자

안녕하세요? 수구리입니다. 이번 포스팅에서는 PostgresSQL 이라는 DB를 소개하고 설치법과 Nest 프로젝트에 연동하는 방법에 대해서 알아 보자! 어느 곳에서든지 DataBase는 필수이다. MongoDB라는 비정

tasddc.tistory.com

2021.11.17 - [프로그래밍/Nest JS] - [ Nest JS ] TypeORM & pg module 설치 명령어

 

[ Nest JS ] TypeORM & pg module 설치 명령어

안녕하세요? 수구리입니다. 저번 포스팅에서는 Nest 애플리케이션에 DB를 적용하기 위해서 Postgre와 pgAdmin을 설치하였다. 이전 글 참고! [ Nest JS ] Postgres 설치 pgAdmin으로 Server와 DB를 구축해보자 안..

tasddc.tistory.com

 

 

이제는 본격적으로 Nest와 postgresDB를 사용하여 아주 기초적인 CRUD 앱을 만들어보려고 합니다.

여기서 잠깐! CRUD가 무엇이냐?!

 

[ CRUD란? ]

Create, Read, Update, Delete의 앞글자만 따온 것으로 "크루드" 라고 부릅니다.

이는 사용자 인터페이스가 갖추어야 할 기능을 의미하죠!

예를 들어 홈쇼핑 웹사이트가 있다고 가정해 보겠습니다. 그러면 우선, 가장 먼저 홈페이지의 홈 화면이 보이죠?

거기에는 다양한 정보들을 조회할 수 있도록 구성해놓았고, 검색을 통해서 특정 상품을 찾을 수도 있습니다.

추가로 중고장터 같은 경우는 본인이 직접 상품을 등록할 수도 있죠? 이러한 모든 활동을 CRUD 라고 합니다.

즉, 정보의 조회, 삭제, 수정, 생성등을 의미합니다.

이 CRUD는 각각의 SQL문으로 대응이 가능한데 바로 아래와 같습니다.

이름 조작 SQL
Create 생성 INSERT
Read 읽기 SELECT
Update 갱신 UPDATE
Delete 삭제 DELETE

이러한 기능을 하는 Nest Backend app을 구상해 봅시다.

 

우선 가장 자주 프로젝트로 진행되어지고, 모든 웹 Backend app의 기본이라고 할 수 있는 간단한 게시판을 구상해보도록 하겠습니다.

필요한 것만 정리해보면,,

  1. 게시물 생성하기
  2. 게시물 조회하기
  3. 게시물 삭제하기
  4. 게시물의 상태 업데이트하기

정도가 있겠죠? 여기서 게시물의 상태란 티스토리를 예로 들면 글을 쓰고 난 뒤, 비공개 글로 설정을 할 것인지 아니면 공개글로 설정을 할 건지에 대한 선택을 할 수 있는데요! 바로 이 상태입니다.

해당 게시물이 공개글이냐 비공개글이냐에 대한 상태를 말합니다.

 

[ Board Model 정의하기 ]

바로 CRUD를 구현하기 이전에 게시물에는 어떤 속성들이 필요하고, 어떤 값을 저장할 건지에 대한 정의가 필요합니다.

즉, 각각의 게시물에 대한 기본적인 틀을 만들어 준다고 생각하면 될 것 같습니다.

우선 boards에 대한 모듈과 컨트롤러, 그리고 서비스는 만들어졌다고 가정하고 진행하겠습니다. 

 

[ boards.model.ts ]

export interface Board {
    id: string;
    title: string;
    description: string;
    status: BoardStatus;
}

export enum BoardStatus {
    PUBLIC = 'PUBLIC',
    PRIVATE = 'PRIVATE'
}

Board라는 interface를 만들어주고, 이에 사용될 속성의 이름과 Type을 지정해 줍니다.

여기서 BoardStatus는 PUBLIC과 PRIVATE 이 두 값만 가지도록 enum을 사용해서 지정해줍니다.

 

[ 게시물 생성하기 ]

그렇다면 모든 준비는 끝났고 이제 본격적으로 CRUD의 C부분인 게시물을 생성해보도록 하겠습니다.

우선 컨트롤러 부분부터 구현해보겠습니다.

 

[ boards.controller.ts ]

    @Post()
    @UsePipes(ValidationPipe)
    createBoard(@Body() createBoardDto: CreateBoardDto): Promise<Board> {
        return this.boardsService.createBoard(createBoardDto);
    }

위의 Post 요청은 "boards"라는 컨트롤러에 속해있으므로 게시물을 생성하기 위해서 보내는 요청은

localhost:3000/boards이며 POST 요청이 되겠습니다.

인자로는 @Body 데코레이터를 통해서 CreateBoardDto 라는 데이터 타입의 형태인 값을 가져올 거라고 명시해 주었습니다.

그리고 이 createBoard라는 post 요청은 1개의 Board 객체를 반환해야 함을 명시해주는 Promise를 사용합니다.

이후 비즈니스 로직은 서비스에서 처리할 예정이므로 서비스에 createBoard라는 함수가 존재해야겠죠?

이 함수는 서비스에서 구현하기로 하고, 값을 검증하기 위해서 DTO를 만들어 줍시다.

 

[ dto/create-board.dto.ts ]

위의 경로에 CreateBoardDto를 정의해주도록 하겠습니다.

import { IsNotEmpty } from "class-validator";

export class CreateBoardDto {
    @IsNotEmpty()
    title: string;

    @IsNotEmpty()
    description: string;
}

게시물을 만드는데 제목과 내용이 비어있으면 안 되겠죠?

class-validator라는 모듈을 사용해서 그 안에 IsNotEmpty라는 것을 가져와서

@InNotEmpty 데코레이터를 통해 title과 description이 비어있지 않는 값이 되도록 검증하도록 합니다.

검증이 완료되면 위에서 서비스 쪽으로 createBoard 함수를 호출하면서 값을 넘겨주도록 합니다.

 

[ boards.service.ts ]

서비스를 구현해보도록 하겠습니다.

@Injectable()
export class BoardsService {
  constructor(
        @InjectRepository(BoardRepository)
        private boardRepository: BoardRepository,
    ) { }
    
  async createBoard(createBoardDto: CreateBoardDto): Promise<Board> {
      const { title, description } = createBoardDto;

      const board = this.boardRepository.create({
          title,
          description,
          status: BoardStatus.PUBLIC
      })

      await this.boardRepository.save(board);
      return board;
  }
    
}

인자로 받아온 CreateboardDto 형태의 값에서 title과 descripton을 가져오고,

board라는 변수를 하나 만들어주며, 제목과 글의 내용 그리고 게시물의 상태를 저장합니다.

현재 위의 서비스는 DB 관련 로직까지 포함되어서 작성되었습니다. 위의 서비스를 좀 더 간단하게 하기 위해서

Repository Pattern을 적용해보도록 하겠습니다. 위의 DB 관련 로직을 서비스에서 Repository로 한번 더 넘겨주어 아래와 같이 따로 분리를 해보도록 하겠습니다.

 

[ board.repository.ts ]

import { EntityRepository, Repository } from "typeorm";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { CreateBoardDto } from "./dto/create-board.dto";


@EntityRepository(Board)
export class BoardRepository extends Repository<Board>{
    async createBoard(
        createBoardDto: CreateBoardDto
    ): Promise<Board> {
        const { title, description } = createBoardDto;

        const board = this.create({
            title,
            description,
            status: BoardStatus.PUBLIC,
        })

        await this.save(board);

        return board;
    }

}

 

그러면 이제 서비스 부분은 아래와 같이 간략하게 줄어들 수 있습니다.

[ boards.service.ts ]

async createBoard(
    createBoardDto: CreateBoardDto
): Promise<Board> {
    return this.boardRepository.createBoard(createBoardDto, user);
}

그러면 서비스에서는 Repository에 DB에서 처리하도록 값을 던져주기만 하는 컨트롤러와 같은 역할을 하게 되죠.

 

[ 게시물 생성 테스트 ]

위에서 컨트롤러와, 서비스 그리고 레포지토리와 관련하여 구현을 해보았고,

터미널에서 Nest 프로젝트를 실행시켜 줍시다!

npm run start:dev

참고로 app.module.ts 파일의 import 부분에 지금까지 만들어준 BoardsModule을 추가해주어야 합니다.

AuthModule은 아직 제외하고, BoardsModule을 추가해주어야 합니다!

 

[ app.module.ts ]

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BoardsModule } from './boards/boards.module';
import { AuthModule } from './auth/auth.module';
import { typeORMConfig } from './configs/typeorm.configs';

@Module({
  imports: [
    TypeOrmModule.forRoot(typeORMConfig),
    BoardsModule,
    AuthModule
  ],
})
export class AppModule { }

 

[ boards.module.ts ]

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthModule } from 'src/auth/auth.module';
import { BoardRepository } from './board.repository';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';

@Module({
  imports: [
    TypeOrmModule.forFeature([BoardRepository]),
    AuthModule
  ],
  controllers: [BoardsController],
  providers: [BoardsService]
})
export class BoardsModule { }

 

서버가 켜지고, 정상적으로 컨트롤러와 서비스들이 연결되어지면

위와 같은 모습으로 매핑된 컨트롤러와 어떤 메소드인지 종류까지 나오게 되네요.

맨 마지막 로그로는 port가 3000번으로 서버가 실행되었다는 메시지입니다.

그러면 이제 localhost:3000으로 요청을 날려보도록 하겠습니다.

Postman 또는 Insomnia 등을 사용해서 localhost:3000으로 요청을 날리면 되는데요~

저는 Insomnia를 사용해서 요청을 날려보도록 하겠습니다.

위와 같이 POST 요청을 날리고..

pgAdmin의 Database를 확인해보면 테이블에 값이 잘 저장된 것을 볼 수 있습니다!

 

 

이상으로 CRUD 중에서 Create 하는 부분에 대해서 알아보았습니다.

추가로 DB 연결하는 부분과 코드에 대한 전반적인 설명이 더 필요할 것 같네요..

아무튼 계속해서 추가해 보도록 하겠습니다!

감사합니다.

반응형