SQL(관계형 DB)과 NoSQL의 차이점과 각각의 적합한 사용 사례를 설명해주세요.
답변 포인트
스키마 유연성, 확장 방식, CAP 정리를 생각해보세요.
정답 및 해설
빠른 요약
SQL (RDBMS): 고정 스키마, 정규화, ACID 트랜잭션, 수직 확장, 복잡한 쿼리/조인. → 금융, 전자상거래, 관계가 복잡한 데이터에 적합 NoSQL: 유연한 스키마, 수평 확장, 다양한 데이터 모델(문서/키-값/컬럼/그래프).
SQL(관계형 데이터베이스)과 NoSQL은 데이터를 저장하고 조회하는 방식에서 근본적으로 다른 철학을 가집니다. 어떤 데이터베이스가 적합한지는 데이터의 구조, 일관성 요구사항, 확장성, 쿼리 패턴에 따라 결정됩니다.
SQL (관계형 데이터베이스)
핵심 특징
**고정 스키마(Fixed Schema)**로 데이터를 저장합니다. 테이블을 생성할 때 컬럼과 데이터 타입을 미리 정의해야 하며, 이 구조에 맞지 않는 데이터는 저장할 수 없습니다.
-- 테이블 생성 시 스키마를 명확히 정의
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
age INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 정규화: 중복을 줄이기 위해 관련 데이터를 별도 테이블로 분리
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
total DECIMAL(10, 2),
status ENUM('pending', 'completed', 'cancelled'),
FOREIGN KEY (user_id) REFERENCES users(id)
);ACID 트랜잭션
SQL 데이터베이스는 ACID 특성을 보장하여 데이터 정합성을 유지합니다.
| 속성 | 의미 | 예시 |
|---|---|---|
| Atomicity (원자성) | 트랜잭션 내 모든 작업이 성공하거나 모두 실패 | 계좌 이체: 출금과 입금이 함께 성공하거나 함께 실패 |
| Consistency (일관성) | 트랜잭션 전후 데이터가 유효한 상태 유지 | 잔액이 음수가 되지 않도록 제약 조건 유지 |
| Isolation (격리성) | 동시에 실행되는 트랜잭션이 서로 영향을 주지 않음 | 다른 사용자의 미완성 트랜잭션 결과를 읽지 않음 |
| Durability (지속성) | 커밋된 트랜잭션은 장애가 발생해도 유지 | 서버가 다운돼도 커밋된 데이터는 보존 |
-- ACID 트랜잭션 예시: 계좌 이체
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
UPDATE accounts SET balance = balance + 100000 WHERE id = 2;
-- 중간에 오류 발생 시 모두 되돌림
COMMIT; -- 또는 ROLLBACK;복잡한 쿼리와 JOIN
여러 테이블에 걸친 복잡한 관계를 SQL로 표현할 수 있습니다.
-- 사용자별 최근 주문과 상품 정보를 함께 조회
SELECT
u.name,
o.id AS order_id,
o.created_at,
SUM(oi.quantity * oi.price) AS total
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY u.id, o.id
ORDER BY o.created_at DESC;수직 확장 (Scale-Up)
SQL 데이터베이스는 주로 서버 자체의 하드웨어 성능을 높이는 방식으로 확장합니다. 수평 확장(샤딩)도 가능하지만 복잡도가 높습니다.
NoSQL
핵심 특징
유연한 스키마(Flexible Schema) 를 가집니다. 컬럼을 미리 정의하지 않아도 되며, 문서마다 다른 구조를 가질 수 있습니다.
NoSQL의 4가지 데이터 모델
1. 문서 기반 (Document) — MongoDB, CouchDB
JSON 유사 형태의 문서로 데이터를 저장합니다. 중첩된 구조를 그대로 저장할 수 있어 계층적 데이터에 자연스럽습니다.
// MongoDB 문서 예시 — 중첩 구조를 그대로 저장
{
"_id": "user_123",
"email": "user@example.com",
"name": "홍길동",
"address": {
"city": "서울",
"district": "강남구",
"zip": "06100"
},
"tags": ["premium", "vip"],
"orders": [
{ "id": "ord_001", "total": 50000, "status": "completed" },
{ "id": "ord_002", "total": 30000, "status": "pending" }
]
}
// 조회
db.users.find({ "address.city": "서울", "tags": "premium" });2. 키-값 기반 (Key-Value) — Redis, DynamoDB
가장 단순한 구조로, 키로 값을 저장하고 조회합니다. 매우 빠른 속도가 장점입니다.
SET user:session:abc123 '{"userId": 42, "role": "admin"}' EX 3600
GET user:session:abc1233. 컬럼 기반 (Wide-Column) — Apache Cassandra, HBase
행 단위가 아닌 컬럼 패밀리 단위로 데이터를 저장합니다. 대용량 쓰기와 분산 처리에 강점이 있습니다.
4. 그래프 기반 (Graph) — Neo4j
노드(데이터)와 엣지(관계)로 데이터를 표현합니다. 소셜 네트워크, 추천 시스템처럼 관계 탐색이 핵심인 경우에 적합합니다.
// Neo4j Cypher 쿼리 — "홍길동의 친구의 친구" 찾기
MATCH (user:Person {name: "홍길동"})-[:FRIENDS_WITH*2]-(fof)
WHERE NOT (user)-[:FRIENDS_WITH]-(fof)
RETURN fof.name수평 확장 (Scale-Out)
NoSQL은 샤딩(Sharding) 을 통한 수평 확장을 기본적으로 지원합니다. 데이터를 여러 서버에 분산 저장하여 트래픽과 데이터양이 늘어나도 서버를 추가하는 방식으로 대응할 수 있습니다.
CAP 정리
분산 시스템에서는 Consistency(일관성), Availability(가용성), Partition Tolerance(분단 내성) 를 동시에 모두 만족할 수 없습니다. NoSQL은 대개 일관성을 약간 희생하고 가용성과 분단 내성을 선택합니다(최종 일관성, Eventual Consistency).
SQL vs NoSQL 비교
| 항목 | SQL (RDBMS) | NoSQL |
|---|---|---|
| 스키마 | 고정 (변경 시 마이그레이션 필요) | 유연 (동적으로 변경 가능) |
| 데이터 모델 | 테이블 (행/열) | 문서, 키-값, 컬럼, 그래프 |
| 트랜잭션 | ACID 완전 지원 | 제한적 (일부 지원, Eventual Consistency) |
| 확장 방식 | 수직 확장 (주로) | 수평 확장 (기본 지원) |
| JOIN | 복잡한 JOIN 지원 | 대부분 JOIN 미지원 |
| 쿼리 언어 | SQL (표준화) | DB마다 다름 |
| 대표 제품 | MySQL, PostgreSQL, Oracle | MongoDB, Redis, Cassandra, DynamoDB |
적합한 사용 사례
SQL을 선택해야 할 때
데이터 간 관계가 복잡하고, 정합성이 중요할 때 SQL이 적합합니다.
- 금융 시스템: 계좌 이체, 결제 처리 — ACID 트랜잭션 필수
- 전자상거래: 주문, 결제, 재고 관리 — 복잡한 관계와 정합성 필요
- ERP/CRM: 고객, 계약, 청구서 관리 — 정규화된 데이터 구조
- 인사관리 시스템: 직원, 부서, 급여 — 관계형 데이터
-- 전자상거래: 재고 차감과 주문 생성을 원자적으로 처리
BEGIN;
INSERT INTO orders (user_id, product_id, quantity) VALUES (1, 42, 2);
UPDATE products SET stock = stock - 2 WHERE id = 42 AND stock >= 2;
-- 재고가 부족하면 ROLLBACK
COMMIT;NoSQL을 선택해야 할 때
대용량, 비정형 데이터, 빠른 읽기/쓰기, 유연한 구조가 필요할 때 NoSQL이 적합합니다.
- 세션/캐시 저장: Redis — 빠른 키-값 접근, 만료 시간 설정
- 콘텐츠 관리 시스템: MongoDB — 구조가 다양한 게시글, 설정 데이터
- 실시간 분석 / 이벤트 로그: Cassandra — 대용량 쓰기, 시계열 데이터
- 소셜 네트워크: Neo4j — 팔로우/팔로워, 추천 알고리즘
- IoT 데이터: 빠른 속도로 쌓이는 센서 데이터
// MongoDB: 상품마다 다른 속성을 유연하게 저장
db.products.insertMany([
{ name: "맥북 프로", category: "노트북", specs: { ram: "16GB", cpu: "M3 Pro" } },
{ name: "청바지", category: "의류", specs: { size: "32", color: "blue" } },
// 카테고리마다 완전히 다른 속성 — SQL에서는 비효율적
]);선택 기준 정리
데이터 구조가 명확하고 관계가 복잡한가? → SQL
데이터 정합성(ACID)이 최우선인가? → SQL
데이터 구조가 자주 바뀌거나 비정형인가? → NoSQL
초당 수만 건의 쓰기가 필요한가? → NoSQL (Cassandra 등)
단순 캐싱/세션 저장이 목적인가? → NoSQL (Redis)
관계 탐색이 핵심 기능인가? → NoSQL (Graph DB)실제로 대규모 시스템에서는 SQL과 NoSQL을 함께 사용하는 경우가 많습니다. 예를 들어 핵심 비즈니스 데이터는 PostgreSQL에, 세션과 캐시는 Redis에, 로그와 분석 데이터는 Cassandra에 저장하는 방식으로 각 DB의 장점을 활용합니다.