개발자는 오늘도 달립니다.
[MongoDB] Mongoose(몽구스) 메뉴얼 - 프로미스 편 본문
개발 프로그램에서 몽고DB를 핸들링 하기 위해서는 벤더사에서 제공하는 API를 활용해야 합니다. 이번 포스팅은 그 중 몽구스라는 API를 ECMA 5 프로미스(promise)를 사용하는 방법에 대해 알아보겠습니다.
기본적으로 몽고DB(노드용 드라이버)는 콜백으로 결과값을 반환합니다. 콜백은 간단하지만, 다들 아시다시피 중첩되었을 경우 콜백 지옥이 발생할 수 있다는 문제점이 있습니다.
따라서 콜백 대신 프로미스를 보통 많이 사용합니다. 게다가 프로미스는 자바스크립트와 노드에서 비동기 API로 밀어주고 있기 때문에 유망합니다. ES2017에서 나온 async/await도 사용할 수 있기 때문에 익숙해진다면 콜백보다 훨씬 가독성이 좋습니다.
몽고DB에 프로미스를 적용하기 위한 많은 라이브러리들이 있습니다. 몽구스도 역시 프로미스를 지원합니다. ES2015의 promise를 사용하지만 다른 프로미스(bluebird같은)로 바꿀 수도 있습니다.
mongoose.Promise = require("bluebird");
mongoose.connect(db, { keepAlive: 300000, connectTimeoutMS: 30000 }, (err) => {
if (err) {
console.log(`===> Error connecting to ${db}`);
console.log(`Reason: ${err}`);
} else {
console.log(`===> Succeeded in connecting to ${db}`);
}
});
첫 줄을 보시면 DB 연결 전에 bluebird를 mongoose.Promise에 대입했습니다. 기본 프로미스를(ES2015) 블루버드의 프로미스로 교체한 것입니다. 첫 줄을 넣지 않으면 그냥 ES2015 프로미스를 사용합니다. 특별한 이유가 있지 않다면 그냥 ES2015 프로미스를 쓰시는 게 좋습니다.
이제 코드를 다음과 같이 사용하면 됩니다. 다음과 같은 콜백 함수들이 있을 때
Users.findOne({ name: "zerocho" }, (err, result) => {
if (err) {
throw err;
}Users.update(
{ name: result.name },
{ updated: true },
(err, updateResult) => {
if (err) {
throw err;
}
console.log(updateResult);
}
);
});
프로미스로 바꾸면 아래와 같습니다.
Users.findOne({ name: "zerocho" })
.exec()
.then((result) => {
return Users.update({ name: result.name }, { updated: true }).exec();
})
.then((updatedResult) => {
console.log(updatedResult);
})
.catch((err) => {
console.error(err);
});
프로미스의 장점(코드 중첩 완화, 조건부 쿼리, 에러 한 번에 처리 등)들을 모두 이용할 수 있기 때문에 편리합니다.
첫 줄 User.findOne({ name: 'zerocho' })는 쿼리입니다. 몽구스 4버전부터 쿼리가 then을 지원합니다. 3버전까지는 쿼리를 프로미스로 만들기 위해서 뒤에 exec()을 필수로 붙여주어야 했습니다. 4버전부터는 필수는 아니지만 그래도 붙이는 것을 추천합니다.
const newUser = new Users({ name: "zerocho", updated: false });
newUser
.save()
.then((savedUser) => {
console.log(savedUser);
})
.catch((err) => {
console.error(err);
});
객체를 생성하는 메소드인 save()도 자체적으로 promise입니다. (save는 exec을 붙이지 않습니다)
나중에 async/await으로 전환도 가능합니다.
try {
const result = await Users.findOne({ name: "zerocho" }).exec();
const updatedResult = await Users.update({ name: result.name }).exec();
console.log(updatedResult);
} catch (err) {
console.error(err);
}
프로미스를 사용했을 때 장점을 소개해보겠습니다. 코드 중첩 완화와 에러 한 번에 처리하는 것은 위의 예제에서도 쉽게 파악할 수 있습니다. 조건부 쿼리할 때 매우 편리한데요.
let promise;
// 로그인 상태면
if (req.isAuthenticated()) {
// 내 정보 조회
promise = Users.findOne({ name: req.user._id }).exec();
} else {
// 아니면
promise = Users.find({ ... }).exec();
}
// 전체 조회
promise.then( ... ).catch( ... );
이런 식으로 활용이 가능합니다. Promise가 변수에 담기기 때문에 가능한 방식입니다. then 안의 return에서도 분기 처리가 가능합니다.
promise.then(() => { return req.isAuthenticated() ? Users.findOne(...).exec() : Users.find({}).exec(); });
프로미스만 잘 활용해도 몽고DB 비동기 때문에 발생하는 문제를 어느 정도 극복할 수 있습니다!
'데이터베이스 > 몽고DB' 카테고리의 다른 글
[MongoDB] 몽고DB 설치하기 - Windows (0) | 2021.11.05 |
---|---|
[MongoDB] 컬렉션 조회 함수 조회 하기 및 특정 컬렉션 빼고 삭제 하기! (0) | 2021.02.09 |
[MongoDB] 몽고DB 샤딩(Sharding) 구성하기! (1) | 2021.01.29 |
[MongoDB] 몽고 DB 설치 및 세팅 With CentOS (0) | 2020.10.07 |
[MongoDB] 비교, 논리 쿼리 연산자 (0) | 2020.07.10 |