DB 어휘 playground
원문에서 다룬 6가지 개념을 실제 데이터·트랜잭션 시뮬레이션으로.
1. 정규화 (Normalization)
같은 데이터를 정규화 전/후로 비교. "전화번호 변경" 버튼을 눌러보세요.
❌ 정규화 전 (한 테이블)
| 주문ID | 고객 | 전화번호 | 상품 |
|---|
✅ 정규화 후 (분리)
customers
| id | name | phone |
|---|
orders
| 주문ID | customer_id (FK) | 상품 |
|---|
# Update Anomaly 시연
# 정규화 전: 박효인의 모든 주문 행(여러 줄)을 다 갱신해야 함
# 정규화 후: customers 테이블의 한 줄만 갱신하면 끝
2. 관계 (Relationships)
1:N / 1:1 / N:M — FK 위치와 중간 테이블의 필요성.
1:N
→
FK는 "N쪽"인 orders에
customers
id (PK)orders
customer_id (FK)
1:1
↔
FK + UNIQUE 제약
users
id (PK)profiles
user_id (FK, UNIQUE)
N:M
→
(중간 테이블)
←
posts
id (PK)post_tags
post_id, tag_id(중간 테이블)
tags
id (PK)
FK 삭제 동작 — 부모를 지울 때 자식은?
CASCADE— 부모 삭제 시 자식도 자동 삭제 (게시글 삭제 → 댓글 삭제)SET NULL— 자식 FK 컬럼을 NULL 로 (작성자 탈퇴 → 글은 유지, 작성자 표시만 비움)RESTRICT(기본) — 자식이 있으면 부모 삭제 거부. 가장 안전
3. 인덱스 (Indexes)
정렬된 16개 숫자에서 값 찾기 — 선형 스캔 vs B-tree(이진 탐색)의 단계 수 비교.
데이터 (이미 정렬됨)
찾을 값:
단계 수: -
# 16개 데이터에서 한 값 찾기
# 선형 스캔: 최악 16번 비교 (O(n))
# B-tree: 최악 4번 비교 (O(log₂ 16) = 4)
# 데이터가 100만 건이면: 100만 vs 약 20
4. 트랜잭션 (Transactions)
A → B 이체 500원. 출금 후 시스템 죽음 시뮬레이션. 트랜잭션 유무에 따라 결과가 갈린다.
A 계좌
1000원
B 계좌
500원
# 트랜잭션 없이 + 출금 직후 실패
# A: 500원 (출금 성공), B: 500원 (입금 실패) → 500원 증발!
#
# 트랜잭션으로 감싸면
# BEGIN; UPDATE A...; UPDATE B...; COMMIT;
# 실패 시 ROLLBACK → A: 1000, B: 500 (원상복귀)
5. 격리 수준 — Dirty Read 시연
두 트랜잭션이 동시에 진행될 때, 격리 수준에 따라 Tx2가 보는 값이 달라진다. "다음 단계" 버튼으로 진행하세요.
격리 수준:
시간
Tx1 (이체)
Tx2 (잔액 조회)
T1
BEGIN;
T2
UPDATE A SET balance = 500;
(아직 COMMIT 안 함)
(아직 COMMIT 안 함)
T3
SELECT balance FROM A;
→ ?
→ ?
T4
ROLLBACK;
# Read Uncommitted: Tx2가 500원을 본다 (Dirty Read)
# → 그런데 Tx1이 ROLLBACK 했으므로 그 500은 "거짓 데이터"
# Read Committed: Tx2는 커밋된 1000원을 본다 (안전)
# Postgres 기본은 Read Committed — Dirty Read 차단
6. 마이그레이션 — Expand-and-Contract
운영 중 컬럼 이름을 바꾸는 4단계. full_name 하나를 first_name + last_name 으로 분리.
단계 0 / 4
1. Expand
새 컬럼 추가
users
├ id
├ full_name
├ first_name (NEW)
└ last_name (NEW)
├ id
├ full_name
├ first_name (NEW)
└ last_name (NEW)
2. Migrate
양쪽 모두 사용하는 코드 배포
앱이 read/write를
both로 처리
(전환 기간)
both로 처리
(전환 기간)
3. Backfill
기존 데이터 채우기
UPDATE users
SET first_name = ...,
last_name = ...
SET first_name = ...,
last_name = ...
4. Contract
옛 컬럼 제거
users
├ id
├ first_name
└ last_name
├ id
├ first_name
└ last_name
나쁜 예 — 한 번에 바꾸기:
ALTER TABLE users DROP COLUMN full_name; ADD COLUMN first_name...
→ 배포 순간 옛 코드가 죽음. 다운타임 발생.
좋은 예 — Expand & Contract:
4 단계로 쪼개면 매 단계마다 옛/새 코드가 공존 가능 → 무중단 배포.