[MongoDB] Mongoose 스키마 유효성 검사

유동균·2023년 1월 29일
0

MongoDB

목록 보기
7/12
post-thumbnail

Schema 유효성 검사

  • 내장된 제약 조건

Schema 타입 & 속성 & 제약조건 알아보기

// (index.js)

const mongoose = require("mongoose");

mongoose.set("strictQuery", true);

// 연결 상태를 알 수 없을때 코드를 실행하는 방법 (mongoose.connect()의 연결을 열기 위해 시간이 걸리기 때문에)
// mongoose는 버퍼링을 작동해서 시작
// 버퍼링으로 우리가 정의하는 모델을 즉시 사용 가능하다. mongoose가 연결될 때까지 기다릴 필요가 없음
// 즉 mongoose.connect().then(() => {이 콜백 안에 코드를 중첩하지 않아도 됨})

mongoose
  .connect("mongodb://localhost:27017/shopApp")
  .then(() => {
    console.log("Connection Open");
    // 이곳에 코드를 작성해야 mongoose.connect() 후에 코드를 실행할 수 있다고 생각하겠지만...
  })
  .catch((e) => {
    console.log("ERROR");
    console.log(e);
  });

const { Schema } = mongoose;
// 새로운 상품 Schema 생성
const productSchema = Schema({
  name: {
    type: String,
    // required를 배열로 처리하여 아래와 같이 입력하면 에러메시지를 커스텀화 할 수 있다.
    required: [true, "상품 이름이 필요합니다."],
  },
  price: {
    type: Number,
    required: true,
  },
});

// 상품 model 생성
const Product = mongoose.model("Product", productSchema);

const bike = new Product({name: "Mountain Bike", price: 599 });
bike
  .save()
  .then((data) => {
    console.log("It Worked!!!");
    console.log(data);
  })
  .catch((err) => {
    console.log("ERROR!!!");
    console.log(err);
  });
  • 정상
> node index.js
Connection Open
It Worked!!!
{
  name: 'Mountain Bike',
  price: 599,
  _id: new ObjectId("63d65f2ce54108a157c08d14"),
  __v: 0
}

Required

  • namerequired : [ture, ...]인데 model을 생성할때 생략한다면 오류 발생
// (index.js)
...

// name: "Mountain Bike" 생략
const bike = new Product({ price: 599 });

...
> node index.js
ERROR!!!
Error: Product validation failed: name: 상품 이름이 필요합니다.
    at ValidationError.inspect (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/error/validation.js:50:26)
    at formatValue (node:internal/util/inspect:806:19)
    at inspect (node:internal/util/inspect:365:10)
    at formatWithOptionsInternal (node:internal/util/inspect:2273:40)
    at formatWithOptions (node:internal/util/inspect:2135:10)
    at console.value (node:internal/console/constructor:340:14)
    at console.log (node:internal/console/constructor:377:61)
    at /Users/users/Desktop/project/mongoose/index.js:45:13
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  errors: {
    name: ValidatorError: 상품 이름이 필요합니다.
        at validate (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schematype.js:1346:13)
        at SchemaType.doValidate (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schematype.js:1330:7)
        at /Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/document.js:2905:18
        at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
      properties: [Object],
      kind: 'required',
      path: 'name',
      value: undefined,
      reason: undefined,
      [Symbol(mongoose:validatorError)]: true
    }
  },
  _message: 'Product validation failed'
}
Connection Open

Type

  • pricetype : Number인데 String 타입을 입력한면 오류 발생
// (index.js)
...

const bike = new Product({name: "Mountain Bike", price: "Hello World" });

...
> node index.js
ERROR!!!
Error: Product validation failed: price: Cast to Number failed for value "Hello World" (type string) at path "price"
    at ValidationError.inspect (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/error/validation.js:50:26)
    at formatValue (node:internal/util/inspect:806:19)
    at inspect (node:internal/util/inspect:365:10)
    at formatWithOptionsInternal (node:internal/util/inspect:2273:40)
    at formatWithOptions (node:internal/util/inspect:2135:10)
    at console.value (node:internal/console/constructor:340:14)
    at console.log (node:internal/console/constructor:377:61)
    at /Users/ryudonggyun/Desktop/code/mongoose/index.js:45:13
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  errors: {
    price: CastError: Cast to Number failed for value "Hello World" (type string) at path "price"
        at SchemaNumber.cast (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schema/number.js:378:11)
        at SchemaType.applySetters (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schematype.js:1201:12)
        at model.$set (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/document.js:1423:22)
        at model.$set (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/document.js:1148:16)
        at model.Document (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/document.js:166:12)
        at model.Model (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/model.js:122:12)
        at new model (/Users/users/Desktop/project/mongoose/ode_modules/mongoose/lib/model.js:5092:15)
        at Object.<anonymous> (/Users/users/Desktop/project/mongoose/index.js:36:14)
        at Module._compile (node:internal/modules/cjs/loader:1218:14)
        at Module._extensions..js (node:internal/modules/cjs/loader:1272:10) {
      stringValue: '"Hello World"',
      messageFormat: undefined,
      kind: 'Number',
      value: 'Hello World',
      path: 'price',
      reason: AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
      
        assert.ok(!isNaN(val))
      
          at castNumber (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/cast/number.js:27:10)
          at SchemaNumber.cast (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schema/number.js:376:12)
          at SchemaType.applySetters (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schematype.js:1201:12)
          at model.$set (/Users/ryudonggyun/Desktop/code/mongoose/node_modules/mongoose/lib/document.js:1423:22)
          at model.$set (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/document.js:1148:16)
          at model.Document (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/document.js:166:12)
          at model.Model (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/model.js:122:12)
          at new model (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/model.js:5092:15)
          at Object.<anonymous> (/Users/users/Desktop/project/mongoose/index.js:36:14)
          at Module._compile (node:internal/modules/cjs/loader:1218:14) {
        generatedMessage: true,
        code: 'ERR_ASSERTION',
        actual: false,
        expected: true,
        operator: '=='
      },
      valueType: 'string'
    }
  },
  _message: 'Product validation failed'
}
Connection Open

Schema에 정의하지 않은 정보 입력시

// (index.js)
...

const bike = new Product({ name: "Mountain Bike", price: 599, color: "red" });

...
> node index.js
Connection Open
It Worked!!!
{
  name: 'Mountain Bike',
  price: 599,
  _id: new ObjectId("63d6622ff897d692ffe7a2e8"),
  __v: 0
}
  • 오류가 발생하지 않고 정보가 추가되지 않음

Default

const productSchema = Schema({
  name: {
    type: String,
    required: [true, "상품 이름이 필요합니다."],
  },
  price: {
    type: Number,
    required: true,
  },
  // onSale property 추가
  onSale: {
    type: Boolean,
    default: false,
  },
});

// Schema에 정의는 했지만 값을 넣지 않을경우 default로 설정했던 값이 들어감
...

const bike = new Product({ name: "Mountain Bike", price: 599});

...
> node index.js
Connection Open
It Worked!!!
{
  name: 'Mountain Bike',
  price: 599,
  onSale: false,
  _id: new ObjectId("63d664e0a32080265e9cf1a5"),
  __v: 0
}

업데이트 유효성 검사하기

... Schema 수정

const productSchema = Schema({
  name: {
    type: String,
    required: true,
    maxlength: 20,
  },
  price: {
    type: Number,
    required: true,
    min : 0,
  },
  onSale: {
    type: Boolean,
    default: false,
  },
  categories : [String],
  qty : {
    online: {
      type: Number,
      default: 0,
    },
    inStore: {
      type: Number,
      default: 0,
    }
  }
});

...

const bike = new Product({
  name: "Tire Pump",
  price: 19.5,
  categories: ["Cycling"],
});
bike
  .save()
  .then((data) => {
    console.log("It Worked!!!");
    console.log(data);
  })
  .catch((err) => {
    console.log("ERROR!!!");
    console.log(err);
  });

...
> node index.js
Connection Open
It Worked!!!
{
  name: 'Tire Pump',
  price: 19.5,
  onSale: false,
  categories: [ 'Cycling' ],
  qty: { online: 0, inStore: 0 },
  _id: new ObjectId("63d66ea2821c87ce860fe862"),
  __v: 0
}
  • price : 100로 업데이트 하기
Product.findOneAndUpdate({ name: "Tire Pump" }, { price: 100 }, { new: true })
  .then((data) => {
    console.log("It Worked!!!");
    console.log(data);
  })
  .catch((err) => {
    console.log("ERROR!!!");
    console.log(err);
  });
  • price : 100로 업데이트 result
> node index.js
Connection Open
It Worked!!!
{
  qty: { online: 0, inStore: 0 },
  _id: new ObjectId("63d66ea2821c87ce860fe862"),
  name: 'Tire Pump',
  price: 100,
  onSale: false,
  categories: [ 'Cycling' ],
  __v: 0
}
  • pricemin 값이 0일때 음수로 업데이트 하기
Product.findOneAndUpdate(
  { name: "Tire Pump" },
  { price: -19.99 },
  { new: true }
)
  .then((data) => {
    console.log("It Worked!!!");
    console.log(data);
  })
  .catch((err) => {
    console.log("ERROR!!!");
    console.log(err);
  });
  • pricemin 값이 0일때 음수로 업데이트 결과
> node index.js
Connection Open
It Worked!!!
{
  qty: { online: 0, inStore: 0 },
  _id: new ObjectId("63d66ea2821c87ce860fe862"),
  name: 'Tire Pump',
  price: -19.99,
  onSale: false,
  categories: [ 'Cycling' ],
  __v: 0
}
  • 유효성 검사의 의미가 없이 음수로 나온다.
  • 무엇인가 만들면 유효성 검사가 자동으로 적용되는데 업데이트 이후에는 Mongoose에게 직접 유효성 검사를 적용하라고 명령해야한다.
  • runValidators: true
Product.findOneAndUpdate(
  { name: "Tire Pump" },
  { price: -19.99 },
  { new: true, runValidators: true }
)
  .then((data) => {
    console.log("It Worked!!!");
    console.log(data);
  })
  .catch((err) => {
    console.log("ERROR!!!");
    console.log(err);
  });
  • runValidators: true result
    유효성 검사 결과 Error
> node index.js
ERROR!!!
Error: Validation failed: price: Path `price` (-19.99) is less than minimum allowed value (0).
    at ValidationError.inspect (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/error/validation.js:50:26)
    at formatValue (node:internal/util/inspect:806:19)
    at inspect (node:internal/util/inspect:365:10)
    at formatWithOptionsInternal (node:internal/util/inspect:2273:40)
    at formatWithOptions (node:internal/util/inspect:2135:10)
    at console.value (node:internal/console/constructor:340:14)
    at console.log (node:internal/console/constructor:377:61)
    at /Users/users/Desktop/project/mongoose/index.js:79:13
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  errors: {
    price: ValidatorError: Path `price` (-19.99) is less than minimum allowed value (0).
        at validate (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schematype.js:1346:13)
        at SchemaType.doValidate (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/schematype.js:1330:7)
        at /Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/helpers/updateValidators.js:151:22
        at module.exports (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/helpers/updateValidators.js:202:7)
        at /Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/query.js:4330:9
        at promiseOrCallback (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/helpers/promiseOrCallback.js:11:14)
        at model.Query.validate (/Users/users/Desktop/project/mongoose/node_modules/mongoose/lib/query.js:4325:10)
        at /Users/users/Desktop/project/mongoose/node_modules/kareem/index.js:497:25
        at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
      properties: [Object],
      kind: 'min',
      path: 'price',
      value: -19.99,
      reason: undefined,
      [Symbol(mongoose:validatorError)]: true
    }
  },
  _message: 'Validation failed'
}
Connection Open

0개의 댓글