Spring AOP와 @Transactional의 동작 원리

김상진 ·2025년 2월 2일
0

CS

목록 보기
28/30

Spring AOP와 @Transactional의 동작 원리

1. AOP란 무엇이며 왜 사용하는가?

AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 공통적으로 사용되는 부가 기능(로깅, 트랜잭션, 보안 등)을 모듈화하여 코드의 중복을 줄이고, 핵심 비즈니스 로직과 분리할 수 있도록 하는 프로그래밍 기법이다.

객체 지향 프로그래밍(OOP)에서의 한계

  • 객체 지향 프로그래밍(OOP)은 단일 책임 원칙(SRP)에 따라 클래스를 분리하지만, 공통적으로 사용되는 기능(흩어진 관심사, Cross Cutting Concern)은 여러 클래스에 반복적으로 등장할 수밖에 없다.
  • 예를 들어, 트랜잭션 관리 코드가 여러 서비스 클래스에 중복되면 유지보수성이 떨어지고 코드가 복잡해진다.

AOP를 사용하면?

  • 공통 기능을 별도의 모듈(Aspect)로 분리할 수 있다.
  • 비즈니스 로직을 담당하는 코드에서 부가 기능이 제거되어 가독성이 향상된다.
  • 공통 기능의 변경이 필요한 경우 한 곳에서 수정하면 되므로 유지보수가 용이해진다.

AOP의 주요 개념

  • Aspect: 부가 기능을 모아둔 모듈 (ex. 트랜잭션 관리, 로깅)
  • Advice: 실행될 부가 기능 로직 (Before, After, Around 등)
  • JoinPoint: Advice가 적용될 수 있는 지점 (메서드 호출, 필드 접근 등)
  • PointCut: Advice가 적용될 특정 위치를 정의하는 표현식
  • Target: 부가 기능을 적용할 대상 객체

2. Spring AOP와 프록시(Proxy) 방식

Spring AOP는 프록시 패턴을 사용하여 동작한다. 프록시 패턴이란 실제 객체를 감싸는 중간 객체(프록시)를 만들어, 요청을 가로채서 특정 로직을 실행한 후 대상 객체를 호출하는 방식이다.

Spring이 프록시 방식을 사용하는 이유

  • AOP 없이 트랜잭션을 적용하려면 서비스 클래스 내부에서 직접 트랜잭션 관리 로직을 호출해야 한다.
  • 하지만 이렇게 하면 중복 코드가 많아지고, 비즈니스 로직과 트랜잭션 관리 코드가 섞여 가독성이 떨어진다.
  • 프록시 객체를 사용하면 원본 객체를 수정하지 않고도 공통 기능을 주입할 수 있어 유지보수성이 높아진다.

Spring AOP에서 사용하는 두 가지 프록시 방식

  1. JDK Dynamic Proxy
    • 인터페이스를 상속받아 프록시 객체를 생성한다.
    • 인터페이스 기반으로 동작하기 때문에 인터페이스가 없는 클래스에는 적용할 수 없다.
    • 내부적으로 Reflection을 사용하여 호출하므로 성능이 상대적으로 낮다.
  2. CGLib Proxy
    • 인터페이스가 없어도 동작하며, 대상 클래스를 상속받아 프록시 객체를 생성한다.
    • JDK Proxy보다 성능이 우수하며, Spring Boot에서는 기본적으로 CGLib Proxy를 사용한다.
    • 단, final 메서드는 오버라이딩할 수 없기 때문에 CGLib Proxy가 적용되지 않는다.

3. @Transactional의 동작 방식

@Transactional은 AOP 기반으로 동작하며, 트랜잭션 처리를 위해 프록시 객체를 생성한다.

  1. 클라이언트가 @Transactional이 적용된 메서드를 호출한다.
  2. AOP 프록시 객체가 호출을 가로채서 트랜잭션을 시작한다.
  3. 원본 메서드(비즈니스 로직)가 실행된다.
  4. 실행이 정상적으로 완료되면 트랜잭션이 커밋된다.
  5. 예외가 발생하면 트랜잭션이 롤백된다.
public class TransactionProxy {
    private final TransactionManager manager = TransactionManager.getInstance();
    
    public void transactionLogic() {
        try {
            manager.begin(); // 트랜잭션 시작
            target.logic();  // 비즈니스 로직 실행
            manager.commit(); // 트랜잭션 커밋
        } catch (Exception e) {
            manager.rollback(); // 예외 발생 시 롤백
        }
    }
}

4. @Transactional 사용 시 주의할 점

  1. private 메서드에는 적용되지 않는다.

    • 트랜잭션 처리는 프록시 객체가 대신 수행하는데, 프록시는 private 메서드에 접근할 수 없다.
    • 따라서 @Transactional이 적용된 메서드는 public으로 선언해야 한다.
  2. 같은 클래스 내부에서 호출하면 트랜잭션이 적용되지 않는다.

    • 프록시 객체가 트랜잭션을 관리하기 때문에, 같은 클래스 내부에서 @Transactional 메서드를 호출하면 AOP가 적용되지 않는다.
    • 해결 방법: self-invocation(자기 자신 호출)을 피하고, 별도의 서비스 클래스로 분리하거나, ApplicationContext를 이용해 프록시를 통해 호출한다.
  3. Checked Exception은 롤백되지 않는다.

    • 기본적으로 @TransactionalRuntimeException(Unchecked Exception)이 발생할 경우에만 롤백한다.
    • Checked Exception을 롤백하려면 @Transactional(rollbackFor = Exception.class)을 명시해야 한다.

5. @Transactional 적용 예제

다음 예제를 통해 트랜잭션 적용 방식의 차이를 확인할 수 있다.

@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() → 예외 발생 시 전체 데이터 롤백

정리

  1. AOP는 공통 기능을 모듈화하여 코드 중복을 줄이고 유지보수성을 높이는 개념이다.
  2. Spring AOP는 프록시 패턴을 사용하여 동작하며, 기본적으로 CGLib Proxy를 사용한다.
  3. @Transactional은 AOP 기반으로 트랜잭션을 관리하며, 프록시 객체가 메서드 호출을 가로채서 트랜잭션을 처리한다.
  4. private 메서드에는 적용되지 않으며, 같은 클래스 내에서 호출하면 AOP가 적용되지 않는다.
  5. Checked Exception은 기본적으로 롤백되지 않으므로 rollbackFor 옵션을 설정해야 한다.

출처 및 참고자료

profile
알고리즘은 백준 허브를 통해 github에 꾸준히 올리고 있습니다.🙂

0개의 댓글

관련 채용 정보