토비의 스프링 Ver5

존스노우·2021년 11월 19일
0

스프링

목록 보기
5/22

서비스 추상화

사용자 레벨 관리 기능 추가

비즈니스 로직 추가.

필드추가

Level 이늄 (이넘아닌가??)

코드 형식으로 단순하게 만들어 클래스 1 / 2 /3 등급으로 만들어도 되지만

불안전하고 변수?가 많이 일어날듯.

User 필드 추가

UserDaoTest 테스트 수정

UserDaoJdbc

사용자 수정 기능 추가

수정 기능 테스트 추가

UserDao 와 UserDaoJdbc 수정

수정 테스트 보완

SQL 문장에서 에러가 나는 경우가 가장 많으니 확인.. 테스트 검증이 필요한 이유이다.

실패한 테스트를 만들어보자.

UserService.upgradeLevels()

UserDao의 인터페이스타입인 UserService DI 주입 생성

UserService 클래스 빈 등록

UserServiceTest 테스트 클래스

upgradeLevels() 메소드

로직을 추가하자

upgradeLevels() 테스트

UserService.add()

처음가입 BASIC 은 어디다 담아야 될까?

비즈니스적 성격을 지닌것은 따로 분리해보자.

UserService에 비즈니스로직을 담당하니 여기다.

코드 개선

  1. 코드의 중복된 부분은 없는가?

  2. 코드가 무엇을 하는 것인지 이해하기 불편하지 않은가?

  3. 코드가 자신이 있어야 할 자리에 있는가?

  4. 앞으로 변경이 일어난다면 어떤 것이 있을 수있고, 그 변화에 쉽게 대응할 수 있게 작성 되어 있는가?

    upgradeLevels() 메소드 코드의 문제점

    if/else 가 너무 많음..

upgradeLevels() 리팩토링

메소드 분리 방법을 이용해서 좀더 코드를 간결하게

각자의 역할이 한꺼번에 있으면 코드가 읽기가 너무 불편해진다.

메소드 분리를 통해서 역할을 나누자.

throws 던지면 밑에 엘스는 생략해도 될듯

코드를 개선하면서 오브젝트와 메소드가 각각 자기 몫의 책음을 맡아 하는
구조로 변경됨..

객체지향코드 -> 다른 오브젝트의 데이터를 가져와서 작업대신
-> 데이터를 가지고 있는 다른 오브젝트에게 작업 해달라고 요청

get 함수를 지양하는 것과 같은거 같다.

오브젝트에게 데이터를 요구하지말고 작업 을 요구해라!

User 테스트

기능이 변경되면서 수정 되는 코드 테스트

상수를 씀으로서 공통 숫자도 중복코드를 없애준다.

상수를 씀으로서 어떤 의도가 담겨있는지 명확해진다.

트랜잭션 서비스 추상화

사용자 레벨 조정 작업은 중간에 문제가 발생해서 작업이 중단 된다면

그때까지 진행된 변경 작업 모두 취소하도록 결정 .

모 아니면 도

테스트 시나리오

5명의 사용자 정보 DB에 넣기 -> 작접 중간 예외 발생

UserService 대역을 만들자

테스트코드 내부에 스태틱으로 대역을 만들자

테스트 코드내 예외로 만들자

테스트 결과

테스트 실패의 원인

트랜잭션 때문에 이런 결과가 발생

모든 사용자의 레벨을 업그레이드 하는 작업인

upgradeLevels() 메소드가 하나의 트랜잭션 안에서 동작하지 않기 때문

트랜잭션의 핵심속성인 원자성 때문..

트랜잭션 경계설정

트랜잭션 롤백 A 작업은성공했는대 B작업이 실패? 다시 전부 취소해줘야됨.

트랜잭션의 트랜잭션 경계설정

시작은한가지 / 끝은 두가지 방법

모든 작업을 무효화하는 롤백 / 모든 작업을 확정하는 커밋

트랜 잭션이 시작되고 끝나는 위치를 트랜잭션 경계라고 부름

setAutoCommit(false) 하나의 트랜잭션으로 묶어주기위해

트랜잭션의 경계설정 : 트랜잭션 시작선언하고 커밋 롤백 종료작업
로컬 트랜잭션 : 하나의 DB 커넥션 안에서 만들어지는 트랜잭션

UserService / UserDao의 트랜잭션 문제

트랜잭션 경계설정 코드를 사용하지 않았다.

JDBC 템플릿에 메소드 호출 한 번에 한 개의 DB 커넥션이 만들어지고

닫히는 일까지 일어나는 것 때문에.

결국 JDBC 템플릿 메소드를 사용하는 UserDao는

각 메소드마다 하나씩 독립적인 트랜잭션으로 실행될 수 밖에없다.

하나의 트랜잭션을 만드려면?

비즈니스 로직 내의 트랜잭션 경계설정

UserService 에 경계작업 설정을 해야된다.

순수 데이터 액세스 로직은 UserDao에 둬야 함으로..
대신 upgradeLevels() 메소드 커넥션을 사용해야 된다.
그래야 하나의 트랜잭션안에서 커밋 / 예외시 롤백이 되기때문이다

UserService 는 Dao update를 직접 요청하지 않는대

Connection c 넘겨줘야됨 .

UserService 트랜잭션 경계설정의 문제점

문제점

  1. JdbcTemplate 활용을 못함

  2. Connection 파라미터를 추가해야됨..

  3. Connection 파라미터가 UserDao 메소드에 추가되면 더이상 액세스기술이 독립적이지 않다.

  1. 테스트 코드도 다시 신경써야됨..

트랜잭션 동기화

스프링 기능을 사용해보자!

Conection 파라미터제거

트랜잭션 동기화 방식 : Connection 오브젝트를 특별한 저장소에 보관해두고

이후에 호출되는 DAO의 메소드 에서 저장된 Connection을 가져다가 사용하게 함

정확히는 DAO가 사용하는 JdbcTemplate이 트랜잭션 동기화 방식을 이용하는것

트랜잭션 동기화 적용

문제점이 하나있따. 멀티스레드 환경에서도 안전한 트랜잭션 동기화 방법 구현하는 일이

기술적으로 간단하지 않지만 JdbcTemplat과 더불어 이런 트랜잭션 동기화 기능을

지원하는 간단한 유틸리티 메소드가 있다.

트랜잭션 테스트 보완

JdbcTemplate과 트랜잭션 동기화

Jdbctemplate의 동작방식?

알아서 커넥션 실행 해줌.

트랜잭션 이 굳이 필요없다면 JdbcTemplate만 호출해주면됨..

트랜잭션 서비스 추상화

기술과 환경에 종속되는 트랜잭션 경계설정 코드

새로운 문제.

여러개의 DB를 사용할 경우?

로컬 트랜잭션 방식은 한개의 디비에 종속되기 때문에 적합X

글로벌 트랜잭션 방식을 적용해야 한다.

JMS: 메시지 관리?

글로벌 트랜잭션을 지원하는 트랜잭션 매너저를 지원하기위한 API
JTA 제공

트랜잭션은 트랜잭션매니저가 관리하도록 한다.

코드구조가 비슷해져버림 이러면 소용이..

여기서 또 한가지 추가 요청사항 하버이네이트로 USERDAO 구현

트랜잭션 API의 의존관계 문제와 해결책

추상화를 통해 문제를 해결해보도록 하자

스프링 트랜잭션 서비스 추상화

PlatformTransactionManger로 시작한 트랜잭션은 트랜잭션 동기화 저장소에 저장됨 ..

DataSourceTransactionManager 오브젝트는 JdbcTemplate에서 사용 할수 있는 방식으로 트랜잭션 관리해줌

트랜잭션 기술 설정의 분리

DataSourceTransactionManager -> JTATransactionManager 로 바꾸면
JTA 글로벌 트랜잭션으로 변경됨..

어떤 트랜잭션 매니저 구현 클래스를 사용할지 문제에서 DI 에 또 위반된다.

컨테이너에서 외부에 제공받게 스프링 DI로 바꿔줘야됨..

DataSourceTransactionManager 스프링 빈 등록

여기선 수정자 메소드를 통해 주입..

서비스 추상화와 단일 책임 원칙

수직 수평 계층구조와 의존관계

기술과 서브시에 추상화 기법을 이용하면 특정 기술환경에 종속되지않는 포터블한 코드를 만들 수 있음.

트랜잭션 추상화는 수평적인 분리와는 다름

애플리케이션의 비즈니스 로직과 그 하위에서 동작하는 로우레벨의 트랜잭션 기술이라는 아예

다른 계층의 특성을 갖는 코드를 분리한 것이다.

단일 책임 원칙

하나의 모듈은 한 가지 책임을 가져야 한다는 의미.
하나의 모듈이 바뀌는 이유는 한 가지여야 한다.

단일 책임 원칙의 장점

  1. 어떤 변경이 필요할 때 수정 대상이 명확해진다.
    기술이 바뀌면 기술계층과의 연동을 담당하는 기술 추상화 계층의 설정만 바꿔주면 된다.

    적절한 책임과 관심이 다른 코드를 분리하고 서로 영향을 주지 않도록
    다양한 추상화 기법을 도입하고, 애플리케이션 로직과 기술/환경을 분리하는 등의
    작업은 갈수록 복잡해지는 엔터프라이즈 애플리케이션이 반드시 필요하다.
    이를 위한 핵심적인 도구가 DI 다.

메일 서비스 추상화

새로운 요청사항
레벨이 업그레이드 되는 사용자에게 안내 메일 발송

  1. 이메일 정보 관리.
  2. UserService의 upgradeLevel() 메소드에 메일 발송 기능 추가

JavaMail을 이용한 메일 발송 기능

JavaMail 발송

![]

JavaMail이 포함된 코드의 테스트

하지만 메일 서버가 준비되어 있지 않다면?

예외가 발생할 수 있다. 그리고 매번 메일을 보내면 서버에도 부담이 심함.

메일 테스트를 할때 JavaMail을 통해 메일 서버까지만 메일이 잘 전달됐으면 여기 까지 확인

테스트를 위한 서비스 추상화

JavaMail을 이용한 테스트의 문제점

JavaMail의 핵심 API에 DataSource 처럼 인터페이스로 만들어져서 구현을 바 꿀 수 있는게

없음

session 인터페이스가 아니라 클래스다 그리고 프라이빗으로 되잇어서 직접 생성 불가능.

스태틱메소드를 생성해 오브젝트를 만드는것이 유일한 방법

그리고 Session fianl이라 상속이 불가능

MilMessage / Trahnsport 도마찬가지

해답은 서비스 추상화

메일 메시지 전송하는 메소드로 구성.

테스트용 메일 발송 오브젝트

JavaMail 사용하지않고 메일 발송기능 포함된 테스트 코드를 작성함.

메일 전송기능을 추상화해서 인터페이스를 적용하고 DI를 통해 빈으로 분리했음

테스트와 서비스 추상화

서비스 추상화 : 사용방법이 다른 로우레벨의 다양한 기술에 대해 추상 인터페이스와 일관성 있는
접근 방법을 제공해주는 것을 말한다.

DI를 함으로서 JavaMail이 아닌 다른 메시징 서버의 API 도 유연적으로 사용할 수 있다.

테스트 대역

DummymailSender 아직 아무것도 구현안됐지만 JavaMail 클래스를 대치하는 역할을함

테스트용으로 따로 설정을해두면 오브젝트를 DI로 바꿔치기해 편리하게 사용가능하기 때문

의존 오브젝트의 변경을 통한 테스트 방법

UserDao 테스트

테스트 만의 DI를 함으로 좀더 효과적임

테스트 대역의 종류와 특징

테스트 환경의 오브젝트들 MailSender 인터페이스를 구현한 것들이라던가..

테스트 스텁: 위 에 오브젝트들을 테스트 애역이라고하고 대표적인 테스트 대역은 테스트 스텁이다.

테스트 대상 오브젝트의 의존객체로서 존재하면서 테스트 동안에 코드가 정상적으로 수행 할 수 있도록

돕는 것을 말함.

테스트 대상 오브젝트의 메소드가 돌려주는 결과뿐 아니라 테스트 오브젝트가 간접적으로
의존 오브젝트에 넘기는 값과 그 행위 자체에 대해서도 검증하고 싶다면?

이런 경우에는 테스트 대상의 간접적인 출력과 결과를 검증하고 테스트 대상 오브젝트와 의존 오브젝트 사이에서 일어나는 일을 검증할 수 있도록 특별히 설계된 목 오브젝트를 사용 한다.

목 오브젝트는 스텁처럼 테스트 오브젝트가 정상적으로 실행되도록 도와주고
테스트 오브젝트와 자신 사이에서 일어나는 커뮤니케이션 저장해줬다
검증하는 과정에서 활용할 수 있게해줌

목 오브젝트를 이용한 테스트

UserServiceTest를 활용해보자..

DummiyMailSender 대신 새로운 MailSender 클래스 생성

정리

  1. 비즈니스 로직을 담은 코드는 데이터 액세스 로직을 담은 코드와 깔끔하게 분리되는 것이다
    비즈니스 로직 코드 또한 내부적으로 책임과 역할에 따라서 깔끔하게 메소드로 정리돼야 한다.

  2. 이를 우해서 DAO의 기술 변화에 서비스 계층의 코드가 영향을 받지 않도록 인터페이스와
    DI를 잘 활용해서 결합도를 낮춰줘야한다.

  3. DAO를 사용하는 비즈니스 로직에는 단위 작업을 보장해주는 트랜잭션이 필요하다

  4. 트랜잭션의 시작과 종료를 지정하는 일을 트랜잭션 경계설정이라고 한다.
    트랜잭션 경계설정은 주로 비즈니스 로직 안에서 일어나는 경우가 많다.

  5. 시작된 트랜잭션 정보를 담은 오브젝트를 파라미터로 DAO에 전달하는 방법은
    비효율 적이기 때문에 스프링이 제공하는 트랜잭션 동기화 기법을 활용하자.

  6. 자바에서 사용되는 트랜잭션API의 종류와 방법은 다양함. 환경과 서버에 따라 트랜 잭션방법이
    변경되면 경계설정 코드도 함께 변경돼야 한다.

  7. 트랜잭션 방법에 따라 비즈니스 로직을 담은 코드가 변경되면 단일 책임 원칙에 위배 되며
    DAO가 사용하는 특정 기술에 대해 강한 결합을 만들어낸다.

  8. 트랜잭션 경계설정 코드가 비즈니스 로직 코드에 영향을 주지 않게 하려면 스프링이 제공하는
    트랜잭션 서비스 추상화를 이용한다.

  9. 서비스 추상화는 로우레벨의 트랜잭션 기술과 API의 변화와 상관없이 일관된 API를 가진
    추상화 계층에 도입한다.

  10. 서비스 추상화는 테스트하기 어려운 JavaMail 같은 기술에도 적용할 수 있다. 테스트를 편리하 게 작성하도록 도와주는 것만으로도 서비스 추상화는 가치가 있다.

  11. 테스트 대상이 사용하는 의존 오브젝트를 대체할 수 있도록 만든 오브젝트를 테스트 대역이라고 한다.

12 테스트 ㄷ역은 테스트 대상 오브젝트가 원활하게 동작할 수 있도록 도우면서 테스트를 위한 간접적인 정보를 제공해 준다.

  1. 테스트 대역중에서 테스트 대상으로부터 전달받은 정보를 검증할 수 있도록 설계된 것을

    목 옵젝트라고 한다.

profile
어제의 나보다 한걸음 더

0개의 댓글