여러 개의 쓰기(write) 작업을 한 번에 처리해야 할 때는 MongoDB의 bulkWrite() 메서드를 사용하는 것이 가장 효율적입니다. bulkWrite()는 업데이트, 삽입, 삭제 등 다양한 연산을 하나의 요청으로 묶어 실행할 수 있게 해주며, 이 글에서는 이를 활용해 조건부 upsert(조건에 맞으면 업데이트, 없으면 삽입)를 수행하는 방법을 단계별로 살펴보겠습니다.
1. 샘플 컬렉션 생성
먼저 테스트용 컬렉션 demo428을 만들고 문서를 여러 개 삽입해 보겠습니다.
> db.demo428.insertOne({ "Name" : "Chris", "Age" : 21 });
{
"acknowledged" : true,
"insertedId" : ObjectId("5e75f428bbc41e36cc3cae83")
}
> db.demo428.insertOne({ "Name" : "Chris", "Age" : 23 });
{
"acknowledged" : true,
"insertedId" : ObjectId("5e75f429bbc41e36cc3cae84")
}
> db.demo428.insertOne({ "Name" : "David", "Age" : 22 });
{
"acknowledged" : true,
"insertedId" : ObjectId("5e75f42abbc41e36cc3cae85")
}
> db.demo428.insertOne({ "Name" : "David", "Age" : 21 });
{
"acknowledged" : true,
"insertedId" : ObjectId("5e75f42abbc41e36cc3cae86")
}저장된 전체 문서 조회
find() 메서드를 사용하면 컬렉션에 저장된 모든 문서를 확인할 수 있습니다.
> db.demo428.find();
위 쿼리는 다음과 같은 결과를 출력합니다.
{ "_id" : ObjectId("5e75f428bbc41e36cc3cae83"), "Name" : "Chris", "Age" : 21 }
{ "_id" : ObjectId("5e75f429bbc41e36cc3cae84"), "Name" : "Chris", "Age" : 23 }
{ "_id" : ObjectId("5e75f42abbc41e36cc3cae85"), "Name" : "David", "Age" : 22 }
{ "_id" : ObjectId("5e75f42abbc41e36cc3cae86"), "Name" : "David", "Age" : 21 }2. 조건부 upsert 쿼리 실행하기
다음은 bulkWrite()를 사용해 특정 조건에 맞는 문서를 업데이트하고, 동시에 새 문서를 삽입하는 예제입니다. updateOne 연산은 Name이 "David"이고 Age가 22인 문서를 찾아 Info 필드를 설정하고, insertOne 연산은 Carol이라는 새 문서를 추가합니다. 두 번째 인자로 전달한 { "ordered": false } 옵션은 연산들이 순서에 얽매이지 않고 실행되도록 하여 성능을 높여줍니다.
> db.demo428.bulkWrite(
... [
... { "updateOne": {
... "filter": { "Name": "David", "Age": 22 },
... "update": { "$set": { "Info": {Name:"John"} } }
... }},
... { "insertOne": {
... "document": { "Name": "Carol", "Age": 22, "Info": {Name:"John"}}
... }}
... ],
... { "ordered": false }
... )
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 1,
"matchedCount" : 1,
"upsertedCount" : 0,
"insertedIds" : {
"1" : ObjectId("5e75f448bbc41e36cc3cae87")
},
"upsertedIds" : {
}
}
응답 결과를 보면 matchedCount가 1로 조건에 맞는 기존 문서 하나가 업데이트되었고, insertedCount가 1로 새 문서가 정상적으로 삽입되었음을 확인할 수 있습니다.
업데이트 결과 확인
쿼리 실행 후 다시 find() 메서드로 전체 문서를 조회해 보겠습니다.
> db.demo428.find();
{ "_id" : ObjectId("5e75f428bbc41e36cc3cae83"), "Name" : "Chris", "Age" : 21 }
{ "_id" : ObjectId("5e75f429bbc41e36cc3cae84"), "Name" : "Chris", "Age" : 23 }
{ "_id" : ObjectId("5e75f42abbc41e36cc3cae85"), "Name" : "David", "Age" : 22, "Info" : { "Name" : "John" } }
{ "_id" : ObjectId("5e75f42abbc41e36cc3cae86"), "Name" : "David", "Age" : 21 }
{ "_id" : ObjectId("5e75f448bbc41e36cc3cae87"), "Name" : "Carol", "Age" : 22, "Info" : { "Name" : "John" } }출력 결과를 보면 Name이 "David"이고 Age가 22인 기존 문서에만 Info 필드가 추가되었고, Age가 21인 David 문서는 그대로 유지된 것을 확인할 수 있습니다. 또한 Carol의 새 문서도 Info 필드와 함께 정상적으로 삽입되었습니다.
정리
bulkWrite()를 사용하면 updateOne, insertOne 등 여러 연산을 하나의 요청으로 묶어 처리할 수 있어 네트워크 왕복 횟수가 줄어들고 전체적인 성능이 향상됩니다. 또한 matchedCount, insertedCount, upsertedCount 같은 응답 필드를 통해 각 연산의 실행 결과를 명확하게 파악할 수 있어 대량 데이터 처리 시 매우 유용합니다.