Nodejs

 

NestJS로 주문(Order) 도메인 모델 설계하기 - Mongoose Entity 구축

 

이번 글에서는 NestJS 기반 Order Microservice에서 주문 데이터를 MongoDB에 저장하기 위한 Mongoose 스키마(Entity) 설계 과정을 다룹니다. 강의에서는 주문에 필요한 여러 서브 도메인(고객, 상품, 결제, 배송지)을 객체로 분리하여 설계하고, 이를 기반으로 최종 Order 스키마를 완성합니다.

 

???? Mongoose Entity 설계 배경

 

 

NestJS에서 MongoDB를 사용할 경우 @nestjs/mongoose와 mongoose를 사용하여 클래스를 기반으로 도메인 모델을 설계합니다. 이 방식은 TypeScript 기반의 강력한 타입 지원과 NestJS의 DI, 모듈 구조와도 잘 맞습니다.

이번 Order 서비스에서는 다음과 같은 도메인 구성요소를 가집니다:

  • Customer: 주문자 정보

  • Product: 주문된 상품 목록

  • DeliveryAddress: 배송지 정보

  • Payment: 결제 정보

  • Order: 위 모든 것을 포함한 최상위 주문 엔터티

 

1. Customer Entity

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

@Schema({ _id: false })
export class Customer {
  @Prop({ required: true })
  userId: string;

  @Prop({ required: true })
  email: string;

  @Prop({ required: true })
  name: string;
}

export const CustomerSchema = SchemaFactory.createForClass(Customer);
  • _id: false 설정은 해당 서브 문서가 별도의 MongoDB _id를 가지지 않도록 합니다.

  • 이 스키마는 Order 스키마 내부에 중첩 서브 문서로 삽입됩니다.

 

 

2. DeliveryAddress Entity

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

@Schema({ _id: false })
export class DeliveryAddress {
  @Prop({ required: true })
  name: string;

  @Prop({ required: true })
  street: string;

  @Prop({ required: true })
  city: string;

  @Prop({ required: true })
  postalCode: string;

  @Prop({ required: true })
  country: string;
}

export const DeliveryAddressSchema = SchemaFactory.createForClass(DeliveryAddress);
  • 배송 주소 역시 주문 내부에 포함되는 서브 문서이며, 별도의 독립 컬렉션을 만들지 않습니다.

 

 

 

3. Product Entity

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

@Schema({ _id: false })
export class Product {
  @Prop({ required: true })
  productId: string;

  @Prop({ required: true })
  name: string;

  @Prop({ required: true })
  price: number;
}

export const ProductSchema = SchemaFactory.createForClass(Product);
  • 하나의 주문은 여러 개의 상품을 포함할 수 있으므로 products: Product[]로 배열로 사용됩니다.

 

 

 

4. Payment Entity

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

export enum PaymentMethod {
  creditCard = 'CreditCard',
  kakao = 'Kakao',
}

@Schema({ _id: false })
export class Payment {
  @Prop()
  paymentId: string;

  @Prop({ enum: PaymentMethod, default: PaymentMethod.creditCard })
  paymentMethod: PaymentMethod;

  @Prop({ required: true })
  paymentName: string;

  @Prop({ required: true })
  amount: number;
}

export const PaymentSchema = SchemaFactory.createForClass(Payment);
  • 결제 방식은 CreditCard, Kakao 중 선택되며, 기본값은 카드 결제입니다.

 

 

5. Order Entity (최종 통합)

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);

 

✅ 설명

  • 이 Order 클래스는 최상위 MongoDB 문서이며 실제 orders 컬렉션에 저장됩니다.

  • 내부에 여러 개의 서브 도메인 스키마들을 중첩 문서 형식으로 내장하고 있습니다.

  • OrderStatus는 주문의 상태를 관리하는 enum입니다.

실무에서는 왜 주문 데이터를 NoSQL로 저장할까?

실제 운영 환경에서도 주문(Order) 데이터를 MongoDB 같은 NoSQL로 저장하는 경우가 많습니다. 그 주요 이유는 다음과 같습니다:

 

 

 

 

✅ 1. 중첩 구조에 적합한 스키마 설계

주문은 고객 정보, 배송지, 결제, 상품 리스트 등 다양한 하위 객체들을 포함하는 복합 도메인입니다. MongoDB는 이들을 하나의 문서로 묶을 수 있어 다음과 같은 장점이 있습니다:

  • 각 주문 문서에 필요한 모든 정보가 중첩되어 저장됨

  • 별도 조인 없이 단일 쿼리로 모든 주문 상세 정보 조회 가능

 

 

 

 

✅ 2. 읽기 속도 및 응답 시간 최적화

관계형 DB에서는 주문 조회 시 여러 테이블을 JOIN해야 하지만, MongoDB는 단일 쿼리로 한 번에 주문 전체 데이터를 조회할 수 있습니다. 이는 실시간성이 중요한 배달 앱, 쇼핑몰 등에서 매우 유리합니다.

 

 

 

 

✅ 3. 유연한 스키마 구조

MongoDB는 스키마 변경에 유연합니다:

  • 새로운 필드 추가 시 테이블 ALTER 불필요

  • 개발/운영 중 빠르게 구조를 확장하거나 축소 가능

→ 변화가 잦은 스타트업 환경, 프로모션 등이 자주 바뀌는 전자상거래에 적합합니다.

 

 

 

✅ 4. 수평 확장과 높은 쓰기 성능

  • MongoDB는 샤딩(Sharding)을 통해 수평 확장이 용이하고,

  • 초당 수천 건의 주문 데이터 쓰기에도 안정적으로 대응할 수 있습니다.

 

 

 

❗️주의할 점

항목설명

트랜잭션MongoDB 4.0 이후 다중 문서 트랜잭션 지원. 그러나 복잡한 트랜잭션은 여전히 RDB에 강점

정합성서브 문서 중복 저장 시 동기화 전략 필요

집계/통계MongoDB Aggregation은 강력하지만 SQL보다 직관적이지 않음

이번 글에서는 Mongoose 기반으로 NestJS의 주문 도메인을 설계하는 전체 흐름을 정리했습니다. 주문은 단순한 하나의 객체가 아니라 여러 하위 속성(고객, 배송, 결제, 상품)을 내장하고 있어, 스키마를 잘게 나눠 관리하는 것이 유지보수성과 확장성을 높이는 데 효과적입니다.

다음 글에서는 이 스키마를 실제 DB에 저장하고 조회하는 서비스 로직을 구현해보겠습니다.

 

 

 

 

 

about author

PHRASE

Level 60  라이트

아랫돌 빼서 윗돌 괴고, 윗돌 빼서 아랫돌 괴기 , 우선 다급한 처지를 모면하기 위하여 이리저리 둘러맞추는 임시 변토을 이르는 말.

댓글 ( 0)

댓글 남기기

작성