Spring Data MongoDB를 활용해 한 도큐먼트(한 row)의 특정 배열 요소만 선택적으로 업데이트하는 방법을 알아보겠습니다.
일반적으로 find로 데이터를 조회한 후 save로 업데이트하는 방식은 편리하지만, 대량 데이터를 처리할 때는 updateOne 또는 updateMany를 이용한 벌크 업데이트가 성능 면에서 큰 이점을 제공합니다. 이번 포스팅에서는 MongoDB의 arrayFilters 옵션과 Spring Data MongoDB를 사용해 배열의 특정 요소만 업데이트하는 방법을 살펴봅니다.
여기서 $[elem0]와 $[elem1]는 자리표현자로, 실제 업데이트 시에는 arrayFilters 옵션에 지정된 조건을 만족하는 배열 요소의 실제 인덱스와 매핑됩니다. 예를 들어, "elem0.name": "나이키 에어 포스" 조건을 통해 MongoDB는 items 배열에서 이름이 “나이키 에어 포스”인 요소를 찾아 그 인덱스에 해당하는 위치에 $[elem0]를 매핑하여 업데이트를 적용합니다. 동일한 방식으로 "elem1.name": "나이키 후드" 조건도 적용됩니다.
실제 상황에서는 배열 요소를 업데이트할 때 name뿐 아니라 category 등 복합 조건으로 필터링해야 하는 경우가 있습니다. 예를 들어, 아래 MongoDB 쿼리는 배열의 조건을 복합 키로 찾아 “나이키 에어 포스”는 신발, “나이키 후드”는 상의인 경우에만 업데이트하도록 합니다.
Spring Data MongoDB에서는 동일한 자리표현자(예: elem0)에 대해 두 번의 filterArray 호출이 이루어지면, 내부적으로 각각 별도의 Document가 생성되어 중복된 최상위 키가 되어 버립니다. 결과적으로 원하는 복합 조건의 arrayFilters가 아닌,
overridefunupdateItems(form: OrderItemQueryForm.UpdateItem) { val query = Query(Criteria.where("_id").`is`(form.orderItem)) val update = Update() val arrayFilters = mutableListOf<Document>()
// 각 항목마다 자리표현자(elem0, elem1, …)를 생성하여 업데이트할 필드와 조건 Document를 구성 form.items.forEachIndexed { index, item -> // 예: "items.$[elem0].price": item.price update.set("items.\$[elem$index].price", item.price) // 복합 조건 Document: { "elem0.name": "나이키 에어 포스", "elem0.category": "신발" } arrayFilters.add( Document("elem${index}.name", item.name) .append("elem${index}.category", item.category) ) }
// 커스텀 UpdateDefinition 생성 – 업데이트 문서와 arrayFilters 옵션을 분리하여 전달합니다. val updateApplyArrayFilters = UpdateWithArrayFilters(update, arrayFilters.toList()) mongoTemplate.updateFirst(query, updateApplyArrayFilters, documentClass) }
이 방식으로 업데이트를 수행하면 최종적으로 MongoDB에 전송되는 명령은 다음과 같습니다.
bulkOps를 사용하면 대량 데이터 처리 시 업데이트 성능이 크게 향상됩니다. 위 그림에서도 볼 수 있듯이, 단일 업데이트에 비해 벌크 업데이트를 적용할 경우 처리 속도가 현저히 개선됩니다.
아래 코드는 BulkOperations를 활용하여 여러 도큐먼트에 대해 벌크 업데이트를 수행하는 편의 기능을 제공합니다. MongoCustomRepositorySupport 추상 클래스는 bulkUpdateDefinition 메서드를 통해, Query와 UpdateDefinition 생성자를 담은 람다 리스트를 받아 BulkOperations 객체에 각 업데이트를 추가한 후 일괄 실행합니다.
OrderItemCustomRepositoryImpl에서는 bulkUpdateDefinition 메서드를 사용하여, 여러 업데이트 폼을 반복 처리합니다. 각 폼마다 _id 조건의 Query와, Update 및 복합 조건의 arrayFilters를 적용한 커스텀 UpdateDefinition(UpdateWithArrayFilters)을 생성하여 BulkOperations에 추가합니다.
Spring Data MongoDB에서 배열의 특정 요소만 업데이트하기 위해서는 updateOne/updateMany와 arrayFilters 옵션을 활용하는 것이 성능 면에서 매우 유리합니다.
특히, 배열 요소를 복합 조건(예: name과 category)으로 필터링해야 하는 경우, 단순한 filterArray 호출로는 원하는 결과를 얻기 어려우므로 커스텀 UpdateDefinition(예: UpdateWithArrayFilters)을 활용하여 업데이트 문서와 arrayFilters 옵션을 분리해 전달하는 방법을 사용할 수 있습니다.
본 포스팅에서는 MongoDB 업데이트 쿼리와 이를 Spring Data MongoDB에서 구현하는 방법을 살펴보았으며, 실제 테스트 코드까지 확인해 보았습니다. 대량 데이터 처리나 업데이트가 빈번한 애플리케이션에서는 find 후 save 방식 대신 벌크 업데이트를 적극 활용하여 성능 최적화를 고려해 보시기 바랍니다.