스프링 DB 1(김영한 강의) - 섹션 4 : 스프링과 문제해결, 트랜잭션

섹션 목적 : 스프링이 트랜잭션을 사용할 때 발생하는 문제점을 어떻게 해결하는지 알아보자

 

1. 문제점들

   (1) 애플리케이션 구성은 Controller(프레젠테이션) -> Service(비즈니스 로직) -> Repository(데이터 접근계층) -> DB 

   (2) 프레젠테이션, 데이터 접근 계층은 다른 기술로 변경해도 비즈니스 로직(Service)는 최대한 변경없이 유지해야 함

   (3) 이렇게 하려면 서비스 계층이 특정 기술에 종속적이지 않게 개발해야 함

   (4) 지금까지 개발한 MemberServiceV2의 문제점

      ① 트랜잭션을 사용하기 위해서 JDBC 기술에 의존함

         * JDBC에서 JPA로 변경할 경우, 모든 코드를 고쳐야함

         * 비즈니스 로직보다 트랜잭션 처리 코드가 더 많음

      ② 예외 누수(ex. SQL Exception)

      ③ JDBC 반복 문제(ex. repository의 try, catch, finally 반복)

   (5) Spring을 이용해서 위 3가지 문제를 해결해보자, 이번 섹션에서는 ① 문제를 해결해보자

 

2. 트랜잭션 추상화

   (1) 구현 기술에 따라서 트랜잭션을 처리하는 방법이 다름

      ① JDBC : con.setAutoCommit(false)

      ② JPA : transaction.begin()

   (2) 만약 JDBC에서 JPA로 변경하면 서비스의 트랜잭션 처리 코드를 모두 변경해야함

   (3) 따라서, 스프링은 트랜잭션을 처리하는 기술을 추상화해서 PlatformTransactionManager라는 트랜잭션 매니저를 제공함

   (4) 우리는 이제 트랜잭션을 사용할 때, txManager.getConnection() -> 비즈니스 로직 -> txManager.commit() or rollback()을 사용하기만 하면되고, JDBC를 쓰다가 JPA로 변경해도 트랜잭션 처리 코드를 변경할 필요가 없음

 

3. 트랜잭션 동기화

   (1) 트랜잭션 매니저는 크게 두가지 역할을 함

      ① 트랜잭션 추상화

      ② 리소스 동기화

   (2) 리소스 동기화란

      ① 트랜잭션을 유지하려면, 트랜잭션의 시작부터 끝까지 같은 DB 커넥션을 유지해야함

      ② 이전에 우리는 커넥션 동기화를 위해서, 커넥션을 파라미터로 전달하는 방법을 사용함

      ③ 이렇게 되면 커넥션을 매번 파라미터로 넘겨야해서 코드가 지저분해짐

      ④ 그래서 스프링은 트랜잭션 동기화 매니저를 제공함

   (3) 트랜잭션 매니저 내부에서 트랜잭션 동기화 매니저를 사용하고, 트랜잭션 동기화 매니저는 멀티 쓰레드 상황에서 안전하게 커넥션을 동기화 해줌

   (4) 따라서, 커넥션이 필요하면 동기화 매니저를 통해서 커넥션을 획득하면 됨

   (5) 그러므로, 커넥션을 파라미터로 전달할 필요가 없음

   (6) 트랜잭션 동작 방식

      ① 트랜잭션 매니저는 데이터 소스를 통해 커넥션을 생성

      ② 이 커넥션은 트랜잭션 동기화 매니저에 보관

      ③ 리포지토리는 동기화 매니저에 등록된 커넥션을 꺼내서 사용

      ④ 트랜잭션이 종료되면, 트랜잭션 매니저는 동기화 매니저에 등록된 커넥션을 닫고 종료

 

4. 트랜잭션 문제 해결 - 트랜잭션 매니저 1

   (1) 트랜잭션 동기화 매니저가 관리하는 커넥션을 사용하려면, repository는 커넥션을 가져올 때, DataSourceUtils.getConnection(dataSource)를 사용해야 함

   (2) 또한, 트랜잭션 중간에 커넥션을 닫으면 안되므로, DataSourceUtils.releaseConnection()을 사용해야 함

   (3) Service는

      ① txManager.getConnection() 으로 트랜잭션 시작

      ② 비즈니스 로직

      ③ txManager.commit() or rollback() 으로 트랜잭션 커밋 or 롤백 후 종료

 

5. 트랜잭션 문제 해결 - 트랜잭션 매니저 2

   (1) 트랜잭션 전체 흐름 정리

      ① 서비스에서 txManager.getTransaction() 으로 트랜잭션 시작

      ② txManager는 DataSource를 통해 DB 커넥션을 생성

      ③ 생성된 커넥션을 setAutoCommit(false)로 변경

      ④ 이 커넥션을 트랜잭션 동기화 매니저에 보관

      ⑤ 비즈니스 로직을 시작하면서 리포지토리 메소드들을 호출

      ⑥ 리포지토리 메서드들은 트랜잭션에서 시작된 커넥션이 필요하므로, DataSourceUtils.getConnection()을 사용해서 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용함 -> 같은 커넥션을 사용

      ⑦ 획득한 커넥션으로 DB에 SQL을 전달

      ⑧ 비즈니스 로직이 끝나면, 해당 트랜잭션을 커밋 or 롤백 후 종료

      ⑨ 트랜잭션 매니저는 사용한 커넥션을 정리함

   (2) 트랜잭션 추상화 덕분에 JDBC가 JPA로 변경되어도 서비스 코드를 유지할 수 있게 되었음

 

6. 트랜잭션 문제 해결 - 트랜잭션 템플릿

   (1) 트랜잭션을 사용하는 코드를 보면

      ① 트랜잭션 시작

      ② 비즈니스 로직

      ③ 커밋 or 롤백

이 반복되는 것을 볼 수 있음

   (2) 이런 형태가 계속 반복되므로, 템플릿 콜백 패턴을 통해 이런 반복문제를 해결할 수 있음

   (3) MemberService에 txTemplate.execute( ... 비즈니스 로직 ... ) 을 사용

   (4) 트랜잭션 템플릿으로 트랜잭션 사용 시 반복되는 코드를 제거할 수 있음

   (5) 하지만, 여전히 Service에 비즈니스 로직 뿐만 아니라 트랜잭션을 처리하는 기술 로직이 함께 들어가 있음

   (6) 어떻게 하면 Service 계층을 순수하게 유지할 수 있을까?

 

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

   (1) 스프링 AOP를 통해 프록시를 도입해서 문제를 해결해보자

   (2) @Transactional을 사용해서 스프링이 제공하는 AOP 기능을 사용하면 Service의 프록시 객체가 생성됨

   (3) 트랜잭션 AOP는 PlatformTransactionManager를 이용해서 만들고 스프링이 프록시 객체를 만들기 때문에, 스프링 빈으로 등록해야 함

   (4) 이제 순수한 비즈니스 로직만 남길 수 있음

 

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

   (1) 스프링 부트는 DataSource와 트랜잭션 매니저를 자동으로 등록해줌

   (2) 우리는 application.properties에 url, username, password를 쓰면, 스프링 부트는 기본적으로 Hikari 데이터소스를 만들고, 이를 이용해서 트랜잭션 매니저를 생성해서 스프링 빈으로 등록함


이펙티브 자바 - 아이템 5 : 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

- 많은 클래스는 하나 이상의 다른 자원(클래스)에 의존함

- 예를 들어 맞춤법 검사기는 사전에 의존하는데, 이런 클래스를 정적 유틸리티 클래스 또는 싱글턴으로 구현한 경우가 있음

- 하지만, 두 방식 모두 단 하나의 사전만 사용할 수 있음. 실전에서는 사전이 언어 별로 있고, 특수 어휘용 사전 등 다양한 사전이 적용될 수 있음

- 따라서, 사용하는 사전에 따라 동작이 달라지는 맞춤법 검사기를 제공해야 함

- 이렇게 사용하는 자원에 따라 동작이 동적으로 달라지는 클래스는 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식이 적절함

- 생성자에 넘겨줄 수도 있고, Supplier<T>를 넘겨주는 방법도 있음


코테준비

한 두문제 푼거같은데... 정리할게...!!! 지금 생각나는건 Stream을 더 익혀보자


오늘 하루도 고생했다!!!

'TIL(Today I Learned)' 카테고리의 다른 글

2023.06.30  (0) 2023.06.30
2023.06.29  (0) 2023.06.30
2023.06.27  (0) 2023.06.27
2023.06.26  (0) 2023.06.26
2023.06.23  (0) 2023.06.23

+ Recent posts