이번 글에서는 타입스크립트 백엔드 프레임워크인 Nest.js에서 POST나 PUT과 데이터 요청이 들어왔을때 그 데이터가 적절한 데이터인지 확인하는 validation과
GET을 했을때 서버 데이터에서 가지고 있는 raw데이터 형식이 아니라 중간에서 Interceptor하여 다른 형태의 데이터로 변환 시켜 줄 수 있는 transform을 한번 해보려고 한다.
먼저 테스트 하기 위한 데이터를 ts파일에서 넣는다.
export type ReportType = 'income' | 'expense'; export interface Reports { reports: { id: string; source: string; amount: number; created_at: Date; updated_at: Date; type: ReportType; }[]; } export const data: Reports = { reports: [ { id: 'uuid', source: 'Salary', amount: 7500, created_at: new Date(), updated_at: new Date(), type: 'income', }, { id: 'uuid232', source: 'Salary', amount: 7500, created_at: new Date(), updated_at: new Date(), type: 'income', }, { id: 'uuid2', source: 'Youtube', amount: 300, created_at: new Date(), updated_at: new Date(), type: 'expense', }, { id: 'uuid3', source: 'Facebook', amount: 7500, created_at: new Date(), updated_at: new Date(), type: 'expense', }, ], };
데이터 구조는 아래와 같이 이루어져있고, POST를 통해 데이터를 삽입하는 것으로 예시를 들어보겠다.
@Post() createReport(@Param('type') pType: ReportType, @Body() body: Report) { return this.appService.createReport(pType, body); }
createReport(pType: ReportType, body: Report) { if (!isDataType(pType)) { return { message: '잘못된 타입' }; } const newReport: Reports['reports'][number] = { id: uuid(), created_at: new Date(), updated_at: new Date(), type: pType, ...body, }; data.reports.push(newReport); return data; }
위와 같은 형식으로 컨트롤러를 처리하면 아직 validation이 안되는 상태이기때문에 잘못된 데이터가 들어와도 에러가 발생하지 않고 컨트롤러 진입하게된다.

{ "reports": [ { "id": "uuid", "source": "Salary", "amount": 7500, "created_at": "2022-05-22T02:07:35.647Z", "updated_at": "2022-05-22T02:07:35.647Z", "type": "income" }, { "id": "uuid232", "source": "Salary", "amount": 7500, "created_at": "2022-05-22T02:07:35.647Z", "updated_at": "2022-05-22T02:07:35.647Z", "type": "income" }, { "id": "uuid2", "source": "Youtube", "amount": 300, "created_at": "2022-05-22T02:07:35.647Z", "updated_at": "2022-05-22T02:07:35.647Z", "type": "expense" }, { "id": "uuid3", "source": "Facebook", "amount": 7500, "created_at": "2022-05-22T02:07:35.647Z", "updated_at": "2022-05-22T02:07:35.647Z", "type": "expense" }, { "id": "d283f562-7598-41ab-8321-4a3ce28c0b7b", "created_at": "2022-05-22T02:07:40.157Z", "updated_at": "2022-05-22T02:07:40.157Z", "type": "income", "amount": -1, "source": "" } ] }
만약 오로지 양수인 amount와 반드시 입력된 source가 있는 데이터를 받고 그 외 데이터가 들어오면 오류를 발생시키려면 어떻게 해야할까?
src → dto → report.dto 생성 후 아래와 같이 입력
import { IsNotEmpty, IsNumber, IsPositive, IsString, } from 'class-validator'; export class CreateReportDto { @IsNumber() @IsPositive() amount: number; @IsString() @IsNotEmpty() source: string; }
IsNumber → 오직 숫자만
IsPositive → 오직 양수만
IsString → 오직 문자만
IsNotEmpty → 무조건 값이 필요
body타입에 해당 Dto 인스턴스 타입을 넣어준다
@Post() createReport( @Param('type') pType: ReportType, @Body() body: CreateReportDto, ) { return this.appService.createReport(pType, body); }

요청이 들어오고 응답을 Interceptor하여 변형된 응답값을 리턴해주고 싶을땐 어떻게 해야할까?
@Get(':id') getReportById(@Param('type') pType: ReportType, @Param('id') pId: string) { return this.appService.getReportById(pType, pId); }
getReportById(pType: ReportType, pId: string): ReportResponseDto { const result = data.reports .filter(({ type }) => type === pType) .find(({ id }) => id === pId); return result; }

만약 위 처럼 서버에 저장되어있는 데이터 형태 그대로 말고 좀 다르게 변형된 형태로 return 해주고 싶으면 어떻게 해야할까?
예를들어 updated_at이라는 데이터는 응답이 안내려가고 created_at이라는 키는 camel case로 createdAt으로 내려주고 싶다면 말이다.
이것또한 Dto를 사용하여 만들어 줄 수 있다.
export class ReportResponseDto { id: string; source: string; amount: number; @Expose({ name: 'createdAt' }) created_at: Date; @Exclude() updated_at: Date; type: ReportType; constructor(partial: Partial<ReportResponseDto>) { Object.assign(this, partial); } }
getReportById(pType: ReportType, pId: string): ReportResponseDto { const result = data.reports .filter(({ type }) => type === pType) .find(({ id }) => id === pId); return new ReportResponseDto(result); }
import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes( new ValidationPipe({ transform: true, transformOptions: { enableImplicitConversion: true, }, }), ); await app.listen(3000); } bootstrap();
import { ClassSerializerInterceptor, Module } from '@nestjs/common'; import { APP_INTERCEPTOR } from '@nestjs/core'; import { AppController } from './app.controller'; import { AppService } from './app.service'; @Module({ imports: [], controllers: [AppController], providers: [ AppService, { provide: APP_INTERCEPTOR, useClass: ClassSerializerInterceptor, }, ], }) export class AppModule {}
이상 Dto의 개념과 dto를 사용하여 validation과 transform 작업을 하는 방법을 알아보았다.