Promises
개요
Node.js 드라이버는 비동기 Javascript API를 사용하여 MongoDB 클러스터와 통신합니다.
비동기 JavaScript 사용하면 처리 스레드가 사용 가능해질 때까지 기다리지 않고 작업을 실행할 수 있습니다. 이렇게 하면 장기 실행 작업을 실행할 때 애플리케이션이 응답하지 않는 것을 방지할 수 있습니다. 비동기 JavaScript에 대한 자세한 내용은 비동기 JavaScript에 대한 MDN 웹 문서를 참조하세요.
이 섹션에서는 Node.js 드라이버와 함께 사용하여 MongoDB 클러스터에 대한 메서드 호출 결과에 액세스할 수 있는 Promises
에 대해 설명합니다.
Promises
Promise는 비동기 메서드 호출에서 반환되는 객체로, 이를 통해 래핑된 작업의 최종 성공 또는 실패에 대한 정보에 액세스할 수 있습니다. 작업이 아직 실행 중인 경우 Promise는 보류 상태이고, 작업이 성공적으로 완료된 경우 이행됨, 작업이 예외를 발생시킨 경우 거부됨 상태가 됩니다. Promise 및 관련 용어에 대한 자세한 내용은 Promise에 대한 MDN 문서를 참조하세요.
findOneAndUpdate()
및 countDocuments()
와 같이 MongoDB cluster와 통신하는 대부분의 드라이버 메서드는 Promise 객체를 반환하며 작업의 성공 또는 실패를 처리하는 논리를 이미 포함하고 있습니다.
then()
메서드를 추가하여 Promise가 완료(Fulfilled) 또는 거부(Rejected) 상태에 도달하면 실행되는 고유한 논리를 정의할 수 있습니다. then()
의 첫 번째 매개변수는 Promise가 완료 상태에 도달할 때 호출되는 메서드이고, 선택적 두 번째 매개변수는 Rejected 상태에 도달할 때 호출되는 메서드입니다. then()
메서드는 then()
메서드를 추가로 더할 수 있는 Promise를 반환합니다.
Promise에 then()
메서드를 하나 이상 추가하면 각 호출은 실행 결과를 다음 메서드로 전달합니다. 해당 패턴을 Promise 체인이라고 합니다. 다음 코드 스니펫은 단일 then()
메서드를 추가하여 Promise 체인의 예를 보여줍니다.
collection .updateOne({ name: "Mount McKinley" }, { $set: { meters: 6190 } }) .then( res => console.log(`Updated ${res.result.n} documents`), err => console.error(`Something went wrong: ${err}`), );
거부됨 상태로 전환되는 Promise만 처리하려면 then()
에 null
첫 번째 매개변수를 전달하는 대신 catch()
메서드를 사용합니다. catch()
메서드는 Promise가 거부됨 상태로 전환될 때 실행되는 단일 콜백을 허용합니다.
catch()
메서드는 발생한 예외를 처리하기 위해 Promise 체인 끝에 추가되는 경우가 많습니다. 다음 코드 스니펫은 Promise 체인 끝에 catch()
메서드를 추가하는 방법을 보여줍니다.
deleteOne({ name: "Mount Doom" }) .then(result => { if (result.deletedCount !== 1) { throw "Could not find Mount Doom!"; } return new Promise((resolve, reject) => { ... }); }) .then(result => console.log(`Vanquished ${result.quantity} Nazgul`)) .catch(err => console.error(`Fatal error occurred: ${err}`));
참고
드라이버의 특정 메서드(예: find()
)는 Promise 대신 Cursor
을(를) 반환합니다. 각 메서드가 반환하는 유형을 확인하려면 Node.js API 문서를 참조하세요.
Await
async
함수를 사용하는 경우 Promise에 await
연산자를 사용하여 Promise가 완료(Fulfilled) 또는 거부(Rejected) 상태에 도달하여 반환할 때까지 추가 실행을 일시 중지할 수 있습니다. await
연산자는 Promise의 해결을 기다리므로, Promise 체인 대신 이 연산자를 사용하여 로직을 순차적으로 실행할 수 있습니다. 다음 코드 스니펫은 await
를 사용하여 첫 번째 Promise 체인 예시와 동일한 로직을 실행합니다.
async function run() { ... try { res = await myColl.updateOne( { name: "Mount McKinley" }, { $set: { meters: 6190 } }, ); console.log(`Updated ${res.result.n} documents`); } catch (err) { console.error(`Something went wrong: ${err}`); } }
자세한 내용은 대기에 대한 MDN 문서를 참조하세요.
운영 고려 사항
async
메서드를 사용할 때 흔히 저지르는 실수 중 하나는 Promise 객체가 아닌 결과 값을 얻기 위해 프로미스에 await
연산자를 사용하는 것을 잊는 것입니다. 다음 예시를 참고합니다. 해당 예시에서는 hasNext()
를 사용해 커서를 반복하는데, 이는 추가 결과가 존재하는지를 나타내는 불(boolean)로 완료되는 Promise를 반환합니다. 또한 next()
는 다음 커서가 가리키는 다음 항목으로 완료되는 Promise를 반환합니다.
async function run() { ... // WARNING: this snippet may cause an infinite loop const cursor = myColl.find(); while (cursor.hasNext()) { console.log(cursor.next()); } }
hasNext()
에 대한 호출은 Promise
를 반환하므로 조건문은 완료되는 값에 관계없이 true
를 반환합니다.
다음 코드 스니펫에 표시된 것처럼 await
로 코드를 변경하여 next()
로만 호출하면 MongoError: Cursor is closed
오류가 발생합니다.
async function run() { ... // WARNING: this snippet throws a MongoError const cursor = myColl.find(); while (cursor.hasNext()) { console.log(await cursor.next()); } }
next()
결과가 반환될 때까지는 hasNext()
가 호출되지 않지만 hasNext()
를 호출하면 앞선 예와 마찬가지로 완료 값이 아닌 true
로 평가되는 Promise가 반환됩니다. 코드는 이미 결과를 반환하고 결과적으로 닫힌 커서에 대해 next()
호출하려고 시도합니다.
다음 예시와 같이 hasNext()
에 대한 호출을 await
만 호출하도록 코드를 변경하면 콘솔에 문서 객체가 아닌 Promise 객체가 인쇄됩니다.
async function run() { ... // WARNING: this snippet prints Promises instead of the objects they resolve to const cursor = myColl.find(); while (await cursor.hasNext()) { console.log(cursor.next()); } }
hasNext()
및 next()
메서드 호출 전에 await
를 사용하여 다음 코드에 설명된 대로 올바른 반환 값을 사용하고 있는지 확인하세요.
async function run() { ... const cursor = myColl.find(); while (await cursor.hasNext()) { console.log(await cursor.next()); } }