문제해결하기 - DI
상속을 피하기 위해 이번시간엔 DI (Dependency Injection)를 통해 문제를 해결해보려고 합니다.
제일 먼저 바꿀것은 BoardService 입니다.
전체적으로 바꿀 구조는 아래와 같습니다.
(DI로 구조를 변경)
BoardService 인터페이스를 BoardServicePerformance와 BoardServiceImpl이 구현하였습니다.
대신 집중해야할 로직인 Board 리스트를 조회하는 것은 BoardServiceImpl이 담당하고,
기타옵션인 수행시간 측정은 BoardServicePerformance가 구현하되, 그 과정에서 BoardServiceImpl을 주입(Injection) 받도록 하였습니다.
이렇게 되면 BoardServiceImpl과 BoardServicePerformance는 느슨한 관계를 가지며, Board 리스트를 조회라는 기능은 BoardServiceImpl만 보면 되는 구조가 될 수 있습니다.
이를 직접 코드로 구현해보겠습니다.
BoardService.java
public interface BoardService {
List<Board> getBoards();
}
BoardServicePerformance.java
@Service
@Primary
public class BoardServicePerformance implements BoardService{
@Autowired
@Qualifier("boardServiceImpl")
private BoardService boardService;
@Override
public List<Board> getBoards() {
long start = before();
List<Board> boards = boardService.getBoards();
after(start);
return boards;
}
private long before() {
return System.currentTimeMillis();
}
private void after(long start) {
long end = System.currentTimeMillis();
System.out.println("수행 시간 : "+ (end - start));
}
}
BoardServiceImpl.java
@Service
public class BoardServiceImpl implements BoardService{
@Autowired
private BoardRepository repository;
@Override
public List<Board> getBoards() {
return repository.findAll();
}
}
지금부터는 테스트코드를 작성해서 테스트를 해보겠습니다.
지속적으로 결과를 확인하는데 있어서 브라우저에서 URL을 입력하며 확인하는 방식은 불편함이 많습니다.
테스트 코드를 통해 좀 더 수월하고 자동화된 테스트를 진행하겠습니다.
ApplicationTests.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Autowired
private BoardService boardService;
@Test
public void findBoards() throws Exception {
assertThat(boardService.getBoards().size()).isEqualTo(100);
}
}
자 이렇게 테스트 코드를 작성후에 코드가 잘 돌아가는지 확인해보겠습니다.
정상적으로 수행시간과 테스트결과가 통과되는 것을 확인하였습니다.
BoardService와 마찬가지고 UserService도 같은 구조로 코드를 작성해보겠습니다.
UserService.java
public interface UserService {
List<User> getUsers();
}
UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserRepository repository;
@Override
public List<User> getUsers() {
return repository.findAll();
}
}
UserServicePerformance.java
@Service
@Primary
public class UserServicePerformance implements UserService{
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
@Override
public List<User> getUsers() {
long start = before();
List<User> users = userService.getUsers();
after(start);
return users;
}
private long before() {
return System.currentTimeMillis();
}
private void after(long start) {
long end = System.currentTimeMillis();
System.out.println("수행 시간 : "+ (end - start));
}
}
테스트를 위해 테스트 코드도 추가해보겠습니다.
ApplicationTests.java
@Autowired
private UserService userService;
@Test
public void findUsers() throws Exception {
assertThat(userService.getUsers().size()).isEqualTo(100);
}
테스트 코드를 작성후 실행해보면!
정상적으로 잘 되는 것을 확인할 수 있습니다.
기능은 정상적으로 잘되지만 코드가 깔끔하지 않고, 많은 관계가 필요한 상태입니다.
만약 getXXX외에 다른 메소드에도 이와 같은 수행시간출력 기능이 포함되어야된다면 코드는 어떻게 될까요?
Board와 User외에 다른 타입의 Service에 수행시간출력이 필요하면 어떻게 될까요?
실제 가장 중요한 비지니스 로직외에 부가 기능들에 대해서는 신경 쓰지 않도록 하려면 어떻게 해야할까요??
이와 비슷한 경우로 메소드 실행전에 Connection을 open하고, 메소드가 정상적으로 실행완료 되면 commit을,
예외 발생시엔 rollback을 처리하도록 하는 트랜잭션은 어떻게 처리되고 있기에 개발자가 비지니스 로직만 작성하면 될까요?
이 의문에 대답하기 위해 AOP에 대해 학습을 시작해보겠습니다.