

📌 들어가며
오늘의 학습 목표: 관계형 DB의 ERD 설계와 MongoDB의 Document 설계를 비교하며 이해하고, 상황에 맞는 데이터 모델링 선택하기
안녕하세요! 오늘은 데이터베이스 설계에 대해 공부했습니다.
사실 이번 주제를 공부하게 된 계기가 있어요. 프로젝트에서 "게시글 + 댓글" 기능을 설계하는데, 팀원이 이런 질문을 했거든요.
"이거 MySQL로 테이블 나눠서 JOIN 할까? 아니면 MongoDB로 한 Document에 다 넣을까?"
솔직히 그 순간 대답을 못 했어요. 😅
- "NoSQL이 빠르다던데?"
- "근데 RDBMS가 안정적이라고 하지 않았나?"
- "둘 다 되긴 할 텐데, 뭘 기준으로 선택하지?"
그래서 이번 기회에 둘의 설계 방식이 어떻게 다른지, 언제 뭘 선택해야 하는지 제대로 공부해봤습니다!
🎯 Today I Learned
✅ RDBMS vs NoSQL(MongoDB) 핵심 차이
✅ ERD(Entity Relationship Diagram) 설계 방법
✅ 정규화란 무엇이고 왜 하는가
✅ MongoDB Document 구조 이해
✅ Embedding vs Referencing 선택 기준
✅ 같은 서비스를 두 방식으로 설계해보기 (실습)
✅ 언제 RDBMS, 언제 MongoDB를 선택할지
🤔 RDBMS와 MongoDB, 뭐가 다른 거야?
처음 느꼈던 혼란
MongoDB를 처음 접했을 때 이런 생각이 들었어요.
나의 혼란:
"테이블이 없다고? 그럼 데이터를 어디에 저장해?"
"JOIN이 없으면 연관된 데이터는 어떻게 가져와?"
"스키마가 없다는데, 그럼 아무 데이터나 막 넣어도 돼?"
핵심 차이를 이해하고 나서
둘의 철학 자체가 다르다는 걸 깨달았어요.

RDBMS (MySQL, PostgreSQL):
┌─────────────────────────────────────────────┐
│ "데이터를 정규화해서 중복 없이 저장하자!" │
│ │
│ - 테이블로 구조화 │
│ - 관계(Relationship)로 연결 │
│ - JOIN으로 필요할 때 조합 │
│ - 스키마가 엄격 (컬럼 타입 고정) │
└─────────────────────────────────────────────┘
MongoDB (NoSQL):
┌─────────────────────────────────────────────┐
│ "자주 함께 쓰는 데이터는 함께 저장하자!" │
│ │
│ - Document(JSON)로 저장 │
│ - 필요하면 내장(Embedding) │
│ - JOIN 대신 한 번에 읽기 │
│ - 스키마가 유연 (필드 자유롭게 추가) │
└─────────────────────────────────────────────┘
비유로 이해하기:
RDBMS = 도서관 📚
- 책(데이터)은 분류 번호에 따라 정해진 위치에
- 찾으려면 색인(인덱스)으로 위치 확인 후 이동
- 책마다 양식이 정해져 있음 (ISBN, 저자, 출판사...)
- 관련 책은 "참고문헌"으로 연결
MongoDB = 개인 노트 📓
- 하나의 주제에 관련 내용 다 적어둠
- 필요한 건 그 페이지만 펼치면 됨
- 양식이 자유로움 (그림, 표, 글 섞어도 됨)
- 관련 내용은 같은 페이지에 함께
📐 ERD 설계 (관계형 DB)
ERD란?
**ERD(Entity Relationship Diagram)**는 데이터베이스의 구조를 시각적으로 표현한 설계도예요.
ERD의 구성 요소:
📦 Entity (엔티티)
- 저장할 대상 (테이블이 됨)
- 예: 회원, 게시글, 댓글, 주문
📝 Attribute (속성)
- 엔티티의 특성 (컬럼이 됨)
- 예: 회원의 이름, 이메일, 가입일
🔗 Relationship (관계)
- 엔티티 간의 연결
- 1:1, 1:N, N:M
실습: 쇼핑몰 ERD 설계
"회원이 상품을 주문한다"는 간단한 쇼핑몰을 설계해볼게요.

요구사항 분석:
1. 회원은 여러 주문을 할 수 있다 (1:N)
2. 하나의 주문에는 여러 상품이 포함될 수 있다 (N:M)
3. 상품은 하나의 카테고리에 속한다 (N:1)
ERD 설계 결과:
┌─────────────┐ ┌─────────────┐
│ Member │ │ Category │
├─────────────┤ ├─────────────┤
│ *member_id │ │*category_id │
│ email │ │ name │
│ name │ │ description │
│ created_at │ └──────┬──────┘
└──────┬──────┘ │ 1
│ 1 │
│ │
│ N │ N
┌──────┴──────┐ ┌──────┴──────┐
│ Order │ │ Product │
├─────────────┤ ├─────────────┤
│ *order_id │ │*product_id │
│ member_id │──┐ │ category_id │
│ order_date │ │ │ name │
│ status │ │ │ price │
│ total_price│ │ │ stock │
└──────┬──────┘ │ └──────┬──────┘
│ 1 │ │
│ │ │
│ N │ │ N
┌──────┴──────┐ │ ┌──────┴──────┐
│ Order_Item │──┘ │ │
├─────────────┤ │ │
│*order_item_id │(N:M 해소) │
│ order_id │───────┤ │
│ product_id │───────┘ │
│ quantity │ │
│ price │ │
└─────────────┘
* = Primary Key (기본키)
정규화, 왜 하는 거야?
처음에 정규화가 왜 필요한지 이해가 안 됐어요.
정규화 전 (비정규형):
┌─────────────────────────────────────────────────────┐
│ Orders │
├─────────────────────────────────────────────────────┤
│ order_id │ member_name │ member_email │ product_name│
├─────────────────────────────────────────────────────┤
│ 1 │ 홍길동 │ hong@mail.com│ 맥북 프로 │
│ 2 │ 홍길동 │ hong@mail.com│ 아이패드 │
│ 3 │ 홍길동 │ hong@mail.com│ 에어팟 │
└─────────────────────────────────────────────────────┘
문제점:
❌ "홍길동" 정보가 3번 중복 저장
❌ 이메일 바꾸면 3군데 다 수정해야 함
❌ 하나만 수정하면 데이터 불일치 발생!
정규화 후:
┌─────────────────┐ ┌─────────────────┐
│ Members │ │ Orders │
├─────────────────┤ ├─────────────────┤
│ member_id: 1 │←─────│ member_id: 1 │
│ name: 홍길동 │ │ product: 맥북 │
│ email: hong@... │ ├─────────────────┤
└─────────────────┘ │ member_id: 1 │
│ product: 아이패드│
├─────────────────┤
│ member_id: 1 │
│ product: 에어팟 │
└─────────────────┘
장점:
✅ 회원 정보는 한 곳에만 저장
✅ 이메일 바꾸면 한 군데만 수정
✅ 데이터 일관성 유지!
정규화의 핵심: "하나의 사실은 한 곳에만 저장한다!"
ERD 설계 시 내가 자주 하던 실수
실수 1: N:M 관계를 그대로 둠
❌ 잘못된 설계:
Order ──────── Product (N:M 직접 연결)
✅ 올바른 설계:
Order ─── Order_Item ─── Product (중간 테이블로 해소)
이유: RDBMS에서 N:M은 직접 표현 불가,
중간 테이블(Order_Item)이 필요!
실수 2: 모든 걸 한 테이블에
❌ 잘못된 설계:
Member 테이블에 주소1, 주소2, 주소3 컬럼
✅ 올바른 설계:
Member 테이블 + Address 테이블 분리 (1:N)
이유: 주소가 4개 이상 필요하면? 컬럼 추가?
유연성이 떨어짐!
실수 3: ID를 안 만듦
❌ 잘못된 설계:
Member 테이블의 PK를 email로 설정
✅ 올바른 설계:
member_id (auto increment) + email (unique)
이유: 이메일 변경 시 연관 테이블 전부 수정해야 함!
비즈니스 값은 PK로 쓰지 않기!
📄 MongoDB Document 설계
Document란?

MongoDB는 테이블 대신 Collection, 행(Row) 대신 Document를 사용해요.
RDBMS 용어 → MongoDB 용어
─────────────────────────────────
Database → Database
Table → Collection
Row → Document
Column → Field
Primary Key → _id
Foreign Key → Reference (또는 Embedding)
// MongoDB Document 예시
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"email": "hong@mail.com",
"name": "홍길동",
"profile": {
"age": 28,
"city": "서울"
},
"orders": [
{ "product": "맥북", "price": 2000000 },
{ "product": "아이패드", "price": 1000000 }
]
}
특징: JSON 형태로 저장되고, 중첩 구조가 가능해요!
Embedding vs Referencing
MongoDB 설계의 가장 중요한 결정!

Embedding (내장):
─────────────────
"관련 데이터를 한 Document에 같이 저장"
{
"_id": "user1",
"name": "홍길동",
"addresses": [ // 주소를 내장!
{ "city": "서울", "zip": "12345" },
{ "city": "부산", "zip": "67890" }
]
}
장점: 한 번의 쿼리로 다 가져옴 (빠름!)
단점: 중복 가능, Document 크기 제한(16MB)
Referencing (참조):
───────────────────
"다른 Document의 ID만 저장"
// users collection
{
"_id": "user1",
"name": "홍길동",
"address_ids": ["addr1", "addr2"] // ID만 저장
}
// addresses collection
{ "_id": "addr1", "city": "서울", "zip": "12345" }
{ "_id": "addr2", "city": "부산", "zip": "67890" }
장점: 중복 없음, 유연함
단점: 여러 번 쿼리 필요 (느릴 수 있음)
언제 Embedding? 언제 Referencing?
이게 제일 고민됐던 부분이에요. 정리해보니까 기준이 생기더라고요.
📌 Embedding이 좋은 경우:
✅ "함께 조회되는" 데이터
예: 게시글 + 작성자 이름
✅ "소유 관계"인 데이터 (부모-자식)
예: 주문 + 주문상품 (주문 없으면 주문상품도 의미 없음)
✅ 1:1 또는 1:Few 관계
예: 사용자 + 프로필, 사용자 + 주소 (몇 개 안 됨)
✅ 자주 변경되지 않는 데이터
예: 상품 정보의 스냅샷 (주문 당시 가격)
📌 Referencing이 좋은 경우:
✅ 독립적으로 조회되는 데이터
예: 사용자 ↔ 게시글 (각각 따로 조회함)
✅ N:M 관계
예: 학생 ↔ 수업
✅ 1:Many에서 Many가 많은 경우
예: 작성자 ↔ 게시글 (게시글이 수천 개 될 수 있음)
✅ 자주 변경되는 데이터
예: 상품 재고 (자주 바뀜, 중복 저장하면 동기화 지옥)
🔄 같은 서비스, 두 가지 설계
예시: 블로그 서비스
"게시글 + 댓글 + 좋아요" 기능을 두 방식으로 설계해볼게요.

요구사항:
- 사용자는 게시글을 작성할 수 있다
- 게시글에 댓글을 달 수 있다
- 게시글에 좋아요를 누를 수 있다
RDBMS (MySQL) 설계
-- 테이블 4개로 분리 (정규화)
CREATE TABLE users (
user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE,
name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
post_id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT,
title VARCHAR(200),
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
CREATE TABLE comments (
comment_id BIGINT PRIMARY KEY AUTO_INCREMENT,
post_id BIGINT,
user_id BIGINT,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(post_id),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
CREATE TABLE likes (
like_id BIGINT PRIMARY KEY AUTO_INCREMENT,
post_id BIGINT,
user_id BIGINT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY unique_like (post_id, user_id),
FOREIGN KEY (post_id) REFERENCES posts(post_id),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
게시글 + 댓글 + 좋아요 수 조회:
SELECT p.*, u.name as author_name,
(SELECT COUNT(*) FROM comments c WHERE c.post_id = p.post_id) as comment_count,
(SELECT COUNT(*) FROM likes l WHERE l.post_id = p.post_id) as like_count
FROM posts p
JOIN users u ON p.user_id = u.user_id
WHERE p.post_id = 1;
→ JOIN 필요, 서브쿼리 필요
→ 테이블 여러 개 접근
MongoDB 설계
// posts collection - 하나의 Document에 관련 데이터 내장
{
"_id": ObjectId("..."),
"title": "오늘의 TIL",
"content": "오늘 배운 내용...",
"author": { // 작성자 정보 내장
"user_id": ObjectId("..."),
"name": "홍길동" // 자주 쓰는 정보만!
},
"comments": [ // 댓글 내장
{
"comment_id": ObjectId("..."),
"user_id": ObjectId("..."),
"user_name": "김철수",
"content": "좋은 글이네요!",
"created_at": ISODate("...")
},
{
"comment_id": ObjectId("..."),
"user_id": ObjectId("..."),
"user_name": "이영희",
"content": "잘 읽었습니다",
"created_at": ISODate("...")
}
],
"likes": [ // 좋아요 누른 사용자 ID 배열
ObjectId("user1"),
ObjectId("user2"),
ObjectId("user3")
],
"like_count": 3, // 미리 계산해서 저장
"comment_count": 2,
"created_at": ISODate("...")
}
// 게시글 + 댓글 + 좋아요 수 조회:
db.posts.findOne({ _id: ObjectId("...") })
→ 쿼리 한 번에 다 가져옴!
→ JOIN 없음
두 설계의 트레이드오프
RDBMS 설계:
───────────
장점:
✅ 데이터 중복 없음 (사용자 이름 한 곳에만)
✅ 수정이 쉬움 (이름 바꾸면 한 군데만)
✅ 복잡한 쿼리 가능 (GROUP BY, 통계 등)
✅ 데이터 정합성 보장 (FK 제약조건)
단점:
❌ JOIN이 많아지면 느려짐
❌ 스키마 변경이 어려움 (ALTER TABLE...)
❌ 수평 확장이 어려움
MongoDB 설계:
─────────────
장점:
✅ 읽기가 빠름 (한 번에 다 가져옴)
✅ 스키마 유연 (필드 자유롭게 추가)
✅ 수평 확장 용이 (샤딩)
단점:
❌ 데이터 중복 (사용자 이름이 여기저기)
❌ 이름 바꾸면 여러 Document 수정 필요
❌ 복잡한 집계는 RDBMS보다 어려움
❌ Document 크기 제한 (16MB)
🤔 그래서 언제 뭘 써야 해?
이런 고민을 했었어요
"MongoDB가 빠르다면서? 그냥 다 MongoDB 쓰면 안 돼?"
→ 아니에요! 상황에 따라 달라요.

선택 기준 정리
📌 RDBMS를 선택해야 할 때:
✅ 데이터 정합성이 중요할 때
예: 금융, 결제, 재고 관리
"잔액이 100원인데 200원 출금되면 안 돼!"
✅ 복잡한 관계와 JOIN이 많을 때
예: ERP, CRM 시스템
여러 테이블의 데이터를 조합해서 리포트 생성
✅ 트랜잭션이 중요할 때
예: 주문 처리 (주문 생성 + 재고 감소 + 결제 처리)
"하나라도 실패하면 전부 롤백!"
✅ 스키마가 명확하고 잘 안 변할 때
예: 회계 시스템, 인사 시스템
📌 MongoDB를 선택해야 할 때:
✅ 읽기 성능이 중요할 때
예: 콘텐츠 서비스, 블로그, SNS 피드
"게시글 + 댓글 + 좋아요를 빠르게!"
✅ 스키마가 자주 변할 때
예: 스타트업 MVP, 빠르게 변하는 서비스
"이번 주에 필드 3개 추가해야 해요"
✅ 대용량 데이터 + 수평 확장이 필요할 때
예: 로그 데이터, IoT 센서 데이터
서버 여러 대로 분산 저장
✅ 비정형 데이터가 많을 때
예: 상품마다 속성이 다른 이커머스
"옷은 사이즈, 전자제품은 전압, 식품은 유통기한..."
실제로는 둘 다 쓰기도 해요!
Polyglot Persistence (다중 저장소):
┌─────────────────────────────────────────┐
│ 쇼핑몰 서비스 │
├─────────────────────────────────────────┤
│ │
│ [MySQL] [MongoDB] │
│ - 회원 정보 - 상품 카탈로그 │
│ - 주문/결제 - 상품 리뷰 │
│ - 재고 관리 - 검색 로그 │
│ │
│ → 정합성 중요! → 유연성/속도! │
│ │
│ [Redis] │
│ - 세션 │
│ - 캐시 │
│ │
└─────────────────────────────────────────┘
→ 각 DB의 장점을 살려서 조합!
🛠️ 설계할 때 내가 겪은 실수들
실수 1: "일단 MongoDB니까 다 Embedding"
처음 생각:
"MongoDB는 Embedding이 장점이라며? 다 내장하자!"
// 잘못된 설계 - 작성자 정보를 통째로 내장
{
"title": "게시글",
"author": {
"user_id": "...",
"name": "홍길동",
"email": "hong@mail.com", // 이것도?
"phone": "010-1234-5678", // 이것도?
"address": "서울시...", // 이것까지?
"profile_image": "base64..." // 이미지까지??
}
}
문제:
❌ Document가 너무 커짐
❌ 작성자 정보 바뀌면 모든 게시글 수정해야 함
❌ 이메일 같은 민감정보가 여기저기 복제됨
올바른 설계:
"자주 함께 조회되는 최소한의 정보만 내장"
{
"title": "게시글",
"author": {
"user_id": ObjectId("..."), // 필수
"name": "홍길동" // 화면에 보여줄 것만!
}
}
// 상세 정보가 필요하면 user_id로 별도 조회
실수 2: "정규화는 무조건 좋은 거야"
처음 생각:
"정규화를 많이 할수록 좋은 설계야!"
// 과도한 정규화
Users → UserProfiles → UserAddresses → Cities → Countries
문제:
❌ 사용자 정보 조회하는데 JOIN 5번?
❌ 성능 저하
❌ 쿼리 복잡해짐
적절한 균형:
"읽기 패턴을 고려해서 적당히"
// 자주 함께 조회되면 같은 테이블에
Users (id, name, email, city, country)
// 독립적으로 관리되면 분리
Users + Addresses (1:N)
실수 3: "ERD 없이 바로 코딩"
처음:
"설계? 그냥 코딩하면서 필요할 때 테이블 만들면 되지!"
3개월 후:
"어... 이 테이블이랑 저 테이블이 어떤 관계지?"
"이 컬럼 왜 있는 거야?"
"같은 데이터가 왜 두 군데 있어?"
배운 점:
✅ 시작 전에 ERD 그리기 (30분 투자로 3시간 절약)
✅ 팀원과 공유하면 소통이 쉬워짐
✅ 나중에 유지보수할 때 큰 도움
💡 오늘 배운 핵심 정리

1️⃣ 철학이 다르다
RDBMS: "중복 없이 정규화해서 저장"
MongoDB: "함께 쓰는 데이터는 함께 저장"
2️⃣ 설계 방식이 다르다
RDBMS: ERD로 테이블과 관계 정의 → JOIN으로 조합
MongoDB: Document 구조 설계 → Embedding or Referencing
3️⃣ 선택 기준
정합성/트랜잭션 중요 → RDBMS
유연성/읽기 속도 중요 → MongoDB
4️⃣ 정답은 없다
서비스 특성에 따라 선택
하나의 서비스에서 둘 다 쓰기도 함
5️⃣ 설계가 중요하다
어떤 DB를 쓰든, 먼저 설계를 잘 해야 함
ERD든 Document 구조든, 그림 그려보기!
😊 좋았던 점 & 😅 아쉬웠던 점
좋았던 점
👍 선택의 기준이 생김
"MongoDB가 빠르대" 같은 막연한 이야기 대신, 구체적인 선택 기준이 생겼어요. 이제 프로젝트 시작할 때 뭘 써야 할지 근거 있게 결정할 수 있어요!
👍 ERD의 중요성 체감
설계 없이 코딩하다가 꼬인 경험이 있어서, ERD 먼저 그리는 습관의 소중함을 알게 됐어요.
👍 Embedding vs Referencing 기준 정리
MongoDB 쓸 때 가장 고민되던 부분이었는데, 이제 어느 정도 기준이 생겼어요.
아쉬웠던 점
😢 인덱스 설계는 더 공부 필요
테이블/Document 구조는 알겠는데, 성능을 위한 인덱스 설계는 아직 부족해요.
😢 실제 대용량 데이터 경험 부족
이론적으로는 이해했는데, 실제로 수백만 건 데이터를 다뤄본 경험이 없어서 체감이 부족해요.
🚀 다음 학습 목표
📌 단기
✓ 프로젝트에 ERD 적용
✓ MongoDB 실습 (간단한 CRUD)
📌 중기
✓ 인덱스 설계 학습
✓ 쿼리 성능 최적화
📌 장기
✓ 실제 서비스에서 DB 선택 경험
✓ 샤딩, 레플리카 등 분산 DB 학습
🎬 마치며
데이터베이스 설계를 공부하면서 가장 크게 느낀 건, **"정답이 없다"**는 거예요.
RDBMS가 좋을 때도 있고, MongoDB가 좋을 때도 있어요. 심지어 하나의 서비스에서 둘 다 쓰기도 하고요.
중요한 건 "왜 이걸 선택했는지" 설명할 수 있는 거예요.
- "우리 서비스는 결제가 핵심이라 트랜잭션이 중요해서 MySQL 썼어요"
- "상품 속성이 다양해서 스키마 유연한 MongoDB 썼어요"
이렇게 근거 있는 선택을 할 수 있게 된 게 오늘의 가장 큰 수확이에요! 💪