@Transactional이란?
@Transactional은 Spring에서 트랜잭션(Transaction)을 관리하는 어노테이션
- 데이터베이스 작업(Insert, Update, Delete 등)을 하나의 단위로 묶어서 처리
- 오류가 발생하면 자동으로 롤백(rollback)하여 데이터의 일관성을 유지
- 트랜잭션이 끝날 때 자동으로 commit 또는 rollback 수행

효과
1️⃣ 트랜잭션 시작 → 성공하면 Commit, 실패하면 Rollback
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public void updateUser(Long id, String newName) { //메서드 실행시 트랜잭션 시작
User user = userRepository.findById(id).orElseThrow();
user.setName(newName);
// ✅ 변경 사항 자동 반영 (JPA가 자동으로 flush)
// 오류없이 끝나면 자동으로 commit, 예외발생하면 rollback
}
}
2️⃣ 예외 발생 시 자동으로 rollback (실패 시 데이터 변경 없음)
@Transactional
public void updateUsers(Long id1, Long id2, String newName) {
User user1 = userRepository.findById(id1).orElseThrow();
user1.setName(newName);
if (true) throw new RuntimeException("예외 발생!"); // 강제 오류
User user2 = userRepository.findById(id2).orElseThrow();
user2.setName(newName);
}
- user1의 이름이 변경되었지만 중간에 예외 발생
- 트랜잭션이 롤백되면서 user1의 변경도 취소됨
- 예외가 발생하면 데이터가 원래 상태로 복구됨
3️⃣ @Transactional을 안 쓰면 데이터가 중간에 변경될 수도 있음
public void updateUsersWithoutTransaction(Long id1, Long id2, String newName) {
User user1 = userRepository.findById(id1).orElseThrow();
user1.setName(newName);
userRepository.save(user1); // ✅ 여기서 변경사항이 DB에 반영됨 (Commit)
if (true) throw new RuntimeException("예외 발생!"); // 강제 오류
User user2 = userRepository.findById(id2).orElseThrow();
user2.setName(newName);
userRepository.save(user2);
}
- 예외 발생 후에도 user1의 데이터가 변경된 상태로 남아 있음 (Rollback 안 됨)
- @Transactional을 사용하면 모든 작업이 하나의 단위로 처리되므로, 중간에 실패하면 이전 작업도 롤백됨.
- @Transactional을 안 쓰면 중간에 예외가 발생해도 이미 변경된 데이터가 저장될 수 있음
위치에 따라 동작 방식이 달라짐
1️⃣ 클래스 전체에 @Transactional 적용
@Service
@Transactional
public class UserService { //클래스에 @Transactional을 적용하면 모든 메서드에 자동으로 트랜잭션이 적용
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void updateUser(Long id, String newName) { //트랜잭션 내에서 실행
User user = userRepository.findById(id).orElseThrow();
user.setName(newName);
}
public void deleteUser(Long id) { //트랜잭션 내에서 실행
userRepository.deleteById(id);
}
}
2️⃣ 특정 메서드에만 @Transactional 적용
@Transactional
public void updateUser(Long id, String newName) { //이 메서드에서만 트랙잭션 적용
User user = userRepository.findById(id).orElseThrow();
user.setName(newName);
}
주의할 점
1️⃣ @Transactional은 public 메서드에서만 동작
@Transactional
private void updateUser(Long id, String newName) { ... } // ❌ 적용 안 됨!
- @Transactional은 public 메서드에서만 동작
- private, protected, default 메서드에서는 적용되지 않음.
2️⃣ 같은 클래스 내부에서 @Transactional 메서드 호출 시 적용되지 않음
@Service
public class UserService {
@Transactional
public void updateUser(Long id, String newName) {
User user = userRepository.findById(id).orElseThrow();
user.setName(newName);
}
public void updateAndNotify(Long id, String newName) {
updateUser(id, newName); // ❌ 트랜잭션 적용 안 됨!
}
}
AOP(Aspect-Oriented Programming)의 관계
1️⃣ @Transactional은 내부적으로 AOP를 사용한다
Spring에서 @Transactional을 사용하면 AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍) 를 통해 트랜잭션이 자동으로 관리됨.
- @Transactional을 메서드에 붙이면 Spring AOP가 해당 메서드를 감싸서 트랜잭션을 시작하고 종료함
- 예외가 발생하면 AOP가 감지해서 rollback을 수행
- 예외가 없으면 AOP가 자동으로 commit을 수행
👉 @Transactional이 동작하는 원리는 AOP 기반의 프록시(Proxy) 패턴을 이용해서 트랜잭션을 관리하는 것!
2️⃣ @Transactional이 실행되는 과정
Spring이 @Transactional을 어떻게 AOP로 관리하는지 흐름을 살펴보자.
1. 클라이언트가 updateUser() 메서드를 호출
2. Spring AOP 프록시가 @Transactional을 감지하여 트랜잭션을 시작
3. updateUser()가 실행됨
4. 예외가 발생하면 → rollback, 예외가 없으면 → commit
5. Spring AOP 프록시가 트랜잭션을 종료
📌 코드 예제
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional //Spring AOP가 자동으로 트랜잭션을 관리함(내부적으로 AOP 프록시 객체가 동작하여 트랜잭션 처리)
public void updateUser(Long id, String newName) {
User user = userRepository.findById(id).orElseThrow();
user.setName(newName);
}
}
3️⃣ AOP 프록시(Proxy)가 트랜잭션을 관리하는 이유
Spring에서는 @Transactional을 적용하면 AOP 기반의 프록시 객체(Proxy Object)가 생성됨
📌 트랜잭션 프록시 동작 방식
클라이언트 → (프록시) → updateUser() 실행 → (프록시) → commit or rollback
- AOP를 통해 메서드 실행 전후에 트랜잭션 시작/종료 기능을 추가할 수 있음
| 개념 | 설명 |
| AOP(Aspect-Oriented Programming) | @Transactional을 감싸는 프록시 역할 |
| 프록시(Proxy) 패턴 | 트랜잭션을 자동으로 시작하고 종료 |
| Spring AOP 동작 방식 | 메서드 호출 시 프록시를 통해 트랜잭션 관리 |
| 트랜잭션 적용 시 주의점 | 같은 클래스 내부 호출 & private 메서드는 적용되지 않음 |
| 트랜잭션 전파(Propagation) | 다른 트랜잭션과 충돌을 방지하기 위해 설정 가능 |
'개발인생 > Backend' 카테고리의 다른 글
| [ORM] JPA, JPQL, QueryDSL - Spring과의 관계 (1) | 2025.03.19 |
|---|---|
| Spring vs Spring Boot (2) | 2025.03.19 |
| [Spring] Spring Data JPA (0) | 2025.03.19 |
| GraphQL 입문 가이드: REST API 대체할 만할까? (0) | 2025.03.07 |
| [Spring] Spring Boot 개발자를 위한 필수 디자인 패턴 6가지 (0) | 2025.02.25 |