이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › Spring › 데이터 접근과 트랜잭션
트랜잭션 기본
출금은 됐는데 입금이 안 됐다면
A 계좌에서 1,000원을 빼고 B 계좌에 1,000원을 더하는 이체는 SQL 두 문장입니다. 두 문장 사이에서 오류가 나거나 서버가 꺼지면 돈이 사라질 수 있습니다. 여러 문장을 하나로 묶어 전부 반영하거나 전부 취소하게 하는 것이 트랜잭션입니다. 두 문장을 묶지 않았을 때와 묶었을 때의 결과를 비교합니다.
실행으로 대조 · SQLite에서 연결 두 개로 열두 가지 조합 실행
이 실습에 나오는 개념
- 트랜잭션
- 여러 데이터 변경 작업을 하나의 단위로 묶어, 전부 반영되거나(커밋) 전부 취소되게(롤백) 하는 것이다. 계좌 이체에서 출금과 입금 중 하나만 반영되는 중간 상태를 막는다. 데이터베이스가 정합성을 지키는 가장 기본적인 수단이다.
- ACID
- 트랜잭션이 보장해야 하는 네 가지 성질이다. 원자성(Atomicity)은 전부 성공하거나 전부 실패하는 것, 일관성(Consistency)은 트랜잭션 전후로 데이터가 정해진 규칙을 만족하는 것, 격리성(Isolation)은 동시에 실행되는 트랜잭션이 서로 간섭하지 않는 것, 지속성(Durability)은 커밋된 결과가 장애가 나도 유지되는 것이다.
- Undo 로그와 Redo 로그
- Undo 로그는 변경 전의 데이터를 기록한 것으로, 트랜잭션을 롤백할 때 쓰이며, InnoDB에서는 다른 트랜잭션에 과거 버전을 보여 줄 때(MVCC)도 쓰인다. Redo 로그는 변경 내용을 기록한 것으로, 커밋된 변경이 데이터 파일에 완전히 쓰이기 전에 장애가 나더라도 재시작 시 이를 다시 적용해 복구하는 데 쓰인다.
- @Transactional
- 메서드나 클래스에 붙여 그 범위를 하나의 트랜잭션으로 실행하게 하는 애너테이션이다. 프록시가 메서드 실행 전에 트랜잭션을 시작하고, 정상적으로 끝나면 커밋, 예외가 발생하면 규칙에 따라 롤백한다. 트랜잭션을 시작하고 끝내는 코드를 직접 쓰지 않아도 되어 선언적 트랜잭션이라고 한다.
이렇게 바꿔 보세요
- 처음 설정으로 — 트랜잭션 없이 중간에 오류. 1,000원이 사라집니다.
- 트랜잭션으로 묶으면 — 같은 오류에서 출금까지 취소됩니다.
- 묶은 상태에서 서버가 꺼지면 — 커밋하지 않은 변경은 남지 않습니다.
- 문제가 없을 때 — 어느 방식이든 결과는 같습니다.
- 중간에 다른 사람이 조회하면 — 트랜잭션 없이: 합계가 1,000원 모자란 순간이 보입니다.
- 묶은 상태에서 중간 조회 — 커밋 전의 변경은 다른 사람에게 보이지 않습니다.
처음 설정으로 실행하면
- A 계좌 5,000원, B 계좌 1,000원. 합계는 6,000원입니다. A에서 B로 1,000원을 이체합니다.
- 출금: A를 4,000원으로 바꿉니다. 여러 문장을 묶는 트랜잭션이 없으므로, 이 문장 하나가 끝나자마자 확정(커밋)됩니다. 되돌릴 수 없습니다.
- 애플리케이션에서 오류가 발생해 입금 문장을 실행하지 못했습니다. 출금은 이미 확정돼 있습니다. A에서는 1,000원이 빠졌는데 B에는 들어가지 않았습니다. 1,000원이 사라졌습니다.
실행 결과
- 여러 문장이 "전부 되거나 전부 안 되거나"여야 하는데 일부만 반영됐습니다. 트랜잭션은 이것을 보장하는 장치입니다(원자성).
- 트랜잭션의 네 가지 성질(ACID): 전부 아니면 전무(원자성), 규칙에 맞는 상태 유지(일관성), 진행 중인 변경은 서로 안 보임(격리성), 커밋된 것은 남음(지속성). 스프링의 @Transactional은 메서드 시작에 START TRANSACTION, 정상 종료에 COMMIT, 런타임 예외에 ROLLBACK(체크 예외는 기본 설정에서 COMMIT)을 대신 보내 주는 것입니다.
코드
-- 자동 커밋: 문장 하나가 끝날 때마다 바로 확정된다
UPDATE account SET balance = balance - 1000 WHERE id = 'A'; -- 출금
-- (여기서 애플리케이션 오류 발생)
-- UPDATE account SET balance = balance + 1000 WHERE id = 'B'; -- 입금: 실행되지 못함
이 모형의 전제
열두 가지 조합의 최종 잔액과 중간 조회 결과를 실제 SQL 엔진(SQLite)에서 연결 두 개로 실행해 확인했습니다. "서버가 꺼짐"은 커밋하지 않은 채 연결을 끊는 것으로 재현했습니다. 중간 조회에서 커밋 전 값이 보이지 않는 것은 대부분의 DB 기본 설정에서의 동작이며, 가장 낮은 격리 수준에서는 보일 수 있습니다.
앞 실습: AOP: 어드바이스와 포인트컷 · 다음 실습: 트랜잭션 전파