오늘은 저번에 이어서 backend 서비스 로직을 완성해 보자
바로 가자~!
개인적인 공부를 위해 작성하는 블로그입니다. 혹시라도 잘못되거나 부족한 부분이 있다면 댓글로 알려주시면 감사하겠습니다.
💁🏻 DTO , 서비스로직, 컨트롤러 설정
nest g res users
nest g res puzzles
nest에서 제공하는 제너레이터를 사용해서 User와 Puzzle에 대한 모듈, 서비스, 컨트롤러를 만들었다.
createPuzzleDto와 createUserDto를 만들어 주었고 유효성 검사를 위해 class-validator와 class-transformer를 다운로드하고 create-puzzle.dto.ts에서 사용해 주었다.
npm i --save class-validator class-transformer
import { Type } from 'class-transformer';
import { IsString, IsIn, IsInt, IsOptional, IsArray, ValidateNested } from 'class-validator';
export class CreatePuzzleDto {
@IsString()
title: string;
@IsString()
@IsIn(['remove', 'move'])
gameType: string;
@IsInt()
limit: number;
@IsOptional()
@IsString()
@IsIn(['Easy', 'Normal', 'Hard', 'Extreme', 'Unrated'])
difficulty?: string;
@IsArray()
@ValidateNested({ each: true})
@Type(() => MatchstickDto)
initialState: MatchstickDto[];
@IsArray()
@ValidateNested({ each: true})
@Type(() => MatchstickDto)
solution: MatchstickDto[];
@IsOptional()
@IsArray()
@IsString({ each: true })
category: string[];
@IsString()
createBy: string;
}
class MatchstickDto {
@IsString()
id: string;
@IsInt()
x: number;
@IsInt()
y: number;
@IsInt()
angle: number;
}
@Injectable()
export class PuzzlesService {
constructor(private readonly prisma: PrismaService) {}
create(createPuzzleDto: CreatePuzzleDto) {
return this.prisma.puzzle.create({ data: {
title: createPuzzleDto.title,
gameType: createPuzzleDto.gameType,
limit: createPuzzleDto.limit,
difficulty: createPuzzleDto.difficulty ?? 'Unrated',
initialState: JSON.stringify(createPuzzleDto.initialState),
solution: JSON.stringify(createPuzzleDto.solution),
category: JSON.stringify(createPuzzleDto.category),
createBy: createPuzzleDto.createBy,
likes: 0,
}})
}
(생략)
그리고 서비스로직에서 이를 사용해 CRUD를 간단하게 구현하였고 테스트를 해보기 위해서 postman으로 post 요청을 보내보았다.
그럼 이렇게 db에 잘 저장되었는데 만약 limit이 Int형이 아니라 "3" 이런 식으로 string 타입으로 온다면 유효하지 않은 데이터가 서버로 들어오게 되고 문제가 발생하게 된다. 이를 해결하기 위해서 좀 전 create-puzzle.dto에서 class-validator와 class-transformer를 통해 설정해 놓은 것을 가지고 main.ts에서 ValidationPipe를 활성화해서 사용해 보도록 하겠다.
👨🏻💻 Validation
next에서는 애플리케이션으로 들어온 데이터에 대한 유효성 검사와 데이터 변환을 위해 여러 개의 내장 파이프(Pipes)를 제공한다.
그 중에 ValidationPipe에 대해서 알아보면 DTO(데이터 전송 객체)에 기반한 데이터 검증을 수행하는 데 사용되고 class-validator 라이브러리를 활용하여 요청 데이터를 검증한다.
import { Module, ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { PrismaModule } from './prisma/prisma.module';
import { UsersModule } from './users/users.module';
import { PuzzlesModule } from './puzzles/puzzles.module';
@Module({
imports : [
PrismaModule,
UsersModule,
PuzzlesModule
]
})
class RootModule {}
async function bootstrap() {
const app = await NestFactory.create(RootModule);
// 전역 ValidationPipe 설정
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // DTO에 정의된 속성만 허용
forbidNonWhitelisted: true, // DTO에 정의되지 않은 속성은 에러 처리
transform: true, // 요청 데이터를 DTO 클래스 인스턴스로 변환
})
)
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
다음과 같이 사용해 주었는데 간단하게 주요 옵션들을 살펴보면
whitelist: true
DTO에 정의된 필드만 요청 데이터에 포함됩니다.
정의되지 않은 필드는 요청에서 자동으로 제거됩니다.
forbidNonWhitelisted: true
DTO에 정의되지 않은 필드가 요청에 포함된 경우 에러를 발생시킨다.
transform: true
요청 데이터를 DTO 인스턴스로 자동 변환
예를 들어 limit이 문자열 "10"로 전달되더라도 숫자 10으로 변환 시켜준다.
이제 다시 limit값을 문자로하여 포스트 요청을 보내보면 에러 메세지와 함께 400 Bad Request로 응답해주는걸 알 수 있다.
만약 문자열도 허용하고 싶다면 create-puzzle.dto.ts에서 다음과 같이 설정해주면 된다.
@IsInt()
@Min(0)
@Type(()=> Number) // 문자열을 숫자로 변환
limit: number;
'개발 > 개발로그' 카테고리의 다른 글
[React] 성냥퍼즐 웹 서비스 만들기 11일차 (Mysql / Prisma / NestJS) (2) | 2024.12.17 |
---|---|
[React] 간단한 성냥퍼즐 웹 서비스 만들기 10일차 (Nest.js) (0) | 2024.12.13 |
[React] 간단한 성냥퍼즐 웹 서비스 만들기 9일차 (정답 검증 구현) (1) | 2024.12.09 |
[React] 간단한 성냥퍼즐 웹 서비스 만들기 8일차 (게임 타입, 횟수 제한 구현) (0) | 2024.12.05 |
[React] 간단한 성냥퍼즐 웹 서비스 만들기 7일차 (Undo, Redo, Remove구현) (0) | 2024.11.25 |