F-LAB JAVA · 6주차 · Phase 6 · 트랜잭션과 ACID
★ 깊이 파기 — ACID 통합 시나리오, Phase 6 완주
이 Unit을 끝내면 다음을 답할 수 있어야 한다.
세션1 이 데이터를 변경하고 commit 하지 않으면 세션2 는 변경 전 데이터를 보고 세션1 만 자기 변경을 볼 수 있는데, 이는 각 세션이 자신만의 트랜잭션 컨텍스트에서 변경을 임시 보관하다가 commit 시점에 공유 영역으로 옮겨지는 구조이며, Redis 같은 공유 캐시가 변경 즉시 모두에게 보이는 것과 정반대다.
시나리오는 — 세션1 이UPDATE후 commit 하지 않은 상태에서 세션2 가 같은 데이터를 조회하면, 세션2 는 변경 전 데이터를 본다 (격리성).
세션1 의 변경은 자기 세션에서만 보이는데 (자기 노트북의 임시 메모), 이는 DB 가 각 세션마다 자신의 변경만 보이는 뷰 를 제공하기 때문이다 (MVCC, undo log 활용).
세션1 이commit하는 순간 변경이 공유 영역에 반영되고, 그제서야 세션2 도 새 데이터를 볼 수 있다.
이 격리 동작은 격리 수준에 따라 달라지며 (READ UNCOMMITTED 만 미커밋 데이터 보임), Redis 같은 공유 캐시는 SET 즉시 모두에게 보여 격리가 없는 정반대 구조라 트랜잭션 보호가 필요한 데이터는 DB 에 두고 캐시는 일시 데이터만 둔다.
Commit 이전 동시성 = 노트북/클라우드:
세션 = 각자의 노트북:
- 세션1, 세션2 각자 노트북 (트랜잭션 컨텍스트)
- 자기 메모는 자기만 봄
변경 = 노트북에 임시 기록:
- 세션1: 자기 노트북에 "잔액 5천"
- 세션2: 자기 노트북 (다른 메모)
- 서로 안 보임
Commit = 공유 클라우드 업로드:
- 세션1 commit → 클라우드에 업로드
- 세션2 도 클라우드 보면 새 값
격리 (Isolation, 6.4):
- 세션2: commit 전엔 옛 클라우드 값
- 변경 전 데이터 봄
격리 수준 따라:
- READ UNCOMMITTED: 다른 노트북 훔쳐봄 (위험)
- READ COMMITTED 이상: 클라우드만
Redis (공유 캐시):
- 노트북 X, 그냥 클라우드 하나
- SET → 즉시 모두에게 보임
- 격리 X (DB 와 정반대)
→ DB: 격리 (트랜잭션 보호)
→ Redis: 즉시 공유 (캐시)
→ Commit 전 = 자기 세션만, Commit 후 = 모두 봄, Redis 는 격리 X.
1. 시나리오 — 세션1 변경 (미커밋)
2. 세션2 의 조회 결과
3. 자기 세션만 변경 보임
4. Commit 시점의 의미
5. 격리 수준에 따른 차이
6. 노트북 / 클라우드 비유
7. Redis 와 격리 차이
8. MVCC 의 동작 (내부)
9. Phase 6 완주 정리
시나리오:
세션1:
BEGIN;
UPDATE accounts SET balance = 5000 WHERE id = 'A';
-- commit 안 함! (트랜잭션 진행 중)
세션2: 같은 데이터 조회
SELECT balance FROM accounts WHERE id = 'A';
-- 무엇이 보일까?
두 가지 가능성:
A) 세션1 의 미커밋 값 (5000)
→ READ UNCOMMITTED (위험)
B) 변경 전 값 (10000, 가정)
→ READ COMMITTED 이상 (안전)
격리의 본질:
Default (대부분 DB):
- B 안전한 선택
- 변경 전 값
- Dirty Read 차단
트랜잭션 진행 중:
세션1 의 변경:
- 자기 트랜잭션에서만 보임
- DB 의 임시 영역
- commit 안 하면 외부 X
→ 격리 보장
-- ILIC 시나리오 (운영자 동시 작업)
-- 운영자 A (세션 1)
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
-- 작업 중 (commit 안 함)
-- 운영자 B (세션 2, 같은 시간)
SELECT status FROM shipments WHERE id = 1;
-- 무엇이 보이나?
-- → 'BOOKED' (이전 값, 운영자 A 의 변경 X)
-- → 격리 (READ COMMITTED 이상)
세션1 변경 (미커밋) + 세션2 조회 시 동작은?
답:
1. 시나리오:
두 가능성:
기본:
진행 중:
세션2 결과:
READ COMMITTED 이상:
- 변경 전 데이터
- 세션1 commit 안 했으니
- 마지막 commit 된 값
일관성 보장:
세션2 가 보는 건:
- "확정된" 데이터
- 신뢰 가능
- 비즈니스 결정 OK
→ Dirty Read 방지
시각화:
세션1 세션2
BEGIN
UPDATE 5000 (자기만)
SELECT → 10000 (변경 전)
(commit 전)
SELECT → 10000 (여전히)
COMMIT
SELECT → 5000 (이제 새 값)
→ 세션2 는 세션1 commit 까지 옛 값
세션1 rollback 시:
세션1: ROLLBACK
→ 미커밋 변경 모두 사라짐
→ 세션2 는 그냥 옛 값 (영향 X)
→ rollback 도 안전
-- 세션2 결과 (ILIC)
-- T 시각 0: shipments.id=1.status='BOOKED' (현재)
-- 세션1 (T=1)
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
-- 세션1 만 봄
-- 세션2 (T=2, 동시)
SELECT status FROM shipments WHERE id = 1;
-- → 'BOOKED' (변경 전)
-- 세션1 의 미커밋 변경 안 보임
-- 세션1 (T=3)
COMMIT;
-- 변경 확정
-- 세션2 (T=4, 새 트랜잭션)
SELECT status FROM shipments WHERE id = 1;
-- → 'SHIPPED' (이제 새 값)
-- → 운영자 B 는 commit 전엔 옛 상태로 작업 (안전)
세션2 가 변경 전 데이터를 보는 이유는?
답:
1. 결과:
이유:
일관성:
rollback 시:
자기 세션 변경:
세션1 의 UPDATE:
- 세션1 의 후속 SELECT → 새 값
- 자기만의 변경 확인 가능
→ 자기 작업 OK
-- 세션1
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
SELECT status FROM shipments WHERE id = 1;
-- → 'SHIPPED' (자기 변경 보임)
-- 세션1 의 트랜잭션 안에서
이유:
자기 트랜잭션 컨텍스트:
- 자기 변경 + 다른 commit 데이터
- 일관성 있게 보임
→ 작업 가능
임시 영역:
세션1 의 변경 위치:
- DB 의 자기 트랜잭션 영역
- undo log + 메모리
- 자기만 접근
→ 다른 세션 X
-- 자기 세션만 (ILIC)
-- 세션1 (운영자 A)
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
INSERT INTO shipment_history (shipment_id, status, changed_at)
VALUES (1, 'SHIPPED', NOW());
-- 자기 작업 확인 (자기 세션만)
SELECT * FROM shipments WHERE id = 1;
-- → 'SHIPPED' (자기 변경 보임)
SELECT * FROM shipment_history WHERE shipment_id = 1;
-- → 새 INSERT 보임 (자기)
-- 세션2 (운영자 B, 동시)
SELECT * FROM shipments WHERE id = 1;
-- → 'BOOKED' (세션1 변경 안 보임)
SELECT * FROM shipment_history WHERE shipment_id = 1;
-- → 새 INSERT 안 보임 (세션1 의 것)
-- 세션1
COMMIT;
-- 변경 확정 → 세션2 도 봄
세션1 변경이 자기 세션에서만 보이는 의미는?
답:
1. 자기 변경:
이유:
임시 영역:
다른 세션:
Commit 시점:
세션1 commit:
- 자기 변경을 공유 영역으로
- 모든 세션이 봄
- 영구 (Durability, 6.5)
→ "공개" 의 순간
Atomic 한 전환:
commit:
- 모든 변경 동시에 공개
- 부분 노출 X
- 한 순간
→ 일관성
다른 세션의 시점:
세션1 commit 전:
- 세션2: 옛 데이터
세션1 commit 후:
- 세션2 의 다음 SELECT (새 트랜잭션)
- 또는 같은 트랜잭션의 다음 SELECT (격리 수준 따라)
- → 새 데이터
REPEATABLE READ:
세션2 가 트랜잭션 시작 후:
- 세션1 의 commit 도 안 보일 수
- 같은 행 반복 조회 일관
새 트랜잭션 시작 시:
- 그제서야 새 데이터
-- Commit 시점 (ILIC)
-- 시나리오: 운영자 A 가 배송 상태 변경
-- T0: 시작 상태
-- shipments.id=1.status = 'BOOKED'
-- 세션1 (운영자 A)
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
UPDATE shipment_freight SET status = 'PAID' WHERE shipment_id = 1;
-- 두 변경 모두 자기만 봄
-- 세션2 (운영자 B, 새 트랜잭션)
SELECT status FROM shipments WHERE id = 1; -- 'BOOKED'
-- 세션1: COMMIT
-- 두 변경 동시에 공유 영역 (Atomic)
-- 세션2 (새 SELECT)
SELECT status FROM shipments WHERE id = 1; -- 'SHIPPED'
SELECT status FROM shipment_freight WHERE shipment_id = 1; -- 'PAID'
-- → 두 변경 모두 보임 (commit Atomic)
-- "절반만 보이는" 상태 절대 X
Commit 후 다른 세션에서 보이는 시점은?
답:
1. commit:
Atomic:
다른 세션:
REPEATABLE READ:
격리 수준별:
READ UNCOMMITTED:
- 세션1 의 미커밋 변경 보임 (Dirty Read)
- 위험
READ COMMITTED:
- 변경 전 (commit 된 것만)
- 안전
REPEATABLE READ (MySQL 기본):
- 트랜잭션 시작 시점 기준
- 트랜잭션 안에서 일관
SERIALIZABLE:
- 가장 엄격
- 락 많음
-- READ COMMITTED
-- 세션2 의 트랜잭션 안에서
BEGIN;
SELECT status FROM shipments WHERE id = 1; -- 'BOOKED'
-- 사이에 세션1 COMMIT (SHIPPED 로 변경)
SELECT status FROM shipments WHERE id = 1; -- 'SHIPPED'
-- → 같은 트랜잭션 안에서 값 변화 (Non-repeatable Read)
COMMIT;
-- REPEATABLE READ (MySQL 기본)
-- 세션2 의 트랜잭션 안에서
BEGIN;
SELECT status FROM shipments WHERE id = 1; -- 'BOOKED'
-- 사이에 세션1 COMMIT (SHIPPED 로 변경)
SELECT status FROM shipments WHERE id = 1; -- 'BOOKED' (여전히)
-- → 트랜잭션 시작 시점 스냅샷
COMMIT;
-- 새 트랜잭션
SELECT status FROM shipments WHERE id = 1; -- 'SHIPPED' (이제)
-- SERIALIZABLE
-- 가장 엄격, 락 강화
-- 세션1 변경 중인 행을 세션2 SELECT 시:
-- → 대기 (락 풀릴 때까지)
-- ILIC 격리 수준별 (MySQL InnoDB REPEATABLE READ 기본)
-- 운영자 A (세션1)
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
-- 운영자 B (세션2, REPEATABLE READ)
BEGIN;
SELECT status FROM shipments WHERE id = 1; -- 'BOOKED' (격리)
-- 운영자 A
COMMIT;
-- 운영자 B (같은 트랜잭션)
SELECT status FROM shipments WHERE id = 1; -- 'BOOKED' (여전히, RR)
-- 같은 트랜잭션 안에서 일관
-- 운영자 B 새 트랜잭션
COMMIT;
BEGIN;
SELECT status FROM shipments WHERE id = 1; -- 'SHIPPED' (이제)
COMMIT;
격리 수준에 따라 동작 차이는?
답:
1. 수준별:
RC:
RR:
SER:
비유 매핑:
세션 = 자기 노트북:
- 자기 트랜잭션 컨텍스트
- 자기만 봄
변경 = 노트북에 임시 메모:
- UPDATE/INSERT
- 자기만 알아
Commit = 클라우드에 업로드:
- 모두 봄
- 영구
격리 = 노트북끼리 안 봄:
- Isolation 보장
일상 예:
Google Docs:
- 자기만 편집 (트랜잭션)
- 저장 (commit) → 모두 봄
단, Google Docs 는 동시 편집 (CRDT)
DB 는 트랜잭션 격리 (락/MVCC)
비유의 한계:
실제 DB:
- 노트북끼리 데이터 공유 (조회)
- 변경만 격리
→ 비유는 변경/공개 측면
격리 수준 비유:
READ UNCOMMITTED:
- "다른 노트북 훔쳐봄" 가능
READ COMMITTED:
- 클라우드 (commit) 만 봄
REPEATABLE READ:
- 처음 본 클라우드 버전 고정
SERIALIZABLE:
- 한 명만 작업
비유 (ILIC 운영자)
ILIC 운영자 환경:
- 운영자 5명 (세션 5개)
- 각자 자기 트랜잭션 (노트북)
- 변경은 commit 까지 자기만
- commit → 공유 (클라우드)
운영자 A: shipments 변경 중 (commit X)
운영자 B-E: 옛 상태 봄 (격리)
운영자 A 가 commit:
- 운영자 B-E 새 트랜잭션:
- 새 상태 봄
→ 마치 각자 노트북에 작업 + 클라우드 sync
→ 동시 작업 가능 + 일관성
노트북 / 클라우드 비유는?
답:
1. 노트북:
변경:
commit:
격리:
Redis 동작:
SET key value:
- 즉시 반영
- 모든 클라이언트 즉시 봄
- 격리 X
→ 단일 공유 메모리
DB vs Redis:
DB:
- 격리 (commit 까지 안 보임)
- 트랜잭션 보호
- 일관성
Redis:
- 즉시 공유
- 격리 X
- 빠름
시나리오 (Redis):
클라이언트 A: SET balance:1 5000
- 즉시 적용
클라이언트 B (동시):
GET balance:1
→ 5000 (즉시 보임!)
→ 격리 X
Redis 용도:
- 캐시
- 세션 저장
- 카운터
- 일시 데이터
→ 트랜잭션 보호 불필요한 것
Redis 트랜잭션?:
MULTI/EXEC:
- 명령어 묶음
- Atomic 실행
- 하지만 격리는 약함
→ DB 의 ACID 와 다름
적절한 분담:
DB:
- 중요 거래 (결제, 재고)
- 트랜잭션 보호 필요
- ACID
Redis:
- 캐시
- 세션
- 즉시 공유 OK
ILIC 의 DB + Redis (분담)
ILIC:
- MySQL (트랜잭션 보호)
- shipments, bookings, freights
- ACID, 격리
- Redis (즉시 공유)
- 세션 (로그인)
- 캐시 (조회 결과)
- 카운터 (방문, 알림)
비교:
- MySQL: 결제 시 격리 (commit 까지 안 보임)
- Redis: 카운터 즉시 공유 (incr 즉시 모두)
→ 트랜잭션 필요는 DB
→ 즉시 공유는 Redis
공유 캐시 (Redis) 와 DB 격리 차이는?
답:
1. DB:
Redis:
분담:
트랜잭션:
MVCC:
Multi-Version Concurrency Control:
- 여러 버전 유지
- 읽기 락 X
- 일관 스냅샷
MVCC 동작:
UPDATE 시:
- 기존 행 + 새 버전
- undo log 에 이전 버전
SELECT 시:
- 트랜잭션의 "스냅샷" 시점 버전
- 자기 트랜잭션 시작 이후 변경 X
→ 읽기 락 없이 일관
자기 변경:
자기 트랜잭션 변경:
- 자기 트랜잭션 ID 표시
- 자기는 봄
- 다른 세션 X
구현체:
- InnoDB (MySQL): undo log + read view
- PostgreSQL: 다중 버전 (vacuum)
- Oracle: undo segments
→ MVCC 가 표준
-- MVCC 동작 (ILIC MySQL InnoDB)
-- 세션1
BEGIN;
UPDATE shipments SET status = 'SHIPPED' WHERE id = 1;
-- InnoDB 내부:
-- - 이전 행 (BOOKED) → undo log
-- - 현재 행 (SHIPPED) + 세션1 트랜잭션 ID
-- 세션2
BEGIN;
SELECT status FROM shipments WHERE id = 1;
-- 세션2 의 read view:
-- - 세션1 의 변경 무시 (다른 트랜잭션 ID)
-- - undo log 보고 이전 버전 (BOOKED) 읽음
-- → 'BOOKED'
-- 세션1: COMMIT
-- 세션2: 같은 트랜잭션이면 여전히 'BOOKED' (REPEATABLE READ)
-- 세션2: 새 트랜잭션이면 'SHIPPED'
-- → 락 없이 일관성
MVCC 의 동작 (내부) 은?
답:
1. MVCC:
동작:
읽기 락 X:
구현:
Phase 6 — 트랜잭션과 ACID
Unit 6.1 — 트랜잭션이란
- 한 단위 작업
- Commit / Rollback
Unit 6.2 — Atomicity ★깊이
- 전부 or 전무
Unit 6.3 — Consistency ★깊이
- 제약조건 항상
Unit 6.4 — Isolation ★깊이
- 격리 수준 4단계
Unit 6.5 — Durability ★깊이
- WAL + fsync
Unit 6.6 — Commit 이전 동시성 ★깊이
- 세션 격리, MVCC
ACID:
A - Atomicity (원자성)
- undo log, 전부 or 전무
C - Consistency (일관성)
- 제약조건, DB + 앱
I - Isolation (격리성)
- MVCC, 격리 수준
D - Durability (지속성)
- WAL, fsync, redo log
Phase 6 핵심 메시지:
"트랜잭션 ACID 는 동시 사용자 환경에서
데이터의 안전성과 일관성을 보장한다.
격리는 commit 까지 변경을 숨기고,
commit 후엔 영구 보존된다."
5주차 ↔ 6주차 (동시성):
5주차 동시성:
- 자바 멀티스레드
- synchronized, Atomic
- 메모리
6주차 동시성:
- DB 멀티세션
- 트랜잭션 ACID
- 영구 저장소
→ 같은 동시성, 다른 계층
Phase 6 → Phase 7:
- ACID 트랜잭션 → 반복 제거
Phase 7 — JdbcTemplate:
Unit 7.1 — JDBC 만 쓸 때 반복 코드
Unit 7.2 — JdbcTemplate 등장 ★깊이
Unit 7.3 — update/queryForObject/query
Unit 7.4 — RowMapper
Unit 7.5 — JdbcTemplate 구조적 의미
| Q | 핵심 답변 |
|---|---|
| 미커밋 + 조회? | 변경 전 |
| 자기 세션만? | 자기 컨텍스트 |
| commit 시점? | 공유 영역 |
| 격리 수준? | 동작 차이 |
| 노트북 비유? | 트랜잭션/공유 |
| Redis 차이? | 즉시 공유 |
| MVCC? | 다중 버전 |
| Phase 6 종합? | ACID |
| 5주차 연결? | 동시성 |
| 다음? | JdbcTemplate |
답:
답:
답:
답:
답:
1. Commit 이전 동시성
2. Commit 의 의미
3. DB vs Redis
💎 Phase 6 — 트랜잭션과 ACID
✅ Unit 6.1 트랜잭션이란
✅ Unit 6.2 Atomicity ★깊이
✅ Unit 6.3 Consistency ★깊이
✅ Unit 6.4 Isolation ★깊이
✅ Unit 6.5 Durability ★깊이
✅ Unit 6.6 Commit 이전 동시성 ★깊이 ← 여기, Phase 6 완주
→ ACID 4성질
→ 멀티 세션 격리
→ MVCC 동작
→ 6주차의 정점
💾 Phase 7 — JdbcTemplate
Unit 7.1 — JDBC 만 쓸 때 반복 코드
Unit 7.2 — JdbcTemplate 등장 ★깊이
Unit 7.3 — update/queryForObject/query
Unit 7.4 — RowMapper
Unit 7.5 — JdbcTemplate 구조적 의미
🧪 Part A (9 Unit) ✅
💾 Part B — DB 접근의 진화
✅ Phase 3 — JDBC (3)
✅ Phase 4 — Connection Pool (4)
✅ Phase 5 — DataSource (4)
✅ Phase 6 — ACID (6) ← 완주!
⏭ Phase 7 — JdbcTemplate (5, 7.2 ★깊이)
총: 26/28 Unit (93%!)
🏆 Phase 6 완주 — 트랜잭션과 ACID