소개
이 랩에서는 MongoDB 프로젝션 (projection) 을 사용하여 쿼리 결과에 어떤 필드가 나타날지 제어하는 방법을 배우게 됩니다. 프로젝션은 반환할 특정 필드를 선택할 수 있게 해주는 강력한 기능으로, 네트워크를 통해 전송되는 데이터 양을 줄이고 쿼리 성능을 향상시킬 수 있습니다. 필드 포함 및 제외, 중첩 문서 내 필드 프로젝션, 그리고 aggregation framework 를 사용하여 출력 형태를 재구성하는 연습을 하게 됩니다. 이 랩이 끝날 때쯤이면 MongoDB 쿼리에서 프로젝션을 효과적으로 사용하는 방법에 대한 확실한 이해를 갖게 될 것입니다.
프로젝션을 사용하여 특정 필드 선택
첫 번째 단계에서는 쿼리 결과에서 보고 싶은 특정 필드만 선택하는 프로젝션의 기본 사항을 배우게 됩니다. 이는 "포함 (inclusive)" 프로젝션으로 알려져 있습니다.
먼저 MongoDB Shell 을 엽니다. 이 대화형 명령줄 인터페이스에서 이 랩의 모든 데이터베이스 명령을 실행하게 됩니다.
mongosh
이제 MongoDB Shell 안에 있습니다. 연결되었음을 나타내도록 터미널 프롬프트가 변경됩니다. projectlab_database라는 새 데이터베이스로 전환하고 샘플 데이터를 삽입해 보겠습니다. 데이터베이스가 존재하지 않으면 MongoDB 는 데이터를 처음 저장할 때 자동으로 생성합니다.
use projectlab_database
다음으로 users라는 컬렉션에 세 개의 문서를 삽입합니다.
db.users.insertMany([
{
name: "John Doe",
age: 30,
email: "john.doe@example.com",
city: "New York",
job: "Software Engineer"
},
{
name: "Jane Smith",
age: 28,
email: "jane.smith@example.com",
city: "San Francisco",
job: "Data Scientist"
},
{
name: "Mike Johnson",
age: 35,
email: "mike.johnson@example.com",
city: "Chicago",
job: "Product Manager"
}
]);
이제 이 컬렉션을 쿼리해 보겠습니다. 모든 문서에 대해 name 및 age 필드만 반환하려면 find() 메서드에 두 번째 인수로 프로젝션 문서를 추가합니다. 프로젝션 문서에서는 포함할 필드를 1로 지정합니다. 기본적으로 _id 필드는 항상 반환됩니다. 이를 0으로 설정하여 명시적으로 제외할 수 있습니다.
db.users.find({}, { name: 1, age: 1, _id: 0 });
각 사용자에 대해 name 및 age 필드만 포함하는 다음 출력이 표시됩니다.
[
{ "name": "John Doe", "age": 30 },
{ "name": "Jane Smith", "age": 28 },
{ "name": "Mike Johnson", "age": 35 }
]
이 기법은 필요한 데이터만 검색하는 데 매우 유용하며, 특히 대용량 문서를 다룰 때 중요합니다.
결과에서 특정 필드 제외
포함할 필드를 선택하는 것 외에도 제외할 필드를 지정할 수도 있습니다. 이는 "제외 (exclusive)" 프로젝션으로 알려져 있으며, 대부분의 필드를 보고 싶지만 민감하거나 큰 필드와 같이 일부 필드를 숨기고 싶을 때 유용합니다.
여전히 mongosh 쉘에 있고 projectlab_database가 선택된 상태여야 합니다.
필드를 제외하려면 프로젝션 문서에서 해당 필드의 값을 0으로 설정합니다. 모든 사용자 데이터를 가져오지만 email 및 city 필드는 제외하는 쿼리를 실행해 보겠습니다.
db.users.find({}, { email: 0, city: 0 });
출력에는 email 및 city를 제외한 모든 필드가 포함됩니다. _id 필드는 기본적으로 여전히 포함된다는 점에 유의하십시오.
[
{
_id: ObjectId("..."),
name: 'John Doe',
age: 30,
job: 'Software Engineer'
},
{
_id: ObjectId("..."),
name: 'Jane Smith',
age: 28,
job: 'Data Scientist'
},
{
_id: ObjectId("..."),
name: 'Mike Johnson',
age: 35,
job: 'Product Manager'
}
]
프로젝션의 주요 규칙은 동일한 프로젝션 문서에서 포함 (1) 과 제외 (0) 를 혼합할 수 없다는 것입니다. 이 규칙의 유일한 예외는 _id 필드입니다. 이전 단계에서 수행한 것처럼 다른 필드를 포함하는 경우에도 _id 필드를 명시적으로 제외 (_id: 0) 할 수 있습니다. 제외 프로젝션에서도 이를 제외할 수 있습니다.
예를 들어, email, city, _id를 제외하려면 다음을 실행합니다.
db.users.find({}, { email: 0, city: 0, _id: 0 });
이 쿼리는 name, age, job 필드만 포함하는 문서를 반환합니다.
중첩 문서의 필드 프로젝션
실제 데이터는 종종 중첩된 문서와 배열로 구조화됩니다. MongoDB 의 프로젝션 기능을 사용하면 문서 구조 깊숙이 포함된 필드도 정확하게 선택할 수 있습니다.
먼저 기존 users 컬렉션을 삭제하고 중첩된 contact 객체와 skills 배열을 가진 새 문서를 삽입해 보겠습니다.
db.users.deleteMany({});
이제 새 데이터를 삽입합니다.
db.users.insertMany([
{
name: "John Doe",
contact: {
email: "john.doe@example.com",
phone: {
mobile: "123-456-7890",
work: "987-654-3210"
}
},
skills: ["JavaScript", "MongoDB", "React"]
},
{
name: "Jane Smith",
contact: {
email: "jane.smith@example.com",
phone: {
mobile: "234-567-8901",
work: "876-543-2109"
}
},
skills: ["Python", "Data Science", "Machine Learning"]
}
]);
중첩된 문서 내의 필드를 프로젝션하려면 "점 표기법 (dot notation)"을 사용합니다. 예를 들어, 사용자의 name과 모바일 전화번호를 선택하려면 "contact.phone.mobile": 1을 지정합니다. name, email, mobile 전화번호를 프로젝션해 보겠습니다.
db.users.find(
{},
{
name: 1,
"contact.email": 1,
"contact.phone.mobile": 1,
_id: 0
}
);
결과는 선택된 필드에 대해 중첩된 구조를 유지합니다.
[
{
"name": "John Doe",
"contact": {
"email": "john.doe@example.com",
"phone": { "mobile": "123-456-7890" }
}
},
{
"name": "Jane Smith",
"contact": {
"email": "jane.smith@example.com",
"phone": { "mobile": "234-567-8901" }
}
}
]
$slice 연산자를 사용하여 배열의 일부를 프로젝션할 수도 있습니다. 각 사용자의 name과 처음 두 개의 스킬만 가져오려면 다음 명령을 실행합니다.
db.users.find({}, { name: 1, skills: { $slice: 2 }, _id: 0 });
출력에는 각 사용자의 skills 배열에 두 개의 요소만 포함됩니다.
[
{ "name": "John Doe", "skills": ["JavaScript", "MongoDB"] },
{ "name": "Jane Smith", "skills": ["Python", "Data Science"] }
]
Aggregation 을 사용한 출력 필드 재구성
필드 이름 변경 또는 새 계산 필드 생성과 같은 더 고급 변환의 경우 find() 메서드의 프로젝션만으로는 충분하지 않습니다. 대신 집계 프레임워크, 특히 $project 단계를 사용할 수 있습니다.
이 단계를 위해 데이터를 준비해 보겠습니다. 컬렉션을 삭제하고 급여 정보가 있는 사용자를 삽입합니다.
db.users.deleteMany({});
db.users.insertMany([
{
name: "John Doe",
age: 30,
salary: 75000,
department: "Engineering"
},
{
name: "Jane Smith",
age: 28,
salary: 85000,
department: "Data Science"
},
{
name: "Mike Johnson",
age: 35,
salary: 95000,
department: "Management"
}
]);
집계 파이프라인의 $project 단계는 새 필드를 추가하거나, 기존 필드 이름을 변경하거나, 필드를 제거하여 문서를 재구성할 수 있습니다. 사용하려면 스테이지 배열을 aggregate() 메서드에 전달합니다.
필드 이름을 변경하려면 새 필드 이름을 정의하고 기존 필드 값 앞에 $ 기호를 붙여 할당합니다. name을 fullName으로, age를 yearsOld로 변경해 보겠습니다.
db.users.aggregate([
{
$project: {
fullName: "$name",
yearsOld: "$age",
_id: 0
}
}
]);
계산을 기반으로 새 필드를 만들 수도 있습니다. 연간 salary를 12 로 나누어 monthlySalary 필드를 만들고, $switch 연산자를 사용하여 조건부 논리를 기반으로 salaryTier 필드를 만들어 보겠습니다.
db.users.aggregate([
{
$project: {
name: 1,
monthlySalary: { $divide: ["$salary", 12] },
salaryTier: {
$switch: {
branches: [
{ case: { $lt: ["$salary", 80000] }, then: "Junior" },
{ case: { $gte: ["$salary", 80000] }, then: "Senior" }
],
default: "Unknown"
}
},
_id: 0
}
}
]);
출력은 $project 단계에서 정의한 완전히 새로운 문서 구조가 됩니다.
[
{ "name": "John Doe", "monthlySalary": 6250, "salaryTier": "Junior" },
{
"name": "Jane Smith",
"monthlySalary": 7083.333333333333,
"salaryTier": "Senior"
},
{
"name": "Mike Johnson",
"monthlySalary": 7916.666666666667,
"salaryTier": "Senior"
}
]
$project 단계는 쿼리 결과의 최종 구조에 대한 완전한 제어를 제공하여 데이터베이스 내에서 직접 강력한 데이터 변환을 가능하게 합니다.
요약
이 실습에서는 프로젝션을 사용하여 MongoDB 쿼리의 출력을 효과적으로 제어하는 방법을 배웠습니다. find() 메서드를 사용하여 필드를 포함하고 제외하는 기본 사항부터 시작했습니다. 그런 다음 중첩된 문서 내의 필드를 프로젝션하기 위해 점 표기법 (dot notation) 을 사용하고 배열의 하위 집합을 반환하기 위해 $slice 연산자를 사용하는 것과 같은 고급 주제로 진행했습니다. 마지막으로 필드 이름을 변경하고, 새 계산 필드를 만들고, 조건부 논리를 적용하여 문서를 완전히 재구성하기 위해 집계 프레임워크의 $project 단계의 강력함을 탐구했습니다. 이러한 기술은 MongoDB 에서 효율적이고 정확한 쿼리를 작성하는 데 필수적입니다.

