[Nest JS ] Mongo

minidoo·2021년 10월 9일
0

NestJS

목록 보기
7/8
post-thumbnail

Nest는 MongoDB 데이터베이스와의 통합을 위해 두가지 방법을 지원한다.

MongoDB용 커넥터가 있는 내장 TypeORM 모듈을 사용하거나 MongoDB 개체 모델링 도구인 Mongoose를 사용할 수 있다. TypeORM은 아직까지 몽고디비와 정확히 호환되지 않아 복잡한 작업을 처리할 때 어려움이 있다. 따라서 우리는 전용 @nestjs/mongoose 패키지를 사용하여 데이터베이스를 연결했다.

$ npm install --save @nestjs/mongoose mongoose

설치가 완료되면 MongooseModule을 루트 모듈로 가져올 수 있다.

ForRoot(Async)

// app.module.ts

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [Mongoose.forRoot(process.env.MONGO_URL)],
})

export class AppModule {}

forRoot() 메소드는 Mongoose 패키지의 mongoose.connect() 와 동일한 구성 객체를 허용한다.

모듈 옵션을 비동기적으로 전달해야 하는 경우 forRootAsync() 메서드를 사용한다. 팩토리 기능을 사용하면 비동기 구성을 처리할 수 있다. 다른 팩토리 프로바이더와 마찬가지로 팩토리 함수는 async 일 수 있으며, inject를 통해 종속성을 삽입한다.

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        uri: configService.get<string>('MONGODB_URL'),
      }),
      inject: [ConfigService],
    });
  ],
  ...
})

export class AppModule {}

Model Injection

Mongoose는 모든 것이 스키마에서 파생된다. 스키마는 모델을 정의하는데 사용된다. NestJS 데코레이터 @Schema() 를 사용하여 클래스를 스키마 정의로 표시한다.

// schemas/order.schema.ts

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

export type OrderDocument = Order & Document;

@Schema()
export class Order {
  @Prop()
  barcode: string;

  @Prop()
  userName: string;

  @Prop()
  price: number;
}

export const OrderSchema = SchemaFactory.createForClass(Order);

Order 클래스를 같은 이름의 MongoDB 컬렉션에 매핑하지만 끝에 's'를 추가하여 최종 몽고 컬렉션 이름은 orders가 된다.

@Prop() 데코레이터는 문서의 속성을 정의한다. 위의 스키마 정의에서 barcode, userName, price 세가지 속성을 정의했다. 이러한 속성의 스키마 유형은 타입스크립트 메타데이터 기능 덕분에 자동 유추된다. 하지만 형식을 반영할 수 없는 경우, 아래와 같이 명시적으로 표시한다.

@Prop(String)
barcode: string;

아래는 필수 여부, any 타입 선언, 다른 모델과의 관계 정의를 지정한 경우의 예시이다.

import * as mongoose from 'mongoose';
import { Price } from './schemas/price.schema';

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

@Prop({ type: mongoose.Schema.Types.Mixed })
userName: any;

@Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Price' })
price: Price;

스키마 파일은 사용할 모듈에 저장하는 곳이 좋다. MongooseModule 은 모듈을 구성하기 위한 forFeature() 메소드를 제공하며 현재 범위에 등록해야하는 모듈을 정의한다.

// order.module.ts

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { OrderController } from './order.controller';
import { OrderService } from './order.service';
import { Order, OrderSchema } from './schemas/order.schema';

@Module({
  imports: [MongooseModule.forFeature([{ name: Order.name, schema: OrderSchema }])],
  controllers: [OrderController],
  providers: [OrderService],
})
export class OrderModule {}

스키마를 등록한 후 @InjectModel() 데코레이터를 사용하여 Order 모델을 OrderService에 삽입할 수 있다.

// order.service.ts

import { Model } from 'mongoose';
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Order, OrderDocument } from './schemas/order.schema';

@Injectable()
export class OrderService {
  constructor(@InjectModel(Order.name) private orderModel: Model<OrderDocument>) {}

  async getOrder(barcode: string): Promise<any> {
    try {
      const result = await this.orderModel.findOne({ barcode }).lean();
      return result;
    } catch (err) {
      console.log('error...');
    }
  }
}

Multiple Databases

여러 데이터베이스 연결이 필요한 경우, 모듈을 통해서 가능하다. 이 경우 연결 이름 지정은 필수이다. 이름이 없거나 같은 이름의 연결이 여러개 있으면 안되며, connectionName 을 사용한다.

// app.module.ts

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost/database1', {
      connectionName: 'database1',
    }),
    MongooseModule.forRoot('mongodb://localhost/database2', {
      connectionName: 'database2',
    }),
  ],
})
export class AppModule {}

이 설정을 사용하려면 forFeature() 함수에 어떤 연결을 사용해야 하는지 명시해야 한다.

@Module({
  imports: [
    MongooseModule.forFeature([{ name: Order.name, schema: OrderSchema }], 'database1'),
  ],
})

팩토리 프로바이더를 사용하는 경우, 연결 이름을 인수로 전달하는 getConnectionToken() 함수를 사용한다. (질문 ✅)

MongooseModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    uri: configService.get<string>('MONGODB_URL'),
  }),
  inject: [ConfigService, getConnectionToken('database1')],
});

참고 사이트

MongoDB | 네스트JS 한국어 매뉴얼

0개의 댓글