본문 바로가기
챕터정리방

[5장] 서비스 추상화

by Jake.. 2021. 12. 26.

 

5장에서는 지금까지 만든 DAO에 트랜잭션을 적용해보면서 스프링이 어떻게 성격이 비슷한 여러 종류의 기술을 추상화하고 이를 일관된 방법으로 사용할 수 있도록 지원하는지를 살펴보자.

 

추상화란 하위 시스템의 공통점을 뽑아내서 분리시키는 것을 말한다. 그렇게 하면 하위 시스템이 어떤 것인지 알지 못해도, 또는 하위 시스템이 바뀌더라도 일관된 방법으로 접근할 수가 있다.

 

 

사용자 레벨 관리 기능 추가

UserDao는 User 오브젝트에 담겨 있는 사용자 정보를 등록, 조회, 수정, 삭제하는 일명 CRUD 기능만 가능하다.

 

여기에 간단한 비즈니스 로직을 적용하여 보자.

 

사용자의 로그인 횟수등에 따라 레벨(BASIC, SILVER, GOLD)을 부여하여 관리할 수 있도록 한다.

JAVA5 이상에서 사용 가능한 enum을 이용하여 레벨을 나타내는 매직넘버를 상수로 추출하여 개발하도록 하였다.

레벨별 체크 로직의 단순 조건식을 메소드로 추출하여 객체별 역할을 분리하였다.

 

 

트랜잭션 서비스 추상화

트랜잭션이란 더 이상 나눌 수 없는 단위의 작업을 말한다.

작업을 쪼개서 작은 단위로 만들 수 없다는 것은 핵심 속성인 원자성을 의미한다.

 

트랜잭션이 성공한다면 커밋을, 중간에 예외가 발생해 작업을 완료할 수 없다면 아예 작업이 시작되지 않은 것처럼 초기

상태로 롤백해야 한다.

 

DB 자체는 완벽한 트랜잭션을 제공한다.

그러나 서비스 메서드에서 여러 개의 쓰기 작업 쿼리를 날리는 경우도 하나의 트랜잭션으로 동작해야 한다.

 

트랜잭션은 Connection 객체를 통해 시작과 종료 등 경계를 지정할 수 있다.

Jdbc는 기본적으로 하나의 작업이 마무리되면 자동으로 커밋이 적용된다.

따라서 여러 DB 작업이 하나의 트랜잭션으로 묶이지 않는다.

따라서 connection.setAutoCommit(false)로 트랜잭션 시작을 선언하며, commit()이나 rollback()을 호출하여 종료를 선언

한다.

 

이러한 작업을 트랜잭션의 경계설정이라 한다.

트랜잭션의 경계는 하나의 Connection이 만들어지고 닫히는 범위에만 존재한다.

이렇게 하나의 DB 커넥션 안에서 만들어지는 트랜잭션을 로컬 트랜잭션이라고도 한다.

 

결과적으로 JDBCTemplate 는 커넥션 매개변수를 넘겨서 공통으로 사용하지 않는 한 하나씩 독릭접인 트랜잭션으로 수행되기 때문에 DB의 여러 작업을 하나의 트랜잭션으로 묶기 어렵다.

 

이러한 문제를 트랜잭션 동기화를 통해 해결할 수 있다. 트랜잭션을 시작하기 위해 만든 Connection 오브젝트를 특별한 저장소에 보관해두고, 이후에 호출되는 Dao의 메소드에서는 저장된 Connection을 가져다가 사용하게 하는 것이다

 

 

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

UserDao와 UserService는 인터페이스와 DI를 통해 연결됨으로써 결합도가 낮아졌다

서로 독립적으로 확장될 수 있다. DI의 가치는 이렇게 관심, 책임, 성격이 다른 코드를 깔끔하 분리하여 영향도를 적게 만들어서 기능 확장을 더 쉽게 만들어준다. 

 

단일 책임 원칙이란 하나의 모듈은 한 가지 책일만 가져야 한다는 의미다. 트랜잭션 기술이 JDBC에서 JTA 등으로 변경된다고 하여도 비즈니스 로직부분의 UserService를 수정하지 않고 트랜잭션 추상화를 통한 DI를 통해 수정 대상이 명확한 각자의 역할을 맡은 부분의 오브젝트만 변경을 하면 된다.

 

 

메일 서비스 추상화

레벨 관리 비즈니스 로직에 이어 추가 요구사항으로 레벨이 업그레이드 되는 사용자에게 안내 메일 발송 기능을 추가해보자.

 

자바에서 메일 발송 표준 기술인 JavaMail을 통하여 메일서버만 구축되어 있다면, 메일서버를 통해 메일 전송이 가능하다. 

 

그러나 테스트코드를 짜는 경우 메일서버를 매번 연동하며 전송하는 것은 시간도 오래 걸리고 메일서버도 정상적으로

운영되고 있어야 테스트가 되므로 비효율적이다.

 

실제 메일 전송을 수행하는 JavaMail 대신에 테스트에서 사용할,  JavaMail과 같은 인터페이스를 갖는 목 오브젝트를 만들어서 사용하면 문제는 모두 해결된다.

 

목 오브젝트와 테스트 스텁을 통해 입력값과 결과값을 목으로 지정하여 원하는 테스트를 쉽게 할 수 있다.

 

테스트 스텁이란 테스트 대상 오브젝트의 의존객체로서 존재하면서, 테스트 동안에 코드가 원활하게 동작하도록 돕는 것이다.

 

오브젝트를 교체하여 테스트 하기 위해 테스트용 오브젝트로 교체하기 위해 메일 발송 기능을 추상화 한다.

 

-JavaMail의 서비스 추상화 인터페이스

package org.springframework.mail;
...
public interface MailSender {
    void send(SimpleMailMessage simpleMessage) throw MailException;
    void send(SimpleMailMessage[] simpleMessages) throw MailException;
 }

 

정리
  • 비즈니스 로직을 담은 코드는 데이터 엑세스 로직을 담은 코드와 깔끔하게 분리되는 것이 바람직하다. 비즈니스 로직 코드 또한 내부적으로 책임과 역할에 따라서 깔끔하게 메소드로 정리돼야 한다.
  • 이를 위해서는 DAO의 기술 변화에 서비스 계층의 코드가 영향을 받지 않도록 인터페이스와 DI를 잘 활용해서 결합도를 낮춰줘야 한다.

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

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

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

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

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

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

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

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

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

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

  • 테스트 대역 중에서 테스트 대상으로부터 전달받은 정보를 검증할 수 있도록 설계된 것을 목 오브젝트라고 한다.

 

'챕터정리방' 카테고리의 다른 글

[6장] AOP  (0) 2022.01.03
[5장] 서비스 추상화  (0) 2021.12.29
[4장] 예외  (0) 2021.12.21
[3장] 템플릿  (0) 2021.12.17
[2장] 테스트  (0) 2021.12.11

댓글