E-commerce Application(Nest js Microservice) - 10. order-service와 catalog-service 통신(3)

#1 에러 케이스

이번 포스트에서는 다음의 케이스들을 다뤄보도록 하겠습니다.

1) ERROR_ORDER인 경우 재주문 요청

1)의 경우 사용자의 UI화면이 존재한다면 주문이 ERROR가 됐으므로 사용자에게 이에 관한 메시지를 보낼 것입니다. 그러면 사용자는 주문 취소 혹은 수량 변경 후 재주문을 해야겠죠. 여기서 2가지 케이스가 생기게 되네요. 이 케이스들을 다음과 같이 나누도록 하겠습니다.

1-1) 주문 취소의 경우 /orders/:orderId/cancel로 요청하기
1-2) 재주문의 경우 /orders/:orderId/reorder로 요청하기, 이 경우에는 기존의 reOrder메서드를 수정하도록 하겠습니다.

#2 1) 케이스

1-1)의 경우 기존에 만들어 두었던 cancelOrder로 대체할 수 있고, 이번 포스트에서는 1-2)의 경우만 다루도록 하겠습니다.

1-2) 재주문의 경우 /orders/:orderId/reorder로 요청하기

  • request.update.ts
import { IsNotEmpty, IsNumber, IsString } from "class-validator";

export class RequestUpdate {
    @IsNumber()
    @IsNotEmpty()
    readonly qty: number;
}

재주문할 경우 수량 수정을 위한 변수만 갖게 하도록 하겠습니다.

  • order.controller.ts
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
import { EventPattern } from '@nestjs/microservices';
import { OrderDto } from 'src/dto/order.dto';
import { RequestCreate } from 'src/vo/request.create';
import { RequestUpdate } from 'src/vo/request.update';
import { ResponseOrder } from 'src/vo/response.order';
import { OrderService } from './order.service';

@Controller('orders')
export class OrderController {
    constructor(private readonly orderService: OrderService,) {}
    ...

    @Post(':orderId/reorder')
    public async reOrder(
        @Param('orderId') orderId: string,
        @Body() requestUpdate: RequestUpdate    
    ) {
        const orderDto = new OrderDto();

        orderDto.orderId = orderId;
        orderDto.qty = requestUpdate.qty;

        return await this.orderService.reOrder(orderDto);
    }

    ...

컨트롤러의 reOrder메서드를 다음과 같이 orderDto에 요청받은 정보값들을 담아 서비스단으로 넘기도록 하겠습니다.

  • order.service.ts
import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { InjectRepository } from '@nestjs/typeorm';
import { OrderDto } from 'src/dto/order.dto';
import { OrderEntity } from 'src/entity/order.entity';
import { ResponseOrder } from 'src/vo/response.order';
import { Repository } from 'typeorm';
import { v4 as uuid } from 'uuid';

@Injectable()
export class OrderService {
    constructor(
        @InjectRepository(OrderEntity) private orderRepository: Repository<OrderEntity>,
        @Inject('order-service') private readonly client: ClientProxy    
    ) {}

    ...

    public async reOrder(orderDto: OrderDto): Promise<ResponseOrder> {
        try {
            const orderEntity = await this.orderRepository.findOne({ where: { orderId: orderDto.orderId }});
            const responseOrder = new ResponseOrder();
            
            orderEntity.qty = orderDto.qty;
            orderEntity.totalPrice = (Number(orderEntity.qty) * Number(orderEntity.unitPrice));
            orderEntity.status = 'RE_ORDER';

            this.client.emit("RE_ORDER", orderEntity);
            
            await this.orderRepository.save(orderEntity);

            responseOrder.orderId = orderEntity.orderId;
            responseOrder.status = orderEntity.status;

            return responseOrder;
        } catch(err) {
            throw new HttpException(err, HttpStatus.BAD_REQUEST);
        }
    }

    ...
}

그러면 서비스에서 비즈니스 로직이 다음과 같이 바뀌게 됩니다. 수량에 대한 변화가 생겼으니 orderEntity.qty의 수량을 변화시켜야 하고, orderEntity.totalPrice 또한 변화가 생겨야겠죠. 이렇게 reOrder에 관한 코드를 변경시키도록 하겠습니다.

#3 auth-service(추후 업로드)

이제 유저 정보를 확인하면 order에 관한 정보를 불러 올 수 있게 코드를 작성해보도록 하겠습니다. user-service.vo 디렉토리에 ResponseOrder라는 파일을 만들도록 하겠습니다.

  • response.order.ts
import { IsBoolean, IsNotEmpty, IsNumber, IsString } from "class-validator";

export class ResponseOrder {
    @IsString()
    orderId: string;
    
    @IsString()
    productId: string;
    
    @IsString()
    productName: string;
    
    @IsNumber()
    qty: number;
    
    @IsNumber()
    unitPrice: number;
    
    @IsNumber()
    totalPrice: number;
    
    @IsString()
    userId: string;

    @IsBoolean()
    status: string;
}
  • user.dto.ts
import { IsNumber, IsString } from 'class-validator';
import { ResponseOrder } from 'src/vo/response.order';

export class UserDto {
    ...

    orders: ResponseOrder[];
}
  • response.user.ts
import { ResponseOrder } from "./response.order";

export class ResponseUser {
    email: string;
    nickname: string;
    encryptedPwd: string;
    userId: string;
    orders: ResponseOrder[];
}

그리고 ResponseUser에 ResponseOrder Array를 추가하겠습니다. 이 vo들은 user정보를 반환할 때 필요한 vo클래스입니다.

npm install --save nest-feign nest-consul-loadbalance nest-consul consul
  • nestcloud/feign 연동을 지속적으로 해봤는데 잘 되지 않아 문서를 좀 더 찾아본 후 업로드하도록 하겠습니다.

#4 test

모든 코드가 대략적으로 완성된 것 같습니다. 그러면 시나리오를 다음과 같이 몇 가지를 구성해보고 테스트를 진행하도록 하겠습니다.

1) 회원가입 후 product-001의 제품을 10개 주문하기
2) product-001의 제품을 취소하기
3) 취소한 product-001의 제품을 20개로 바꾸어 재주문
4) product-002의 제품을 300개 주문하기
5) ERROR_ORDER상태 값인 product-002의 주문을 10개로 재주문

본 시나리오를 바탕으로 테스트를 진행해보도록 하겠습니다.

1) 회원가입 후 product-001의 제품을 10개 주문하기




2) product-001의 제품을 취소하기


3) 취소한 product-001의 제품을 20개로 바꾸어 재주문

4) product-002의 제품을 300개 주문하기

5) ERROR_ORDER상태 값인 product-002의 주문을 10개로 재주문

모든 시나리오들이 정상적으로 작동할 수 있는 모습을 볼 수 있습니다. 이로써 실제 서비스들에 대한 모든 코드를 구현해 봤습니다.

다른 시리즈인 Spring Cloud기반의 Microservice는 API Gateway Service를 이용해 하나의 포트로 모든 요청을 받고 각 서비스로 요청을 전달하고, 혹은 Spring Security를 이용해 인터셉터도 구현할 수 있습니다.

아직까지 NestJS가 미숙해서 위의 기능들을 구현할 수 있지만 제가 미처 구현 못한 것일수도 있으므로 위의 기능들과 관련해서 조금 더 공부를 하고 난 뒤 추가적으로 포스트를 한번 작성해보도록 하겠습니다. 감사합니다.

좋은 웹페이지 즐겨찾기