minghxx.blog
  • [Spring] 스프링 DB 1편 4) 스프링과 문제 해결 - 트랜잭션(2)
    2023년 11월 19일 09시 40분 22초에 업로드 된 글입니다.
    작성자: 민발자
    728x90

    스프링 DB 1편 데이터 접근 핵심 원리

    Session 4 스프링과 문제 해결 - 트랜잭션

    7. 트랜잭션 문제 해결 - 트랜잭션 AOP 이해 

    1) 프록시

    트랜잭션을 편리하게 처리하기 위해 트랜잭션 추상화를 도입하고 반복적인 트랜잭션 로직을 해결하기 위해 트랜잭션 템플릿을 도입했다.

    하지만 서비스 계층에 순수한 비즈니스 로직 외에 트랜잭션 처리 로직이 섞여있다.

    스프링 AOP를 통해 프록시를 도입해 해결할 수 있다.

    트랜잭션 프록시가 트랜잭션 처리 로직을 모두 가져가고 트랜잭션을 시작한 후에 실제 서비스를 대신 호출, 서비스 계층에는 순수한 비즈니스 로직만 남길 수 있다.

    프록시를 도입하기 전에는 서비스의 로직에서 직접 트랜잭션을 시작

    프록시를 사용하면 트랜잭션을 처리하는 객체와 비즈니스 로직을 처리하는 서비스 객체를 명확하게 분리할 수 있다.

     

    2) 스프링이 제공하는 트랜잭션 AOP

    스프링이 제공하는 AOP 기능을 사용하면 프록시를 편리하게 사용 가능

    스프링 부트를 사용하면 필요한 스프링 빈도 자동으로 등록

    트랜잭션 처리가 필요한 곳에 @Transactional 애노테이션만 붙이면 스프링이 트랜잭션 프록시를 적용한다.


    8. 트랜잭션 문제 해결 - 트랜잭션 AOP 적용 

    1) @Transactional

    private final MemberRepositoryV3 memberRepository;
    
    @Transactional
    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        bizLogic(fromId, toId, money);
    }

    순수한 비즈니스 로직만 남기고 트랜잭션 코드는 모두 제거

    @Transactional 애노테이션은 메서드, 클래스 모두 가능

    클래스에 붙이면 외부에서 호출가능한 pulic 메서드가 AOP 적용 대상이 된다.

     

    2) @Transactional 테스트

    @SpringBootTest
    class MemberServiceV3_3Test {
    
        ...
    
        @Autowired
        private MemberRepositoryV3 memberRepository;
        @Autowired
        private MemberServiceV3_3 memberService;
    
        @TestConfiguration
        static class TestConfig{
            @Bean
            DataSource dataSource() {
                return new DriverManagerDataSource(URL, USERNAME, PASSWORD);
            }
    
            @Bean
            PlatformTransactionManager transactionManager() {
                return new DataSourceTransactionManager(dataSource());
            }
    
            @Bean
            MemberRepositoryV3 memberRepositoryV3() {
                return new MemberRepositoryV3(dataSource());
            }
    
            @Bean
            MemberServiceV3_3 memberServiceV3_3() {
                return new MemberServiceV3_3(memberRepositoryV3());
            }
    
        }
        
        ...

    @SpringBootTest : 스프링 AOP를 적용하려면 스프링 컨테이너가 필요, 테스트 시 스프링 부트를 통해 스프링 컨테이너를 생성, 스프링 빈들을 사용할 수 있다.

    @TestConfiguration : 테스트 안에서 내부 설정 클래스를 만들어 사용, 스프링 부트가 자동으로 필요한 스프링 빈들을 등록하고 테스트 수행

    DataSource : 스프링에서 기본으로 사용할 데이터소스를 스프링 빈으로 등록

    DataSourceTransactionManager : 트랜잭션 매니저 스프링 빈으로 등록, 스프링이 제공하는 트랜잭션 AOP는 스프링 빈에 등록된 트랜잭션 매니저를 사용하기 때문에 빈으로 등록해야 한다.

     

    3) AOP 프록시 적용 확인

    memberService class=class hello.jdbc.service.MemberServiceV3_3$ $EnhancerBySpringCGLIB$$...
    memberRepository class=class hello.jdbc.repository.MemberRepositoryV3

    EnhancerBySpringCGLIB라는 부분을 통해 프록시가 적용된 것을 확인

    memberRepository는 AOP를 적용하지 않았기 때문에 프록시가 적용되지 않았다.

     


    9. 트랜잭션 문제 해결 - 트랜잭션 AOP 정리

    1) 트랜잭션 AOP 동작

    1. 클라이언트 프록시 호출

    2. AOP 프록시에서 스프링 컨테이너를 통해 트랜잭션 매니저 획득

    3. getTransaction() 트랜잭션 시작

    4. 데이터소스에서 커넥션을 생성하고 수동 커밋 모드로 변경 후 트랜잭션 동기화 매니저에 커넥션 보관

    5. AOP 프록시는 실제 서비스를 호출

    6. 서비스에서 비즈니스 로직을 수행하며 데이터 접근 로직을 통해 트랜잭션 동기화 매니저에 보관된 커넥션을 획득

    7. 비즈니스 로직 결과에 따라 커밋, 롤백을 수행

    8. 트랜잭션 종료

     

    2) 선언적 트랜잭션 관리 vs 프로그래밍 방식 트랜잭션 관리 

    선언적 트랜잭션 관리 : @Transactional 애노테이션 하나만 선언해 트랜잭션을 적용

    프로그래밍 방식 트랜잭션 관리 : 트랜잭션 매니저, 트랜잭션 템플릿 등을 사용해 관련 코드를 직접 작성하는 것

    실무에서는 선언적 방식이 훨씬 간편하고 실용적인 방법이기 때문에 선언적 방식을 사용

    선언적 방식은 스프링 컨테이너, 스프링 AOP가 필요하지만 실무에서 대부분 사용하기 때문에 선언적 방식을 사용하자


    10. 스프링 부트의 자동 리소스 등록 

    1) 데이터 소스 자동 등록

    스프링 부트는 DataSource를 스프링 빈에 자동 등록한다. 자동 등록된 빈 이름 dataSource

    개발자가 직접 빈으로 등록하면 스프링은 자동으로 등록하지 않는다.

    application.properties에 있는 속성을 사용해 DataSource를 생성한다. 커넥션 풀과 관련된 설정도 지정 가능

    url 속성이 없으면 내장 데이터베이스(메모리 DB)를 생성하려고 시도한다.

    스프링 부트가 기본으로 생성하는 데이터소스는 HikariDataSource

    spring.datasource.url=jdbc:h2:tcp://localhost/~/test
    spring.datasource.username=sa
    spring.datasource.password=

     

    2) 트랜잭션 매니저 자동 등록

    스프링 부트는 트랜잭션 매니저(PlatformTransactionManager)를 자동으로 스프링 빈에 등록, 자동 등록된 빈 이름 transactionManager

    개발자가 직접 빈으로 등록하면 스프링은 자동으로 등록하지 않는다.

    어떤 트랜잭션 매니저를 선택할지는 현대 등록된 라이브러리를 보고 판단

    JDBC→DataSourceTransactionManager

    JPA→JpaTransactionManager

    둘 다→JpaTransactionManager(JDBC 트랜잭션 매니저 기능을 대부분 지원하기 때문에 Jpa 트랜잭션 매니저 등록)

     

    3) @TestConfiguration 수정

    @TestConfiguration
    static class TestConfig{
    
        private final DataSource dataSource;
    
        public TestConfig(DataSource dataSource) {
            this.dataSource = dataSource; // 스프링이 자동으로 등록한 dataSource를 넣어줌
        }
    
        @Bean
        MemberRepositoryV3 memberRepositoryV3() {
            return new MemberRepositoryV3(dataSource);
        }
    
        @Bean
        MemberServiceV3_3 memberServiceV3_3() {
            return new MemberServiceV3_3(memberRepositoryV3());
        }
    
    }

    데이터소스와 트랜잭션 매니저 스프링 빈 등록 코드 제거

     

    728x90
    댓글