본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 27. · 0 Views
MongoDB 업데이트 연산자 완벽 가이드
MongoDB에서 문서를 수정할 때 사용하는 다양한 업데이트 연산자를 학습합니다. $set부터 arrayFilters까지 실무에서 자주 쓰이는 연산자들을 예제와 함께 알아봅니다.
목차
1. $set과 $unset
김개발 씨는 회원 정보 수정 기능을 구현하던 중 난감한 상황에 빠졌습니다. 사용자가 닉네임만 바꾸고 싶어하는데, 전체 문서를 덮어쓰지 않고 특정 필드만 수정하려면 어떻게 해야 할까요?
선배 개발자가 다가와 한마디 했습니다. "MongoDB의 $set 연산자를 써봐요."
$set 연산자는 문서의 특정 필드 값을 수정하거나 새로운 필드를 추가하는 연산자입니다. 마치 연필로 노트의 특정 부분만 지우고 다시 쓰는 것과 같습니다.
반대로 $unset은 필드 자체를 삭제할 때 사용합니다. 이 두 연산자를 이해하면 문서의 부분 수정이 자유로워집니다.
다음 코드를 살펴봅시다.
// 사용자의 닉네임과 나이를 수정합니다
db.users.updateOne(
{ _id: ObjectId("507f1f77bcf86cd799439011") },
{
$set: {
nickname: "코딩마스터",
age: 28,
"profile.bio": "백엔드 개발자입니다" // 중첩 필드도 수정 가능
}
}
);
// 더 이상 필요 없는 필드를 삭제합니다
db.users.updateOne(
{ _id: ObjectId("507f1f77bcf86cd799439011") },
{ $unset: { tempPassword: "", oldEmail: "" } } // 값은 무엇이든 상관없음
);
김개발 씨는 입사 첫 주에 MongoDB를 처음 접했습니다. 관계형 데이터베이스만 다뤄봤던 터라 UPDATE 문 대신 무엇을 써야 할지 막막했습니다.
처음에는 findOne으로 문서를 가져온 뒤 JavaScript에서 수정하고 다시 저장하는 방식을 사용했습니다. 하지만 이 방식에는 문제가 있었습니다.
동시에 여러 사용자가 같은 문서를 수정하면 데이터가 덮어써지는 경쟁 상태가 발생했습니다. 선배 박시니어 씨가 이 코드를 보고 고개를 저었습니다.
"김 개발자, 이렇게 하면 안 돼요. $set 연산자를 사용해야 합니다." $set 연산자는 마치 문서의 특정 칸만 수정하는 마법 펜과 같습니다.
전체 문서를 가져와서 수정하고 다시 저장하는 것이 아니라, 데이터베이스에 직접 "이 필드만 이 값으로 바꿔줘"라고 명령하는 것입니다. 이 방식의 장점은 원자성이 보장된다는 것입니다.
여러 사용자가 동시에 같은 문서의 다른 필드를 수정해도 충돌이 발생하지 않습니다. A 사용자가 닉네임을 바꾸는 동안 B 사용자가 프로필 사진을 바꿔도 서로 영향을 주지 않습니다.
$set의 또 다른 강력한 기능은 중첩된 필드도 수정할 수 있다는 점입니다. "profile.bio"처럼 점 표기법을 사용하면 객체 안의 객체도 정밀하게 수정할 수 있습니다.
마치 러시아 인형 안의 특정 인형만 꺼내 색칠하는 것과 같습니다. 반면 $unset은 필드 자체를 문서에서 제거합니다.
값을 null로 설정하는 것과는 다릅니다. null은 필드가 존재하되 값이 없는 것이고, $unset은 필드 자체가 사라지는 것입니다.
회원 탈퇴 시 개인정보를 완전히 삭제해야 할 때 유용합니다. 주의할 점이 있습니다.
$unset을 사용할 때 값은 무엇을 넣든 상관없습니다. 관례적으로 빈 문자열 ""을 사용하지만, 1이나 true를 넣어도 동작은 같습니다.
중요한 것은 어떤 필드를 삭제할지 키를 지정하는 것입니다. 실무에서는 이 두 연산자를 함께 사용하는 경우가 많습니다.
예를 들어 사용자가 전화번호를 변경하면서 기존 인증 토큰을 삭제해야 할 때, $set과 $unset을 한 번의 업데이트 명령에 함께 넣을 수 있습니다. 김개발 씨는 이제 자신감 있게 회원 정보 수정 API를 완성했습니다.
코드가 훨씬 간결해졌고, 무엇보다 동시성 문제에서 해방되었습니다.
실전 팁
💡 - $set으로 존재하지 않는 필드를 지정하면 해당 필드가 새로 생성됩니다
- $unset은 배열 요소에 사용하면 요소를 삭제하는 대신 null로 바꿉니다
2. $inc와 $mul 수치 연산
조회수를 1 증가시키는 기능을 구현하던 김개발 씨가 고민에 빠졌습니다. 현재 조회수를 읽어서 1을 더하고 다시 저장하자니 동시 접속 상황에서 숫자가 꼬일 것 같았습니다.
이때 박시니어 씨가 지나가며 한마디 던졌습니다. "그건 $inc로 한 방에 해결돼요."
$inc는 숫자 필드의 값을 증가시키거나 감소시키는 연산자입니다. $mul은 곱셈 연산을 수행합니다.
마치 은행에서 잔고를 직접 더하고 빼는 것처럼, 현재 값을 읽지 않고도 수치 연산이 가능합니다. 원자적 연산이 보장되어 동시성 문제를 걱정할 필요가 없습니다.
다음 코드를 살펴봅시다.
// 게시글 조회수를 1 증가시킵니다
db.posts.updateOne(
{ _id: ObjectId("507f1f77bcf86cd799439011") },
{ $inc: { viewCount: 1 } }
);
// 포인트를 100 차감하고 구매 횟수를 1 증가시킵니다
db.users.updateOne(
{ userId: "user123" },
{ $inc: { points: -100, purchaseCount: 1 } }
);
// 상품 가격을 10% 인상합니다 (1.1을 곱함)
db.products.updateOne(
{ sku: "ITEM001" },
{ $mul: { price: 1.1 } }
);
김개발 씨가 처음 조회수 기능을 구현했을 때 코드는 이랬습니다. 먼저 게시글을 조회하고, 조회수에 1을 더한 뒤, 다시 저장합니다.
논리적으로는 완벽해 보였습니다. 하지만 서비스 오픈 후 이상한 현상이 발생했습니다.
분명 100명이 조회했는데 조회수가 87로 표시되는 것이었습니다. 동시에 여러 사용자가 접속하면서 값이 덮어써진 것입니다.
$inc 연산자는 이런 문제를 근본적으로 해결합니다. 현재 값이 무엇인지 읽어올 필요 없이 "지금 값에서 1을 더해줘"라고 데이터베이스에 직접 명령합니다.
마치 은행 창구에서 "현재 잔고가 얼마든 100만원을 입금해주세요"라고 말하는 것과 같습니다. 이 연산은 원자적으로 수행됩니다.
즉, 다른 연산이 끼어들 틈이 없습니다. 100명이 동시에 조회해도 조회수는 정확히 100이 증가합니다.
데이터베이스 레벨에서 락을 걸고 연산을 수행하기 때문입니다. $inc는 음수도 사용할 수 있습니다.
포인트 차감이나 재고 감소처럼 값을 줄여야 할 때 유용합니다. $inc: { stock: -1 }처럼 음수를 넣으면 현재 값에서 해당 숫자만큼 빼줍니다.
한 번의 업데이트에 여러 필드를 동시에 증감시킬 수도 있습니다. 결제 완료 시 포인트를 차감하면서 동시에 구매 횟수를 증가시키는 것처럼 말입니다.
이 모든 연산이 하나의 트랜잭션처럼 원자적으로 처리됩니다. $mul은 곱셈 연산자입니다.
가격을 일괄적으로 10% 인상하거나 20% 할인할 때 유용합니다. $mul: { price: 0.8 }은 가격을 80%로 만드는, 즉 20% 할인하는 효과가 있습니다.
주의할 점이 있습니다. $inc나 $mul을 존재하지 않는 필드에 사용하면 어떻게 될까요?
놀랍게도 MongoDB는 해당 필드를 0으로 초기화한 뒤 연산을 수행합니다. 따라서 조회수 필드가 없는 새 게시글에 $inc를 사용해도 오류가 발생하지 않습니다.
그러나 문자열 필드에 $inc를 사용하면 오류가 발생합니다. 숫자 연산자는 숫자 타입의 필드에만 사용할 수 있습니다.
스키마 설계 시 이 점을 염두에 두어야 합니다. 김개발 씨는 조회수 로직을 $inc로 바꾼 뒤 더 이상 숫자가 꼬이는 문제를 겪지 않았습니다.
코드도 훨씬 간결해져서 일석이조였습니다.
실전 팁
💡 - $inc에 0을 넣으면 아무 변화가 없지만, 필드가 없으면 0으로 생성됩니다
- $mul과 $inc를 같은 필드에 동시에 사용하면 오류가 발생합니다
3. $push와 $pull 배열 조작
김개발 씨는 사용자의 관심 태그 목록을 관리하는 기능을 구현하고 있었습니다. 배열에 새 태그를 추가하거나 기존 태그를 삭제해야 하는데, 매번 전체 배열을 읽어서 수정하고 저장하자니 비효율적으로 느껴졌습니다.
"배열 요소만 쏙쏙 추가하고 삭제할 수 있는 방법이 없을까요?"
$push는 배열에 새 요소를 추가하는 연산자이고, $pull은 조건에 맞는 요소를 삭제합니다. 마치 줄 서 있는 대기열에 사람을 추가하거나 빼는 것과 같습니다.
배열 전체를 건드리지 않고 원하는 요소만 조작할 수 있어 효율적입니다.
다음 코드를 살펴봅시다.
// 사용자의 관심 태그에 새 태그를 추가합니다
db.users.updateOne(
{ userId: "user123" },
{ $push: { tags: "MongoDB" } }
);
// 여러 태그를 한 번에 추가합니다
db.users.updateOne(
{ userId: "user123" },
{ $push: { tags: { $each: ["Node.js", "Express", "React"] } } }
);
// 특정 태그를 삭제합니다
db.users.updateOne(
{ userId: "user123" },
{ $pull: { tags: "Express" } }
);
// 조건에 맞는 객체를 배열에서 삭제합니다
db.orders.updateOne(
{ orderId: "order001" },
{ $pull: { items: { productId: "ITEM003", quantity: { $lt: 2 } } } }
);
김개발 씨가 처음 배열 조작 기능을 구현했을 때는 이런 방식이었습니다. 먼저 사용자 문서를 조회하고, tags 배열에 push 메서드로 새 태그를 추가한 뒤, 전체 문서를 다시 저장합니다.
작동은 하지만 뭔가 찝찝했습니다. 박시니어 씨가 코드를 보더니 말했습니다.
"이 방식은 네트워크 왕복이 두 번이에요. 조회 한 번, 저장 한 번.
MongoDB의 $push를 쓰면 한 번에 끝나요." $push 연산자는 배열의 끝에 새 요소를 추가합니다. 현재 배열 내용이 무엇인지 알 필요 없이 "여기에 이 요소를 붙여줘"라고 명령하면 됩니다.
데이터베이스가 알아서 배열 끝에 추가해줍니다. 여러 요소를 한 번에 추가하고 싶다면 $each 수정자를 함께 사용합니다.
$push: { tags: { $each: ["A", "B", "C"] } }처럼 작성하면 세 개의 태그가 한꺼번에 추가됩니다. 반복문을 돌릴 필요가 없습니다.
$push와 함께 사용할 수 있는 유용한 수정자들이 더 있습니다. $position은 추가할 위치를 지정하고, $slice는 배열 크기를 제한하며, $sort는 추가 후 정렬합니다.
이를 조합하면 "최근 10개의 검색어만 시간순으로 유지"같은 기능을 구현할 수 있습니다. $pull은 반대로 배열에서 요소를 삭제합니다.
단순히 값이 일치하는 요소를 삭제할 수도 있고, 복잡한 조건을 지정할 수도 있습니다. $pull: { scores: { $lt: 60 } }은 60점 미만의 모든 점수를 삭제합니다.
객체 배열에서도 $pull이 유용합니다. 장바구니에서 특정 상품을 삭제하거나, 댓글 목록에서 특정 사용자의 댓글을 모두 삭제할 때 사용합니다.
조건만 정확히 지정하면 일치하는 모든 요소가 삭제됩니다. 비슷한 연산자로 $pop도 있습니다.
$pop은 배열의 첫 번째 또는 마지막 요소를 삭제합니다. $pop: { array: 1 }은 마지막 요소를, $pop: { array: -1 }은 첫 번째 요소를 삭제합니다.
큐나 스택 구조를 구현할 때 유용합니다. 주의할 점이 있습니다.
$pull은 조건에 맞는 모든 요소를 삭제합니다. 첫 번째로 일치하는 것만 삭제하고 싶다면 별도의 로직이 필요합니다.
이 점을 간과하면 예상치 못한 데이터 손실이 발생할 수 있습니다. 김개발 씨는 이제 배열 조작이 필요할 때마다 $push와 $pull을 자연스럽게 사용합니다.
코드가 간결해졌을 뿐만 아니라 성능도 개선되었습니다.
실전 팁
💡 - $push와 $slice를 함께 쓰면 배열 크기를 제한할 수 있습니다 (예: 최근 알림 100개만 유지)
- $pull은 조건에 맞는 모든 요소를 삭제하므로 신중하게 사용하세요
4. $addToSet 중복 방지
김개발 씨는 사용자의 좋아요 목록을 구현하면서 고민이 생겼습니다. 같은 게시글에 좋아요를 여러 번 누르면 배열에 중복으로 들어가는 문제였습니다.
$push 전에 이미 존재하는지 확인하는 로직을 추가해야 할까요? 박시니어 씨가 더 좋은 방법을 알려주었습니다.
$addToSet은 배열에 요소를 추가하되, 이미 존재하면 추가하지 않는 연산자입니다. 마치 집합(Set)에 원소를 추가하는 것과 같습니다.
중복 검사를 별도로 하지 않아도 자동으로 유일성이 보장되어 좋아요, 즐겨찾기 같은 기능에 안성맞춤입니다.
다음 코드를 살펴봅시다.
// 좋아요 목록에 게시글 ID를 추가합니다 (중복 방지)
db.users.updateOne(
{ userId: "user123" },
{ $addToSet: { likedPosts: "post456" } }
);
// 여러 태그를 중복 없이 추가합니다
db.users.updateOne(
{ userId: "user123" },
{ $addToSet: { tags: { $each: ["JavaScript", "Python", "JavaScript"] } } }
);
// 결과: JavaScript는 한 번만 추가됨
// 이미 존재하는 값이면 아무 일도 일어나지 않습니다
db.users.updateOne(
{ userId: "user123" },
{ $addToSet: { likedPosts: "post456" } } // 이미 있으면 무시
);
김개발 씨가 좋아요 기능을 처음 구현했을 때는 이런 흐름이었습니다. 사용자가 좋아요 버튼을 클릭하면, 먼저 해당 게시글이 이미 좋아요 목록에 있는지 확인합니다.
없으면 $push로 추가하고, 있으면 무시합니다. 코드가 꽤 길어졌습니다.
박시니어 씨가 이 코드를 보더니 웃으며 말했습니다. "그 복잡한 로직, $addToSet 하나면 끝나요." $addToSet은 집합(Set)의 특성을 배열에 적용한 연산자입니다.
집합은 중복을 허용하지 않는 자료구조입니다. $addToSet도 마찬가지로, 배열에 이미 같은 값이 존재하면 아무 동작도 하지 않습니다.
이 연산자의 장점은 조건 분기가 필요 없다는 것입니다. "이미 있는지 확인하고, 없으면 추가하고"라는 로직이 한 줄로 줄어듭니다.
코드가 간결해지고, 무엇보다 원자적으로 동작하기 때문에 동시성 문제도 없습니다. $each 수정자와 함께 사용하면 여러 요소를 한 번에 추가할 수 있습니다.
이때도 중복은 자동으로 걸러집니다. ["A", "B", "A"]를 $addToSet과 $each로 추가하면 실제로는 A와 B만 추가됩니다.
그러나 주의할 점이 있습니다. $addToSet은 완전히 일치하는 값만 중복으로 판단합니다.
객체 배열의 경우, 모든 필드와 값이 완전히 같아야 중복으로 인식합니다. { id: 1, name: "A" }와 { name: "A", id: 1 }은 필드 순서가 달라서 다른 값으로 취급됩니다.
이런 특성 때문에 객체 배열에서는 $addToSet을 신중하게 사용해야 합니다. 단순한 문자열이나 숫자 배열에서는 완벽하게 동작하지만, 복잡한 객체 배열에서는 예상과 다르게 동작할 수 있습니다.
좋아요 취소는 어떻게 구현할까요? 당연히 $pull을 사용합니다.
$addToSet으로 추가하고 $pull로 삭제하는 패턴은 MongoDB에서 좋아요, 즐겨찾기, 팔로우 기능을 구현할 때 정석처럼 사용됩니다. 실무에서는 좋아요 수를 별도 필드로 관리하는 경우가 많습니다.
이때 $addToSet과 $inc를 함께 사용합니다. 좋아요 목록에 추가되면 좋아요 수도 1 증가시키는 식입니다.
단, 중복 추가 방지와 카운트 동기화는 별개의 문제이므로 추가 로직이 필요합니다. 김개발 씨는 $addToSet 덕분에 좋아요 기능 코드를 절반으로 줄였습니다.
더 이상 중복 걱정 없이 깔끔하게 구현할 수 있게 되었습니다.
실전 팁
💡 - 객체 배열에서 $addToSet을 사용할 때는 필드 순서까지 완전히 일치해야 중복으로 판단됩니다
- 좋아요 기능은 $addToSet으로 추가, $pull로 삭제하는 패턴이 일반적입니다
5. upsert 옵션 활용
김개발 씨는 사용자 설정을 저장하는 기능을 만들고 있었습니다. 설정이 이미 있으면 업데이트하고, 없으면 새로 생성해야 합니다.
먼저 존재 여부를 확인하고, 있으면 updateOne, 없으면 insertOne을 호출하는 코드를 작성했습니다. 그런데 이게 최선일까요?
upsert는 update와 insert를 합친 옵션으로, 문서가 존재하면 업데이트하고 없으면 새로 생성합니다. 마치 호텔 체크인처럼 예약이 있으면 방을 배정하고, 없으면 새 예약을 만들어 방을 배정하는 것과 같습니다.
존재 여부 확인 로직이 필요 없어 코드가 간결해집니다.
다음 코드를 살펴봅시다.
// 사용자 설정이 있으면 업데이트, 없으면 생성
db.settings.updateOne(
{ userId: "user123" },
{
$set: {
theme: "dark",
language: "ko",
updatedAt: new Date()
},
$setOnInsert: {
createdAt: new Date() // 새로 생성될 때만 설정됨
}
},
{ upsert: true }
);
// 방문 카운터: 첫 방문이면 생성, 재방문이면 증가
db.visitors.updateOne(
{ date: "2024-01-15", page: "/home" },
{ $inc: { count: 1 } },
{ upsert: true }
);
김개발 씨의 초기 코드는 이랬습니다. 먼저 findOne으로 설정 문서가 있는지 확인합니다.
있으면 updateOne을 호출하고, 없으면 insertOne을 호출합니다. 작동은 하지만 데이터베이스 왕복이 두 번이고, 코드도 장황합니다.
더 큰 문제는 경쟁 상태입니다. findOne으로 확인하고 insertOne을 호출하는 사이에 다른 요청이 먼저 문서를 생성하면 중복 문서가 생기거나 오류가 발생합니다.
박시니어 씨가 이 문제를 짚어주었습니다. "upsert 옵션을 사용하면 이 모든 문제가 해결돼요." upsert는 update와 insert를 합친 말입니다.
조건에 맞는 문서가 있으면 업데이트하고, 없으면 새 문서를 생성합니다. 이 모든 과정이 원자적으로 이루어져 경쟁 상태가 발생하지 않습니다.
사용법은 간단합니다. updateOne의 세 번째 인자로 { upsert: true }를 전달하면 됩니다.
조건에 맞는 문서가 없으면 조건의 필드들과 업데이트 연산자의 내용을 합쳐 새 문서를 만듭니다. 여기서 $setOnInsert라는 특별한 연산자가 등장합니다.
이 연산자는 문서가 새로 생성될 때만 동작합니다. 기존 문서를 업데이트할 때는 무시됩니다.
생성일시(createdAt)처럼 최초 한 번만 설정해야 하는 필드에 안성맞춤입니다. 실무에서 upsert가 빛나는 대표적인 사례가 일별 통계입니다.
오늘 날짜로 통계 문서가 있으면 값을 증가시키고, 없으면 새로 만들어 1로 시작합니다. $inc와 upsert를 조합하면 이 로직이 한 줄로 끝납니다.
캐시 갱신에도 유용합니다. 캐시된 데이터가 있으면 갱신하고, 없으면 새로 캐시합니다.
만료된 캐시를 삭제하는 별도 로직과 새 캐시를 생성하는 로직을 하나로 합칠 수 있습니다. 주의할 점도 있습니다.
upsert로 생성된 문서는 필터 조건의 필드들을 포함합니다. { userId: "user123" }으로 검색하면 새 문서에도 userId가 "user123"으로 설정됩니다.
하지만 범위 조건이나 $or 같은 복잡한 조건은 새 문서에 반영되지 않습니다. 또한 unique 인덱스가 있는 필드에서 upsert를 사용할 때는 주의해야 합니다.
동시에 여러 요청이 같은 키로 upsert를 시도하면 duplicate key 오류가 발생할 수 있습니다. 이 경우 재시도 로직이 필요합니다.
김개발 씨는 upsert를 알고 나서 코드가 훨씬 간결해졌습니다. "있으면 업데이트, 없으면 생성"이라는 패턴이 나올 때마다 upsert를 떠올리게 되었습니다.
실전 팁
💡 - $setOnInsert는 새 문서 생성 시에만 동작하므로 createdAt 같은 필드에 적합합니다
- 복잡한 필터 조건은 새 문서에 반영되지 않으니 $set으로 명시적으로 설정하세요
6. arrayFilters 고급 활용
김개발 씨는 주문 내역에서 특정 상품의 상태만 변경해야 하는 상황에 부딪혔습니다. 주문 문서 안에 상품 배열이 있고, 그중 특정 조건에 맞는 상품만 업데이트해야 합니다.
배열 전체를 가져와서 JavaScript로 처리해야 할까요? 박시니어 씨가 고급 기술을 알려주었습니다.
arrayFilters는 배열 내 특정 조건에 맞는 요소만 선택적으로 업데이트하는 기능입니다. 마치 학급에서 특정 조건의 학생만 골라 상을 주는 것과 같습니다.
위치 연산자 $[<identifier>]와 함께 사용하며, 복잡한 배열 조작을 데이터베이스 레벨에서 처리할 수 있습니다.
다음 코드를 살펴봅시다.
// 주문 내 '배송중' 상태인 상품들만 '배송완료'로 변경
db.orders.updateOne(
{ orderId: "order001" },
{ $set: { "items.$[item].status": "delivered" } },
{ arrayFilters: [{ "item.status": "shipping" }] }
);
// 80점 미만인 모든 점수에 5점 보너스 추가
db.students.updateOne(
{ studentId: "student001" },
{ $inc: { "scores.$[score].value": 5 } },
{ arrayFilters: [{ "score.value": { $lt: 80 } }] }
);
// 중첩 배열에서 특정 조건의 요소 업데이트
db.courses.updateOne(
{ courseId: "course001" },
{ $set: { "modules.$[mod].lessons.$[les].completed": true } },
{
arrayFilters: [
{ "mod.title": "기초편" },
{ "les.duration": { $lt: 10 } }
]
}
);
김개발 씨가 처음 이 문제를 만났을 때 해결책은 이랬습니다. 주문 문서를 조회하고, JavaScript에서 items 배열을 순회하며 조건에 맞는 요소를 찾아 수정한 뒤, 전체 배열을 다시 저장합니다.
동작은 하지만 비효율적이고 동시성 문제도 있습니다. 박시니어 씨가 arrayFilters를 소개해주었습니다.
"이 기능을 쓰면 데이터베이스가 알아서 조건에 맞는 요소만 찾아서 업데이트해줘요." arrayFilters는 MongoDB 3.6에서 도입된 강력한 기능입니다. 배열 요소 중 특정 조건을 만족하는 것만 골라서 업데이트할 수 있습니다.
마치 SQL의 WHERE 절처럼 배열 내부에서 조건 검색을 하는 것입니다. 사용법을 이해하려면 먼저 위치 연산자를 알아야 합니다.
**$[]**는 배열의 모든 요소를 의미하고, **$[<identifier>]**는 arrayFilters에서 정의한 조건에 맞는 요소만 의미합니다. identifier는 원하는 이름을 붙일 수 있습니다.
예를 들어 "items.$[item].status"는 items 배열에서 item이라는 이름의 조건에 맞는 요소의 status 필드를 가리킵니다. 그리고 arrayFilters에서 { "item.status": "shipping" }이라고 정의하면, status가 "shipping"인 요소만 선택됩니다.
이 기능의 진가는 중첩 배열에서 발휘됩니다. 강의 문서 안에 모듈 배열이 있고, 각 모듈 안에 레슨 배열이 있는 구조를 생각해보세요.
특정 모듈의 특정 레슨만 업데이트해야 할 때, arrayFilters 없이는 거의 불가능합니다. arrayFilters에 여러 조건을 넣을 수 있습니다.
각 조건은 다른 중첩 레벨의 배열에 적용됩니다. "modules.$[mod].lessons.$[les]"처럼 작성하고, arrayFilters에 mod 조건과 les 조건을 각각 정의하면 됩니다.
조건에는 비교 연산자도 사용할 수 있습니다. $lt, $gt, $in, $ne 등 MongoDB의 쿼리 연산자를 그대로 사용합니다.
점수가 60점 미만인 것만 찾거나, 상태가 특정 목록에 포함된 것만 찾는 식입니다. 주의할 점이 있습니다.
arrayFilters의 identifier는 필드 경로에서 사용한 것과 정확히 일치해야 합니다. $[elem]을 사용했다면 arrayFilters에서도 "elem.필드"로 시작해야 합니다.
이름이 다르면 오류가 발생합니다. 또한 arrayFilters는 updateOne, updateMany, findOneAndUpdate 등 업데이트 메서드에서만 사용할 수 있습니다.
find에서는 사용할 수 없습니다. 조회 시 배열 필터링은 $elemMatch나 집계 파이프라인을 사용해야 합니다.
김개발 씨는 arrayFilters 덕분에 복잡한 배열 업데이트도 한 번의 쿼리로 처리할 수 있게 되었습니다. JavaScript에서 배열을 순회하던 코드가 사라지니 성능도 좋아지고 동시성 문제도 해결되었습니다.
실전 팁
💡 - identifier 이름은 자유롭게 지을 수 있지만 필드 경로와 arrayFilters에서 일관되게 사용하세요
- 중첩 배열에서는 각 레벨마다 별도의 identifier와 조건을 정의합니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
MongoDB 집계 파이프라인 고급 완벽 가이드
MongoDB의 고급 집계 파이프라인 기능을 초급 개발자도 쉽게 이해할 수 있도록 설명합니다. $lookup, $unwind, $facet, $bucket, $graphLookup 등 실무에서 자주 사용하는 연산자들을 실제 예제와 함께 다룹니다.
MongoDB 집계 파이프라인 기초
MongoDB의 집계 파이프라인을 처음 접하는 개발자를 위한 가이드입니다. 데이터를 필터링하고, 그룹화하고, 변환하는 방법을 실무 예제와 함께 차근차근 알아봅니다.
MongoDB 인덱스 기초 완벽 가이드
MongoDB에서 쿼리 성능을 획기적으로 개선하는 인덱스의 모든 것을 다룹니다. 단일 필드 인덱스부터 복합 인덱스, 그리고 실무에서 반드시 알아야 할 인덱스 관리 방법까지 초급 개발자도 쉽게 이해할 수 있도록 설명합니다.
MongoDB 쿼리 연산자 완벽 가이드
MongoDB에서 데이터를 효율적으로 검색하고 필터링하는 쿼리 연산자를 알아봅니다. 비교, 논리, 배열 연산자부터 정규표현식, 프로젝션, 정렬까지 실무에서 바로 활용할 수 있는 내용을 다룹니다.
MongoDB CRUD 기본 연산 완벽 가이드
MongoDB에서 데이터를 생성, 조회, 수정, 삭제하는 기본 연산을 배웁니다. 실무에서 바로 활용할 수 있는 CRUD 패턴과 대량 처리 기법까지 다룹니다.