무제한 배열 안티 패턴
임베딩으로 인해 문서가 한없이 커지는 경우를 파악하고, 대신 참조를 사용하도록 스키마를 재구성합니다.
무제한 배열 안티 패턴은(는) CoddyKit의 무료 MongoDB Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 MongoDB Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. MongoDB Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
제한 없는 배열이란 무엇인가요
제한 없는 배열은 시간이 지나면서 끝없이 커질 수 있는 문서 내부의 배열 필드입니다. 스키마 설계에서 배열에 상한 없이 항목이 계속 쌓이도록 허용한다면, 예를 들어 사용자 문서에 모든 사용자 댓글을 저장하거나 이벤트 문서에 모든 로그 항목을 저장한다면 제한 없는 배열 안티 패턴에 해당합니다. 이는 MongoDB에서 가장 흔한 설계 실수 중 하나입니다.
16MB 문서 크기 제한
MongoDB는 문서 크기를 16MB로 엄격하게 제한합니다. 제한 없는 배열은 시간이 지나면서 해당 문서의 크기를 키웁니다. 모든 메시지, 모든 활동 로그 항목 또는 모든 구매 이력을 임베딩한 사용자 문서는 결국 이 한도에 도달합니다. 한도에 도달하면 삽입이 BSONObjectTooLarge 오류와 함께 실패하며, 스키마를 다시 설계하지 않고는 문제를 우아하게 해결할 방법이 없습니다.
전형적인 안티 패턴 예시
사용자의 모든 댓글을 사용자 문서 안에 직접 임베딩한다고 가정해 보겠습니다. 새 댓글이 추가될 때마다 comments 배열에 요소가 하나씩 더해집니다. 활동량이 많은 사용자는 몇 달 동안 수천 개의 댓글을 작성할 수 있습니다. 이 스키마는 처음에는 문제가 없어 보이지만 문서가 제한 없이 커집니다.
// ANTI-PATTERN: comments array grows forever
db.users.insertOne({
_id: ObjectId('u1'),
name: 'Alice',
comments: [
{ postId: ObjectId('p1'), text: 'Great article!', createdAt: new Date() },
{ postId: ObjectId('p2'), text: 'I disagree...', createdAt: new Date() }
// ... potentially thousands more
]
});제한에 도달하기 전에도 성능 저하
16MB에 도달하기 전에도 큰 문서는 성능을 저하시킵니다. MongoDB는 필드 하나만 필요하더라도 모든 작업에서 문서 전체를 메모리로 읽어야 합니다. 댓글 만 개가 임베딩된 사용자 문서는 RAM과 I/O를 낭비합니다. 또한 문서가 커지면 WiredTiger가 문서를 새로운 저장 위치로 이동시키므로 단편화와 쓰기 증폭이 발생합니다.
제한 없는 배열로 인한 인덱스 비대화
MongoDB는 인덱싱된 배열의 각 요소마다 다중 키 인덱스 항목을 생성합니다. 만 개까지 커지는 배열에서 comments.text를 인덱싱하면 사용자마다 인덱스에 만 개의 항목이 포함됩니다. 이로 인해 메모리와 디스크에서 인덱스가 비대해지고 전체 컬렉션에 대한 인덱스 스캔이 느려집니다.
스키마에서 안티 패턴 식별하기
어떤 배열 필드든 다음 질문을 스스로에게 해 보십시오. (1) 이 배열이 비즈니스에서 정한 상한 없이 커질 수 있나요? (2) 이 배열의 데이터는 주로 추가되며 한 번에 전체를 읽는 경우는 드문가요? (3) 이 배열이 포함된 하나의 문서가 몇 킬로바이트를 초과할 수 있나요? 이 중 하나라도 그렇다면 리팩터링이 필요한 제한 없는 배열일 가능성이 높습니다.
별도 컬렉션으로 리팩터링하기
올바른 해결 방법은 계속 증가하는 항목을 별도의 컬렉션으로 옮기고 참조를 저장하는 것입니다. 각 comment를 작성자를 가리키는 userId 필드가 있는 자체 문서로 만드십시오. 그러면 사용자 문서는 간결하게 유지되고 댓글 컬렉션은 문서 크기 제한에 걸리지 않은 채 수십억 개의 행까지 커질 수 있습니다.
// FIXED: comments live in their own collection
db.comments.insertMany([
{ _id: ObjectId(), userId: ObjectId('u1'), postId: ObjectId('p1'), text: 'Great article!', createdAt: new Date() },
{ _id: ObjectId(), userId: ObjectId('u1'), postId: ObjectId('p2'), text: 'I disagree...', createdAt: new Date() }
]);
// User document stays small
db.users.findOne({ _id: ObjectId('u1') }); // no comments array대안으로 사용하는 버킷 패턴
효율성을 위해 관련 이벤트를 계속 그룹화하고 싶은 경우도 있습니다. 예를 들어 시간별 IoT 센서 판독값이 이에 해당합니다. 버킷 패턴은 각 시간 구간마다 하나의 문서를 만들고, 해당 기간의 판독값을 임베딩된 배열로 저장합니다. 각 버킷은 시간 범위에 의해 제한되므로 어떤 문서도 제한 없이 커지지 않습니다. 이 패턴은 시계열 및 분석 스키마에서 흔히 사용됩니다.
// Bucket pattern: one document per device per hour
db.sensorReadings.insertOne({
deviceId: 'sensor-42',
bucketStart: new Date('2024-01-01T09:00:00Z'),
readings: [
{ ts: new Date('2024-01-01T09:00:10Z'), temp: 22.1 },
{ ts: new Date('2024-01-01T09:00:20Z'), temp: 22.3 }
// bounded to at most ~60 readings per hour bucket
],
count: 2
});애플리케이션 로직으로 배열 크기 제한하기
최근 알림 5개 표시와 같이 크기를 제한해야 하는 경우에는 $push와 $slice를 사용해 배열을 고정된 최대 길이로 유지할 수 있습니다. 이렇게 하면 배열이 알려진 크기를 초과하지 않습니다. 가장 최근의 N개 항목만 중요하고 오래된 항목을 삭제해도 되는 경우에 적합합니다.
// Keep only the 5 most recent notifications
db.users.updateOne(
{ _id: ObjectId('u1') },
{
$push: {
notifications: {
$each: [{ message: 'New follower', createdAt: new Date() }],
$slice: -5 // retain only the last 5 elements
}
}
}
);운영 환경에서 큰 문서 감지하기
실행 중인 컬렉션에서 크기 제한에 가까워진 문서를 찾으려면 $bsonSize와 함께 집계 파이프라인을 사용하십시오(MongoDB 4.4 이상). 이 표현식은 문서 크기를 바이트 단위로 반환하므로 운영 환경에서 장애가 발생하기 전에 스키마 리팩터링이 필요한 대상을 식별하고 우선순위를 정할 수 있습니다.
// Find documents larger than 1 MB in the users collection
db.users.aggregate([
{
$project: {
name: 1,
docSize: { $bsonSize: '$$ROOT' }
}
},
{ $match: { docSize: { $gt: 1048576 } } }, // 1 MB
{ $sort: { docSize: -1 } }
]);적절한 리팩터링 전략 선택하기
제한 없는 배열을 발견했다면 데이터의 특성에 따라 리팩터링 전략을 선택하십시오.
- 별도 컬렉션 + 참조: 독립적인 쿼리가 필요하거나 수천 개의 항목까지 증가할 수 있는 데이터에 적합합니다.
- 버킷 패턴: 자연스러운 시간 구간별로 그룹화되는 시간 순서 이벤트에 적합합니다.
- $push + $slice: 오래된 데이터를 삭제할 수 있는, 개수가 제한된 최근 항목 목록에 적합합니다.
빠른 확인
이 레슨에서 다룬 MongoDB 및 NoSQL 데이터베이스 개념에 대한 이해도를 확인해 보십시오.
레슨 요약
이 레슨에서는 다음을 배웠습니다. 제한 없는 배열은 문서를 16MB 제한을 초과할 때까지 키웁니다. 큰 임베디드 배열은 메모리 낭비, 인덱스 비대화 및 쓰기 증폭을 일으킵니다. 또한 해결 방법으로 별도 컬렉션, 버킷 패턴 또는 $slice를 사용한 크기 제한 배열이 있습니다. 다음에는 임베딩과 참조 중 하나를 체계적으로 선택하기 위한 스키마 설계 의사 결정 프레임워크를 만들어 봅니다.
AI 튜터와 함께 JavaScript을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 30
- 레슨
- 120
자주 묻는 질문
“무제한 배열 안티 패턴” 강의는 무료인가요?
네 — “무제한 배열 안티 패턴” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 MongoDB Academy 강의 전체를 잠금 해제할 수 있습니다. MongoDB Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“무제한 배열 안티 패턴”에서 뭘 배우나요?
임베딩으로 인해 문서가 한없이 커지는 경우를 파악하고, 대신 참조를 사용하도록 스키마를 재구성합니다. 브라우저에서 직접 실행하는 실습 코드로 MongoDB Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
MongoDB Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 MongoDB Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“무제한 배열 안티 패턴” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 MongoDB Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 MongoDB Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 임베딩: 일대소 관계
- 참조: 일대다 및 다대다
- 무제한 배열 안티 패턴
- 스키마 설계 결정 프레임워크