기존 RDB(Relational Database)의 한계
관계형 데이터베이스(RDB)는 오랫동안 안정적인 데이터 저장소로 사용되어 왔다. 스키마가 명확하고, 트랜잭션과 정합성을 보장한다는 점에서 여전히 많은 시스템의 핵심 구성 요소다. 하지만 서비스 규모가 커질수록, RDB가 가진 구조적인 한계도 점점 분명해진다.
가장 먼저 마주치는 문제는 스키마 변경의 부담이다. 새로운 기능을 추가하면서 컬럼을 하나 더 넣어야 하는 상황이 생기면, RDB에서는 테이블 스키마 자체를 변경해야 한다.
ALTER TABLE users ADD COLUMN profile_image_url TEXT;
데이터가 적을 때는 큰 문제가 되지 않지만, 이미 수백만 건 이상의 데이터가 쌓인 테이블이라면 이러한 스키마 변경은 락(lock)을 유발하거나, 운영 중인 서비스에 영향을 줄 수 있다. 이 때문에 RDB는 데이터 구조가 자주 변하는 환경에서 유연한 확장이 어렵다는 단점을 가진다.
또 다른 특징은 정규화를 기반으로 한 설계다. RDB에서는 데이터 중복을 최소화하기 위해 테이블을 분리하고, 관계를 외래 키로 연결한다.
예를 들어 주문 정보를 저장하는 구조를 생각해보면 다음과 같다.
-- orders 테이블
order_id | user_id | product_id | created_at
-- users 테이블
user_id | user_name | email
-- products 테이블
product_id | product_name | price
이 구조에서 주문 정보를 조회하려면 여러 테이블을 조인해야 한다.
SELECT o.order_id, u.user_name, p.product_name
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN products p ON o.product_id = p.product_id;
정규화를 통해 데이터 중복은 줄일 수 있지만, 조인이 많아질수록 쿼리 비용은 증가하고, 그만큼 DB 서버의 CPU와 메모리 자원을 더 많이 사용하게 된다. 트래픽이 증가하는 상황에서는 이러한 조인 비용이 성능 병목으로 이어질 수 있다.
확장 방식에서도 제약이 존재한다. 일반적인 RDB는 기본적으로 하나의 DB 서버에 데이터를 저장하고, 성능이 부족해지면 Scale Up, 즉 서버의 물리적 성능을 높이는 방식으로 대응한다. CPU를 더 좋은 것으로 교체하거나, 메모리를 늘리는 식이다.
물론 Replication을 통해 read와 write를 분리하는 방법도 있다. write는 Primary 서버에서 처리하고, read는 Replica 서버로 분산시키는 방식이다. 하지만 write 트래픽 자체가 증가하는 상황에서는 이 역시 근본적인 해결책이 되기 어렵다. 결국 write는 하나의 Primary 서버가 감당해야 하기 때문이다.
Multi-master나 Sharding 같은 방식도 존재하지만, 트랜잭션과 정합성을 유지해야 하는 RDB의 특성상 Scale Out, 즉 DB 서버를 여러 대 추가하는 방식은 설계와 운영 난이도가 급격히 높아진다. 이 때문에 RDB는 구조적으로 Scale Out에 유연한 데이터베이스라고 보기는 어렵다.
한편 RDB는 ACID 트랜잭션을 보장한다는 강력한 장점을 가진다. 데이터의 일관성과 안정성을 보장하는 대신, 그만큼 성능 측면에서는 trade-off가 존재한다. 강한 정합성을 유지하기 위한 제약들이 대규모 트래픽 환경에서는 성능 부담으로 작용할 수 있다.
정리해보면, RDB는 안정성과 정합성이 중요한 시스템에서는 여전히 훌륭한 선택이지만,
대규모 트래픽, 빠른 확장, 유연한 데이터 구조가 요구되는 환경에서는 한계가 분명해진다.
이러한 배경 속에서, RDB의 단점을 보완하기 위한 대안으로 NoSQL이 등장하게 된다.
NoSQL의 등장 배경
2000년대 초중반, SNS와 대규모 웹 서비스가 빠르게 성장하면서 기존 RDB만으로는 트래픽을 감당하기 어려운 상황이 나타나기 시작했다. 이전보다 훨씬 많은 요청을 처리해야 했고, 동시에 높은 처리량(high throughput)과 낮은 지연 시간(low latency)이 요구되었다.
또한 서비스의 변화 속도가 빨라지면서 데이터 구조를 미리 예측하기 어려워졌고, 로그, 사용자 행동 기록, 다양한 형태의 메타데이터 등 비정형 데이터의 비중도 급격히 증가했다. 이런 환경에서는 스키마 변경 비용이 크고, scale out에 제약이 있는 RDB만으로는 한계가 분명했다.
이러한 문제를 해결하기 위해 등장한 개념이 NoSQL이다.
NoSQL은 “Not Only SQL”의 약자로, SQL을 사용하지 않겠다는 의미가 아니라 RDB가 잘하지 못하는 영역까지 다루기 위한 데이터 저장 방식을 의미한다.
NoSQL의 공통적인 특징
NoSQL은 종류가 매우 다양하지만, 전반적으로 다음과 같은 특징을 공유한다.
Flexible Schema (유연한 스키마)
RDB에서는 테이블을 생성할 때 컬럼과 타입을 미리 정의해야 한다. 반면 NoSQL에서는 스키마를 강제하지 않는 방식을 사용한다.
MongoDB를 예로 들면, 테이블에 해당하는 개념은 컬렉션(collection)이고, 각 행은 문서(document) 형태로 저장된다. 같은 컬렉션 안에서도 문서마다 서로 다른 구조를 가질 수 있다.
// 서로 다른 구조의 document를 같은 컬렉션에 저장
db.student.insertOne({
name: "easycode",
age: 20,
major: "Computer Science"
});
db.student.insertOne({
name: "baemin",
age: 22,
hobby: ["coding", "blogging"]
});
조회 역시 JSON 형태로 이루어진다.
db.student.find({ name: "easycode" });
db.student.find({});
이러한 유연성 덕분에 데이터 구조가 자주 변하는 서비스에서도 부담 없이 확장할 수 있다. 다만, 스키마를 DB가 강제하지 않기 때문에 데이터 구조에 대한 책임은 애플리케이션 레벨에서 직접 관리해야 한다는 점이 trade-off다.
중복 허용과 Join 회피
RDB는 정규화를 통해 데이터 중복을 제거하고, 필요할 때 join으로 데이터를 조합한다. 하지만 join은 연산 비용이 크고, 트래픽이 많아질수록 DB 서버의 부담이 된다.
NoSQL에서는 이러한 비용을 줄이기 위해 중복 데이터를 허용하고 join을 피하는 설계를 자주 사용한다.
RDB에서의 주문 구조는 보통 다음과 같다.
-- orders
order_id | user_id | product_id | created_at
-- users
user_id | user_name | email
-- products
product_id | product_name | price
주문 정보를 조회하려면 join이 필요하다.
SELECT o.order_id, u.user_name, p.product_name
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN products p ON o.product_id = p.product_id;
반면 NoSQL에서는 주문 정보를 하나의 document에 함께 저장한다.
db.order.insertOne({
order_id: 1,
user: {
user_id: 10,
name: "baemin",
email: "baemin@email.com"
},
product: {
product_id: 200,
name: "keyboard",
price: 50000
},
created_at: new Date()
});
db.order.find({});
이렇게 하면 조회 시 join이 필요 없어 성능은 좋아진다. 대신 사용자 이름이나 상품 정보가 변경되었을 때, 중복된 데이터들을 모두 갱신해야 하는 책임이 애플리케이션에 생긴다.
Scale Out에 최적화된 구조
NoSQL은 처음부터 scale out을 전제로 설계된 경우가 많다. 하나의 DB 서버가 감당하기 어려워지면, 서버를 추가해 클러스터를 구성하고 데이터를 분산 저장한다.
데이터가 여러 서버에 나뉘어 저장되더라도, 중복을 허용하기 때문에 외래 키나 join 문제로 인한 제약이 상대적으로 적다. 반면 RDB에서는 서로 다른 서버에 있는 데이터를 join해야 하므로 네트워크 비용과 복잡성이 크게 증가한다.
이 때문에 대규모 트래픽 환경에서는 NoSQL이 구조적으로 더 유리한 선택이 되는 경우가 많다.
ACID 일부를 포기하고 성능을 선택
NoSQL은 높은 처리량과 낮은 지연 시간을 위해 ACID의 일부를 포기하는 경우가 많다. 대신 eventual consistency와 같은 모델을 사용해 성능과 확장성을 확보한다.
이러한 특성 때문에 금융 시스템처럼 강한 일관성이 반드시 필요한 환경에서는 NoSQL 사용에 주의가 필요하다. 반대로 로그, 이벤트, 피드, 캐시처럼 약한 일관성을 허용할 수 있는 영역에서는 매우 효과적이다.
Redis: 대표적인 NoSQL 예시
Redis는 가장 널리 사용되는 NoSQL 중 하나로, In-memory 기반 Key-Value 저장소다. 데이터베이스로도 사용되며, 캐시로 특히 많이 활용된다.
SET name baemin
GET name
Redis는 문자열뿐 아니라 hash, list, set 등 다양한 데이터 타입을 지원한다. 또한 replication과 automatic failover를 지원해 고가용성(HA) 환경을 구성할 수 있다.
Redis가 특히 강점을 가지는 이유는 메모리 기반이라는 점이다. SSD 기반 DB보다 훨씬 빠른 응답 속도를 제공하기 때문에, 트래픽이 많은 서비스에서는 캐시로 자주 사용된다.
예를 들어 유튜브 영상 정보를 조회하는 상황을 생각해보면 다음과 같은 흐름이 된다.

- 사용자가 영상 요청
- Backend가 Redis 캐시 조회
- 캐시에 있으면 바로 응답
- 없으면 DB 조회 후 결과를 Redis에 저장
- 이후 요청은 Redis에서 처리
# 영상 정보 캐시 (TTL 60초)
SET video:123 "{title:'DB Tutorial', views:10000}" EX 60
이 구조를 통해 DB까지 가지 않아도 되는 요청을 크게 늘릴 수 있고, 전체 시스템 성능도 안정적으로 유지할 수 있다.
NoSQL은 RDB를 완전히 대체하기 위한 기술이 아니라,
RDB가 잘하지 못하는 문제를 해결하기 위해 등장한 선택지다.
- 강한 정합성과 트랜잭션이 중요하다면 RDB
- 대규모 트래픽, 빠른 확장, 유연한 데이터 구조가 필요하다면 NoSQL
- 빠른 응답과 트래픽 완충이 필요하다면 Redis와 같은 캐시
결국 중요한 것은 기술 자체가 아니라, 현재 서비스의 요구사항과 병목을 정확히 이해하고 그에 맞는 도구를 선택하는 것이다.
쉬운코드: BJ.53 NoSQL 설명!! RDB와는 어떤 차이가 있는지도 설명!! MongoDB, Redis 매우 간단한 예제 포함!!
'CS > DB' 카테고리의 다른 글
| DB 성능 문제, 구조를 나누는 것부터 시작하기 (0) | 2025.12.28 |
|---|---|
| DBCP가 뭐지..? (0) | 2025.12.28 |
| 데이터베이스 성능을 저하시키는 N+1 쿼리 문제란? (0) | 2025.10.03 |
| Transaction 알아보기 (0) | 2025.09.15 |
| Enum VS VARCHAR (0) | 2025.09.15 |
