강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

김태형님의 프로필 이미지
김태형

작성한 질문수

토비의 스프링 6 - 이해와 원리

@Transactional private 사용유무

작성

·

83

0

proxy 패턴으로 애플리케이션 서비스에서 반복적인 트랜잭션 처리 적용까지는 이해했습니다.

여기서 만약 애플리케이션 서비스 내부에서 private 로 두개의 트랜잭션은 어떻게 적용해야할까요?

아래는 제가 생각한 흐름입니다.


가령 현재 강의에서는 OrderService 내부에서 주문 정보만 생성하고 있는데 주문 후 결제까지 이뤄진다면?

public class OrderServiceImpl implements OrderService {
    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;

    public OrderServiceImpl(
            OrderRepository orderRepository,
            PaymentRepository paymentRepository
    ) {
        this.orderRepository = orderRepository;
        this.paymentRepository = paymentRepository;
    }

    @Override
    public Order createOrder(Order order) {
        // 주문 도메인 생성
        
        // 결제 도메인 생성
    }

    @Transactional
    private void save(Order order, Payment payment) {
        // 주문 및 결제 정보 저장
    }
}

제가 찾아보기로는 @Transactional 경우 Proxy 패턴으로 스프링에서 begin, rollback, commit 을 자동으로 수행해주는데 이를 private 으로 선언 한 경우 스프링에 private 메소드에 접근을 못해서 정상 동작을 안하는걸로 알고 있습니다.

(강의를 듣고서) 제가 추측하기로는 TransactionTemplate 으로 프록시 패턴으로 적용한경우 private 메소드에는 접근을 못하니깐 애노테이션 또한 정상동작을 안하는것으로 생각됩니다.
(제 추측이 틀렸다면 자세한 설명 부탁드립니다.)

 

그러면 애플리케이션 서비스는 기술 의존적으로 바뀌게 될텐데 private 함수에서 애노테이션이 아닌 TransactionTemplate 을 써야되나?

 

강의 듣고서는 이정도밖에 생각이 안드는데 어떤식으로 접근하고 private 에서도 트랜잭션을 적용하기위한 원리는 어떤게있는지 답변 주시면 감사하겠습니다!

 

답변 1

0

토비님의 프로필 이미지
토비
지식공유자

말씀하신 대로 private 메소드에는 스프링의 프록시 기반 AOP를 적용할 수 없습니다. 기본적으로 인터페이스 구현 또는 상속을 통한 오버라이딩 기법을 써서 프록시 역할을 하는 코드를 최종 클래스의 메소드를 실행하기 전후에 넣어야 하는데, 그런 코드가 나올 수가 없기 때문이죠.

그러면 TransactionTemplate을 써서 트랜잭션을 만드는 방법 뿐인가라고 생각할 수도 있겠네요.

하지만 좀 더 근본적으로 생각해볼 필요가 있겠네요.

모든 클래스의 private 메소드는 결국 어떤 public 또는 그에 준하는 접근이 가능한 메소드를 타고 실행이 됩니다. 그런다고 하면 트랜잭션의 경계를 굳이 특정 private으로 제한할 필요는 없을 겁니다. 보통 public으로 외부에 공개한 메소드가 결국 이 오브젝트가 제공하는 중요한 기능일테고, 트랜잭션은 그런 기능 단위로 시작하고 종료하는 게 자연스럽습니다. private 메소드는 시간이 지나면서 자주 바뀌거나 변경됩니다. 내부의 디테일한 구현은 리팩터링이나 여러 다른 이유로 변경을 해도 오브젝트를 사용하는 쪽에 영향을 주지 않기 때문에 변경에서 자유롭습니다. 그런데 그 메소드에 트랜잭션을 선언한다는 것은 매우 불안정한 코드와 트랜잭션 기능 적용이 되겠죠.

모든 DB 기능이 사용되는 메소드에서 다시 트랜잭션을 시작할 필요는 없습니다. 진입점에 해당하는 public 메소드에서 트랜잭션을 시작해두면 이후에 호출되는 모든 메소드, 심지어 다른 빈의 메소드까지 트랜잭션에 포함됩니다.

따라서 private 메소드에 트랜잭션을 지정하는 것은 결코 좋은 설계가 아닙니다. 아주 복잡한 AOP 기술을 쓰면 불가능한 것도 아니지만, 그럴만큼 필요할리가 없을 듯하네요. 트랜잭션을 시작해두는 것 때문에 특별히 성능에 영향이 있거나 하지 않습니다. 그래서 public 메소드나 인터페이스의 공개된 메소드에 트랜잭션을 지정하는 것이 좋습니다.

위의 코드라면 createOrder() 메소드에 트랜잭션 설정을 적용하세요.

김태형님의 프로필 이미지
김태형

작성한 질문수

질문하기