await User.findOnd({id}).exec()
위와 같은 코드를 보았다.
.exec()이 없어도 결과는 똑같았는데 왜 exec()을 사용하는지 궁금해 공식문서를 찾아보았다.

몽구스 오퍼레이터인 .save()와 queries는 thenables한 값을 리턴해준다고 나와있다.
.then() 을 사용할 수 있다는 뜻인데, 마치 프로미스처럼 보인다.
.save()는 promise를 리턴한다.

그러나 query는 프로미스를 리턴하지 않는다.
갑자기 query는 뭐지? 라는 생각이 들 수 있다.
Model.findById()
위 메서드는 몽구스를 사용한다면 정말 많이 쓰게 되는 메서드이다.

Return이 Query 임을 볼 수 있다.

공식 문서를 보면 query는 프로미스가 아니지만, async/await을 사용할 수 있도록 .then()을 갖고있다고 한다.
또 promise와 완전히 동일하게 만드려면 .exec() 함수를 사용하라고 나와있다.
const query = Band.findOne({ name: 'Guns N\' Roses' });
assert.ok(!(query instanceof Promise)); // Promise 가 아니다.
// A query is not a fully-fledged promise, but it does have a `.then()`.
query.then(function(doc) {
// use doc
});
// `.exec()` gives you a fully-fledged promise
const promise = Band.findOne({ name: 'Guns N\' Roses' }).exec();
assert.ok(promise instanceof Promise); // .exec()을 사용하면 promise가 된다.
promise.then(function(doc) {
// use doc
});
queries는 프로미스가 아니지만, thenable하다.
promise chaning을 사용하거나 async await을 사용할 수 있다는 뜻이다.
await User.findOne();
await User.findOne().exec();
기능적으로 동일하다.
그러나 공식 문서에서는 .exec()을 붙여 사용하길 권장한다.
.exec()을 사용할 경우, 에러 검증 시 더 상세한 stack traces를 제공한다.
공식문서의 예시
const doc = await Band.findOne({ name: 'Guns N\' Roses' }); // works
const badId = 'this is not a valid id';
try {
await Band.findOne({ _id: badId });
} catch (err) { .exec()가 없는 경우, 코드 어디서 에러가 발생했는지 정확히 말해주지 않는다.
// Without `exec()`, the stack trace does **not** include the
// calling code. Below is the stack trace:
//
// CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises"
// at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11)
// at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21)
// at model.Query.Query.then (/app/node_modules/mongoose/lib/query.js:4423:15)
// at process._tickCallback (internal/process/next_tick.js:68:7)
err.stack;
}
try {
await Band.findOne({ _id: badId }).exec();
} catch (err) { .exec()을 사용한 경우, 코드 어디서 에러가 났는지 말해준다.
// With `exec()`, the stack trace includes where in your code you
// called `exec()`. Below is the stack trace:
//
// CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises"
// at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11)
// at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21)
// at Context.<anonymous> (/app/test/index.test.js:138:42) <- 여기서 에러가 났음!
// at process._tickCallback (internal/process/next_tick.js:68:7)
err.stack;
}
exec()을 사용하도록 하자.
참고자료
Mongoose v8.0.0: Promises
Mongoose async operations, like .save() and queries, return thenables. This means that you can do things like MyModel.findOne({}).then() and await MyModel.findOne({}).exec() if you're using async/await. You can find the return type of specific operations i
mongoosejs.com
Mongoose v8.0.0: Model
Parameters: doc «Object» values for initial set [fields] «Object» optional object containing the fields that were selected in the query which returned this document. You do not need to set this parameter to ensure Mongoose handles your query projection
mongoosejs.com
Mongoose v8.0.0: Documents
Mongoose documents represent a one-to-one mapping to documents as stored in MongoDB. Each document is an instance of its Model. Document and Model are distinct classes in Mongoose. The Model class is a subclass of the Document class. When you use the Model
mongoosejs.com