본문 바로가기
웹 백엔드/NestJS

NestJS : mongoose virtual field를 사용해서 원하는 값만 반환하기

by 번데기 개발자 2024. 2. 9.
반응형

 

 

NestJS에서 Mongoose를 사용해서 백엔드 개발을 진행하고 있습니다. 이때 return으로 모델을 반환하면 response로 반환하고 싶지 않은 정보들도 같이 반환될때가 있습니다.

 

이럴때 mongoose 에서 제공하는 virtual 속성을 통해 반환을 위한 값들을 제거하고 원하는 필드만 반환하도록 설정할 수 있습니다.

 

 

Mongoose v8.1.1: Mongoose Tutorials: Mongoose Virtuals

Mongoose Virtuals In Mongoose, a virtual is a property that is not stored in MongoDB. Virtuals are typically used for computed properties on documents. Your First Virtual Suppose you have a User model. Every user has an email, but you also want the email's

mongoosejs.com

 

 

 

NestJS에서 virtual 설정해보기

 

현재 프로젝트를 조회하는 GET /project API가 있다고 가정해보도록 하겠습니다.

 

NestJS의 controller와 Repository는 아래와 같이 설정되어 있습니다.

 

// Project 레포지토리
import { InjectModel } from '@nestjs/mongoose';
import { Project } from '../schema/project.schema';

@Injectable()
export class ProjectsRepository {
  constructor(
    @InjectModel(Project.name) private readonly projectModel: Model<Project>,
  ) {}

  async findById(id: string): Promise<Project> {
    return await this.projectModel.findById(id).exec();
  }
}

 

/** 프로젝트를 불러오는 Controller **/
@Controller('/projects')
export class ProjectsController {
  constructor(private readonly projectsService: ProjectsService) {}

  @ApiOperation({ summary: '단일 프로젝트 조회 (LD 프로젝트 상세 정보)' })
  @Get(':projectId')
  getProject(@Param('projectId', ValidateMongoIdPipe) id: string) {
    return this.projectsService.getProject(id);
  }
}

 

실제 서비스 함수에서 데이터를 불러올때는 아래와 같이 Project 모델을 반환해줍니다.

 

/** 프로젝트를 불러오는 서비스 함수 **/
export class ProjectsService {
  constructor(
    private readonly projectsRepository: ProjectsRepository,
  ) {}

  async getProject(id: string) {
    const project = await this.projectsRepository.findById(id)

    return {
      result: project,
      message: 'Project found successfully',
    };
  }
}

 

이때 Model을 바로 반환하게 되면 Project 모델의 모든 정보가 Client로 반환되게 됩니다. 현재 Project 모델은 아래와 같이 정의되어 있습니다.

 

 

/** Project Mongoose 모델 **/
import { Document, SchemaOptions } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

const options: SchemaOptions = {
  versionKey: false,
};

@Schema(options)
export class Project extends Document {
  @Prop({ default: null })
  title: string;

  @Prop({ default: null })
  domain: string;

  @Prop({ default: Date.now })
  createdAt: Date;

  @Prop({ default: Date.now })
  updatedAt: Date;
}

const _ProjectSchema = SchemaFactory.createForClass(Project);

_ProjectSchema.set('toObject', { virtuals: true });
_ProjectSchema.set('toJSON', { virtuals: true });

_ProjectSchema.index({ userId: 1 });

export const ProjectSchema = _ProjectSchema;

 

 

이때 GET API를 호출하게 되면 다음과 같이 응답이 전달됩니다.

 

 

여기서 불필요한 값인 createdAt, updatedAt, _id를 지우고 싶으면 아래와 같이 코드를 수정해야합니다.

 

export class ProjectsService {
  constructor(
    private readonly projectsRepository: ProjectsRepository,
  ) {}
  
  async getProject(id: string) {
    const project = await this.projectsRepository.update(id, {
      updatedAt: new Date(),
    });

    if (!project) {
      throw new NotFoundException('Project is not found');
    }

    return {
      result: {
        id: project.id,
        title: project.title,
        domain: project.domain,
      },
      success: true,
      message: 'Project found successfully',
    };
  }
}

 

field가 1,2개라면 위의 방법도 나쁜 방법은 아니지만 Mongoose의 virtual 속성을 이용하면 위의 로직을 좀더 간편하게 만들 수 있습니다.

 

 

Mongoose virtuals 

 

Mongoose에서 virtual 속성은 실제 DB에 저장되지 않는 값입니다. 보통 실제 DB을 이용해서 보여줄 값들을 가공해서 반환할때 주로 사용됩니다.

 

Mongoose의 virtual 속성을 이용해서 Model에서 반환을 하고싶은 값만 가지는 가상의 속성을 만들어서 해당 속성을 통해 응답을 처리할 수 있습니다.

 

앞서 만들어본 Mongoose 의 Project Model 코드를 일부 수정하여 virtual 속성을 적용해보도록 하겠습니다.

 

 

/** Project Mongoose 모델 **/
import { Document, SchemaOptions } from 'mongoose';
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

const options: SchemaOptions = {
  versionKey: false,
};

@Schema(options)
export class Project extends Document {
  @Prop({ default: null })
  title: string;

  @Prop({ default: null })
  domain: string;

  @Prop({ default: Date.now })
  createdAt: Date;

  @Prop({ default: Date.now })
  updatedAt: Date;
  
  readonly readOnlyData: {
    id: string;
    title: string;
    domain: string;
  }
}

const _ProjectSchema = SchemaFactory.createForClass(Project);

_ProjectSchema.set('toObject', { virtuals: true });
_ProjectSchema.set('toJSON', { virtuals: true });
_ProjectSchema.index({ userId: 1 });
_ProjectSchema.virtual('readOnlyData').get(function (this: Project){
  return {
    id: this.id,
    title: this.title,
    domain: this.domain
}

export const ProjectSchema = _ProjectSchema;

 

이후 서비스에서 다음과 같이 응답을 반환하도록 수정하여줍니다.

 

export class ProjectsService {
  constructor(
    private readonly projectsRepository: ProjectsRepository,
  ) {}
  
  async getProject(id: string) {
    const project = await this.projectsRepository.update(id, {
      updatedAt: new Date(),
    });

    if (!project) {
      throw new NotFoundException('Project is not found');
    }

    return {
      result: project.readOnlyData, // virtual 속성을 통해 원하는 값만 반환
      success: true,
      message: 'Project found successfully',
    };
  }
}

 

 

 

 

 

 

 

마무리

 

오늘은 NestJS에서 Mongoose의 virtual 속성을 이용하여 원하는 값만 반환할수 있는 필드를 만들고 이를 응답에 적용하는 방법에 대해 알아보았습니다.

 

다음에도 NestJS를 개발할때 유용한 정보에 대해 포스팅하도록 하겠습니다.

 

감사합니다.!

반응형