우아한 테크코스 프리코스를 진행하면서,
인터페이스를 분리하여 쉽게 테스트 할 수 있는 코드를 만들자. 라는 요구사항이 있었다.
오늘은 이에 대해 알아보고자 한다.
인터페이스를 테스트
쉽게 이야기 하면 테스트 코드를 작성할 때 특정 변수에 의존하지 말자는 이야기이다.
인터페이스에 의존하고, 인터페이스의 다형성을 통하여 테스트를 수행하자.
예를 들어
public Order createOrder(List<OrderItem> orderItems, OrderRequest orderRequest) {
OrderItems orderItems = new OrderItems(orderItems);
return Order.of(orderItems, orderRequest.getDate());
}
라는 코드가 있다.
이 코드를 테스트 할 때는 상위로 이동시킨 orderItems
과 orderRequest
에 의존하게 된다.
만약 이 orderItems
과 orderRequest
를 만들어 넘기는 주체와 이 코드의 의존 관계의 거리가 멀어진다면,
결국 이 코드의 응집도는 떨어진다.
그럼 어떻게 개선할 수 있을까?
전략 패턴을 이용하자
전략 패턴은 객체의 행위를 동적으로 변경할 수 있도록 설계하는 디자인 패턴이다.
이 패턴은 알고리즘을 캡슐화하여 런타임에 행동을 변경하거나 대체할 수 있도록 하는 것이 핵심이다.
따라서 저 예시 코드를 한번 수정하면
private final ItemFindService itemFindService;
public Order createOrder(OrderRequest orderRequest) {
List<ItemOrderRequest> requests = orderRequest.getOrderRequests();
ItemInfo items = findItems(requests.stream().map(ItemOrderRequest::getName).toList());
List<OrderItem> orderItemList = createOrderItemList(requests, items);
OrderItems orderItems = new OrderItems(orderItemList);
return Order.of(orderItems, orderRequest.getDate());
}
private ItemInfo findItems(List<String> itemNames) {
return itemFindService.findItems(ItemFindRequest.create(itemNames));
}
로 orderItemList
(여담이지만 변수명과 메서드명은 수정하는 것이 좋아보인다. 변수명에 객체 타입을 드러내지 말자) 를 만들어내는 로직을 메서드 안으로 가져왔다.
아직 부족하다. 전략 패턴을 구현하기 위해서는 createOrder
메서드의 런타임 환경에서 ItemFindService
에 의존하게 된다.
public Order createOrder(OrderRequest orderRequest, ItemFindService itemFindService) {
List<ItemOrderRequest> orderRequests = orderRequest.getOrderRequests();
List<String> orderItemNames = orderRequests.stream().map(ItemOrderRequest::getName).toList();
itemFindService.findItems(ItemFindRequest.create(orderItemNames));
...
return Order.of(orderItems, orderRequest.getDate());
}
물론 여기서 메서드 분리는 불가피 하겠지만,
이제 createOrder
는 직접 ItemFindService
의 구현체에 대한 선택권이 없어졌다.
완전히 item정보를 갖는 동작을 위임하였다.
이를 통해 test 시 직접 service에 대한 mock객체를 만들어 테스트하기 쉬워졌다.
DIP
가만 보니 전략 패턴은 DIP를 성실하게 잘 따른다고 생각했다.
DIP는 Dependency Inversion Principle로, SOLID 중 하나다. 고수준 모듈이 저수준 모듈에 의존하지 않고, 추상화된 인터페이스에 의존하도록 설계하는 것을 의미한다.
- 고수준 모듈: 프로그램의 비즈니스 로직이나 중요한 정책을 구현한 모듈.
- 저수준 모듈: 고수준 모듈에서 사용하는 세부적인 구현체(예: 데이터베이스, 네트워크 통신, 알고리즘 등).
예시에서도 database와 통신하는 행위를 갖는 service 모듈을 직접 의존하지 않고 interface를 통하여 의존하도록 하였다. 이를 통해 DIP를 잘 따랐다고 생각했다.
또한, 전략 패턴을 통해 행위에 대한 Interface에도 의존하도록 설계했다.
이를 통해 더욱 test하기 쉬운 코드를 만들 수 있었다.
factory클래스는 언제나 test코드를 만들기 어려웠지만, 전략 패턴을 사용하여 한결 테스트 코드를 제작하기 편하게 하였다.
테스트 코드 관련 내용은 아래 포스팅을 참고해주세요!
전략 패턴을 사용하여 test에 적용하기
지난 글에 전략 패턴과 DIP를 사용하여 구현하는 법을 알아보았다.오늘은 실제 전략 패턴의 사용 예시와, test를 구현하면 어떤 점이 용이한지 알아보고자 한다. 전략 패턴을 사용하여 구현하기
hyogu.tistory.com
'Backend' 카테고리의 다른 글
AWS lambda python 패키징 오류 타파 (0) | 2024.12.07 |
---|---|
전략 패턴을 사용하여 test에 적용하기 (0) | 2024.11.27 |
맨땅에 머리부터 박아보는 DDD 도전기 - 1 (1) | 2024.11.24 |
[MVC] Domain과 Model의 차이에 대하여 (1) | 2024.11.13 |
방어적 프로그래밍 (0) | 2024.11.06 |