Strategy Pattern In Spring (feat. JPA)

최근에 Medium 에서 아주 신기한 글을 봤다.
spring 에서 전략 패턴을 구사하는 내용인데, 새로운 DI 방법을 보고 너무 신기했다. 혼자 다른 곳에 적용하고 싶어서 이것저것 건들다가 손을 댔는데, 예제가 허접하다. 양해 부탁한다.

우선, 해당 글 링크
https://medium.com/javarevisited/implementing-the-strategy-pattern-with-spring-de2bad3abc2f

전략 패턴이란 해당 글에서 이렇게 설명한다.
The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm at runtime.

런타임에서 알고리즘을 선택하는 행동적인 패턴이라고 한다.
처음 듣는다면, 설명 만으로 한번에 이해하기 조금 어려울 수 있다.
해당 글에서는 e-commerce  주문 영역을 다루고 있다.

필자가 구현해본 예제는 조금 전략패턴에 맞지 않는 도메인과 구조일 수 있다는 점 다시 한 번 양해를 구한다.

전자제품, 의류, 음식, 책은 Product 를 상속받은 구조이며, TABLE_PER_CLASS 전략으로 각각의 테이블로 구성하도록 설계했다.

아래에서 Product 를 확인할 수 있다.

그리고 아래에는 Product 를 상속받은 Book class 의 구조이다.
Author, Weight, Size 는 @Embedded 를 사용하여 구현하였다.
Electronics, Clothing, Food class 는 생략하겠다.

그렇다면, 어디서 전략패턴을 구사하고 있는가를 이제 보자.
필자가 가장 신기했던 부분이 바로, 아래에 있다.
productCode PathVariable 을 이용하여 ProductType enum 을 이용하여 serviceName 을 가져오고 service 들이 있는 Map 에서 원하는 service 를 꺼내서 findAll() 메서드를 호출하는 방식이다.

여기서 신기한 점은 바로 Map 을 이용한 DI 이며, 또한 service 를 맵에서 name 을 통해서 가져올 수 있다는 점이다.


아래에는 ProductType enum 이다. PathVriable 에 1~4 값이 들어오고 이에 따라 serviceName을 return 하고 있는 메서드가 find 이다.


그렇다면 각각의 서비스들의 구성을 보자.
아래와 같이 각 4개의 서비스들은 ProductService interface 를 구현하고 있다.


먼저 ProductService 이다 제너릭을 이용하여 Product 를 상속받은 Object 만 가능하도록 했다.


그리고 아래는 BookProductService 이다.
서비스 이름을 @Service("서비스이름") 을 통해서 지정했다.

다른 서비스들도 Book 서비스와 마찬가지로 구현하였다.

결론
-> Spring DI 의 새로운 방법을 알게 되었다. 졸라게 awesome 하다고 느껴진다. 여러모로 유용하게 사용하면 좋을 것 같다.

하지만, 내가 만든 예제에서 부적합한 면이 있다.
첫째로, 조회에서 적용할 패턴이 아니다. 지금의 예제는 기껏해야 동일하게 findAll을 하기 때문에 무리가 없어 보이지만 비즈니스 프로세스를 풀기위해서 복잡해지고 확장이 어려워 질 수 있다고 본다.
 위에 링크의 예제처럼 결제 종류마다 프로세스의 차이가 있을 경우 사용하는것이 훨씬 적합하다고 생각한다.

둘째로, 제너릭을 사용해서 Controller 에서 IDE 가 Raw use of parameterized class 'ProductService' 해당 Warning 을 뿜는다.
private final Map<String, ProductService<Product>> productFindService; 라고 타입을 정확하게 명시해주지 않으면 해당 warning message 를 뿜는다.
하지 Product 로 지정할 시 상속받은 해당 4개의 type DI 가 이뤄지지 않아서 .get() 메서드 호출 시 NPE 를 달달하게 마주하게 된다.
혹은
private final Map<String, ProductService<Book>> productFindService; 라고 타입을 Book 으로 지정하게 되면, 나머지 3개의 type 은 DI가 이뤄지지 않아서 무쓸모가 된다.
다시 한 번 되돌아보면 음.. 제너릭을 이용한 설계와 return 타입이 있는경우에 사용하는게 아니라는점..!
return 타입이 void 인 메서드, 즉 단순히 호출하고 처리하는 메서드에 적합하다.

또한 이에 비춰 보면, 동기 처리보다 비동기 처리에 더 적합하지 않을까 하는 생각도 든다.
더 고민해 볼 여지가 많이 생긴다.

그래서 결론 Spring 에서도 이렇게 전략패턴이 가능하다! 존나 좋다. 하지만 내가 만든 예제는 조금 별로다.
적합한 도메인과 적합한 기능에 사용해야 된다!

너무 졸려서 그만 써야겠다.
추후에 다른 시도도 많이 해봐야겠다. : )

해당 프로젝트 github 주소이다.
https://github.com/ndgndg91/commerce

댓글

이 블로그의 인기 게시물

About JVM Warm up

About idempotent

About Kafka Basic

About ZGC

sneak peek jitpack

Spring Boot Actuator readiness, liveness probes on k8s

About Websocket minimize data size and data transfer cost on cloud

About G1 GC

대학생 코딩 과제 대행 java, python, oracle 네 번째