AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 공통적으로 사용되는 부가 기능(로깅, 트랜잭션, 보안 등)을 모듈화하여 코드의 중복을 줄이고, 핵심 비즈니스 로직과 분리할 수 있도록 하는 프로그래밍 기법이다.
객체 지향 프로그래밍(OOP)에서의 한계
AOP를 사용하면?
AOP의 주요 개념
Spring AOP는 프록시 패턴을 사용하여 동작한다. 프록시 패턴이란 실제 객체를 감싸는 중간 객체(프록시)를 만들어, 요청을 가로채서 특정 로직을 실행한 후 대상 객체를 호출하는 방식이다.
Spring이 프록시 방식을 사용하는 이유
Spring AOP에서 사용하는 두 가지 프록시 방식
final
메서드는 오버라이딩할 수 없기 때문에 CGLib Proxy가 적용되지 않는다. @Transactional
은 AOP 기반으로 동작하며, 트랜잭션 처리를 위해 프록시 객체를 생성한다.
@Transactional
이 적용된 메서드를 호출한다. public class TransactionProxy {
private final TransactionManager manager = TransactionManager.getInstance();
public void transactionLogic() {
try {
manager.begin(); // 트랜잭션 시작
target.logic(); // 비즈니스 로직 실행
manager.commit(); // 트랜잭션 커밋
} catch (Exception e) {
manager.rollback(); // 예외 발생 시 롤백
}
}
}
private 메서드에는 적용되지 않는다.
private
메서드에 접근할 수 없다. @Transactional
이 적용된 메서드는 public
으로 선언해야 한다. 같은 클래스 내부에서 호출하면 트랜잭션이 적용되지 않는다.
@Transactional
메서드를 호출하면 AOP가 적용되지 않는다. self-invocation
(자기 자신 호출)을 피하고, 별도의 서비스 클래스로 분리하거나, ApplicationContext
를 이용해 프록시를 통해 호출한다. Checked Exception은 롤백되지 않는다.
@Transactional
은 RuntimeException
(Unchecked Exception
)이 발생할 경우에만 롤백한다. Checked Exception
을 롤백하려면 @Transactional(rollbackFor = Exception.class)
을 명시해야 한다. 다음 예제를 통해 트랜잭션 적용 방식의 차이를 확인할 수 있다.
@Service
public class CouponService {
@Autowired
private CouponRepository couponRepository;
@Transactional
public void createCoupon(Coupon coupon) {
couponRepository.save(coupon);
}
public void createCouponsWithoutTransaction() {
for (int i = 0; i < 3; i++) {
Coupon coupon = new Coupon("쿠폰 " + i);
createCoupon(coupon); // 같은 클래스 내에서 호출 (트랜잭션 적용 안 됨)
}
throw new RuntimeException(); // 일부만 저장되고 롤백되지 않음
}
@Transactional
public void createCouponsWithTransaction() {
for (int i = 0; i < 3; i++) {
Coupon coupon = new Coupon("쿠폰 " + i);
couponRepository.save(coupon);
}
throw new RuntimeException(); // 전체 롤백됨
}
}
실행 결과
createCouponsWithoutTransaction()
→ 일부 데이터가 저장되고 롤백되지 않음 createCouponsWithTransaction()
→ 예외 발생 시 전체 데이터 롤백 @Transactional
은 AOP 기반으로 트랜잭션을 관리하며, 프록시 객체가 메서드 호출을 가로채서 트랜잭션을 처리한다. private
메서드에는 적용되지 않으며, 같은 클래스 내에서 호출하면 AOP가 적용되지 않는다. Checked Exception
은 기본적으로 롤백되지 않으므로 rollbackFor
옵션을 설정해야 한다.