"어떤 데이터베이스를 사용할 것인가?" 백엔드 아키텍처를 설계할 때 가장 먼저 결정해야 하는 부분입니다. 모든 서비스에 통용되는 완벽한 DB(은탄환)는 없으며, 서비스가 어떤 성격의 데이터를 얼만큼 처리하느냐에 따라 관계형 데이터베이스(SQL)와 비관계형 데이터베이스(NoSQL) 사이에서 줄다리기를 해야 합니다.

이 글에서는 두 종류 DB의 근본적인 차이점, 각각의 대표 제품들, 실제 쿼리 코드, 그리고 어떤 상황에서 무엇을 선택해야 하는지까지 완전히 정리합니다.

1. RDBMS (SQL) — 관계형 데이터베이스

MySQL, PostgreSQL, Oracle, SQL Server 등으로 대표되는 관계형 데이터베이스는 데이터의 정합성과 무결성을 최우선으로 합니다. 테이블(Table)과 행(Row), 열(Column)으로 이루어진 스프레드시트와 유사한 구조로 데이터를 저장하며, 테이블 간의 관계를 외래 키(Foreign Key)로 연결합니다.

  • 데이터 무결성 보장: 스키마(Schema)가 엄격하게 정의되어 있기 때문에, 엉뚱한 타입의 데이터가 입력되는 것을 방지합니다.
  • ACID 트랜잭션: 쇼핑몰의 결제 시스템처럼 돈이 빠져나갔는데 주문이 실패하는 사고를 막기 위해 은행, 금융권, 상거래에 필수적입니다.
  • 강력한 JOIN: 여러 테이블의 데이터를 하나의 쿼리로 조합할 수 있어 복잡한 비즈니스 로직 처리에 강합니다.
  • 단점: 구조가 고정적인 탓에, 스키마 마이그레이션이 번거로우며 수평적 확장(Scale-out)에 기술적인 제약이 큽니다.

2. SQL 기본 문법과 실전 예시

전자상거래 서비스를 예시로 테이블 생성부터 복잡한 JOIN 쿼리까지 살펴봅니다.

-- 1. 테이블 생성
CREATE TABLE users (
    id         SERIAL PRIMARY KEY,
    email      VARCHAR(255) UNIQUE NOT NULL,
    name       VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE orders (
    id         SERIAL PRIMARY KEY,
    user_id    INTEGER REFERENCES users(id) ON DELETE CASCADE,
    total      NUMERIC(10, 2) NOT NULL,
    status     VARCHAR(50) DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE order_items (
    id         SERIAL PRIMARY KEY,
    order_id   INTEGER REFERENCES orders(id),
    product    VARCHAR(255) NOT NULL,
    quantity   INTEGER NOT NULL,
    price      NUMERIC(10, 2) NOT NULL
);

-- 2. 데이터 삽입
INSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice');

-- 3. JOIN으로 사용자별 주문 내역 조회
SELECT
    u.name        AS 고객명,
    o.id          AS 주문번호,
    o.total       AS 총금액,
    o.created_at  AS 주문일시
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.email = 'alice@example.com'
ORDER BY o.created_at DESC;

-- 4. 집계: 월별 매출 통계
SELECT
    DATE_TRUNC('month', created_at) AS 월,
    COUNT(*) AS 주문수,
    SUM(total) AS 매출
FROM orders
WHERE status = 'completed'
GROUP BY 1
ORDER BY 1 DESC;

-- 5. 인덱스로 조회 성능 향상
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status  ON orders(status);

3. ACID 트랜잭션이란?

ACID는 데이터베이스 트랜잭션의 4가지 핵심 속성으로, 시스템 장애가 발생해도 데이터 일관성을 보장합니다.

  • A — Atomicity (원자성): 트랜잭션의 모든 연산은 전부 성공하거나 전부 실패해야 합니다. "계좌 이체"에서 출금은 됐는데 입금이 안 되는 상황이 불가.
  • C — Consistency (일관성): 트랜잭션 전후로 DB가 항상 유효한 상태를 유지합니다. 잔고가 음수가 되는 등 무결성 규칙 위반 상황을 막습니다.
  • I — Isolation (격리성): 동시에 실행되는 트랜잭션들이 서로 간섭하지 않습니다. 두 사용자가 동시에 같은 재고를 감소시킬 때 정합성을 보장.
  • D — Durability (내구성): 커밋된 트랜잭션은 시스템 장애 후에도 영구적으로 저장됩니다.
-- PostgreSQL 트랜잭션 예시 (계좌 이체)
BEGIN;

UPDATE accounts SET balance = balance - 50000 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 50000 WHERE user_id = 2;

-- 중간에 오류 발생 시 ROLLBACK으로 원상복구
-- ROLLBACK;

COMMIT;  -- 두 UPDATE 모두 성공 시 확정

4. NoSQL — 비관계형 데이터베이스

NoSQL은 "Not Only SQL"의 약자로, 관계형 모델을 사용하지 않는 다양한 데이터베이스를 통칭합니다. RDBMS의 뻣뻣한 구조를 깨고 대용량 트래픽의 분산 처리를 목적으로 만들어졌습니다. 저장 방식에 따라 크게 4가지로 분류됩니다.

  • 도큐먼트 DB (MongoDB, CouchDB): JSON/BSON 형태의 도큐먼트로 저장. 스키마가 유연하여 필드 구조가 레코드마다 달라도 됩니다. 콘텐츠 관리 시스템, 카탈로그에 적합.
  • 키-값 DB (Redis, DynamoDB): 단순한 키-값 쌍으로 데이터 저장. 메모리 기반으로 초고속 읽기/쓰기. 캐시, 세션 관리, 실시간 리더보드에 사용.
  • 컬럼 패밀리 DB (Apache Cassandra, HBase): 대규모 쓰기 최적화, 높은 가용성. 시계열 데이터, 로그, IoT에 주로 활용.
  • 그래프 DB (Neo4j, Amazon Neptune): 노드와 엣지로 관계를 표현. 소셜 네트워크, 추천 엔진, 사기 탐지에 특화.

5. MongoDB 실전 예시

상품 카탈로그 서비스를 MongoDB로 구현하는 예시입니다. 유연한 스키마 덕분에 상품마다 속성이 달라도 하나의 컬렉션에 저장할 수 있습니다.

// 1. 도큐먼트 삽입 (상품마다 필드가 다를 수 있음)
db.products.insertMany([
  {
    name: "맥북 프로 14인치",
    category: "laptop",
    price: 3490000,
    specs: { cpu: "M3 Pro", ram: "18GB", storage: "512GB SSD" },
    tags: ["apple", "macOS", "프리미엄"]
  },
  {
    name: "개발자 기계식 키보드",
    category: "peripheral",
    price: 150000,
    switch_type: "청축",
    backlight: true,
    tags: ["keyboard", "mechanical"]
  }
]);

// 2. 조회: 가격 범위 + 태그 필터
db.products.find({
  price: { $gte: 100000, $lte: 500000 },
  tags: "apple"
}).sort({ price: -1 }).limit(10);

// 3. 집계 파이프라인: 카테고리별 평균 가격
db.products.aggregate([
  { $group: {
    _id: "$category",
    avgPrice: { $avg: "$price" },
    count:    { $sum: 1 }
  }},
  { $sort: { avgPrice: -1 } }
]);

// 4. 인덱스 생성
db.products.createIndex({ category: 1, price: -1 });
db.products.createIndex({ tags: 1 });

6. Redis: 캐싱과 세션 관리

Redis는 메모리 기반의 초고속 키-값 데이터베이스로, DB 앞단에 캐시 레이어로 사용하거나 세션 저장소로 많이 활용됩니다.

# Redis CLI 기본 명령어

# 문자열 저장 (TTL 3600초 = 1시간)
SET session:user:123 "{ userId: 123, role: 'admin' }" EX 3600

# 캐시 확인 및 조회
GET session:user:123

# 리스트: 최근 본 상품 기록 (최대 10개 유지)
LPUSH recent:user:123 "product_456"
LTRIM recent:user:123 0 9
LRANGE recent:user:123 0 -1

# 해시: 사용자 프로필
HSET user:123 name "Alice" email "alice@example.com" plan "premium"
HGETALL user:123

# 정렬 셋: 실시간 리더보드
ZADD leaderboard 9850 "player_A"
ZADD leaderboard 8720 "player_B"
ZREVRANGE leaderboard 0 9 WITHSCORES   # TOP 10 조회

7. CAP 정리 — 분산 시스템의 트레이드오프

분산 데이터베이스를 선택할 때 이해해야 할 핵심 이론입니다. CAP 정리에 따르면, 분산 시스템에서는 다음 3가지 속성 중 동시에 2가지만 보장할 수 있습니다.

  • C — Consistency (일관성): 모든 노드가 같은 시점에 같은 데이터를 반환합니다.
  • A — Availability (가용성): 모든 요청에 항상 응답합니다 (최신 데이터가 아닐 수도 있음).
  • P — Partition Tolerance (분할 내성): 네트워크 분리(파티션)가 발생해도 시스템이 동작합니다.

실제 네트워크에서 P(분할 내성)는 피할 수 없기 때문에, 결국 C와 A 사이에서 선택해야 합니다.

  • CP 시스템 (일관성 우선): MongoDB, HBase, Redis → 전자상거래 주문, 금융 거래
  • AP 시스템 (가용성 우선): Cassandra, DynamoDB, CouchDB → SNS 피드, 로그 수집

8. DB 선택 기준 가이드

다음 체크리스트로 어떤 DB를 선택할지 결정할 수 있습니다.

  • 복잡한 쿼리와 JOIN이 많다 → PostgreSQL, MySQL (RDBMS)
  • ACID 트랜잭션이 필수다 (금융, 결제) → PostgreSQL, Oracle
  • 데이터 구조가 자주 바뀌거나 중첩 구조다 → MongoDB (도큐먼트)
  • 초고속 캐시, 세션 관리가 필요하다 → Redis, Memcached (키-값)
  • 대용량 쓰기와 수평 확장이 핵심이다 → Cassandra, DynamoDB (컬럼)
  • 관계(Relationship) 탐색이 핵심이다 (추천, 소셜) → Neo4j (그래프)
  • 시계열 데이터 (모니터링, IoT) → InfluxDB, TimescaleDB

결론: Polyglot Persistence

최근의 마이크로서비스 아키텍처에서는 회원, 결제 같은 핵심 도메인에는 RDBMS를 사용하고, 상품 카탈로그나 장바구니, 로그 기록 등 조회 성능이 중요하거나 용량이 큰 도메인에는 NoSQL 파생 기술들을 결합하여 사용하는 Polyglot Persistence 방식이 트렌드로 자리잡고 있습니다.

예를 들어 대형 이커머스 서비스는 일반적으로 이런 구조를 택합니다: 사용자 계정/주문은 PostgreSQL, 상품 카탈로그는 MongoDB, 세션/캐시는 Redis, 검색 기능은 Elasticsearch. 중요한 것은 각 도구의 강점을 이해하고, 요구사항에 맞는 최고의 조합을 찾는 것입니다.