icon

Mongoose를 활용한 MongoDB 연동


 NestJS와 Mongoose를 함께 사용하면 MongoDB의 유연성과 NestJS의 구조화된 아키텍처를 결합하여 강력한 애플리케이션을 구축할 수 있습니다.

Mongoose 설정 및 MongoDB 연결

  1. 필요한 패키지 설치
npm install @nestjs/mongoose mongoose
  1. app.module.ts에 MongooseModule 추가
app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
 
@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost/nest'),
  ],
})
export class AppModule {}

스키마 및 모델 정의

 NestJS에서는 데코레이터를 사용하여 Mongoose 스키마를 정의할 수 있습니다.

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
 
@Schema()
export class Cat extends Document {
  @Prop()
  name: string;
 
  @Prop()
  age: number;
 
  @Prop()
  breed: string;
}
 
export const CatSchema = SchemaFactory.createForClass(Cat);

모델 주입 및 사용

 NestJS의 의존성 주입 시스템을 사용하여 Mongoose 모델을 서비스에 주입합니다.

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Cat } from './schemas/cat.schema';
 
@Injectable()
export class CatsService {
  constructor(@InjectModel(Cat.name) private catModel: Model<Cat>) {}
 
  async create(createCatDto: CreateCatDto): Promise<Cat> {
    const createdCat = new this.catModel(createCatDto);
    return createdCat.save();
  }
 
  async findAll(): Promise<Cat[]> {
    return this.catModel.find().exec();
  }
}

 모듈에 스키마 등록

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { Cat, CatSchema } from './schemas/cat.schema';
 
@Module({
  imports: [MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])],
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

CRUD 작업 구현

@Injectable()
export class CatsService {
  constructor(@InjectModel(Cat.name) private catModel: Model<Cat>) {}
 
  async create(createCatDto: CreateCatDto): Promise<Cat> {
    const createdCat = new this.catModel(createCatDto);
    return createdCat.save();
  }
 
  async findAll(): Promise<Cat[]> {
    return this.catModel.find().exec();
  }
 
  async findOne(id: string): Promise<Cat> {
    return this.catModel.findById(id).exec();
  }
 
  async update(id: string, updateCatDto: UpdateCatDto): Promise<Cat> {
    return this.catModel.findByIdAndUpdate(id, updateCatDto, { new: true }).exec();
  }
 
  async remove(id: string): Promise<Cat> {
    return this.catModel.findByIdAndRemove(id).exec();
  }
}

고급 쿼리 기능

 쿼리 빌더 사용

async findYoungCats(): Promise<Cat[]> {
  return this.catModel.find().where('age').lt(5).exec();
}

 집계 파이프라인

async getAverageAge(): Promise<number> {
  const result = await this.catModel.aggregate([
    { $group: { _id: null, avgAge: { $avg: '$age' } } }
  ]).exec();
  return result[0]?.avgAge || 0;
}

Mongoose 미들웨어와 훅

 스키마에 미들웨어 추가

CatSchema.pre('save', function(next) {
  if (this.isNew) {
    console.log('A new cat is being added!');
  }
  next();
});

성능 최적화 및 인덱싱

  1. 인덱스 생성
@Schema()
export class Cat extends Document {
  @Prop({ index: true })
  name: string;
}
  1. 복합 인덱스
CatSchema.index({ name: 1, age: -1 });
  1. 지연 로딩
async findAllWithPagination(page: number, limit: number): Promise<Cat[]> {
  return this.catModel.find()
    .skip((page - 1) * limit)
    .limit(limit)
    .exec();
}

Best Practices와 주의사항

  1. 스키마 설계 : MongoDB의 유연성을 고려하되, 일관된 스키마 구조를 유지하세요.
  2. 데이터 유효성 검사 : Mongoose의 내장 유효성 검사 기능을 활용하세요.
@Prop({ required: true, minlength: 2, maxlength: 30 })
name: string;
  1. 비동기 처리 : Mongoose 연산은 비동기이므로 항상 async/await를 사용하세요.
  2. 인덱스 최적화 : 자주 조회하는 필드에 인덱스를 추가하되, 과도한 인덱싱은 피하세요.
  3. 대량 연산 주의 : 대량의 데이터를 다룰 때는 스트림이나 배치 처리를 고려하세요.
  4. 연결 관리
MongooseModule.forRoot('mongodb://localhost/nest', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  serverSelectionTimeoutMS: 5000,
})
  1. 타입 안전성 : Mongoose와 TypeScript의 타입을 적절히 조합하여 타입 안전성을 확보하세요.
  2. 서브도큐먼트 활용 : 필요한 경우 서브도큐먼트를 활용하여 관련 데이터를 그룹화하세요.
  3. 프로젝션 사용 : 필요한 필드만 조회하여 성능을 최적화하세요.
this.catModel.find({}, { name: 1, age: 1 }).exec();
  1. 에러 처리 : Mongoose 연산 시 발생할 수 있는 에러를 적절히 처리하세요.
try {
  await this.catModel.save();
} catch (error) {
  if (error.code === 11000) {
    throw new ConflictException('Duplicate key error');
  }
  throw error;
}

 Mongoose의 스키마 기반 접근 방식은 MongoDB의 스키마리스 특성에 일정 수준의 구조를 제공하며, 이는 데이터 일관성을 유지하는 데 도움이 됩니다.

 성능 최적화는 MongoDB를 사용할 때 특히 중요합니다.

 적절한 인덱싱, 쿼리 최적화, 그리고 필요한 경우 읽기 선호 복제본 사용 등의 전략을 고려해야 합니다.

 또한 MongoDB의 집계 파이프라인을 효과적으로 활용하면 복잡한 데이터 처리 작업을 효율적으로 수행할 수 있습니다.

 Mongoose 미들웨어와 훅을 활용하면 데이터 조작 전후에 추가적인 로직을 쉽게 삽입할 수 있습니다.

 이는 로깅, 데이터 변환, 보안 검사 등 다양한 용도로 활용될 수 있습니다.