[MongoDB]Mongoose Virtuals

Robin·2023년 9월 29일

DB

목록 보기
1/1
post-thumbnail

Mongoose Virtuals

Mongoose는 Virtual 기능을 통해 가상의 필드를 만들어 프론트에 응답값으로 보내줄 수 있다. 👉 공식문서

쉽게 말해, DB에서 원하는 필드만 골라서, 혹은 원하는 값만 가공하여 내려주는 기능으로 활용할 수 있다는 의미이다.


예를 들어, 아래와 같은 Cat 스키마가 있다고 가정하자. (NestJS,TypeScript,Mongoose,MongoDB, class-validator).

@Schema(options)
export class Cat extends Document {
  @Prop({
    required: true,
    unique: true,
  })
  @IsEmail()
  @IsNotEmpty()
  email: string;

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

  @Prop({
    required: true,
  })
  @IsString()
  @IsNotEmpty()
  password: string;

  @Prop()
  @IsString()
  imgUrl: string;
}

export const CatSchema = SchemaFactory.createForClass(Cat);

상기 스키마는 email, name, password를 필수값으로 갖는다.


회원가입과 같이 'Cat 생성'에 관한 요청이 들어오는 경우를 생각해보자.

단순히 아래와 같이 생성된 값을 return 해 줄 수도 있다.

const createdCat = await this.catModel.create({
      email,
      name,
      password: hashedPassword,
    });

return createdCat
{
    "success": true,
    "data": {
        "email": "testEmail0@test.com",
        "name": "testName0",
        "password": "$2b$10$2zub5lcloMnzUiLbklectOksIiQjvlRUKg24ga5UTriAtYgJEtOui",
        "_id": "6516d20eb20f56cb694205f7",
        "createdAt": "2023-09-29T13:33:02.661Z",
        "updatedAt": "2023-09-29T13:33:02.661Z",
        "__v": 0
    }
}

하지만 아무리 해싱되었다한들 password를 고스란히 프론트에 응답해주는 것은 바람직 하지 못하다.


이에 Schema에 아래와 같이 Mongoose Virtuals 기능을 사용할 수 있다.

@Schema(options)
export class Cat extends Document {
  @Prop({
    required: true,
    unique: true,
  })
  @IsEmail()
  @IsNotEmpty()
  email: string;

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

  @Prop({
    required: true,
  })
  @IsString()
  @IsNotEmpty()
  password: string;

  @Prop()
  @IsString()
  imgUrl: string;

  
  readonly readOnlyData: { //2️⃣
    id: string;
    email: string;
    name: string;
  };
}

export const CatSchema = SchemaFactory.createForClass(Cat);

CatSchema.virtual('readOnlyData').get(function (this: Cat) { //1️⃣
  return {
    id: this.id,
    email: this.email,
    name: this.name,
  };
});

1️⃣ readOnlyData라는 프로퍼티를 만들어 return 해줄 값을 설정하고 있다.

2️⃣ 가상의 readOnlyData 필드를 스키마에도 추가시켜준다.

이후, 아래와 같이 응답값에 해당 필드를 return한다.

const createdCat = await this.catModel.create({
      email,
      name,
      password: hashedPassword,
    });

return createdCat.readOnlyData;

다시금 응답값을 한눈에 비교해보자:

[before]

{
    "success": true,
    "data": {
        "email": "testEmail0@test.com",
        "name": "testName0",
        "password": "$2b$10$2zub5lcloMnzUiLbklectOksIiQjvlRUKg24ga5UTriAtYgJEtOui",
        "_id": "6516d20eb20f56cb694205f7",
        "createdAt": "2023-09-29T13:33:02.661Z",
        "updatedAt": "2023-09-29T13:33:02.661Z",
        "__v": 0
    }
}

[after]

{
    "success": true,
    "data": {
        "id": "65168c2078037b475711043c",
        "email": "testEmail1@test.com",
        "name": "testName1"
    }
}

예시 외에도 setter를 활용하여 값을 가공할 수도 있다.

✅ 주의할 점은 Mongoose virtuals은 DB에 저장되지 않는다는 점이다. 즉, Mongoose virtuals을 바탕으로 쿼리를 진행할 수 없음을 인지해야한다.

profile
Always testing, sometimes dog walking

0개의 댓글