검증절차
이전에 만들었던 priduct의 가격 부분에서 가격이 -이거나 소수점이면 안되기 떄문에 이런것들을 검증해야한다.
nest에서 추가로 패키지 설치가 필요한데
yarn add class-validator class-transformer 로 설치한다.
create product dto 에서
가격의 최소값이 0 Min(0)<import 필요> 으로 가격을 마이너스로 만들수 없게 만들었다.
import { Field, InputType, Int } from '@nestjs/graphql';
import { Min } from 'class-validator';
@InputType()
export class CreateProductInput {
// @IsString()
@Field(() => String)
readonly name: string;
// @IsString()
@Field(() => String)
readonly description: string;
// @IsString()
@Min(0)
@Field(() => Int)
readonly price: number;
}
main 수정
main 에서 파이프를 연결 시켜줌
main 에 express 처럼 따로 미들웨어가 더 없는데 어떻게 pipe 가 resolve 보다 먼저 동작하는가는
https://docs.nestjs.com/faq/request-lifecycle
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea
docs.nestjs.com
여기를 보면 나와있다.
라이프 사이클상 요청은 무조건 글로벌파이프를 먼저 거쳐서 product resolve에 도달하게 됨
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe()); // 이걸 연결해줘야 벨리데이션이 작동함
// 리퀘스트 요청을 보낼때 바로 가지 않고 pipe 를 통과한 내용만 아래쪽으로 가게됨
await app.listen(3000);
}
bootstrap();
이제 priduct 를 수정하는 api 를 만들기 시작
먼저 resolve부터
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { ProductsService } from './products.service';
import { CreateProductInput } from './dto/create-product.input';
import { Product } from './entities/product.entity';
import { UpdateProductInput } from './dto/update-product.input';
@Resolver()
export class ProductsResolver {
constructor(private readonly productsService: ProductsService) {}
@Query(() => [Product])
fetchProducts(): Promise<Product[]> {
return this.productsService.findAll();
}
@Query(() => Product)
fetchProduct(@Args('productId') productId: string): Promise<Product> {
return this.productsService.findOne({ productId });
}
@Mutation(() => Product)
createProduct(
@Args('createProductInput') createProductInput: CreateProductInput,
): Promise<Product> {
// <브라우저에 결과 보내주는 2가지 방법>
// 1. 등록된 내용이 담긴 객체를 그대로 브라우저에 돌려보내주기
return this.productsService.create({ createProductInput });
// 2. 결과메세지만 간단히 보내주기
// return '정상적으로 등록되었습니다.'
}
updateProduct(
@Args('productId') productId: string,
@Args('updateProductInput') updateProductInput: UpdateProductInput,
) {
this.productsService.update();
}
}
service
import { UpdateProductInput } from './dto/update-product.input';
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { Product } from './entities/product.entity';
import { InjectRepository } from '@nestjs/typeorm';
import {
IProductsServiceCreate,
IProductsServiceFindOne,
} from './interfaces/products-service.interface';
@Injectable()
export class ProductsService {
constructor(
@InjectRepository(Product)
private readonly productsRepository: Repository<Product>, //
) {}
findAll(): Promise<Product[]> {
return this.productsRepository.find();
}
findOne({ productId }: IProductsServiceFindOne): Promise<Product> {
return this.productsRepository.findOne({ where: { id: productId } });
}
// async await 를 안달아도 nest 에서는 resolver 에서 알아서 await가 적용되어진다.
// express 는 async await 달아줘야함
// Promise 는 외부에 요청을 해야되서 시간이 걸리는 로직때 사용
create({ createProductInput }: IProductsServiceCreate): Promise<Product> {
const result = this.productsRepository.save({
...createProductInput,
// 하나하나 직접 나열하는 방식식
// name: '마우스',
// description: '좋은 마우스',
// price: 10000,
//resilt 안에는 무엇이 있는가?
// result = {
// id : uuid
// name : '마우스'
// description : '좋은 마우스'
// price : 10000
// }
});
return result;
}
//검증을 어디서 해야하는가?
// 서비스에서 해도되고 리졸브해서 해도 되지않는가?
// 해도됨 그렇지만 재사용성 때문에 서비스에서 함
async update({
productId,
updateProductInput,
}: IProductsServiceUpdate): Promise<Product> {
//this.findOne({ productId });
const product = await this.productsRepository.findOne({
where: { id: productId },
});
// 에러 : 아에 실행이 안되는것
// 버그 : 작동의 되는데 의도하지 않고 다르게 작동
// 예외 : 데이터를 수정하려고하는데 db가 꺼져있다 같이 예외적인 내용
//좀더 간소화
if (product.isSoldOut) {
throw new UnprocessableEntityException('이미 판매 완료된 상품입니다.');
}
//같은에러 메세지 표시
// if (product.isSoldOut) {
// throw new HttpException(
// '이미 판매 완료된 상품입니다.',
// HttpStatus.UNPROCESSABLE_ENTITY,
// );
// }
//
// this.productsRepository.create //DB 접속과 관련이 없음 등록을 위한 빈 객체를 만드는 것
// this.productsRepository.insert // 결과를 객체로 못 돌려받는 등록방법
// this.productsRepository.update // 결과를 객체로 못 돌려받는 수정방법
// this.productsRepository.save //save 는 안에 id 가 없으면 등록 , 있으면 수정으로 작동
// 등록된 결과를 const resilt 에 저장해서 브라우저에 보내줄 수있음 수정된 내용을 다시 조회하지않아도됨
//
const result = this.productsRepository.save({
...product, // 스프레드 / 수정 후 수정되지 않은 다른 결과값까지 모두 객체로 돌려받고 싶을때
...updateProductInput, //
});
return result;
}
}
interface IProductsServiceUpdate {
productId: string;
updateProductInput: UpdateProductInput;
}
dto
create dto 를 상속받아서 필수적으로 입력하지 않아도 되는 정보로 만들어야 함
graohql 에서 제공하는 partilType 을 사용
주석 처리된 내용들을 상속받음
import { InputType, PartialType } from '@nestjs/graphql';
import { CreateProductInput } from './create-product.input';
@InputType()
export class UpdateProductInput extends PartialType(CreateProductInput) {
//아래 내용들을 상속 받음
// name?: string;
// description?: string;
// price?: number;
}
그런데 service 부분에서 검증을 할때 재사용 되는 부분이 생김
그럴경우 모든 곳에 에러코드를 적어주는게 아니라 함수로 만들어서 그 함수를 불러오는식으로 해야함
마찬가지로
import { UpdateProductInput } from './dto/update-product.input';
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { Product } from './entities/product.entity';
import { InjectRepository } from '@nestjs/typeorm';
import {
IProductsServiceCreate,
IProductsServiceFindOne,
IProductsServiceUpdate,
IproductsServiceCheckSoldOut,
} from './interfaces/products-service.interface';
@Injectable()
export class ProductsService {
constructor(
@InjectRepository(Product)
private readonly productsRepository: Repository<Product>, //
) {}
findAll(): Promise<Product[]> {
return this.productsRepository.find();
}
findOne({ productId }: IProductsServiceFindOne): Promise<Product> {
return this.productsRepository.findOne({ where: { id: productId } });
}
// async await 를 안달아도 nest 에서는 resolver 에서 알아서 await가 적용되어진다.
// express 는 async await 달아줘야함
// Promise 는 외부에 요청을 해야되서 시간이 걸리는 로직때 사용
create({ createProductInput }: IProductsServiceCreate): Promise<Product> {
const result = this.productsRepository.save({
...createProductInput,
// 하나하나 직접 나열하는 방식식
// name: '마우스',
// description: '좋은 마우스',
// price: 10000,
//resilt 안에는 무엇이 있는가?
// result = {
// id : uuid
// name : '마우스'
// description : '좋은 마우스'
// price : 10000
// }
});
return result;
}
async update({
productId,
updateProductInput,
}: IProductsServiceUpdate): Promise<Product> {
// 기존에 있는 내용을 재사용해서 , 로직을 통일하자
const product = await this.findOne({ productId });
//검증은 서비스에서 한다
this.cheakSoldOut({ product });
//
// this.productsRepository.create //DB 접속과 관련이 없음 등록을 위한 빈 객체를 만드는 것
// this.productsRepository.insert // 결과를 객체로 못 돌려받는 등록방법
// this.productsRepository.update // 결과를 객체로 못 돌려받는 수정방법
// this.productsRepository.save //save 는 안에 id 가 없으면 등록 , 있으면 수정으로 작동
// 등록된 결과를 const resilt 에 저장해서 브라우저에 보내줄 수있음 수정된 내용을 다시 조회하지않아도됨
//
try {
const result = this.productsRepository.save({
...product, // 스프레드 / 수정 후 수정되지 않은 다른 결과값까지 모두 객체로 돌려받고 싶을때
...updateProductInput, //
});
} catch (error) {
console.log(error);
}
return result;
}
// 에러 : 아에 실행이 안되는것
// 버그 : 작동의 되는데 의도하지 않고 다르게 작동
// 예외 : 데이터를 수정하려고하는데 db가 꺼져있다 같이 예외적인 내용
// cheakSoldOut 을 함수로 만드는 이유 => 수정 삭제 등 같은 검증 로직 사용
cheakSoldOut({ product }: IproductsServiceCheckSoldOut): void {
//좀더 간소화
if (product.isSoldOut) {
throw new UnprocessableEntityException('이미 판매 완료된 상품입니다.');
}
//같은에러 메세지 표시
// if (product.isSoldOut) {
// throw new HttpException(
// '이미 판매 완료된 상품입니다.',
// HttpStatus.UNPROCESSABLE_ENTITY,
// );
// }
}
}
그리고 예상치 못한 에러가 생겼을 경우 에러를 표시하기 위해 try catch 문을 사용하게 되는데 그렇다면
api를 만들때마다 전부 사용하게 됨 nest js 에는 이것을 하나로 통합할 수 있음 exception-filter
common / filter / http-excoption.filter.ts
import { Catch, HttpException } from '@nestjs/common';
@Catch(HttpException)
class HttpExceptionFilter implements HttpExceptionFilter {
catch(exception: HttpException) {
const status = exception.getStatus();
const message = exception.message;
console.log('=======================');
console.log('예외가 발생했습니다.');
console.log('예외내용 : ', message);
console.log('예외코드 : ', status);
console.log('=======================');
}
}
main.ts
글로벌 필터로 적용시켜줘야함
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import { HttpExceptionFilter } from './common/filter/http-excoption.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe()); // 이걸 연결해줘야 벨리데이션이 작동함
app.useGlobalFilters(new HttpExceptionFilter()); // 이걸 연결해줘야 익셉션 필터 작동
// 리퀘스트 요청을 보낼때 바로 가지 않고 pipe 를 통과한 내용만 아래쪽으로 가게됨
await app.listen(3000);
}
bootstrap();
'NEST.js' 카테고리의 다른 글
| NEST 소프트 딜리트 (0) | 2023.10.01 |
|---|---|
| NEST API 만들기 (0) | 2023.09.30 |
| NEST Typescript 심화 타입 ( Utility, Generic) (0) | 2023.09.29 |
| NEST GraphQL (0) | 2023.09.29 |
| NEST API구조 (0) | 2023.09.29 |