DB-스프링과 문제해결

Shaun·2022년 9월 3일
1

DB

목록 보기
4/5
post-thumbnail

애플리케이션 구조

  • 보통 애플리케이션 구조는 크게 Controller/Service/Repository 로 나뉜다.

  • 우리가 눈여겨 볼곳은 Service단인데 Service단은 주로 핵심 비즈니스 로직이 들어있는 곳이다. 또한 데이터 저장 기술을 변경해도, 비즈니스 로직은 최대한 변경없이 유지해야 한다.

  • 이렇게 만들려면 서비스 계층을 특정 기술에 종속적이지 않게 하여야 한다.

=> 서비스 계층은 가급적 비즈니스 로직만 구현하고 특정 구현 기술에 직접 의존해서는 안된다. 이렇게 하면 향후 구현 기술이 변경될 때 변경의 영향 범위를 최소화 할 수 있다.

문제점

  • 트랜잭션은 비즈니스 로직이 있는 서비스 단에서 하는것이 좋다. 하지만 대략적으로만 봐도 트랜잭션을 사용하기위한 JDBC 기술들 (Connection/DataSource/SqlException/) 에 너무 의존하고 있는게 보인다.

  • 어쩌면 비즈니스 로직보다 트랜잭션 처리를 위한 JDBC기술들이 더 많아보여 주객전도가 된 느낌이다.

  • 나중에 데이터접근 기술이 변경이되면 JDBC기술들 또한 다 바꿔야하며 그에따른 비즈니스 로직도 함께 수정하는 문제가 발생한다. 즉 유지보수가 어렵다는 문제점이 발생한다.

1.서비스 계층은 순수해야한다
2.트랜잭션 동기화 문제
3.트랜잭션 반복문제 (try,catch,finally)

문제해결 - 스프링(트랜잭션 추상화)

  • 데이터 접근기술을 변경하면 그에 따라 서비스 로직도 수정해줘야 되는 문제가 발생한다. 트랜잭션 추상화로 이문제를 해결 해주자

  • 이제 서비스단은 특정 트랜잭션 기술에 의존하는게 아니라 추상화된 인터페이스에 의존한다. 이제 원하는 구현체를 갈아 껴주기만 하면됀다. 서비스단은 더이상 수정할 필요없다.(필요한 구현체를 주입)

문제해결 - 스프링(리소스 동기화)

  • 트랜잭션을 사용하기 위해서는 시작부터 끝까지 같은 커넥션을 유지해야 한다. 저번시간에는 파라미터로 같은 커넥션을 넘겼지만 좋지 않은 방법이다. 스프링에서는 어떻게 해결하는지 보자.

  • 스프링은 트랜잭션 동기화 매니저를 제공한다.

  • 간략하게 flow를 설명하면 트랜잭션 매니저에서 dataSource를 통해 커넥션을 획득하고 트랜잭션을 시작한다. 이때 획득한 트랜잭션 커넥션트랜잭션 동기화 매니저에 안전하게 저장한다

  • 이후로 트랜잭션 커넥션이 필요할떄마다 트랜잭션 동기화 매니저에 저장되어있는 커넥션을 가져다가 사용하면 된다.

트랜잭션 매니저 활용

  • 트랜잭션 매니저는 크게 트랜잭션 추상화리소스 동기화를 처리한다.

  • 트랜잭션 동기화 매니저가 관리하는 커넥션이 있으면 그것을 반환하고 없으면 새로운 커넥션을 생성해서 반환.

  • 새로운 커넥션순서는 위에서 설명 햇듯이 DataSource를 통해 커넥션을 획득하고 트랜잭션을 시작한다. 그리고 그 커넥션을 트랜잭션 동기화 매니저에 저장

  • 리소스를 닫을때도 DataSoruceUtils 사용 -> 주의해야할점은 트랜잭션 커넥션은 유지시켜주고 일반 커넥션은 닫아준다.

  • 트랜잭션 매니저를 주입받은뒤 사용해주면된다. 사용법은 사진에서 보듯이 간단하다.

  • 자동커밋으로 바꿔주고, 리소스를 닫는 일들은 트랜잭션 매니저가 알아서 해준다.

트랜잭션 매니저 test-code

  • 트랜잭션 매니저는 dataSource를 통해 커넥션이 필요함으로 파마리터로 넘겨준다. 이 부분과 레퍼지토리에서 커넥션을 가져오는 경우 (DataSourceUtils.getConnection(dataSource)) 를 헷갈리지 말자. 레파지토리에서 가져오는 부분은 이미 트랜잭션 동기화 매니저에 이미 생성된 커넥션을 가져오는 것일뿐

  • JDBC를 사용함으로 트랜잭션 매니저 구현체인 DataSourceTransacitonManaer을 주입한다.

=> 트랜잭션 매니저도 JDBC기술에 의존하지 않게 해주고 많이 편리해 졌지만 매번 try-catch문으로 감싸고 패턴이 같고 결국에는 반복코드이다. 이를 해결해주기위해 나온 템플릿 콜백 패턴을 살펴보자. 스프링에서는 이를 적용하기위해 트랜잭션 템플릿 이라는것을 제공해준다.

트랜잭션 템플릿

  • 트랜잭션 템플릿을 주입받을때 트랜잭션 매니저가 꼭 필요하다.

  • 비즈니스 로직에서 SQLException을 던지기 떄문에 try-catch 처리

  • 커밋하고 롤백하는 코드가 모두 사라졌다. 비즈니스 로직이 정상 수행되면 커밋하고 언체크 예외가 발생하면 롤백한다. 그외의 경우 커밋한다.

=> 많은 코드가 줄고 편리해졌지만 이곳은 서비스단 이기에 되도록이면 순수 서비스코드들만 즉 핵심 비즈니스 로직만 있는것이 좋다.(지금은 트랜잭션코드와 비즈니스 로직이 함께 있다.) 기능들을 분리해야 서로 독릭접이여야 나중에 유지보수 하기가 쉬워진다.

이를 해결하기위해 트랜잭션AOP에 대해 알아보자 AOP에 대한 설명은 AOP 저번에 썼던 글이 있기에 대체한다.

@Transactional/프록시

  • 스프링에서는 @Transactional이라는 애노테이션을 제공해 AOP를 사용해서 트랜잭션을 편리하게 처리하도록 도와준다.

  • 프록시를 사용하면 트랜잭션을 처리하는 객체와 비즈니스 로직을 처리하는 서비스 객체를 명확하게 분리할수 있다.
  • 위의 사진처럼 클라이언트가 요청을 하면 프록시라는 가짜 객체에서 트랜잭션을 시작하고 비즈니스 로직을 호출할때는 실제 서비스를 호출한뒤 다시 프록시에서 트랜잭션을 종료 한다.

  • 프록시와 트랜잭션에 대한 코드는 스프링에서 알아서 짜주고 개발자는 비즈니스 로직만 서비스단에 깔끔하게 남길수있다.

  • 사용법은 간단하게 애노테이션만 붙여주면 됀다.

  • 기존 테스트 코드를 만들때는 내가 직접만들고 조립해서 테스트 하는식 즉 스프링을 전혀 사용하지 않았다. 하지만 스프링AOP 사용시 주의해야할점은 스프링에서 AOP를 제공할때 필요한것들이 따로 컨테이너에 미리 존재 해야한다.

  • @SpringBootTest를 사용해 테스트시 스프링부트에서 컨테이너를 만들도록 생성한다.

  • 그다음 스프링에서 필요한것들을 빈으로 등록해야하며 의존성 주입도 해야한다. @TestConfiguration 은 애노테이션에서을 보면 알수있듯이 필요한 것들을 빈으로 등록해주는 애노테이션이다. 빈으로 등록하고 @Autowired로 의존성을 주입했다. 테스트용 컨테이너, 테스트용 빈, 테스트용 의존성 주입을 따로 해준다고 보면됀다.

스프링부트 자동 리소스 등록

  • 지금까지 데이터 소스나, 트랜잭션 매니저를 직접 등록했지만 사실상 스프링부트 에서 다 자동으로 등록을 해준다. 그래서 스프링부트로 개발을 시작 했던 나에게는 사실상 데이터소스나 이런게 생소했다..

스프링부트

DataSource

  • 스프링부트는 properties에 있는 속성을 사용해서 데이터 소스 자동등록 해준다.
  • 기본 데이터 소스는 히카리
  • url 이 없으면 내장데이터 베이스 (메모리DB) 를 생성하려고 시도함.

트랜잭션 매니저

  • 자동으로 트랜잭션 매니저를 스프링 빈에 등록 해줌.

=> 물론 개발자가 내가 했던것처럼 직접 데이터소스나, 트랜잭션 매니저 생성가능

profile
호주쉐프에서 개발자까지..

0개의 댓글