목표: 관계형 vs NoSQL, ACID vs BASE, CAP 정리 를 이해하고 서비스 요구에 맞는 DB를 선택할 수 있어야 한다. FE 개발자도 DB 선택 이유를 알면 API 설계와 협업이 쉬워진다.
0. DB가 뭔지부터 — 초심자용
0-1. "왜 그냥 파일에 저장 안 해?"
초보자가 가장 먼저 드는 생각: "데이터를 저장할 거면 JSON 파일 하나 만들면 되는 거 아닌가?"
작은 프로젝트는 실제로 된다. 문제는 규모.
- 유저 1만 명이 동시에 같은 파일 수정 → 데이터 깨짐
- 100만 개 데이터에서 조건 검색 → 전체 파일 읽어야 함, 느림
- 서버가 중간에 터지면 → 쓰던 데이터 손실
- 여러 서버에서 같은 파일 공유 → 락, 동기화 지옥
데이터베이스(DB) 는 이런 문제들을 수십 년 동안 해결해 온 전문 파일 시스템이다. 그냥 "저장" 이 아니라 동시 접근, 검색 최적화, 복구, 트랜잭션 이 모두 포함된 것.
0-2. DB를 구성하는 기본 용어
| 용어 | 한 줄 의미 | 비유 |
|---|---|---|
| 테이블(Table) | 같은 종류 데이터의 모임 | 엑셀 시트 |
| 행(Row, Record) | 하나의 데이터 항목 | 엑셀의 한 줄 |
| 열(Column, Field) | 데이터의 속성 | 엑셀의 한 칸 |
| 스키마(Schema) | "어떤 열이 있는지"의 설계도 | 엑셀 헤더 정의 |
| 쿼리(Query) | DB에 던지는 질문/명령 | "이런 조건의 행 찾아줘" |
| SQL | 쿼리를 쓰는 표준 언어 | 영어/한국어 |
0-3. FE 개발자가 DB를 아는 이유
"DB는 백엔드 영역 아닌가?" 절반만 맞다.
- API 설계: BE가 "이 쿼리는 비싸서 불가능"하다고 할 때 이해할 수 있어야 함
- 에러 메시지: "unique constraint violation" 같은 에러를 프론트에서 처리
- 프로토타이핑: Firebase, Supabase, PlanetScale 처럼 FE에서 DB를 직접 쓰는 BaaS 증가
- 성능: 페이지가 느린 이유가 FE가 아니라 DB 쿼리인 경우가 아주 많음
0-4. 이 단계(4단계)의 여정
| 챕터 | 무엇 |
|---|---|
| 4-1 (지금) | 어떤 DB 를 고를 것인가 |
| 4-2 | SQL 기본 문법 |
| 4-3 | 정규화 — 중복 없는 테이블 설계 |
| 4-4 | 인덱스 — 쿼리를 빠르게 만드는 도구 |
| 4-5 | 트랜잭션 — 여러 작업을 하나로 묶기 |
| 4-6 | FE가 자주 만나는 실전 이슈 |
0-5. 이 장의 핵심 질문
"모든 DB가 다 하는 일이 다른데, 내가 만드는 서비스엔 어떤 DB가 맞나?" — 이걸 판단하기 위한 기준이 이 장에서 다룰 ACID, BASE, CAP이다.
1. 데이터베이스의 종류
관계형 (RDBMS)
테이블·행·열·스키마. 대표: PostgreSQL, MySQL, SQLite, Oracle.
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(255) UNIQUE,
created_at TIMESTAMP
);
NoSQL
| 종류 | 대표 | 특징 |
|---|---|---|
| Document | MongoDB, CouchDB | JSON 유사. 스키마리스 |
| Key-Value | Redis, DynamoDB | 초고속, 캐시·세션 |
| Wide-Column | Cassandra, HBase | 대규모 분산, 쓰기 많은 워크로드 |
| Graph | Neo4j, ArangoDB | 관계 중심 질의 |
| Time-series | InfluxDB, TimescaleDB | 시계열 최적화 |
| Search | Elasticsearch, OpenSearch | 전문 검색 |
| Vector | Pinecone, pgvector | 임베딩, RAG |
NewSQL
- CockroachDB, Google Spanner, TiDB
- SQL 인터페이스 + 수평 확장 + ACID
2. ACID — 관계형 DB의 약속
| 의미 | |
|---|---|
| Atomicity | 트랜잭션은 전부 성공 또는 전부 실패 |
| Consistency | 트랜잭션 전후로 무결성 제약 유지 |
| Isolation | 동시 실행 트랜잭션이 서로 간섭 안 함 |
| Durability | 커밋된 데이터는 장애에도 보존 |
3. BASE — NoSQL의 철학
| 의미 | |
|---|---|
| Basically Available | 부분 장애에도 일부 응답 가능 |
| Soft state | 시간이 지나며 상태가 변할 수 있음 |
| Eventually consistent | 결국 일관성에 수렴 |
4. CAP 정리 (Eric Brewer, 2000)
분산 시스템은 세 속성 중 최대 두 개만 보장 가능.
| 의미 | |
|---|---|
| Consistency | 모든 노드가 같은 데이터를 봄 |
| Availability | 모든 요청이 응답 받음 |
| Partition Tolerance | 네트워크 분할 발생해도 동작 |
네트워크 분할은 현실에서 불가피 → P는 사실상 필수. 선택은 C vs A.
| 유형 | 예 |
|---|---|
| CP | MongoDB (기본), HBase, Redis (single primary) |
| AP | Cassandra, DynamoDB, Couchbase |
| CA | 전통 RDBMS (단일 노드 가정, 실제 분산에선 불가) |
PACELC (CAP 확장)
"분할이 없을 때도 Latency vs Consistency 트레이드오프가 있다."
If Partition: A or C ?
Else (정상): L or C ?
5. 격리 수준 (Isolation Levels)
SQL 표준이 정의.
| 수준 | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
| Read Uncommitted | ✅ 허용 | ✅ | ✅ |
| Read Committed | ❌ | ✅ | ✅ |
| Repeatable Read | ❌ | ❌ | ✅ |
| Serializable | ❌ | ❌ | ❌ |
현상 정의
- Dirty Read: 커밋 안 된 다른 트랜잭션의 변경을 읽음
- Non-Repeatable Read: 같은 쿼리가 두 번 다른 값을 반환
- Phantom Read: 같은 조건 쿼리의 결과 행 개수가 달라짐
PostgreSQL / MySQL 기본
- PostgreSQL 기본: Read Committed
- MySQL (InnoDB) 기본: Repeatable Read — 갭 락으로 Phantom도 사실상 막음
6. 정규화 vs 역정규화
정규화 (Normalize)
- 중복 제거, 갱신 이상 방지
- JOIN 증가 → 읽기 비용 ↑
역정규화 (Denormalize)
- 읽기 성능 최적화, 중복 허용
- 쓰기·갱신 시 여러 곳 수정 필요
선택 규칙
OLTP (주문, 결제) → 정규화 + 인덱스
OLAP (분석 대시보드) → 역정규화, 스타 스키마
Document DB → 역정규화 선호 (JOIN 대안)
7. DB 선택 플로우차트
질의가 복잡·관계 중심?
├─ Yes → 관계형 (PostgreSQL 기본)
│ └─ 수평 확장 필요? → CockroachDB, Spanner
├─ No, 문서형 데이터 (JSON)
│ └─ Document DB (MongoDB)
├─ No, 키-값 고속 캐시
│ └─ Redis
├─ No, 대규모 쓰기 (로그, IoT)
│ └─ Cassandra, TimescaleDB
├─ No, 관계 질의 (추천·SNS 친구)
│ └─ Graph DB (Neo4j)
└─ No, 전문 검색
└─ Elasticsearch
중요 원칙
- 작은 서비스는 PostgreSQL 하나 로 대부분 해결
JSONB로 관계형 + 문서형 하이브리드 가능- 시작부터 NoSQL 은 과잉 설계인 경우가 많음
8. 프론트엔드와 DB의 접점
직접 DB에 연결하지 않지만, 알아야 하는 것:
페이지네이션
- Offset:
LIMIT 10 OFFSET 100— 뒤로 갈수록 느림 - Cursor:
WHERE id > 100 ORDER BY id LIMIT 10— 일정, 무한 스크롤에 유리 - 응답 형식:
{data, nextCursor}
정렬 / 필터
- 서버 정렬이 원칙 (페이지네이션과 함께)
- 필터 조건은 인덱스 활용 가능 한 것 우선
N+1 문제
const users = await fetch('/users'); // 1번
for (const u of users) {
const orders = await fetch(`/users/${u.id}/orders`); // N번
}
→ 백엔드가 JOIN or batch API 제공, 또는 GraphQL + Dataloader.
낙관적 업데이트
- 쓰기 응답을 기다리지 않고 UI 먼저 변경
- 실패 시 롤백 + 토스트
- React Query
onMutate로 구현
9. 프론트 로컬 DB
| 용도 | |
|---|---|
| IndexedDB | 대용량 구조화 데이터, 오프라인 PWA |
| Dexie, idb | IndexedDB 래퍼 |
| RxDB, WatermelonDB | 로컬 ↔ 서버 동기화 |
| SQLite WASM (sqlite.org/wasm) | 브라우저 내 SQL |
| DuckDB WASM | 브라우저 내 분석 SQL |
10. ⚠️ 자주 하는 오해
| 오해 | 실제 |
|---|---|
| NoSQL이 관계형보다 항상 빠르다 | 쿼리 패턴과 인덱스에 따라 다름 |
| MongoDB는 스키마가 없다 | 어차피 애플리케이션에서 스키마 강제함 — $jsonSchema 권장 |
| CAP에서 CA를 고르면 된다 | 분산에서는 P 필수 → CA는 불가능 |
| Redis는 휘발성이다 | RDB/AOF 영속화 가능 |
| 트랜잭션은 RDBMS 전용 | MongoDB 4.0+, Redis 7+ 도 지원 |
11. 연습 문제
Q1. ACID의 네 속성을 설명하라.
정답
- Atomicity: All-or-nothing, 트랜잭션 내 연산이 모두 성공하거나 모두 실패.
- Consistency: 제약조건·불변식이 트랜잭션 전후에 유지됨.
- Isolation: 동시 트랜잭션이 서로 간섭받지 않음 (격리 수준 따라 정도 다름).
- Durability: 커밋 후에는 장애 발생해도 데이터 유지 (WAL + fsync).
Q2. CAP 정리에서 왜 "CA"는 실질적으로 불가능한가?
정답
분산 시스템에서 네트워크 파티션은 언젠가는 발생한다. 파티션이 발생하지 않으면 C와 A를 모두 보장할 수 있지만, 발생 시 선택해야 한다. 따라서 설계상 P를 포기할 수 없으며, 남는 선택은 C vs A.
Q3. Read Committed와 Repeatable Read의 차이는?
정답
- Read Committed: 같은 트랜잭션 안에서 같은 쿼리를 여러 번 해도 매번 최신 커밋 데이터를 읽음 → Non-Repeatable Read 가능.
- Repeatable Read: 같은 트랜잭션 안의 쿼리는 트랜잭션 시작 시점의 스냅샷을 일관되게 반환 → Non-Repeatable Read 불가.
둘 다 Phantom Read는 허용 (MySQL InnoDB는 갭 락으로 막음).
Q4. OLTP와 OLAP에 적합한 스키마 설계는?
정답
- OLTP (트랜잭션): 정규화(3NF) + 적절한 인덱스. 작은 쓰기가 많고 데이터 중복 최소화.
- OLAP (분석): 스타/스노우플레이크 스키마 등 역정규화. 읽기 중심, 조인 최소화, 집계 성능 최적화.
Q5. 무한 스크롤에 Offset 페이지네이션을 쓰면 안 되는 이유는?
정답
- Offset이 커질수록 DB가 그 앞의 모든 행을 스캔 → 느려짐 (LIMIT/OFFSET 10000 = 선행 만 건 스킵)
- 새 데이터가 중간에 삽입되면 중복·누락 발생
- 커서 기반 (
WHERE id > last ORDER BY id LIMIT 10) 은 일정 O(log N) + 정확한 경계
Q6. Redis를 쓰면 CAP에서 어떤 선택을 한 것인가?
정답
Redis (기본 싱글 마스터)는 CP. 마스터 장애 시 복제본으로 failover 동안 일시적 가용성 저하가 있지만 데이터 일관성을 우선. Redis Cluster도 majority quorum 기반 CP. Redis Enterprise 등에서는 AP 모드 옵션도 있음.
Q7. PostgreSQL의 JSONB로 MongoDB 같은 문서형 DB를 대체할 수 있는가?
정답
상당 부분 가능. JSONB는 GIN 인덱스로 키 검색 가능하고, 관계형 테이블과 JOIN도 된다. 작은~중규모 서비스는 PostgreSQL 단일 DB로 관계형 + 문서형 혼합이 실무 권장. 다만 대용량 문서 + 수평 샤딩이 필요하면 MongoDB가 유리.
12. 체크리스트
- 관계형 vs NoSQL의 대표와 용도를 구분한다
- ACID 4가지를 설명할 수 있다
- CAP 정리와 PACELC를 안다
- 격리 수준 4단계와 현상 3가지를 외운다
- 정규화 vs 역정규화 선택 기준을 안다
- Offset vs Cursor 페이지네이션의 차이를 안다
- PostgreSQL JSONB의 하이브리드 활용을 안다