오늘은 개발을 할 때 중요한 어노테이션 @Transaction에 대해서 글을 작성해보며 공부하려고 한다.
@Transactional 이 없으면 위 코드에서 예외가 발생하더라도
포인트 차감이 이미 DB에 반영된 채로 남을 수 있다.
Service 계층에 붙이는 것이 가장 좋다.
Controller나 Repository에 붙이는 것은 권장되지 않는다.
예시
Spring의 기본 설정은 RuntimeException (Unchecked) 발생 시에만 rollback 된다.
예외 타입 롤백 여부 설명 RuntimeException ✅ 기본적으로 롤백됨 SQLException ❌ Checked 예외 — 명시적으로 지정해야 함
Checked 예외까지 롤백하고 싶다면 다음과 같이 작성한다.
Spring AOP는 프록시 기반으로 동작하므로,
public 메서드가 아니면 트랜잭션이 동작하지 않는다.
해결 방법은 다음과 같다.
1) 내부 호출 대신 별도의 Bean(클래스) 으로 분리하거나
2) ApplicationContext 로 자기 자신을 주입받아 호출한다.
이렇게 하면 Hibernate가 flush나 dirty checking을 생략하므로
성능이 향상된다.
실무에서는 예외가 발생했을 때 로그를 남기거나
사용자에게 에러 응답을 전달해야 한다.
주제 핵심 요약 사용 위치 Service 계층 기본 롤백 대상 RuntimeException Checked 예외 rollbackFor = Exception.class 내부 호출 트랜잭션 미적용 주의 readOnly 조회 전용 메서드에 사용
@Transactional 은 “붙이는 순간 다 해결되는 마법의 주석”이 아니다.
어디에 붙이고, 무슨 예외가 발생할 때 롤백되는지
정확히 이해해야 한다.
트랜잭션은 데이터의 신뢰성을 지켜주는 최후의 보루다.
Spring Boot를 다룬다면 반드시 이 원리를 이해하고 사용해야 한다.
🔹 오늘의 나는 무엇을 잘했는지
처음엔 @Transactional이 단순히 트랜잭션을 관리해주는 어노테이션 정도라고만 생각했는데, 공부하면서 내부 동작 원리(AOP 기반 프록시)와 롤백 규칙까지 정확히 이해할 수 있었다.
특히 @Transactional(readOnly = true)가 단순 옵션이 아니라 성능 최적화 역할도 한다는 점을 스스로 정리해낸 것이 뿌듯했다.
🔹 어떤 문제를 겪었고, 어떻게 해결할지
Checked Exception에서는 롤백이 자동으로 되지 않는다는 사실을 모르고 테스트하다가 데이터가 실제로 DB에 반영돼버리는 실수를 했다.
원인을 찾는 데 시간이 꽤 걸렸지만, rollbackFor = Exception.class를 사용해야 한다는 것을 알게 되었다.
앞으로는 테스트 코드에 명시적으로 예외를 던져보고 트랜잭션 동작 여부를 검증하는 습관을 들이려고 한다.
🔹 오늘 배운 것
이 과정을 코드로 실습해보며 트랜잭션이란 결국 DB 안전벨트라고 하는 말이 무슨 뜻인지 좀 알게된 것 같다~