트랜잭션 (Transaction)과 ACID

한눈에 보기

트랜잭션은 전부 반영되거나 전부 취소되어야 하는 논리적 작업 단위다. 원자성, 일관성, 격리성, 지속성의 ACID 성질과 언두, 리두 로그 기반 구현, 실무 트랜잭션 관리 원칙을 다룬다.

최종 업데이트 2026.09.17

트랜잭션(Transaction)은 데이터베이스에서 하나의 논리적 작업 단위를 이루는 연산들의 묶음으로, 전부 성공해서 반영되거나 전부 취소되어야 한다. 트랜잭션이 지켜야 할 네 가지 성질인 원자성, 일관성, 격리성, 지속성을 머리글자를 따서 ACID라고 부르며, 계좌 이체처럼 중간 상태가 노출되면 안 되는 작업의 정합성을 보장하는 핵심 개념이다.

트랜잭션이 필요한 이유

A 계좌에서 B 계좌로 1만 원을 이체하는 작업은 "A 잔액 차감"과 "B 잔액 증가"라는 두 개의 UPDATE로 이루어진다. 첫 번째만 성공한 상태에서 서버가 죽으면 1만 원이 증발한다. 두 연산을 하나의 트랜잭션으로 묶으면 데이터베이스가 둘 다 반영하거나 둘 다 되돌리는 것을 보장하므로 이런 중간 상태가 데이터에 남지 않는다.

START TRANSACTION;

UPDATE account SET balance = balance - 10000 WHERE id = 'A';
UPDATE account SET balance = balance + 10000 WHERE id = 'B';

COMMIT;   -- 두 변경을 함께 확정
-- 문제가 생겼다면 ROLLBACK; 으로 함께 취소

커밋(Commit)은 트랜잭션의 변경 사항을 확정하는 연산이고, 롤백(Rollback)은 트랜잭션 시작 시점 상태로 되돌리는 연산이다. 트랜잭션 중간에 세이브포인트(Savepoint)를 만들어 그 지점까지만 부분적으로 되돌릴 수도 있다.

ACID 네 가지 성질

원자성(Atomicity)

트랜잭션 안의 연산들은 모두 반영되거나 모두 반영되지 않아야 한다. 데이터베이스는 변경 전 값을 언두 로그(Undo Log)에 기록해 두었다가, 롤백이나 장애 복구 시 이를 이용해 미완성 트랜잭션의 흔적을 지운다.

일관성(Consistency)

트랜잭션이 끝난 뒤에도 데이터베이스는 정해진 무결성 제약과 업무 규칙을 만족하는 일관된 상태여야 한다. 잔액이 음수가 될 수 없다는 제약이 있다면, 이를 위반하는 트랜잭션은 실패해야 한다. 일관성은 데이터베이스의 제약 조건과 애플리케이션 로직이 함께 지켜 내는 성질이다.

격리성(Isolation)

동시에 실행되는 트랜잭션들은 서로의 중간 상태를 보지 못하게 격리되어야 한다. 이상적으로는 모든 트랜잭션을 순차 실행한 것과 같은 결과여야 하지만, 완전한 격리는 동시성을 크게 떨어뜨린다. 그래서 데이터베이스는 격리 수준(Isolation Level)이라는 단계적 옵션을 제공해 정합성과 성능 사이에서 선택하게 하며, 내부적으로는 락(Lock)과 다중 버전 동시성 제어(MVCC, Multi-Version Concurrency Control)로 구현한다.

지속성(Durability)

커밋이 완료된 트랜잭션의 결과는 정전이나 시스템 장애가 나도 사라지지 않아야 한다. 데이터베이스는 변경 내용을 먼저 리두 로그(Redo Log)에 순차 기록한 뒤 커밋을 완료 처리하는 로그 선행 기입(WAL, Write-Ahead Logging) 방식으로 이를 보장하고, 장애 후 재시작 시 로그를 재적용해 커밋된 변경을 복원한다.

동시성 문제와 격리 수준의 관계

격리성이 완벽하지 않으면 커밋되지 않은 데이터를 읽는 더티 리드(Dirty Read), 같은 행을 두 번 읽었는데 값이 달라지는 반복 불가능 읽기(Non-Repeatable Read), 같은 조건으로 조회했는데 행 수가 달라지는 팬텀 리드(Phantom Read) 같은 현상이 발생할 수 있다. SQL 표준은 이를 어느 수준까지 허용할지에 따라 READ UNCOMMITTED부터 SERIALIZABLE까지 네 단계의 격리 수준을 정의한다. 또한 두 트랜잭션이 서로가 잡은 락을 기다리며 멈추는 교착 상태(Deadlock)도 동시성 제어에서 다뤄야 할 문제로, 데이터베이스는 이를 감지해 한쪽을 강제 롤백한다.

실무에서의 트랜잭션 관리

스프링(Spring) 같은 프레임워크에서는 @Transactional 어노테이션으로 메서드 단위의 트랜잭션 경계를 선언한다. 이때 트랜잭션은 커넥션을 점유한 채 유지되므로, 트랜잭션 안에서 외부 API 호출처럼 오래 걸리는 작업을 하면 커넥션 풀이 고갈될 수 있다. 트랜잭션은 가능한 한 짧게 유지하고, 데이터베이스 작업이 아닌 로직은 경계 밖으로 빼는 것이 원칙이다.

또한 하나의 데이터베이스를 넘어 여러 서비스에 걸친 작업에서는 단일 트랜잭션으로 묶을 수 없다. 이 경우 2단계 커밋(Two-Phase Commit) 같은 분산 트랜잭션 기법이나, 각 단계를 로컬 트랜잭션으로 처리하고 실패 시 보상 트랜잭션으로 되돌리는 사가(Saga) 패턴을 사용한다. 마이크로서비스 환경에서는 강한 정합성 대신 최종 일관성을 받아들이는 설계가 흔하다.

면접에서 이렇게 물어본다

  • 트랜잭션의 ACID 성질을 각각 예시와 함께 설명해 보라.
  • 원자성과 지속성을 데이터베이스가 어떻게 구현하는지 언두 로그와 리두 로그로 설명할 수 있는가.
  • 커밋과 롤백은 언제 일어나며, 롤백 시 데이터는 어떻게 복원되는가.
  • 트랜잭션을 길게 유지하면 어떤 문제가 생기는가.
  • 여러 마이크로서비스에 걸친 작업의 정합성은 어떻게 보장할 수 있는가.
데이터베이스 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기