디렉토리 구조
apps/
 └── order/
      └── src/
           └── order/
                ├── dto/
                │    ├── address.dto.ts
                │    ├── create-order.dto.ts
                │    └── payment.dto.ts
                ├── order.controller.ts
                └── order.service.ts
dto 디렉토리에는 요청/응답에서 사용할 데이터 전송 객체들을 모아두었고, order.controller.ts와 order.service.ts는 각각 컨트롤러와 서비스 계층을 담당합니다. 이 구조는 모듈별로 코드를 관리하기 용이하며 확장성 있는 아키텍처를 제공합니다.
1. DTO(Data Transfer Object) 설계
DTO는 클라이언트 → 서버 간에 전달되는 데이터를 구조화하고, 유효성 검증(Validation) 을 보장하는 역할을 합니다. NestJS에서는 class-validator와 class-transformer를 활용하여 간단히 DTO를 정의할 수 있습니다.
(1) 주소 DTO – address.dto.ts
import { IsNotEmpty, IsString } from 'class-validator';
export class AddressDto {
  @IsString()
  @IsNotEmpty()
  name: string;
  @IsString()
  @IsNotEmpty()
  street: string;
  @IsString()
  @IsNotEmpty()
  city: string;
  @IsString()
  @IsNotEmpty()
  postalCode: string;
  @IsString()
  @IsNotEmpty()
  country: string;
}
배송 시 필요한 기본적인 주소 정보를 검증합니다. 모든 필드는 문자열(String) 이어야 하며, 빈 값 허용 불가로 설정했습니다.
(2) 결제 DTO – payment.dto.ts
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
import { PaymentMethod } from '../entity/payment.entity';
export class PaymentDto {
  @IsString()
  @IsNotEmpty()
  paymentMethod: PaymentMethod;
  @IsString()
  @IsNotEmpty()
  paymentName: string;
  @IsString()
  @IsNotEmpty()
  cardNumber: string;
  @IsString()
  @IsNotEmpty()
  expiryYear: string;
  @IsString()
  @IsNotEmpty()
  expiryMonth: string;
  @IsString()
  @IsNotEmpty()
  birthOrRegistration: string;
  @IsString()
  @IsNotEmpty()
  passwordTwoDigits: string;
  @IsNumber()
  @IsNotEmpty()
  amount: number;
}
결제 수단과 카드 정보, 결제 금액을 포함합니다. 특히 paymentMethod는 프로젝트에서 정의한 PaymentMethod 타입을 사용합니다. 실제 운영 환경에서는 카드 번호 암호화나 보안 토큰 처리가 필요하다는 점을 고려해야 합니다.
(3) 주문 생성 DTO – create-order.dto.ts
import { Type } from 'class-transformer';
import { IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
import { AddressDto } from './address.dto';
import { PaymentDto } from './payment.dto';
export class CreateOrderDto {
  @IsArray()
  @IsString({ each: true })
  @IsNotEmpty({ each: true })
  productIds: string[];
  @ValidateNested()
  @Type(() => AddressDto)
  @IsNotEmpty()
  address: AddressDto;
  @ValidateNested()
  @Type(() => PaymentDto)
  @IsNotEmpty()
  payment: PaymentDto;
}
주문 생성 시 필요한 상품 목록, 배송지 정보, 결제 정보를 모두 포함한 DTO입니다.
productIds 배열을 통해 여러 개의 상품을 주문할 수 있습니다.
@ValidateNested()와 @Type()을 활용하여 AddressDto, PaymentDto를 중첩 검증합니다.
이 방식 덕분에 클라이언트에서 잘못된 형태의 JSON을 전달하더라도 서버가 자동으로 검증하고 에러를 반환할 수 있습니다.
2. Controller 설계
Controller는 클라이언트의 요청을 받아 Service로 전달하는 역할을 합니다.
import { Body, Controller, Post } from '@nestjs/common';
import { OrderService } from './order.service';
@Controller('order')
export class OrderController {
  constructor(private readonly orderService: OrderService) {}
  @Post()
  async createOrder(token: string, @Body() body): Promise<any> {
    // return this.orderService.createOrder(body, token);
  }
}
여기서는 /order 엔드포인트를 POST 요청으로 받아 주문 생성을 처리합니다.
실제 서비스에서는 AuthGuard 등을 활용해 JWT 토큰에서 사용자 정보를 추출해야 합니다.
@Body()는 CreateOrderDto 타입으로 지정하는 것이 일반적이며, 현재 코드에서는 단순 body로 되어 있으므로 추후 개선이 필요합니다.
3. Order Service 설계
Service 계층은 실제 비즈니스 로직이 구현되는 곳입니다. createOrder 함수에는 주문 생성의 전체 플로우가 포함됩니다.
import { Injectable } from '@nestjs/common';
import { CreateOrderDto } from './dto/create-order.dto';
@Injectable()
export class OrderService {
  async createOrder(createOrderDto: CreateOrderDto, token: string) {
    /// 1) 사용자 정보 가져오기
    /// 2) 주문 생성하기 (초기 상태: 결제 대기)
    /// 3) 총 금액 계산하기
    /// 4) 금액 검증하기 - total 이 맞는지 (프론트에서 보내준 데이터와 비교)
    /// 5) 주문 데이터 저장하기 - 데이터베이스에 insert
    /// 6) 결제 시도하기 - PG사 API 호출
    /// 7) 주문 상태 업데이트하기 (결제 성공/실패)
    /// 8) 최종 결과 반환하기 (주문 내역, 결제 상태)
  }
}
주문 서비스의 주요 단계는 다음과 같습니다.
사용자 정보 가져오기 – 토큰 기반 인증을 통해 사용자 확인
주문 요청 데이터 검증 – DTO 검증을 통해 요청 무결성 보장
총 금액 계산 – DB에서 상품 가격을 불러와 합산
금액 검증 – 프론트엔드에서 전달된 금액과 일치 여부 확인 (보안상 중요한 단계)
주문 데이터 저장 – DB에 주문 데이터 저장 (주문 상태는 PENDING)
결제 서비스 연동 – PG사/카드사 API 호출 및 결제 승인 요청
주문 상태 업데이트 – 결제 성공 시 PAID, 실패 시 FAILED로 변경
결과 반환 – 사용자에게 최종 주문 정보와 결제 상태 반환













댓글 ( 0)  
댓글 남기기