트랜잭션은 데이터베이스에서 논리적인 작업의 단위를 의미. 주로 하나의 작업을 처리하기 위해 여러 개의 작업(쿼리)이 실행될 수 있는데, 이 모든 작업을 하나의 단위로 묶어서 처리하는 것을 트랜잭션이라고 함.
트랜잭션은 데이터베이스의 상태를 한 상태에서 다른 상태로 변화 시키는 작업을 의미하며, 모든 작업이 성공적으로 완료되거나, 문제가 발생하면 모두 취소(롤백, rollback)되어야 한다.
Commit : 트랜잭션 내의 모든 작업이 성공적으로 완료되면 데이터베이스에 영구적으로 반영
Rollback : 트랜잭션 내의 일부 작업이 실패하면 모든 작업을 취소하고 데이터베이스를 이전 상태로 복구
트랜잭션은 데이터 무결성과 일관성을 보장하기 위해 매우 중요하다.
트랜잭션이 데이터베이스의 무결성과 일관성을 유지하기 위해 따라야할 4가지 주요 특성
Atomicity(원자성) : 트랜잭션의 모든 작업이 모두 성공하거나, 모두 실패해야 함을 보장. 부분적으로 실행된 상태는 존재하지 않음.
Consistency(일관성) : 트랜잭션이 실행되기 전과 후에 데이터베이스는 항상 일관된 상태를 유지해야 함. 트랜잭션은 데이터 무결성 제약 조건을 항상 만족해야 함.
Isolation(격리성) : 하나의 트랜잭션이 실행 중일 때, 다른 트랜잭션이 해당 작업에 영향을 주지 않도록 해야 함. 여러 트랜잭션이 동시에 실행될 때도 각 트랜잭션이 독립적으로 실행된 것과 동일한 결과를 보장.
Durability(지속성) : 트랜잭션이 성공적으로 커밋되면, 해당 변경 내용은 영구적으로 반영되어야 함. 시스템 장애가 발생하더라도 데이터는 손실되지 않도록 보장
신입 및 취준생 Java, Spring 백엔드 개발자의 입장에서 트랜잭션(Transaction)과 ACID 특성을 이해하고 활용하기 위해 실습할 수 있는 몇 가지 방법과 아이디어를 정리해 드릴게요.
목표: 기본적인 트랜잭션 처리와 롤백/커밋의 동작 이해하기
내용: JDBC를 활용해 직접 트랜잭션 처리 코드 작성
사전 준비:
예제: 은행 계좌 이체 시뮬레이션:
CREATE TABLE Account (
id INT PRIMARY KEY,
name VARCHAR(50),
balance INT
);
INSERT INTO Account VALUES (1, 'Alice', 1000);
INSERT INTO Account VALUES (2, 'Bob', 1000);JDBC 코드 예시:
Connection conn = null;
try {
conn = DriverManager.getConnection(DB_URL, USER, PASSWORD);
conn.setAutoCommit(false); // 트랜잭션 시작
// Alice 계좌에서 출금
PreparedStatement pstmt1 = conn.prepareStatement("UPDATE Account SET balance = balance - 200 WHERE id = 1");
pstmt1.executeUpdate();
// Bob 계좌에 입금
PreparedStatement pstmt2 = conn.prepareStatement("UPDATE Account SET balance = balance + 200 WHERE id = 2");
pstmt2.executeUpdate();
conn.commit(); // 성공 시 커밋
} catch (Exception e) {
if (conn != null) conn.rollback(); // 실패 시 롤백
e.printStackTrace();
} finally {
if (conn != null) conn.close();
}
목표: Spring의 트랜잭션 관리와 @Transactional의 동작 이해
프로젝트 세팅:
데이터베이스 설정:
간단한 Account 엔터티 생성:
@Entity
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int balance;
}
데이터 초기화:
INSERT INTO Account (name, balance) VALUES ('Alice', 1000), ('Bob', 1000);
서비스 레이어 트랜잭션 처리:
@Transactional을 활용해 이체 기능 구현:
@Service
public class AccountService {
@Autowired
private AccountRepository accountRepository;
@Transactional
public void transferMoney(Long fromId, Long toId, int amount) {
Account fromAccount = accountRepository.findById(fromId).orElseThrow();
Account toAccount = accountRepository.findById(toId).orElseThrow();
fromAccount.setBalance(fromAccount.getBalance() - amount);
toAccount.setBalance(toAccount.getBalance() + amount);
accountRepository.save(fromAccount);
accountRepository.save(toAccount);
// 예외 강제로 발생 (롤백 테스트용)
if (toAccount.getBalance() > 1500) {
throw new RuntimeException("Balance too high!");
}
}
}
테스트:
@Transactional로 묶은 작업에 일부 실패를 강제로 발생시켜 롤백되는지 확인.spring:
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
jpa:
properties:
hibernate.connection.isolation: 2 # READ_COMMITTEDREAD_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE의 차이를 관찰.Spring AOP와 트랜잭션 관리:
@Transactional 내부 동작 원리 확인.동시성 이슈 및 격리 수준 실습:
실제 프로젝트에 응용:
이러한 실습은 트랜잭션의 개념뿐 아니라, Spring Boot의 동작 방식과 데이터베이스 관리 능력을 키우는 데 효과적입니다. 필요한 부분에 대해 추가로 설명이 필요하면 말씀해주세요! 😊