핵심 인사이트 (3줄 요약)
- 본질: 스키마리스(Schemaless)는 "스키마가 없음"이 아니라 "스키마가 애플리케이션 코드에 있음"으로, 데이터 모델링의 책임이 DB에서 애플리케이션으로 이동한 것이다.
- 가치: 임베딩(비정규화)과 참조(정규화)의 전략적 조합으로 특정 접근 패턴에 최적화된 데이터 모델을 구성하면, RDBMS JOIN 비용 없이 단일 조회로 필요한 모든 데이터를 가져올 수 있다.
- 판단 포인트: 설계 원칙은 "접근 패턴(Query Pattern)으로 설계하고, 엔티티 관계로 설계하지 말라"로, 가장 빈번한 읽기 패턴에 데이터를 비정규화하여 정렬하는 것이 NoSQL 모델링의 핵심이다.
Ⅰ. 개요 및 필요성
스키마리스의 실제 의미
RDBMS 스키마 변경 흐름:
1. ALTER TABLE 계획
2. DBA 검토 및 승인
3. 야간 유지보수 시간 확보
4. 데이터 마이그레이션 실행 (수시간~수일)
5. 애플리케이션 코드 배포
NoSQL 스키마 변경 흐름:
1. 애플리케이션 코드에서 새 필드 추가
2. 즉시 배포
단, 스키마 검증(validation)은 애플리케이션이 수행:
- 타입 검사, 필수 필드 확인 → 앱 코드 책임
- MongoDB: JSON Schema Validation으로 서버 측 강제 가능
접근 패턴 우선 설계 원칙
잘못된 설계 방식 (엔티티 중심):
"주문과 고객이 있으니 orders, customers 테이블을 만들자"
→ JOIN이 필요한 쿼리 발생
올바른 설계 방식 (접근 패턴 중심):
Q1: "주문 상세 페이지에서 무엇을 보여줄 것인가?"
→ 주문 + 고객 이름 + 주문 상품 목록이 필요
→ 하나의 orders 문서에 모두 임베딩
Q2: "고객 프로필 수정이 주문 내역에도 반영되어야 하는가?"
→ 반드시 → 참조 전략 사용 (또는 Extended Reference 패턴)
📢 섹션 요약 비유
NoSQL 스키마리스 설계는 맞춤 양복과 같다. 기성품(RDBMS 정규화)은 어느 몸에나 맞지만 완벽하지 않고, 맞춤 양복(접근 패턴 기반 설계)은 특정 사람(워크로드)에게 최적이지만 다른 사람이 입기엔 맞지 않을 수 있다.
Ⅱ. 아키텍처 및 핵심 원리
임베딩 vs 참조 결정 매트릭스
┌──────────────────────────────────────────────────────────────┐
│ 임베딩 vs 참조 선택 기준 │
│ │
│ 임베딩(Embedding) 선호: │
│ ✅ "자식은 부모 없이 독립적으로 의미 없음" │
│ (주문 없는 주문 상품이 존재하지 않음) │
│ ✅ "항상 함께 조회" │
│ (주문 페이지에서 항상 상품 목록도 표시) │
│ ✅ "자식 수가 제한적이고 예측 가능" │
│ (최대 100개 리뷰 → 하나의 문서에 ok) │
│ ✅ "원자적 업데이트 필요" │
│ (주문 상태와 배송 추적을 동시에 업데이트) │
│ │
│ 참조(Referencing) 선호: │
│ ✅ "자식이 독립적으로 의미 있음" │
│ (상품은 여러 주문에서 참조, 독립 수정 가능) │
│ ✅ "동일 데이터를 여러 문서에서 공유" │
│ (동일 사용자 정보가 수천 개 주문에서 참조) │
│ ✅ "자식 수가 무한정 증가 가능" │
│ (소셜 포스트의 댓글이 수만 개) │
│ ✅ "자식 데이터만 독립적으로 쿼리" │
│ (최근 댓글 10개만 페이지네이션) │
└──────────────────────────────────────────────────────────────┘
MongoDB 고급 설계 패턴 (Design Patterns)
┌─────────────────────────────────────────────────────────────┐
│ 1. 버킷 패턴 (Bucket Pattern) — IoT 시계열 │
│ │
│ 문제: 센서가 초당 1개 문서 → 시간당 3600개 문서 │
│ 해결: 1시간 분량을 하나의 버킷 문서에 배열로 저장 │
│ │
│ { sensorId: "S1", │
│ hour: "2026-04-21T09:00:00", │
│ readings: [23.5, 23.7, 23.6, ...], // 3600개 임베딩 │
│ count: 3600, avg: 23.6, min: 22.1, max: 24.0 } │
│ │
│ 효과: 문서 수 3600 → 1 (99.97% 감소), 집계 필드 포함 │
├─────────────────────────────────────────────────────────────┤
│ 2. 아웃라이어 패턴 (Outlier Pattern) — 소수의 예외 처리 │
│ │
│ 문제: 일반 영화는 리뷰 수백 개, 블록버스터는 수백만 개 │
│ 해결: has_extras 플래그 + 오버플로우 문서 │
│ │
│ { movieId: "avatar", reviews: [...첫 1000개], │
│ has_extras: true } │
│ { movieId: "avatar", extras_page: 2, │
│ reviews: [...다음 1000개] } │
├─────────────────────────────────────────────────────────────┤
│ 3. 계산된 패턴 (Computed Pattern) — 집계 캐싱 │
│ │
│ 문제: 상품 평점 평균을 매번 리뷰 전체 집계로 계산 │
│ 해결: 문서에 계산된 값을 미리 저장 │
│ │
│ { productId: "P1", │
│ review_count: 1500, │
│ rating_sum: 6750, │
│ avg_rating: 4.5 } // 쓸 때 계산, 읽을 때 즉시 반환 │
├─────────────────────────────────────────────────────────────┤
│ 4. 확장 참조 패턴 (Extended Reference Pattern) │
│ │
│ 문제: 주문에서 고객 이름이 항상 필요한데 매번 JOIN 시 느림 │
│ 해결: 자주 쓰는 필드를 참조하는 문서에 복사 │
│ │
│ { orderId: "O1", │
│ customerId: "C1", // 참조 (ID) │
│ customerName: "홍길동", // 복사 (비정규화) │
│ customerPhone: "010-xxxx" // 복사 (자주 쓰는 것만) │
│ } │
│ │
│ 고객 이름 변경 시 → customerId로 최신 정보 조회 + 주문 이력 │
│ 은 당시 이름 유지 (실제 비즈니스 요구와 일치) │
└─────────────────────────────────────────────────────────────┘
다형성 패턴 (Polymorphic Pattern)
문제: 상품이 의류·전자기기·식품으로 각기 다른 속성을 가짐
RDBMS: 30개 NULL 컬럼 or EAV 패턴의 복잡성
MongoDB 해결: 같은 컬렉션에 다른 구조 허용
{ _id: "P1", type: "clothing",
size: ["S","M","L"], color: "red" }
{ _id: "P2", type: "electronics",
voltage: 220, warranty_months: 24 }
{ _id: "P3", type: "food",
expiry: "2026-12", allergens: ["nuts"] }
공통 인덱스: type, name, price
타입별 인덱스: type="clothing" 필터 인덱스
📢 섹션 요약 비유
버킷 패턴은 장보기 영수증 관리와 같다. 매번 산 물건을 낱장 영수증(각 문서)으로 보관하면 서랍이 가득 차지만, 한 달치를 봉투(버킷 문서)에 모아두면 훨씬 관리가 쉽고 월 지출 합계도 봉투에 미리 적어두면 바로 알 수 있다.
Ⅲ. 비교 및 연결
전체 설계 패턴 요약 테이블
| 패턴 | 문제 | 해결 | 트레이드오프 |
|---|---|---|---|
| 임베딩 | JOIN 비용 | 관련 데이터를 한 문서에 | 중복, 큰 문서 |
| 참조 | 데이터 중복 | ID로만 참조 | 추가 조회 필요 |
| 버킷 | 문서 폭발 | 시간/범위로 묶음 | 문서 설계 복잡 |
| 아웃라이어 | 소수 예외 처리 | 플래그 + 오버플로우 | 앱 로직 복잡 |
| 계산된 | 집계 반복 계산 | 미리 계산 저장 | 쓰기 오버헤드 |
| 확장 참조 | 반복 JOIN | 자주 쓰는 필드 복사 | 동기화 비용 |
| 다형성 | 타입별 다른 스키마 | 같은 컬렉션, 다른 구조 | 타입 관리 |
문서 크기 제한과 청크 패턴
MongoDB 문서 크기 제한: 16MB
대용량 데이터 처리:
GridFS: 청크로 나눠 binary 파일 저장 (이미지·동영상)
청크 패턴: 데이터를 여러 문서로 분할, 공통 시퀀스 키로 연결
{ orderId: "O1", page: 1, items: [...처음 100개...] }
{ orderId: "O1", page: 2, items: [...다음 100개...] }
📢 섹션 요약 비유
계산된 패턴은 쇼핑몰의 베스트셀러 랭킹 게시판과 같다. 손님이 올 때마다 모든 판매 기록을 헤아리는 대신(실시간 집계), 이미 계산된 순위표(계산된 필드)를 보여주고 판매가 일어날 때마다 순위표를 갱신한다(쓰기 시 계산). 읽기가 10만 배 빠른 대신 쓰기가 약간 더 걸린다.
Ⅳ. 실무 적용 및 기술사 판단
전자상거래 플랫폼 데이터 모델 설계 예시
설계 과정:
STEP 1: 핵심 접근 패턴 정의
Q1. 상품 상세 페이지 조회 (초당 수천 번)
Q2. 주문 내역 조회 (사용자별, 시간 역순)
Q3. 카테고리별 상품 목록 (필터·정렬)
Q4. 리뷰 조회 (상품별, 최근 20개)
STEP 2: 컬렉션·임베딩·참조 결정
products 컬렉션:
+ 기본 정보, 가격, 재고 → 임베딩
+ 카테고리 → 참조 (카테고리 독립 수정)
+ 평균 평점, 리뷰 수 → 계산된 필드 임베딩
+ 리뷰 전체 → 별도 컬렉션 참조 (무한 증가)
orders 컬렉션:
+ 주문자 이름/연락처 → 확장 참조 (당시 정보 보존)
+ 주문 상품 목록 (qty, price, name) → 임베딩 (당시 가격 보존)
+ 배송 상태 이력 → 임베딩 (함께 조회)
스키마 버전 관리 패턴
스키마 진화(Schema Evolution) 처리:
{ _id: "P1",
schema_version: 2, // 버전 필드 추가
name: "키보드",
price: 89000
// v2 추가 필드:
tags: ["mechanical", "wireless"]
}
앱 코드:
if doc.schema_version == 1:
doc = migrate_v1_to_v2(doc)
db.save(doc) // 읽을 때 마이그레이션 (Lazy Migration)
📢 섹션 요약 비유
Lazy Migration은 도서관 책 재분류와 같다. 모든 책을 한꺼번에 재분류(빅뱅 마이그레이션)하면 도서관 문을 닫아야 하지만, 손님이 대출할 때마다 새 분류 체계로 이동(Lazy Migration)하면 도서관은 계속 운영하면서 서서히 완전히 이전된다.
Ⅴ. 기대효과 및 결론
스키마리스 설계의 정량적 효과
| 항목 | RDBMS 정규화 | NoSQL 접근 패턴 설계 | 개선 |
|---|---|---|---|
| 상품 페이지 조회 | 8개 테이블 JOIN | 1개 문서 조회 | 5~10배 빠름 |
| 스키마 변경 | 수시간 다운타임 | 즉시 (Lazy 마이그레이션) | 개발 민첩성 |
| 주문 이력 보존 | 최신 데이터 반영 | 주문 당시 가격 보존 | 비즈니스 정확성 |
| 저장 공간 | 정규화 최소 | 비정규화 약간 증가 | 성능 우선 |
결론
스키마리스 설계는 자유가 아니라 책임의 이동이다. 접근 패턴 분석 → 임베딩/참조 결정 → 고급 패턴(버킷·계산된·확장 참조) 적용의 단계적 설계 방법론을 따르면, RDBMS가 할 수 없는 수준의 읽기 성능 최적화가 가능하다. 기술사 시험에서는 임베딩 vs 참조 선택 기준, 버킷 패턴의 IoT 적용, 계산된 패턴의 읽기 최적화 원리, 확장 참조 패턴의 설계 의도가 핵심 논점이다.
📢 섹션 요약 비유
NoSQL 스키마리스 설계를 마스터한 개발자는 뷔페 요리사와 같다. 손님(쿼리 패턴)이 원하는 음식을 미리 예측해서 이미 조리해둔다(임베딩). 주문이 들어올 때마다 처음부터 요리하는 식당(JOIN 쿼리)과 달리, 이미 준비된 음식을 그릇에 담기만 하면 된다. 단, 메뉴를 잘못 예측하면 음식을 버려야 한다(비정규화 비용).
📌 관련 개념 맵
| 개념 | 관계 | 설명 |
|---|---|---|
| 비정규화 | 핵심 원칙 | 읽기 성능을 위한 데이터 중복 허용 |
| 접근 패턴 | 설계 기준 | 쿼리 기반 스키마 설계 |
| JSON Schema | 유효성 검사 | MongoDB 서버 측 스키마 강제 |
| Lazy Migration | 버전 관리 | 읽기 시 스키마 업그레이드 |
| GridFS | 대용량 처리 | MongoDB 16MB 초과 파일 청크 저장 |
📈 관련 키워드 및 발전 흐름도
[관계형 DB (RDBMS) — 엄격한 스키마 사전 정의 필수]
│
▼
[NoSQL 등장 — 스키마리스, 수평 확장, 다양한 데이터 모델 지원]
│
▼
[스키마리스 설계 패턴 — 문서(Document) / 컬럼(Column) / 그래프(Graph) 모델]
│
▼
[Schema-on-Read — 저장 시 자유, 조회 시 스키마 적용 (데이터 레이크 철학)]
│
▼
[하이브리드 접근 — HTAP(OLTP+OLAP 혼합) 및 NewSQL로 스키마 유연성+ACID 양립]
관계형 DB의 엄격한 스키마 제약을 NoSQL이 스키마리스 패턴으로 극복했고, Schema-on-Read 철학과 NewSQL의 등장으로 유연성과 일관성을 동시에 추구하고 있다.
👶 어린이를 위한 3줄 비유 설명
- 스키마리스는 "자유 일기장"처럼 형식이 없는 게 아니라, 형식을 내가 직접 정해야 한다는 것 — 더 자유롭지만 더 많은 책임이 있어요.
- 임베딩은 도시락에 밥·반찬을 모두 담는 것(한 번에 꺼낼 수 있음), 참조는 밥과 반찬을 각각 다른 통에 넣는 것(각자 다른 사람이 먹을 수 있음)이에요.
- 버킷 패턴은 매일 소액 동전들을 저금통에 모아두다가 주기적으로 은행에 가는 것 — 매번 은행을 가는(개별 문서) 대신 모아서(버킷) 한 번에 처리해요.