Nodejs

1. Order Proto 정의 (order.proto)

 

주문과 관련된 gRPC 서비스 인터페이스를 정의합니다.

syntax = "proto3";

package order;

service OrderService {
    rpc DeliveryStarted(DeliveryStartedRequest) returns (DeliveryStartedResponse);
    rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
}

message DeliveryStartedRequest {
    string id = 1;
}

message DeliveryStartedResponse {}

message CreateOrderRequest {
    message Meta {
        message UserPayload {
            string sub = 1;
        }
        UserPayload user = 1;
    }

    message Address {
        string name = 1;
        string street = 2;
        string city = 3;
        string postalCode = 4;
        string country = 5;
    }

    message Payment {
        string paymentMethod = 1;
        string paymentName = 2;
        string cardNumber = 3;
        string expiryYear = 4;
        string expiryMonth = 5;
        string birthOrRegistration = 6;
        string passwordTwoDigits = 7;
        double amount = 8;
    }

    Meta meta = 1;
    repeated string productIds = 2;
    Address address = 3;
    Payment payment = 4;
}

message CreateOrderResponse {
    message Customer {
        string userId = 1;
        string email = 2;
        string name = 3;
    }

    message Product {
        string productId = 1;
        string name = 2;
        double price = 3;
    }

    message DeliveryAddress {
        string name = 1;
        string street = 2;
        string city = 3;
        string postalCode = 4;
        string country = 5;
    }

    message Payment {
        string paymentId = 1;
        string paymentMethod = 2;
        string paymentName = 3;
        double amount = 4;
    }

    Customer customer = 1;
    repeated Product products = 2;
    DeliveryAddress deliveryAddress = 3;
    string status = 4;
    Payment payment = 5;
}

✅ 설명

  • DeliveryStarted: 배송 시작 이벤트를 처리

  • CreateOrder: 주문 생성 요청을 처리

  • Request/Response 메시지: 사용자 정보, 상품 목록, 배송지, 결제 정보 포함

 

 

2. OrderController 구현

NestJS에서 gRPC와 이벤트 패턴을 이용하여 주문 서비스의 엔드포인트를 작성합니다.

import { Controller, UseInterceptors } from '@nestjs/common';
import { OrderService } from './order.service';
import { CreateOrderDto } from './dto/create-order.dto';
import { EventPattern, MessagePattern, Payload } from '@nestjs/microservices';
import { RpcInterceptor } from '@app/common';
import { DeliveryStartedDto } from './dto/delivery-started.dto';
import { OrderStatus } from './entity/order.entity';

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

  @EventPattern({ cmd: 'delivery_started' })
  @UseInterceptors(RpcInterceptor)
  async deliveryStarted(@Payload() payload: DeliveryStartedDto) {
    console.log('deliveryStartedDto', payload);

    this.orderService.changeOrderStatus(
      payload.id,
      OrderStatus.deliveryStarted,
    );
  }

  @MessagePattern({ cmd: 'create_order' })
  async createOrder(@Payload() createOrderDto: CreateOrderDto) {
    console.log('???? OrderController createOrder', createOrderDto);
    return this.orderService.createOrder(createOrderDto);
  }
}

✅ 설명

  • @EventPattern은 비동기 이벤트를 수신 (배송 시작 등)

  • @MessagePattern은 RPC 방식으로 주문 생성 요청을 처리

  • RpcInterceptor를 사용하여 응답 형식 및 에러 처리 표준화

 

 

3. Order 엔티티 정의

Mongoose를 사용하여 주문(Order) 스키마를 정의합니다.

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Customer, CustomerSchema } from './customer.entity';
import { Product, ProductSchema } from './product.entity';
import { DeliveryAddress, DeliveryAddressSchema } from './delivery-address.entity';
import { Payment, PaymentSchema } from './payment.entity';
import { Document, ObjectId } from 'mongoose';

export enum OrderStatus {
  pending = 'Pending',
  paymentCancelled = 'PaymentCancelled',
  paymentFailed = 'PaymentFailed',
  paymentProcessed = 'PaymentProcessed',
  deliveryStarted = 'DeliveryStarted',
  deliveryDone = 'DeliveryDone',
}

@Schema()
export class Order extends Document<ObjectId> {
  @Prop({ type: CustomerSchema, required: true })
  customer: Customer;

  @Prop({ type: [ProductSchema], required: true })
  products: Product[];

  @Prop({ type: DeliveryAddressSchema, required: true })
  deliveryAddress: DeliveryAddress;

  @Prop({ enum: OrderStatus, default: OrderStatus.pending })
  status: OrderStatus;

  @Prop({ type: PaymentSchema, required: true })
  payment: Payment;
}

export const OrderSchema = SchemaFactory.createForClass(Order);

✅ 설명

  • OrderStatus enum으로 주문 상태를 관리

  • customer, products, deliveryAddress, payment를 하위 스키마로 포함

  • 주문의 라이프사이클: 생성 → 결제 확인 → 배송 시작 → 배송 완료

 

 

4. DTO 정의

CreateOrderDto

import { Type } from 'class-transformer';
import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
import { AddressDto } from './address.dto';
import { PaymentDto } from './payment.dto';
import { UserMeta, UserPayloadDto } from '@app/common';

export class CreateOrderDto implements UserMeta {
  @ValidateNested()
  @IsNotEmpty()
  meta: { user: UserPayloadDto };

  @IsString()
  @IsNotEmpty()
  token: string;

  @IsArray()
  @IsString({ each: true })
  @IsNotEmpty({ each: true })
  productIds: string[];

  @ValidateNested()
  @Type(() => AddressDto)
  @IsNotEmpty()
  address: AddressDto;

  @ValidateNested()
  @Type(() => PaymentDto)
  @IsNotEmpty()
  payment: PaymentDto;
}

UserPayloadDto

import { IsNotEmpty, IsString } from 'class-validator';

export class UserPayloadDto {
  @IsString()
  @IsNotEmpty()
  sub: string;
}

 

 

5. 동작 흐름

  1. 클라이언트가 주문 생성 요청을 보냄 → create_order RPC 호출

  2. OrderController에서 CreateOrderDto 검증 후 OrderService.createOrder 호출

  3. 주문 생성 → 결제 처리 → 알림(Notification) 서비스 호출

  4. 배송 단계에 도달하면 delivery_started 이벤트 수신 후 상태 변경

 

 

 

about author

PHRASE

Level 60  라이트

뺨맞는 데 구레나룻이 한 부조 , [뺨을 맞아도 구레나룻 때문에 덜 아프다는 뜻으로] 아무 소용이 없는 듯한 것도 때에 따라 쓰일 때가 있다는 말.

댓글 ( 0)

댓글 남기기

작성