작성일: 2026-04-20
대상: RDB(MySQL) 중심으로 개발해온 백엔드 개발자
목적: NoSQL이 왜 등장했는지, 언제 써야 하는지, 포기한 것을 어떻게 보완하는지 이해
Not Only SQL의 약자. SQL(RDB)만이 답이 아니라는 뜻.
RDB가 잘 못하는 특정 상황을 해결하기 위해 나온 DB 계열 전체를 부르는 말.
RDB와 NoSQL은 경쟁 관계가 아니라 역할 분담
RDB → 정합성이 중요한 데이터 (돈, 계약)
NoSQL → 정합성보다 다른 것이 중요한 데이터 (속도, 유연성, 확장성)
MySQL은 ACID를 보장하기 위해 쓸 때 잠금(Lock)을 걸어요.
카카오톡 점심시간 12:00
동시에 500만 명이 메시지 전송
→ 초당 수만 건 INSERT
Thread 1: INSERT → Lock 획득 → 쓰기 중
Thread 2: INSERT → Lock 대기
Thread 3: INSERT → Lock 대기
Thread N: INSERT → 대기열 수천 개
→ 응답 지연 → Connection Pool 고갈 → 장애
MySQL 안정적인 쓰기 한계: 약 5,000~10,000 TPS
카카오톡 메시지: 초당 약 11만 건
→ MySQL 한계치의 10배 이상
네이버 카페 게시글 테이블: 30억 건 (약 5TB)
기획팀: "해시태그 기능 추가해주세요"
ALTER TABLE posts ADD COLUMN hashtags VARCHAR(500);
MySQL이 하는 일
→ 30억 건 전체를 새 구조로 복사
→ 복사 중 테이블 Lock
→ 게시글 작성 불가
→ 소요 시간: 약 40시간
기능 추가할 때마다 40시간 서비스 중단
→ 개발팀이 기능 추가를 거부하게 됨
→ 서비스 발전이 멈춤
데이터가 너무 많아서 서버 3대로 분산
[서버1 MySQL] [서버2 MySQL] [서버3 MySQL]
문제 1: JOIN
user가 서버1, orders가 서버2 → JOIN 불가
문제 2: 트랜잭션
서버 여러 대에 걸친 분산 트랜잭션 → 구현 극악
문제 3: 라우팅
쿼리가 들어오면 어느 서버 데이터인지? → 직접 짜야 함
쿠팡 상품 테이블: 카테고리마다 속성이 완전히 다름
id name inch resolution size expiry voltage
1 삼성TV 65 4K NULL NULL 220
2 나이키 NULL NULL 270 NULL NULL
3 감귤 NULL NULL NULL 14 NULL
컬럼 200개 × 상품 1억 건
대부분 NULL → 디스크 낭비, 조회 느림
새 카테고리 = 또 ALTER TABLE = 또 수십 시간 중단
가장 단순한 구조. 키: 값 쌍으로 저장.
"session:user:42" → "{ name: 김대리, role: FORWARDER }"
"exchange:USD:KRW" → "1380.50"
"online:42" → "true"
특징
- 메모리 기반 → 극도로 빠름
- 구조 없음 → 단순한 데이터만 가능
- TTL 설정 가능 (자동 만료)
적합한 곳
세션, 캐시, 실시간 랭킹, 분산 락, 임시 데이터
JSON 형태의 문서를 저장. 컬렉션 안에 다양한 구조 가능.
{ id: 1, type: "BOOKING", bookingId: 42, title: "새 접수" }
{ id: 2, type: "SHIPMENT", eta: "내일", port: "부산" }
{ id: 3, type: "SYSTEM", severity: "CRITICAL" }
RDB 용어 대응
테이블 → 컬렉션 (Collection)
행 → 문서 (Document)
컬럼 → 필드 (Field)
적합한 곳
비정형 데이터, 스키마 자주 변경, 중첩 구조
생긴 건 RDB와 비슷하지만 대량 쓰기에 극도로 최적화.
JOIN, 트랜잭션 없음.
특징
- 처음부터 분산 설계
- 초당 수십만 건 쓰기 가능
- 시계열 데이터에 최적
적합한 곳
IoT 센서, 채팅 메시지, 로그, 시계열 데이터
노드(점)와 엣지(관계선)로 데이터 표현.
(김대리) -[담당]→ (부킹A)
(박과장) -[승인]→ (부킹A)
(김대리) -[소속]→ (국제물류㈜)
적합한 곳
친구 추천, 물류 경로 최적화, 권한 관리, 사기 탐지
RDB — ACID (정합성 보장)
Atomicity 트랜잭션은 전부 성공 or 전부 실패
Consistency 항상 일관된 상태 유지
Isolation 트랜잭션끼리 서로 영향 없음
Durability 저장되면 절대 안 사라짐
NoSQL — BASE (정합성 일부 포기, 속도/확장성 확보)
Basically Available 일단 응답은 함
Soft state 잠깐 데이터가 안맞을 수 있음
Eventually consistent 나중엔 결국 맞춰짐
문제
카카오톡 사용자 폭증
초당 수만 건 메시지 INSERT
MySQL Lock 경합 → 대기열 폭증 → 장애 반복
해결: HBase (Column DB) 도입
메시지 저장을 MySQL에서 HBase로 분리
HBase 저장 방식
메시지 전송
↓
WAL 로그 (디스크에 먼저 기록)
↓
MemStore (메모리)에 씀 → Lock 없음
↓
백그라운드에서 HFile (디스크)에 flush
서버 꺼져도 WAL 로그로 복구 → 유실 없음
Row Key 설계: room_id + timestamp (역순)
→ 특정 채팅방 최근 메시지 바로 스캔
→ MySQL 인덱스보다 훨씬 빠름
결과
MySQL HBase
초당 1만 건 한계 → 초당 수십만 건 가능
Lock 경합 장애 → Lock 없음
응답 지연 수초 → 응답 수ms
정합성을 포기한 것과 보완
포기한 것
메모리에 먼저 쓰는 구조 → 서버 장애 시 유실 가능성
보완 방법
1. WAL (Write Ahead Log): 메모리 쓰기 전 디스크 로그 선기록
2. 복제 (Replication): 데이터 3벌 복제, 서버 2대 죽어도 살아있음
3. 메시지 특성 활용: 순서 1~2개 바뀌어도 사용자가 크게 못 느낌
→ 100% 정합성 포기 가능한 데이터였음
문제
카페/블로그 게시글 30억 건 (약 5TB)
기능 추가 요청마다 ALTER TABLE
→ 수십 시간 서비스 중단 반복
→ 개발팀이 기능 추가를 거부하는 상황 발생
해결: MongoDB 도입
MySQL → MongoDB 이전 후
기능 추가 전 게시글
{ title: "오늘 모임", content: "즐거웠어요" }
해시태그 추가 후 (ALTER TABLE 없이)
{ title: "주말 번개", content: "같이가요", hashtags: ["번개", "주말"] }
투표 추가 후 (또 ALTER TABLE 없이)
{ title: "메뉴 투표", content: "뭐먹을까요",
poll: { question: "어디서?", options: ["치킨", "피자"] } }
기존 문서는 그대로
새 필드가 없어도 에러 없음
서비스 중단 없음
소요 시간 0초
코드 레벨 차이
// MySQL - 기능 추가 시
// 1. ALTER TABLE (수십 시간 중단)
// 2. Entity 컬럼 추가
// 3. Repository 수정
// 4. 배포
// MongoDB - 기능 추가 시
// 1. Entity에 필드만 추가
// 2. 그냥 저장
// 해시태그 추가 전
postRepository.save(new Post(title, content));
// 해시태그 추가 후 - DB 변경 없음
postRepository.save(new Post(title, content, hashtags));
스키마 포기를 보완한 방법
방법 1 — 애플리케이션 레벨 검증
DB가 막아주지 않으니 코드에서 직접 검증
@Document(collection = "posts")
public class Post {
@NotNull
private String title;
@NotBlank
private String content;
private List<String> hashtags; // optional
private Poll poll; // optional
}
방법 2 — MongoDB Schema Validation
필수 필드는 강제, 선택 필드는 자유
db.createCollection("posts", {
validator: {
$jsonSchema: {
required: ["title", "content", "cafe_id"],
properties: {
title: { bsonType: "string", maxLength: 500 }
}
}
}
})
방법 3 — 이벤트 소싱 패턴
변경 이력 자체를 저장
{ event: "POST_CREATED", postId: 1, title: "제목" }
{ event: "HASHTAG_ADDED", postId: 1, hashtags: ["여행"] }
→ 이벤트 순서대로 적용하면 최신 상태 재현
→ 넷플릭스, 우버 이 방식 사용
문제
상품 테이블 1억 건
카테고리마다 속성이 완전히 다름
TV: inch, resolution, panel, refreshRate, ports
신발: sizes, colors, material, sole
식품: weight, origin, expiryDays, storage
MySQL에 억지로 넣으면
컬럼 200개 × 1억 건 = 대부분 NULL
디스크 낭비 어마어마 (약 20TB)
새 카테고리 = ALTER TABLE = 수십 시간 중단 반복
해결: MongoDB 도입 (상품 속성 한정)
TV
{
id: 1,
name: "삼성 QLED 65인치",
price: 1500000,
category: "TV",
specs: {
inch: 65,
resolution: "4K",
panel: "QLED",
refreshRate: 120,
ports: { hdmi: 4, usb: 2 }
}
}
신발
{
id: 2,
name: "나이키 에어맥스",
price: 150000,
category: "신발",
specs: {
sizes: [240, 245, 250, 255, 260],
colors: ["흰색", "검정"],
material: "메쉬"
}
}
캠핑 카테고리 추가 시 차이
MySQL
ALTER TABLE products
ADD COLUMN tent_capacity INT,
ADD COLUMN waterproof_rating INT,
ADD COLUMN pole_material VARCHAR;
→ 1억 건 테이블 수십 시간 중단
→ 또 NULL 컬럼 추가
MongoDB
그냥 새 문서 넣으면 끝
{ category: "캠핑", specs: { tentCapacity: 4, waterproofRating: 3000 } }
→ 서비스 중단 없음
→ 기존 상품 영향 없음
디스크 절약 효과
MySQL 상품 1억 건
컬럼 200개 × 1억 건 (대부분 NULL)
디스크: 약 20TB
MongoDB 상품 1억 건
필요한 필드만 저장
디스크: 약 5TB
→ 디스크 75% 절약
→ 조회 속도 향상 (읽어야 할 데이터 자체가 줄어듦)
JOIN 포기를 보완한 방법
방법 1 — Embedding (자주 조회하는 데이터 미리 포함)
{
id: 1,
name: "삼성 QLED",
seller: { ← JOIN 대신 embed
id: 500,
name: "삼성전자 공식",
rating: 4.9
},
specs: { inch: 65, ... }
}
방법 2 — 자주 바뀌는 데이터는 별도 조회
embed: 판매자 이름 (거의 안 바뀜)
별도 조회: 재고 (매초 바뀜)
방법 3 — CQRS 패턴 (읽기/쓰기 DB 분리)
쓰기: MySQL (정규화, 정합성)
읽기: MongoDB (비정규화, JOIN 없음, 빠름)
MySQL 저장 → 이벤트 발행 → MongoDB 문서 생성
조회 요청 → MongoDB에서 바로 반환
→ 쿠팡, 네이버쇼핑 이 방식 사용
포기한 것
원자성: 일부 작업만 성공할 수 있음
내구성: 메모리에 있을 때 서버 꺼지면 유실 가능
보완 방법
1. WAL (Write Ahead Log)
디스크 로그 선기록 → 서버 재시작 시 복구
2. 복제 (Replication)
데이터 3벌 복제
Cassandra: replication_factor = 3
서버 2대 죽어도 살아있음
3. 데이터 특성 활용
정합성이 덜 중요한 데이터에만 NoSQL 적용
좋아요 수, 조회수, 메시지, 로그
→ 잠깐 틀려도 결국 맞춰지면 됨
4. 돈 관련은 무조건 MySQL 유지
결제, 정산, 포인트 → 어떤 기업도 NoSQL 단독 안 씀
포기한 것
DB가 잘못된 데이터 타입/구조를 막아주지 않음
보완 방법
1. 애플리케이션 레벨 검증
@NotNull, @NotBlank, @Size 등으로 직접 검증
2. MongoDB Schema Validation
필수 필드 강제, 선택 필드 자유
3. 이벤트 소싱
변경 이력 저장 → 언제든 상태 재현 가능
포기한 것
여러 컬렉션 데이터를 한 번에 가져올 수 없음
보완 방법
1. Embedding
자주 함께 조회되는 데이터를 한 문서에 미리 합침
단, 자주 바뀌는 데이터는 embed 비적합
2. $lookup (MongoDB의 JOIN)
성능이 RDB JOIN보다 떨어짐
자주 쓰면 안 됨, 가끔 쓸 때만
3. CQRS 패턴
쓰기: MySQL (정규화)
읽기: MongoDB (비정규화, embed)
포기한 것
여러 컬렉션에 걸친 원자적 처리 보장 어려움
보완 방법
1. Saga 패턴
트랜잭션을 여러 단계로 분리
실패 시 보상 트랜잭션 실행
주문 생성 성공 → 재고 감소 실패
→ 보상: 주문 취소 실행
→ 결국 일관된 상태로 수렴
2. MongoDB 4.0+ 트랜잭션
멀티 문서 트랜잭션 지원
단, 샤딩 환경에서 성능 저하 있음
꼭 필요한 곳에만 사용
3. 단일 문서 설계
트랜잭션이 필요한 데이터를 한 문서에 embed
단일 문서 트랜잭션은 MongoDB도 보장
로그 데이터
API 요청/응답 로그, 에러 로그
→ 구조가 매번 다름, 대량 쌓임, 정합성 불필요
알림 이력
타입마다 구조가 다름
→ MongoDB 적합 (단, 공통 구조로 담기면 MySQL로 충분)
채팅 메시지
대화방 단위 저장, 대량 쓰기
→ Cassandra/HBase 적합
상품 속성
카테고리마다 속성 수십 개가 완전히 다름
→ MongoDB 적합
IoT 센서 데이터
기기마다 데이터 구조 다름, 초당 수만 건
→ Cassandra 적합
사용자 행동 분석
클릭, 페이지뷰, 필터 등 이벤트 다양
→ MongoDB 적합
세션/캐시
임시 데이터, 빠른 속도 필요
→ Redis 적합
결제/정산 → 돈이 관련된 모든 것
계약/부킹 → 법적 효력이 있는 데이터
회원 정보 → 로그인, 권한 관련
재고 관리 → 트랜잭션 필수
운임/견적 → 금액 계산
서류 관리 → 법적 문서
→ 이런 데이터는 어떤 기업도 NoSQL 단독 안 씀
대부분의 대기업은 이렇게 혼용해요.
ILIC 글로벌 확장 시 예시
MySQL (RDB) — 핵심 비즈니스
부킹, 정산, 회원, 운임, 서류
→ 정합성이 생명
Redis (NoSQL) — 속도
세션, 환율 캐시, SSE 다중 서버
→ 빠른 속도, 임시 데이터
MongoDB (NoSQL) — 유연성
API 로그, 알림 이력, 화물 타입별 속성
→ 구조 자유, 스키마 변경 잦음
Cassandra (NoSQL) — 대량 쓰기
IoT 선박 센서, 시계열 데이터
→ 초당 수십만 건 쓰기
Elasticsearch — 검색
포워더 검색, 화물 전문 검색
→ 역인덱스 기반 빠른 검색
Lock 경합
초당 수만 건 이상 쓰기 발생
MySQL 응답 지연이 시작될 때
→ Redis(캐시), Cassandra(대량 쓰기) 검토
ALTER TABLE
핵심 테이블이 수억 건 넘었는데
스키마를 자주 변경해야 할 때
→ MongoDB 검토
수평 확장
서버 10대 이상 필요한 규모
MySQL 샤딩이 복잡해질 때
→ MongoDB, Cassandra 샤딩 검토
스키마 다양성
카테고리/타입마다 속성이 수십 개씩 달라서
NULL 컬럼이 200개가 넘어갈 때
→ MongoDB 검토
전문 검색
MySQL LIKE가 느려서
사용자 검색 UX가 나빠질 때
→ Elasticsearch 검토
NoSQL이 해결하는 문제 딱 3가지
1. Lock 경합
RDB → 동시 쓰기 많으면 Lock 병목
NoSQL → Lock 없거나 최소화 → 대량 동시 쓰기 가능
2. ALTER TABLE
RDB → 수억 건 테이블 스키마 변경 = 수십 시간 중단
NoSQL → 스키마 없음 → 변경 즉시 가능
3. 수평 확장
RDB → 서버 늘리면 JOIN, 트랜잭션 문제
NoSQL → 처음부터 분산 설계 → 서버 추가 자연스러움
NoSQL은 RDB 대체재가 아니라 역할 분담
정합성 중요 + 관계 복잡 → MySQL
빠른 캐시 + 임시 데이터 → Redis
구조 자유 + 비정형 → MongoDB
초대량 쓰기 + 시계열 → Cassandra
텍스트 검색 → Elasticsearch
기업들이 NoSQL로 완전히 갈아탄 게 아님
MySQL로 시작 → 규모가 커져 MySQL이 발목 잡힐 때 → NoSQL 일부 도입
돈 관련은 어떤 기업도 NoSQL 단독으로 안 씀
| 날짜 | 내용 |
|---|---|
| 2026-04-20 | 초안 — NoSQL 개념, 기업 사례, RDB 장점 포기 및 보완 전략 |