API 응답을 위한 프로젝션 모범 사례
REST API 응답 형태에 맞는 프로젝션을 설계하여 페이로드 크기를 줄이고 민감한 필드를 보호합니다.
API 응답을 위한 프로젝션 모범 사례은(는) CoddyKit의 무료 MongoDB Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 MongoDB Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. MongoDB Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Align Projections With API Response Shapes
Every REST or GraphQL endpoint your application exposes has a defined response shape. The ideal MongoDB projection returns exactly the fields that shape requires—no more, no less. When your projection mirrors your API response contract, you avoid two common anti-patterns: over-fetching (returning fields the endpoint never sends) and under-fetching (returning fields that force a second query).
Define Projection Constants
Hardcoding projection objects inline in every query leads to duplication and drift over time. Define projection constants alongside your data access functions or repositories. If the API response shape changes, you update one constant rather than hunting for every query in the codebase.
// projections.js — centralised projection definitions
export const USER_PUBLIC = { _id: 0, username: 1, avatarUrl: 1, createdAt: 1 };
export const USER_PROFILE = { _id: 0, username: 1, email: 1, bio: 1, avatarUrl: 1 };
export const USER_ADMIN = { _id: 0, username: 1, email: 1, role: 1, lastLoginAt: 1, isActive: 1 };
// Usage
const user = await db.collection('users').findOne({ username: 'alice' }, { projection: USER_PROFILE });Never Return Sensitive Fields to Clients
Fields like passwordHash, totpSecret, apiKey, ssn, and paymentMethodToken should never appear in API responses. Define a secure base projection that excludes them by default, and only fetch them in internal service calls that specifically require them. Apply the principle of least privilege at the data layer.
// Always exclude sensitive fields from user queries
const SECURE_USER_BASE = {
passwordHash: 0,
totpSecret: 0,
resetToken: 0
};
// All user API responses go through this projection
const user = await db.collection('users').findOne(
{ _id: userId },
{ projection: SECURE_USER_BASE }
);
// Result never contains passwordHash or totpSecretList Endpoints: Project Only Summary Fields
List endpoints (e.g., GET /products) typically return a summary of each item, not the full document. A product list might show name, price, thumbnailUrl, and rating—not the full description, specifications array, or reviews. Using a tight projection for list queries can reduce payload size by 90% when full documents contain large text or arrays.
const PRODUCT_SUMMARY = {
_id: 0,
slug: 1,
name: 1,
price: 1,
thumbnailUrl: 1,
rating: 1,
reviewCount: 1
};
// GET /products — lightweight list query
const products = await db.collection('products')
.find({ category: 'electronics', isActive: true })
.projection(PRODUCT_SUMMARY)
.sort({ rating: -1 })
.limit(20)
.toArray();Detail Endpoints: Project the Full Object
Detail endpoints (e.g., GET /products/:slug) return a richer view of a single document. Even here, consider excluding internal-only fields. You might project all public fields while suppressing internal cost price, supplier contact details, or inventory source system IDs that clients should not see.
const PRODUCT_DETAIL = {
supplierCost: 0, // internal — never expose to clients
warehouseLocation: 0, // internal
syncedFromErpAt: 0 // internal audit field
};
// GET /products/:slug — rich detail query
const product = await db.collection('products').findOne(
{ slug: req.params.slug, isActive: true },
{ projection: PRODUCT_DETAIL }
);Use Projections in Aggregation Pipelines Too
Projection best practices extend to the aggregation pipeline. Place a $project stage after $match and before expensive stages like $lookup or $unwind to reduce the document size flowing through the pipeline. Smaller documents in the pipeline mean less memory and CPU usage on the server.
db.orders.aggregate([
{ $match: { status: 'shipped', customerId: ObjectId('c1') } },
// Project early to reduce document size before $lookup
{ $project: { total: 1, createdAt: 1, customerId: 1, _id: 0 } },
{
$lookup: {
from: 'customers',
localField: 'customerId',
foreignField: '_id',
as: 'customer'
}
}
]);Projections and API Versioning
When you add new fields to MongoDB documents, old API clients may not expect them. Using strict inclusion projections (listing exactly the fields to return) means new document fields are invisible to existing API consumers until you explicitly add them to the projection. This gives you a natural versioning boundary: update the projection when you update the API version.
// v1 projection — stable contract for existing clients
export const USER_V1 = { _id: 0, username: 1, email: 1 };
// v2 projection — includes new avatarUrl and bio fields
export const USER_V2 = { _id: 0, username: 1, email: 1, avatarUrl: 1, bio: 1 };Test That Projections Match Response Schemas
Write unit tests that assert the MongoDB projection object matches your API response schema (e.g., a Joi schema or a TypeScript type). This prevents the common bug where a developer adds a field to the API response type but forgets to include it in the projection—the field comes back as undefined in production while passing TypeScript type checks.
// Example test asserting projection covers all required response fields
const USER_RESPONSE_FIELDS = ['username', 'email', 'avatarUrl'];
const projection = { username: 1, email: 1, avatarUrl: 1, _id: 0 };
for (const field of USER_RESPONSE_FIELDS) {
if (projection[field] !== 1) {
throw new Error('Projection missing field: ' + field);
}
}
console.log('Projection covers all required response fields');Avoiding Projection Mismatch in Mongoose
Mongoose schemas with select: false on a field prevent that field from appearing in any query result unless explicitly re-included. Combine this with schema-level virtuals to compute derived values without storing them. Together, these tools let you enforce a secure default projection at the model level, reducing the chance of accidentally leaking data through a missing query projection.
const userSchema = new mongoose.Schema({
username: String,
email: String,
// Excluded from all queries by default — must explicitly use +passwordHash
passwordHash: { type: String, select: false },
// Virtual — computed, not stored, never in DB
get displayName() { return this.username.toUpperCase(); }
});
userSchema.virtual('displayName').get(function() {
return this.username.toUpperCase();
});Monitoring Projection Efficiency
Use explain('executionStats')
nReturned vs keysExamined and docsExamined ratio. If docsExamined equals the number of matched documents (not zero), your projection is not covered by an index but is still correct—you can weigh whether adding a covering index is worth the maintenance cost.const result = await db.collection('users').find(
{ role: 'admin' },
{ projection: { username: 1, email: 1, _id: 0 } }
).explain('executionStats');
console.log('Docs examined:', result.executionStats.totalDocsExamined);
console.log('Keys examined:', result.executionStats.totalKeysExamined);
console.log('Docs returned:', result.executionStats.nReturned);Summary: Projection Best Practice Checklist
Apply this checklist to every MongoDB query in your API:
- Define named projection constants — one per endpoint or response shape
- Use inclusion mode for API responses — list exactly what you need
- Always exclude sensitive fields — passwordHash, tokens, internal IDs
- Use tight projections for list endpoints — summary only, no large bodies
- Place $project early in aggregation pipelines — reduce data flowing downstream
Quick Check
Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.
Lesson Recap
In this lesson you learned: projection constants should be defined per API endpoint to prevent drift, sensitive fields must always be excluded from client-facing queries, and placing $project early in aggregation pipelines reduces memory pressure. Next up we explore sorting and pagination to order and page through large result sets efficiently.
AI 튜터와 함께 JavaScript을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 30
- 레슨
- 120
자주 묻는 질문
“API 응답을 위한 프로젝션 모범 사례” 강의는 무료인가요?
네 — “API 응답을 위한 프로젝션 모범 사례” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 MongoDB Academy 강의 전체를 잠금 해제할 수 있습니다. MongoDB Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“API 응답을 위한 프로젝션 모범 사례”에서 뭘 배우나요?
REST API 응답 형태에 맞는 프로젝션을 설계하여 페이로드 크기를 줄이고 민감한 필드를 보호합니다. 브라우저에서 직접 실행하는 실습 코드로 MongoDB Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
MongoDB Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 MongoDB Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“API 응답을 위한 프로젝션 모범 사례” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 MongoDB Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 MongoDB Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 포함 및 제외 프로젝션 비교
- 중첩 및 배열 필드 프로젝션
- $ 및 $elemMatch 배열 프로젝션
- API 응답을 위한 프로젝션 모범 사례