MongoDB CRUD 작업 마스터하기: 실용적인 명령어 가이드

이 실용적인 명령어 가이드를 통해 MongoDB의 핵심 CRUD 작업을 마스터하세요. `insert`, `find`, `update`, `delete` 명령어를 사용하여 데이터를 효율적으로 관리하는 방법을 배웁니다. 이 문서는 MongoDB 컬렉션에서 문서를 생성, 읽기, 업데이트 및 삭제하는 방법에 대한 명확한 설명, 실제 예제 및 모범 사례를 제공합니다. 개발자와 관리자 모두에게 완벽한 MongoDB 데이터 조작 마스터를 위한 필수 자료입니다.

MongoDB CRUD 작업 마스터하기: 실용적인 명령어 가이드

MongoDB는 유연성, 확장성 및 성능 덕분에 수많은 현대 애플리케이션의 기반을 형성하는 인기 있는 NoSQL 문서 데이터베이스입니다. 데이터베이스와 상호 작용하는 핵심은 기본적인 생성, 읽기, 업데이트 및 삭제(CRUD) 작업입니다. 이러한 명령어를 마스터하는 것은 새로운 기능을 구축하는 개발자부터 데이터를 관리하는 관리자까지 MongoDB로 작업하는 모든 사람에게 필수적입니다.

이 가이드는 작은 users 컬렉션을 사용하여 실제 작업 중에 사용하는 MongoDB CRUD 명령어를 보여줍니다: 가입 양식에서 레코드 추가, 지원 티켓 뒤에 있는 문서 찾기, 문서의 나머지 부분을 덮어쓰지 않고 필드 변경, 실수로 컬렉션을 지우지 않고 데이터 삭제. 예제는 mongosh를 사용하지만 동일한 필터 및 업데이트 연산자는 애플리케이션 드라이버에도 적용됩니다.

사전 요구 사항

명령어를 시작하기 전에 다음이 있는지 확인하세요:

  • MongoDB 설치 및 실행: 공식 MongoDB 웹사이트에서 다운로드하거나 MongoDB Atlas와 같은 클라우드 서비스를 사용할 수 있습니다.
  • mongosh(MongoDB 셸) 설치: MongoDB용 대화형 JavaScript 인터페이스입니다.

MongoDB에 연결 및 데이터베이스 선택

시작하려면 터미널 또는 명령 프롬프트를 열고 mongosh를 사용하여 MongoDB 인스턴스에 연결하세요:

mongosh

연결되면 기본 test 데이터베이스에 있게 됩니다. 새 데이터베이스로 전환하거나 생성하려면 use 명령어를 사용하세요:

use myDatabase;

myDatabase가 존재하지 않으면 MongoDB는 해당 데이터베이스 내의 컬렉션에 첫 번째 문서를 삽입할 때 암시적으로 생성합니다.

생성 작업 (Insert)

생성 작업은 컬렉션에 새 문서를 추가하는 것을 포함합니다. MongoDB는 단일 또는 여러 문서를 삽입하는 메서드를 제공합니다.

1. db.collection.insertOne()

이 메서드는 컬렉션에 단일 문서를 삽입합니다. 컬렉션이 존재하지 않으면 MongoDB가 생성합니다.

// 'users'라는 컬렉션 선택
db.users.insertOne({
  name: "Alice Smith",
  age: 30,
  city: "New York",
  email: "[email protected]",
  interests: ["reading", "hiking"]
});

출력에는 acknowledged 상태와 새 문서의 insertedId가 표시됩니다.

2. db.collection.insertMany()

이 메서드를 사용하면 단일 작업으로 컬렉션에 여러 문서를 삽입할 수 있습니다. 문서 배열을 사용합니다.

db.users.insertMany([
  {
    name: "Bob Johnson",
    age: 24,
    city: "Los Angeles",
    email: "[email protected]",
    interests: ["coding", "gaming"]
  },
  {
    name: "Charlie Brown",
    age: 35,
    city: "New York",
    email: "[email protected]",
    interests: ["cooking", "photography"]
  },
  {
    name: "Diana Prince",
    age: 29,
    city: "London",
    email: "[email protected]",
    interests: ["fitness", "travel"]
  }
]);

이것은 추가된 모든 문서의 insertedIds 배열을 반환합니다.

: MongoDB는 제공하지 않으면 각 문서에 _id 필드(고유 ObjectId)를 자동으로 추가합니다.

읽기 작업 (Find)

읽기 작업은 컬렉션에서 문서를 쿼리하는 것을 포함합니다. find() 메서드가 이를 위한 기본 도구입니다.

1. db.collection.find()

a. 모든 문서 찾기

컬렉션의 모든 문서를 검색하려면 인수 없이 find()를 호출하세요:

db.users.find();

b. 쿼리 필터로 문서 찾기

문서 선택 기준을 지정하려면 find()에 쿼리 문서를 전달하세요. 이는 SQL의 WHERE 절 역할을 합니다.

// 뉴욕 사용자 찾기
db.users.find({ city: "New York" });

// 25세 이상 사용자 찾기
db.users.find({ age: { $gt: 25 } });

// 25세에서 35세 사이 사용자 찾기 (35세 제외)
db.users.find({ age: { $gt: 25, $lt: 35 } });

// 관심사에 'coding'이 포함된 사용자 찾기
db.users.find({ interests: "coding" });

// 관심사에 'reading'과 'hiking'이 모두 포함된 사용자 찾기
db.users.find({ interests: { $all: ["reading", "hiking"] } });

// Alice Smith라는 이름 또는 런던 출신 사용자 찾기
db.users.find({
  $or: [
    { name: "Alice Smith" },
    { city: "London" }
  ]
});

일반적인 쿼리 연산자:

  • $eq: 같음 (연산자가 지정되지 않은 경우 기본값)
  • $ne: 같지 않음
  • $gt: 보다 큼
  • $gte: 크거나 같음
  • $lt: 보다 작음
  • $lte: 작거나 같음
  • $in: 배열에 지정된 값 중 하나와 일치
  • $nin: 배열에 지정된 값 중 어느 것과도 일치하지 않음
  • $and, $or, $not, $nor: 논리 연산자

c. 프로젝션: 특정 필드 선택

필드의 하위 집합만 반환하려면 find()의 두 번째 인수로 프로젝션 문서를 전달하세요. 값 1은 필드를 포함하고 0은 제외합니다. _id 필드는 명시적으로 제외하지 않는 한 기본적으로 포함됩니다.

// 이름과 이메일만 반환, _id 제외
db.users.find({ city: "New York" }, { name: 1, email: 1, _id: 0 });

d. 정렬, 제한 및 건너뛰기

메서드 체이닝을 통해 더 복잡한 쿼리가 가능합니다:

// 나이 내림차순 정렬 (1은 오름차순, -1은 내림차순)
db.users.find().sort({ age: -1 });

// 결과를 2개 문서로 제한
db.users.find().limit(2);

// 처음 2개 문서를 건너뛰고 나머지 반환
db.users.find().skip(2);

// 작업 결합: 뉴욕 사용자 찾기, 나이순 정렬, 1개 건너뛰기, 1개 제한
db.users.find({ city: "New York" }).sort({ age: 1 }).skip(1).limit(1);

2. db.collection.findOne()

이 메서드는 쿼리 기준과 일치하는 단일 문서를 반환합니다. 여러 문서가 일치하면 처음 발견된 문서를 반환합니다.

db.users.findOne({ name: "Alice Smith" });

업데이트 작업

업데이트 작업은 컬렉션의 기존 문서를 수정합니다. 단일 문서, 여러 문서를 업데이트하거나 전체 문서를 교체할 수 있습니다.

1. db.collection.updateOne()

필터 기준과 일치하는 단일 문서를 업데이트합니다.

// Alice의 도시를 'San Francisco'로 업데이트
db.users.updateOne(
  { name: "Alice Smith" },
  { $set: { city: "San Francisco", lastUpdated: new Date() } }
);

2. db.collection.updateMany()

필터 기준과 일치하는 모든 문서를 업데이트합니다.

// 뉴욕에 있는 모든 사용자의 나이를 1씩 증가
db.users.updateMany(
  { city: "New York" },
  { $inc: { age: 1 } }
);

// 아직 'reading' 관심사가 없는 사용자에게 추가
db.users.updateMany(
  {}, // 모든 문서에 적용
  { $addToSet: { interests: "reading" } }
);

// Bob Johnson의 관심사에서 'gaming' 제거
db.users.updateOne(
  { name: "Bob Johnson" },
  { $pull: { interests: "gaming" } }
);

일반적인 업데이트 연산자:

  • $set: 문서의 필드 값을 설정합니다. 필드가 없으면 생성합니다.
  • $inc: 필드 값을 지정된 양만큼 증가시킵니다.
  • $unset: 문서에서 필드를 제거합니다.
  • $push: 배열 필드에 값을 추가합니다.
  • $pull: 배열에서 지정된 쿼리와 일치하는 값의 모든 인스턴스를 제거합니다.
  • $addToSet: 값이 아직 없을 때만 배열에 값을 추가합니다.

3. db.collection.replaceOne()

필터 기준과 일치하는 단일 문서를 새 문서로 교체합니다. 교체되는 문서의 _id 필드는 변경할 수 없습니다.

// Bob Johnson의 문서를 완전히 교체
db.users.replaceOne(
  { name: "Bob Johnson" },
  {
    name: "Robert Johnson",
    occupation: "Software Engineer",
    status: "active",
    email: "[email protected]"
  }
);

Upsert 옵션

updateOne()updateMany() 모두 upsert 옵션을 지원합니다. true로 설정하고 필터와 일치하는 문서가 없으면 MongoDB는 쿼리 및 업데이트 작업을 기반으로 새 문서를 삽입합니다.

db.users.updateOne(
  { name: "David Lee" },
  { $set: { age: 28, city: "Seattle" } },
  { upsert: true } // David Lee가 없으면 생성
);

삭제 작업

삭제 작업은 컬렉션에서 문서를 제거합니다. 삭제 작업은 되돌릴 수 없으므로 매우 주의하세요.

1. db.collection.deleteOne()

지정된 필터와 일치하는 최대 하나의 문서를 삭제합니다.

// 'Robert Johnson' (이전 'Bob Johnson') 사용자 삭제
db.users.deleteOne({ name: "Robert Johnson" });

2. db.collection.deleteMany()

지정된 필터와 일치하는 모든 문서를 삭제합니다.

// 런던의 모든 사용자 삭제
db.users.deleteMany({ city: "London" });

경고: 컬렉션의 모든 문서를 삭제하려면 빈 필터 {}를 사용하세요. 이 작업은 취소할 수 없으므로 매우 주의하세요:

db.users.deleteMany({}); // 'users' 컬렉션의 모든 문서 삭제

3. db.collection.drop()

이 메서드는 모든 문서와 인덱스를 포함하여 데이터베이스에서 전체 컬렉션을 영구적으로 제거합니다.

db.users.drop(); // 전체 'users' 컬렉션 삭제

경고: 컬렉션을 삭제하는 것은 매우 파괴적인 작업입니다. 이 명령어를 실행하기 전에 적절한 백업이 있거나 절대적으로 확신하는지 확인하세요.

MongoDB CRUD 모범 사례

  • 인덱싱: 자주 쿼리되는 필드의 경우 인덱스를 생성하여 읽기 작업 속도를 크게 높이세요. db.collection.createIndex({ fieldName: 1 }).
  • 프로젝션: 필요한 데이터만 검색하세요. 프로젝션({ field: 1 })을 사용하면 네트워크 대역폭과 메모리 사용량이 줄어듭니다.
  • 배치 작업: 많은 문서를 삽입, 업데이트 또는 삭제할 때 개별 작업 대신 insertMany(), updateMany()deleteMany()를 사용하여 오버헤드를 줄이세요.
  • 연산자 이해: MongoDB의 풍부한 쿼리 및 업데이트 연산자 세트를 숙지하세요. 데이터를 조작하는 강력한 방법을 제공합니다.
  • 오류 처리: 프로덕션 애플리케이션에서는 데이터베이스 작업에 대해 항상 강력한 오류 처리를 구현하세요.
  • 스키마 디자인: MongoDB는 스키마가 없지만 효율적인 쿼리와 데이터 일관성을 위해 신중한 스키마 디자인이 중요합니다.

실제 데이터베이스에서 CRUD를 연습하는 더 안전한 방법

MongoDB CRUD의 위험한 부분은 구문이 아닙니다. 특히 updateMany() 또는 deleteMany()를 사용할 때 잘못된 데이터베이스에서 광범위한 필터를 실행하는 것입니다. 저는 기존 데이터를 건드리는 모든 쓰기 작업에 대해 세 단계 습관을 사용하는 것을 좋아합니다.

먼저, 필터를 읽기로 실행하세요:

db.users.find(
  { city: "New York", status: "inactive" },
  { name: 1, email: 1, city: 1, status: 1 }
).limit(20);

미리보기가 예상치 못한 문서를 반환하면 거기서 멈추세요. 업데이트를 생각하기 전에 필터를 수정하세요. 아무것도 반환하지 않으면 db.getName()으로 올바른 데이터베이스에 있는지, 필드 이름이 실제 문서와 일치하는지 확인하세요.

둘째, 일치하는 문서 수를 세세요:

db.users.countDocuments({ city: "New York", status: "inactive" });

이것은 대략적인 영향 범위를 제공합니다. 12명의 사용자를 예상했는데 카운트가 12,000이라고 말하면 명령어가 뭔가 잘못되었음을 알려줍니다. 카운트는 백업을 대체할 수 없지만 저렴한 가드레일입니다.

셋째, 작업에 가장 적합한 가장 좁은 명령어로 쓰기를 실행하세요. 고유한 이메일, 계정 ID 또는 _id가 하나의 문서와 일치해야 할 때 updateOne()을 사용하세요. 전체 세그먼트를 변경하는 것이 의도적일 때만 updateMany()를 사용하세요.

db.users.updateMany(
  { city: "New York", status: "inactive" },
  {
    $set: {
      marketingEmailEnabled: false,
      updatedBy: "ops-maintenance-2025-11-04"
    }
  }
);

updatedBy, updatedAt 또는 유지 관리 메모를 추가하는 것은 MongoDB에서 필수는 아니지만 나중에 누군가 필드가 변경된 이유를 물을 때 도움이 됩니다.

실제 버그를 유발하는 일반적인 실수

첫 번째 실수는 필드를 업데이트하려고 할 때 문서를 교체하는 것입니다. 이 명령어는 무해해 보이지만 표시된 필드만으로 일치하는 전체 문서를 교체합니다:

db.users.updateOne(
  { email: "[email protected]" },
  { city: "Boston" }
);

최신 MongoDB는 교체 스타일 메서드를 사용하지 않는 한 업데이트 문서에 업데이트 연산자를 기대하므로 이 패턴은 명령어와 버전에 따라 실패할 수 있습니다. 더 안전한 사고 모델은 간단합니다: 필드를 제자리에서 변경하는 경우 $set, $unset, $inc, $push, $pull 또는 다른 업데이트 연산자를 사용하세요.

db.users.updateOne(
  { email: "[email protected]" },
  { $set: { city: "Boston" } }
);

두 번째 실수는 순서가 항상 중요하다고 가정하고 배열을 쿼리하는 것입니다. { interests: ["reading", "hiking"] }는 배열을 정확히 일치시킵니다. { interests: "reading" }는 배열에 해당 값이 포함된 문서를 일치시킵니다. { interests: { $all: ["reading", "hiking"] } }는 순서에 관계없이 두 값을 모두 포함하는 배열을 일치시킵니다. 이들은 세 가지 다른 질문입니다.

세 번째 실수는 필터가 고유하지 않을 때 findOne()이 "올바른 것"을 반환한다고 가정하는 것입니다. 다음을 실행하면:

db.users.findOne({ city: "New York" });

MongoDB는 하나의 일치하는 문서를 반환하지만 정렬 없이는 해당 문서를 가장 최신, 가장 오래된, 가장 중요하거나 가장 대표적인 것으로 취급해서는 안 됩니다. 순서가 중요하다면 명시하세요:

db.users.find({ city: "New York" }).sort({ createdAt: -1 }).limit(1);

네 번째 실수는 앱이 이미 느려질 때까지 인덱스를 건너뛰는 것입니다. 수천 개의 문서가 있는 컬렉션은 비효율적인 쿼리를 숨길 수 있습니다. 동일한 쿼리는 컬렉션이 커질 때 고통스러워질 수 있습니다. 애플리케이션이 email로 사용자를 자주 찾는 경우 데이터 모델이 허용할 때 고유 인덱스를 생성하세요:

db.users.createIndex({ email: 1 }, { unique: true });

이것은 성능과 데이터 품질을 모두 보호합니다. 중복 이메일이 이미 존재하면 명령어가 실패할 것이며, 이는 이메일을 식별자로 사용하기 전에 발견하고 싶은 종류의 문제입니다.

쓰기가 실제로 무엇을 했는지 확인

MongoDB 쓰기 결과를 읽을 가치가 있습니다. 업데이트 후 matchedCountmodifiedCount를 확인하세요. matchedCount는 필터와 일치한 문서 수를 알려줍니다. modifiedCount는 실제로 변경된 문서 수를 알려줍니다.

matchedCount1이고 modifiedCount0이면 명령어는 여전히 괜찮을 수 있습니다. 필드가 이미 요청된 값을 가지고 있었을 수 있습니다. matchedCount0이면 필터가 아무것도 일치하지 않은 것입니다. 이는 _idObjectId 대신 문자열로 전달될 때 일반적입니다.

db.users.findOne({ _id: ObjectId("6650f1e59d0a41a37c2d8011") });

삭제의 경우 deletedCount를 확인하세요. 하나의 삭제된 문서를 예상했는데 결과에 deletedCount: 0이라고 표시되면 즉시 더 광범위한 삭제를 실행하지 마세요. 데이터베이스, 컬렉션 및 필터를 다시 확인하세요.

CRUD가 충분하지 않을 때

기본 CRUD 명령어는 대부분의 일상적인 데이터 작업을 다루지만 일부 작업에는 더 강력한 도구가 필요합니다. 함께 일관성을 유지해야 하는 여러 컬렉션을 업데이트하는 경우 복제 세트 또는 샤딩된 클러스터의 트랜잭션을 살펴보세요. 많은 문서에 걸쳐 데이터를 재구성하는 경우 집계 파이프라인이 긴 클라이언트 측 루프 시리즈보다 더 명확할 수 있습니다. 프로덕션 데이터를 마이그레이션하는 경우 로깅, 드라이런 모드, 백업 및 롤백 계획이 있는 스크립트를 사용하세요.

mongosh에서 일회성 작업의 경우 명령어를 읽기 쉽게 유지하세요. 영리한 한 줄짜리는 검토하기 어렵고 복구하기도 어렵습니다. 프로덕션에서는 지루한 명령어가 일반적으로 더 좋습니다.

MongoDB CRUD 명령어는 문서, 필터 및 업데이트 연산자에 익숙해지면 간단합니다. 실제 작업에서 중요한 기술은 신중함입니다: 필터 미리보기, 영향 계산, 가장 좁은 쓰기 명령어 선택, 결과 읽기, 다음 사람이 무엇이 변경되었는지 이해할 수 있도록 충분한 컨텍스트 남기기.