

처음 부분은 신기능에 대한 세일즈 부분이지만 개발팀으로 알고 있으면 좋을 점이 있어 공유한다.
MongoDB 8.0의 핵심은 “성능 향상”이다. 개발자가 코드를 바꾸지 않아도, 데이터베이스 자체가 업그레이드되어 더 빨라졌다는 것을 의미한다. 가장 큰 변화는 두 가지다.



데이터를 읽어오는 내부 명령 실행 경로에서 불필요한 단계를 제거해 처리 속도를 크게 높였다. 마치 목적지까지 가는 길에 있던 신호등과 비보호 좌회전을 없애고 직선 도로를 뚫어준 것과 같다.
가장 체감이 큰 부분은 단일 문서를 찾는 쿼리가 매우 빨라졌다는 점이다.
findById**가 17% 더 빨라졌어요!_id를 사용해 문서를 찾는 것은 MongoDB에서 가장 흔한 작업 중 하나다. 공유해준 코드처럼 우리 프로젝트에서도 많이 사용한다.
TypeScript// 우리가 매일 쓰는 이 코드, 이제 17% 더 빨라진다! async fetchProductById(id: number): Promise<ProductFindOneResponseDto> { return await this.productModel.findById(id); } // 파트너, 리뷰 조회도 마찬가지! 코드 수정 없이 자동 성능 향상! async getPartnerById(partnerId: string) { return await this.partnerModel.findById(partnerId); }
MongoDB 8.0으로 업데이트하는 것만으로 이 코드들의 성능이 자동으로 향상된다.
이것이 바로 이번 성능 향상의 핵심이다. 아주 쉬운 예시로 설명한다.
도서관 사서를 생각해보자.
도서 관리 번호(_id에 해당)로 책을 찾는 데는 전문가다. 번호를 주면 즉시 찾아온다. 하지만 책 제목이나 저자 이름으로 찾아달라고 하면, 색인 카드를 뒤적이는 등 조금 더 복잡하고 느린 방법으로 찾아야 했다.도서 관리 번호든, 책 제목이든, 저자 이름이든 어떤 정보로 물어봐도 똑같이 빠르고 효율적인 방법(Express Path)으로 책을 바로 찾아온다.
즉, ’모든 필드 매치 지원’이란 _id로 조회할 때만 적용되던 빠른 길이 이제는 어떤 필드로 조회하든 똑같이 적용된다는 의미다.TypeScript// 1. _id로 찾기 (전에도 빨랐지만, 17% 더 빨라짐) db.products.findOne({ _id: "unique-product-id-123" }); // 2. 다른 필드로 찾기 (이제 _id만큼 빨라짐! 이게 '모든 필드 매치'의 힘) db.products.findOne({ product_name: "맛있는 닭가슴살" }); db.users.findOne({ email: "seungchan@example.com" });
결론적으로, 이제는 findById 뿐만 아니라 findOne({ email: ... }) 이나 findOne({ name: ... }) 같은 다양한 쿼리들도 별도의 최적화 없이 자동으로 빨라지는 큰 이점을 얻게 된 것이다.
쓰기 기능에서 클러스터 복제 과정이 훨씬 효율적으로 바뀌었다. 특히 우리에게 중요한 점은 대량 쓰기(bulk write) 성능이 56% 향상되었다는 것이다.
매일 엑셀 파일로 처리하는 대량 상품 업데이트 작업을 예로 들 수 있다.
TypeScript// 현재 우리 코드 - 엑셀 파일로 대량 상품 업데이트 시 async bulkUpdateProducts(products: ProductUpdateDto[]) { const bulkOps = products.map(product => ({ updateOne: { filter: { _id: product.id }, update: { $set: product.data }, upsert: true } })); // MongoDB 8.0에서는 이 작업이 56% 더 빨라진다! return await this.productModel.bulkWrite(bulkOps); }
이처럼 기존 코드를 전혀 바꾸지 않아도, MongoDB 8.0을 사용하는 것만으로 대량 데이터 처리 속도가 크게 향상된다.
시계열 데이터 처리 성능은 200% 이상 향상되었다. 이전에는 데이터를 하나씩 처리했다면, 이제는 여러 데이터를 한 묶음으로 처리(batch approach)하는 방식으로 바뀌었기 때문이다.
이는 마치 택배 기사가 물건 하나 배송할 때마다 물류센터에 다녀오는 대신, 한 동네 물건을 모두 모아서 한 번에 배송하는 것과 같다. 이 방식으로 실시간 주문 데이터나 사용자 행동 로그 같은 시계열 데이터의 집계 쿼리 속도가 극적으로 빨라졌다.
TypeScript// 예시: 특정 기간 동안의 사용자 시간대별 활동 집계 // 이런 롤업(Roll-up) 작업이 200% 이상 빨라진다. async aggregateUserActions(startDate: Date, endDate: Date) { return await this.userActionsCollection.aggregate([ // ... (집계 로직) ... ]); }
이러한 성능 향상 덕분에 IoT, 금융 분석처럼 데이터가 끊임없이 쏟아지는 환경에서도 적은 리소스로 빠른 분석이 가능해진다.
데이터 분산 속도가 50배 빨라지고, 시작 비용은 50% 줄었다. 이는 Config Shard라는 새로운 개념 덕분이다.
샤딩은 하나의 거대한 데이터를 여러 개의 작은 조각으로 나누어 여러 서버에 분산 저장하는 기술이다.
도서관을 예로 들어보자.
더 중요한 것은 moveCollection 명령어로 데이터를 자유롭게 이동시킬 수 있다는 점이다.
TypeScript// '브랜드 A'의 모든 상품 데이터를 전용 서버(샤드)로 이동시켜 완벽히 분리 async isolateBrandData(brandId: string) { await db.admin().command({ moveCollection: `brand_${brandId}.products`, toShard: `brand-${brandId}-dedicated-shard` }); }
이 기능을 활용하면 특정 고객사의 데이터를 물리적으로 분리하여 안정성과 성능을 높일 수 있다.
MongoDB 8.0의 쿼리 가능 암호화(Queryable Encryption)는 데이터가 암호화된 상태에서도 범위 검색(>, <)을 지원한다. 데이터베이스 관리자나 해커가 서버에 직접 접근해도 암호화된 쓰레기 값만 보일 뿐, 실제 내용은 절대 알 수 없다.
우리 파트너 시스템의 민감한 급여 정보를 예로 들어보자.
TypeScript// '급여' 필드는 암호화되어 DB에 저장된다. // 'range' 옵션 덕분에 암호화된 상태로 범위 검색이 가능하다. const encryptedFields = { fields: [ { path: "salary", bsonType: "int", queries: { queryType: "range" } // 이게 핵심! } ] }; // 실제 사용 예시 // "급여가 300만원에서 500만원 사이인 파트너 찾기" async findPartnersInSalaryRange(min: number, max: number) { return await this.partnerSalaryModel.find({ salary: { $gte: min, $lte: max } }); }
이 쿼리가 실행될 때, MongoDB 서버는 3000000이나 5000000이라는 실제 숫자를 전혀 보지 못한다. 그저 암호화된 값(A#@!, B$*&)끼리 대소를 비교하여 결과를 반환할 뿐이다. 이는 애플리케이션은 정상적으로 동작하면서도, 데이터베이스 단에서는 완벽한 보안을 유지할 수 있음을 의미한다.
Atlas 클라우드 서비스가 대폭 개선되었다. 수직적 확장 (서버의 사양을 높이는 것) 속도가 50% 빨라졌고, 트래픽에 따라 자동으로 서버 자원을 조절하는 오토스케일링의 반응 속도는 5배 향상되었다.
이는 블랙프라이데이처럼 갑자기 사용자가 몰리는 상황에서, 시스템이 훨씬 더 빠르고 안정적으로 대응할 수 있게 되었음을 의미한다.







최신 검색 기술은 이전 기술을 대체하는 것이 아니라, 서로의 단점을 보완하며 함께 발전한다.

과거에는 키워드 검색과 의미 검색을 각각 실행한 후, 개발자가 복잡한 코드를 작성하여 두 결과를 합치고 점수를 다시 매겨야 했다.
MongoDB 8.1의 $rankFusion은 이 모든 과정을 자동화한다. 개발자는 어떤 검색 방식을 얼마의 가중치로 섞을지만 지정하면 된다.


TypeScript// $rankFusion 예시: 복잡한 로직 없이 하이브리드 검색 구현 async hybridSearch(query: string) { return await this.collection.aggregate([ { $rankFusion: { pipelines: [ // Pipeline 1: 키워드 검색 (정확도 담당) [ { $search: { /* ... */ } } ], // Pipeline 2: 의미 검색 (유사성 담당) [ { $vectorSearch: { /* ... */ } } ] ], weights: [0.4, 0.6] // 키워드 검색에 40%, 의미 검색에 60% 가중치 부여 } } ]); }
발표자가 든 예시가 딱 우리 상황이었다.
“지난달에 샀던 그 러닝화랑 비슷한 제품 추천해줘”라는 요청을 처리한다고 생각해보자.
TypeScript// 우리 상품 추천 시스템 개선안 class ProductRecommendationService { async findSimilarProducts(userId: string, productId: string) { // 1. 사용자가 구매한 상품 정보 가져오기 const purchasedProduct = await this.orderModel.findOne({ userId, 'items.productId': productId }); // 2. 해당 상품의 임베딩 생성 (또는 미리 저장된 것 사용) const productEmbedding = await this.getProductEmbedding(purchasedProduct); // 3. 하이브리드 검색으로 유사 상품 찾기 return await this.productModel.aggregate([ { $rankFusion: { pipelines: [ // 카테고리와 브랜드가 같은 상품 (정확도) [ { $match: { category: purchasedProduct.category, brand: purchasedProduct.brand, _id: { $ne: productId } } } ], // 의미적으로 유사한 상품 (벡터 검색) [ { $vectorSearch: { queryVector: productEmbedding, path: "description_embedding", numCandidates: 200, filter: { _id: { $ne: productId } } } } ] ], weights: [0.3, 0.7] // 유사성에 더 높은 가중치 } }, { $limit: 20 } ]); } }
RAG(Retrieval-Augmented Generation)는 AI가 답변을 생성하기 전에, 먼저 관련 정보를 데이터베이스에서 검색하여 그 내용을 바탕으로 더 정확한 답변을 만드는 기술이다. 이 기술은 3단계로 진화한다.
가장 기초적인 RAG. 단순히 질문과 관련 있어 보이는 문서를 찾아 그 내용을 그대로 LLM(거대 언어 모델)에게 전달하여 답변을 생성하게 한다.
TypeScript// Naive RAG의 작동 방식 class NaiveRAG { async answer(question: string) { // 1. 질문과 유사한 문서를 단순 검색한다. const docs = await this.vectorSearch(question); // 2. 검색된 문서와 질문을 LLM에 그대로 전달한다. return await this.llm.generate(docs, question); } }
Naive RAG의 문제점을 해결하기 위해 여러 단계를 추가하여 LLM에 전달할 정보를 정교하게 다듬는다.
TypeScript// Advanced RAG의 작동 방식 class AdvancedRAG { async answer(question: string) { // 1. 질문을 더 명확하게 만들고 확장한다. const refinedQuestion = await this.refineAndExpand(question); // 2. 하이브리드 검색으로 최적의 정보를 찾는다. const docs = await this.hybridSearch(refinedQuestion); // 3. 찾은 정보 중 가장 중요한 순서로 재정렬하고 요약한다. const compressedContext = await this.rerankAndCompress(docs); // 4. 정제된 최종 정보와 질문을 LLM에 전달한다. return await this.llm.generate(compressedContext, question); } }
AI가 단순한 정보 검색을 넘어, 스스로 계획을 세우고 여러 도구를 자율적으로 사용하여 문제를 해결하는 단계다.
TypeScript// Agentic RAG의 작동 방식 class AgenticRAG { async answer(question: string) { // 1. 질문의 의도를 파악하고, 해결을 위한 계획을 세운다. const plan = await this.createPlan(question); // 2. 계획에 따라 필요한 도구(검색, API 호출, 계산기 등)를 실행한다. const results = await this.executeTools(plan); // 3. 모든 도구의 실행 결과를 종합하여 최종 답변을 만든다. const finalAnswer = await this.synthesize(results); // 4. 이 대화를 기억하여 다음을 대비한다. await this.updateMemory(question, finalAnswer); return finalAnswer; } }
MongoDB 컨퍼런스에서는 Atlas를 활용하여 실제 비즈니스 문제를 해결한 여러 기업의 성공 사례가 공유되었다.
글로벌 제약사 Novo Nordisk는 MongoDB Atlas를 중심으로 통합 데이터 아키텍처를 구축하여 신약 연구 개발 프로세스를 획기적으로 단축했다.
발표에서 특히 강조된 점은, 처음에는 관계형 데이터베이스로 시도했다가 실패했다는 사실이다. 수십 개의 테이블을 조인(join)하는 과정에서 성능 저하가 심각했고, 고정된 스키마 구조 때문에 새로운 데이터를 추가하거나 변경하는 것이 어려워 빠른 실험이 불가능했다.
반면, MongoDB의 유연한 문서 모델은 복잡한 연구 데이터를 있는 그대로 저장하고 빠르게 수정할 수 있게 해주었다. 이를 통해 RAG(검색 증강 생성) 시스템을 성공적으로 구현하여, 이전에 12주가 걸리던 분석 작업을 단 10분 만에 완료하는 성과를 거두었다.
미디어 인텔리전스 기업 Meltwater는 전 세계 뉴스, 소셜 미디어 등에서 발생하는 하루 10억 건 이상의 데이터를 실시간으로 수집하고 분석한다.
이 사례의 핵심은 Spark나 Flink 같은 별도의 스트림 처리 프레임워크 없이, MongoDB Atlas Stream Processing 기능만으로 이 모든 대용량 데이터를 처리했다는 점이다. 이는 데이터 파이프라인을 단순화하여 인프라 복잡성과 운영 비용을 크게 절감하는 효과를 가져온다. 데이터를 한곳에 모아 저장, 처리, 분석까지 모두 해결한 것이다.
LG U+는 AI 컨택센터(AICC)의 상담원 지원 시스템(Agent Assist System)에 MongoDB Atlas를 도입하여 큰 성공을 거두었다. 이 시스템은 고객과의 통화 내용을 실시간으로 분석하여 상담원에게 최적의 답변 가이드라인을 제공한다.












시스템의 작동 방식은 다음과 같다.
TypeScript// LG U+ AICC 아키텍처 (발표 내용 기반 재구성) class AgentAssistSystem { constructor( private atlas: MongoClient, private vectorSearch: AtlasVectorSearch, private llm: LLMService ) {} async assistAgent(callId: string, customerQuery: string) { // 1. 실시간 고객 의도 파악 const intent = await this.detectIntent(customerQuery); // 2. 고객 이력 조회 - MongoDB의 유연한 스키마 활용 const customerHistory = await this.atlas.collection('customers').findOne({ phoneNumber: callId, // 복잡한 중첩 구조의 고객 데이터 }); // 3. 관련 매뉴얼/FAQ 검색 - Vector Search const relevantDocs = await this.vectorSearch.search({ queryVector: await this.getEmbedding(customerQuery), path: "content_embedding", filter: { category: intent.category, confidence: { $gte: 0.8 } } }); // 4. 답변 가이드라인 생성 const guideline = await this.generateGuideline( intent, customerHistory, relevantDocs ); // 5. 상담원에게 실시간 제공 return { suggestedResponse: guideline.response, relatedPolicies: guideline.policies, customerSentiment: guideline.sentiment, nextBestAction: guideline.nextAction }; } }
이 시스템 도입 결과, 상담 콜당 평균 처리 시간이 7% 감소하고 자원 효율성은 30% 향상되었으며, 상담원 만족도 역시 크게 개선되었다고 한다.
Anthropic이 만든 MCP를 MongoDB가 지원한다는 발표가 있었다. 이게 왜 대단한가?

실제 데모가 인상적이었다. VS Code에서 Cursor AI를 통해,
TypeScript// 개발자: "지난 달 매출이 가장 높은 상품 10개 찾아줘" // AI가 생성한 코드: async function getTopSellingProducts() { const lastMonth = new Date(); lastMonth.setMonth(lastMonth.getMonth() - 1); return await db.collection('orders').aggregate([ { $match: { createdAt: { $gte: lastMonth } } }, { $unwind: "$items" }, { $group: { _id: "$items.productId", totalRevenue: { $sum: { $multiply: ["$items.quantity", "$items.price"] } }, totalQuantity: { $sum: "$items.quantity" } } }, { $sort: { totalRevenue: -1 } }, { $limit: 10 }, { $lookup: { from: "products", localField: "_id", foreignField: "_id", as: "productInfo" } } ]); }
AI가 컨텍스트를 이해하고, 스키마를 파악하고, 최적의 쿼리를 생성한다.
발표에서 가장 충격적이었던 부분이 “이제는 Test-Driven이 아니라 Evaluation-Driven이다”라는 메시지였다.
TypeScript// 기존: Unit Test (결정론적) describe('ProductService', () => { it('should return product by id', async () => { const product = await service.getProductById('123'); expect(product.id).toBe('123'); expect(product.name).toBe('Test Product'); }); }); // AI 시대: Evaluation Metrics (확률적) class RAGEvaluator { async evaluateResponse(question: string, answer: string, groundTruth: string) { // 1. Relevance Score - 답변이 질문과 얼마나 관련 있는가? const relevance = await this.calculateRelevance(question, answer); // 2. Faithfulness Score - 답변이 소스 문서에 충실한가? const faithfulness = await this.calculateFaithfulness(answer, groundTruth); // 3. Hallucination Score - 환각이 얼마나 있는가? const hallucination = await this.detectHallucination(answer, groundTruth); // 4. Completeness Score - 답변이 완전한가? const completeness = await this.calculateCompleteness(answer, groundTruth); return { relevance, // 0.92 faithfulness, // 0.88 hallucination, // 0.05 (낮을수록 좋음) completeness, // 0.85 overall: (relevance + faithfulness + completeness - hallucination) / 3 }; } // A/B 테스트로 모델 성능 비교 async compareModels(modelA: LLM, modelB: LLM, testCases: TestCase[]) { const resultsA = []; const resultsB = []; for (const testCase of testCases) { const answerA = await modelA.generate(testCase.question); const answerB = await modelB.generate(testCase.question); resultsA.push(await this.evaluateResponse( testCase.question, answerA, testCase.groundTruth )); resultsB.push(await this.evaluateResponse( testCase.question, answerB, testCase.groundTruth )); } return { modelA: this.calculateAverage(resultsA), modelB: this.calculateAverage(resultsB), winner: this.determineWinner(resultsA, resultsB) }; } }
컨퍼런스를 통해 본 MongoDB의 미래 방향성
TypeScript// 미래의 개발 방식 예측 class FutureDevWorkflow { // 1. 자연어 요구사항을 코드로 async requirementToCode(requirement: string) { // "사용자가 최근 본 상품과 유사한 상품 5개를 추천해줘" const code = await this.ai.generateCode(requirement); return code; } // 2. 코드가 스스로 최적화 async selfOptimizingQuery(query: AggregationPipeline) { // 쿼리 실행 패턴 분석 const patterns = await this.analyzeQueryPatterns(query); // 자동 인덱스 제안 const indexes = await this.suggestIndexes(patterns); // 쿼리 리팩토링 const optimizedQuery = await this.refactorQuery(query, patterns); return optimizedQuery; } // 3. 데이터가 스스로 구조 결정 async adaptiveSchema(data: any[]) { // 데이터 패턴 분석 const patterns = await this.analyzeDataPatterns(data); // 최적 스키마 제안 const schema = await this.suggestSchema(patterns); // 자동 마이그레이션 await this.migrateToNewSchema(schema); } }
컨퍼런스에서 제시된 핵심 메시지는 “진정한 현대화는 데이터베이스의 종류를 바꾸는 것이 아니라, 데이터의 구조 자체를 재설계하는 것”이라는 점이다.


“오라클에서 PostgreSQL로 옮기는 건 현대화가 아니다. 그건 그냥 비용 절감일 뿐이다. 진정한 현대화는 데이터 모델을 완전히 재설계하는 것이다.”
10년 이상 된 레거시 시스템의 가장 큰 문제는 관계형 데이터베이스의 정규화(Normalization) 구조에 있다.
과거에는 저장 공간이 비쌌기 때문에, 데이터의 중복을 최소화하는 것이 매우 중요했다. 이를 위해 ’주문’이라는 하나의 개념을 주문 기본 정보, 주문 상품, 상태 변경 이력, 결제 정보 등 20개 이상의 잘게 쪼개진 테이블에 나누어 저장했다.
MongoDB와 같은 문서 데이터베이스는 하나의 주문과 관련된 모든 정보를 하나의 ‘문서(Document)’ 안에 저장하는 임베딩(Embedding) 패턴을 사용한다.
발표자는 레거시 시스템을 성공적으로 현대화하기 위한 4단계 접근법을 제시했다.
“우리 시스템의 사용 설명서를 역으로 만들기”
이 단계의 목표는 현재 시스템이 ’어떻게 사용되고 있는지’를 완벽하게 파악하는 것이다. 데이터베이스 스키마만 보는 것이 아니라, 실제 애플리케이션이 데이터를 어떻게 읽고 쓰는지를 분석한다.
orders와 order_items는 거의 항상 같이 쓰인다.)“새로운 설계도로 모형 집 지어보기”
분석 단계에서 설계한 새로운 데이터 모델이 실제로 잘 작동하는지 검증하는 단계다. 전체 시스템을 바꾸기 전에, 핵심 기능에 대해 개념 증명(POC, Proof of Concept)을 진행한다.
“오래된 데이터를 새로운 그릇에 옮겨 담기”
기존 관계형 데이터베이스의 여러 테이블에 흩어져 있는 데이터를 새로운 MongoDB 문서 구조에 맞게 변환하고 옮기는 단계다. 보통 ETL(Extract, Transform, Load) 파이프라인을 구축하여 이 작업을 수행한다.
“안전하게 이사하기”
실제 운영 환경의 트래픽을 기존 시스템에서 새로운 시스템으로 전환하는, 가장 중요하고 위험도가 높은 단계다. 서비스 중단 없이 안전하게 전환하기 위한 전략이 필수적이다.
MongoDB 스키마 설계를 이해하는 가장 좋은 방법은 자동차에 비유하는 것이다.


이러한 차이 때문에 MongoDB의 스키마 설계는 ’어떻게 데이터를 효율적인 완성품으로 만들 것인가’에서 출발하며, 이 판단의 가장 중요한 기준이 바로 워크로드이다.
아래 이미지가 이 MongoDB라는 것을 가장 직관적으로 표현한 것이 아닐까라는 생각을 한다.

워크로드란 애플리케이션이 데이터를 어떻게, 얼마나 자주 읽고 쓰는지에 대한 총체적인 분석이다. 이는 단순히 데이터의 관계를 넘어, 실제 비즈니스 로직과 사용자 행동이 데이터베이스에 어떤 영향을 미치는지를 파악하는 과정이다.


훌륭한 셰프가 만들 요리(워크로드)를 먼저 고려하여 주방을 설계하듯, MongoDB 스키마는 애플리케이션의 고유한 워크로드에 맞춰 최적화되어야 한다. 이를 위해 비즈니스 요구사항 분석, 데이터 관계도(DRD) 작성 후, 예상되는 부하와 데이터 사용 패턴을 포함한 상세한 워크로드 분석이 선행되어야 한다.
워크로드를 분석할 때는 아래와 같은 구체적인 질문들을 통해 데이터의 특성을 다각도로 파악해야 한다. 이 질문들에 대한 답이 모여 임베딩과 레퍼런싱을 결정하는 중요한 근거가 된다.


데이터가 애플리케이션 내에서 어떻게 함께 사용되는지를 분석한다.



데이터의 크기와 앞으로 어떻게 변할지를 예측한다.
데이터의 생명주기나 비즈니스 규칙을 고려한다.
효과적인 스키마 설계를 위한다면 아래 질문들을 통해 워크로드와 데이터의 특성을 파악할 수 있을 것이다.
따라서 위의 질문들을 통해 워크로드와 데이터의 특성을 파악하고 최적의 패턴을 선택할 수 있다.




데이터를 하나의 문서 안에 포함시키는 방식으로, 데이터 간의 관계가 명확하고 함께 사용될 때 적합하다.
TypeScript// 임베딩 패턴 - 함께 조회되는 데이터 interface BlogPost { _id: ObjectId; title: string; content: string; author: { id: string; name: string; avatar: string; // 자주 필요한 정보만 임베딩 }; comments: Comment[]; // 1:N 관계, 함께 조회 tags: string[]; // 1:Few 관계 }
문서에 데이터 전체가 아닌, 다른 문서의 ID값만 저장하여 연결하는 방식이다.
TypeScript// 레퍼런싱 패턴 - 독립적으로 관리되는 데이터 interface Product { _id: ObjectId; name: string; price: number; categoryId: ObjectId; // 카테고리는 별도 컬렉션 supplierId: ObjectId; // 공급업체도 별도 컬렉션 // 리뷰는 너무 많을 수 있으므로 별도 컬렉션 } interface Review { _id: ObjectId; productId: ObjectId; // 레퍼런스 userId: ObjectId; // 레퍼런스 rating: number; comment: string; createdAt: Date; }
필요에 의해 임베딩과 래퍼런싱을 동시에 쓰는 방식이다.
TypeScript// 하이브리드 패턴 - 성능 최적화 interface UserProfile { _id: ObjectId; username: string; email: string; // 최근 주문 5개만 임베딩 (자주 조회) recentOrders: Order[]; // 전체 주문은 레퍼런스 (필요시에만 조회) allOrderIds: ObjectId[]; // 통계 정보는 미리 계산해서 저장 (비정규화) stats: { totalOrders: number; totalSpent: number; memberSince: Date; lastOrderDate: Date; }; }
워크로드 분석을 통해 기본적인 임베딩, 레퍼런싱, 하이브리드 패턴을 넘어 다음과 같은 고급 패턴들을 적용할 수 있다.



코코네는 서비스 성능 저하 문제에 대응하는 과정에서 단순한 하드웨어 증설의 한계를 경험하고, 데이터 모델과 애플리케이션 구조를 근본적으로 개선하여 문제를 해결한 사례이다.
MongoDB의 칭찬만 하는 것이 아니라 장단점과 한계 실패사례를 잘 보여준 케이스인 것 같아 첨부한다.
초기 성능 저하 문제에 대응하기 위해 DB의 사양(IOPS)을 계속 올리는 방식을 택했지만, 이는 또 다른 문제를 발생시켰다.
근본적인 원인이 하드웨어가 아닌 데이터 모델에 있음을 파악하고 대대적인 리팩토링을 진행했다.
Aggregation Pipeline을 과도하게 사용하여 성능 부하가 심했다.UserId 기반의 효율적인 단일 인덱스로 변경했다.Aggregation 쿼리를, 인덱스와 프로젝션(Projection, 필요한 필드만 조회)을 활용하는 효율적인 find 쿼리로 대부분 전환했다.





캐시워크는 MongoDB Atlas의 다양한 고급 기능을 적극적으로 활용하여 서비스 효율을 높이고 있다. 실무에서 유용하게 사용될 수 있는 주요 기능들을 소개해주셨다.





