
트랜잭션(Transaction)은 데이터베이스의 무결성과 안정성을 지키기 위해 꼭 이해해야 하는 핵심 개념입니다.
오늘은 다음 내용을 단계별로 살펴봅니다
- 트랜잭션의 기본 개념
- ACID 속성이 중요한 이유
- MySQL에서 트랜잭션이 어떻게 동작하는지
- 스토리지 엔진(MyISAM vs InnoDB)에 따른 동작 차이
- 트랜잭션 격리 수준 4단계와 관련 문제(Dirty Read, Phantom Read 등)
- Spring에서
@Transactional로 격리 수준을 설정하는 방법
이 글을 읽고 나면, DB 트랜잭션을 안정적으로 설계하고 활용하기 위한 실무 기준을 자연스럽게 이해할 수 있을거에요!
트랜잭션이란 무엇인가

트랜잭션(Transaction)은 데이터베이스에서 실행되는 하나의 논리적 작업 단위(Logical Unit of Work) 입니다.
읽기, 쓰기, 수정, 삭제처럼 여러 연산을 하나로 묶어 모두 성공(All)하거나 모두 실패(None) 하도록 보장합니다.
예를 들어, 계좌 A → 계좌 B로 10,000원을 송금할 때 다음 두 작업이 함께 이루어져야 합니다.
- 계좌 A에서 10,000원 차감
- 계좌 B에 10,000원 추가
이 두 연산이 모두 성공해야 정상적인 송금이 됩니다.
하지만 트랜잭션이 없다면 다음과 같은 문제가 발생할 수 있습니다
- A에서는 돈이 빠졌지만 B에는 돈이 입금되지 않거나
- A에서는 돈이 빠지지 않았는데 B에는 돈이 생기거나
즉, 돈이 사라지거나 갑자기 생겨나는 심각한 오류가 발생할 수 있습니다.
트랜잭션의 목적: 데이터의 무결성을 지키는 것
ACID

트랜잭션을 제대로 이해하려면 ACID 네 가지 속성을 함께 살펴보는 것이 중요합니다.
1. 원자성 (Atomicity)
트랜잭션에 포함된 작업은 하나의 단위처럼 처리됩니다.
즉, 모든 작업이 성공하면 COMMIT, 하나라도 실패하면 ROLLBACK합니다.
- 예: 송금 시 출금은 성공했지만 입금이 실패한 경우
→ 전체 작업을 되돌려야 정상입니다.
2. 일관성 (Consistency)
트랜잭션이 끝난 후 데이터베이스는 정의된 모든 규칙(제약조건)을 만족해야 합니다.
- 예: 재고를 -1 감소시켰는데 주문 생성이 실패한 경우
→ 데이터가 규칙을 어기게 되며, 트랜잭션은 이를 방지합니다.
3. 격리성 (Isolation)
여러 트랜잭션이 동시에 실행되더라도 서로 간섭하지 않아야 합니다.
중간 결과가 노출되면 잘못된 데이터가 저장될 수 있습니다.
- 예: 호텔 101호를 동시에 두 사용자가 예약한 상황
→ 격리성이 충분하지 않다면 두 사람 모두 예약에 성공하는 문제가 발생할 수 있습니다.
4. 지속성 (Durability)
트랜잭션이 커밋된 이후에는 결과가 영구적으로 저장됩니다.
서버가 장애를 일으키더라도 데이터는 유지되어야 합니다.
- 예: 결제 완료 기록이 장애로 사라져서는 안 됨
- InnoDB는 WAL(redo log)을 사용해 커밋된 내용을 안전하게 보존합니다.
MySQL에서의 트랜잭션
MySQL에서 트랜잭션은 보통 다음 순서로 실행합니다.
START TRANSACTION; -- 또는 BEGIN
-- 작업 수행
UPDATE accounts SET balance = balance - 100 WHERE id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE id = 'B';
COMMIT; -- 모든 작업이 정상적으로 끝났을 때
-- 또는
ROLLBACK; -- 중간에 오류가 발생했을 때 전체 되돌림
트랜잭션은 필요한 최소 범위에서만 사용해야 합니다.
범위가 넓어질수록 락이 오래 유지되어 동시성 저하와 성능 문제가 발생할 수 있기 때문입니다.
MySQL 스토리지 엔진 비교
스토리지 엔진에 따라 트랜잭션 지원 여부와 동작 방식이 달라집니다. 대표적으로 MyISAM과 InnoDB가 자주 비교됩니다.
| 기능 | MyISAM | InnoDB |
| 트랜잭션 지원 | X | O |
| 락 방식 | 테이블 락 | 행(Row) 락 |
| 외래 키 지원 | X | O |
| 동시성 처리 | 낮음 | 높음 |
| 충돌 후 복구 | 불가 | 자동 복구 |
- MyISAM → 읽기 속도가 빠르지만 트랜잭션과 무결성 검증이 필요 없는 경우에만 적합
- InnoDB → 트랜잭션, 외래 키, 자동 복구 등 대부분의 서비스에서 요구하는 기능을 제공
왜 차이가 중요할까?
ROLLBACK 동작만 봐도 두 엔진의 차이가 명확합니다.
- MyISAM: ROLLBACK을 실행해도 데이터가 그대로 남습니다.
- InnoDB: ROLLBACK 시 변경 사항이 정상적으로 되돌아갑니다.
즉, 실무에서 트랜잭션 안정성이 필요하다면 선택지는 사실상 InnoDB입니다.
트랜잭션의 격리 수준 (4단계)
트랜잭션 격리 수준(Isolation Level)은 내 트랜잭션이 다른 트랜잭션의 변경 데이터를 어디까지 읽을 수 있는가?를 정의합니다.
| 격리 수준 | Dirty Read | Non-repeatable Read | Phantom Read |
| READ UNCOMMITTED | 발생 | 발생 | 발생 |
| READ COMMITTED | 해결 | 발생 | 발생 |
| REPEATABLE READ | 해결 | 해결 | (InnoDB는 대부분 해결) |
| SERIALIZABLE | 해결 | 해결 | 해결 |
READ UNCOMMITTED — 커밋되지 않은 데이터도 읽는다
가장 낮은 격리 수준으로, 실무에서는 거의 사용하지 않습니다.
커밋되지 않은 데이터를 읽을 수 있어 Dirty Read가 발생합니다.
예시
- A 계좌 잔액을 1000 → 900으로 수정 중(아직 커밋 X)
- 다른 트랜잭션이 이 값을 읽어 900으로 확인
- 이후 원래 트랜잭션이 ROLLBACK 하면
→ 읽었던 900은 존재하지 않는 잘못된 값이 됩니다.
READ COMMITTED — 커밋된 데이터만 읽는다
가장 널리 사용되는 격리 수준입니다.
Dirty Read는 막지만 Non-repeatable Read는 발생할 수 있습니다.
예시
- A 잔액 조회 → 1000
- 다른 트랜잭션이 900으로 수정 후 커밋
- 같은 트랜잭션에서 다시 조회 → 900
즉, 같은 트랜잭션 안에서도 조회 결과가 달라질 수 있습니다.
REPEATABLE READ — 같은 트랜잭션에서는 항상 같은 결과를 읽는다
MySQL InnoDB의 기본 격리 수준입니다.
같은 트랜잭션 안에서는 조회 결과가 일정하게 유지되어 Non-repeatable Read를 방지합니다.
SQL 표준에서는 여전히 Phantom Read가 발생하지만,
InnoDB는 갭 락(Gap Lock)과 넥스트 키 락(Next-Key Lock)을 사용해 대부분의 경우 이를 방지합니다.
SERIALIZABLE — 모든 트랜잭션을 순차 실행한다고 가정
가장 강력한 격리 수준으로, Dirty Read·Non-repeatable Read·Phantom Read 모두 방지합니다.
하지만 동시에 실행되는 트랜잭션이 크게 제한되어 성능이 급격히 떨어집니다.
→ 실무에서는 거의 사용되지 않습니다.
스프링에서 트랜잭션 격리 수준 설정
Spring에서는 @Transactional 애노테이션을 사용해 트랜잭션의 격리 수준(Isolation Level)을 직접 지정할 수 있습니다.
@Service
public class AccountService {
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferMoney(Account from, Account to, double amount) {
// 비즈니스 로직
}
}
지원되는 격리 수준 옵션
Isolation.DEFAULTIsolation.READ_UNCOMMITTEDIsolation.READ_COMMITTEDIsolation.REPEATABLE_READIsolation.SERIALIZABLE
Spring은 기본적으로 데이터베이스가 설정한 기본 격리 수준을 그대로 따릅니다.
예를 들어 MySQL(InnoDB)의 경우 기본값은 REPEATABLE_READ입니다.
결론: 어떤 격리 수준을 사용해야 할까?
트랜잭션 격리 수준은 서비스의 요구사항에 따라 선택해야 합니다. 아래는 상황별 추천 수준입니다.
| 요구사항 | 추천 격리 수준 |
| 일반적인 웹 서비스 | READ COMMITTED |
| 금융 서비스 / 데이터 정합성이 매우 중요한 경우 | REPEATABLE READ |
| 데이터 충돌을 절대 허용할 수 없는 경우 | SERIALIZABLE |
| 성능이 최우선이고 일관성이 크게 중요하지 않은 경우 | READ UNCOMMITTED |
MySQL(InnoDB)의 기본값인 REPEATABLE READ는
대부분의 일반적인 애플리케이션에서 충분히 안전하면서 효율적인 선택입니다.
트랜잭션은 데이터베이스의 무결성과 신뢰성을 지키는 핵심 도구입니다.
ACID 속성과 격리 수준을 제대로 이해하면, 실무에서 자주 발생하는 대부분의 데이터 오류를 예방할 수 있습니다.
또한 MySQL과 Spring에서 트랜잭션이 어떻게 동작하는지 알면, 서비스 요구사항에 맞춰 안정적인 데이터 처리 전략을 세울 수 있을거에요!
'Computer Science > Data 📊' 카테고리의 다른 글
| [MySQL] 문자열 데이터를 저장하는 방법 (0) | 2025.03.24 |
|---|---|
| 데이터베이스 vs 데이터 웨어하우스 vs 데이터 레이크 (0) | 2024.03.08 |
| Docker 환경에서 PostgreSQL Master–Slave Replication 구축하기 (0) | 2024.02.07 |
안녕하세요, 저는 주니어 개발자 박석희 입니다. 언제든 하단 연락처로 연락주세요 😆