토비의 스프링 [5.3장 - 5장 마무리] 스터디

WonJune Kang·2021년 9월 15일
0

toby-spring

목록 보기
8/8

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

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

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

그림은 수직적 의존관계 Application - Service Abstract - Tech Service
수펑적 의존 관계를 잘 나타내준다.
UserService는 순수하게 사용자 관리 업무 비즈니스 로직을 담고 있다.
UserDao는 데이터를 어떻게 가져오고 등록할 것인가에 대한 데이터 액세스 로직을 담고 있다.
UserDao <- -> UserService 는 인터페이스와 DI를 통해 연결됨으로써 결합도가 낮아졌다.
UserDao는 DB 연결을 생성하는 방법에 대해 독립적이다.
UserService와 트랜잭션 기술과의 관계 또한 PlatformTransactionManager 인터페이스를 통해 결합도가 낮아졌다.
각각의 모듈들은 서로 최대한 영향을 주지 않고 자유롭게 확장될 수 있는 구조를 띄고 있다.

단일 책임 원칙 Single Responsibility Principle

단일 책임 원칙은 하나의 모듈은 한 가지 책임을 가져야 한다는 의미이다.
하나의 모듈이 바뀌는 이유는 한 가지여야 한다고 설명할 수도 있다.

예를 들어 UserService에 JDBC Connection 메소드를 직접 사용하는 트랜잭션 코드가 들어 있을 때, UserService는 비즈니스, 트랜잭션 두가지의 책임을 가지게 되므로 단일 책임 원칙에 위배된다. 하지만, PlatformTransactionManager인터페이스가 트랜잭션을 관리하도록 위임해줌으로써 UserService는 온전히 비즈니스적인 요소에만 신경쓸 수 있게 되었다.

단일 책임 원칙의 장점

단일 책임원칙을 잘 지키고 있다면, 어떤 변경이 필요할 때 수정 대상이 명확해진다.
기술이 바뀌면 기술 계층과의 연동을 담당하는 기술 추상화 계층 설정만 바꿔주면 되고, 애플리케이션의 요구사항이 변경될 경우 온전히 비즈니스적인 요소 또는 데이터 액세스 요소만 명확하게 수정하면 된다.

5.4. 메일 서비스 추상화

5.4.1. JavaMail을 이용한 메일 발송 기능

사용자 정보에 이메일을 추가하는 일은 레벨을 추가했을 때와 동일하게 진행한다.
!! 테스트 요소: User 생성자가 email을 받을 수 있게 하고, 테스트 데이터를 맞게 준비한 뒤, 등록, 수정, 조회에서 email 값이 잘 처리되는지 확인할 수 있도록 UserDaoTest를 수정한다.

JavaMail 메일 발송

자바에서 메일을 발송할 때는 표준 기술인 JavaMail을 사용한다.

upgradeLevel 메소드에 수정사항 추가

JavaMail을 사용하는 전형적인 코드

5.4.2. JavaMail이 포함된 코드의 테스트

이때까지 웹 개발을 하면서 나는 실제 로컬에 서버를 구동하고, 브라우저 상에서 수정/개발된 해당 기능을 확인하려고 눈으로 직접 테스트를 진행하였다.
하지만 눈보다 정확한 것은 테스트 코드를 작성하여 프로그램적으로 확인하는 방법이 더 정확하므로 가능한한 모든 테스트는 테스트코드를 작성하는 것을 생각하고 코딩을 해야할 것 같다.
JavaMail은 이미 충분히 많은 사람들에 의해 검증된 API이므로 실제 메일 서버에서 전송하여 부하를 주거나 메일받아 눈으로 확인 하는 방법보다 JavaMail을 사용할 때와 동일한 인터페이스를 갖는 코드가 동작하도록 하여 테스트를 진행하는 것도 괜찮아 보인다.

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

JavaMail은 구조상 확장이나 지원이 불가능하도록 만들어진 API이므로 스프링이 제공하는 메일 서비스 추상화 인터페이스를 사용하여 테스트를 진행 및 JavaMail로 구현된 소스를 리팩토링 하도록 한다.

메일 발송 기능 추상화


이 인터페이스는 SimpleMailMessage라는 인터페이스를 구현한 클래스에 담긴 메일 메시지를 전송하는 메소드로만 구성되어 있다.
기본적으로는 JavaMail을 사용해 메일 발송 기능을 제공하는 JavaMailSenderImpl 클래스를 이용하면 된다.

복잡한 JavaMail API보다는 간결해졌지만, 아직 테스트 오브젝트로 대체할 수는 없을 것 같다.
이유: 코드 내에서 JavaMailSenderImpl을 구현한 클래스로 직접 오브젝트를 생성하고 있고, mail을 전송해주는 객체를 설정해주는 부분과 메시지를 내용을 작성하는 부분이 하나의 메소드 내에서 이뤄지고 있기 때문이다.
추후에 mail 전송 객체의 설정을 바꿔야 할 경우, 메일을 전송하는 모든 메소드에게서 해당 설정을 바꿔줘야하는 불필요한 수고를 해야 할 수도 있다.

위 코드를 좀 더 간결하고, 메시지 작성과 전송에만 신경쓸 수 있도록 스프링 DI를 적용하여 구현해보자

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

MailSender 인터페이스를 구현한 테스트용 DummyMailSender 클래스를 만든다.

테스트와 서비스 추상화

서비스 추상화는 트랜잭션과 같이 기능은 유사하나 사용 방법이 다른 로우레벨의 다양한 기술에 대해 추상 인터페이스와 일관성 있는 접근 방법을 제공해주는 것.

JavaMail의 경우 테스트가 어려운 API 이므로 실제 메일을 전송하는 구현 클래스를 더미 전송 구현 클래스로 바꿔치기하여 UserService 코드를 수정하지 않고 테스트를 진행할 수 있다.

5.4.4. 테스트 대역

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

UserDao/UserService를 테스트할 때 운영/테스트를 구분하는 property 혹은 자바 configure를 이용하여 아래 그림과 같이 구성할 수 있다.

테스트 대역의 종류와 특징

테스트 대역Test Double은 테스트 환경을 만들어주기 위해, 테스트 대상이 되는 오브젝트의 기능에만 충실히 수행하면서, 빠르게, 자주 테스트를 실행할 수 있도록 사용하는 오브젝트를 통틀어 지칭한다.

테스트 스텁Test Stub
테스트 대상 오브젝트의 의존객체로서 존재하면서 테스트 동안 코드가 정상적으로 수행할 수 있도록 돕는 것
ex) DummyMailSender

테스트 스텁은 단순히 호출만 하고 그만인 경우도 있지만, 실행 후 결과를 반환받아 처리해야 하는 경우도 있다. 또는 어떤 스텁은 메소드를 호출하면 강제로 예외를 발생시키게 해서 테스트 대상 오브젝트가 예외상황에서 어떻게 처리하면 좋을지 사용될 수 있다. 이런 경우, 단순 assertThat으로 검증하는 것만으론 힘들다.

Mock Object 활용

목 오브젝트는 스텁처럼 테스트 오브젝트가 정상적으로 실행되도록 도와주면서, 테스트 오브젝트와 자신(테스트 대상 오브젝트) 사이에서 일어나는 커뮤니케이션 내용을 저장해뒀다가 테스트 결과를 검증하는데 활용할 수 있게 해준다.

Mock Object 사용

테스트 스텁인 DummyMailSender를 대체하여 McokMailSender 클래스를 구현해 테스트에 이용한다.

위와 같이 구성하면, McokMailSender.send(mailMessage); 메소드 호출 후 MockMailSender의 static member variable인 requests에 메일 주소가 추가되게 된다. send 메소드를 호출한 후, 검증 작업에서 실제 메시지를 세팅한 값과 send 처리된 값 사이의 정합성을 검증할 수 있게 된다.

Mock Object를 사용하여 테스트를 진행하면 단순 요청, 호출 후 반환된 값에 대한 검증 등 테스트 진행에 있어 매우 용이하게 진행할 수 있다.

참고 MockMvc Object
MockMvc 개념 및 사용법
Spring MockMvc Reference API

profile
Excelsior!

0개의 댓글