MongoDB 중첩 문서 관리

MongoDBBeginner
지금 연습하기

소개

이 랩에서는 MongoDB 에서 임베디드 문서를 효과적으로 관리하는 방법을 배우게 됩니다. 임베디드 문서 또는 중첩 문서는 MongoDB 의 데이터 모델 핵심 기능으로, 단일 문서 내에 복잡하고 계층적인 데이터를 저장할 수 있게 해줍니다. 중첩된 데이터를 가진 문서 생성, 내부 특정 필드 업데이트, 요소 제거, 이러한 복잡한 구조 쿼리 등 다양한 기법을 배우게 됩니다. 또한 스키마 유효성 검사 (schema validation) 를 사용하여 일관된 데이터 구조를 강제하는 방법도 배우게 됩니다. 이러한 기술은 MongoDB 를 사용하여 견고하고 효율적인 애플리케이션을 구축하는 데 필수적입니다.

중첩 데이터로 문서 생성하기

이 단계에서는 MongoDB 에서 중첩된 구조를 가진 문서를 생성하는 방법을 배우게 됩니다. 이는 단일 문서 내에서 관련된 데이터를 모델링하는 기본적인 기술입니다.

먼저 MongoDB Shell (mongosh) 을 열어 데이터베이스와 상호 작용을 시작합니다. 별도로 명시되지 않는 한, 이 랩의 모든 후속 명령은 이 쉘 내에서 실행됩니다.

mongosh

다음으로 bookstore라는 새 데이터베이스로 전환합니다. 이 데이터베이스가 존재하지 않으면 MongoDB 는 처음 데이터를 저장할 때 자동으로 생성합니다.

use bookstore

이제 books라는 컬렉션을 생성하고 단일 문서를 삽입합니다. 이 문서는 책의 세부 정보를 나타내는 중첩된 객체와 중첩된 객체의 배열을 포함합니다.

db.books.insertOne({
    title: "Advanced MongoDB",
    author: {
        name: "Jane Smith",
        contact: {
            email: "jane.smith@example.com",
            phone: "+1-555-123-4567"
        }
    },
    published: {
        year: 2023,
        publisher: "Tech Publications"
    },
    chapters: [
        { number: 1, title: "Introduction to Nested Documents" },
        { number: 2, title: "Advanced Document Structures" }
    ]
})

방금 생성한 문서에서:

  • authorpublished는 중첩된 문서 (객체) 입니다.
  • author 문서에는 contact라는 또 다른 중첩된 문서가 포함되어 있습니다.
  • chapters는 여러 중첩된 문서를 포함하는 배열이며, 각 문서는 챕터를 나타냅니다.

문서를 보고 구조를 확인하려면 find() 메서드를 사용합니다.

db.books.find()

다음과 같은 출력이 표시되어 문서가 올바르게 삽입되었음을 확인할 수 있습니다. _id는 MongoDB 에서 자동으로 생성되는 고유 식별자입니다.

[
  {
    _id: ObjectId("..."),
    title: 'Advanced MongoDB',
    author: {
      name: 'Jane Smith',
      contact: {
        email: 'jane.smith@example.com',
        phone: '+1-555-123-4567'
      }
    },
    published: {
      year: 2023,
      publisher: 'Tech Publications'
    },
    chapters: [
      { number: 1, title: 'Introduction to Nested Documents' },
      { number: 2, title: 'Advanced Document Structures' }
    ]
  }
]

중첩 필드 및 배열 요소 업데이트

문서를 생성한 후에는 종종 해당 문서의 일부를 수정해야 합니다. 이 단계에서는 중첩된 문서 및 배열 내의 특정 필드를 업데이트하는 방법을 배우게 됩니다. 이전 단계에서와 마찬가지로 mongosh 쉘에 있어야 합니다.

먼저, 저자의 연락처 정보를 업데이트해 보겠습니다. 이를 위해 $set 연산자와 점 표기법 (dot notation) 을 사용하여 변경하려는 필드의 정확한 경로를 지정합니다. 이 명령은 전화번호를 변경하고 새로운 website 필드를 추가합니다.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $set: {
        "author.contact.phone": "+1-888-999-0000",
        "author.contact.website": "www.janesmith.com"
    } }
)

점 표기법 "author.contact.phone"은 MongoDB 에 author 객체로 이동한 다음 contact 객체로 이동하여 phone 필드를 업데이트하도록 지시합니다.

다음으로, $push 연산자를 사용하여 chapters 배열에 새 챕터를 추가합니다.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $push: {
        chapters: {
            number: 3,
            title: "MongoDB Advanced Techniques"
        }
    } }
)

배열 내의 특정 요소도 업데이트할 수 있습니다. 첫 번째 챕터의 제목을 수정해 보겠습니다. 여기서는 위치 지정자 $ 연산자를 사용합니다. 이 연산자는 쿼리 조건 ("chapters.number": 1) 과 일치하는 첫 번째 배열 요소를 위한 자리 표시자 역할을 합니다.

db.books.updateOne(
    { title: "Advanced MongoDB", "chapters.number": 1 },
    { $set: {
        "chapters.$.title": "Introduction to Nested Documents (Revised)"
    } }
)

모든 변경 사항을 확인하려면 문서를 다시 검색합니다. .pretty() 메서드는 출력을 더 읽기 쉽게 형식화합니다.

db.books.find({ title: "Advanced MongoDB" }).pretty()

출력에는 업데이트된 전화번호, 새로운 웹사이트, 추가된 세 번째 챕터, 그리고 첫 번째 챕터의 수정된 제목이 표시됩니다.

중첩 필드 및 배열 요소 제거

이 단계에서는 문서의 일부를 제거하는 방법을 배우게 됩니다. 여기에는 특정 필드, 전체 중첩된 문서 및 배열의 요소를 제거하는 것이 포함됩니다.

먼저 $unset 연산자를 사용하여 저자의 연락처 정보에서 website 필드를 제거해 보겠습니다. $unset에 제공된 값 (이 경우 빈 문자열 "") 은 중요하지 않습니다. 이 연산자는 단순히 지정된 필드를 제거합니다.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $unset: { "author.contact.website": "" } }
)

다음으로 배열에서 요소를 제거합니다. $pull 연산자를 사용하여 chapters 배열에서 세 번째 챕터 ( number: 3을 가진 챕터) 를 제거해 보겠습니다. $pull 연산자는 지정된 조건과 일치하는 모든 배열 요소를 제거합니다.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $pull: {
        chapters: { number: 3 }
    } }
)

마지막으로 전체 중첩 객체를 제거할 수 있습니다. 다시 $unset 연산자를 사용하여 author 문서에서 contact 객체를 제거해 보겠습니다.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $unset: { "author.contact": "" } }
)

이러한 제거 작업의 결과를 보려면 문서를 다시 쿼리합니다.

db.books.find({ title: "Advanced MongoDB" }).pretty()

출력에는 website 필드와 전체 contact 객체가 사라지고 chapters 배열에는 이제 두 개의 요소만 포함된 것을 볼 수 있습니다.

중첩 데이터로 문서 쿼리하기

쿼리는 기본적인 데이터베이스 작업입니다. 이 단계에서는 중첩된 객체 및 배열 내의 값을 기반으로 문서를 쿼리하는 방법을 배우게 됩니다. 이러한 예제를 위해 깨끗한 데이터 세트를 확보하기 위해 먼저 기존 books 컬렉션을 삭제합니다.

db.books.drop()

이제 쿼리 연습을 위해 서로 다른 중첩 구조를 가진 몇 개의 새 문서를 삽입합니다.

db.books.insertMany([
    {
        title: "MongoDB Essentials",
        author: {
            name: "John Doe",
            experience: { years: 5, specialization: "Database Design" }
        },
        tags: ["beginner", "database", "nosql"],
        chapters: [
            { number: 1, title: "Introduction", pages: 25 },
            { number: 2, title: "Advanced Concepts", pages: 45 }
        ]
    },
    {
        title: "Advanced Database Techniques",
        author: {
            name: "Jane Smith",
            experience: { years: 8, specialization: "Distributed Systems" }
        },
        tags: ["advanced", "distributed", "nosql"],
        chapters: [
            { number: 1, title: "System Design", pages: 35 },
            { number: 2, title: "Performance Optimization", pages: 55 }
        ]
    }
])

중첩된 문서의 필드로 책을 찾으려면 점 표기법 (dot notation) 을 사용합니다. 이 쿼리는 "John Doe"가 작성한 책을 찾습니다.

db.books.find({ "author.name": "John Doe" })

중첩된 구조로 더 깊이 들어갈 수 있습니다. 이 쿼리는 저자의 전문 분야가 "Database Design"인 책을 찾습니다.

db.books.find({ "author.experience.specialization": "Database Design" })

배열에 특정 값이 포함된 문서를 찾으려면 배열 필드를 직접 쿼리할 수 있습니다. 이 쿼리는 "nosql" 태그가 지정된 모든 책을 찾습니다.

db.books.find({ tags: "nosql" })

또한 배열 요소가 특정 조건을 충족하는 문서를 쿼리할 수도 있습니다. 이 쿼리는 $gt (greater than) 연산자를 사용하여 40 페이지보다 긴 챕터가 하나 이상 있는 책을 찾습니다.

db.books.find({ "chapters.pages": { $gt: 40 } })

마지막으로 여러 조건을 결합할 수 있습니다. 이 쿼리는 "advanced" 태그가 지정되었고 5 년 이상의 경력 ($gte는 greater than or equal to 를 의미) 을 가진 저자가 작성한 책을 찾습니다.

db.books.find({
    "author.experience.years": { $gte: 5 },
    tags: "advanced"
})

스키마 유효성 검사로 문서 구조 강제 적용

데이터 일관성을 유지하기 위해 MongoDB 는 JSON 스키마 유효성 검사 (JSON Schema validation) 를 사용하여 컬렉션의 문서에 특정 구조를 강제할 수 있습니다. 이 단계에서는 미리 정의된 구조를 모든 문서가 준수하도록 하기 위해 유효성 검사기 (validator) 를 사용하여 새 컬렉션을 생성합니다.

먼저 courses라는 새 컬렉션을 생성합니다. 생성 시 스키마 규칙을 포함하는 validator 옵션을 전달합니다.

db.createCollection("courses", {
   validator: {
      $jsonSchema: {
         bsonType: "object",
         required: ["title", "instructor", "duration", "topics"],
         properties: {
            title: {
               bsonType: "string",
               description: "must be a string and is required"
            },
            instructor: {
               bsonType: "object",
               required: ["name", "email"],
               properties: {
                  name: { bsonType: "string" },
                  email: {
                     bsonType: "string",
                     pattern: "^.+@.+$"
                  }
               }
            },
            duration: {
               bsonType: "int",
               minimum: 1,
               maximum: 100
            },
            topics: {
               bsonType: "array",
               minItems: 1,
               items: { bsonType: "string" }
            }
         }
      }
   }
})

이 스키마는 모든 문서에 title, instructor, duration, topics 필드가 있어야 함을 요구합니다. 또한 각 필드의 데이터 유형을 정의하며, instructor 객체와 topics 배열에 대한 중첩 규칙도 포함합니다.

이제 이 규칙을 준수하는 문서를 삽입해 보겠습니다. 이 작업은 성공해야 합니다.

db.courses.insertOne({
   title: "MongoDB Advanced Techniques",
   instructor: {
      name: "Jane Smith",
      email: "jane.smith@example.com"
   },
   duration: 40,
   topics: ["Nested Documents", "Schema Validation"]
})

다음으로 스키마 규칙을 위반하는 문서를 삽입해 보겠습니다. 이 문서는 instructor.name에 잘못된 데이터 유형, 잘못된 이메일 형식, 허용 범위를 벗어난 duration, 그리고 비어 있는 topics 배열을 가지고 있습니다.

db.courses.insertOne({
   title: "Invalid Course",
   instructor: {
      name: 123,
      email: "invalid-email"
   },
   duration: 200,
   topics: []
})

이 삽입은 실패하며 MongoDB 는 Document failed validation 오류를 반환합니다. 이는 일관성 없는 데이터가 저장되는 것을 방지하고 데이터베이스 무결성을 유지하는 데 도움이 됩니다.

요약

이 실습에서는 MongoDB 에서 중첩된 문서를 관리하는 기본적인 기술을 배웠습니다. 객체와 배열을 포함한 복잡한 중첩 구조로 문서를 생성하는 것부터 시작했습니다. 그런 다음 점 표기법 (dot notation) 과 $set, $push와 같은 연산자를 사용하여 특정 필드를 업데이트하는 연습을 했습니다. 또한 $unset$pull을 사용하여 데이터를 제거하는 방법도 배웠습니다. 더 나아가 중첩된 데이터에 대한 대상 쿼리를 수행하는 방법을 탐색했으며, 마지막으로 컬렉션에 JSON 스키마 유효성 검사기 (JSON Schema validator) 를 정의하고 적용하여 데이터 무결성을 강제하는 방법을 배웠습니다. 이러한 기술은 MongoDB 의 유연한 문서 모델을 효과적으로 활용하는 애플리케이션을 구축하는 데 매우 중요합니다.