AI 에이전트의 경우: 문서 인덱스는 https://www.mongodb.com/ko-kr/docs/llms.txt에서 사용할 수 있으며, 모든 페이지의 마크다운 버전은 어떤 URL 경로에 .md를 추가하여 사용할 수 있습니다.
See how MongoDB 9.0 delivers up to 2x higher throughput.
MongoDB Branding Shape
Register now >
Docs Menu

복합 작업

이 가이드에서는 Scala 드라이버를 사용하여 복합 작업을 수행하는 방법을 학습할 수 있습니다.

복합 작업은 하나의 원자 작업으로 수행되는 읽기 작업과 쓰기 작업으로 구성됩니다. 원자 작업은 완전히 완료되거나 전혀 완료되지 않는 작업입니다.

원자 작업은 코드에서의 경쟁 상태를 피하는 데 도움이 될 수 있습니다. 경쟁 상태는 코드의 동작이 제어할 수 없는 이벤트의 순서에 따라 달라질 때 발생합니다.

MongoDB는 다음과 같은 복합 연산을 지원합니다.

  • 하나의 문서 찾기 및 업데이트

  • 하나의 문서 찾기 및 바꾸기

  • 문서 한 개 찾기 및 삭제

원자조작 및 원자성에 대한 자세한 내용은 MongoDB Server 매뉴얼의 원자성 및 트랜잭션을 참조하세요.

팁

트랜잭션

하나 이상의 문서를 읽고 쓰는 등 더 복잡한 작업을 원자적으로 수행해야 하는 경우 트랜잭션을 사용하세요. 트랜잭션을 사용하면 일연의 데이터베이스 명령을 원자 작업으로 수행할 수 있습니다. 트랜잭션에 대한 자세한 내용은 MongoDB Server 수동의 트랜잭션을 참조하세요.

이 섹션에서는 Scala 드라이버를 사용하여 복합 작업을 수행하는 방법을 설명합니다.

다음 예제에서는 두 개의 샘플 문서가 포함된 컬렉션을 사용합니다.

{"_id": {"$oid": "1"}, "food": "donut", "color": "green"}
{"_id": {"$oid": "2"}, "food": "pear", "color": "yellow"}

예시에서는 다음 collection 참조를 사용하여 각 문서를 Document 클래스의 인스턴스에 매핑합니다.

val database: MongoDatabase = mongoClient.getDatabase("compound_operations")
val collection: MongoCollection[Document] = database.getCollection("example")
val hotelCollection: MongoCollection[Document] = database.getCollection("rooms")

각 복합 작업은 일치하는 문서를 반환하는 SingleObservable[Document]을 반환하거나, 쿼리 필터와 일치하는 문서가 없는 경우 값을 반환하지 않고 완료됩니다. 쿼리 필터가 여러 문서와 일치하는 경우 메서드는 첫 번째 일치에 대해 작업하며, first은 해당 옵션 객체에 정렬 순서를 지정하지 않는 한 기본 순서를 의미합니다.

참고

쓰기 작업 후 문서 반환

기본적으로 복합 작업은 일치하는 문서를 반환한 다음 문서에 대해 쓰기 작업을 수행합니다. 해당 메서드에 옵션 객체를 전달하여 복합 작업이 먼저 쓰기 작업을 수행하도록 지시할 수 있습니다. 찾기 및 바꾸기 예시에서 이 구성을 볼 수 있습니다.

단일 문서를 찾아 업데이트하려면 MongoCollection 클래스의 findOneAndUpdate() 메서드를 사용합니다.

다음 예시에서는 findOneAndUpdate() 메서드를 사용하여 color 필드에 "green" 값이 있는 문서를 찾습니다. 그러한 다음에는 해당 문서의 food 필드를 "pizza"(으)로 업데이트합니다.

또한 FindOneAndUpdateOptions 인스턴스를 사용하여 다음 옵션을 지정합니다.

  • 쿼리와 일치하는 문서가 없는 경우 쿼리 필터에 지정된 문서를 삽입하는 업서트를 지정합니다.

  • MongoDB 인스턴스에서 이 작업의 최대 실행 시간을 5 초로 설정합니다. 작업에 시간이 오래 걸리면 findOneAndUpdate() 메서드는 오버저의 onError() 콜백을 통해 MongoExecutionTimeoutException를 발생시킵니다.

val filter = equal("color", "green")
val update = set("food", "pizza")
val options = new FindOneAndUpdateOptions()
.upsert(true)
.maxTime(5, TimeUnit.SECONDS)
val observable: SingleObservable[Document] =
collection.findOneAndUpdate(filter, update, options)
observable.subscribe(new Observer[Document] {
override def onNext(doc: Document): Unit = println(doc.toJson())
override def onError(e: Throwable): Unit = println(s"Failed: ${e.getMessage}")
override def onComplete(): Unit = println("Completed")
})

이 섹션에 사용된 메서드 및 클래스에 대한 자세한 내용은 다음 API 설명서를 참조하세요.

단일 문서를 찾아 바꾸려면 MongoCollection 클래스의 findOneAndReplace() 메서드를 사용합니다.

다음 예시에서는 findOneAndReplace() 메서드를 사용하여 color 필드에 "green" 값이 있는 문서를 찾습니다. 일치하는 문서 다음으로 대체합니다.

{"music": "classical", "color": "green"}

또한 FindOneAndReplaceOptions 인스턴스를 사용하여 반환된 문서가 대체 작업 후의 상태여야 함을 지정합니다.

val replaceFilter = equal("color", "green")
val replacement = Document(
"music" -> "classical",
"color" -> "green"
)
val replaceOptions = new FindOneAndReplaceOptions()
.returnDocument(ReturnDocument.AFTER)
val replaceObservable: SingleObservable[Document] =
collection.findOneAndReplace(replaceFilter, replacement, replaceOptions)
replaceObservable.subscribe(new Observer[Document] {
override def onNext(doc: Document): Unit = println(doc.toJson())
override def onError(e: Throwable): Unit = println(s"Failed: ${e.getMessage}")
override def onComplete(): Unit = println("Completed")
})

이 섹션에 사용된 메서드 및 클래스에 대한 자세한 내용은 다음 API 설명서를 참조하세요.

단일 문서를 찾아 삭제하려면 MongoCollection 클래스의 findOneAndDelete() 메서드를 사용합니다.

다음 예시 findOneAndDelete() 메서드를 사용하여 _id 필드 에서 가장 큰 값을 가진 문서 찾아 삭제 .

이 예제에서는 FindOneAndDeleteOptions 인스턴스를 사용하여 _id 필드에 내림차순 정렬을 지정합니다.

val deleteOptions = new FindOneAndDeleteOptions()
.sort(descending("_id"))
val deleteObservable: SingleObservable[Document] =
collection.findOneAndDelete(empty(), deleteOptions)
deleteObservable.subscribe(new Observer[Document] {
override def onNext(doc: Document): Unit = println(doc.toJson())
override def onError(e: Throwable): Unit = println(s"Failed: ${e.getMessage}")
override def onComplete(): Unit = println("Completed")
})

이 섹션에 사용된 메서드 및 클래스에 대한 자세한 내용은 다음 API 설명서를 참조하세요.

복합 작업은 여러 작업을 원자적으로 수행하여 경쟁 상태를 피할 수 있도록 도와줍니다.

이 섹션의 예시를 위해 방 하나로 호텔을 운영한다고 가정해 보세요. 작은 Scala 프로그램을 사용하여 객이 방에 체크인합니다.

다음 문서는 호텔 방을 나타냅니다.

{"_id": {"$oid": "1"}, "guest": null, "room": "Blue Room", "reserved": false}

다음 예시에서는 bookARoomUnsafe() 함수를 사용하여 게스트를 방에 체크인합니다. 이 함수는 찾기 작업과 업데이트 작업을 별도로 수행합니다.

def bookARoomUnsafe(guestName: String): Unit = {
val availableFilter = equal("reserved", false)
val findResults = Await.result(
hotelCollection.find(availableFilter).first().toFuture(),
Duration(10, TimeUnit.SECONDS)
)
if (Option(findResults).isEmpty) {
println(s"Sorry, we are booked, $guestName")
return
}
val room = findResults
val roomName = room.getString("room")
println(s"You got the $roomName, $guestName")
val reserveUpdate = combine(
set("reserved", true),
set("guest", guestName)
)
val roomFilter = equal("_id", room.get("_id").get)
Await.result(
hotelCollection.updateOne(roomFilter, reserveUpdate).toFuture(),
Duration(10, TimeUnit.SECONDS)
)
}

두 게스트 Jan과 Pat가 동시에 같은 객실을 예약하려고 한다고 가정해 보세요.

잔은 다음 출력을 확인합니다.

You got the Blue Room, Jan

패트는 다음 출력을 확인합니다.

You got the Blue Room, Pat

데이터베이스를 살펴보면 다음 문서가 표시됩니다.

{"_id": {"$oid": "1"}, "guest": "Jan", "room": "Blue Room", "reserved": true}

애플리케이션은 Pat와 Jan 모두에게 방을 예약했다고 알려주었지만 데이터베이스에는 Jan에 대한 예약만 표시됩니다. MongoDB가 수행한 작업 순서는 다음과 같습니다.

  1. 1월의 빈 방 찾기 및 반환

  2. 패트를 위한 빈 방 찾기 및 반환

  3. Pat의 예약으로 방 업데이트

  4. Jan의 예약으로 방 업데이트

Pat이 잠시 동안 방을 예약했지만 Jan의 업데이트 작업이 마지막으로 실행되어 Pat의 예약이 오버라이드되었습니다.

다음 예시에서는 bookARoomSafe() 함수를 사용하여 게스트를 방에 체크인합니다. 이 함수는 findOneAndUpdate()을(를) 사용하여 찾기 작업과 업데이트 작업을 단일 원자 조작으로 수행합니다.

def bookARoomSafe(guestName: String): Unit = {
val reserveUpdate = combine(
set("reserved", true),
set("guest", guestName)
)
val availableFilter = equal("reserved", false)
val room: Document = Await.result(
hotelCollection.findOneAndUpdate(availableFilter, reserveUpdate).toFuture(),
Duration(10, TimeUnit.SECONDS)
)
if (Option(room).isEmpty) {
println(s"Sorry, we are booked, $guestName")
return
}
val roomName = room.getString("room")
println(s"You got the $roomName, $guestName")
}

두 게스트 Jan과 Pat가 동시에 같은 객실을 예약하려고 한다고 가정해 보세요.

잔은 다음 출력을 확인합니다.

You got the Blue Room, Jan

패트는 다음 출력을 확인합니다.

Sorry, we are booked, Pat

데이터베이스를 살펴보면 다음 문서가 표시됩니다.

{"_id": {"$oid": "1"}, "guest": "Jan", "room": "Blue Room", "reserved": true}

Jan이 방을 예약했고 Pat는 사용 가능한 방이 없다는 올바른 메시지를 받았습니다. MongoDB가 수행한 작업 순서는 다음과 같습니다.

  1. 1월의 빈 자리를 찾아 예약합니다.

  2. Pat을 위한 빈 방을 찾아 예약하려고 합니다.

  3. 빈 방을 찾지 못하고 값을 발생하지 않고 완료합니다.

findOneAndUpdate()는 찾기 및 업데이트 원자적으로 수행하기 때문에, 월의 예약이 완료된 후에는 패트의 작업을 사용 가능한 방으로 읽을 수 없습니다.

중요

쓰기 락

MongoDB 인스턴스 는 복합 작업 기간 동안 수정하려는 문서 에 쓰기 잠금 (write lock) 설정합니다.

이 섹션에 사용된 메서드 및 클래스에 대한 자세한 내용은 다음 API 설명서를 참조하세요.