- ํ์ดํ๋ @Injectable () ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฃผ์์ด ๋ฌ๋ฆฐ ํด๋์ค
- data transformation๊ณผ data validation์ ์ํด์ ์ฌ์ฉ
- ํ์ดํ๋ ์ปจํธ๋กค๋ฌ ๊ฒฝ๋ก ์ฒ๋ฆฌ๊ธฐ์ ์ํด ์ฒ๋ฆฌ๋๋ ์ธ์์ ๋ํด ์๋
Nest๋ ๋ฉ์๋๊ฐ ํธ์ถ๋๊ธฐ ์ง์ ์ ํ์ดํ๋ฅผ ์ฝ์
ํ๊ณ ํ์ดํ๋ ๋ฉ์๋๋ก ํฅํ๋ ์ธ์๋ฅผ ์์ ํ๊ณ ์ด์ ๋ํด ์๋ํฉ๋๋ค.
์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ํ๋ ํ์์ผ๋ก ๋ณํ (์ : ๋ฌธ์์ด์์ ์ ์๋ก)
์ซ์๋ฅผ ๋ฐ๊ธธ ์ํ๋๋ฐ ๋ฌธ์์ด ํ์์ผ๋ก ์จ๋ค๋ฉด ํ์ดํ์์ ์๋์ผ๋ก ์ซ์๋ก ๋ฐ๊ฟ์ค๋ค.
String to Integer EX) string '7' => Integer 7
์
๋ ฅ ๋ฐ์ดํฐ๋ฅผ ํ๊ฐํ๊ณ ์ ํจํ ๊ฒฝ์ฐ ๋ณ๊ฒฝ๋์ง ์์ ์ํ๋ก ์ ๋ฌํ๋ฉด ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ๋ฐ์ดํฐ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์ ์์ธ๋ฅผ ๋ฐ์์ํต๋๋ค.
๋ง์ฝ ์ด๋ฆ์ ๊ธธ์ด๊ฐ 10์ ์ดํ์ฌ์ผ ํ๋๋ฐ 10์ ์ด์ ๋๋ฉด ์๋ฌ๋ฅผ ๋ฐ์
ํ์ดํ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ(Binding pipes)์ ์ธ๊ฐ์ง๋ก ๋๋ ์ง์ ์๋ค.
ํธ๋ค๋ฌ ๋ ๋ฒจ์์ @UsePipes() ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ด์ฉํ๋ค.
์ด ํ์ดํ๋ ๋ชจ๋ ํ๋ผ๋ฏธํฐ์ ์ ์ฉ๋๋ค. (title, description)
ํ๋ผ๋ฏธํฐ ๋ ๋ฒจ์ ํ์ดํ ์ด๊ธฐ์ ํน์ ํ ํ๋ผ๋ฏธํฐ์๊ฒ๋ง ์ ์ฉ์ด ๋๋ ํ์ดํ
๊ธ๋ก๋ฒ ํ์ดํ๋ก์ ์ ํ๋ฆฌ์ผ์ด์
๋ ๋ฒจ์ ํ์ด๋ธ
ํด๋ผ์ด์ธํธ์์ ๋ค์ด์ค๋ ๋ชจ๋ ์์ฒญ์ ์ ์ฉ
๊ฐ์ฅ ์๋จ ์์ญ์ธ main.ts์ ์์ฑ
Nest JS ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ๋ง๋ค์ด ๋์ 6๊ฐ์ง์ ํ์ดํ๊ฐ ์๋ค.
class-validator , class-transformer
npm install class-validator class-transformer --save
-์ฐธ์กฐ โฌโฌ
Documentation ํ์ด์ง
ํ์ฌ๋ ๊ฒ์๋ฌผ์ ์์ฑํ ๋ ์ด๋ฆ๊ณผ ์ค๋ช
์ ์๋ฌด๋ฐ ๊ฐ์ ์ฃผ์ง ์์๋ ์๋ฌด ๋ฌธ์ ์์ด ์์ฑ์ด ๋๋ค.=> ํ์ดํ๋ก ์์
<create-board.dto.ts>
import { IsNotEmpty } from "class-validator";
export class createBoardDto{
@IsNotEmpty() // null ๋ถํ
title: string;
@IsNotEmpty()
description: string;
}
<controller.ts>
@Post()
@UsePipes(ValidationPipe) //์ ํจ์ฑ ์ฒดํฌ
createBoard(
@Body() createBoardDto: createBoardDto
): Board { //return ๊ฐ์ ํ์
์ Board, Board[]๋ก ์ฃผ๋ฉด ์๋จ : service์ createBoard์ return ๊ฐ์ด board ํ๋์ด๊ธฐ ๋๋ฌธ์
return this.boardsService.createBoard(createBoardDto);
}
<๊ฒฐ๊ณผ>
์๋ฌ๊ฐ ๋์จ๋ค!
ํ์ฌ ํน์ ๊ฒ์๋ฌผ์ ID๋ก ๊ฐ์ ธ์ฌ ๋ ๋ง์ฝ ์๋ ์์ด๋์ ๊ฒ์๋ฌผ์ ๊ฐ์ ธ์ค๋ ค๊ณ ํ๋ค๋ฉด ๊ฒฐ๊ณผ๊ฐ์ผ๋ก ์๋ฌด ๋ด์ฉ์ด ์์ด ๋์์จ๋ค.
=> ๊ฐ์ ธ์ค๋ ค๋ ๋ด์ฉ์ด ์๋ค๋ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ด์ค๋ค.
<service.ts> ๊ธฐ์กด์ getBoardId๋ฅผ ์์
getBoardId(id: string): Board{ //๊ฒ์๋ฌผ ํ๋๋ฅผ return ํ๊ธฐ ๋๋ฌธ์ Board[] X
const found = this.boards.find((board)=>board.id === id);
if(!found){
throw new NotFoundException(`Can't find Board with id ${id}`);
}
return found;
}
<๊ฒฐ๊ณผ>
<service.ts>์ deleteBoard๋ฅผ ์์
deleteBoard(id: string): void{ // return ์ ๋ฐ๋ก ์ฃผ์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ void
const found = this.getBoardId(id);
this.boards = this.boards.filter((board) => board.id !==found.id);
}
์ง๊ธ๊น์ง๋ NestJS์์ ์ด๋ฏธ ๊ตฌ์ฑํด๋์ built-in ํ์ดํ๋ฅผ ์ฌ์ฉํด๋ณด์๋ค.
๋ฐ๋ก ์์ฑํด์ ์ฌ์ฉํ ์ ์๋ CUSTOM PIPE๋ฅผ ๋ง๋ค์ด๋ณด์!
**PipeTransform์ด๋ ์ธํฐํ์ด์ค**๋ฅผ ์๋กญ๊ฒ ๋ง๋ค ์ปค์คํ
ํ์ดํ์ ๊ตฌํํด์ค์ผ ํ๋ค.
์ด PipeTransform ์ธํฐํ์ด์ค๋ ๋ชจ๋ ํ์ดํ์์ ๊ตฌํํด์ค์ผ ํ๋ ์ธํฐํ์ด์ค์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๊ฒ๊ณผ ํจ๊ป ๋ชจ๋ ํ์ดํ๋ **transform()** ๋ฉ์๋๊ฐ ํ์ํ๋ค. ์ด ๋ฉ์๋๋ NestJS๊ฐ ์ธ์(arguments)๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด์ ์ฌ์ฉ๋๋ค.
<board-status-validation.pipe.ts>
import { ArgumentMetadata, PipeTransform } from "@nestjs/common";
export class BoardStatusValidationPipe implements PipeTransform{
transform(value: any, metadata: ArgumentMetadata) {
console.log('value',value)
console.log('metadata',metadata)
return value;
}
}
๋๊ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง๋ ๋ฉ์๋
์ฒซ๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ ์ฒ๋ฆฌ๊ฐ ๋ ์ธ์์ ๊ฐ(value)์ด๋ฉฐ,
๋๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ ์ธ์์ ๋ํ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ํฌํจํ ๊ฐ์ฒด์ด๋ค.
transform()๋ฉ์๋์์ Return ๋ ๊ฐ์ Route ํธ๋ค๋ฌ๋ก ์ ํด์ง๋ค.
๋ง์ฝ ์์ธ(Exception)๊ฐ ๋ฐ์ํ๋ฉด ํด๋ผ์ด์ธํธ์ ๋ฐ๋ก ์ ํด์ง๋ค.
<controller.ts>์ :id/status ๋ถ๋ถ ์์
status ๋ฅผ ๋ฐ๊ฟ ๋ ์ ํจ์ฑ ์ฒดํฌ(status ๊ฐ๋ง!)
@Patch('/:id/status')
updateBoardStatus(
@Param('id') id: string,
@Body('status', BoardStatusValidationPipe) status: BoardStatus,
): Board {
return this.boardsService.updateBoardStatus(id, status);
}
<postman ํธ์ถ ์>
<terminal ์ฝ์>
๊ตฌํ ํ ๊ธฐ๋ฅ : ์ํ(Status)๋ PUBLIC๊ณผ PRIVATE๋ง ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ ์ด์ธ์ ๊ฐ์ด ์ค๋ฉด ์๋ฌ ์ ์ก
<board-status-validation.pipe.ts>
import { ArgumentMetadata, BadRequestException, PipeTransform } from "@nestjs/common";
import { BoardStatus } from "../board.model";
export class BoardStatusValidationPipe implements PipeTransform{
//readonly : ํด๋์ค ์ธ๋ถ์์ ์ ๊ทผ์ ๊ฐ๋ฅํ์ง๋ง ๋ณ๊ฒฝ์ ๋ถ๊ฐ!
readonly StatusOptions =[
BoardStatus.PRIVATE,
BoardStatus.PUBLIC
]
transform(value: any) { // value : ์ฌ์ฉ์๊ฐ ์
๋ ฅํ status
value = value.toUpperCase(); //๋๋ฌธ์ ๋ณํ
if(!this.isStatusValid(value)){
throw new BadRequestException(`${value} isn'i in the status`)
}
return value;
}
private isStatusValid(status: any){
const index = this.StatusOptions.indexOf(status); //๋ฐฐ์ด์์ ์๋์ง, ์์ผ๋ฉด ๊ทธ ๋ฐฐ์ด๋ด ์ธ๋ฑ์ค๊ฐ
return index !== -1;
}
}
<๊ฒฐ๊ณผ1>
<๊ฒฐ๊ณผ2>
์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐ๊ฒฐํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณด๊ด, ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ Postgres๋ฅผ ์ฌ์ฉ
<์ค์น ๋ชฉ๋ก>
1. PostgresSQL
2. pgAdmin (๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ณด๋ ํด(Tool)์
๋๋ค.)
pgAdmin ์คํ
1. Servers > register Server
2. BoardProject ์ฐํด๋ฆญ > Create > database
TypeORM์ node.js์์ ์คํ๋๊ณ TypeScript๋ก ์์ฑ๋ ๊ฐ์ฒด ๊ด๊ณํ ๋งคํผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
TypeORM์ MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana ๋ฐ WebSQL๊ณผ ๊ฐ์ ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ง์ํ๋ค.
๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๋ณํ ๋ฐ ์ฐ๊ฒฐํ๋ ์์
ORM์ ์ด์ฉํ ๊ฐ๋ฐ์ ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณํ์ ์ ์ฐํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
const boards = Board.find({ title: 'Hello' , status: 'PUBLIC' });
db.query('SELECT * FROM boards WHERE title = "Hello" AND status = "PUBLIC" , (err, result) => {
if(err) {
throw new Error('Error')
}
boards = result.rows;
})
@nestjs/typeorm
typeorm
pg
npm install pg typeorm @nestjs/typeorm --save
<typeorm.config.ts>
import { TypeOrmModuleOptions } from "@nestjs/typeorm";
export const typeORMConfig : TypeOrmModuleOptions = {
//Database Type
type: 'postgres',
host: 'localhost',
port: 5432,
username:'postgres',
password: '0121',
database: 'board-app',
//์ํฐํฐ๋ ํ๋์ฉ ๋ฃ์ ์๋ ์์ง๋ง ์ด๋ ๊ฒ ๋ค ํฌํจํ๊ฒ ํ ์ ์๋ค.
entities: [__dirname+'/../**/*.entity.{js,ts}'],
synchronize: true
}
<app.module.ts> -typeOrm ์ฌ์ฉ ์ค์
import { Module } from '@nestjs/common';
import { BoardsModule } from './boards/boards.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeORMConfig } from './configs/typeorm.config';
@Module({
imports: [
TypeOrmModule.forRoot(typeORMConfig),
BoardsModule],
})
export class AppModule {}
์๋ ORM ์์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ์์ฑํ ๋๋ ์๋์ ๊ฐ๋ค.
CREATE TABLE board (
id INTEGER AUTO_INCREMENT PRIMARY KEY.
title VARCHAR(255) NOT NULL,
decsription VARCHAR(255) NOT NULL
)
ํ์ง๋ง TypeORM์ ์ฌ์ฉํ ๋๋ Class๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ๋ก ๋ณํ์์ผ์ฃผ๊ธฐ ๋๋ฌธ์ ํด๋์ค๋ฅผ ์์ฑํ ํ ๊ทธ ์์ ์ปฌ๋ผ๋ค์ ์ ์ํด์ฃผ๋ฉด ๋๋ค.
<board.entity.ts>
Entity () ๋ฐ์ฝ๋ ์ดํฐ ํด๋์ค๋ Board ํด๋์ค๊ฐ ์ํฐํฐ์์ ๋ํ๋ด๋ ๋ฐ ์ฌ์ฉ (sql ๋ฌธ์ CREATE TABLE board ๋ถ๋ถ)
PrimaryGeneratedColumn () ๋ฐ์ฝ๋ ์ดํฐ ํด๋์ค๋ id ์ด์ด Board ์ํฐํฐ์ ๊ธฐ๋ณธ ํค ์ด์์ ๋ํ๋ด๋ ๋ฐ ์ฌ์ฉ
Column () ๋ฐ์ฝ๋ ์ดํฐ ํด๋์ค๋ Board ์ํฐํฐ์ title ๋ฐ description๊ณผ ๊ฐ์ ๋ค๋ฅธ ์ด์ ๋ํ๋ด๋ ๋ฐ ์ฌ์ฉ
๋ฆฌํฌ์งํ ๋ฆฌ๋ ์ํฐํฐ ๊ฐ์ฒด์ ํจ๊ป ์๋ํ๋ฉฐ ์ํฐํฐ ์ฐพ๊ธฐ, ์ฝ์
, ์
๋ฐ์ดํธ, ์ญ์ ๋ฑ์ ์ฒ๋ฆฌ
Repository๋?
๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ด๋ จ ๋ ์ผ(INSERT, FIND, DELETE..๋ฑ๋ฑ)์ ์๋น์ค์์ ํ๋๊ฒ ์๋ Repository์์ ํด์ฃผ๋ฉด ๋๋ค.
์ด๊ฒ์ Repository Pattern ์ด๋ผ๊ณ ๋ ํ๋ค.
๋ฆฌํฌ์งํ ๋ฆฌ ํ์ผ ์์ฑํ๊ธฐ
์์ฑํ ํ์ผ์ ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ์ํ ํด๋์ค ์์ฑํ๊ธฐ (BoardRepository)
<board.repository.ts>
import { EntityRepository, Repository } from "typeorm";
import { Board } from "./board.entity";
@EntityRepository(Board)
export class BoardRepository extends Repository<Board>{
}
@EntityRepository()
import { Module } from '@nestjs/common';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BoardRepository } from './board.repository';
@Module({
imports:[TypeOrmModule.forFeature([BoardRepository])],
controllers: [BoardsController],
providers: [BoardsService]
})
export class BoardsModule {}